diff --git a/ImageSharp.Textures.sln b/ImageSharp.Textures.sln index 636514f8..3f610385 100644 --- a/ImageSharp.Textures.sln +++ b/ImageSharp.Textures.sln @@ -49,35 +49,92 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{ .github\workflows\build-and-test.yml = .github\workflows\build-and-test.yml EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageSharp.Textures.Astc", "src\ImageSharp.Textures.Astc\ImageSharp.Textures.Astc.csproj", "{AE37301B-3635-4C61-A026-DEB2E1328DD1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageSharp.Textures.Astc.Reference.Tests", "tests\ImageSharp.Textures.Astc.Reference.Tests\ImageSharp.Textures.Astc.Reference.Tests.csproj", "{E15C2E1A-FCD6-42B1-82D2-2C72CC4DC720}" +EndProject Global - GlobalSection(SharedMSBuildProjectFiles) = preSolution - shared-infrastructure\src\SharedInfrastructure\SharedInfrastructure.projitems*{1588f6c4-2186-4a35-9693-e9f296791393}*SharedItemsImports = 5 - tests\Images\Images.projitems*{17fcbd4d-d232-45e8-876f-dfbc2fad52cf}*SharedItemsImports = 5 - tests\Images\Images.projitems*{18be79b6-6b95-4ed7-a963-ad75f6cb9f3c}*SharedItemsImports = 5 - tests\Images\Images.projitems*{68a8cc40-6aed-4e96-b524-31b1158fdeea}*SharedItemsImports = 13 - tests\Images\Images.projitems*{b159ffd1-e646-42d0-892c-4abf69103712}*SharedItemsImports = 5 - EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {1588F6C4-2186-4A35-9693-E9F296791393}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1588F6C4-2186-4A35-9693-E9F296791393}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1588F6C4-2186-4A35-9693-E9F296791393}.Debug|x64.ActiveCfg = Debug|Any CPU + {1588F6C4-2186-4A35-9693-E9F296791393}.Debug|x64.Build.0 = Debug|Any CPU + {1588F6C4-2186-4A35-9693-E9F296791393}.Debug|x86.ActiveCfg = Debug|Any CPU + {1588F6C4-2186-4A35-9693-E9F296791393}.Debug|x86.Build.0 = Debug|Any CPU {1588F6C4-2186-4A35-9693-E9F296791393}.Release|Any CPU.ActiveCfg = Release|Any CPU {1588F6C4-2186-4A35-9693-E9F296791393}.Release|Any CPU.Build.0 = Release|Any CPU + {1588F6C4-2186-4A35-9693-E9F296791393}.Release|x64.ActiveCfg = Release|Any CPU + {1588F6C4-2186-4A35-9693-E9F296791393}.Release|x64.Build.0 = Release|Any CPU + {1588F6C4-2186-4A35-9693-E9F296791393}.Release|x86.ActiveCfg = Release|Any CPU + {1588F6C4-2186-4A35-9693-E9F296791393}.Release|x86.Build.0 = Release|Any CPU {B159FFD1-E646-42D0-892C-4ABF69103712}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B159FFD1-E646-42D0-892C-4ABF69103712}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B159FFD1-E646-42D0-892C-4ABF69103712}.Debug|x64.ActiveCfg = Debug|Any CPU + {B159FFD1-E646-42D0-892C-4ABF69103712}.Debug|x64.Build.0 = Debug|Any CPU + {B159FFD1-E646-42D0-892C-4ABF69103712}.Debug|x86.ActiveCfg = Debug|Any CPU + {B159FFD1-E646-42D0-892C-4ABF69103712}.Debug|x86.Build.0 = Debug|Any CPU {B159FFD1-E646-42D0-892C-4ABF69103712}.Release|Any CPU.ActiveCfg = Release|Any CPU {B159FFD1-E646-42D0-892C-4ABF69103712}.Release|Any CPU.Build.0 = Release|Any CPU + {B159FFD1-E646-42D0-892C-4ABF69103712}.Release|x64.ActiveCfg = Release|Any CPU + {B159FFD1-E646-42D0-892C-4ABF69103712}.Release|x64.Build.0 = Release|Any CPU + {B159FFD1-E646-42D0-892C-4ABF69103712}.Release|x86.ActiveCfg = Release|Any CPU + {B159FFD1-E646-42D0-892C-4ABF69103712}.Release|x86.Build.0 = Release|Any CPU {18BE79B6-6B95-4ED7-A963-AD75F6CB9F3C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {18BE79B6-6B95-4ED7-A963-AD75F6CB9F3C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18BE79B6-6B95-4ED7-A963-AD75F6CB9F3C}.Debug|x64.ActiveCfg = Debug|Any CPU + {18BE79B6-6B95-4ED7-A963-AD75F6CB9F3C}.Debug|x64.Build.0 = Debug|Any CPU + {18BE79B6-6B95-4ED7-A963-AD75F6CB9F3C}.Debug|x86.ActiveCfg = Debug|Any CPU + {18BE79B6-6B95-4ED7-A963-AD75F6CB9F3C}.Debug|x86.Build.0 = Debug|Any CPU {18BE79B6-6B95-4ED7-A963-AD75F6CB9F3C}.Release|Any CPU.ActiveCfg = Release|Any CPU {18BE79B6-6B95-4ED7-A963-AD75F6CB9F3C}.Release|Any CPU.Build.0 = Release|Any CPU + {18BE79B6-6B95-4ED7-A963-AD75F6CB9F3C}.Release|x64.ActiveCfg = Release|Any CPU + {18BE79B6-6B95-4ED7-A963-AD75F6CB9F3C}.Release|x64.Build.0 = Release|Any CPU + {18BE79B6-6B95-4ED7-A963-AD75F6CB9F3C}.Release|x86.ActiveCfg = Release|Any CPU + {18BE79B6-6B95-4ED7-A963-AD75F6CB9F3C}.Release|x86.Build.0 = Release|Any CPU {17FCBD4D-D232-45E8-876F-DFBC2FAD52CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {17FCBD4D-D232-45E8-876F-DFBC2FAD52CF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {17FCBD4D-D232-45E8-876F-DFBC2FAD52CF}.Debug|x64.ActiveCfg = Debug|Any CPU + {17FCBD4D-D232-45E8-876F-DFBC2FAD52CF}.Debug|x64.Build.0 = Debug|Any CPU + {17FCBD4D-D232-45E8-876F-DFBC2FAD52CF}.Debug|x86.ActiveCfg = Debug|Any CPU + {17FCBD4D-D232-45E8-876F-DFBC2FAD52CF}.Debug|x86.Build.0 = Debug|Any CPU {17FCBD4D-D232-45E8-876F-DFBC2FAD52CF}.Release|Any CPU.ActiveCfg = Release|Any CPU {17FCBD4D-D232-45E8-876F-DFBC2FAD52CF}.Release|Any CPU.Build.0 = Release|Any CPU + {17FCBD4D-D232-45E8-876F-DFBC2FAD52CF}.Release|x64.ActiveCfg = Release|Any CPU + {17FCBD4D-D232-45E8-876F-DFBC2FAD52CF}.Release|x64.Build.0 = Release|Any CPU + {17FCBD4D-D232-45E8-876F-DFBC2FAD52CF}.Release|x86.ActiveCfg = Release|Any CPU + {17FCBD4D-D232-45E8-876F-DFBC2FAD52CF}.Release|x86.Build.0 = Release|Any CPU + {AE37301B-3635-4C61-A026-DEB2E1328DD1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AE37301B-3635-4C61-A026-DEB2E1328DD1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AE37301B-3635-4C61-A026-DEB2E1328DD1}.Debug|x64.ActiveCfg = Debug|Any CPU + {AE37301B-3635-4C61-A026-DEB2E1328DD1}.Debug|x64.Build.0 = Debug|Any CPU + {AE37301B-3635-4C61-A026-DEB2E1328DD1}.Debug|x86.ActiveCfg = Debug|Any CPU + {AE37301B-3635-4C61-A026-DEB2E1328DD1}.Debug|x86.Build.0 = Debug|Any CPU + {AE37301B-3635-4C61-A026-DEB2E1328DD1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AE37301B-3635-4C61-A026-DEB2E1328DD1}.Release|Any CPU.Build.0 = Release|Any CPU + {AE37301B-3635-4C61-A026-DEB2E1328DD1}.Release|x64.ActiveCfg = Release|Any CPU + {AE37301B-3635-4C61-A026-DEB2E1328DD1}.Release|x64.Build.0 = Release|Any CPU + {AE37301B-3635-4C61-A026-DEB2E1328DD1}.Release|x86.ActiveCfg = Release|Any CPU + {AE37301B-3635-4C61-A026-DEB2E1328DD1}.Release|x86.Build.0 = Release|Any CPU + {E15C2E1A-FCD6-42B1-82D2-2C72CC4DC720}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E15C2E1A-FCD6-42B1-82D2-2C72CC4DC720}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E15C2E1A-FCD6-42B1-82D2-2C72CC4DC720}.Debug|x64.ActiveCfg = Debug|Any CPU + {E15C2E1A-FCD6-42B1-82D2-2C72CC4DC720}.Debug|x64.Build.0 = Debug|Any CPU + {E15C2E1A-FCD6-42B1-82D2-2C72CC4DC720}.Debug|x86.ActiveCfg = Debug|Any CPU + {E15C2E1A-FCD6-42B1-82D2-2C72CC4DC720}.Debug|x86.Build.0 = Debug|Any CPU + {E15C2E1A-FCD6-42B1-82D2-2C72CC4DC720}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E15C2E1A-FCD6-42B1-82D2-2C72CC4DC720}.Release|Any CPU.Build.0 = Release|Any CPU + {E15C2E1A-FCD6-42B1-82D2-2C72CC4DC720}.Release|x64.ActiveCfg = Release|Any CPU + {E15C2E1A-FCD6-42B1-82D2-2C72CC4DC720}.Release|x64.Build.0 = Release|Any CPU + {E15C2E1A-FCD6-42B1-82D2-2C72CC4DC720}.Release|x86.ActiveCfg = Release|Any CPU + {E15C2E1A-FCD6-42B1-82D2-2C72CC4DC720}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -90,8 +147,17 @@ Global {17FCBD4D-D232-45E8-876F-DFBC2FAD52CF} = {6DF92068-B792-4038-8E3F-5FDF2E026BE7} {E6224AB7-6987-4BA1-B2A6-ECFB7DA281DE} = {9F1F0B0F-704F-4B71-89EF-EE36042A27C9} {9F19EBB4-32DB-4AFE-A5E4-722EDFAAE04B} = {E6224AB7-6987-4BA1-B2A6-ECFB7DA281DE} + {AE37301B-3635-4C61-A026-DEB2E1328DD1} = {5E6840D2-9CBB-4FDE-8378-33086CF5A8D8} + {E15C2E1A-FCD6-42B1-82D2-2C72CC4DC720} = {6DF92068-B792-4038-8E3F-5FDF2E026BE7} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {F1762A0D-74C4-454A-BCB7-C010BB067E58} EndGlobalSection + GlobalSection(SharedMSBuildProjectFiles) = preSolution + shared-infrastructure\src\SharedInfrastructure\SharedInfrastructure.projitems*{1588f6c4-2186-4a35-9693-e9f296791393}*SharedItemsImports = 5 + tests\Images\Images.projitems*{17fcbd4d-d232-45e8-876f-dfbc2fad52cf}*SharedItemsImports = 5 + tests\Images\Images.projitems*{18be79b6-6b95-4ed7-a963-ad75f6cb9f3c}*SharedItemsImports = 5 + tests\Images\Images.projitems*{68a8cc40-6aed-4e96-b524-31b1158fdeea}*SharedItemsImports = 13 + tests\Images\Images.projitems*{b159ffd1-e646-42d0-892c-4abf69103712}*SharedItemsImports = 5 + EndGlobalSection EndGlobal diff --git a/README.md b/README.md index 5be8e659..6fb3d82e 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ SixLabors.ImageSharp.Textures [![Build Status](https://img.shields.io/github/actions/workflow/status/SixLabors/ImageSharp.Textures/build-and-test.yml?branch=main)](https://github.com/SixLabors/ImageSharp.Textures/actions) [![Code coverage](https://codecov.io/gh/SixLabors/ImageSharp.Textures/branch/main/graph/badge.svg)](https://codecov.io/gh/SixLabors/ImageSharp) -[![License: Apache 2.0](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) +[![License: Six Labors Split](https://img.shields.io/badge/license-Six%20Labors%20Split-%23e30183)](https://github.com/SixLabors/ImageSharp/blob/main/LICENSE) [![Twitter](https://img.shields.io/twitter/url/http/shields.io.svg?style=flat&logo=twitter)](https://twitter.com/intent/tweet?hashtags=imagesharp,dotnet,oss&text=ImageSharp.+A+new+cross-platform+2D+graphics+API+in+C%23&url=https%3a%2f%2fgithub.com%2fSixLabors%2fImageSharp&via=sixlabors) @@ -33,6 +33,7 @@ with the following compressions: - BC5 - BC6H - BC7 +- ASTC Encoding textures is **not** yet supported. PR are of course very welcome. diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 2813cc4b..7060cf45 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -11,7 +11,7 @@ --> - + @@ -25,6 +25,7 @@ + diff --git a/src/ImageSharp.Textures.Astc/AstcDecoder.cs b/src/ImageSharp.Textures.Astc/AstcDecoder.cs new file mode 100644 index 00000000..c243265e --- /dev/null +++ b/src/ImageSharp.Textures.Astc/AstcDecoder.cs @@ -0,0 +1,447 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Buffers; +using System.Buffers.Binary; +using SixLabors.ImageSharp.Textures.Astc.BlockDecoder; +using SixLabors.ImageSharp.Textures.Astc.ColorEncoding; +using SixLabors.ImageSharp.Textures.Astc.Core; +using SixLabors.ImageSharp.Textures.Astc.IO; +using SixLabors.ImageSharp.Textures.Astc.TexelBlock; + +namespace SixLabors.ImageSharp.Textures.Astc; + +/// +/// Provides methods to decode ASTC-compressed texture data into uncompressed pixel formats. +/// +public static class AstcDecoder +{ + private static readonly ArrayPool ArrayPool = ArrayPool.Shared; + private const int BytesPerPixelUnorm8 = 4; + + /// + /// Decompresses ASTC-compressed data to uncompressed RGBA8 format (4 bytes per pixel). + /// + /// The ASTC-compressed texture data + /// Image width in pixels + /// Image height in pixels + /// The ASTC block footprint (e.g., 4x4, 5x5) + /// Array of bytes in RGBA8 format (width * height * 4 bytes total) + /// If decompression fails for any block + public static Span DecompressImage(ReadOnlySpan astcData, int width, int height, Footprint footprint) + { + byte[] imageBuffer = new byte[width * height * BytesPerPixelUnorm8]; + + return DecompressImage(astcData, width, height, footprint, imageBuffer) + ? imageBuffer + : []; + } + + /// + /// Decompresses ASTC-compressed data to uncompressed RGBA8 format into a caller-provided buffer. + /// + /// The ASTC-compressed texture data + /// Image width in pixels + /// Image height in pixels + /// The ASTC block footprint (e.g., 4x4, 5x5) + /// Output buffer. Must be at least width * height * 4 bytes. + /// True if decompression succeeded, false if input was invalid. + /// If decompression fails for any block + public static bool DecompressImage(ReadOnlySpan astcData, int width, int height, Footprint footprint, Span imageBuffer) + { + if (!TryGetBlockLayout(astcData, width, height, footprint, out int blocksWide, out int blocksHigh)) + { + return false; + } + + byte[] decodedBlock = []; + + try + { + // Create a buffer once for fallback blocks; fast path writes directly to image + decodedBlock = ArrayPool.Rent(footprint.Width * footprint.Height * BytesPerPixelUnorm8); + Span decodedPixels = decodedBlock.AsSpan(); + int blockIndex = 0; + int footprintWidth = footprint.Width; + int footprintHeight = footprint.Height; + + for (int blockY = 0; blockY < blocksHigh; blockY++) + { + for (int blockX = 0; blockX < blocksWide; blockX++) + { + int blockDataOffset = blockIndex++ * PhysicalBlock.SizeInBytes; + if (blockDataOffset + PhysicalBlock.SizeInBytes > astcData.Length) + { + continue; + } + + ulong low = BinaryPrimitives.ReadUInt64LittleEndian(astcData[blockDataOffset..]); + ulong high = BinaryPrimitives.ReadUInt64LittleEndian(astcData[(blockDataOffset + 8)..]); + UInt128 blockBits = new(high, low); + + int dstBaseX = blockX * footprintWidth; + int dstBaseY = blockY * footprintHeight; + int copyWidth = Math.Min(footprintWidth, width - dstBaseX); + int copyHeight = Math.Min(footprintHeight, height - dstBaseY); + + BlockInfo info = BlockInfo.Decode(blockBits); + if (!info.IsValid) + { + continue; + } + + // Fast path: fuse decode directly into image buffer for interior full blocks + if (!info.IsVoidExtent && info.PartitionCount == 1 && !info.IsDualPlane + && !info.EndpointMode0.IsHdr() + && copyWidth == footprintWidth && copyHeight == footprintHeight) + { + FusedLdrBlockDecoder.DecompressBlockFusedLdrToImage( + blockBits, + in info, + footprint, + dstBaseX, + dstBaseY, + width, + imageBuffer); + continue; + } + + // Fallback: decode to temp buffer, then copy + if (!info.IsVoidExtent && info.PartitionCount == 1 && !info.IsDualPlane + && !info.EndpointMode0.IsHdr()) + { + FusedLdrBlockDecoder.DecompressBlockFusedLdr(blockBits, in info, footprint, decodedPixels); + } + else + { + LogicalBlock? logicalBlock = LogicalBlock.UnpackLogicalBlock(footprint, blockBits, in info); + if (logicalBlock is null) + { + continue; + } + + logicalBlock.WriteAllPixelsLdr(footprint, decodedPixels); + } + + int copyBytes = copyWidth * BytesPerPixelUnorm8; + for (int pixelY = 0; pixelY < copyHeight; pixelY++) + { + int srcOffset = pixelY * footprintWidth * BytesPerPixelUnorm8; + int dstOffset = (((dstBaseY + pixelY) * width) + dstBaseX) * BytesPerPixelUnorm8; + decodedPixels.Slice(srcOffset, copyBytes) + .CopyTo(imageBuffer.Slice(dstOffset, copyBytes)); + } + } + } + } + finally + { + ArrayPool.Return(decodedBlock); + } + + return true; + } + + /// + /// Decompress a single ASTC block to RGBA8 pixel data + /// + /// The data to decode + /// The type of ASTC block footprint e.g. 4x4, 5x5, etc. + /// The decoded block of pixels as RGBA values + public static Span DecompressBlock(ReadOnlySpan blockData, Footprint footprint) + { + byte[] decodedPixels = []; + try + { + decodedPixels = ArrayPool.Rent(footprint.Width * footprint.Height * BytesPerPixelUnorm8); + Span decodedPixelBuffer = decodedPixels.AsSpan(); + + DecompressBlock(blockData, footprint, decodedPixelBuffer); + } + finally + { + ArrayPool.Return(decodedPixels); + } + + return decodedPixels; + } + + /// + /// Decompresses a single ASTC block to RGBA8 pixel data + /// + /// The data to decode + /// The type of ASTC block footprint e.g. 4x4, 5x5, etc. + /// The buffer to write the decoded pixels into + public static void DecompressBlock(ReadOnlySpan blockData, Footprint footprint, Span buffer) + { + // Read the 16 bytes that make up the ASTC block as a 128-bit value + ulong low = BinaryPrimitives.ReadUInt64LittleEndian(blockData); + ulong high = BinaryPrimitives.ReadUInt64LittleEndian(blockData[8..]); + UInt128 blockBits = new(high, low); + + BlockInfo info = BlockInfo.Decode(blockBits); + if (!info.IsValid) + { + return; + } + + // Fully fused fast path for single-partition, non-dual-plane, LDR blocks + if (!info.IsVoidExtent && info.PartitionCount == 1 && !info.IsDualPlane + && !info.EndpointMode0.IsHdr()) + { + FusedLdrBlockDecoder.DecompressBlockFusedLdr(blockBits, in info, footprint, buffer); + return; + } + + // Fallback for void extent, multi-partition, dual plane, HDR + LogicalBlock? logicalBlock = LogicalBlock.UnpackLogicalBlock(footprint, blockBits, in info); + if (logicalBlock is null) + { + return; + } + + logicalBlock.WriteAllPixelsLdr(footprint, buffer); + } + + /// + /// Decompresses ASTC-compressed data to RGBA values. + /// + /// The ASTC-compressed texture data + /// Image width in pixels + /// Image height in pixels + /// The ASTC block footprint (e.g., 4x4, 5x5) + /// + /// Values in RGBA order. For HDR content, values may exceed 1.0. + /// + public static Span DecompressHdrImage(ReadOnlySpan astcData, int width, int height, Footprint footprint) + { + const int channelsPerPixel = 4; + float[] imageBuffer = new float[width * height * channelsPerPixel]; + if (!DecompressHdrImage(astcData, width, height, footprint, imageBuffer)) + { + return []; + } + + return imageBuffer; + } + + /// + /// Decompresses ASTC-compressed data to RGBA float values into a caller-provided buffer. + /// + /// The ASTC-compressed texture data + /// Image width in pixels + /// Image height in pixels + /// The ASTC block footprint (e.g., 4x4, 5x5) + /// Output buffer. Must be at least width * height * 4 floats. + /// True if decompression succeeded, false if input was invalid. + /// If decompression fails for any block + public static bool DecompressHdrImage(ReadOnlySpan astcData, int width, int height, Footprint footprint, Span imageBuffer) + { + if (!TryGetBlockLayout(astcData, width, height, footprint, out int blocksWide, out int blocksHigh)) + { + return false; + } + + const int channelsPerPixel = 4; + float[] decodedBlock = []; + + try + { + // Create a buffer once for fallback blocks; fast path writes directly to image + decodedBlock = ArrayPool.Shared.Rent(footprint.Width * footprint.Height * channelsPerPixel); + Span decodedPixels = decodedBlock.AsSpan(); + int blockIndex = 0; + int footprintWidth = footprint.Width; + int footprintHeight = footprint.Height; + + for (int blockY = 0; blockY < blocksHigh; blockY++) + { + for (int blockX = 0; blockX < blocksWide; blockX++) + { + int blockDataOffset = blockIndex++ * PhysicalBlock.SizeInBytes; + if (blockDataOffset + PhysicalBlock.SizeInBytes > astcData.Length) + { + continue; + } + + ulong low = BinaryPrimitives.ReadUInt64LittleEndian(astcData[blockDataOffset..]); + ulong high = BinaryPrimitives.ReadUInt64LittleEndian(astcData[(blockDataOffset + 8)..]); + UInt128 blockBits = new(high, low); + + int dstBaseX = blockX * footprintWidth; + int dstBaseY = blockY * footprintHeight; + int copyWidth = Math.Min(footprintWidth, width - dstBaseX); + int copyHeight = Math.Min(footprintHeight, height - dstBaseY); + + BlockInfo info = BlockInfo.Decode(blockBits); + if (!info.IsValid) + { + continue; + } + + // Fast path: fuse decode directly into image buffer for interior full blocks + if (!info.IsVoidExtent && info.PartitionCount == 1 && !info.IsDualPlane + && copyWidth == footprintWidth && copyHeight == footprintHeight) + { + FusedHdrBlockDecoder.DecompressBlockFusedHdrToImage( + blockBits, + in info, + footprint, + dstBaseX, + dstBaseY, + width, + imageBuffer); + continue; + } + + // Fused decode to temp buffer for single-partition non-dual-plane + if (!info.IsVoidExtent && info.PartitionCount == 1 && !info.IsDualPlane) + { + FusedHdrBlockDecoder.DecompressBlockFusedHdr(blockBits, in info, footprint, decodedPixels); + } + else + { + // Fallback: LogicalBlock path for void extent, multi-partition, dual plane + LogicalBlock? logicalBlock = LogicalBlock.UnpackLogicalBlock(footprint, blockBits, in info); + if (logicalBlock is null) + { + continue; + } + + for (int row = 0; row < footprintHeight; row++) + { + for (int column = 0; column < footprintWidth; ++column) + { + int pixelOffset = (footprintWidth * row * channelsPerPixel) + (column * channelsPerPixel); + logicalBlock.WriteHdrPixel(column, row, decodedPixels.Slice(pixelOffset, channelsPerPixel)); + } + } + } + + int copyFloats = copyWidth * channelsPerPixel; + for (int pixelY = 0; pixelY < copyHeight; pixelY++) + { + int srcOffset = pixelY * footprintWidth * channelsPerPixel; + int dstOffset = (((dstBaseY + pixelY) * width) + dstBaseX) * channelsPerPixel; + decodedPixels.Slice(srcOffset, copyFloats) + .CopyTo(imageBuffer.Slice(dstOffset, copyFloats)); + } + } + } + } + finally + { + ArrayPool.Shared.Return(decodedBlock); + } + + return true; + } + + /// + /// Decompresses ASTC-compressed data to RGBA values. + /// + /// The ASTC-compressed texture data + /// Image width in pixels + /// Image height in pixels + /// The ASTC block footprint type + /// + /// Values in RGBA order. For HDR content, values may exceed 1.0. + /// + public static Span DecompressHdrImage(ReadOnlySpan astcData, int width, int height, FootprintType footprint) + { + Footprint footPrint = Footprint.FromFootprintType(footprint); + return DecompressHdrImage(astcData, width, height, footPrint); + } + + /// + /// Decompresses a single ASTC block to float RGBA values. + /// + /// The 16-byte ASTC block to decode + /// The ASTC block footprint + /// The buffer to write decoded values into (must be at least footprint.Width * footprint.Height * 4 elements) + public static void DecompressHdrBlock(ReadOnlySpan blockData, Footprint footprint, Span buffer) + { + // Read the 16 bytes that make up the ASTC block as a 128-bit value + ulong low = BinaryPrimitives.ReadUInt64LittleEndian(blockData); + ulong high = BinaryPrimitives.ReadUInt64LittleEndian(blockData[8..]); + UInt128 blockBits = new(high, low); + + BlockInfo info = BlockInfo.Decode(blockBits); + if (!info.IsValid) + { + return; + } + + // Fused fast path for single-partition, non-dual-plane blocks + if (!info.IsVoidExtent && info.PartitionCount == 1 && !info.IsDualPlane) + { + FusedHdrBlockDecoder.DecompressBlockFusedHdr(blockBits, in info, footprint, buffer); + return; + } + + // Fallback for void extent, multi-partition, dual plane + LogicalBlock? logicalBlock = LogicalBlock.UnpackLogicalBlock(footprint, blockBits, in info); + if (logicalBlock is null) + { + return; + } + + const int channelsPerPixel = 4; + for (int row = 0; row < footprint.Height; row++) + { + for (int column = 0; column < footprint.Width; ++column) + { + int pixelOffset = (footprint.Width * row * channelsPerPixel) + (column * channelsPerPixel); + logicalBlock.WriteHdrPixel(column, row, buffer.Slice(pixelOffset, channelsPerPixel)); + } + } + } + + internal static Span DecompressImage(AstcFile file) + { + ArgumentNullException.ThrowIfNull(file); + + return DecompressImage(file.Blocks, file.Width, file.Height, file.Footprint); + } + + internal static Span DecompressImage(ReadOnlySpan astcData, int width, int height, FootprintType footprint) + { + Footprint footPrint = Footprint.FromFootprintType(footprint); + + return DecompressImage(astcData, width, height, footPrint); + } + + private static bool TryGetBlockLayout( + ReadOnlySpan astcData, + int width, + int height, + Footprint footprint, + out int blocksWide, + out int blocksHigh) + { + int blockWidth = footprint.Width; + int blockHeight = footprint.Height; + blocksWide = 0; + blocksHigh = 0; + + if (blockWidth == 0 || blockHeight == 0 || width == 0 || height == 0) + { + return false; + } + + blocksWide = (width + blockWidth - 1) / blockWidth; + if (blocksWide == 0) + { + return false; + } + + blocksHigh = (height + blockHeight - 1) / blockHeight; + int expectedBlockCount = blocksWide * blocksHigh; + if (astcData.Length % PhysicalBlock.SizeInBytes != 0 || astcData.Length / PhysicalBlock.SizeInBytes != expectedBlockCount) + { + return false; + } + + return true; + } +} diff --git a/src/ImageSharp.Textures.Astc/BiseEncoding/BiseEncodingMode.cs b/src/ImageSharp.Textures.Astc/BiseEncoding/BiseEncodingMode.cs new file mode 100644 index 00000000..ae168b4b --- /dev/null +++ b/src/ImageSharp.Textures.Astc/BiseEncoding/BiseEncodingMode.cs @@ -0,0 +1,18 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Textures.Astc.BiseEncoding; + +/// +/// The encoding modes supported by BISE. +/// +/// +/// Note that the values correspond to the number of symbols in each alphabet. +/// +internal enum BiseEncodingMode +{ + Unknown = 0, + BitEncoding = 1, + TritEncoding = 3, + QuintEncoding = 5, +} diff --git a/src/ImageSharp.Textures.Astc/BiseEncoding/BoundedIntegerSequenceCodec.cs b/src/ImageSharp.Textures.Astc/BiseEncoding/BoundedIntegerSequenceCodec.cs new file mode 100644 index 00000000..8ee2c9f9 --- /dev/null +++ b/src/ImageSharp.Textures.Astc/BiseEncoding/BoundedIntegerSequenceCodec.cs @@ -0,0 +1,264 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Textures.Astc.BiseEncoding; + +/// +/// The Bounded Integer Sequence Encoding (BISE) allows storage of character sequences using +/// arbitrary alphabets of up to 256 symbols. Each alphabet size is encoded in the most +/// space-efficient choice of bits, trits, and quints. +/// +internal partial class BoundedIntegerSequenceCodec +{ + /// + /// The maximum number of bits needed to encode an ISE value. + /// + /// + /// The ASTC specification does not give a maximum number, however unquantized color + /// values have a maximum range of 255, meaning that we can't feasibly have more + /// than eight bits per value. + /// + private const int Log2MaxRangeForBits = 8; + + /// + /// The number of bits used after each value to store the interleaved quint block. + /// + protected static readonly int[] InterleavedQuintBits = [3, 2, 2]; + + /// + /// The number of bits used after each value to store the interleaved trit block. + /// + protected static readonly int[] InterleavedTritBits = [2, 2, 1, 2, 1]; + + /// + /// Trit encodings for BISE blocks. + /// + /// + /// + /// These tables are used to decode the blocks of values encoded using the ASTC + /// integer sequence encoding. The theory is that five trits (values that can + /// take any number in the range [0, 2]) can take on a total of 3^5 = 243 total + /// values, which can be stored in eight bits. These eight bits are used to + /// decode the five trits based on the ASTC specification in Section C.2.12. + /// + /// + /// For simplicity, we have stored a look-up table here so that we don't need + /// to implement the decoding logic. Similarly, seven bits are used to decode + /// three quints. + /// + /// + protected static readonly int[][] TritEncodings = + [ + [0, 0, 0, 0, 0], [1, 0, 0, 0, 0], [2, 0, 0, 0, 0], [0, 0, 2, 0, 0], [0, 1, 0, 0, 0], [1, 1, 0, 0, 0], [2, 1, 0, 0, 0], [1, 0, 2, 0, 0], [0, 2, 0, 0, 0], + [1, 2, 0, 0, 0], [2, 2, 0, 0, 0], [2, 0, 2, 0, 0], [0, 2, 2, 0, 0], [1, 2, 2, 0, 0], [2, 2, 2, 0, 0], [2, 0, 2, 0, 0], [0, 0, 1, 0, 0], [1, 0, 1, 0, 0], + [2, 0, 1, 0, 0], [0, 1, 2, 0, 0], [0, 1, 1, 0, 0], [1, 1, 1, 0, 0], [2, 1, 1, 0, 0], [1, 1, 2, 0, 0], [0, 2, 1, 0, 0], [1, 2, 1, 0, 0], [2, 2, 1, 0, 0], + [2, 1, 2, 0, 0], [0, 0, 0, 2, 2], [1, 0, 0, 2, 2], [2, 0, 0, 2, 2], [0, 0, 2, 2, 2], [0, 0, 0, 1, 0], [1, 0, 0, 1, 0], [2, 0, 0, 1, 0], [0, 0, 2, 1, 0], + [0, 1, 0, 1, 0], [1, 1, 0, 1, 0], [2, 1, 0, 1, 0], [1, 0, 2, 1, 0], [0, 2, 0, 1, 0], [1, 2, 0, 1, 0], [2, 2, 0, 1, 0], [2, 0, 2, 1, 0], [0, 2, 2, 1, 0], + [1, 2, 2, 1, 0], [2, 2, 2, 1, 0], [2, 0, 2, 1, 0], [0, 0, 1, 1, 0], [1, 0, 1, 1, 0], [2, 0, 1, 1, 0], [0, 1, 2, 1, 0], [0, 1, 1, 1, 0], [1, 1, 1, 1, 0], + [2, 1, 1, 1, 0], [1, 1, 2, 1, 0], [0, 2, 1, 1, 0], [1, 2, 1, 1, 0], [2, 2, 1, 1, 0], [2, 1, 2, 1, 0], [0, 1, 0, 2, 2], [1, 1, 0, 2, 2], [2, 1, 0, 2, 2], + [1, 0, 2, 2, 2], [0, 0, 0, 2, 0], [1, 0, 0, 2, 0], [2, 0, 0, 2, 0], [0, 0, 2, 2, 0], [0, 1, 0, 2, 0], [1, 1, 0, 2, 0], [2, 1, 0, 2, 0], [1, 0, 2, 2, 0], + [0, 2, 0, 2, 0], [1, 2, 0, 2, 0], [2, 2, 0, 2, 0], [2, 0, 2, 2, 0], [0, 2, 2, 2, 0], [1, 2, 2, 2, 0], [2, 2, 2, 2, 0], [2, 0, 2, 2, 0], [0, 0, 1, 2, 0], + [1, 0, 1, 2, 0], [2, 0, 1, 2, 0], [0, 1, 2, 2, 0], [0, 1, 1, 2, 0], [1, 1, 1, 2, 0], [2, 1, 1, 2, 0], [1, 1, 2, 2, 0], [0, 2, 1, 2, 0], [1, 2, 1, 2, 0], + [2, 2, 1, 2, 0], [2, 1, 2, 2, 0], [0, 2, 0, 2, 2], [1, 2, 0, 2, 2], [2, 2, 0, 2, 2], [2, 0, 2, 2, 2], [0, 0, 0, 0, 2], [1, 0, 0, 0, 2], [2, 0, 0, 0, 2], + [0, 0, 2, 0, 2], [0, 1, 0, 0, 2], [1, 1, 0, 0, 2], [2, 1, 0, 0, 2], [1, 0, 2, 0, 2], [0, 2, 0, 0, 2], [1, 2, 0, 0, 2], [2, 2, 0, 0, 2], [2, 0, 2, 0, 2], + [0, 2, 2, 0, 2], [1, 2, 2, 0, 2], [2, 2, 2, 0, 2], [2, 0, 2, 0, 2], [0, 0, 1, 0, 2], [1, 0, 1, 0, 2], [2, 0, 1, 0, 2], [0, 1, 2, 0, 2], [0, 1, 1, 0, 2], + [1, 1, 1, 0, 2], [2, 1, 1, 0, 2], [1, 1, 2, 0, 2], [0, 2, 1, 0, 2], [1, 2, 1, 0, 2], [2, 2, 1, 0, 2], [2, 1, 2, 0, 2], [0, 2, 2, 2, 2], [1, 2, 2, 2, 2], + [2, 2, 2, 2, 2], [2, 0, 2, 2, 2], [0, 0, 0, 0, 1], [1, 0, 0, 0, 1], [2, 0, 0, 0, 1], [0, 0, 2, 0, 1], [0, 1, 0, 0, 1], [1, 1, 0, 0, 1], [2, 1, 0, 0, 1], + [1, 0, 2, 0, 1], [0, 2, 0, 0, 1], [1, 2, 0, 0, 1], [2, 2, 0, 0, 1], [2, 0, 2, 0, 1], [0, 2, 2, 0, 1], [1, 2, 2, 0, 1], [2, 2, 2, 0, 1], [2, 0, 2, 0, 1], + [0, 0, 1, 0, 1], [1, 0, 1, 0, 1], [2, 0, 1, 0, 1], [0, 1, 2, 0, 1], [0, 1, 1, 0, 1], [1, 1, 1, 0, 1], [2, 1, 1, 0, 1], [1, 1, 2, 0, 1], [0, 2, 1, 0, 1], + [1, 2, 1, 0, 1], [2, 2, 1, 0, 1], [2, 1, 2, 0, 1], [0, 0, 1, 2, 2], [1, 0, 1, 2, 2], [2, 0, 1, 2, 2], [0, 1, 2, 2, 2], [0, 0, 0, 1, 1], [1, 0, 0, 1, 1], + [2, 0, 0, 1, 1], [0, 0, 2, 1, 1], [0, 1, 0, 1, 1], [1, 1, 0, 1, 1], [2, 1, 0, 1, 1], [1, 0, 2, 1, 1], [0, 2, 0, 1, 1], [1, 2, 0, 1, 1], [2, 2, 0, 1, 1], + [2, 0, 2, 1, 1], [0, 2, 2, 1, 1], [1, 2, 2, 1, 1], [2, 2, 2, 1, 1], [2, 0, 2, 1, 1], [0, 0, 1, 1, 1], [1, 0, 1, 1, 1], [2, 0, 1, 1, 1], [0, 1, 2, 1, 1], + [0, 1, 1, 1, 1], [1, 1, 1, 1, 1], [2, 1, 1, 1, 1], [1, 1, 2, 1, 1], [0, 2, 1, 1, 1], [1, 2, 1, 1, 1], [2, 2, 1, 1, 1], [2, 1, 2, 1, 1], [0, 1, 1, 2, 2], + [1, 1, 1, 2, 2], [2, 1, 1, 2, 2], [1, 1, 2, 2, 2], [0, 0, 0, 2, 1], [1, 0, 0, 2, 1], [2, 0, 0, 2, 1], [0, 0, 2, 2, 1], [0, 1, 0, 2, 1], [1, 1, 0, 2, 1], + [2, 1, 0, 2, 1], [1, 0, 2, 2, 1], [0, 2, 0, 2, 1], [1, 2, 0, 2, 1], [2, 2, 0, 2, 1], [2, 0, 2, 2, 1], [0, 2, 2, 2, 1], [1, 2, 2, 2, 1], [2, 2, 2, 2, 1], + [2, 0, 2, 2, 1], [0, 0, 1, 2, 1], [1, 0, 1, 2, 1], [2, 0, 1, 2, 1], [0, 1, 2, 2, 1], [0, 1, 1, 2, 1], [1, 1, 1, 2, 1], [2, 1, 1, 2, 1], [1, 1, 2, 2, 1], + [0, 2, 1, 2, 1], [1, 2, 1, 2, 1], [2, 2, 1, 2, 1], [2, 1, 2, 2, 1], [0, 2, 1, 2, 2], [1, 2, 1, 2, 2], [2, 2, 1, 2, 2], [2, 1, 2, 2, 2], [0, 0, 0, 1, 2], + [1, 0, 0, 1, 2], [2, 0, 0, 1, 2], [0, 0, 2, 1, 2], [0, 1, 0, 1, 2], [1, 1, 0, 1, 2], [2, 1, 0, 1, 2], [1, 0, 2, 1, 2], [0, 2, 0, 1, 2], [1, 2, 0, 1, 2], + [2, 2, 0, 1, 2], [2, 0, 2, 1, 2], [0, 2, 2, 1, 2], [1, 2, 2, 1, 2], [2, 2, 2, 1, 2], [2, 0, 2, 1, 2], [0, 0, 1, 1, 2], [1, 0, 1, 1, 2], [2, 0, 1, 1, 2], + [0, 1, 2, 1, 2], [0, 1, 1, 1, 2], [1, 1, 1, 1, 2], [2, 1, 1, 1, 2], [1, 1, 2, 1, 2], [0, 2, 1, 1, 2], [1, 2, 1, 1, 2], [2, 2, 1, 1, 2], [2, 1, 2, 1, 2], + [0, 2, 2, 2, 2], [1, 2, 2, 2, 2], [2, 2, 2, 2, 2], [2, 1, 2, 2, 2] + ]; + + /// + /// Quint encodings for BISE blocks. + /// + /// + /// See for more details. + /// + protected static readonly int[][] QuintEncodings = + [ + [0, 0, 0], [1, 0, 0], [2, 0, 0], [3, 0, 0], [4, 0, 0], [0, 4, 0], [4, 4, 0], [4, 4, 4], [0, 1, 0], [1, 1, 0], [2, 1, 0], [3, 1, 0], [4, 1, 0], + [1, 4, 0], [4, 4, 1], [4, 4, 4], [0, 2, 0], [1, 2, 0], [2, 2, 0], [3, 2, 0], [4, 2, 0], [2, 4, 0], [4, 4, 2], [4, 4, 4], [0, 3, 0], [1, 3, 0], + [2, 3, 0], [3, 3, 0], [4, 3, 0], [3, 4, 0], [4, 4, 3], [4, 4, 4], [0, 0, 1], [1, 0, 1], [2, 0, 1], [3, 0, 1], [4, 0, 1], [0, 4, 1], [4, 0, 4], + [0, 4, 4], [0, 1, 1], [1, 1, 1], [2, 1, 1], [3, 1, 1], [4, 1, 1], [1, 4, 1], [4, 1, 4], [1, 4, 4], [0, 2, 1], [1, 2, 1], [2, 2, 1], [3, 2, 1], + [4, 2, 1], [2, 4, 1], [4, 2, 4], [2, 4, 4], [0, 3, 1], [1, 3, 1], [2, 3, 1], [3, 3, 1], [4, 3, 1], [3, 4, 1], [4, 3, 4], [3, 4, 4], [0, 0, 2], + [1, 0, 2], [2, 0, 2], [3, 0, 2], [4, 0, 2], [0, 4, 2], [2, 0, 4], [3, 0, 4], [0, 1, 2], [1, 1, 2], [2, 1, 2], [3, 1, 2], [4, 1, 2], [1, 4, 2], + [2, 1, 4], [3, 1, 4], [0, 2, 2], [1, 2, 2], [2, 2, 2], [3, 2, 2], [4, 2, 2], [2, 4, 2], [2, 2, 4], [3, 2, 4], [0, 3, 2], [1, 3, 2], [2, 3, 2], + [3, 3, 2], [4, 3, 2], [3, 4, 2], [2, 3, 4], [3, 3, 4], [0, 0, 3], [1, 0, 3], [2, 0, 3], [3, 0, 3], [4, 0, 3], [0, 4, 3], [0, 0, 4], [1, 0, 4], + [0, 1, 3], [1, 1, 3], [2, 1, 3], [3, 1, 3], [4, 1, 3], [1, 4, 3], [0, 1, 4], [1, 1, 4], [0, 2, 3], [1, 2, 3], [2, 2, 3], [3, 2, 3], [4, 2, 3], + [2, 4, 3], [0, 2, 4], [1, 2, 4], [0, 3, 3], [1, 3, 3], [2, 3, 3], [3, 3, 3], [4, 3, 3], [3, 4, 3], [0, 3, 4], [1, 3, 4] + ]; + + /// + /// The maximum ranges for BISE encoding. + /// + /// + /// These are the numbers between 1 and + /// that can be represented exactly as a number in the ranges + /// [0, 2^k), [0, 3 * 2^k), and [0, 5 * 2^k). + /// + internal static readonly int[] MaxRanges = [1, 2, 3, 4, 5, 7, 9, 11, 15, 19, 23, 31, 39, 47, 63, 79, 95, 127, 159, 191, 255]; + + // Flat encoding tables: eliminates jagged array indirection + protected static readonly int[] FlatTritEncodings = FlattenEncodings(TritEncodings, 5); + protected static readonly int[] FlatQuintEncodings = FlattenEncodings(QuintEncodings, 3); + + private static readonly (BiseEncodingMode Mode, int BitCount)[] PackingModeCache = InitPackingModeCache(); + + /// + /// Initializes a new instance of the class. + /// operate on sequences of integers and produce bit patterns that pack the + /// integers based on the encoding scheme specified in the ASTC specification + /// Section C.2.12. + /// + /// + /// + /// The resulting bit pattern is a sequence of encoded blocks. + /// All blocks in a sequence are one of the following encodings: + /// + /// + /// Bit encoding: one encoded value of the form 2^k + /// Trit encoding: five encoded values of the form 3*2^k + /// Quint encoding: three encoded values of the form 5*2^k + /// + /// The layouts of each block are designed such that the blocks can be truncated + /// during encoding in order to support variable length input sequences (i.e. a + /// sequence of values that are encoded using trit encoded blocks does not + /// need to have a multiple-of-five length). + /// + /// Creates a decoder that decodes values within [0, ] (inclusive). + protected BoundedIntegerSequenceCodec(int range) + { + (BiseEncodingMode encodingMode, int bitCount) = GetPackingModeBitCount(range); + this.Encoding = encodingMode; + this.BitCount = bitCount; + } + + protected BiseEncodingMode Encoding { get; } + + protected int BitCount { get; } + + /// + /// The number of bits needed to encode the given number of values with respect to the + /// number of trits, quints, and bits specified by . + /// + public static (BiseEncodingMode Mode, int BitCount) GetPackingModeBitCount(int range) + { + ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(range, 0); + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(range, 1 << Log2MaxRangeForBits); + + return PackingModeCache[range]; + } + + /// + /// Returns the overall bit count for a range of values encoded + /// + public static int GetBitCount(BiseEncodingMode encodingMode, int valuesCount, int bitCount) + { + int encodingBitCount = encodingMode switch + { + BiseEncodingMode.TritEncoding => ((valuesCount * 8) + 4) / 5, + BiseEncodingMode.QuintEncoding => ((valuesCount * 7) + 2) / 3, + BiseEncodingMode.BitEncoding => 0, + _ => throw new ArgumentOutOfRangeException(nameof(encodingMode), "Invalid encoding mode"), + }; + int baseBitCount = valuesCount * bitCount; + + return encodingBitCount + baseBitCount; + } + + /// + /// The number of bits needed to encode a given number of values within the range [0, ] (inclusive). + /// + public static int GetBitCountForRange(int valuesCount, int range) + { + (BiseEncodingMode mode, int bitCount) = GetPackingModeBitCount(range); + + return GetBitCount(mode, valuesCount, bitCount); + } + + /// + /// The size of a single ISE block in bits + /// + protected int GetEncodedBlockSize() + { + (int blockSize, int extraBlockSize) = this.Encoding switch + { + BiseEncodingMode.TritEncoding => (5, 8), + BiseEncodingMode.QuintEncoding => (3, 7), + BiseEncodingMode.BitEncoding => (1, 0), + _ => (0, 0), + }; + + return extraBlockSize + (blockSize * this.BitCount); + } + + private static int[] FlattenEncodings(int[][] jagged, int stride) + { + int[] flat = new int[jagged.Length * stride]; + for (int i = 0; i < jagged.Length; i++) + { + for (int j = 0; j < stride; j++) + { + flat[(i * stride) + j] = jagged[i][j]; + } + } + + return flat; + } + + private static (BiseEncodingMode, int)[] InitPackingModeCache() + { + (BiseEncodingMode, int)[] cache = new (BiseEncodingMode, int)[1 << Log2MaxRangeForBits]; + + // Precompute for all valid ranges [1, 255] + for (int range = 1; range < cache.Length; range++) + { + int index = -1; + for (int i = 0; i < MaxRanges.Length; i++) + { + if (MaxRanges[i] >= range) + { + index = i; + break; + } + } + + int maxValue = index < 0 + ? MaxRanges[^1] + 1 + : MaxRanges[index] + 1; + + // Check QuintEncoding (5), TritEncoding (3), BitEncoding (1) in descending order + BiseEncodingMode encodingMode = BiseEncodingMode.Unknown; + ReadOnlySpan modes = [BiseEncodingMode.QuintEncoding, BiseEncodingMode.TritEncoding, BiseEncodingMode.BitEncoding]; + foreach (BiseEncodingMode em in modes) + { + if (maxValue % (int)em == 0 && int.IsPow2(maxValue / (int)em)) + { + encodingMode = em; + break; + } + } + + if (encodingMode == BiseEncodingMode.Unknown) + { + throw new InvalidOperationException($"Invalid range for BISE encoding: {range}"); + } + + cache[range] = (encodingMode, int.Log2(maxValue / (int)encodingMode)); + } + + return cache; + } +} diff --git a/src/ImageSharp.Textures.Astc/BiseEncoding/BoundedIntegerSequenceDecoder.cs b/src/ImageSharp.Textures.Astc/BiseEncoding/BoundedIntegerSequenceDecoder.cs new file mode 100644 index 00000000..bf9ec0f9 --- /dev/null +++ b/src/ImageSharp.Textures.Astc/BiseEncoding/BoundedIntegerSequenceDecoder.cs @@ -0,0 +1,159 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Textures.Astc.IO; + +namespace SixLabors.ImageSharp.Textures.Astc.BiseEncoding; + +internal sealed class BoundedIntegerSequenceDecoder : BoundedIntegerSequenceCodec +{ + private static readonly BoundedIntegerSequenceDecoder?[] Cache = new BoundedIntegerSequenceDecoder?[256]; + + public BoundedIntegerSequenceDecoder(int range) + : base(range) + { + } + + public static BoundedIntegerSequenceDecoder GetCached(int range) + { + BoundedIntegerSequenceDecoder? decoder = Cache[range]; + if (decoder is null) + { + decoder = new BoundedIntegerSequenceDecoder(range); + Cache[range] = decoder; + } + + return decoder; + } + + /// + /// Decode a sequence of bounded integers into a caller-provided span. + /// + /// The number of values to decode. + /// The source of values to decode from. + /// The span to write decoded values into. + /// Thrown when the encoded block size is too large. + /// Thrown when there are not enough bits to decode. + public void Decode(int valuesCount, ref BitStream bitSource, Span result) + { + int totalBitCount = GetBitCount(this.Encoding, valuesCount, this.BitCount); + int bitsPerBlock = this.GetEncodedBlockSize(); + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(bitsPerBlock, 64); + + Span blockResult = stackalloc int[5]; + int resultIndex = 0; + int bitsRemaining = totalBitCount; + + while (bitsRemaining > 0) + { + int bitsToRead = Math.Min(bitsRemaining, bitsPerBlock); + if (!bitSource.TryGetBits(bitsToRead, out ulong blockBits)) + { + throw new InvalidOperationException("Not enough bits in BitStream to decode BISE block"); + } + + if (this.Encoding == BiseEncodingMode.BitEncoding) + { + if (resultIndex < valuesCount) + { + result[resultIndex++] = (int)blockBits; + } + } + else + { + int decoded = DecodeISEBlock(this.Encoding, blockBits, this.BitCount, blockResult); + for (int i = 0; i < decoded && resultIndex < valuesCount; ++i) + { + result[resultIndex++] = blockResult[i]; + } + } + + bitsRemaining -= bitsPerBlock; + } + + if (resultIndex < valuesCount) + { + throw new InvalidOperationException("Decoded fewer values than expected from BISE block"); + } + } + + /// + /// Decode a sequence of bounded integers. The number of bits read is dependent on the number + /// of bits required to encode based on the calculation provided + /// in Section C.2.22 of the ASTC specification. + /// + /// The number of values to decode. + /// The source of values to decode from. + /// The decoded values. The collection always contains exactly elements. + /// Thrown when the encoded block size is too large. + /// Thrown when there are not enough bits to decode. + public int[] Decode(int valuesCount, ref BitStream bitSource) + { + int[] result = new int[valuesCount]; + this.Decode(valuesCount, ref bitSource, result); + return result; + } + + /// + /// Decode a trit/quint block into a caller-provided span. + /// Returns the number of values written. + /// Uses direct bit extraction (no BitStream) and flat encoding tables. + /// + public static int DecodeISEBlock(BiseEncodingMode mode, ulong encodedBlock, int encodedBitCount, Span result) + { + ulong mantissaMask = (1UL << encodedBitCount) - 1; + + if (mode == BiseEncodingMode.TritEncoding) + { + // 5 values, interleaved bits = [2, 2, 1, 2, 1] = 8 bits total + int bitPosition = 0; + int mantissa0 = (int)((encodedBlock >> bitPosition) & mantissaMask); + bitPosition += encodedBitCount; + ulong encodedTrits = (encodedBlock >> bitPosition) & 0x3; + bitPosition += 2; + int mantissa1 = (int)((encodedBlock >> bitPosition) & mantissaMask); + bitPosition += encodedBitCount; + encodedTrits |= ((encodedBlock >> bitPosition) & 0x3) << 2; + bitPosition += 2; + int mantissa2 = (int)((encodedBlock >> bitPosition) & mantissaMask); + bitPosition += encodedBitCount; + encodedTrits |= ((encodedBlock >> bitPosition) & 0x1) << 4; + bitPosition += 1; + int mantissa3 = (int)((encodedBlock >> bitPosition) & mantissaMask); + bitPosition += encodedBitCount; + encodedTrits |= ((encodedBlock >> bitPosition) & 0x3) << 5; + bitPosition += 2; + int mantissa4 = (int)((encodedBlock >> bitPosition) & mantissaMask); + encodedTrits |= ((encodedBlock >> (bitPosition + encodedBitCount)) & 0x1) << 7; + + int tritTableBase = (int)encodedTrits * 5; + result[0] = (FlatTritEncodings[tritTableBase] << encodedBitCount) | mantissa0; + result[1] = (FlatTritEncodings[tritTableBase + 1] << encodedBitCount) | mantissa1; + result[2] = (FlatTritEncodings[tritTableBase + 2] << encodedBitCount) | mantissa2; + result[3] = (FlatTritEncodings[tritTableBase + 3] << encodedBitCount) | mantissa3; + result[4] = (FlatTritEncodings[tritTableBase + 4] << encodedBitCount) | mantissa4; + return 5; + } + else + { + // 3 values, interleaved bits = [3, 2, 2] = 7 bits total + int bitPosition = 0; + int mantissa0 = (int)((encodedBlock >> bitPosition) & mantissaMask); + bitPosition += encodedBitCount; + ulong encodedQuints = (encodedBlock >> bitPosition) & 0x7; + bitPosition += 3; + int mantissa1 = (int)((encodedBlock >> bitPosition) & mantissaMask); + bitPosition += encodedBitCount; + encodedQuints |= ((encodedBlock >> bitPosition) & 0x3) << 3; + bitPosition += 2; + int mantissa2 = (int)((encodedBlock >> bitPosition) & mantissaMask); + encodedQuints |= ((encodedBlock >> (bitPosition + encodedBitCount)) & 0x3) << 5; + + int quintTableBase = (int)encodedQuints * 3; + result[0] = (FlatQuintEncodings[quintTableBase] << encodedBitCount) | mantissa0; + result[1] = (FlatQuintEncodings[quintTableBase + 1] << encodedBitCount) | mantissa1; + result[2] = (FlatQuintEncodings[quintTableBase + 2] << encodedBitCount) | mantissa2; + return 3; + } + } +} diff --git a/src/ImageSharp.Textures.Astc/BiseEncoding/BoundedIntegerSequenceEncoder.cs b/src/ImageSharp.Textures.Astc/BiseEncoding/BoundedIntegerSequenceEncoder.cs new file mode 100644 index 00000000..341e2c29 --- /dev/null +++ b/src/ImageSharp.Textures.Astc/BiseEncoding/BoundedIntegerSequenceEncoder.cs @@ -0,0 +1,168 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Textures.Astc.IO; + +namespace SixLabors.ImageSharp.Textures.Astc.BiseEncoding; + +internal sealed class BoundedIntegerSequenceEncoder : BoundedIntegerSequenceCodec +{ + private readonly List values = []; + + public BoundedIntegerSequenceEncoder(int range) + : base(range) + { + } + + /// + /// Adds a value to the encoding sequence. + /// + public void AddValue(int val) => this.values.Add(val); + + /// + /// Encodes and writes the stored values encoding to the sink. Repeated calls will produce the same result. + /// + public void Encode(ref BitStream bitSink) + { + int totalBitCount = GetBitCount(this.Encoding, this.values.Count, this.BitCount); + + int index = 0; + int bitsWrittenCount = 0; + while (index < this.values.Count) + { + switch (this.Encoding) + { + case BiseEncodingMode.TritEncoding: + List trits = []; + for (int i = 0; i < 5; ++i) + { + if (index < this.values.Count) + { + trits.Add(this.values[index++]); + } + else + { + trits.Add(0); + } + } + + EncodeISEBlock(trits, this.BitCount, ref bitSink, ref bitsWrittenCount, totalBitCount); + break; + case BiseEncodingMode.QuintEncoding: + List quints = []; + for (int i = 0; i < 3; ++i) + { + int value = index < this.values.Count + ? this.values[index++] + : 0; + quints.Add(value); + } + + EncodeISEBlock(quints, this.BitCount, ref bitSink, ref bitsWrittenCount, totalBitCount); + break; + case BiseEncodingMode.BitEncoding: + bitSink.PutBits((uint)this.values[index++], this.GetEncodedBlockSize()); + break; + } + } + } + + /// + /// Clear the stored values. + /// + public void Reset() => this.values.Clear(); + + private static void EncodeISEBlock(List values, int bitsPerValue, ref BitStream bitSink, ref int bitsWritten, int totalBitCount) + where T : unmanaged + { + int valueCount = values.Count; + int valueRange = (valueCount == 3) ? 5 : 3; + int bitsPerBlock = (valueRange == 5) ? 7 : 8; + int[] interleavedBits = (valueRange == 5) + ? InterleavedQuintBits + : InterleavedTritBits; + + int[] nonBitComponents = new int[valueCount]; + int[] bitComponents = new int[valueCount]; + for (int i = 0; i < valueCount; ++i) + { + bitComponents[i] = values[i] & ((1 << bitsPerValue) - 1); + nonBitComponents[i] = values[i] >> bitsPerValue; + } + + // Determine how many interleaved bits for this block given the global + // totalBitCount and how many bits have already been written. + int tempBitsAdded = bitsWritten; + int encodedBitCount = 0; + for (int i = 0; i < valueCount; ++i) + { + tempBitsAdded += bitsPerValue; + if (tempBitsAdded >= totalBitCount) + { + break; + } + + encodedBitCount += interleavedBits[i]; + tempBitsAdded += interleavedBits[i]; + if (tempBitsAdded >= totalBitCount) + { + break; + } + } + + int nonBitEncoding = -1; + for (int j = (1 << encodedBitCount) - 1; j >= 0; --j) + { + bool matches = true; + for (int i = 0; i < valueCount; ++i) + { + if (valueRange == 5) + { + if (QuintEncodings[j][i] != nonBitComponents[i]) + { + matches = false; + break; + } + } + else + { + if (TritEncodings[j][i] != nonBitComponents[i]) + { + matches = false; + break; + } + } + } + + if (matches) + { + nonBitEncoding = j; + break; + } + } + + if (nonBitEncoding < 0) + { + throw new InvalidOperationException(); + } + + int nonBitEncodingCopy = nonBitEncoding; + for (int i = 0; i < valueCount; ++i) + { + if (bitsWritten + bitsPerValue <= totalBitCount) + { + bitSink.PutBits((uint)bitComponents[i], bitsPerValue); + bitsWritten += bitsPerValue; + } + + int interleavedBitCount = interleavedBits[i]; + int interleavedBitsValue = nonBitEncodingCopy & ((1 << interleavedBitCount) - 1); + if (bitsWritten + interleavedBitCount <= totalBitCount) + { + bitSink.PutBits((uint)interleavedBitsValue, interleavedBitCount); + bitsWritten += interleavedBitCount; + nonBitEncodingCopy >>= interleavedBitCount; + } + } + } +} diff --git a/src/ImageSharp.Textures.Astc/BiseEncoding/Quantize/BitQuantizationMap.cs b/src/ImageSharp.Textures.Astc/BiseEncoding/Quantize/BitQuantizationMap.cs new file mode 100644 index 00000000..a6b442af --- /dev/null +++ b/src/ImageSharp.Textures.Astc/BiseEncoding/Quantize/BitQuantizationMap.cs @@ -0,0 +1,65 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Textures.Astc.BiseEncoding.Quantize; + +internal sealed class BitQuantizationMap : QuantizationMap +{ + // TotalUnquantizedBits is 8 for endpoint values and 6 for weights + public BitQuantizationMap(int range, int totalUnquantizedBits) + { + // ensure range+1 is power of two + ArgumentOutOfRangeException.ThrowIfNotEqual(CountOnes(range + 1), 1); + + int bitCount = Log2Floor(range + 1); + + for (int bits = 0; bits <= range; bits++) + { + int unquantized = bits; + int unquantizedBitCount = bitCount; + while (unquantizedBitCount < totalUnquantizedBits) + { + int destinationShiftUp = Math.Min(bitCount, totalUnquantizedBits - unquantizedBitCount); + int sourceShiftDown = bitCount - destinationShiftUp; + unquantized <<= destinationShiftUp; + unquantized |= bits >> sourceShiftDown; + unquantizedBitCount += destinationShiftUp; + } + + if (unquantizedBitCount != totalUnquantizedBits) + { + throw new InvalidOperationException(); + } + + this.UnquantizationMapBuilder.Add(unquantized); + + if (bits > 0) + { + int previousUnquantized = this.UnquantizationMapBuilder[bits - 1]; + while (this.QuantizationMapBuilder.Count <= (previousUnquantized + unquantized) / 2) + { + this.QuantizationMapBuilder.Add(bits - 1); + } + } + + while (this.QuantizationMapBuilder.Count <= unquantized) + { + this.QuantizationMapBuilder.Add(bits); + } + } + + this.Freeze(); + } + + private static int CountOnes(int value) + { + int count = 0; + while (value != 0) + { + count += value & 1; + value >>= 1; + } + + return count; + } +} diff --git a/src/ImageSharp.Textures.Astc/BiseEncoding/Quantize/Quantization.cs b/src/ImageSharp.Textures.Astc/BiseEncoding/Quantize/Quantization.cs new file mode 100644 index 00000000..d97c2bea --- /dev/null +++ b/src/ImageSharp.Textures.Astc/BiseEncoding/Quantize/Quantization.cs @@ -0,0 +1,245 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Textures.Astc.BiseEncoding.Quantize; + +internal static class Quantization +{ + public const int EndpointRangeMinValue = 5; + public const int WeightRangeMaxValue = 31; + + private static readonly SortedDictionary EndpointMaps = InitEndpointMaps(); + private static readonly SortedDictionary WeightMaps = InitWeightMaps(); + + // Flat lookup tables indexed by range value for O(1) access. + // Each slot maps to the QuantizationMap for the greatest supported range <= that index. + private static readonly QuantizationMap?[] EndpointMapByRange = InitEndpointMapFlat(); + private static readonly QuantizationMap?[] WeightMapByRange = InitWeightMapFlat(); + + // Pre-computed flat tables for weight unquantization: entry[quantizedValue] = final unquantized weight. + // Includes the dq > 32 -> dq + 1 adjustment. Indexed by weight range. + // Valid ranges: 1, 2, 3, 4, 5, 7, 9, 11, 15, 19, 23, 31 + private static readonly int[]?[] UnquantizeWeightsFlat = InitializeUnquantizeWeightsFlat(); + + // Pre-computed flat tables for endpoint unquantization. + // Indexed by range value. Valid ranges: 5, 7, 9, 11, 15, 19, 23, 31, 39, 47, 63, 79, 95, 127, 159, 191, 255 + private static readonly int[]?[] UnquantizeEndpointsFlat = InitializeUnquantizeEndpointsFlat(); + + public static int QuantizeCEValueToRange(int value, int rangeMaxValue) + { + ArgumentOutOfRangeException.ThrowIfLessThan(rangeMaxValue, EndpointRangeMinValue); + ArgumentOutOfRangeException.ThrowIfGreaterThan(rangeMaxValue, byte.MaxValue); + ArgumentOutOfRangeException.ThrowIfLessThan(value, 0); + ArgumentOutOfRangeException.ThrowIfGreaterThan(value, byte.MaxValue); + + QuantizationMap? map = GetQuantMapForValueRange(rangeMaxValue); + return map != null ? map.Quantize(value) : 0; + } + + public static int UnquantizeCEValueFromRange(int value, int rangeMaxValue) + { + ArgumentOutOfRangeException.ThrowIfLessThan(rangeMaxValue, EndpointRangeMinValue); + ArgumentOutOfRangeException.ThrowIfGreaterThan(rangeMaxValue, byte.MaxValue); + ArgumentOutOfRangeException.ThrowIfLessThan(value, 0); + ArgumentOutOfRangeException.ThrowIfGreaterThan(value, rangeMaxValue); + + QuantizationMap? map = GetQuantMapForValueRange(rangeMaxValue); + return map != null ? map.Unquantize(value) : 0; + } + + public static int QuantizeWeightToRange(int weight, int rangeMaxValue) + { + ArgumentOutOfRangeException.ThrowIfLessThan(rangeMaxValue, 1); + ArgumentOutOfRangeException.ThrowIfGreaterThan(rangeMaxValue, WeightRangeMaxValue); + ArgumentOutOfRangeException.ThrowIfLessThan(weight, 0); + ArgumentOutOfRangeException.ThrowIfGreaterThan(weight, 64); + + if (weight > 33) + { + weight -= 1; + } + + QuantizationMap? map = GetQuantMapForWeightRange(rangeMaxValue); + return map != null ? map.Quantize(weight) : 0; + } + + public static int UnquantizeWeightFromRange(int weight, int rangeMaxValue) + { + ArgumentOutOfRangeException.ThrowIfLessThan(rangeMaxValue, 1); + ArgumentOutOfRangeException.ThrowIfGreaterThan(rangeMaxValue, WeightRangeMaxValue); + ArgumentOutOfRangeException.ThrowIfLessThan(weight, 0); + ArgumentOutOfRangeException.ThrowIfGreaterThan(weight, rangeMaxValue); + + QuantizationMap? map = GetQuantMapForWeightRange(rangeMaxValue); + int dequantized = map != null ? map.Unquantize(weight) : 0; + if (dequantized > 32) + { + dequantized += 1; + } + + return dequantized; + } + + /// + /// Batch unquantize: uses pre-computed flat table for O(1) lookup per value. + /// No per-call validation, no conditional branch per weight. + /// + internal static void UnquantizeWeightsBatch(Span weights, int count, int range) + { + int[]? table = UnquantizeWeightsFlat[range]; + if (table == null) + { + return; + } + + for (int i = 0; i < count; i++) + { + weights[i] = table[weights[i]]; + } + } + + /// + /// Batch unquantize color endpoint values: uses pre-computed flat table. + /// No per-call validation, single array lookup per value. + /// + internal static void UnquantizeCEValuesBatch(Span values, int count, int rangeMaxValue) + { + int[]? table = UnquantizeEndpointsFlat[rangeMaxValue]; + if (table == null) + { + return; + } + + for (int i = 0; i < count; i++) + { + values[i] = table[values[i]]; + } + } + + private static SortedDictionary InitEndpointMaps() + { + SortedDictionary d = new() + { + { 5, new TritQuantizationMap(5, TritQuantizationMap.GetUnquantizedValue) }, + { 7, new BitQuantizationMap(7, 8) }, + { 9, new QuintQuantizationMap(9, QuintQuantizationMap.GetUnquantizedValue) }, + { 11, new TritQuantizationMap(11, TritQuantizationMap.GetUnquantizedValue) }, + { 15, new BitQuantizationMap(15, 8) }, + { 19, new QuintQuantizationMap(19, QuintQuantizationMap.GetUnquantizedValue) }, + { 23, new TritQuantizationMap(23, TritQuantizationMap.GetUnquantizedValue) }, + { 31, new BitQuantizationMap(31, 8) }, + { 39, new QuintQuantizationMap(39, QuintQuantizationMap.GetUnquantizedValue) }, + { 47, new TritQuantizationMap(47, TritQuantizationMap.GetUnquantizedValue) }, + { 63, new BitQuantizationMap(63, 8) }, + { 79, new QuintQuantizationMap(79, QuintQuantizationMap.GetUnquantizedValue) }, + { 95, new TritQuantizationMap(95, TritQuantizationMap.GetUnquantizedValue) }, + { 127, new BitQuantizationMap(127, 8) }, + { 159, new QuintQuantizationMap(159, QuintQuantizationMap.GetUnquantizedValue) }, + { 191, new TritQuantizationMap(191, TritQuantizationMap.GetUnquantizedValue) }, + { 255, new BitQuantizationMap(255, 8) } + }; + return d; + } + + private static SortedDictionary InitWeightMaps() + { + SortedDictionary d = new() + { + { 1, new BitQuantizationMap(1, 6) }, + { 2, new TritQuantizationMap(2, TritQuantizationMap.GetUnquantizedWeight) }, + { 3, new BitQuantizationMap(3, 6) }, + { 4, new QuintQuantizationMap(4, QuintQuantizationMap.GetUnquantizedWeight) }, + { 5, new TritQuantizationMap(5, TritQuantizationMap.GetUnquantizedWeight) }, + { 7, new BitQuantizationMap(7, 6) }, + { 9, new QuintQuantizationMap(9, QuintQuantizationMap.GetUnquantizedWeight) }, + { 11, new TritQuantizationMap(11, TritQuantizationMap.GetUnquantizedWeight) }, + { 15, new BitQuantizationMap(15, 6) }, + { 19, new QuintQuantizationMap(19, QuintQuantizationMap.GetUnquantizedWeight) }, + { 23, new TritQuantizationMap(23, TritQuantizationMap.GetUnquantizedWeight) }, + { 31, new BitQuantizationMap(31, 6) } + }; + return d; + } + + private static QuantizationMap?[] BuildFlatLookup(SortedDictionary maps, int size) + { + QuantizationMap?[] flat = new QuantizationMap?[size]; + QuantizationMap? current = null; + for (int i = 0; i < size; i++) + { + if (maps.TryGetValue(i, out QuantizationMap? map)) + { + current = map; + } + + flat[i] = current; + } + + return flat; + } + + private static QuantizationMap?[] InitEndpointMapFlat() + => BuildFlatLookup(InitEndpointMaps(), 256); + + private static QuantizationMap?[] InitWeightMapFlat() + => BuildFlatLookup(InitWeightMaps(), 32); + + private static QuantizationMap? GetQuantMapForValueRange(int r) + { + if ((uint)r >= (uint)EndpointMapByRange.Length) + { + return null; + } + + return EndpointMapByRange[r]; + } + + private static QuantizationMap? GetQuantMapForWeightRange(int r) + { + if ((uint)r >= (uint)WeightMapByRange.Length) + { + return null; + } + + return WeightMapByRange[r]; + } + + private static int[]?[] InitializeUnquantizeWeightsFlat() + { + int[]?[] tables = new int[]?[WeightRangeMaxValue + 1]; + foreach (KeyValuePair kvp in WeightMaps) + { + int range = kvp.Key; + QuantizationMap map = kvp.Value; + int[] table = new int[range + 1]; + for (int i = 0; i <= range; i++) + { + int dequantized = map.Unquantize(i); + table[i] = dequantized > 32 ? dequantized + 1 : dequantized; + } + + tables[range] = table; + } + + return tables; + } + + private static int[]?[] InitializeUnquantizeEndpointsFlat() + { + int[]?[] tables = new int[]?[256]; + foreach (KeyValuePair kvp in EndpointMaps) + { + int range = kvp.Key; + QuantizationMap map = kvp.Value; + int[] table = new int[range + 1]; + for (int i = 0; i <= range; i++) + { + table[i] = map.Unquantize(i); + } + + tables[range] = table; + } + + return tables; + } +} diff --git a/src/ImageSharp.Textures.Astc/BiseEncoding/Quantize/QuantizationMap.cs b/src/ImageSharp.Textures.Astc/BiseEncoding/Quantize/QuantizationMap.cs new file mode 100644 index 00000000..ffeb8146 --- /dev/null +++ b/src/ImageSharp.Textures.Astc/BiseEncoding/Quantize/QuantizationMap.cs @@ -0,0 +1,74 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Textures.Astc.BiseEncoding.Quantize; + +internal class QuantizationMap +{ + // Flat arrays for O(1) lookup on the hot path (set by Freeze) + private int[] quantizationMap = []; + private int[] unquantizationMap = []; + + protected List QuantizationMapBuilder { get; set; } = []; + + protected List UnquantizationMapBuilder { get; set; } = []; + + public int Quantize(int x) + => (uint)x < (uint)this.quantizationMap.Length + ? this.quantizationMap[x] + : 0; + + public int Unquantize(int x) + => (uint)x < (uint)this.unquantizationMap.Length + ? this.unquantizationMap[x] + : 0; + + internal static int Log2Floor(int value) + { + int result = 0; + while ((1 << (result + 1)) <= value) + { + result++; + } + + return result; + } + + /// + /// Converts builder lists to flat arrays. Called after construction is complete. + /// + protected void Freeze() + { + this.unquantizationMap = [.. this.UnquantizationMapBuilder]; + this.quantizationMap = [.. this.QuantizationMapBuilder]; + this.UnquantizationMapBuilder = []; + this.QuantizationMapBuilder = []; + } + + protected void GenerateQuantizationMap() + { + if (this.UnquantizationMapBuilder.Count <= 1) + { + return; + } + + this.QuantizationMapBuilder.Clear(); + for (int i = 0; i < 256; ++i) + { + int bestIndex = 0; + int bestScore = int.MaxValue; + for (int index = 0; index < this.UnquantizationMapBuilder.Count; ++index) + { + int diff = i - this.UnquantizationMapBuilder[index]; + int score = diff * diff; + if (score < bestScore) + { + bestIndex = index; + bestScore = score; + } + } + + this.QuantizationMapBuilder.Add(bestIndex); + } + } +} diff --git a/src/ImageSharp.Textures.Astc/BiseEncoding/Quantize/QuintQuantizationMap.cs b/src/ImageSharp.Textures.Astc/BiseEncoding/Quantize/QuintQuantizationMap.cs new file mode 100644 index 00000000..3c77263f --- /dev/null +++ b/src/ImageSharp.Textures.Astc/BiseEncoding/Quantize/QuintQuantizationMap.cs @@ -0,0 +1,65 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Textures.Astc.BiseEncoding.Quantize; + +internal sealed class QuintQuantizationMap : QuantizationMap +{ + public QuintQuantizationMap(int range, Func unquantFunc) + { + ArgumentOutOfRangeException.ThrowIfNotEqual((range + 1) % 5, 0); + + int bitsPowerOfTwo = (range + 1) / 5; + int bitCount = bitsPowerOfTwo == 0 ? 0 : Log2Floor(bitsPowerOfTwo); + + for (int quint = 0; quint < 5; ++quint) + { + for (int bits = 0; bits < (1 << bitCount); ++bits) + { + this.UnquantizationMapBuilder.Add(unquantFunc(quint, bits, range)); + } + } + + this.GenerateQuantizationMap(); + this.Freeze(); + } + + internal static int GetUnquantizedValue(int quint, int bits, int range) + { + int a = (bits & 1) != 0 ? 0x1FF : 0; + (int b, int c) = range switch + { + 9 => (0, 113), + 19 => ((bits >> 1) & 0x1) is var x ? ((x << 2) | (x << 3) | (x << 8), 54) : default, + 39 => ((bits >> 1) & 0x3) is var x ? ((x >> 1) | (x << 1) | (x << 7), 26) : default, + 79 => ((bits >> 1) & 0x7) is var x ? ((x >> 1) | (x << 6), 13) : default, + 159 => ((bits >> 1) & 0xF) is var x ? ((x >> 3) | (x << 5), 6) : default, + _ => throw new ArgumentException("Illegal quint encoding") + }; + int t = (quint * c) + b; + t ^= a; + t = (a & 0x80) | (t >> 2); + return t; + } + + internal static int GetUnquantizedWeight(int quint, int bits, int range) + { + if (range == 4) + { + int[] weights = [0, 16, 32, 47, 63]; + return weights[quint]; + } + + int a = (bits & 1) != 0 ? 0x7F : 0; + (int b, int c) = range switch + { + 9 => (0, 28), + 19 => ((bits >> 1) & 0x1) is var x ? ((x << 1) | (x << 6), 13) : default, + _ => throw new ArgumentException("Illegal quint encoding") + }; + int t = (quint * c) + b; + t ^= a; + t = (a & 0x20) | (t >> 2); + return t; + } +} diff --git a/src/ImageSharp.Textures.Astc/BiseEncoding/Quantize/TritQuantizationMap.cs b/src/ImageSharp.Textures.Astc/BiseEncoding/Quantize/TritQuantizationMap.cs new file mode 100644 index 00000000..9daa76d7 --- /dev/null +++ b/src/ImageSharp.Textures.Astc/BiseEncoding/Quantize/TritQuantizationMap.cs @@ -0,0 +1,74 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Textures.Astc.BiseEncoding.Quantize; + +internal sealed class TritQuantizationMap : QuantizationMap +{ + public TritQuantizationMap(int range, Func unquantFunc) + { + ArgumentOutOfRangeException.ThrowIfNotEqual((range + 1) % 3, 0); + + int bitsPowerOfTwo = (range + 1) / 3; + int bitCount = bitsPowerOfTwo == 0 ? 0 : Log2Floor(bitsPowerOfTwo); + + for (int trit = 0; trit < 3; ++trit) + { + for (int bits = 0; bits < (1 << bitCount); ++bits) + { + this.UnquantizationMapBuilder.Add(unquantFunc(trit, bits, range)); + } + } + + this.GenerateQuantizationMap(); + this.Freeze(); + } + + internal static int GetUnquantizedValue(int trit, int bits, int range) + { + int a = (bits & 1) != 0 ? 0x1FF : 0; + (int b, int c) = range switch + { + 5 => (0, 204), + 11 => ((bits >> 1) & 0x1) is var x ? ((x << 1) | (x << 2) | (x << 4) | (x << 8), 93) : default, + 23 => ((bits >> 1) & 0x3) is var x ? (x | (x << 2) | (x << 7), 44) : default, + 47 => ((bits >> 1) & 0x7) is var x ? (x | (x << 6), 22) : default, + 95 => ((bits >> 1) & 0xF) is var x ? ((x >> 2) | (x << 5), 11) : default, + 191 => ((bits >> 1) & 0x1F) is var x ? ((x >> 4) | (x << 4), 5) : default, + _ => throw new ArgumentException("Illegal trit encoding") + }; + int t = (trit * c) + b; + t ^= a; + t = (a & 0x80) | (t >> 2); + return t; + } + + internal static int GetUnquantizedWeight(int trit, int bits, int range) + { + if (range == 2) + { + return trit switch + { + 0 => 0, + 1 => 32, + _ => 63 + }; + } + + int a = (bits & 1) != 0 ? 0x7F : 0; + (int b, int c) = range switch + { + 5 => (0, 50), + 11 => ((bits >> 1) & 1) is var x + ? (x | (x << 2) | (x << 6), 23) + : default, + 23 => ((bits >> 1) & 0x3) is var x + ? (x | (x << 5), 11) + : default, + _ => throw new ArgumentException("Illegal trit encoding") + }; + int t = (trit * c) + b; + t ^= a; + return (a & 0x20) | (t >> 2); + } +} diff --git a/src/ImageSharp.Textures.Astc/BlockDecoder/FusedBlockDecoder.cs b/src/ImageSharp.Textures.Astc/BlockDecoder/FusedBlockDecoder.cs new file mode 100644 index 00000000..8ca24c51 --- /dev/null +++ b/src/ImageSharp.Textures.Astc/BlockDecoder/FusedBlockDecoder.cs @@ -0,0 +1,183 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Textures.Astc.BiseEncoding; +using SixLabors.ImageSharp.Textures.Astc.BiseEncoding.Quantize; +using SixLabors.ImageSharp.Textures.Astc.ColorEncoding; +using SixLabors.ImageSharp.Textures.Astc.Core; +using SixLabors.ImageSharp.Textures.Astc.IO; +using SixLabors.ImageSharp.Textures.Astc.TexelBlock; + +namespace SixLabors.ImageSharp.Textures.Astc.BlockDecoder; + +/// +/// Shared decode core for the fused (zero-allocation) ASTC block decode pipeline. +/// Contains BISE extraction and weight infill used by both LDR and HDR decoders. +/// +internal static class FusedBlockDecoder +{ + /// + /// Shared decode core: BISE decode, unquantize, and infill. + /// Populates and returns the decoded endpoint pair. + /// + [MethodImpl(MethodImplOptions.AggressiveOptimization)] + internal static ColorEndpointPair DecodeFusedCore( + UInt128 bits, in BlockInfo info, Footprint footprint, Span texelWeights) + { + // 1. BISE decode color endpoint values + int colorCount = info.EndpointMode0.GetColorValuesCount(); + Span colors = stackalloc int[colorCount]; + DecodeBiseValues(bits, info.ColorStartBit, info.ColorBitCount, info.ColorValuesRange, colorCount, colors); + + // 2. Batch unquantize color values, then decode endpoint pair + Quantization.UnquantizeCEValuesBatch(colors, colorCount, info.ColorValuesRange); + ColorEndpointPair endpointPair = EndpointCodec.DecodeColorsForModePolymorphicUnquantized(colors, info.EndpointMode0); + + // 3. BISE decode weights + int gridSize = info.GridWidth * info.GridHeight; + Span gridWeights = stackalloc int[gridSize]; + DecodeBiseWeights(bits, info.WeightBitCount, info.WeightRange, gridSize, gridWeights); + + // 4. Batch unquantize weights + Quantization.UnquantizeWeightsBatch(gridWeights, gridSize, info.WeightRange); + + // 5. Infill weights from grid to texels (or pass through if identity mapping) + if (info.GridWidth == footprint.Width && info.GridHeight == footprint.Height) + { + gridWeights[..footprint.PixelCount].CopyTo(texelWeights); + } + else + { + DecimationInfo decimationInfo = DecimationTable.Get(footprint, info.GridWidth, info.GridHeight); + DecimationTable.InfillWeights(gridWeights, decimationInfo, texelWeights); + } + + return endpointPair; + } + + /// + /// Decodes BISE-encoded values from the specified bit region of the block. + /// For bit-only encoding with small total bit count, extracts directly from ulong + /// without creating a BitStream (avoids per-value ShiftBuffer overhead). + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void DecodeBiseValues(UInt128 bits, int startBit, int bitCount, int range, int valuesCount, Span result) + { + (BiseEncodingMode encMode, int bitsPerValue) = BoundedIntegerSequenceCodec.GetPackingModeBitCount(range); + + if (encMode == BiseEncodingMode.BitEncoding) + { + // Fast path: extract N-bit values directly via shifts + int totalBits = valuesCount * bitsPerValue; + ulong mask = (1UL << bitsPerValue) - 1; + + if (startBit + totalBits <= 64) + { + // All color data fits in the low 64 bits + ulong data = bits.Low() >> startBit; + for (int i = 0; i < valuesCount; i++) + { + result[i] = (int)(data & mask); + data >>= bitsPerValue; + } + } + else + { + // Spans both halves — use UInt128 shift then extract from low + UInt128 shifted = (bits >> startBit) & UInt128Extensions.OnesMask(totalBits); + ulong lowBits = shifted.Low(); + ulong highBits = shifted.High(); + int bitPos = 0; + for (int i = 0; i < valuesCount; i++) + { + if (bitPos < 64) + { + ulong val = (lowBits >> bitPos) & mask; + if (bitPos + bitsPerValue > 64) + { + val |= (highBits << (64 - bitPos)) & mask; + } + + result[i] = (int)val; + } + else + { + result[i] = (int)((highBits >> (bitPos - 64)) & mask); + } + + bitPos += bitsPerValue; + } + } + + return; + } + + // Trit/quint encoding: fall back to full BISE decoder + UInt128 colorBitMask = UInt128Extensions.OnesMask(bitCount); + UInt128 colorBits = (bits >> startBit) & colorBitMask; + BitStream colorBitStream = new(colorBits, 128); + BoundedIntegerSequenceDecoder decoder = BoundedIntegerSequenceDecoder.GetCached(range); + decoder.Decode(valuesCount, ref colorBitStream, result); + } + + /// + /// Decodes BISE-encoded weight values from the reversed high-end of the block. + /// For bit-only encoding, extracts directly from the reversed bits without BitStream. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void DecodeBiseWeights(UInt128 bits, int weightBitCount, int weightRange, int count, Span result) + { + (BiseEncodingMode encMode, int bitsPerValue) = BoundedIntegerSequenceCodec.GetPackingModeBitCount(weightRange); + UInt128 weightBits = UInt128Extensions.ReverseBits(bits) & UInt128Extensions.OnesMask(weightBitCount); + + if (encMode == BiseEncodingMode.BitEncoding) + { + // Fast path: extract N-bit values directly via shifts + int totalBits = count * bitsPerValue; + ulong mask = (1UL << bitsPerValue) - 1; + + if (totalBits <= 64) + { + ulong data = weightBits.Low(); + for (int i = 0; i < count; i++) + { + result[i] = (int)(data & mask); + data >>= bitsPerValue; + } + } + else + { + ulong lowBits = weightBits.Low(); + ulong highBits = weightBits.High(); + int bitPos = 0; + for (int i = 0; i < count; i++) + { + if (bitPos < 64) + { + ulong val = (lowBits >> bitPos) & mask; + if (bitPos + bitsPerValue > 64) + { + val |= (highBits << (64 - bitPos)) & mask; + } + + result[i] = (int)val; + } + else + { + result[i] = (int)((highBits >> (bitPos - 64)) & mask); + } + + bitPos += bitsPerValue; + } + } + + return; + } + + // Trit/quint encoding: fall back to full BISE decoder + BitStream weightBitStream = new(weightBits, 128); + BoundedIntegerSequenceDecoder decoder = BoundedIntegerSequenceDecoder.GetCached(weightRange); + decoder.Decode(count, ref weightBitStream, result); + } +} diff --git a/src/ImageSharp.Textures.Astc/BlockDecoder/FusedHdrBlockDecoder.cs b/src/ImageSharp.Textures.Astc/BlockDecoder/FusedHdrBlockDecoder.cs new file mode 100644 index 00000000..d88b2eee --- /dev/null +++ b/src/ImageSharp.Textures.Astc/BlockDecoder/FusedHdrBlockDecoder.cs @@ -0,0 +1,223 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Textures.Astc.ColorEncoding; +using SixLabors.ImageSharp.Textures.Astc.Core; +using SixLabors.ImageSharp.Textures.Astc.TexelBlock; + +namespace SixLabors.ImageSharp.Textures.Astc.BlockDecoder; + +/// +/// HDR pixel writers and entry points for the fused decode pipeline. +/// All methods handle single-partition, non-dual-plane blocks. +/// +internal static class FusedHdrBlockDecoder +{ + /// + /// Fused HDR decode to contiguous float buffer. + /// Handles single-partition, non-dual-plane blocks with both LDR and HDR endpoints. + /// + [MethodImpl(MethodImplOptions.AggressiveOptimization)] + internal static void DecompressBlockFusedHdr(UInt128 bits, in BlockInfo info, Footprint footprint, Span buffer) + { + Span texelWeights = stackalloc int[footprint.PixelCount]; + ColorEndpointPair endpointPair = FusedBlockDecoder.DecodeFusedCore(bits, in info, footprint, texelWeights); + WriteHdrOutputPixels(buffer, footprint.PixelCount, in endpointPair, texelWeights); + } + + /// + /// Fused HDR decode writing directly to image buffer at strided positions. + /// Handles single-partition, non-dual-plane blocks with both LDR and HDR endpoints. + /// + [MethodImpl(MethodImplOptions.AggressiveOptimization)] + internal static void DecompressBlockFusedHdrToImage( + UInt128 bits, + in BlockInfo info, + Footprint footprint, + int dstBaseX, + int dstBaseY, + int imageWidth, + Span imageBuffer) + { + Span texelWeights = stackalloc int[footprint.PixelCount]; + ColorEndpointPair endpointPair = FusedBlockDecoder.DecodeFusedCore(bits, in info, footprint, texelWeights); + WriteHdrOutputPixelsToImage(imageBuffer, footprint, dstBaseX, dstBaseY, imageWidth, in endpointPair, texelWeights); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void WriteHdrOutputPixels( + Span buffer, int pixelCount, in ColorEndpointPair endpointPair, Span texelWeights) + { + if (endpointPair.IsHdr) + { + WriteHdrPixels(buffer, pixelCount, in endpointPair, texelWeights); + } + else + { + WriteLdrAsHdrPixels(buffer, pixelCount, in endpointPair, texelWeights); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void WriteHdrOutputPixelsToImage( + Span imageBuffer, + Footprint footprint, + int dstBaseX, + int dstBaseY, + int imageWidth, + in ColorEndpointPair endpointPair, + Span texelWeights) + { + if (endpointPair.IsHdr) + { + WriteHdrPixelsToImage(imageBuffer, footprint, dstBaseX, dstBaseY, imageWidth, in endpointPair, texelWeights); + } + else + { + WriteLdrAsHdrPixelsToImage(imageBuffer, footprint, dstBaseX, dstBaseY, imageWidth, in endpointPair, texelWeights); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void WriteLdrAsHdrPixels(Span buffer, int pixelCount, in ColorEndpointPair endpointPair, Span texelWeights) + { + int lowR = endpointPair.LdrLow.R, lowG = endpointPair.LdrLow.G, lowB = endpointPair.LdrLow.B, lowA = endpointPair.LdrLow.A; + int highR = endpointPair.LdrHigh.R, highG = endpointPair.LdrHigh.G, highB = endpointPair.LdrHigh.B, highA = endpointPair.LdrHigh.A; + + for (int i = 0; i < pixelCount; i++) + { + int weight = texelWeights[i]; + int offset = i * 4; + buffer[offset + 0] = InterpolateLdrAsFloat(lowR, highR, weight); + buffer[offset + 1] = InterpolateLdrAsFloat(lowG, highG, weight); + buffer[offset + 2] = InterpolateLdrAsFloat(lowB, highB, weight); + buffer[offset + 3] = InterpolateLdrAsFloat(lowA, highA, weight); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void WriteLdrAsHdrPixelsToImage( + Span imageBuffer, + Footprint footprint, + int dstBaseX, + int dstBaseY, + int imageWidth, + in ColorEndpointPair endpointPair, + Span texelWeights) + { + int lowR = endpointPair.LdrLow.R, lowG = endpointPair.LdrLow.G, lowB = endpointPair.LdrLow.B, lowA = endpointPair.LdrLow.A; + int highR = endpointPair.LdrHigh.R, highG = endpointPair.LdrHigh.G, highB = endpointPair.LdrHigh.B, highA = endpointPair.LdrHigh.A; + + const int channelsPerPixel = 4; + int footprintWidth = footprint.Width; + int footprintHeight = footprint.Height; + int rowStride = imageWidth * channelsPerPixel; + + for (int pixelY = 0; pixelY < footprintHeight; pixelY++) + { + int dstRowOffset = ((dstBaseY + pixelY) * rowStride) + (dstBaseX * channelsPerPixel); + int srcRowBase = pixelY * footprintWidth; + + for (int pixelX = 0; pixelX < footprintWidth; pixelX++) + { + int weight = texelWeights[srcRowBase + pixelX]; + int dstOffset = dstRowOffset + (pixelX * channelsPerPixel); + imageBuffer[dstOffset + 0] = InterpolateLdrAsFloat(lowR, highR, weight); + imageBuffer[dstOffset + 1] = InterpolateLdrAsFloat(lowG, highG, weight); + imageBuffer[dstOffset + 2] = InterpolateLdrAsFloat(lowB, highB, weight); + imageBuffer[dstOffset + 3] = InterpolateLdrAsFloat(lowA, highA, weight); + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void WriteHdrPixels(Span buffer, int pixelCount, in ColorEndpointPair endpointPair, Span texelWeights) + { + bool alphaIsLdr = endpointPair.AlphaIsLdr; + int lowR = endpointPair.HdrLow.R, lowG = endpointPair.HdrLow.G, lowB = endpointPair.HdrLow.B, lowA = endpointPair.HdrLow.A; + int highR = endpointPair.HdrHigh.R, highG = endpointPair.HdrHigh.G, highB = endpointPair.HdrHigh.B, highA = endpointPair.HdrHigh.A; + + for (int i = 0; i < pixelCount; i++) + { + int weight = texelWeights[i]; + int offset = i * 4; + buffer[offset + 0] = InterpolateHdrAsFloat(lowR, highR, weight); + buffer[offset + 1] = InterpolateHdrAsFloat(lowG, highG, weight); + buffer[offset + 2] = InterpolateHdrAsFloat(lowB, highB, weight); + + if (alphaIsLdr) + { + int interpolated = ((lowA * (64 - weight)) + (highA * weight) + 32) / 64; + buffer[offset + 3] = (ushort)Math.Clamp(interpolated, 0, 0xFFFF) / 65535.0f; + } + else + { + buffer[offset + 3] = InterpolateHdrAsFloat(lowA, highA, weight); + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void WriteHdrPixelsToImage( + Span imageBuffer, + Footprint footprint, + int dstBaseX, + int dstBaseY, + int imageWidth, + in ColorEndpointPair endpointPair, + Span texelWeights) + { + bool alphaIsLdr = endpointPair.AlphaIsLdr; + int lowR = endpointPair.HdrLow.R, lowG = endpointPair.HdrLow.G, lowB = endpointPair.HdrLow.B, lowA = endpointPair.HdrLow.A; + int highR = endpointPair.HdrHigh.R, highG = endpointPair.HdrHigh.G, highB = endpointPair.HdrHigh.B, highA = endpointPair.HdrHigh.A; + + const int channelsPerPixel = 4; + int footprintWidth = footprint.Width; + int footprintHeight = footprint.Height; + int rowStride = imageWidth * channelsPerPixel; + + for (int pixelY = 0; pixelY < footprintHeight; pixelY++) + { + int dstRowOffset = ((dstBaseY + pixelY) * rowStride) + (dstBaseX * channelsPerPixel); + int srcRowBase = pixelY * footprintWidth; + + for (int pixelX = 0; pixelX < footprintWidth; pixelX++) + { + int weight = texelWeights[srcRowBase + pixelX]; + int dstOffset = dstRowOffset + (pixelX * channelsPerPixel); + imageBuffer[dstOffset + 0] = InterpolateHdrAsFloat(lowR, highR, weight); + imageBuffer[dstOffset + 1] = InterpolateHdrAsFloat(lowG, highG, weight); + imageBuffer[dstOffset + 2] = InterpolateHdrAsFloat(lowB, highB, weight); + + if (alphaIsLdr) + { + int interpolated = ((lowA * (64 - weight)) + (highA * weight) + 32) / 64; + imageBuffer[dstOffset + 3] = (ushort)Math.Clamp(interpolated, 0, 0xFFFF) / 65535.0f; + } + else + { + imageBuffer[dstOffset + 3] = InterpolateHdrAsFloat(lowA, highA, weight); + } + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float InterpolateLdrAsFloat(int p0, int p1, int weight) + { + int c0 = (p0 << 8) | p0; + int c1 = (p1 << 8) | p1; + int interpolated = ((c0 * (64 - weight)) + (c1 * weight) + 32) / 64; + return Math.Clamp(interpolated, 0, 0xFFFF) / 65535.0f; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float InterpolateHdrAsFloat(int p0, int p1, int weight) + { + int interpolated = ((p0 * (64 - weight)) + (p1 * weight) + 32) / 64; + ushort clamped = (ushort)Math.Clamp(interpolated, 0, 0xFFFF); + ushort halfFloatBits = LogicalBlock.LnsToSf16(clamped); + return (float)BitConverter.UInt16BitsToHalf(halfFloatBits); + } +} diff --git a/src/ImageSharp.Textures.Astc/BlockDecoder/FusedLdrBlockDecoder.cs b/src/ImageSharp.Textures.Astc/BlockDecoder/FusedLdrBlockDecoder.cs new file mode 100644 index 00000000..5391c4b3 --- /dev/null +++ b/src/ImageSharp.Textures.Astc/BlockDecoder/FusedLdrBlockDecoder.cs @@ -0,0 +1,172 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; +using SixLabors.ImageSharp.Textures.Astc.ColorEncoding; +using SixLabors.ImageSharp.Textures.Astc.Core; +using SixLabors.ImageSharp.Textures.Astc.TexelBlock; + +namespace SixLabors.ImageSharp.Textures.Astc.BlockDecoder; + +/// +/// LDR pixel writers and entry points for the fused decode pipeline. +/// All methods handle single-partition, non-dual-plane blocks. +/// +internal static class FusedLdrBlockDecoder +{ + private const int BytesPerPixelUnorm8 = 4; + + /// + /// Fused LDR decode to contiguous buffer. + /// Only handles single-partition, non-dual-plane, LDR blocks. + /// + [MethodImpl(MethodImplOptions.AggressiveOptimization)] + internal static void DecompressBlockFusedLdr(UInt128 bits, in BlockInfo info, Footprint footprint, Span buffer) + { + Span texelWeights = stackalloc int[footprint.PixelCount]; + ColorEndpointPair endpointPair = FusedBlockDecoder.DecodeFusedCore(bits, in info, footprint, texelWeights); + WriteLdrPixels(buffer, footprint.PixelCount, in endpointPair, texelWeights); + } + + /// + /// Fused LDR decode writing directly to image buffer at strided positions. + /// Only handles single-partition, non-dual-plane, LDR blocks. + /// + [MethodImpl(MethodImplOptions.AggressiveOptimization)] + internal static void DecompressBlockFusedLdrToImage( + UInt128 bits, + in BlockInfo info, + Footprint footprint, + int dstBaseX, + int dstBaseY, + int imageWidth, + Span imageBuffer) + { + Span texelWeights = stackalloc int[footprint.PixelCount]; + ColorEndpointPair endpointPair = FusedBlockDecoder.DecodeFusedCore(bits, in info, footprint, texelWeights); + WriteLdrPixelsToImage(imageBuffer, footprint, dstBaseX, dstBaseY, imageWidth, in endpointPair, texelWeights); + } + + /// + /// Writes all pixels for a single-partition LDR block using SIMD where possible. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void WriteLdrPixels(Span buffer, int pixelCount, in ColorEndpointPair endpointPair, Span texelWeights) + { + int lowR = endpointPair.LdrLow.R, lowG = endpointPair.LdrLow.G, lowB = endpointPair.LdrLow.B, lowA = endpointPair.LdrLow.A; + int highR = endpointPair.LdrHigh.R, highG = endpointPair.LdrHigh.G, highB = endpointPair.LdrHigh.B, highA = endpointPair.LdrHigh.A; + + int i = 0; + if (Vector128.IsHardwareAccelerated) + { + int limit = pixelCount - 3; + for (; i < limit; i += 4) + { + Vector128 weights = Vector128.Create( + texelWeights[i], + texelWeights[i + 1], + texelWeights[i + 2], + texelWeights[i + 3]); + SimdHelpers.Write4PixelLdr( + buffer, + i * 4, + lowR, + lowG, + lowB, + lowA, + highR, + highG, + highB, + highA, + weights); + } + } + + for (; i < pixelCount; i++) + { + SimdHelpers.WriteSinglePixelLdr( + buffer, + i * 4, + lowR, + lowG, + lowB, + lowA, + highR, + highG, + highB, + highA, + texelWeights[i]); + } + } + + /// + /// Writes LDR pixels directly to image buffer at strided positions. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void WriteLdrPixelsToImage( + Span imageBuffer, + Footprint footprint, + int dstBaseX, + int dstBaseY, + int imageWidth, + in ColorEndpointPair endpointPair, + Span texelWeights) + { + int lowR = endpointPair.LdrLow.R, lowG = endpointPair.LdrLow.G, lowB = endpointPair.LdrLow.B, lowA = endpointPair.LdrLow.A; + int highR = endpointPair.LdrHigh.R, highG = endpointPair.LdrHigh.G, highB = endpointPair.LdrHigh.B, highA = endpointPair.LdrHigh.A; + + int footprintWidth = footprint.Width; + int footprintHeight = footprint.Height; + int rowStride = imageWidth * BytesPerPixelUnorm8; + + for (int pixelY = 0; pixelY < footprintHeight; pixelY++) + { + int dstRowOffset = ((dstBaseY + pixelY) * rowStride) + (dstBaseX * BytesPerPixelUnorm8); + int srcRowBase = pixelY * footprintWidth; + int pixelX = 0; + + if (Vector128.IsHardwareAccelerated) + { + int limit = footprintWidth - 3; + for (; pixelX < limit; pixelX += 4) + { + int texelIndex = srcRowBase + pixelX; + Vector128 weights = Vector128.Create( + texelWeights[texelIndex], + texelWeights[texelIndex + 1], + texelWeights[texelIndex + 2], + texelWeights[texelIndex + 3]); + SimdHelpers.Write4PixelLdr( + imageBuffer, + dstRowOffset + (pixelX * BytesPerPixelUnorm8), + lowR, + lowG, + lowB, + lowA, + highR, + highG, + highB, + highA, + weights); + } + } + + for (; pixelX < footprintWidth; pixelX++) + { + SimdHelpers.WriteSinglePixelLdr( + imageBuffer, + dstRowOffset + (pixelX * BytesPerPixelUnorm8), + lowR, + lowG, + lowB, + lowA, + highR, + highG, + highB, + highA, + texelWeights[srcRowBase + pixelX]); + } + } + } +} diff --git a/src/ImageSharp.Textures.Astc/ColorEncoding/ColorEndpointMode.cs b/src/ImageSharp.Textures.Astc/ColorEncoding/ColorEndpointMode.cs new file mode 100644 index 00000000..14c9bc37 --- /dev/null +++ b/src/ImageSharp.Textures.Astc/ColorEncoding/ColorEndpointMode.cs @@ -0,0 +1,38 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Textures.Astc.ColorEncoding; + +/// +/// ASTC supports 16 color endpoint encoding schemes, known as endpoint modes +/// +/// +/// The options for endpoint modes let you vary the following: +/// +/// The number of color channels. For example, luminance, luminance+alpha, rgb, or rgba +/// The encoding method. For example, direct, base+offset, base+scale, or quantization level +/// The data range. For example, low dynamic range or High Dynamic Range +/// +/// +internal enum ColorEndpointMode +{ + LdrLumaDirect = 0, + LdrLumaBaseOffset, + HdrLumaLargeRange, + HdrLumaSmallRange, + LdrLumaAlphaDirect, + LdrLumaAlphaBaseOffset, + LdrRgbBaseScale, + HdrRgbBaseScale, + LdrRgbDirect, + LdrRgbBaseOffset, + LdrRgbBaseScaleTwoA, + HdrRgbDirect, + LdrRgbaDirect, + LdrRgbaBaseOffset, + HdrRgbDirectLdrAlpha, + HdrRgbDirectHdrAlpha, + + // Number of endpoint modes defined by the ASTC specification. + ColorEndpointModeCount +} diff --git a/src/ImageSharp.Textures.Astc/ColorEncoding/ColorEndpointModeExtensions.cs b/src/ImageSharp.Textures.Astc/ColorEncoding/ColorEndpointModeExtensions.cs new file mode 100644 index 00000000..c1a6e077 --- /dev/null +++ b/src/ImageSharp.Textures.Astc/ColorEncoding/ColorEndpointModeExtensions.cs @@ -0,0 +1,31 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Textures.Astc.ColorEncoding; + +internal static class ColorEndpointModeExtensions +{ + public static int GetEndpointModeClass(this ColorEndpointMode mode) + => (int)mode / 4; + + public static int GetColorValuesCount(this ColorEndpointMode mode) + => (mode.GetEndpointModeClass() + 1) * 2; + + /// + /// Determines whether the specified endpoint mode uses HDR (High Dynamic Range) encoding. + /// + /// + /// True if the mode is one of the 6 HDR modes (2, 3, 7, 11, 14, 15), false otherwise. + /// + public static bool IsHdr(this ColorEndpointMode mode) + => mode switch + { + ColorEndpointMode.HdrLumaLargeRange => true, // Mode 2 + ColorEndpointMode.HdrLumaSmallRange => true, // Mode 3 + ColorEndpointMode.HdrRgbBaseScale => true, // Mode 7 + ColorEndpointMode.HdrRgbDirect => true, // Mode 11 + ColorEndpointMode.HdrRgbDirectLdrAlpha => true, // Mode 14 + ColorEndpointMode.HdrRgbDirectHdrAlpha => true, // Mode 15 + _ => false + }; +} diff --git a/src/ImageSharp.Textures.Astc/ColorEncoding/ColorEndpointPair.cs b/src/ImageSharp.Textures.Astc/ColorEncoding/ColorEndpointPair.cs new file mode 100644 index 00000000..45cae3a1 --- /dev/null +++ b/src/ImageSharp.Textures.Astc/ColorEncoding/ColorEndpointPair.cs @@ -0,0 +1,32 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Textures.Astc.Core; + +namespace SixLabors.ImageSharp.Textures.Astc.ColorEncoding; + +/// +/// A value-type discriminated union representing either an LDR or HDR color endpoint pair. +/// +[StructLayout(LayoutKind.Auto)] +internal struct ColorEndpointPair +{ + public bool IsHdr; + + // LDR fields (used when IsHdr == false) + public RgbaColor LdrLow; + public RgbaColor LdrHigh; + + // HDR fields (used when IsHdr == true) + public RgbaHdrColor HdrLow; + public RgbaHdrColor HdrHigh; + public bool AlphaIsLdr; + public bool ValuesAreLns; + + public static ColorEndpointPair Ldr(RgbaColor low, RgbaColor high) + => new() { IsHdr = false, LdrLow = low, LdrHigh = high }; + + public static ColorEndpointPair Hdr(RgbaHdrColor low, RgbaHdrColor high, bool alphaIsLdr = false, bool valuesAreLns = true) + => new() { IsHdr = true, HdrLow = low, HdrHigh = high, AlphaIsLdr = alphaIsLdr, ValuesAreLns = valuesAreLns }; +} diff --git a/src/ImageSharp.Textures.Astc/ColorEncoding/EndpointCodec.cs b/src/ImageSharp.Textures.Astc/ColorEncoding/EndpointCodec.cs new file mode 100644 index 00000000..3263ac79 --- /dev/null +++ b/src/ImageSharp.Textures.Astc/ColorEncoding/EndpointCodec.cs @@ -0,0 +1,249 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Textures.Astc.BiseEncoding.Quantize; +using SixLabors.ImageSharp.Textures.Astc.Core; + +namespace SixLabors.ImageSharp.Textures.Astc.ColorEncoding; + +internal static class EndpointCodec +{ + /// + /// Decodes color endpoints for the specified mode, returning a polymorphic endpoint pair + /// that supports both LDR and HDR modes. + /// + /// Quantized integer values from the ASTC block + /// Maximum quantization value + /// The color endpoint mode + /// A ColorEndpointPair representing either LDR or HDR endpoints + public static ColorEndpointPair DecodeColorsForModePolymorphic(ReadOnlySpan values, int maxValue, ColorEndpointMode mode) + { + if (mode.IsHdr()) + { + (RgbaHdrColor low, RgbaHdrColor high) = HdrEndpointDecoder.DecodeHdrMode(values, maxValue, mode); + bool alphaIsLdr = mode == ColorEndpointMode.HdrRgbDirectLdrAlpha; + return ColorEndpointPair.Hdr(low, high, alphaIsLdr); + } + else + { + (RgbaColor low, RgbaColor high) = DecodeColorsForMode(values, maxValue, mode); + return ColorEndpointPair.Ldr(low, high); + } + } + + public static (RgbaColor EndpointLowRgba, RgbaColor EndpointHighRgba) DecodeColorsForMode(ReadOnlySpan values, int maxValue, ColorEndpointMode mode) + { + int count = mode.GetColorValuesCount(); + Span unquantizedValues = stackalloc int[count]; + int copyLen = Math.Min(count, values.Length); + for (int i = 0; i < copyLen; i++) + { + unquantizedValues[i] = values[i]; + } + + UnquantizeInline(unquantizedValues, maxValue); + ColorEndpointPair pair = DecodeColorsForModeUnquantized(unquantizedValues, mode); + return (pair.LdrLow, pair.LdrHigh); + } + + /// + /// Decodes color endpoints from already-unquantized values, supporting both LDR and HDR modes. + /// Called from the fused HDR decode path where BISE decode + batch unquantize + /// have already been performed. Returns a ColorEndpointPair (LDR or HDR). + /// + internal static ColorEndpointPair DecodeColorsForModePolymorphicUnquantized(ReadOnlySpan unquantizedValues, ColorEndpointMode mode) + { + if (mode.IsHdr()) + { + (RgbaHdrColor low, RgbaHdrColor high) = HdrEndpointDecoder.DecodeHdrModeUnquantized(unquantizedValues, mode); + bool alphaIsLdr = mode == ColorEndpointMode.HdrRgbDirectLdrAlpha; + return ColorEndpointPair.Hdr(low, high, alphaIsLdr); + } + + return DecodeColorsForModeUnquantized(unquantizedValues, mode); + } + + /// + /// Decodes color endpoints from already-unquantized values. + /// Called from the fused decode path where BISE decode + batch unquantize + /// have already been performed. Returns an LDR ColorEndpointPair. + /// + internal static ColorEndpointPair DecodeColorsForModeUnquantized(ReadOnlySpan unquantizedValues, ColorEndpointMode mode) + { + RgbaColor endpointLowRgba, endpointHighRgba; + + switch (mode) + { + case ColorEndpointMode.LdrLumaDirect: + endpointLowRgba = new RgbaColor(unquantizedValues[0], unquantizedValues[0], unquantizedValues[0]); + endpointHighRgba = new RgbaColor(unquantizedValues[1], unquantizedValues[1], unquantizedValues[1]); + break; + case ColorEndpointMode.LdrLumaBaseOffset: + { + int l0 = (unquantizedValues[0] >> 2) | (unquantizedValues[1] & 0xC0); + int l1 = Math.Min(l0 + (unquantizedValues[1] & 0x3F), 0xFF); + endpointLowRgba = new RgbaColor(l0, l0, l0); + endpointHighRgba = new RgbaColor(l1, l1, l1); + break; + } + + case ColorEndpointMode.LdrLumaAlphaDirect: + endpointLowRgba = new RgbaColor(unquantizedValues[0], unquantizedValues[0], unquantizedValues[0], unquantizedValues[2]); + endpointHighRgba = new RgbaColor(unquantizedValues[1], unquantizedValues[1], unquantizedValues[1], unquantizedValues[3]); + break; + case ColorEndpointMode.LdrLumaAlphaBaseOffset: + { + (int b0, int a0) = BitOperations.TransferPrecision(unquantizedValues[1], unquantizedValues[0]); + (int b2, int a2) = BitOperations.TransferPrecision(unquantizedValues[3], unquantizedValues[2]); + endpointLowRgba = new RgbaColor(a0, a0, a0, a2); + int highLuma = a0 + b0; + endpointHighRgba = new RgbaColor(highLuma, highLuma, highLuma, a2 + b2); + break; + } + + case ColorEndpointMode.LdrRgbBaseScale: + endpointLowRgba = new RgbaColor( + (unquantizedValues[0] * unquantizedValues[3]) >> 8, + (unquantizedValues[1] * unquantizedValues[3]) >> 8, + (unquantizedValues[2] * unquantizedValues[3]) >> 8); + endpointHighRgba = new RgbaColor(unquantizedValues[0], unquantizedValues[1], unquantizedValues[2]); + break; + case ColorEndpointMode.LdrRgbDirect: + { + int sum0 = unquantizedValues[0] + unquantizedValues[2] + unquantizedValues[4]; + int sum1 = unquantizedValues[1] + unquantizedValues[3] + unquantizedValues[5]; + if (sum1 < sum0) + { + endpointLowRgba = new RgbaColor( + r: (unquantizedValues[1] + unquantizedValues[5]) >> 1, + g: (unquantizedValues[3] + unquantizedValues[5]) >> 1, + b: unquantizedValues[5]); + endpointHighRgba = new RgbaColor( + r: (unquantizedValues[0] + unquantizedValues[4]) >> 1, + g: (unquantizedValues[2] + unquantizedValues[4]) >> 1, + b: unquantizedValues[4]); + } + else + { + endpointLowRgba = new RgbaColor(unquantizedValues[0], unquantizedValues[2], unquantizedValues[4]); + endpointHighRgba = new RgbaColor(unquantizedValues[1], unquantizedValues[3], unquantizedValues[5]); + } + + break; + } + + case ColorEndpointMode.LdrRgbBaseOffset: + { + (int b0, int a0) = BitOperations.TransferPrecision(unquantizedValues[1], unquantizedValues[0]); + (int b1, int a1) = BitOperations.TransferPrecision(unquantizedValues[3], unquantizedValues[2]); + (int b2, int a2) = BitOperations.TransferPrecision(unquantizedValues[5], unquantizedValues[4]); + if (b0 + b1 + b2 < 0) + { + endpointLowRgba = new RgbaColor( + r: (a0 + b0 + a2 + b2) >> 1, + g: (a1 + b1 + a2 + b2) >> 1, + b: a2 + b2); + endpointHighRgba = new RgbaColor( + r: (a0 + a2) >> 1, + g: (a1 + a2) >> 1, + b: a2); + } + else + { + endpointLowRgba = new RgbaColor(a0, a1, a2); + endpointHighRgba = new RgbaColor(a0 + b0, a1 + b1, a2 + b2); + } + + break; + } + + case ColorEndpointMode.LdrRgbBaseScaleTwoA: + endpointLowRgba = new RgbaColor( + r: (unquantizedValues[0] * unquantizedValues[3]) >> 8, + g: (unquantizedValues[1] * unquantizedValues[3]) >> 8, + b: (unquantizedValues[2] * unquantizedValues[3]) >> 8, + a: unquantizedValues[4]); + endpointHighRgba = new RgbaColor(unquantizedValues[0], unquantizedValues[1], unquantizedValues[2], unquantizedValues[5]); + break; + case ColorEndpointMode.LdrRgbaDirect: + { + int sum0 = unquantizedValues[0] + unquantizedValues[2] + unquantizedValues[4]; + int sum1 = unquantizedValues[1] + unquantizedValues[3] + unquantizedValues[5]; + if (sum1 >= sum0) + { + endpointLowRgba = new RgbaColor(unquantizedValues[0], unquantizedValues[2], unquantizedValues[4], unquantizedValues[6]); + endpointHighRgba = new RgbaColor(unquantizedValues[1], unquantizedValues[3], unquantizedValues[5], unquantizedValues[7]); + } + else + { + endpointLowRgba = new RgbaColor( + r: (unquantizedValues[1] + unquantizedValues[5]) >> 1, + g: (unquantizedValues[3] + unquantizedValues[5]) >> 1, + b: unquantizedValues[5], + a: unquantizedValues[7]); + endpointHighRgba = new RgbaColor( + r: (unquantizedValues[0] + unquantizedValues[4]) >> 1, + g: (unquantizedValues[2] + unquantizedValues[4]) >> 1, + b: unquantizedValues[4], + a: unquantizedValues[6]); + } + + break; + } + + case ColorEndpointMode.LdrRgbaBaseOffset: + { + (int b0, int a0) = BitOperations.TransferPrecision(unquantizedValues[1], unquantizedValues[0]); + (int b1, int a1) = BitOperations.TransferPrecision(unquantizedValues[3], unquantizedValues[2]); + (int b2, int a2) = BitOperations.TransferPrecision(unquantizedValues[5], unquantizedValues[4]); + (int b3, int a3) = BitOperations.TransferPrecision(unquantizedValues[7], unquantizedValues[6]); + if (b0 + b1 + b2 < 0) + { + endpointLowRgba = new RgbaColor( + r: (a0 + b0 + a2 + b2) >> 1, + g: (a1 + b1 + a2 + b2) >> 1, + b: a2 + b2, + a: a3 + b3); + endpointHighRgba = new RgbaColor( + r: (a0 + a2) >> 1, + g: (a1 + a2) >> 1, + b: a2, + a: a3); + } + else + { + endpointLowRgba = new RgbaColor(a0, a1, a2, a3); + endpointHighRgba = new RgbaColor(a0 + b0, a1 + b1, a2 + b2, a3 + b3); + } + + break; + } + + default: + endpointLowRgba = RgbaColor.Empty; + endpointHighRgba = RgbaColor.Empty; + break; + } + + return ColorEndpointPair.Ldr(endpointLowRgba, endpointHighRgba); + } + + internal static int[] UnquantizeArray(int[] values, int maxValue) + { + int[] result = new int[values.Length]; + for (int i = 0; i < values.Length; ++i) + { + result[i] = Quantization.UnquantizeCEValueFromRange(values[i], maxValue); + } + + return result; + } + + private static void UnquantizeInline(Span values, int maxValue) + { + for (int i = 0; i < values.Length; ++i) + { + values[i] = Quantization.UnquantizeCEValueFromRange(values[i], maxValue); + } + } +} diff --git a/src/ImageSharp.Textures.Astc/ColorEncoding/EndpointEncoder.cs b/src/ImageSharp.Textures.Astc/ColorEncoding/EndpointEncoder.cs new file mode 100644 index 00000000..1cb40a93 --- /dev/null +++ b/src/ImageSharp.Textures.Astc/ColorEncoding/EndpointEncoder.cs @@ -0,0 +1,549 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Textures.Astc.BiseEncoding.Quantize; +using SixLabors.ImageSharp.Textures.Astc.Core; + +namespace SixLabors.ImageSharp.Textures.Astc.ColorEncoding; + +internal static class EndpointEncoder +{ + public static bool UsesBlueContract(int maxValue, ColorEndpointMode mode, List values) + { + int valueCount = mode.GetColorValuesCount(); + ArgumentOutOfRangeException.ThrowIfLessThan(values.Count, valueCount); + + switch (mode) + { + case ColorEndpointMode.LdrRgbDirect: + case ColorEndpointMode.LdrRgbaDirect: + { + int maxValueCount = Math.Max(ColorEndpointMode.LdrRgbDirect.GetColorValuesCount(), ColorEndpointMode.LdrRgbaDirect.GetColorValuesCount()); + int[] v = new int[maxValueCount]; + for (int i = 0; i < maxValueCount; ++i) + { + v[i] = i < values.Count ? values[i] : 0; + } + + int[] unquantizedValues = EndpointCodec.UnquantizeArray(v, maxValue); + int s0 = unquantizedValues[0] + unquantizedValues[2] + unquantizedValues[4]; + int s1 = unquantizedValues[1] + unquantizedValues[3] + unquantizedValues[5]; + return s0 > s1; + } + + case ColorEndpointMode.LdrRgbBaseOffset: + case ColorEndpointMode.LdrRgbaBaseOffset: + { + int maxValueCount = Math.Max(ColorEndpointMode.LdrRgbBaseOffset.GetColorValuesCount(), ColorEndpointMode.LdrRgbaBaseOffset.GetColorValuesCount()); + int[] v = new int[maxValueCount]; + for (int i = 0; i < maxValueCount; ++i) + { + v[i] = i < values.Count ? values[i] : 0; + } + + int[] unquantizedValues = EndpointCodec.UnquantizeArray(v, maxValue); + (int b0, int a0) = BitOperations.TransferPrecision(unquantizedValues[1], unquantizedValues[0]); + (int b1, int a1) = BitOperations.TransferPrecision(unquantizedValues[3], unquantizedValues[2]); + (int b2, int a2) = BitOperations.TransferPrecision(unquantizedValues[5], unquantizedValues[4]); + return (b0 + b1 + b2) < 0; + } + + default: + return false; + } + } + + // TODO: Extract an interface and implement instances for each encoding mode + public static bool EncodeColorsForMode(RgbaColor endpointLowRgba, RgbaColor endpointHighRgba, int maxValue, EndpointEncodingMode encodingMode, out ColorEndpointMode astcMode, List values) + { + bool needsWeightSwap = false; + astcMode = ColorEndpointMode.LdrLumaDirect; + int valueCount = encodingMode.GetValuesCount(); + for (int i = values.Count; i < valueCount; ++i) + { + values.Add(0); + } + + switch (encodingMode) + { + case EndpointEncodingMode.DirectLuma: + return EncodeColorsLuma(endpointLowRgba, endpointHighRgba, maxValue, out astcMode, values); + case EndpointEncodingMode.DirectLumaAlpha: + { + int avg1 = endpointLowRgba.Average; + int avg2 = endpointHighRgba.Average; + values[0] = Quantization.QuantizeCEValueToRange(avg1, maxValue); + values[1] = Quantization.QuantizeCEValueToRange(avg2, maxValue); + values[2] = Quantization.QuantizeCEValueToRange(endpointLowRgba[3], maxValue); + values[3] = Quantization.QuantizeCEValueToRange(endpointHighRgba[3], maxValue); + astcMode = ColorEndpointMode.LdrLumaAlphaDirect; + break; + } + + case EndpointEncodingMode.BaseScaleRgb: + case EndpointEncodingMode.BaseScaleRgba: + { + RgbaColor baseColor = endpointHighRgba; + RgbaColor scaled = endpointLowRgba; + + int numChannelsGe = 0; + for (int i = 0; i < 3; ++i) + { + numChannelsGe += endpointHighRgba[i] >= endpointLowRgba[i] ? 1 : 0; + } + + if (numChannelsGe < 2) + { + needsWeightSwap = true; + (scaled, baseColor) = (baseColor, scaled); + } + + int[] quantizedBase = QuantizeColorArray(baseColor, maxValue); + int[] unquantizedBase = EndpointCodec.UnquantizeArray(quantizedBase, maxValue); + + int numSamples = 0; + int scaleSum = 0; + for (int i = 0; i < 3; ++i) + { + int x = unquantizedBase[i]; + if (x != 0) + { + ++numSamples; + scaleSum += (scaled[i] * 256) / x; + } + } + + values[0] = quantizedBase[0]; + values[1] = quantizedBase[1]; + values[2] = quantizedBase[2]; + if (numSamples > 0) + { + int avgScale = Math.Clamp(scaleSum / numSamples, 0, 255); + values[3] = Quantization.QuantizeCEValueToRange(avgScale, maxValue); + } + else + { + values[3] = maxValue; + } + + astcMode = ColorEndpointMode.LdrRgbBaseScale; + + if (encodingMode == EndpointEncodingMode.BaseScaleRgba) + { + values[4] = Quantization.QuantizeCEValueToRange(scaled[3], maxValue); + values[5] = Quantization.QuantizeCEValueToRange(baseColor[3], maxValue); + astcMode = ColorEndpointMode.LdrRgbBaseScaleTwoA; + } + + break; + } + + case EndpointEncodingMode.DirectRbg: + case EndpointEncodingMode.DirectRgba: + return EncodeColorsRGBA(endpointLowRgba, endpointHighRgba, maxValue, encodingMode == EndpointEncodingMode.DirectRgba, out astcMode, values); + default: + throw new InvalidOperationException("Unimplemented color encoding."); + } + + return needsWeightSwap; + } + + private static int[] QuantizeColorArray(RgbaColor c, int maxValue) + { + int[] array = new int[RgbaColor.BytesPerPixel]; + for (int i = 0; i < RgbaColor.BytesPerPixel; ++i) + { + array[i] = Quantization.QuantizeCEValueToRange(c[i], maxValue); + } + + return array; + } + + private static bool EncodeColorsLuma(RgbaColor endpointLow, RgbaColor endpointHigh, int maxValue, out ColorEndpointMode astcMode, List values) + { + astcMode = ColorEndpointMode.LdrLumaDirect; + ArgumentOutOfRangeException.ThrowIfLessThan(values.Count, 2); + + int avg1 = endpointLow.Average; + int avg2 = endpointHigh.Average; + + bool needsWeightSwap = false; + if (avg1 > avg2) + { + needsWeightSwap = true; + (avg2, avg1) = (avg1, avg2); + } + + int offset = Math.Min(avg2 - avg1, 0x3F); + int quantOffLow = Quantization.QuantizeCEValueToRange((avg1 & 0x3F) << 2, maxValue); + int quantOffHigh = Quantization.QuantizeCEValueToRange((avg1 & 0xC0) | offset, maxValue); + + int quantLow = Quantization.QuantizeCEValueToRange(avg1, maxValue); + int quantHigh = Quantization.QuantizeCEValueToRange(avg2, maxValue); + + values[0] = quantOffLow; + values[1] = quantOffHigh; + (RgbaColor decLowOff, RgbaColor decHighOff) = EndpointCodec.DecodeColorsForMode(values.ToArray(), maxValue, ColorEndpointMode.LdrLumaBaseOffset); + + values[0] = quantLow; + values[1] = quantHigh; + (RgbaColor decLowDir, RgbaColor decHighDir) = EndpointCodec.DecodeColorsForMode(values.ToArray(), maxValue, ColorEndpointMode.LdrLumaDirect); + + int calculateErrorOff = 0; + int calculateErrorDir = 0; + if (needsWeightSwap) + { + calculateErrorDir = RgbaColor.SquaredError(decLowDir, endpointHigh) + RgbaColor.SquaredError(decHighDir, endpointLow); + calculateErrorOff = RgbaColor.SquaredError(decLowOff, endpointHigh) + RgbaColor.SquaredError(decHighOff, endpointLow); + } + else + { + calculateErrorDir = RgbaColor.SquaredError(decLowDir, endpointLow) + RgbaColor.SquaredError(decHighDir, endpointHigh); + calculateErrorOff = RgbaColor.SquaredError(decLowOff, endpointLow) + RgbaColor.SquaredError(decHighOff, endpointHigh); + } + + if (calculateErrorDir <= calculateErrorOff) + { + values[0] = quantLow; + values[1] = quantHigh; + astcMode = ColorEndpointMode.LdrLumaDirect; + } + else + { + values[0] = quantOffLow; + values[1] = quantOffHigh; + astcMode = ColorEndpointMode.LdrLumaBaseOffset; + } + + return needsWeightSwap; + } + + private static bool EncodeColorsRGBA(RgbaColor endpointLowRgba, RgbaColor endpointHighRgba, int maxValue, bool withAlpha, out ColorEndpointMode astcMode, List values) + { + astcMode = ColorEndpointMode.LdrRgbDirect; + int numChannels = withAlpha ? 4 : 3; + + RgbaColor invertedBlueContractLow = endpointLowRgba.WithInvertedBlueContract(); + RgbaColor invertedBlueContractHigh = endpointHighRgba.WithInvertedBlueContract(); + + int[] directBase = new int[4]; + int[] directOffset = new int[4]; + for (int i = 0; i < 4; ++i) + { + directBase[i] = endpointLowRgba[i]; + directOffset[i] = Math.Clamp(endpointHighRgba[i] - endpointLowRgba[i], -32, 31); + (directOffset[i], directBase[i]) = BitOperations.TransferPrecisionInverse(directOffset[i], directBase[i]); + } + + int[] invertedBlueContractBase = new int[4]; + int[] invertedBlueContractOffset = new int[4]; + for (int i = 0; i < 4; ++i) + { + invertedBlueContractBase[i] = invertedBlueContractHigh[i]; + invertedBlueContractOffset[i] = Math.Clamp(invertedBlueContractLow[i] - invertedBlueContractHigh[i], -32, 31); + (invertedBlueContractOffset[i], invertedBlueContractBase[i]) = BitOperations.TransferPrecisionInverse(invertedBlueContractOffset[i], invertedBlueContractBase[i]); + } + + int[] directBaseSwapped = new int[4]; + int[] directOffsetSwapped = new int[4]; + for (int i = 0; i < 4; ++i) + { + directBaseSwapped[i] = endpointHighRgba[i]; + directOffsetSwapped[i] = Math.Clamp(endpointLowRgba[i] - endpointHighRgba[i], -32, 31); + (directOffsetSwapped[i], directBaseSwapped[i]) = BitOperations.TransferPrecisionInverse(directOffsetSwapped[i], directBaseSwapped[i]); + } + + int[] invertedBlueContractBaseSwapped = new int[4]; + int[] invertedBlueContractOffsetSwapped = new int[4]; + for (int i = 0; i < 4; ++i) + { + invertedBlueContractBaseSwapped[i] = invertedBlueContractLow[i]; + invertedBlueContractOffsetSwapped[i] = Math.Clamp(invertedBlueContractHigh[i] - invertedBlueContractLow[i], -32, 31); + (invertedBlueContractOffsetSwapped[i], invertedBlueContractBaseSwapped[i]) = BitOperations.TransferPrecisionInverse(invertedBlueContractOffsetSwapped[i], invertedBlueContractBaseSwapped[i]); + } + + QuantizedEndpointPair directQuantized = new(endpointLowRgba, endpointHighRgba, maxValue); + QuantizedEndpointPair bcQuantized = new(invertedBlueContractLow, invertedBlueContractHigh, maxValue); + + QuantizedEndpointPair offsetQuantized = new(new RgbaColor(directBase[0], directBase[1], directBase[2], directBase[3]), new RgbaColor(directOffset[0], directOffset[1], directOffset[2], directOffset[3]), maxValue); + QuantizedEndpointPair bcOffsetQuantized = new(new RgbaColor(invertedBlueContractBase[0], invertedBlueContractBase[1], invertedBlueContractBase[2], invertedBlueContractBase[3]), new RgbaColor(invertedBlueContractOffset[0], invertedBlueContractOffset[1], invertedBlueContractOffset[2], invertedBlueContractOffset[3]), maxValue); + + QuantizedEndpointPair offsetSwappedQuantized = new(new RgbaColor(directBaseSwapped[0], directBaseSwapped[1], directBaseSwapped[2], directBaseSwapped[3]), new RgbaColor(directOffsetSwapped[0], directOffsetSwapped[1], directOffsetSwapped[2], directOffsetSwapped[3]), maxValue); + QuantizedEndpointPair bcOffsetSwappedQuantized = new(new RgbaColor(invertedBlueContractBaseSwapped[0], invertedBlueContractBaseSwapped[1], invertedBlueContractBaseSwapped[2], invertedBlueContractBaseSwapped[3]), new RgbaColor(invertedBlueContractOffsetSwapped[0], invertedBlueContractOffsetSwapped[1], invertedBlueContractOffsetSwapped[2], invertedBlueContractOffsetSwapped[3]), maxValue); + + List errors = new(6); + + // 3.1 regular unquantized error + { + int[] rgbaLow = directQuantized.UnquantizedLow(); + int[] rgbaHigh = directQuantized.UnquantizedHigh(); + RgbaColor lowColor = new(rgbaLow[0], rgbaLow[1], rgbaLow[2], rgbaLow[3]); + RgbaColor highColor = new(rgbaHigh[0], rgbaHigh[1], rgbaHigh[2], rgbaHigh[3]); + int squaredRgbError = withAlpha + ? RgbaColor.SquaredError(lowColor, endpointLowRgba) + RgbaColor.SquaredError(highColor, endpointHighRgba) + : RgbColor.SquaredError(lowColor, endpointLowRgba) + RgbColor.SquaredError(highColor, endpointHighRgba); + errors.Add(new CEEncodingOption(squaredRgbError, directQuantized, false, false, false)); + } + + // 3.2 blue-contract + { + int[] blueContractUnquantizedLow = bcQuantized.UnquantizedLow(); + int[] blueContractUnquantizedHigh = bcQuantized.UnquantizedHigh(); + RgbaColor blueContractLow = RgbaColorExtensions.WithBlueContract(blueContractUnquantizedLow[0], blueContractUnquantizedLow[1], blueContractUnquantizedLow[2], blueContractUnquantizedLow[3]); + RgbaColor blueContractHigh = RgbaColorExtensions.WithBlueContract(blueContractUnquantizedHigh[0], blueContractUnquantizedHigh[1], blueContractUnquantizedHigh[2], blueContractUnquantizedHigh[3]); + + // TODO: How to handle alpha for this entire functions?? + int blueContractSquaredError = withAlpha + ? RgbaColor.SquaredError(blueContractLow, endpointLowRgba) + RgbaColor.SquaredError(blueContractHigh, endpointHighRgba) + : RgbColor.SquaredError(blueContractLow, endpointLowRgba) + RgbColor.SquaredError(blueContractHigh, endpointHighRgba); + + errors.Add(new CEEncodingOption(blueContractSquaredError, bcQuantized, swapEndpoints: false, blueContract: true, useOffsetMode: false)); + } + + // 3.3 base/offset + void ComputeBaseOffsetError(QuantizedEndpointPair pair, bool swapped) + { + int[] baseArr = pair.UnquantizedLow(); + int[] offsetArr = pair.UnquantizedHigh(); + + RgbaColor baseColor = new(baseArr[0], baseArr[1], baseArr[2], baseArr[3]); + RgbaColor offsetColor = new RgbaColor(offsetArr[0], offsetArr[1], offsetArr[2], offsetArr[3]).AsOffsetFrom(baseColor); + + int baseOffsetError = 0; + if (swapped) + { + baseOffsetError = withAlpha + ? RgbaColor.SquaredError(baseColor, endpointHighRgba) + RgbaColor.SquaredError(offsetColor, endpointLowRgba) + : RgbColor.SquaredError(baseColor, endpointHighRgba) + RgbColor.SquaredError(offsetColor, endpointLowRgba); + } + else + { + baseOffsetError = withAlpha + ? RgbaColor.SquaredError(baseColor, endpointLowRgba) + RgbaColor.SquaredError(offsetColor, endpointHighRgba) + : RgbColor.SquaredError(baseColor, endpointLowRgba) + RgbColor.SquaredError(offsetColor, endpointHighRgba); + } + + errors.Add(new CEEncodingOption(baseOffsetError, pair, swapped, false, true)); + } + + ComputeBaseOffsetError(offsetQuantized, false); + + void ComputeBaseOffsetBlueContractError(QuantizedEndpointPair pair, bool swapped) + { + int[] baseArr = pair.UnquantizedLow(); + int[] offsetArr = pair.UnquantizedHigh(); + + RgbaColor baseColor = new(baseArr[0], baseArr[1], baseArr[2], baseArr[3]); + RgbaColor offsetColor = new RgbaColor(offsetArr[0], offsetArr[1], offsetArr[2], offsetArr[3]).AsOffsetFrom(baseColor); + + baseColor = baseColor.WithBlueContract(); + offsetColor = offsetColor.WithBlueContract(); + + int squaredBlueContractError = 0; + if (swapped) + { + squaredBlueContractError = withAlpha + ? RgbaColor.SquaredError(baseColor, endpointLowRgba) + RgbaColor.SquaredError(offsetColor, endpointHighRgba) + : RgbColor.SquaredError(baseColor, endpointLowRgba) + RgbColor.SquaredError(offsetColor, endpointHighRgba); + } + else + { + squaredBlueContractError = withAlpha + ? RgbaColor.SquaredError(baseColor, endpointHighRgba) + RgbaColor.SquaredError(offsetColor, endpointLowRgba) + : RgbColor.SquaredError(baseColor, endpointHighRgba) + RgbColor.SquaredError(offsetColor, endpointLowRgba); + } + + errors.Add(new CEEncodingOption(squaredBlueContractError, pair, swapped, true, true)); + } + + ComputeBaseOffsetBlueContractError(bcOffsetQuantized, false); + ComputeBaseOffsetError(offsetSwappedQuantized, true); + ComputeBaseOffsetBlueContractError(bcOffsetSwappedQuantized, true); + + errors.Sort((a, b) => a.Error().CompareTo(b.Error())); + + foreach (CEEncodingOption measurement in errors) + { + bool needsWeightSwap = false; + if (measurement.Pack(withAlpha, out ColorEndpointMode modeUnused, values, ref needsWeightSwap)) + { + return needsWeightSwap; + } + } + + throw new InvalidOperationException("Shouldn't have reached this point"); + } + + private class QuantizedEndpointPair + { + private readonly RgbaColor originalLow; + private readonly RgbaColor originalHigh; + private readonly int[] quantizedLow; + private readonly int[] quantizedHigh; + private readonly int[] unquantizedLow; + private readonly int[] unquantizedHigh; + + public QuantizedEndpointPair(RgbaColor low, RgbaColor high, int maxValue) + { + this.originalLow = low; + this.originalHigh = high; + this.quantizedLow = QuantizeColorArray(low, maxValue); + this.quantizedHigh = QuantizeColorArray(high, maxValue); + this.unquantizedLow = EndpointCodec.UnquantizeArray(this.quantizedLow, maxValue); + this.unquantizedHigh = EndpointCodec.UnquantizeArray(this.quantizedHigh, maxValue); + } + + public int[] QuantizedLow() => this.quantizedLow; + + public int[] QuantizedHigh() => this.quantizedHigh; + + public int[] UnquantizedLow() => this.unquantizedLow; + + public int[] UnquantizedHigh() => this.unquantizedHigh; + + public RgbaColor OriginalLow() => this.originalLow; + + public RgbaColor OriginalHigh() => this.originalHigh; + } + + private class CEEncodingOption + { + private readonly int squaredError; + private readonly QuantizedEndpointPair quantizedEndpoints; + private readonly bool swapEndpoints; + private readonly bool blueContract; + private readonly bool useOffsetMode; + + public CEEncodingOption( + int squaredError, + QuantizedEndpointPair quantizedEndpoints, + bool swapEndpoints, + bool blueContract, + bool useOffsetMode) + { + this.squaredError = squaredError; + this.quantizedEndpoints = quantizedEndpoints; + this.swapEndpoints = swapEndpoints; + this.blueContract = blueContract; + this.useOffsetMode = useOffsetMode; + } + + public bool Pack(bool hasAlpha, out ColorEndpointMode endpointMode, List values, ref bool needsWeightSwap) + { + endpointMode = ColorEndpointMode.LdrLumaDirect; + int[] unquantizedLowOriginal = this.quantizedEndpoints.UnquantizedLow(); + int[] unquantizedHighOriginal = this.quantizedEndpoints.UnquantizedHigh(); + + int[] unquantizedLow = (int[])unquantizedLowOriginal.Clone(); + int[] unquantizedHigh = (int[])unquantizedHighOriginal.Clone(); + + if (this.useOffsetMode) + { + for (int i = 0; i < 4; ++i) + { + (unquantizedHigh[i], unquantizedLow[i]) = BitOperations.TransferPrecision(unquantizedHigh[i], unquantizedLow[i]); + } + } + + int sum0 = 0, sum1 = 0; + for (int i = 0; i < 3; ++i) + { + sum0 += unquantizedLow[i]; + sum1 += unquantizedHigh[i]; + } + + bool swapVals = false; + if (this.useOffsetMode) + { + if (this.blueContract) + { + swapVals = sum1 >= 0; + } + else + { + swapVals = sum1 < 0; + } + + if (swapVals) + { + return false; + } + } + else + { + if (this.blueContract) + { + if (sum1 == sum0) + { + return false; + } + + swapVals = sum1 > sum0; + needsWeightSwap = !needsWeightSwap; + } + else + { + swapVals = sum1 < sum0; + } + } + + int[] quantizedLowOriginal = this.quantizedEndpoints.QuantizedLow(); + int[] quantizedHighOriginal = this.quantizedEndpoints.QuantizedHigh(); + + int[] quantizedLow = (int[])quantizedLowOriginal.Clone(); + int[] quantizedHigh = (int[])quantizedHighOriginal.Clone(); + + if (swapVals) + { + if (this.useOffsetMode) + { + throw new InvalidOperationException(); + } + + (quantizedHigh, quantizedLow) = (quantizedLow, quantizedHigh); + needsWeightSwap = !needsWeightSwap; + } + + values[0] = quantizedLow[0]; + values[1] = quantizedHigh[0]; + values[2] = quantizedLow[1]; + values[3] = quantizedHigh[1]; + values[4] = quantizedLow[2]; + values[5] = quantizedHigh[2]; + + if (this.useOffsetMode) + { + endpointMode = ColorEndpointMode.LdrRgbBaseOffset; + } + else + { + endpointMode = ColorEndpointMode.LdrRgbDirect; + } + + if (hasAlpha) + { + values[6] = quantizedLow[3]; + values[7] = quantizedHigh[3]; + if (this.useOffsetMode) + { + endpointMode = ColorEndpointMode.LdrRgbaBaseOffset; + } + else + { + endpointMode = ColorEndpointMode.LdrRgbaDirect; + } + } + + if (this.swapEndpoints) + { + needsWeightSwap = !needsWeightSwap; + } + + return true; + } + + public bool BlueContract() => this.blueContract; + + public int Error() => this.squaredError; + } +} diff --git a/src/ImageSharp.Textures.Astc/ColorEncoding/EndpointEncodingMode.cs b/src/ImageSharp.Textures.Astc/ColorEncoding/EndpointEncodingMode.cs new file mode 100644 index 00000000..08f2f58e --- /dev/null +++ b/src/ImageSharp.Textures.Astc/ColorEncoding/EndpointEncodingMode.cs @@ -0,0 +1,14 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Textures.Astc.ColorEncoding; + +internal enum EndpointEncodingMode +{ + DirectLuma, + DirectLumaAlpha, + BaseScaleRgb, + BaseScaleRgba, + DirectRbg, + DirectRgba +} diff --git a/src/ImageSharp.Textures.Astc/ColorEncoding/EndpointEncodingModeExtensions.cs b/src/ImageSharp.Textures.Astc/ColorEncoding/EndpointEncodingModeExtensions.cs new file mode 100644 index 00000000..1b81a1ac --- /dev/null +++ b/src/ImageSharp.Textures.Astc/ColorEncoding/EndpointEncodingModeExtensions.cs @@ -0,0 +1,15 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Textures.Astc.ColorEncoding; + +internal static class EndpointEncodingModeExtensions +{ + public static int GetValuesCount(this EndpointEncodingMode mode) => mode switch + { + EndpointEncodingMode.DirectLuma => 2, + EndpointEncodingMode.DirectLumaAlpha or EndpointEncodingMode.BaseScaleRgb => 4, + EndpointEncodingMode.DirectRbg or EndpointEncodingMode.BaseScaleRgba => 6, + _ => 8 + }; +} diff --git a/src/ImageSharp.Textures.Astc/ColorEncoding/HdrEndpointDecoder.cs b/src/ImageSharp.Textures.Astc/ColorEncoding/HdrEndpointDecoder.cs new file mode 100644 index 00000000..68040c57 --- /dev/null +++ b/src/ImageSharp.Textures.Astc/ColorEncoding/HdrEndpointDecoder.cs @@ -0,0 +1,478 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Textures.Astc.BiseEncoding.Quantize; +using SixLabors.ImageSharp.Textures.Astc.Core; + +namespace SixLabors.ImageSharp.Textures.Astc.ColorEncoding; + +/// +/// Decodes HDR (High Dynamic Range) color endpoints for ASTC texture compression. +/// +/// +/// HDR modes produce 12-bit intermediate values (0-4095) which are shifted left by 4 +/// to produce the final 16-bit values (0-65520) stored as FP16 bit patterns. +/// +internal static class HdrEndpointDecoder +{ + public static (RgbaHdrColor Low, RgbaHdrColor High) DecodeHdrMode(ReadOnlySpan values, int maxValue, ColorEndpointMode mode) + { + int count = mode.GetColorValuesCount(); + Span unquantizedValues = stackalloc int[count]; + int copyLength = Math.Min(count, values.Length); + for (int i = 0; i < copyLength; i++) + { + unquantizedValues[i] = Quantization.UnquantizeCEValueFromRange(values[i], maxValue); + } + + return DecodeHdrModeUnquantized(unquantizedValues, mode); + } + + /// + /// Decodes HDR endpoints from already-unquantized values. + /// Called from the fused decode path where BISE decode + batch unquantize + /// have already been performed. + /// + public static (RgbaHdrColor Low, RgbaHdrColor High) DecodeHdrModeUnquantized(ReadOnlySpan value, ColorEndpointMode mode) => mode switch + { + ColorEndpointMode.HdrLumaLargeRange => UnpackHdrLuminanceLargeRangeCore(value[0], value[1]), + ColorEndpointMode.HdrLumaSmallRange => UnpackHdrLuminanceSmallRangeCore(value[0], value[1]), + ColorEndpointMode.HdrRgbBaseScale => UnpackHdrRgbBaseScaleCore(value[0], value[1], value[2], value[3]), + ColorEndpointMode.HdrRgbDirect => UnpackHdrRgbDirectCore(value[0], value[1], value[2], value[3], value[4], value[5]), + ColorEndpointMode.HdrRgbDirectLdrAlpha => UnpackHdrRgbDirectLdrAlphaCore(value), + ColorEndpointMode.HdrRgbDirectHdrAlpha => UnpackHdrRgbDirectHdrAlphaCore(value), + _ => throw new InvalidOperationException($"Mode {mode} is not an HDR mode") + }; + + /// + /// Performs an unsigned left shift of a signed value, avoiding undefined behavior + /// that would occur with signed left shift of negative values. + /// + private static int SafeSignedLeftShift(int value, int shift) => (int)((uint)value << shift); + + private static (RgbaHdrColor Low, RgbaHdrColor High) UnpackHdrLuminanceLargeRangeCore(int v0, int v1) + { + int y0, y1; + if (v1 >= v0) + { + y0 = v0 << 4; + y1 = v1 << 4; + } + else + { + y0 = (v1 << 4) + 8; + y1 = (v0 << 4) - 8; + } + + RgbaHdrColor low = new((ushort)(y0 << 4), (ushort)(y0 << 4), (ushort)(y0 << 4), 0x7800); + RgbaHdrColor high = new((ushort)(y1 << 4), (ushort)(y1 << 4), (ushort)(y1 << 4), 0x7800); + return (low, high); + } + + private static (RgbaHdrColor Low, RgbaHdrColor High) UnpackHdrLuminanceSmallRangeCore(int v0, int v1) + { + int y0, y1; + if ((v0 & 0x80) != 0) + { + y0 = ((v1 & 0xE0) << 4) | ((v0 & 0x7F) << 2); + y1 = (v1 & 0x1F) << 2; + } + else + { + y0 = ((v1 & 0xF0) << 4) | ((v0 & 0x7F) << 1); + y1 = (v1 & 0x0F) << 1; + } + + y1 += y0; + if (y1 > 0xFFF) + { + y1 = 0xFFF; + } + + RgbaHdrColor low = new((ushort)(y0 << 4), (ushort)(y0 << 4), (ushort)(y0 << 4), 0x7800); + RgbaHdrColor high = new((ushort)(y1 << 4), (ushort)(y1 << 4), (ushort)(y1 << 4), 0x7800); + return (low, high); + } + + private static (RgbaHdrColor Low, RgbaHdrColor High) UnpackHdrRgbBaseScaleCore(int v0, int v1, int v2, int v3) + { + int modeValue = ((v0 & 0xC0) >> 6) | (((v1 & 0x80) >> 7) << 2) | (((v2 & 0x80) >> 7) << 3); + + int majorComponent; + int mode; + + (majorComponent, mode) = modeValue switch + { + _ when (modeValue & 0xC) != 0xC => (modeValue >> 2, modeValue & 3), + not 0xF => (modeValue & 3, 4), + _ => (0, 5) + }; + + int red = v0 & 0x3F; + int green = v1 & 0x1F; + int blue = v2 & 0x1F; + int scale = v3 & 0x1F; + + int bit0 = (v1 >> 6) & 1; + int bit1 = (v1 >> 5) & 1; + int bit2 = (v2 >> 6) & 1; + int bit3 = (v2 >> 5) & 1; + int bit4 = (v3 >> 7) & 1; + int bit5 = (v3 >> 6) & 1; + int bit6 = (v3 >> 5) & 1; + + int oneHotMode = 1 << mode; + + if ((oneHotMode & 0x30) != 0) + { + green |= bit0 << 6; + } + + if ((oneHotMode & 0x3A) != 0) + { + green |= bit1 << 5; + } + + if ((oneHotMode & 0x30) != 0) + { + blue |= bit2 << 6; + } + + if ((oneHotMode & 0x3A) != 0) + { + blue |= bit3 << 5; + } + + if ((oneHotMode & 0x3D) != 0) + { + scale |= bit6 << 5; + } + + if ((oneHotMode & 0x2D) != 0) + { + scale |= bit5 << 6; + } + + if ((oneHotMode & 0x04) != 0) + { + scale |= bit4 << 7; + } + + if ((oneHotMode & 0x3B) != 0) + { + red |= bit4 << 6; + } + + if ((oneHotMode & 0x04) != 0) + { + red |= bit3 << 6; + } + + if ((oneHotMode & 0x10) != 0) + { + red |= bit5 << 7; + } + + if ((oneHotMode & 0x0F) != 0) + { + red |= bit2 << 7; + } + + if ((oneHotMode & 0x05) != 0) + { + red |= bit1 << 8; + } + + if ((oneHotMode & 0x0A) != 0) + { + red |= bit0 << 8; + } + + if ((oneHotMode & 0x05) != 0) + { + red |= bit0 << 9; + } + + if ((oneHotMode & 0x02) != 0) + { + red |= bit6 << 9; + } + + if ((oneHotMode & 0x01) != 0) + { + red |= bit3 << 10; + } + + if ((oneHotMode & 0x02) != 0) + { + red |= bit5 << 10; + } + + // Shift amounts per mode (from ARM reference) + ReadOnlySpan shiftAmounts = [1, 1, 2, 3, 4, 5]; + int shiftAmount = shiftAmounts[mode]; + + red <<= shiftAmount; + green <<= shiftAmount; + blue <<= shiftAmount; + scale <<= shiftAmount; + + if (mode != 5) + { + green = red - green; + blue = red - blue; + } + + // Swap components based on major component + (red, green, blue) = majorComponent switch + { + 1 => (green, red, blue), + 2 => (blue, green, red), + _ => (red, green, blue) + }; + + // Low endpoint is base minus scale offset + int red0 = red - scale; + int green0 = green - scale; + int blue0 = blue - scale; + + // Clamp to [0, 0xFFF] + red = Math.Max(red, 0); + green = Math.Max(green, 0); + blue = Math.Max(blue, 0); + red0 = Math.Max(red0, 0); + green0 = Math.Max(green0, 0); + blue0 = Math.Max(blue0, 0); + + RgbaHdrColor low = new((ushort)(red0 << 4), (ushort)(green0 << 4), (ushort)(blue0 << 4), 0x7800); + RgbaHdrColor high = new((ushort)(red << 4), (ushort)(green << 4), (ushort)(blue << 4), 0x7800); + return (low, high); + } + + private static (RgbaHdrColor Low, RgbaHdrColor High) UnpackHdrRgbDirectCore(int v0, int v1, int v2, int v3, int v4, int v5) + { + int modeValue = ((v1 & 0x80) >> 7) | (((v2 & 0x80) >> 7) << 1) | (((v3 & 0x80) >> 7) << 2); + int majorComponent = ((v4 & 0x80) >> 7) | (((v5 & 0x80) >> 7) << 1); + + // Special case: majorComponent == 3 (direct passthrough) + if (majorComponent == 3) + { + RgbaHdrColor low = new( + (ushort)(v0 << 8), + (ushort)(v2 << 8), + (ushort)((v4 & 0x7F) << 9), + 0x7800); + RgbaHdrColor high = new( + (ushort)(v1 << 8), + (ushort)(v3 << 8), + (ushort)((v5 & 0x7F) << 9), + 0x7800); + return (low, high); + } + + int a = v0 | ((v1 & 0x40) << 2); + int b0 = v2 & 0x3F; + int b1 = v3 & 0x3F; + int c = v1 & 0x3F; + int d0 = v4 & 0x7F; + int d1 = v5 & 0x7F; + + // Data bits table from ARM reference + ReadOnlySpan dataBitsTable = [7, 6, 7, 6, 5, 6, 5, 6]; + int dataBits = dataBitsTable[modeValue]; + + int bit0 = (v2 >> 6) & 1; + int bit1 = (v3 >> 6) & 1; + int bit2 = (v4 >> 6) & 1; + int bit3 = (v5 >> 6) & 1; + int bit4 = (v4 >> 5) & 1; + int bit5 = (v5 >> 5) & 1; + + int oneHotModeValue = 1 << modeValue; + + // Bit placement for 'a' + if ((oneHotModeValue & 0xA4) != 0) + { + a |= bit0 << 9; + } + + if ((oneHotModeValue & 0x8) != 0) + { + a |= bit2 << 9; + } + + if ((oneHotModeValue & 0x50) != 0) + { + a |= bit4 << 9; + } + + if ((oneHotModeValue & 0x50) != 0) + { + a |= bit5 << 10; + } + + if ((oneHotModeValue & 0xA0) != 0) + { + a |= bit1 << 10; + } + + if ((oneHotModeValue & 0xC0) != 0) + { + a |= bit2 << 11; + } + + // Bit placement for 'c' + if ((oneHotModeValue & 0x4) != 0) + { + c |= bit1 << 6; + } + + if ((oneHotModeValue & 0xE8) != 0) + { + c |= bit3 << 6; + } + + if ((oneHotModeValue & 0x20) != 0) + { + c |= bit2 << 7; + } + + // Bit placement for 'b0' and 'b1' + if ((oneHotModeValue & 0x5B) != 0) + { + b0 |= bit0 << 6; + b1 |= bit1 << 6; + } + + if ((oneHotModeValue & 0x12) != 0) + { + b0 |= bit2 << 7; + b1 |= bit3 << 7; + } + + // Bit placement for 'd0' and 'd1' + if ((oneHotModeValue & 0xAF) != 0) + { + d0 |= bit4 << 5; + d1 |= bit5 << 5; + } + + if ((oneHotModeValue & 0x5) != 0) + { + d0 |= bit2 << 6; + d1 |= bit3 << 6; + } + + // Sign-extend d0 and d1 based on dataBits + int signExtendShift = 32 - dataBits; + d0 = (d0 << signExtendShift) >> signExtendShift; + d1 = (d1 << signExtendShift) >> signExtendShift; + + // Expand to 12 bits + int valueShift = (modeValue >> 1) ^ 3; + a = SafeSignedLeftShift(a, valueShift); + b0 = SafeSignedLeftShift(b0, valueShift); + b1 = SafeSignedLeftShift(b1, valueShift); + c = SafeSignedLeftShift(c, valueShift); + d0 = SafeSignedLeftShift(d0, valueShift); + d1 = SafeSignedLeftShift(d1, valueShift); + + // Compute color values per ARM reference + int red1 = a; + int green1 = a - b0; + int blue1 = a - b1; + int red0 = a - c; + int green0 = a - b0 - c - d0; + int blue0 = a - b1 - c - d1; + + // Clamp to [0, 4095] + red0 = Math.Clamp(red0, 0, 0xFFF); + green0 = Math.Clamp(green0, 0, 0xFFF); + blue0 = Math.Clamp(blue0, 0, 0xFFF); + red1 = Math.Clamp(red1, 0, 0xFFF); + green1 = Math.Clamp(green1, 0, 0xFFF); + blue1 = Math.Clamp(blue1, 0, 0xFFF); + + // Swap components based on major component + (red0, green0, blue0, red1, green1, blue1) = majorComponent switch + { + 1 => (green0, red0, blue0, green1, red1, blue1), + 2 => (blue0, green0, red0, blue1, green1, red1), + _ => (red0, green0, blue0, red1, green1, blue1) + }; + + RgbaHdrColor lowResult = new((ushort)(red0 << 4), (ushort)(green0 << 4), (ushort)(blue0 << 4), 0x7800); + RgbaHdrColor highResult = new((ushort)(red1 << 4), (ushort)(green1 << 4), (ushort)(blue1 << 4), 0x7800); + return (lowResult, highResult); + } + + private static (RgbaHdrColor Low, RgbaHdrColor High) UnpackHdrRgbDirectLdrAlphaCore(ReadOnlySpan unquantizedValues) + { + (RgbaHdrColor rgbLow, RgbaHdrColor rgbHigh) = UnpackHdrRgbDirectCore(unquantizedValues[0], unquantizedValues[1], unquantizedValues[2], unquantizedValues[3], unquantizedValues[4], unquantizedValues[5]); + + ushort alpha0 = (ushort)(unquantizedValues[6] * 257); + ushort alpha1 = (ushort)(unquantizedValues[7] * 257); + + RgbaHdrColor low = new(rgbLow.R, rgbLow.G, rgbLow.B, alpha0); + RgbaHdrColor high = new(rgbHigh.R, rgbHigh.G, rgbHigh.B, alpha1); + return (low, high); + } + + private static (RgbaHdrColor Low, RgbaHdrColor High) UnpackHdrRgbDirectHdrAlphaCore(ReadOnlySpan unquantizedValues) + { + (RgbaHdrColor rgbLow, RgbaHdrColor rgbHigh) = UnpackHdrRgbDirectCore(unquantizedValues[0], unquantizedValues[1], unquantizedValues[2], unquantizedValues[3], unquantizedValues[4], unquantizedValues[5]); + + (ushort alpha0, ushort alpha1) = UnpackHdrAlpha(unquantizedValues[6], unquantizedValues[7]); + + RgbaHdrColor low = new(rgbLow.R, rgbLow.G, rgbLow.B, alpha0); + RgbaHdrColor high = new(rgbHigh.R, rgbHigh.G, rgbHigh.B, alpha1); + return (low, high); + } + + /// + /// Decodes HDR alpha values + /// + private static (ushort Low, ushort High) UnpackHdrAlpha(int v6, int v7) + { + int selector = ((v6 >> 7) & 1) | ((v7 >> 6) & 2); + v6 &= 0x7F; + v7 &= 0x7F; + + int a0, a1; + + if (selector == 3) + { + // Simple mode: direct 7-bit values shifted to 12-bit + a0 = v6 << 5; + a1 = v7 << 5; + } + else + { + // Complex mode: base + sign-extended offset + v6 |= (v7 << (selector + 1)) & 0x780; + v7 &= 0x3F >> selector; + v7 ^= 32 >> selector; + v7 -= 32 >> selector; + v6 <<= 4 - selector; + v7 <<= 4 - selector; + v7 += v6; + + if (v7 < 0) + { + v7 = 0; + } + else if (v7 > 0xFFF) + { + v7 = 0xFFF; + } + + a0 = v6; + a1 = v7; + } + + a0 = Math.Clamp(a0, 0, 0xFFF); + a1 = Math.Clamp(a1, 0, 0xFFF); + + return ((ushort)(a0 << 4), (ushort)(a1 << 4)); + } +} diff --git a/src/ImageSharp.Textures.Astc/ColorEncoding/Partition.cs b/src/ImageSharp.Textures.Astc/ColorEncoding/Partition.cs new file mode 100644 index 00000000..1ea3df1d --- /dev/null +++ b/src/ImageSharp.Textures.Astc/ColorEncoding/Partition.cs @@ -0,0 +1,248 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Textures.Astc.Core; + +namespace SixLabors.ImageSharp.Textures.Astc.ColorEncoding; + +internal sealed class Partition +{ + private static readonly System.Collections.Concurrent.ConcurrentDictionary<(Footprint, int, int), Partition> PartitionCache = new(); + + public Partition(Footprint footprint, int partitionCount, int? id = null) + { + this.Footprint = footprint; + this.PartitionCount = partitionCount; + this.PartitionId = id; + this.Assignment = []; + } + + public Footprint Footprint { get; set; } + + public int PartitionCount { get; set; } + + public int? PartitionId { get; set; } + + public int[] Assignment { get; set; } + + public override bool Equals(object? obj) + { + if (obj is not Partition other) + { + return false; + } + + return PartitionMetric(this, other) == 0; + } + + public override int GetHashCode() => HashCode.Combine(this.Footprint, this.PartitionCount, this.PartitionId); + + public static int PartitionMetric(Partition a, Partition b) + { + ArgumentOutOfRangeException.ThrowIfNotEqual(a.Footprint, b.Footprint); + + const int maxNumSubsets = 4; + int width = a.Footprint.Width; + int height = a.Footprint.Height; + + List<(int A, int B, int Count)> pairCounts = []; + for (int y = 0; y < 4; ++y) + { + for (int x = 0; x < 4; ++x) + { + pairCounts.Add((x, y, 0)); + } + } + + for (int y = 0; y < height; ++y) + { + for (int x = 0; x < width; ++x) + { + int idx = (y * width) + x; + int aVal = a.Assignment[idx]; + int bVal = b.Assignment[idx]; + pairCounts[(bVal * 4) + aVal] = (aVal, bVal, pairCounts[(bVal * 4) + aVal].Count + 1); + } + } + + List<(int A, int B, int Count)> sorted = [.. pairCounts.OrderByDescending(p => p.Count)]; + bool[,] assigned = new bool[maxNumSubsets, maxNumSubsets]; + int pixelsMatched = 0; + foreach ((int pairA, int pairB, int count) in sorted) + { + bool isAssigned = false; + for (int i = 0; i < maxNumSubsets; ++i) + { + if (assigned[pairA, i] || assigned[i, pairB]) + { + isAssigned = true; + break; + } + } + + if (!isAssigned) + { + assigned[pairA, pairB] = true; + pixelsMatched += count; + } + } + + return (width * height) - pixelsMatched; + } + + // Basic GetASTCPartition implementation using selection function from C++ + public static Partition GetASTCPartition(Footprint footprint, int partitionCount, int partitionId) + { + (Footprint Footprint, int PartitionCount, int PartitionId) key = (footprint, partitionCount, partitionId); + if (PartitionCache.TryGetValue(key, out Partition? cached)) + { + return cached; + } + + Partition part = new(footprint, partitionCount, partitionId); + int w = footprint.Width; + int h = footprint.Height; + int[] assignment = new int[w * h]; + int idx = 0; + for (int y = 0; y < h; ++y) + { + for (int x = 0; x < w; ++x) + { + assignment[idx++] = SelectASTCPartition(partitionId, x, y, 0, partitionCount, footprint.PixelCount); + } + } + + part.Assignment = assignment; + PartitionCache.TryAdd(key, part); + return part; + } + + // For now, implement a naive FindClosestASTCPartition that just checks a + // small set of candidate partitions generated by enum footprints. We'll + // return the same footprint size partitions with ID=0 for simplicity. + public static Partition FindClosestASTCPartition(Partition candidate) + { + // Search a few partitions and pick the one with minimal PartitionMetric + Partition best = GetASTCPartition(candidate.Footprint, Math.Max(1, candidate.PartitionCount), 0); + int bestDist = PartitionMetric(best, candidate); + return best; + } + + // Very small port of selection function; behavior taken from C++ file. + private static int SelectASTCPartition(int seed, int x, int y, int z, int partitionCount, int pixelCount) + { + if (partitionCount <= 1) + { + return 0; + } + + if (pixelCount < 31) + { + x <<= 1; + y <<= 1; + z <<= 1; + } + + seed += (partitionCount - 1) * 1024; + uint randomNumber = (uint)seed; + randomNumber ^= randomNumber >> 15; + randomNumber -= randomNumber << 17; + randomNumber += randomNumber << 7; + randomNumber += randomNumber << 4; + randomNumber ^= randomNumber >> 5; + randomNumber += randomNumber << 16; + randomNumber ^= randomNumber >> 7; + randomNumber ^= randomNumber >> 3; + randomNumber ^= randomNumber << 6; + randomNumber ^= randomNumber >> 17; + + uint seed1 = randomNumber & 0xF; + uint seed2 = (randomNumber >> 4) & 0xF; + uint seed3 = (randomNumber >> 8) & 0xF; + uint seed4 = (randomNumber >> 12) & 0xF; + uint seed5 = (randomNumber >> 16) & 0xF; + uint seed6 = (randomNumber >> 20) & 0xF; + uint seed7 = (randomNumber >> 24) & 0xF; + uint seed8 = (randomNumber >> 28) & 0xF; + uint seed9 = (randomNumber >> 18) & 0xF; + uint seed10 = (randomNumber >> 22) & 0xF; + uint seed11 = (randomNumber >> 26) & 0xF; + uint seed12 = ((randomNumber >> 30) | (randomNumber << 2)) & 0xF; + + seed1 *= seed1; + seed2 *= seed2; + seed3 *= seed3; + seed4 *= seed4; + seed5 *= seed5; + seed6 *= seed6; + seed7 *= seed7; + seed8 *= seed8; + seed9 *= seed9; + seed10 *= seed10; + seed11 *= seed11; + seed12 *= seed12; + + int sh1, sh2, sh3; + if ((seed & 1) != 0) + { + sh1 = (seed & 2) != 0 ? 4 : 5; + sh2 = (partitionCount == 3) ? 6 : 5; + } + else + { + sh1 = (partitionCount == 3) ? 6 : 5; + sh2 = (seed & 2) != 0 ? 4 : 5; + } + + sh3 = (seed & 0x10) != 0 ? sh1 : sh2; + + seed1 >>= sh1; + seed2 >>= sh2; + seed3 >>= sh1; + seed4 >>= sh2; + seed5 >>= sh1; + seed6 >>= sh2; + seed7 >>= sh1; + seed8 >>= sh2; + seed9 >>= sh3; + seed10 >>= sh3; + seed11 >>= sh3; + seed12 >>= sh3; + + int a = (int)((seed1 * x) + (seed2 * y) + (seed11 * z) + (randomNumber >> 14)); + int b = (int)((seed3 * x) + (seed4 * y) + (seed12 * z) + (randomNumber >> 10)); + int c = (int)((seed5 * x) + (seed6 * y) + (seed9 * z) + (randomNumber >> 6)); + int d = (int)((seed7 * x) + (seed8 * y) + (seed10 * z) + (randomNumber >> 2)); + + a &= 0x3F; + b &= 0x3F; + c &= 0x3F; + d &= 0x3F; + if (partitionCount <= 3) + { + d = 0; + } + + if (partitionCount <= 2) + { + c = 0; + } + + if (a >= b && a >= c && a >= d) + { + return 0; + } + else if (b >= c && b >= d) + { + return 1; + } + else if (c >= d) + { + return 2; + } + else + { + return 3; + } + } +} diff --git a/src/ImageSharp.Textures.Astc/ColorEncoding/RgbaColorExtensions.cs b/src/ImageSharp.Textures.Astc/ColorEncoding/RgbaColorExtensions.cs new file mode 100644 index 00000000..c8b7740e --- /dev/null +++ b/src/ImageSharp.Textures.Astc/ColorEncoding/RgbaColorExtensions.cs @@ -0,0 +1,54 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Textures.Astc.Core; + +namespace SixLabors.ImageSharp.Textures.Astc.ColorEncoding; + +internal static class RgbaColorExtensions +{ + /// + /// Uses the value in the blue channel to tint the red and green + /// + /// + /// Applies the 'blue_contract' function defined in Section C.2.14 of the ASTC specification. + /// + public static RgbaColor WithBlueContract(int red, int green, int blue, int alpha) + => new( + r: (red + blue) >> 1, + g: (green + blue) >> 1, + b: blue, + a: alpha); + + /// + /// Uses the value in the blue channel to tint the red and green + /// + /// + /// Applies the 'blue_contract' function defined in Section C.2.14 of the ASTC specification. + /// + public static RgbaColor WithBlueContract(this RgbaColor color) + => WithBlueContract(color.R, color.G, color.B, color.A); + + /// + /// The inverse of + /// + public static RgbaColor WithInvertedBlueContract(this RgbaColor color) + => new( + r: (2 * color.R) - color.B, + g: (2 * color.G) - color.B, + b: color.B, + a: color.A); + + public static RgbaColor AsOffsetFrom(this RgbaColor color, RgbaColor baseColor) + { + int[] offset = [color.R, color.G, color.B, color.A]; + + for (int i = 0; i < RgbaColor.BytesPerPixel; ++i) + { + (int a, int b) = BitOperations.TransferPrecision(offset[i], baseColor[i]); + offset[i] = Math.Clamp(baseColor[i] + a, byte.MinValue, byte.MaxValue); + } + + return new RgbaColor(offset[0], offset[1], offset[2], offset[3]); + } +} diff --git a/src/ImageSharp.Textures.Astc/Core/BitOperations.cs b/src/ImageSharp.Textures.Astc/Core/BitOperations.cs new file mode 100644 index 00000000..4f6f8fbc --- /dev/null +++ b/src/ImageSharp.Textures.Astc/Core/BitOperations.cs @@ -0,0 +1,107 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Textures.Astc.Core; + +internal static class BitOperations +{ + /// + /// Return the specified range as a (low bits in lower 64 bits) + /// + public static UInt128 GetBits(UInt128 value, int start, int length) + { + if (length <= 0) + { + return UInt128.Zero; + } + + UInt128 shifted = value >> start; + if (length >= 128) + { + return shifted; + } + + if (length >= 64) + { + ulong lowMask = ~0UL; + int highBits = length - 64; + ulong highMask = (highBits == 64) + ? ~0UL + : ((1UL << highBits) - 1UL); + + return new UInt128(shifted.High() & highMask, shifted.Low() & lowMask); + } + else + { + ulong mask = (length == 64) + ? ~0UL + : ((1UL << length) - 1UL); + + return new UInt128(0, shifted.Low() & mask); + } + } + + /// + /// Return the specified range as a ulong + /// + public static ulong GetBits(ulong value, int start, int length) + { + if (length <= 0) + { + return 0UL; + } + + int totalBits = sizeof(ulong) * 8; + ulong mask = length == totalBits + ? ~0UL + : ~0UL >> (totalBits - length); + + return (value >> start) & mask; + } + + /// + /// Transfers a few bits of precision from one value to another. + /// + /// + /// The 'bit_transfer_signed' function defined in Section C.2.14 of the ASTC specification + /// + public static (int A, int B) TransferPrecision(int a, int b) + { + b >>= 1; + b |= a & 0x80; + a >>= 1; + a &= 0x3F; + + if ((a & 0x20) != 0) + { + a -= 0x40; + } + + return (a, b); + } + + /// + /// Takes two values, |a| in the range [-32, 31], and |b| in the range [0, 255], + /// and returns the two values in [0, 255] that will reconstruct |a| and |b| when + /// passed to the function. + /// + public static (int A, int B) TransferPrecisionInverse(int a, int b) + { + ArgumentOutOfRangeException.ThrowIfLessThan(a, -32); + ArgumentOutOfRangeException.ThrowIfGreaterThan(a, 31); + ArgumentOutOfRangeException.ThrowIfLessThan(b, byte.MinValue); + ArgumentOutOfRangeException.ThrowIfGreaterThan(b, byte.MaxValue); + + if (a < 0) + { + a += 0x40; + } + + a <<= 1; + a |= b & 0x80; + b <<= 1; + b &= 0xff; + + return (a, b); + } +} diff --git a/src/ImageSharp.Textures.Astc/Core/DecimationInfo.cs b/src/ImageSharp.Textures.Astc/Core/DecimationInfo.cs new file mode 100644 index 00000000..d513b353 --- /dev/null +++ b/src/ImageSharp.Textures.Astc/Core/DecimationInfo.cs @@ -0,0 +1,27 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Textures.Astc.Core; + +/// +/// Pre-computed weight infill data for a specific (footprint, weightGridX, weightGridY) combination. +/// Stores bilinear interpolation indices and factors in a transposed layout. +/// +internal sealed class DecimationInfo +{ + // Transposed layout: [contribution * TexelCount + texel] + // 4 contributions per texel (bilinear interpolation from weight grid). + // For edge texels where some grid points are out of bounds, factor is 0 and index is 0. + public DecimationInfo(int texelCount, int[] weightIndices, int[] weightFactors) + { + this.TexelCount = texelCount; + this.WeightIndices = weightIndices; + this.WeightFactors = weightFactors; + } + + public int TexelCount { get; } + + public int[] WeightIndices { get; } + + public int[] WeightFactors { get; } +} diff --git a/src/ImageSharp.Textures.Astc/Core/DecimationTable.cs b/src/ImageSharp.Textures.Astc/Core/DecimationTable.cs new file mode 100644 index 00000000..5be931ff --- /dev/null +++ b/src/ImageSharp.Textures.Astc/Core/DecimationTable.cs @@ -0,0 +1,140 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.Textures.Astc.Core; + +/// +/// Caches pre-computed DecimationInfo tables and provides weight infill. +/// For each unique (footprint, gridX, gridY) combination, the bilinear interpolation +/// indices and factors are computed once and reused for every block with that configuration. +/// Uses a flat array indexed by (footprintType, gridX, gridY) for O(1) lookup. +/// +internal static class DecimationTable +{ + // Grid dimensions range from 2 to 12 inclusive + private const int GridMin = 2; + private const int GridRange = 11; // 12 - 2 + 1 + private const int FootprintCount = 14; + private static readonly DecimationInfo?[] Table = new DecimationInfo?[FootprintCount * GridRange * GridRange]; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static DecimationInfo Get(Footprint footprint, int gridX, int gridY) + { + int index = ((int)footprint.Type * GridRange * GridRange) + ((gridX - GridMin) * GridRange) + (gridY - GridMin); + DecimationInfo? decimationInfo = Table[index]; + if (decimationInfo is null) + { + decimationInfo = Compute(footprint.Width, footprint.Height, gridX, gridY); + Table[index] = decimationInfo; + } + + return decimationInfo; + } + + /// + /// Performs weight infill using pre-computed tables. + /// Maps unquantized grid weights to per-texel weights via bilinear interpolation + /// with pre-computed indices and factors. + /// + [MethodImpl(MethodImplOptions.AggressiveOptimization)] + public static void InfillWeights(ReadOnlySpan gridWeights, DecimationInfo decimationInfo, Span result) + { + int texelCount = decimationInfo.TexelCount; + int[] weightIndices = decimationInfo.WeightIndices; + int[] weightFactors = decimationInfo.WeightFactors; + int offset1 = texelCount, offset2 = texelCount * 2, offset3 = texelCount * 3; + + for (int i = 0; i < texelCount; i++) + { + result[i] = (8 + + (gridWeights[weightIndices[i]] * weightFactors[i]) + + (gridWeights[weightIndices[offset1 + i]] * weightFactors[offset1 + i]) + + (gridWeights[weightIndices[offset2 + i]] * weightFactors[offset2 + i]) + + (gridWeights[weightIndices[offset3 + i]] * weightFactors[offset3 + i])) >> 4; + } + } + + private static int GetScaleFactorD(int blockDimensions) => (int)((1024f + (blockDimensions >> 1)) / (blockDimensions - 1)); + + private static DecimationInfo Compute(int footprintWidth, int footprintHeight, int gridWidth, int gridHeight) + { + int texelCount = footprintWidth * footprintHeight; + + int[] indices = new int[4 * texelCount]; + int[] factors = new int[4 * texelCount]; + + int scaleHorizontal = GetScaleFactorD(footprintWidth); + int scaleVertical = GetScaleFactorD(footprintHeight); + int gridLimit = gridWidth * gridHeight; + int maxGridX = gridWidth - 1; + int maxGridY = gridHeight - 1; + + int texelIndex = 0; + for (int texelY = 0; texelY < footprintHeight; ++texelY) + { + int scaledY = scaleVertical * texelY; + int gridY = ((scaledY * maxGridY) + 32) >> 6; + int gridRowIndex = gridY >> 4; + int fractionY = gridY & 0xF; + + for (int texelX = 0; texelX < footprintWidth; ++texelX) + { + int scaledX = scaleHorizontal * texelX; + int gridX = ((scaledX * maxGridX) + 32) >> 6; + int gridColIndex = gridX >> 4; + int fractionX = gridX & 0xF; + + int gridPoint0 = gridColIndex + (gridWidth * gridRowIndex); + int gridPoint1 = gridPoint0 + 1; + int gridPoint2 = gridColIndex + (gridWidth * (gridRowIndex + 1)); + int gridPoint3 = gridPoint2 + 1; + + int factor3 = ((fractionX * fractionY) + 8) >> 4; + int factor2 = fractionY - factor3; + int factor1 = fractionX - factor3; + int factor0 = 16 - fractionX - fractionY + factor3; + + // For out-of-bounds grid points, zero the factor and use index 0 (safe dummy) + if (gridPoint3 >= gridLimit) + { + factor3 = 0; + gridPoint3 = 0; + } + + if (gridPoint2 >= gridLimit) + { + factor2 = 0; + gridPoint2 = 0; + } + + if (gridPoint1 >= gridLimit) + { + factor1 = 0; + gridPoint1 = 0; + } + + if (gridPoint0 >= gridLimit) + { + factor0 = 0; + gridPoint0 = 0; + } + + indices[(0 * texelCount) + texelIndex] = gridPoint0; + indices[(1 * texelCount) + texelIndex] = gridPoint1; + indices[(2 * texelCount) + texelIndex] = gridPoint2; + indices[(3 * texelCount) + texelIndex] = gridPoint3; + + factors[(0 * texelCount) + texelIndex] = factor0; + factors[(1 * texelCount) + texelIndex] = factor1; + factors[(2 * texelCount) + texelIndex] = factor2; + factors[(3 * texelCount) + texelIndex] = factor3; + + texelIndex++; + } + } + + return new DecimationInfo(texelCount, indices, factors); + } +} diff --git a/src/ImageSharp.Textures.Astc/Core/Footprint.cs b/src/ImageSharp.Textures.Astc/Core/Footprint.cs new file mode 100644 index 00000000..3ccd7027 --- /dev/null +++ b/src/ImageSharp.Textures.Astc/Core/Footprint.cs @@ -0,0 +1,85 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Textures.Astc.Core; + +/// +/// Represents the dimensions of an ASTC block footprint. +/// +public readonly record struct Footprint +{ + private Footprint(FootprintType type, int width, int height) + { + ArgumentOutOfRangeException.ThrowIfNegative(width); + ArgumentOutOfRangeException.ThrowIfNegative(height); + + this.Type = type; + this.Width = width; + this.Height = height; + this.PixelCount = width * height; + } + + /// Gets the block width in texels. + public int Width { get; } + + /// Gets the block height in texels. + public int Height { get; } + + /// Gets the footprint type enum value. + public FootprintType Type { get; } + + /// Gets the total number of texels in the block (Width * Height). + public int PixelCount { get; } + + /// + /// Creates a from the specified . + /// + /// The footprint type to create a footprint from. + /// A matching the specified type. + public static Footprint FromFootprintType(FootprintType type) => type switch + { + FootprintType.Footprint4x4 => Get4x4(), + FootprintType.Footprint5x4 => Get5x4(), + FootprintType.Footprint5x5 => Get5x5(), + FootprintType.Footprint6x5 => Get6x5(), + FootprintType.Footprint6x6 => Get6x6(), + FootprintType.Footprint8x5 => Get8x5(), + FootprintType.Footprint8x6 => Get8x6(), + FootprintType.Footprint8x8 => Get8x8(), + FootprintType.Footprint10x5 => Get10x5(), + FootprintType.Footprint10x6 => Get10x6(), + FootprintType.Footprint10x8 => Get10x8(), + FootprintType.Footprint10x10 => Get10x10(), + FootprintType.Footprint12x10 => Get12x10(), + FootprintType.Footprint12x12 => Get12x12(), + _ => throw new ArgumentOutOfRangeException($"Invalid FootprintType: {type}"), + }; + + internal static Footprint Get4x4() => new(FootprintType.Footprint4x4, 4, 4); + + internal static Footprint Get5x4() => new(FootprintType.Footprint5x4, 5, 4); + + internal static Footprint Get5x5() => new(FootprintType.Footprint5x5, 5, 5); + + internal static Footprint Get6x5() => new(FootprintType.Footprint6x5, 6, 5); + + internal static Footprint Get6x6() => new(FootprintType.Footprint6x6, 6, 6); + + internal static Footprint Get8x5() => new(FootprintType.Footprint8x5, 8, 5); + + internal static Footprint Get8x6() => new(FootprintType.Footprint8x6, 8, 6); + + internal static Footprint Get8x8() => new(FootprintType.Footprint8x8, 8, 8); + + internal static Footprint Get10x5() => new(FootprintType.Footprint10x5, 10, 5); + + internal static Footprint Get10x6() => new(FootprintType.Footprint10x6, 10, 6); + + internal static Footprint Get10x8() => new(FootprintType.Footprint10x8, 10, 8); + + internal static Footprint Get10x10() => new(FootprintType.Footprint10x10, 10, 10); + + internal static Footprint Get12x10() => new(FootprintType.Footprint12x10, 12, 10); + + internal static Footprint Get12x12() => new(FootprintType.Footprint12x12, 12, 12); +} diff --git a/src/ImageSharp.Textures.Astc/Core/FootprintType.cs b/src/ImageSharp.Textures.Astc/Core/FootprintType.cs new file mode 100644 index 00000000..65d983cb --- /dev/null +++ b/src/ImageSharp.Textures.Astc/Core/FootprintType.cs @@ -0,0 +1,52 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Textures.Astc.Core; + +/// +/// The supported ASTC block footprint sizes. +/// +public enum FootprintType +{ + /// 4x4 texel block. + Footprint4x4, + + /// 5x4 texel block. + Footprint5x4, + + /// 5x5 texel block. + Footprint5x5, + + /// 6x5 texel block. + Footprint6x5, + + /// 6x6 texel block. + Footprint6x6, + + /// 8x5 texel block. + Footprint8x5, + + /// 8x6 texel block. + Footprint8x6, + + /// 8x8 texel block. + Footprint8x8, + + /// 10x5 texel block. + Footprint10x5, + + /// 10x6 texel block. + Footprint10x6, + + /// 10x8 texel block. + Footprint10x8, + + /// 10x10 texel block. + Footprint10x10, + + /// 12x10 texel block. + Footprint12x10, + + /// 12x12 texel block. + Footprint12x12, +} diff --git a/src/ImageSharp.Textures.Astc/Core/RgbColor.cs b/src/ImageSharp.Textures.Astc/Core/RgbColor.cs new file mode 100644 index 00000000..19d5e4c4 --- /dev/null +++ b/src/ImageSharp.Textures.Astc/Core/RgbColor.cs @@ -0,0 +1,63 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Textures.Astc.Core; + +internal readonly record struct RgbColor(byte R, byte G, byte B) +{ + public RgbColor(int r, int g, int b) + : this( + (byte)Math.Clamp(r, byte.MinValue, byte.MaxValue), + (byte)Math.Clamp(g, byte.MinValue, byte.MaxValue), + (byte)Math.Clamp(b, byte.MinValue, byte.MaxValue)) + { + } + + public static int BytesPerPixel => 3; + + public static RgbColor Empty => default; + + /// + /// Gets the rounded arithmetic mean of the R, G, and B channels. + /// + public byte Average + { + get + { + int sum = this.R + this.G + this.B; + return (byte)(((sum * 256) + 384) / 768); + } + } + + public int this[int i] + => i switch + { + 0 => this.R, + 1 => this.G, + 2 => this.B, + _ => throw new ArgumentOutOfRangeException(nameof(i), $"Index must be between 0 and {BytesPerPixel - 1}. Actual value: {i}.") + }; + + public static int SquaredError(RgbColor a, RgbColor b) + { + int result = 0; + for (int i = 0; i < BytesPerPixel; i++) + { + int diff = a[i] - b[i]; + result += diff * diff; + } + + return result; + } + + /// + /// Computes the squared error comparing only the RGB channels of two RgbaColors. + /// + public static int SquaredError(RgbaColor a, RgbaColor b) + { + int dr = a.R - b.R; + int dg = a.G - b.G; + int db = a.B - b.B; + return (dr * dr) + (dg * dg) + (db * db); + } +} diff --git a/src/ImageSharp.Textures.Astc/Core/RgbaColor.cs b/src/ImageSharp.Textures.Astc/Core/RgbaColor.cs new file mode 100644 index 00000000..69824fdf --- /dev/null +++ b/src/ImageSharp.Textures.Astc/Core/RgbaColor.cs @@ -0,0 +1,60 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Textures.Astc.Core; + +internal readonly record struct RgbaColor(byte R, byte G, byte B, byte A) +{ + public RgbaColor(int r, int g, int b, int a = byte.MaxValue) + : this( + (byte)Math.Clamp(r, byte.MinValue, byte.MaxValue), + (byte)Math.Clamp(g, byte.MinValue, byte.MaxValue), + (byte)Math.Clamp(b, byte.MinValue, byte.MaxValue), + (byte)Math.Clamp(a, byte.MinValue, byte.MaxValue)) + { + } + + public static int BytesPerPixel => 4; + + public static RgbaColor Empty => default; + + /// + /// Gets the rounded arithmetic mean of the R, G, and B channels. + /// + public byte Average + { + get + { + int sum = this.R + this.G + this.B; + return (byte)(((sum * 256) + 384) / 768); + } + } + + public int this[int i] + => i switch + { + 0 => this.R, + 1 => this.G, + 2 => this.B, + 3 => this.A, + _ => throw new ArgumentOutOfRangeException(nameof(i), $"Index must be between 0 and {BytesPerPixel - 1}. Actual value: {i}.") + }; + + public static int SquaredError(RgbaColor a, RgbaColor b) + { + int result = 0; + for (int i = 0; i < BytesPerPixel; i++) + { + int diff = a[i] - b[i]; + result += diff * diff; + } + + return result; + } + + public bool IsCloseTo(RgbaColor other, int tolerance) + => Math.Abs(this.R - other.R) <= tolerance && + Math.Abs(this.G - other.G) <= tolerance && + Math.Abs(this.B - other.B) <= tolerance && + Math.Abs(this.A - other.A) <= tolerance; +} diff --git a/src/ImageSharp.Textures.Astc/Core/RgbaHdrColor.cs b/src/ImageSharp.Textures.Astc/Core/RgbaHdrColor.cs new file mode 100644 index 00000000..f73b810d --- /dev/null +++ b/src/ImageSharp.Textures.Astc/Core/RgbaHdrColor.cs @@ -0,0 +1,51 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Textures.Astc.Core; + +/// +/// Represents an HDR (High Dynamic Range) color with 16-bit per-channel precision. +/// +/// +/// HDR colors use ushort values (0-65535) for each channel, allowing representation +/// of values beyond the standard 0-255 LDR range. This enables encoding of High Dynamic +/// Range content that can represent brightness values exceeding the typical white point. +/// +internal readonly record struct RgbaHdrColor(ushort R, ushort G, ushort B, ushort A) +{ + public static RgbaHdrColor Empty => default; + + /// + /// Indexer to access channels by index: 0=R, 1=G, 2=B, 3=A + /// + public ushort this[int i] => i switch + { + 0 => this.R, + 1 => this.G, + 2 => this.B, + 3 => this.A, + _ => throw new ArgumentOutOfRangeException(nameof(i), $"Index must be between 0 and 3. Actual value: {i}.") + }; + + /// + /// Converts an LDR color (0-255) to HDR range (0-65535). + /// + public static RgbaHdrColor FromRgba(RgbaColor ldr) + => new((ushort)(ldr.R * 257), (ushort)(ldr.G * 257), (ushort)(ldr.B * 257), (ushort)(ldr.A * 257)); + + /// + /// Converts an HDR color (0-65535) to LDR range (0-255). + /// + /// + /// Values are clamped to 0-255 range, so HDR values exceeding + /// the standard white point will be clipped. + /// + public RgbaColor ToLowDynamicRange() + => new((byte)(this.R >> 8), (byte)(this.G >> 8), (byte)(this.B >> 8), (byte)(this.A >> 8)); + + public bool IsCloseTo(RgbaHdrColor other, int tolerance) + => Math.Abs(this.R - other.R) <= tolerance && + Math.Abs(this.G - other.G) <= tolerance && + Math.Abs(this.B - other.B) <= tolerance && + Math.Abs(this.A - other.A) <= tolerance; +} diff --git a/src/ImageSharp.Textures.Astc/Core/SimdHelpers.cs b/src/ImageSharp.Textures.Astc/Core/SimdHelpers.cs new file mode 100644 index 00000000..bc6f7448 --- /dev/null +++ b/src/ImageSharp.Textures.Astc/Core/SimdHelpers.cs @@ -0,0 +1,175 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; + +namespace SixLabors.ImageSharp.Textures.Astc.Core; + +internal static class SimdHelpers +{ + private static readonly Vector128 Vec32 = Vector128.Create(32); + private static readonly Vector128 Vec64 = Vector128.Create(64); + private static readonly Vector128 Vec255 = Vector128.Create(255); + private static readonly Vector128 Vec32767 = Vector128.Create(32767); + + /// + /// Interpolates one channel for 4 pixels simultaneously. + /// All 4 pixels share the same endpoint values but have different weights. + /// Returns 4 byte results packed into the lower bytes of a . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 Interpolate4ChannelPixels(int p0, int p1, Vector128 weights) + { + // Bit-replicate endpoint bytes to 16-bit + Vector128 c0 = Vector128.Create((p0 << 8) | p0); + Vector128 c1 = Vector128.Create((p1 << 8) | p1); + + // c = (c0 * (64 - w) + c1 * w + 32) >> 6 + // NOTE: Using >> 6 instead of / 64 because Vector128 division + // has no hardware support and decomposes to scalar operations. + Vector128 w64 = Vec64 - weights; + Vector128 c = ((c0 * w64) + (c1 * weights) + Vec32) >> 6; + + // Quantize: (c * 255 + 32767) >> 16, clamped to [0, 255] + Vector128 result = ((c * Vec255) + Vec32767) >>> 16; + return Vector128.Min(Vector128.Max(result, Vector128.Zero), Vec255); + } + + /// + /// Writes 4 LDR pixels directly to output buffer using SIMD. + /// Processes each channel across 4 pixels in parallel, then interleaves to RGBA output. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Write4PixelLdr( + Span output, + int offset, + int lowR, + int lowG, + int lowB, + int lowA, + int highR, + int highG, + int highB, + int highA, + Vector128 weights) + { + Vector128 r = Interpolate4ChannelPixels(lowR, highR, weights); + Vector128 g = Interpolate4ChannelPixels(lowG, highG, weights); + Vector128 b = Interpolate4ChannelPixels(lowB, highB, weights); + Vector128 a = Interpolate4ChannelPixels(lowA, highA, weights); + + // Pack 4 RGBA pixels into 16 bytes via vector OR+shift. + // Each int element has its channel value in bits [0:7]. + // Combine: element[i] = R[i] | (G[i] << 8) | (B[i] << 16) | (A[i] << 24) + // On little-endian, storing this int32 writes bytes [R, G, B, A]. + Vector128 rgba = r | (g << 8) | (b << 16) | (a << 24); + rgba.AsByte().CopyTo(output.Slice(offset, 16)); + } + + /// + /// Scalar single-pixel LDR interpolation, writing directly to buffer. + /// No RgbaColor allocation. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteSinglePixelLdr( + Span output, + int offset, + int lowR, + int lowG, + int lowB, + int lowA, + int highR, + int highG, + int highB, + int highA, + int weight) + { + output[offset + 0] = (byte)InterpolateChannelScalar(lowR, highR, weight); + output[offset + 1] = (byte)InterpolateChannelScalar(lowG, highG, weight); + output[offset + 2] = (byte)InterpolateChannelScalar(lowB, highB, weight); + output[offset + 3] = (byte)InterpolateChannelScalar(lowA, highA, weight); + } + + /// + /// Scalar single-pixel dual-plane LDR interpolation, writing directly to buffer. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteSinglePixelLdrDualPlane( + Span output, + int offset, + int lowR, + int lowG, + int lowB, + int lowA, + int highR, + int highG, + int highB, + int highA, + int weight, + int dpChannel, + int dpWeight) + { + output[offset + 0] = (byte)InterpolateChannelScalar( + lowR, + highR, + dpChannel == 0 ? dpWeight : weight); + output[offset + 1] = (byte)InterpolateChannelScalar( + lowG, + highG, + dpChannel == 1 ? dpWeight : weight); + output[offset + 2] = (byte)InterpolateChannelScalar( + lowB, + highB, + dpChannel == 2 ? dpWeight : weight); + output[offset + 3] = (byte)InterpolateChannelScalar( + lowA, + highA, + dpChannel == 3 ? dpWeight : weight); + } + + // Keep the old API for ColorAt() (used by tests and non-hot paths) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaColor InterpolateColorLdr(RgbaColor low, RgbaColor high, int weight) + => new( + r: InterpolateChannelScalar(low.R, high.R, weight), + g: InterpolateChannelScalar(low.G, high.G, weight), + b: InterpolateChannelScalar(low.B, high.B, weight), + a: InterpolateChannelScalar(low.A, high.A, weight)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RgbaColor InterpolateColorLdrDualPlane( + RgbaColor low, + RgbaColor high, + int weight, + int dualPlaneChannel, + int dualPlaneWeight) + => new( + r: InterpolateChannelScalar( + low.R, + high.R, + dualPlaneChannel == 0 ? dualPlaneWeight : weight), + g: InterpolateChannelScalar( + low.G, + high.G, + dualPlaneChannel == 1 ? dualPlaneWeight : weight), + b: InterpolateChannelScalar( + low.B, + high.B, + dualPlaneChannel == 2 ? dualPlaneWeight : weight), + a: InterpolateChannelScalar( + low.A, + high.A, + dualPlaneChannel == 3 ? dualPlaneWeight : weight)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static int InterpolateChannelScalar(int p0, int p1, int weight) + { + int c0 = (p0 << 8) | p0; + int c1 = (p1 << 8) | p1; + int c = ((c0 * (64 - weight)) + (c1 * weight) + 32) / 64; + int quantized = ((c * 255) + 32767) / 65536; + + return Math.Clamp(quantized, 0, 255); + } +} diff --git a/src/ImageSharp.Textures.Astc/Core/UInt128Extensions.cs b/src/ImageSharp.Textures.Astc/Core/UInt128Extensions.cs new file mode 100644 index 00000000..4df89401 --- /dev/null +++ b/src/ImageSharp.Textures.Astc/Core/UInt128Extensions.cs @@ -0,0 +1,77 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Textures.Astc.Core; + +internal static class UInt128Extensions +{ + /// + /// The lower 64 bits of the value + /// + public static ulong Low(this UInt128 value) + => (ulong)(value & 0xFFFFFFFFFFFFFFFFUL); + + /// + /// The upper 64 bits of the value + /// + public static ulong High(this UInt128 value) + => (ulong)(value >> 64); + + /// + /// A mask with the lowest n bits set to 1 + /// + public static UInt128 OnesMask(int n) + { + if (n <= 0) + { + return UInt128.Zero; + } + + if (n >= 128) + { + return new UInt128(~0UL, ~0UL); + } + + if (n <= 64) + { + ulong low = (n == 64) + ? ~0UL + : ((1UL << n) - 1UL); + + return new UInt128(0UL, low); + } + else + { + int highBits = n - 64; + ulong low = ~0UL; + ulong high = (highBits == 64) + ? ~0UL + : ((1UL << highBits) - 1UL); + + return new UInt128(high, low); + } + } + + /// + /// Reverse bits across the full 128-bit value + /// + public static UInt128 ReverseBits(this UInt128 value) + { + ulong revLow = ReverseBits(value.Low()); + ulong revHigh = ReverseBits(value.High()); + + return new UInt128(revLow, revHigh); + } + + private static ulong ReverseBits(ulong x) + { + x = ((x >> 1) & 0x5555555555555555UL) | ((x & 0x5555555555555555UL) << 1); + x = ((x >> 2) & 0x3333333333333333UL) | ((x & 0x3333333333333333UL) << 2); + x = ((x >> 4) & 0x0F0F0F0F0F0F0F0FUL) | ((x & 0x0F0F0F0F0F0F0F0FUL) << 4); + x = ((x >> 8) & 0x00FF00FF00FF00FFUL) | ((x & 0x00FF00FF00FF00FFUL) << 8); + x = ((x >> 16) & 0x0000FFFF0000FFFFUL) | ((x & 0x0000FFFF0000FFFFUL) << 16); + x = (x >> 32) | (x << 32); + + return x; + } +} diff --git a/src/ImageSharp.Textures.Astc/IO/AstcFile.cs b/src/ImageSharp.Textures.Astc/IO/AstcFile.cs new file mode 100644 index 00000000..84a46481 --- /dev/null +++ b/src/ImageSharp.Textures.Astc/IO/AstcFile.cs @@ -0,0 +1,69 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Textures.Astc.Core; + +namespace SixLabors.ImageSharp.Textures.Astc.IO; + +/// +/// A very simple format consisting of a small header followed immediately +/// by the binary payload for a single image surface. +/// +/// +/// See https://github.com/ARM-software/astc-encoder/blob/main/Docs/FileFormat.md +/// +internal record AstcFile +{ + private readonly AstcFileHeader header; + private readonly byte[] blocks; + + internal AstcFile(AstcFileHeader header, byte[] blocks) + { + this.header = header; + this.blocks = blocks; + this.Footprint = this.GetFootprint(); + } + + public ReadOnlySpan Blocks => this.blocks; + + public Footprint Footprint { get; } + + public int Width => this.header.ImageWidth; + + public int Height => this.header.ImageHeight; + + public int Depth => this.header.ImageDepth; + + public static AstcFile FromMemory(byte[] data) + { + AstcFileHeader header = AstcFileHeader.FromMemory(data.AsSpan(0, AstcFileHeader.SizeInBytes)); + + // Remaining bytes are blocks; C++ reference keeps them as string; here we keep as byte[] + byte[] blocks = new byte[data.Length - AstcFileHeader.SizeInBytes]; + Array.Copy(data, AstcFileHeader.SizeInBytes, blocks, 0, blocks.Length); + + return new AstcFile(header, blocks); + } + + /// + /// Map the block dimensions in the header to a Footprint, if possible. + /// + private Footprint GetFootprint() => (this.header.BlockWidth, this.header.BlockHeight) switch + { + (4, 4) => Footprint.FromFootprintType(FootprintType.Footprint4x4), + (5, 4) => Footprint.FromFootprintType(FootprintType.Footprint5x4), + (5, 5) => Footprint.FromFootprintType(FootprintType.Footprint5x5), + (6, 5) => Footprint.FromFootprintType(FootprintType.Footprint6x5), + (6, 6) => Footprint.FromFootprintType(FootprintType.Footprint6x6), + (8, 5) => Footprint.FromFootprintType(FootprintType.Footprint8x5), + (8, 6) => Footprint.FromFootprintType(FootprintType.Footprint8x6), + (8, 8) => Footprint.FromFootprintType(FootprintType.Footprint8x8), + (10, 5) => Footprint.FromFootprintType(FootprintType.Footprint10x5), + (10, 6) => Footprint.FromFootprintType(FootprintType.Footprint10x6), + (10, 8) => Footprint.FromFootprintType(FootprintType.Footprint10x8), + (10, 10) => Footprint.FromFootprintType(FootprintType.Footprint10x10), + (12, 10) => Footprint.FromFootprintType(FootprintType.Footprint12x10), + (12, 12) => Footprint.FromFootprintType(FootprintType.Footprint12x12), + _ => throw new ArgumentOutOfRangeException($"Unsupported block dimensions: {this.header.BlockWidth}x{this.header.BlockHeight}"), + }; +} diff --git a/src/ImageSharp.Textures.Astc/IO/AstcFileHeader.cs b/src/ImageSharp.Textures.Astc/IO/AstcFileHeader.cs new file mode 100644 index 00000000..497a6ceb --- /dev/null +++ b/src/ImageSharp.Textures.Astc/IO/AstcFileHeader.cs @@ -0,0 +1,41 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Textures.Astc.IO; + +/// +/// The 16 byte ASTC file header +/// +/// +/// ASTC block and decoded image dimensions in texels. +/// +/// For 2D images the Z dimension must be set to 1. +/// +/// Note that the image is not required to be an exact multiple of the compressed block +/// size; the compressed data may include padding that is discarded during decompression. +/// +internal readonly record struct AstcFileHeader(byte BlockWidth, byte BlockHeight, byte BlockDepth, int ImageWidth, int ImageHeight, int ImageDepth) +{ + public const uint Magic = 0x5CA1AB13; + public const int SizeInBytes = 16; + + public static AstcFileHeader FromMemory(Span data) + { + ArgumentOutOfRangeException.ThrowIfLessThan(data.Length, SizeInBytes); + + // ASTC header is 16 bytes: + // - magic (4), + // - blockdim (3), + // - xsize,y,z (each 3 little-endian bytes) + uint magic = BitConverter.ToUInt32(data); + ArgumentOutOfRangeException.ThrowIfNotEqual(magic, Magic); + + return new AstcFileHeader( + BlockWidth: data[4], + BlockHeight: data[5], + BlockDepth: data[6], + ImageWidth: data[7] | (data[8] << 8) | (data[9] << 16), + ImageHeight: data[10] | (data[11] << 8) | (data[12] << 16), + ImageDepth: data[13] | (data[14] << 8) | (data[15] << 16)); + } +} diff --git a/src/ImageSharp.Textures.Astc/IO/BitStream.cs b/src/ImageSharp.Textures.Astc/IO/BitStream.cs new file mode 100644 index 00000000..e111b1eb --- /dev/null +++ b/src/ImageSharp.Textures.Astc/IO/BitStream.cs @@ -0,0 +1,188 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Globalization; +using SixLabors.ImageSharp.Textures.Astc.Core; + +namespace SixLabors.ImageSharp.Textures.Astc.IO; + +/// +/// A simple bit stream used for reading/writing arbitrary-sized chunks. +/// +internal struct BitStream +{ + private ulong low; + private ulong high; + private uint dataSize; // number of valid bits in the 128-bit buffer + + public BitStream(ulong data = 0, uint dataSize = 0) + { + this.low = data; + this.high = 0; + this.dataSize = dataSize; + } + + public BitStream(UInt128 data, uint dataSize) + { + this.low = data.Low(); + this.high = data.High(); + this.dataSize = dataSize; + } + + public readonly uint Bits => this.dataSize; + + public void PutBits(T x, int size) + where T : unmanaged + { + ulong value = x switch + { + uint ui => ui, + ulong ul => ul, + ushort us => us, + byte b => b, + _ => Convert.ToUInt64(x, CultureInfo.InvariantCulture) + }; + + if (this.dataSize + (uint)size > 128) + { + throw new InvalidOperationException("Not enough space in BitStream"); + } + + if (this.dataSize < 64) + { + int lowFree = (int)(64 - this.dataSize); + if (size <= lowFree) + { + this.low |= (value & MaskFor(size)) << (int)this.dataSize; + } + else + { + this.low |= (value & MaskFor(lowFree)) << (int)this.dataSize; + this.high |= (value >> lowFree) & MaskFor(size - lowFree); + } + } + else + { + int shift = (int)(this.dataSize - 64); + this.high |= (value & MaskFor(size)) << shift; + } + + this.dataSize += (uint)size; + } + + /// + /// Attempt to retrieve the specified number of bits from the buffer. + /// The buffer is shifted accordingly if successful. + /// + public bool TryGetBits(int count, out T bits) + where T : unmanaged + { + T? result = null; + + if (typeof(T) == typeof(UInt128)) + { + result = (T?)(object?)this.GetBitsUInt128(count); + } + else if (count <= this.dataSize) + { + ulong value = count switch + { + 0 => 0, + <= 64 => this.low & MaskFor(count), + _ => this.low + }; + + this.ShiftBuffer(count); + object boxed = Convert.ChangeType(value, typeof(T), CultureInfo.InvariantCulture); + result = (T)boxed; + } + + bits = result ?? default; + + return result is not null; + } + + public bool TryGetBits(int count, out ulong bits) + { + if (count > this.dataSize) + { + bits = 0; + return false; + } + + bits = count switch + { + 0 => 0, + <= 64 => this.low & MaskFor(count), + _ => this.low + }; + this.ShiftBuffer(count); + return true; + } + + public bool TryGetBits(int count, out uint bits) + { + if (count > this.dataSize) + { + bits = 0; + return false; + } + + bits = (uint)(count switch + { + 0 => 0UL, + <= 64 => this.low & MaskFor(count), + _ => this.low + }); + this.ShiftBuffer(count); + return true; + } + + private static ulong MaskFor(int bits) + => bits == 64 + ? ~0UL + : ((1UL << bits) - 1UL); + + private UInt128? GetBitsUInt128(int count) + { + if (count > this.dataSize) + { + return null; + } + + UInt128 result = count switch + { + 0 => UInt128.Zero, + <= 64 => (UInt128)(this.low & MaskFor(count)), + 128 => new UInt128(this.high, this.low), + _ => new UInt128( + (count - 64 == 64) ? this.high : (this.high & MaskFor(count - 64)), + this.low) + }; + + this.ShiftBuffer(count); + + return result; + } + + private void ShiftBuffer(int count) + { + if (count < 64) + { + this.low = (this.low >> count) | (this.high << (64 - count)); + this.high >>= count; + } + else if (count == 64) + { + this.low = this.high; + this.high = 0; + } + else + { + this.low = this.high >> (count - 64); + this.high = 0; + } + + this.dataSize -= (uint)count; + } +} diff --git a/src/ImageSharp.Textures.Astc/ImageSharp.Textures.Astc.csproj b/src/ImageSharp.Textures.Astc/ImageSharp.Textures.Astc.csproj new file mode 100644 index 00000000..a4d3f596 --- /dev/null +++ b/src/ImageSharp.Textures.Astc/ImageSharp.Textures.Astc.csproj @@ -0,0 +1,33 @@ + + + + + SixLabors.ImageSharp.Textures.Astc + SixLabors.ImageSharp.Textures.Astc + + + + + enable + Nullable + + + + + + net8.0;net9.0 + + + + + net8.0 + + + + + + + + + + diff --git a/src/ImageSharp.Textures.Astc/TexelBlock/BlockInfo.cs b/src/ImageSharp.Textures.Astc/TexelBlock/BlockInfo.cs new file mode 100644 index 00000000..61e847f5 --- /dev/null +++ b/src/ImageSharp.Textures.Astc/TexelBlock/BlockInfo.cs @@ -0,0 +1,363 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Textures.Astc.BiseEncoding; +using SixLabors.ImageSharp.Textures.Astc.ColorEncoding; +using SixLabors.ImageSharp.Textures.Astc.Core; + +namespace SixLabors.ImageSharp.Textures.Astc.TexelBlock; + +/// +/// Fused block info computed in a single pass from raw ASTC block bits +/// +internal struct BlockInfo +{ + private static readonly int[] WeightRanges = + [-1, -1, 1, 2, 3, 4, 5, 7, -1, -1, 9, 11, 15, 19, 23, 31]; + + private static readonly int[] ExtraCemBitsForPartition = [0, 2, 5, 8]; + + // Valid BISE endpoint ranges in descending order (only these produce valid encodings) + private static readonly int[] ValidEndpointRanges = + [255, 191, 159, 127, 95, 79, 63, 47, 39, 31, 23, 19, 15, 11, 9, 7, 5]; + + public bool IsValid; + public bool IsVoidExtent; + + // Weight grid + public int GridWidth; + public int GridHeight; + public int WeightRange; + public int WeightBitCount; + + // Partitions + public int PartitionCount; + + // Dual plane + public bool IsDualPlane; + public int DualPlaneChannel; // only valid if IsDualPlane + + // Color endpoints + public int ColorStartBit; + public int ColorBitCount; + public int ColorValuesRange; + public int ColorValuesCount; + + // Endpoint modes (up to 4 partitions) + public ColorEndpointMode EndpointMode0; + public ColorEndpointMode EndpointMode1; + public ColorEndpointMode EndpointMode2; + public ColorEndpointMode EndpointMode3; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly ColorEndpointMode GetEndpointMode(int partition) => partition switch + { + 0 => this.EndpointMode0, + 1 => this.EndpointMode1, + 2 => this.EndpointMode2, + 3 => this.EndpointMode3, + _ => this.EndpointMode0 + }; + + /// + /// Decode all block info from raw 128-bit ASTC block data in a single pass. + /// Returns a BlockInfo with IsValid=false if the block is illegal or reserved. + /// Returns a BlockInfo with IsVoidExtent=true for void extent blocks. + /// + [MethodImpl(MethodImplOptions.AggressiveOptimization)] + public static BlockInfo Decode(UInt128 bits) + { + ulong lowBits = bits.Low(); + + // ---- Step 1: Check void extent ---- + // Void extent: bits[0:9] == 0x1FC (9 bits) + if ((lowBits & 0x1FF) == 0x1FC) + { + return new BlockInfo + { + IsVoidExtent = true, + IsValid = !CheckVoidExtentIsIllegal(bits, lowBits) + }; + } + + // ---- Step 2: Decode block mode, grid dims, weight range in ONE pass ---- + // This inlines DecodeBlockMode + DecodeWeightProperties + int gridWidth, gridHeight; + bool isWidthA6HeightB6 = false; + uint rBits; // 3-bit range index component + + // bits[0:2] != 0 + if ((lowBits & 0x3) != 0) + { + ulong modeBits = (lowBits >> 2) & 0x3; // bits[2:4] + int a = (int)((lowBits >> 5) & 0x3); // bits[5:7] + + (gridWidth, gridHeight) = modeBits switch + { + 0 => ((int)((lowBits >> 7) & 0x3) + 4, a + 2), + 1 => ((int)((lowBits >> 7) & 0x3) + 8, a + 2), + 2 => (a + 2, (int)((lowBits >> 7) & 0x3) + 8), + 3 when ((lowBits >> 8) & 1) != 0 => ((int)((lowBits >> 7) & 0x1) + 2, a + 2), + 3 => (a + 2, (int)((lowBits >> 7) & 0x1) + 6), + _ => default // unreachable + }; + + // Range r[2:0] = {bit4, bit1, bit0} for these modes + rBits = (uint)(((lowBits >> 4) & 1) | (((lowBits >> 0) & 0x3) << 1)); + } + else + { + // bits[0:2] == 0 + ulong modeBits = (lowBits >> 5) & 0xF; // bits[5:9] + int a = (int)((lowBits >> 5) & 0x3); // bits[5:7] + + switch (modeBits) + { + case var _ when (modeBits & 0xC) == 0x0: + if ((lowBits & 0xF) == 0) + { + return default; // reserved block mode + } + + gridWidth = 12; + gridHeight = a + 2; + break; + case var _ when (modeBits & 0xC) == 0x4: + gridWidth = a + 2; + gridHeight = 12; + break; + case 0xC: + gridWidth = 6; + gridHeight = 10; + break; + case 0xD: + gridWidth = 10; + gridHeight = 6; + break; + case var _ when (modeBits & 0xC) == 0x8: + gridWidth = a + 6; + gridHeight = (int)((lowBits >> 9) & 0x3) + 6; + isWidthA6HeightB6 = true; + break; + default: + return default; // reserved + } + + // Range r[2:0] = {bit4, bit3, bit2} for these modes + rBits = (uint)(((lowBits >> 4) & 1) | (((lowBits >> 2) & 0x3) << 1)); + } + + // ---- Step 3: Compute weight range from r and h bits ---- + uint hBit = isWidthA6HeightB6 + ? 0u + : (uint)((lowBits >> 9) & 1); + int rangeIdx = (int)((hBit << 3) | rBits); + if ((uint)rangeIdx >= (uint)WeightRanges.Length) + { + return default; + } + + int weightRange = WeightRanges[rangeIdx]; + if (weightRange < 0) + { + return default; + } + + // ---- Step 4: Dual plane ---- + // WidthA6HeightB6 mode never has dual plane; otherwise check bit 10 + bool isDualPlane = !isWidthA6HeightB6 && ((lowBits >> 10) & 1) != 0; + + // ---- Step 5: Partition count ---- + int partitionCount = 1 + (int)((lowBits >> 11) & 0x3); + + // ---- Step 6: Validate weight count ---- + int numWeights = gridWidth * gridHeight; + if (isDualPlane) + { + numWeights *= 2; + } + + if (numWeights > 64) + { + return default; + } + + // 4 partitions + dual plane is illegal + if (partitionCount == 4 && isDualPlane) + { + return default; + } + + // ---- Step 7: Weight bit count ---- + int weightBitCount = BoundedIntegerSequenceCodec.GetBitCountForRange(numWeights, weightRange); + if (weightBitCount is < 24 or > 96) + { + return default; + } + + // ---- Step 8: Endpoint modes + extra CEM bits ---- + ColorEndpointMode cem0 = default, cem1 = default, cem2 = default, cem3 = default; + int colorValuesCount = 0; + int numExtraCEMBits = 0; + + if (partitionCount == 1) + { + cem0 = (ColorEndpointMode)((lowBits >> 13) & 0xF); + colorValuesCount = (((int)cem0 / 4) + 1) * 2; + } + else + { + // Multi-partition CEM decode + ulong sharedCemMarker = (lowBits >> 23) & 0x3; + + if (sharedCemMarker == 0) + { + // Shared CEM: all partitions use the same mode + ColorEndpointMode sharedCem = (ColorEndpointMode)((lowBits >> 25) & 0xF); + cem0 = cem1 = cem2 = cem3 = sharedCem; + for (int i = 0; i < partitionCount; i++) + { + colorValuesCount += sharedCem.GetColorValuesCount(); + } + } + else + { + // Non-shared CEM: per-partition modes + numExtraCEMBits = ExtraCemBitsForPartition[partitionCount - 1]; + + int extraCemStartPos = 128 - numExtraCEMBits - weightBitCount; + UInt128 extraCem = BitOperations.GetBits(bits, extraCemStartPos, numExtraCEMBits); + + ulong cemval = (lowBits >> 23) & 0x3F; // 6 bits starting at bit 23 + int baseCem = (int)(((cemval & 0x3) - 1) * 4); + cemval >>= 2; + + ulong combined = cemval | (extraCem.Low() << 4); + ulong cembits = combined; + + // Extract c bits (1 bit per partition) + Span c = stackalloc int[4]; + for (int i = 0; i < partitionCount; i++) + { + c[i] = (int)(cembits & 0x1); + cembits >>= 1; + } + + // Extract m bits (2 bits per partition) + for (int i = 0; i < partitionCount; i++) + { + int m = (int)(cembits & 0x3); + cembits >>= 2; + ColorEndpointMode mode = (ColorEndpointMode)(baseCem + (4 * c[i]) + m); + switch (i) + { + case 0: + cem0 = mode; + break; + case 1: + cem1 = mode; + break; + case 2: + cem2 = mode; + break; + case 3: + cem3 = mode; + break; + } + + colorValuesCount += mode.GetColorValuesCount(); + } + } + } + + if (colorValuesCount > 18) + { + return default; + } + + // ---- Step 9: Dual plane start position and channel ---- + int dualPlaneBitStartPos = 128 - weightBitCount - numExtraCEMBits; + if (isDualPlane) + { + dualPlaneBitStartPos -= 2; + } + + int dualPlaneChannel = isDualPlane + ? (int)BitOperations.GetBits(bits, dualPlaneBitStartPos, 2).Low() + : -1; + + // ---- Step 10: Color values info ---- + int colorStartBit = (partitionCount == 1) ? 17 : 29; + int maxColorBits = dualPlaneBitStartPos - colorStartBit; + + // Minimum bits needed check + int requiredColorBits = ((13 * colorValuesCount) + 4) / 5; + if (maxColorBits < requiredColorBits) + { + return default; + } + + // Find max color range that fits (only check valid BISE ranges: 17 vs up to 255) + int colorValuesRange = 0, colorBitCount = 0; + foreach (int rv in ValidEndpointRanges) + { + int bitCount = BoundedIntegerSequenceCodec.GetBitCountForRange(colorValuesCount, rv); + if (bitCount <= maxColorBits) + { + colorValuesRange = rv; + colorBitCount = bitCount; + break; + } + } + + if (colorValuesRange == 0) + { + return default; + } + + // ---- Step 11: Validate endpoint modes are not HDR for batchable checks ---- + // (HDR blocks are still valid, just flagged for downstream use) + return new BlockInfo + { + IsValid = true, + IsVoidExtent = false, + GridWidth = gridWidth, + GridHeight = gridHeight, + WeightRange = weightRange, + WeightBitCount = weightBitCount, + PartitionCount = partitionCount, + IsDualPlane = isDualPlane, + DualPlaneChannel = dualPlaneChannel, + ColorStartBit = colorStartBit, + ColorBitCount = colorBitCount, + ColorValuesRange = colorValuesRange, + ColorValuesCount = colorValuesCount, + EndpointMode0 = cem0, + EndpointMode1 = cem1, + EndpointMode2 = cem2, + EndpointMode3 = cem3, + }; + } + + /// + /// Inline void extent validation (replaces PhysicalBlock.CheckVoidExtentIsIllegal). + /// + private static bool CheckVoidExtentIsIllegal(UInt128 bits, ulong lowBits) + { + if (BitOperations.GetBits(bits, 10, 2).Low() != 0x3UL) + { + return true; + } + + int c0 = (int)BitOperations.GetBits(lowBits, 12, 13); + int c1 = (int)BitOperations.GetBits(lowBits, 25, 13); + int c2 = (int)BitOperations.GetBits(lowBits, 38, 13); + int c3 = (int)BitOperations.GetBits(lowBits, 51, 13); + + const int all1s = (1 << 13) - 1; + bool coordsAll1s = c0 == all1s && c1 == all1s && c2 == all1s && c3 == all1s; + + return !coordsAll1s && (c0 >= c1 || c2 >= c3); + } +} diff --git a/src/ImageSharp.Textures.Astc/TexelBlock/IntermediateBlock.cs b/src/ImageSharp.Textures.Astc/TexelBlock/IntermediateBlock.cs new file mode 100644 index 00000000..086aa7ed --- /dev/null +++ b/src/ImageSharp.Textures.Astc/TexelBlock/IntermediateBlock.cs @@ -0,0 +1,284 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Textures.Astc.BiseEncoding; +using SixLabors.ImageSharp.Textures.Astc.ColorEncoding; +using SixLabors.ImageSharp.Textures.Astc.Core; +using SixLabors.ImageSharp.Textures.Astc.IO; + +namespace SixLabors.ImageSharp.Textures.Astc.TexelBlock; + +internal static class IntermediateBlock +{ + // From Table C.2.7 -- valid weight ranges + public static readonly int[] ValidWeightRanges = [1, 2, 3, 4, 5, 7, 9, 11, 15, 19, 23, 31]; + + // Returns the maximum endpoint value range or negative on error + private const int EndpointRangeInvalidWeightDimensions = -1; + private const int EndpointRangeNotEnoughColorBits = -2; + + public static IntermediateBlockData? UnpackIntermediateBlock(PhysicalBlock physicalBlock) + { + if (physicalBlock.IsIllegalEncoding || physicalBlock.IsVoidExtent) + { + return null; + } + + BlockInfo info = BlockInfo.Decode(physicalBlock.BlockBits); + if (!info.IsValid || info.IsVoidExtent) + { + return null; + } + + return UnpackIntermediateBlock(physicalBlock.BlockBits, in info); + } + + /// + /// Fast overload that uses pre-computed BlockInfo instead of calling PhysicalBlock getters. + /// + public static IntermediateBlockData? UnpackIntermediateBlock(UInt128 bits, in BlockInfo info) + { + if (!info.IsValid || info.IsVoidExtent) + { + return null; + } + + IntermediateBlockData data = default; + + // Use cached values from BlockInfo instead of PhysicalBlock getters + UInt128 colorBitMask = UInt128Extensions.OnesMask(info.ColorBitCount); + UInt128 colorBits = (bits >> info.ColorStartBit) & colorBitMask; + BitStream colorBitStream = new(colorBits, 128); + + BoundedIntegerSequenceDecoder colorDecoder = BoundedIntegerSequenceDecoder.GetCached(info.ColorValuesRange); + Span colors = stackalloc int[info.ColorValuesCount]; + colorDecoder.Decode(info.ColorValuesCount, ref colorBitStream, colors); + + data.WeightGridX = info.GridWidth; + data.WeightGridY = info.GridHeight; + data.WeightRange = info.WeightRange; + + data.PartitionId = info.PartitionCount > 1 + ? (int)BitOperations.GetBits(bits.Low(), 13, 10) + : null; + + data.DualPlaneChannel = info.IsDualPlane + ? info.DualPlaneChannel + : null; + + int colorIndex = 0; + data.EndpointCount = info.PartitionCount; + for (int i = 0; i < info.PartitionCount; ++i) + { + ColorEndpointMode mode = info.GetEndpointMode(i); + int colorCount = mode.GetColorValuesCount(); + IntermediateEndpointData ep = new() + { Mode = mode, ColorCount = colorCount }; + for (int j = 0; j < colorCount; ++j) + { + ep.Colors[j] = colors[colorIndex++]; + } + + data.Endpoints[i] = ep; + } + + data.EndpointRange = info.ColorValuesRange; + + UInt128 weightBits = UInt128Extensions.ReverseBits(bits) & UInt128Extensions.OnesMask(info.WeightBitCount); + BitStream weightBitStream = new(weightBits, 128); + + BoundedIntegerSequenceDecoder weightDecoder = BoundedIntegerSequenceDecoder.GetCached(data.WeightRange); + int weightsCount = data.WeightGridX * data.WeightGridY; + if (info.IsDualPlane) + { + weightsCount *= 2; + } + + data.Weights = new int[weightsCount]; + data.WeightsCount = weightsCount; + weightDecoder.Decode(weightsCount, ref weightBitStream, data.Weights); + + return data; + } + + public static int EndpointRangeForBlock(in IntermediateBlockData data) + { + int dualPlaneMultiplier = data.DualPlaneChannel.HasValue + ? 2 + : 1; + if (BoundedIntegerSequenceCodec.GetBitCountForRange(data.WeightGridX * data.WeightGridY * dualPlaneMultiplier, data.WeightRange) > 96) + { + return EndpointRangeInvalidWeightDimensions; + } + + int partitionCount = data.EndpointCount; + int bitsWrittenCount = 11 + 2 + + ((partitionCount > 1) ? 10 : 0) + + ((partitionCount == 1) ? 4 : 6); + int availableColorBitsCount = ExtraConfigBitPosition(data) - bitsWrittenCount; + + int colorValuesCount = 0; + for (int i = 0; i < data.EndpointCount; i++) + { + colorValuesCount += data.Endpoints[i].Mode.GetColorValuesCount(); + } + + int bitsNeededCount = ((13 * colorValuesCount) + 4) / 5; + if (availableColorBitsCount < bitsNeededCount) + { + return EndpointRangeNotEnoughColorBits; + } + + int colorValueRange = byte.MaxValue; + for (; colorValueRange > 1; --colorValueRange) + { + int bitCountForRange = BoundedIntegerSequenceCodec.GetBitCountForRange(colorValuesCount, colorValueRange); + if (bitCountForRange <= availableColorBitsCount) + { + break; + } + } + + return colorValueRange; + } + + public static VoidExtentData? UnpackVoidExtent(PhysicalBlock physicalBlock) + { + int? colorStartBit = physicalBlock.GetColorStartBit(); + int? colorBitCount = physicalBlock.GetColorBitCount(); + if (physicalBlock.IsIllegalEncoding || !physicalBlock.IsVoidExtent || colorStartBit is null || colorBitCount is null) + { + return null; + } + + UInt128 colorBits = (physicalBlock.BlockBits >> colorStartBit.Value) & UInt128Extensions.OnesMask(colorBitCount.Value); + + // We expect low 64 bits contain the 4x16-bit channels + ulong low = colorBits.Low(); + + VoidExtentData data = default; + + // Bit 9 of the block mode indicates HDR (1) vs LDR (0) void extent + data.IsHdr = (physicalBlock.BlockBits.Low() & (1UL << 9)) != 0; + data.R = (ushort)((low >> 0) & 0xFFFF); + data.G = (ushort)((low >> 16) & 0xFFFF); + data.B = (ushort)((low >> 32) & 0xFFFF); + data.A = (ushort)((low >> 48) & 0xFFFF); + + int[]? coords = physicalBlock.GetVoidExtentCoordinates(); + data.Coords = new ushort[4]; + if (coords != null) + { + data.Coords[0] = (ushort)coords[0]; + data.Coords[1] = (ushort)coords[1]; + data.Coords[2] = (ushort)coords[2]; + data.Coords[3] = (ushort)coords[3]; + } + else + { + ushort allOnes = (1 << 13) - 1; + for (int i = 0; i < 4; ++i) + { + data.Coords[i] = allOnes; + } + } + + return data; + } + + /// + /// Determines if all endpoint modes in the intermediate block data are the same + /// + internal static bool SharedEndpointModes(in IntermediateBlockData data) + { + if (data.EndpointCount == 0) + { + return true; + } + + ColorEndpointMode first = data.Endpoints[0].Mode; + for (int i = 1; i < data.EndpointCount; i++) + { + if (data.Endpoints[i].Mode != first) + { + return false; + } + } + + return true; + } + + internal static int ExtraConfigBitPosition(in IntermediateBlockData data) + { + bool hasDualChannel = data.DualPlaneChannel.HasValue; + int weightCount = data.WeightGridX * data.WeightGridY * (hasDualChannel ? 2 : 1); + int weightBitCount = BoundedIntegerSequenceCodec.GetBitCountForRange(weightCount, data.WeightRange); + + int extraConfigBitCount = 0; + if (!SharedEndpointModes(data)) + { + int encodedCemBitCount = 2 + (data.EndpointCount * 3); + extraConfigBitCount = encodedCemBitCount - 6; + } + + if (hasDualChannel) + { + extraConfigBitCount += 2; + } + + return 128 - weightBitCount - extraConfigBitCount; + } + + internal struct VoidExtentData + { + public bool IsHdr; + public ushort R; + public ushort G; + public ushort B; + public ushort A; + public ushort[] Coords; // length 4 + } + + [System.Runtime.CompilerServices.InlineArray(MaxColorValues)] + internal struct EndpointColorValues + { + public const int MaxColorValues = 8; +#pragma warning disable CS0169, S1144 // Accessed by runtime via [InlineArray] + private int element0; +#pragma warning restore CS0169, S1144 + } + + internal struct IntermediateBlockData + { + public int WeightGridX; + public int WeightGridY; + public int WeightRange; + + public int[] Weights; + public int WeightsCount; + + public int? PartitionId; + public int? DualPlaneChannel; + + public IntermediateEndpointBuffer Endpoints; + public int EndpointCount; + + public int? EndpointRange; + } + + internal struct IntermediateEndpointData + { + public ColorEndpointMode Mode; + public EndpointColorValues Colors; + public int ColorCount; + } + + [System.Runtime.CompilerServices.InlineArray(MaxPartitions)] + internal struct IntermediateEndpointBuffer + { + public const int MaxPartitions = 4; +#pragma warning disable CS0169, S1144 // Accessed by runtime via [InlineArray] + private IntermediateEndpointData element0; +#pragma warning restore CS0169, S1144 + } +} diff --git a/src/ImageSharp.Textures.Astc/TexelBlock/IntermediateBlockPacker.cs b/src/ImageSharp.Textures.Astc/TexelBlock/IntermediateBlockPacker.cs new file mode 100644 index 00000000..df157e29 --- /dev/null +++ b/src/ImageSharp.Textures.Astc/TexelBlock/IntermediateBlockPacker.cs @@ -0,0 +1,403 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Textures.Astc.BiseEncoding; +using SixLabors.ImageSharp.Textures.Astc.Core; +using SixLabors.ImageSharp.Textures.Astc.IO; + +namespace SixLabors.ImageSharp.Textures.Astc.TexelBlock; + +internal static class IntermediateBlockPacker +{ + private static readonly BlockModeInfo[] BlockModeInfoTable = [ + new BlockModeInfo { MinWeightGridDimX = 4, MaxWeightGridDimX = 7, MinWeightGridDimY = 2, MaxWeightGridDimY = 5, R0BitPos = 4, R1BitPos = 0, R2BitPos = 1, WeightGridXOffsetBitPos = 7, WeightGridYOffsetBitPos = 5, RequireSinglePlaneLowPrec = false }, + new BlockModeInfo { MinWeightGridDimX = 8, MaxWeightGridDimX = 11, MinWeightGridDimY = 2, MaxWeightGridDimY = 5, R0BitPos = 4, R1BitPos = 0, R2BitPos = 1, WeightGridXOffsetBitPos = 7, WeightGridYOffsetBitPos = 5, RequireSinglePlaneLowPrec = false }, + new BlockModeInfo { MinWeightGridDimX = 2, MaxWeightGridDimX = 5, MinWeightGridDimY = 8, MaxWeightGridDimY = 11, R0BitPos = 4, R1BitPos = 0, R2BitPos = 1, WeightGridXOffsetBitPos = 5, WeightGridYOffsetBitPos = 7, RequireSinglePlaneLowPrec = false }, + new BlockModeInfo { MinWeightGridDimX = 2, MaxWeightGridDimX = 5, MinWeightGridDimY = 6, MaxWeightGridDimY = 7, R0BitPos = 4, R1BitPos = 0, R2BitPos = 1, WeightGridXOffsetBitPos = 5, WeightGridYOffsetBitPos = 7, RequireSinglePlaneLowPrec = false }, + new BlockModeInfo { MinWeightGridDimX = 2, MaxWeightGridDimX = 3, MinWeightGridDimY = 2, MaxWeightGridDimY = 5, R0BitPos = 4, R1BitPos = 0, R2BitPos = 1, WeightGridXOffsetBitPos = 7, WeightGridYOffsetBitPos = 5, RequireSinglePlaneLowPrec = false }, + new BlockModeInfo { MinWeightGridDimX = 12, MaxWeightGridDimX = 12, MinWeightGridDimY = 2, MaxWeightGridDimY = 5, R0BitPos = 4, R1BitPos = 2, R2BitPos = 3, WeightGridXOffsetBitPos = -1, WeightGridYOffsetBitPos = 5, RequireSinglePlaneLowPrec = false }, + new BlockModeInfo { MinWeightGridDimX = 2, MaxWeightGridDimX = 5, MinWeightGridDimY = 12, MaxWeightGridDimY = 12, R0BitPos = 4, R1BitPos = 2, R2BitPos = 3, WeightGridXOffsetBitPos = 5, WeightGridYOffsetBitPos = -1, RequireSinglePlaneLowPrec = false }, + new BlockModeInfo { MinWeightGridDimX = 6, MaxWeightGridDimX = 6, MinWeightGridDimY = 10, MaxWeightGridDimY = 10, R0BitPos = 4, R1BitPos = 2, R2BitPos = 3, WeightGridXOffsetBitPos = -1, WeightGridYOffsetBitPos = -1, RequireSinglePlaneLowPrec = false }, + new BlockModeInfo { MinWeightGridDimX = 10, MaxWeightGridDimX = 10, MinWeightGridDimY = 6, MaxWeightGridDimY = 6, R0BitPos = 4, R1BitPos = 2, R2BitPos = 3, WeightGridXOffsetBitPos = -1, WeightGridYOffsetBitPos = -1, RequireSinglePlaneLowPrec = false }, + new BlockModeInfo { MinWeightGridDimX = 6, MaxWeightGridDimX = 9, MinWeightGridDimY = 6, MaxWeightGridDimY = 9, R0BitPos = 4, R1BitPos = 2, R2BitPos = 3, WeightGridXOffsetBitPos = 5, WeightGridYOffsetBitPos = 9, RequireSinglePlaneLowPrec = true } + ]; + + private static readonly uint[] BlockModeMasks = [0x0u, 0x4u, 0x8u, 0xCu, 0x10Cu, 0x0u, 0x80u, 0x180u, 0x1A0u, 0x100u]; + + public static (string? Error, UInt128 PhysicalBlockBits) Pack(in IntermediateBlock.IntermediateBlockData data) + { + UInt128 physicalBlockBits = 0; + int expectedWeightsCount = data.WeightGridX * data.WeightGridY + * (data.DualPlaneChannel.HasValue ? 2 : 1); + int actualWeightsCount = data.WeightsCount > 0 + ? data.WeightsCount + : (data.Weights?.Length ?? 0); + if (actualWeightsCount != expectedWeightsCount) + { + return ("Incorrect number of weights!", 0); + } + + BitStream bitSink = new(0UL, 0); + + // First we need to encode the block mode. + string? errorMessage = PackBlockMode(data.WeightGridX, data.WeightGridY, data.WeightRange, data.DualPlaneChannel.HasValue, ref bitSink); + if (errorMessage != null) + { + return (errorMessage, 0); + } + + // number of partitions minus one + int partitionCount = data.EndpointCount; + bitSink.PutBits((uint)(partitionCount - 1), 2); + + if (partitionCount > 1) + { + int id = data.PartitionId ?? 0; + ArgumentOutOfRangeException.ThrowIfLessThan(id, 0); + bitSink.PutBits((uint)id, 10); + } + + (BitStream weightSink, int weightBitsCount) = EncodeWeights(data); + + (string? error, int extraConfig) = EncodeColorEndpointModes(data, partitionCount, ref bitSink); + if (error != null) + { + return (error, 0); + } + + int colorValueRange = data.EndpointRange ?? IntermediateBlock.EndpointRangeForBlock(data); + if (colorValueRange == -1) + { + throw new InvalidOperationException($"{nameof(colorValueRange)} must not be EndpointRangeInvalidWeightDimensions"); + } + + if (colorValueRange == -2) + { + return ("Intermediate block emits illegal color range", 0); + } + + BoundedIntegerSequenceEncoder colorEncoder = new(colorValueRange); + for (int i = 0; i < data.EndpointCount; i++) + { + IntermediateBlock.IntermediateEndpointData ep = data.Endpoints[i]; + for (int j = 0; j < ep.ColorCount; j++) + { + int color = ep.Colors[j]; + if (color > colorValueRange) + { + return ("Color outside available color range!", 0); + } + + colorEncoder.AddValue(color); + } + } + + colorEncoder.Encode(ref bitSink); + + int extraConfigBitPosition = IntermediateBlock.ExtraConfigBitPosition(data); + int extraConfigBits = 128 - weightBitsCount - extraConfigBitPosition; + + ArgumentOutOfRangeException.ThrowIfNegative(extraConfigBits); + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(extraConfig, 1 << extraConfigBits); + + int bitsToSkip = extraConfigBitPosition - (int)bitSink.Bits; + ArgumentOutOfRangeException.ThrowIfNegative(bitsToSkip); + while (bitsToSkip > 0) + { + int skipping = Math.Min(32, bitsToSkip); + bitSink.PutBits(0u, skipping); + bitsToSkip -= skipping; + } + + if (extraConfigBits > 0) + { + bitSink.PutBits((uint)extraConfig, extraConfigBits); + } + + ArgumentOutOfRangeException.ThrowIfNotEqual(bitSink.Bits, 128u - weightBitsCount); + + // Flush out the bit writer + if (!bitSink.TryGetBits(128 - weightBitsCount, out UInt128 astcBits)) + { + throw new InvalidOperationException(); + } + + if (!weightSink.TryGetBits(weightBitsCount, out UInt128 revWeightBits)) + { + throw new InvalidOperationException(); + } + + UInt128 combined = astcBits | UInt128Extensions.ReverseBits(revWeightBits); + physicalBlockBits = combined; + + PhysicalBlock block = PhysicalBlock.Create(physicalBlockBits); + string? illegal = block.IdentifyInvalidEncodingIssues(); + + return (illegal, physicalBlockBits); + } + + public static (string? Error, UInt128 PhysicalBlockBits) Pack(IntermediateBlock.VoidExtentData data) + { + // Pack void extent + // Assemble the 128-bit value explicitly: low 64 bits = RGBA (4x16) + // high 64 bits = 12-bit header (0xDFC) followed by four 13-bit coords. + ulong high64 = ((ulong)data.A << 48) | ((ulong)data.B << 32) | ((ulong)data.G << 16) | data.R; + ulong low64 = 0UL; + + // Header occupies lowest 12 bits of the high word + low64 |= 0xDFCu; + for (int i = 0; i < 4; ++i) + { + low64 |= ((ulong)(data.Coords[i] & 0x1FFF)) << (12 + (13 * i)); + } + + UInt128 physicalBlockBits; + + // Decide representation: if the RGBA low word is zero we emit the + // compact single-ulong representation (low word = header+coords, + // high word = 0) to match the reference tests. Otherwise the + // low word holds RGBA and the high word holds header+coords. + if (high64 == 0UL) + { + physicalBlockBits = (UInt128)low64; + + // using compact void extent representation + } + else + { + physicalBlockBits = new UInt128(high64, low64); + + // using full void extent representation + } + + PhysicalBlock block = PhysicalBlock.Create(physicalBlockBits); + string? illegal = block.IdentifyInvalidEncodingIssues(); + if (illegal is not null) + { + throw new InvalidOperationException($"{nameof(Pack)}(void extent) produced illegal encoding"); + } + + return (illegal, physicalBlockBits); + } + + private static (string? Error, int[] Range) GetEncodedWeightRange(int range) + { + int[][] validRangeEncodings = [ + [0, 1, 0], [1, 1, 0], [0, 0, 1], [1, 0, 1], [0, 1, 1], [1, 1, 1], + [0, 1, 0], [1, 1, 0], [0, 0, 1], [1, 0, 1], [0, 1, 1], [1, 1, 1] + ]; + + int smallestRange = IntermediateBlock.ValidWeightRanges.First(); + int largestRange = IntermediateBlock.ValidWeightRanges.Last(); + if (range < smallestRange || largestRange < range) + { + return ($"Could not find block mode. Invalid weight range: {range} not in [{smallestRange}, {largestRange}]", new int[3]); + } + + int index = Array.FindIndex(IntermediateBlock.ValidWeightRanges, v => v >= range); + if (index < 0) + { + index = IntermediateBlock.ValidWeightRanges.Length - 1; + } + + int[] encoding = validRangeEncodings[index]; + return (null, [encoding[0], encoding[1], encoding[2]]); + } + + private static string? PackBlockMode(int dimX, int dimY, int range, bool dualPlane, ref BitStream bitSink) + { + bool highPrec = range > 7; + (string? maybeErr, int[]? rangeValues) = GetEncodedWeightRange(range); + if (maybeErr != null) + { + return maybeErr; + } + + // Ensure top two bits of r1 and r2 not both zero per reference + if ((rangeValues[1] | rangeValues[2]) <= 0) + { + throw new InvalidOperationException($"{nameof(rangeValues)}[1] | {nameof(rangeValues)}[2] must be > 0"); + } + + for (int mode = 0; mode < BlockModeInfoTable.Length; ++mode) + { + BlockModeInfo blockMode = BlockModeInfoTable[mode]; + bool isValidMode = true; + isValidMode &= blockMode.MinWeightGridDimX <= dimX; + isValidMode &= dimX <= blockMode.MaxWeightGridDimX; + isValidMode &= blockMode.MinWeightGridDimY <= dimY; + isValidMode &= dimY <= blockMode.MaxWeightGridDimY; + isValidMode &= !(blockMode.RequireSinglePlaneLowPrec && dualPlane); + isValidMode &= !(blockMode.RequireSinglePlaneLowPrec && highPrec); + + if (!isValidMode) + { + continue; + } + + uint encodedMode = BlockModeMasks[mode]; + void SetBit(uint value, int offset) + { + if (offset < 0) + { + return; + } + + encodedMode = (encodedMode & ~(1u << offset)) | ((value & 1u) << offset); + } + + SetBit((uint)rangeValues[0], blockMode.R0BitPos); + SetBit((uint)rangeValues[1], blockMode.R1BitPos); + SetBit((uint)rangeValues[2], blockMode.R2BitPos); + + int offsetX = dimX - blockMode.MinWeightGridDimX; + int offsetY = dimY - blockMode.MinWeightGridDimY; + + if (blockMode.WeightGridXOffsetBitPos >= 0) + { + encodedMode |= (uint)(offsetX << blockMode.WeightGridXOffsetBitPos); + } + else + { + ArgumentOutOfRangeException.ThrowIfNotEqual(offsetX, 0); + } + + if (blockMode.WeightGridYOffsetBitPos >= 0) + { + encodedMode |= (uint)(offsetY << blockMode.WeightGridYOffsetBitPos); + } + else + { + ArgumentOutOfRangeException.ThrowIfNotEqual(offsetY, 0); + } + + if (!blockMode.RequireSinglePlaneLowPrec) + { + SetBit(highPrec ? 1u : 0u, 9); + SetBit(dualPlane ? 1u : 0u, 10); + } + + if (bitSink.Bits != 0) + { + throw new InvalidOperationException($"{nameof(bitSink)}.{nameof(bitSink.Bits)} must be 0"); + } + + bitSink.PutBits(encodedMode, 11); + return null; + } + + return "Could not find viable block mode"; + } + + private static (BitStream WeightSink, int WeightBitsCount) EncodeWeights(in IntermediateBlock.IntermediateBlockData data) + { + BitStream weightSink = new(0UL, 0); + BoundedIntegerSequenceEncoder weightsEncoder = new(data.WeightRange); + int weightCount = data.WeightsCount > 0 + ? data.WeightsCount + : (data.Weights?.Length ?? 0); + if (data.Weights is null) + { + throw new InvalidOperationException($"{nameof(data.Weights)} is null in {nameof(EncodeWeights)}"); + } + + for (int i = 0; i < weightCount; i++) + { + weightsEncoder.AddValue(data.Weights[i]); + } + + weightsEncoder.Encode(ref weightSink); + + int weightBitsCount = (int)weightSink.Bits; + if ((int)weightSink.Bits != BoundedIntegerSequenceCodec.GetBitCountForRange(weightCount, data.WeightRange)) + { + throw new InvalidOperationException($"{nameof(weightSink)}.{nameof(weightSink.Bits)} does not match expected bit count"); + } + + return (weightSink, weightBitsCount); + } + + private static (string? Error, int ExtraConfig) EncodeColorEndpointModes(in IntermediateBlock.IntermediateBlockData data, int partitionCount, ref BitStream bitSink) + { + int extraConfig = 0; + bool sharedEndpointMode = IntermediateBlock.SharedEndpointModes(data); + + if (sharedEndpointMode) + { + if (partitionCount > 1) + { + bitSink.PutBits(0u, 2); + } + + bitSink.PutBits((uint)data.Endpoints[0].Mode, 4); + } + else + { + // compute min_class, max_class + int minClass = 2; + int maxClass = 0; + for (int i = 0; i < data.EndpointCount; i++) + { + int endpointModeClass = ((int)data.Endpoints[i].Mode) >> 2; + minClass = Math.Min(minClass, endpointModeClass); + maxClass = Math.Max(maxClass, endpointModeClass); + } + + if (maxClass - minClass > 1) + { + return ("Endpoint modes are invalid", 0); + } + + BitStream cemEncoder = new(0UL, 0); + cemEncoder.PutBits((uint)(minClass + 1), 2); + + for (int i = 0; i < data.EndpointCount; i++) + { + int endpointModeClass = ((int)data.Endpoints[i].Mode) >> 2; + int classSelectorBit = endpointModeClass - minClass; + cemEncoder.PutBits(classSelectorBit, 1); + } + + for (int i = 0; i < data.EndpointCount; i++) + { + int epMode = ((int)data.Endpoints[i].Mode) & 3; + cemEncoder.PutBits(epMode, 2); + } + + int cemBits = 2 + (partitionCount * 3); + if (!cemEncoder.TryGetBits(cemBits, out uint encodedCem)) + { + throw new InvalidOperationException(); + } + + extraConfig = (int)(encodedCem >> 6); + + bitSink.PutBits(encodedCem, Math.Min(6, cemBits)); + } + + // dual plane channel + if (data.DualPlaneChannel.HasValue) + { + int channel = data.DualPlaneChannel.Value; + ArgumentOutOfRangeException.ThrowIfLessThan(channel, 0); + ArgumentOutOfRangeException.ThrowIfGreaterThan(channel, 3); + extraConfig = (extraConfig << 2) | channel; + } + + return (null, extraConfig); + } + + private struct BlockModeInfo + { + public int MinWeightGridDimX; + public int MaxWeightGridDimX; + public int MinWeightGridDimY; + public int MaxWeightGridDimY; + public int R0BitPos; + public int R1BitPos; + public int R2BitPos; + public int WeightGridXOffsetBitPos; + public int WeightGridYOffsetBitPos; + public bool RequireSinglePlaneLowPrec; + } +} diff --git a/src/ImageSharp.Textures.Astc/TexelBlock/LogicalBlock.cs b/src/ImageSharp.Textures.Astc/TexelBlock/LogicalBlock.cs new file mode 100644 index 00000000..e9cc92e7 --- /dev/null +++ b/src/ImageSharp.Textures.Astc/TexelBlock/LogicalBlock.cs @@ -0,0 +1,750 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Textures.Astc.BiseEncoding.Quantize; +using SixLabors.ImageSharp.Textures.Astc.BlockDecoder; +using SixLabors.ImageSharp.Textures.Astc.ColorEncoding; +using SixLabors.ImageSharp.Textures.Astc.Core; + +namespace SixLabors.ImageSharp.Textures.Astc.TexelBlock; + +internal sealed class LogicalBlock +{ + private ColorEndpointPair[] endpoints; + private int endpointCount; + private readonly int[] weights; + private Partition partition; + private DualPlaneData? dualPlane; + + public LogicalBlock(Footprint footprint) + { + this.endpoints = [ColorEndpointPair.Ldr(RgbaColor.Empty, RgbaColor.Empty)]; + this.endpointCount = 1; + this.weights = new int[footprint.PixelCount]; + this.partition = new Partition(footprint, 1, 0) + { + Assignment = new int[footprint.PixelCount] + }; + } + + public LogicalBlock(Footprint footprint, in IntermediateBlock.IntermediateBlockData block) + { + this.endpoints = new ColorEndpointPair[block.EndpointCount]; + this.endpointCount = DecodeEndpoints(in block, this.endpoints); + this.partition = ComputePartition(footprint, in block); + this.weights = new int[footprint.PixelCount]; + this.CalculateWeights(footprint, in block); + } + + public LogicalBlock(Footprint footprint, IntermediateBlock.VoidExtentData block) + { + this.endpoints = new ColorEndpointPair[1]; + this.endpointCount = DecodeEndpoints(block, this.endpoints); + this.partition = ComputePartition(footprint); + this.weights = new int[footprint.PixelCount]; + } + + /// + /// Initializes a new instance of the class. + /// Decodes directly from raw bits + BlockInfo, + /// bypassing IntermediateBlock and using batch unquantize operations. + /// + private LogicalBlock(Footprint footprint, UInt128 bits, in BlockInfo info) + { + // --- BISE decode + batch unquantize color endpoint values --- + Span colors = stackalloc int[info.ColorValuesCount]; + FusedBlockDecoder.DecodeBiseValues( + bits, + info.ColorStartBit, + info.ColorBitCount, + info.ColorValuesRange, + info.ColorValuesCount, + colors); + Quantization.UnquantizeCEValuesBatch(colors, info.ColorValuesCount, info.ColorValuesRange); + + // --- Decode endpoints per partition --- + this.endpointCount = info.PartitionCount; + this.endpoints = new ColorEndpointPair[this.endpointCount]; + int colorIndex = 0; + for (int i = 0; i < this.endpointCount; i++) + { + ColorEndpointMode mode = info.GetEndpointMode(i); + int colorCount = mode.GetColorValuesCount(); + ReadOnlySpan slice = colors.Slice(colorIndex, colorCount); + this.endpoints[i] = EndpointCodec.DecodeColorsForModePolymorphicUnquantized(slice, mode); + colorIndex += colorCount; + } + + // --- Set up partition --- + this.partition = info.PartitionCount > 1 + ? Partition.GetASTCPartition( + footprint, + info.PartitionCount, + (int)BitOperations.GetBits(bits.Low(), 13, 10)) + : GenerateSinglePartition(footprint); + + // --- BISE decode + unquantize + infill weights --- + int gridSize = info.GridWidth * info.GridHeight; + bool isDualPlane = info.IsDualPlane; + int totalWeights = isDualPlane ? gridSize * 2 : gridSize; + + Span rawWeights = stackalloc int[totalWeights]; + FusedBlockDecoder.DecodeBiseWeights( + bits, + info.WeightBitCount, + info.WeightRange, + totalWeights, + rawWeights); + + DecimationInfo decimationInfo = DecimationTable.Get(footprint, info.GridWidth, info.GridHeight); + this.weights = new int[footprint.PixelCount]; + + if (!isDualPlane) + { + Quantization.UnquantizeWeightsBatch(rawWeights, gridSize, info.WeightRange); + DecimationTable.InfillWeights(rawWeights[..gridSize], decimationInfo, this.weights); + } + else + { + // De-interleave: even indices -> plane0, odd indices -> plane1 + Span plane0 = stackalloc int[gridSize]; + Span plane1 = stackalloc int[gridSize]; + for (int i = 0; i < gridSize; i++) + { + plane0[i] = rawWeights[i * 2]; + plane1[i] = rawWeights[(i * 2) + 1]; + } + + Quantization.UnquantizeWeightsBatch(plane0, gridSize, info.WeightRange); + Quantization.UnquantizeWeightsBatch(plane1, gridSize, info.WeightRange); + + DecimationTable.InfillWeights(plane0, decimationInfo, this.weights); + + this.dualPlane = new DualPlaneData + { + Channel = info.DualPlaneChannel, + Weights = new int[footprint.PixelCount] + }; + DecimationTable.InfillWeights(plane1, decimationInfo, this.dualPlane.Weights); + } + } + + public Footprint GetFootprint() => this.partition.Footprint; + + public void SetWeightAt(int x, int y, int weight) + { + if (weight is < 0 or > 64) + { + throw new ArgumentOutOfRangeException(nameof(weight)); + } + + this.weights[(y * this.GetFootprint().Width) + x] = weight; + } + + public int WeightAt(int x, int y) => this.weights[(y * this.GetFootprint().Width) + x]; + + public void SetDualPlaneWeightAt(int channel, int x, int y, int weight) + { + ArgumentOutOfRangeException.ThrowIfNegative(channel); + ArgumentOutOfRangeException.ThrowIfGreaterThan(weight, 64); + + if (!this.IsDualPlane()) + { + throw new InvalidOperationException("Not a dual plane block"); + } + + if (this.dualPlane is not null && this.dualPlane.Channel == channel) + { + this.dualPlane.Weights[(y * this.GetFootprint().Width) + x] = weight; + } + else + { + this.SetWeightAt(x, y, weight); + } + } + + public int DualPlaneWeightAt(int channel, int x, int y) + { + if (!this.IsDualPlane()) + { + return this.WeightAt(x, y); + } + + return this.dualPlane is not null && this.dualPlane.Channel == channel + ? this.dualPlane.Weights[(y * this.GetFootprint().Width) + x] + : this.WeightAt(x, y); + } + + public RgbaColor ColorAt(int x, int y) + { + Footprint footprint = this.GetFootprint(); + + ArgumentOutOfRangeException.ThrowIfNegative(x); + ArgumentOutOfRangeException.ThrowIfNegative(y); + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(x, footprint.Width); + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(y, footprint.Height); + + int index = (y * footprint.Width) + x; + int part = this.partition.Assignment[index]; + ref ColorEndpointPair endpoint = ref this.endpoints[part]; + + int weight = this.weights[index]; + if (!endpoint.IsHdr) + { + if (this.dualPlane is not null) + { + return SimdHelpers.InterpolateColorLdrDualPlane( + endpoint.LdrLow, endpoint.LdrHigh, weight, this.dualPlane.Channel, this.dualPlane.Weights[index]); + } + + return SimdHelpers.InterpolateColorLdr(endpoint.LdrLow, endpoint.LdrHigh, weight); + } + else + { + if (this.dualPlane is not null) + { + int dualPlaneChannel = this.dualPlane.Channel; + int dualPlaneWeight = this.dualPlane.Weights[index]; + int rWeight = dualPlaneChannel == 0 ? dualPlaneWeight : weight; + int gWeight = dualPlaneChannel == 1 ? dualPlaneWeight : weight; + int bWeight = dualPlaneChannel == 2 ? dualPlaneWeight : weight; + int aWeight = dualPlaneChannel == 3 ? dualPlaneWeight : weight; + return new RgbaColor( + r: InterpolateChannelHdr(endpoint.HdrLow[0], endpoint.HdrHigh[0], rWeight) >> 8, + g: InterpolateChannelHdr(endpoint.HdrLow[1], endpoint.HdrHigh[1], gWeight) >> 8, + b: InterpolateChannelHdr(endpoint.HdrLow[2], endpoint.HdrHigh[2], bWeight) >> 8, + a: InterpolateChannelHdr(endpoint.HdrLow[3], endpoint.HdrHigh[3], aWeight) >> 8); + } + + return new RgbaColor( + r: InterpolateChannelHdr(endpoint.HdrLow[0], endpoint.HdrHigh[0], weight) >> 8, + g: InterpolateChannelHdr(endpoint.HdrLow[1], endpoint.HdrHigh[1], weight) >> 8, + b: InterpolateChannelHdr(endpoint.HdrLow[2], endpoint.HdrHigh[2], weight) >> 8, + a: InterpolateChannelHdr(endpoint.HdrLow[3], endpoint.HdrHigh[3], weight) >> 8); + } + } + + /// + /// Returns the HDR color at the specified pixel position. + /// + /// + /// For HDR endpoints, returns full 16-bit precision (0-65535) per channel. + /// For LDR endpoints, upscales to HDR range. + /// + public RgbaHdrColor ColorAtHdr(int x, int y) + { + Footprint footprint = this.GetFootprint(); + + ArgumentOutOfRangeException.ThrowIfNegative(x); + ArgumentOutOfRangeException.ThrowIfNegative(y); + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(x, footprint.Width); + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(y, footprint.Height); + + int index = (y * footprint.Width) + x; + int part = this.partition.Assignment[index]; + ref ColorEndpointPair endpoint = ref this.endpoints[part]; + + int weight = this.weights[index]; + if (endpoint.IsHdr) + { + if (this.dualPlane != null) + { + int dualPlaneChannel = this.dualPlane.Channel; + int dualPlaneWeight = this.dualPlane.Weights[index]; + int rWeight = dualPlaneChannel == 0 ? dualPlaneWeight : weight; + int gWeight = dualPlaneChannel == 1 ? dualPlaneWeight : weight; + int bWeight = dualPlaneChannel == 2 ? dualPlaneWeight : weight; + int aWeight = dualPlaneChannel == 3 ? dualPlaneWeight : weight; + return new RgbaHdrColor( + InterpolateChannelHdr(endpoint.HdrLow[0], endpoint.HdrHigh[0], rWeight), + InterpolateChannelHdr(endpoint.HdrLow[1], endpoint.HdrHigh[1], gWeight), + InterpolateChannelHdr(endpoint.HdrLow[2], endpoint.HdrHigh[2], bWeight), + InterpolateChannelHdr(endpoint.HdrLow[3], endpoint.HdrHigh[3], aWeight)); + } + + return new RgbaHdrColor( + InterpolateChannelHdr(endpoint.HdrLow[0], endpoint.HdrHigh[0], weight), + InterpolateChannelHdr(endpoint.HdrLow[1], endpoint.HdrHigh[1], weight), + InterpolateChannelHdr(endpoint.HdrLow[2], endpoint.HdrHigh[2], weight), + InterpolateChannelHdr(endpoint.HdrLow[3], endpoint.HdrHigh[3], weight)); + } + else + { + if (this.dualPlane != null) + { + int dualPlaneChannel = this.dualPlane.Channel; + int dualPlaneWeight = this.dualPlane.Weights[index]; + int rWeight = dualPlaneChannel == 0 ? dualPlaneWeight : weight; + int gWeight = dualPlaneChannel == 1 ? dualPlaneWeight : weight; + int bWeight = dualPlaneChannel == 2 ? dualPlaneWeight : weight; + int aWeight = dualPlaneChannel == 3 ? dualPlaneWeight : weight; + return new RgbaHdrColor( + (ushort)(InterpolateChannel(endpoint.LdrLow.R, endpoint.LdrHigh.R, rWeight) * 257), + (ushort)(InterpolateChannel(endpoint.LdrLow.G, endpoint.LdrHigh.G, gWeight) * 257), + (ushort)(InterpolateChannel(endpoint.LdrLow.B, endpoint.LdrHigh.B, bWeight) * 257), + (ushort)(InterpolateChannel(endpoint.LdrLow.A, endpoint.LdrHigh.A, aWeight) * 257)); + } + + return new RgbaHdrColor( + (ushort)(InterpolateChannel(endpoint.LdrLow.R, endpoint.LdrHigh.R, weight) * 257), + (ushort)(InterpolateChannel(endpoint.LdrLow.G, endpoint.LdrHigh.G, weight) * 257), + (ushort)(InterpolateChannel(endpoint.LdrLow.B, endpoint.LdrHigh.B, weight) * 257), + (ushort)(InterpolateChannel(endpoint.LdrLow.A, endpoint.LdrHigh.A, weight) * 257)); + } + } + + /// + /// Writes the HDR float values for the pixel at (x, y) into the output span. + /// + /// + /// For HDR endpoints, values are in LNS (Log-Normalized Space). After interpolation + /// in LNS, the result is converted to FP16 via then widened to float. + /// For Mode 14 (HDR RGB + LDR Alpha), the alpha channel is UNORM16 instead of LNS. + /// For LDR endpoints, the interpolated UNORM16 value is normalized to 0.0-1.0. + /// + public void WriteHdrPixel(int x, int y, Span output) + { + Footprint footprint = this.GetFootprint(); + + ArgumentOutOfRangeException.ThrowIfNegative(x); + ArgumentOutOfRangeException.ThrowIfNegative(y); + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(x, footprint.Width); + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(y, footprint.Height); + + int index = (y * footprint.Width) + x; + int part = this.partition.Assignment[index]; + ref ColorEndpointPair endpoint = ref this.endpoints[part]; + + int weight = this.weights[index]; + int dualPlaneChannel = this.dualPlane?.Channel ?? -1; + int dualPlaneWeight = this.dualPlane?.Weights[index] ?? weight; + + if (endpoint.IsHdr) + { + for (int channel = 0; channel < RgbaColor.BytesPerPixel; ++channel) + { + int channelWeight = (channel == dualPlaneChannel) + ? dualPlaneWeight + : weight; + ushort interpolated = InterpolateChannelHdr(endpoint.HdrLow[channel], endpoint.HdrHigh[channel], channelWeight); + + if (channel == 3 && endpoint.AlphaIsLdr) + { + // Mode 14: alpha is UNORM16, normalize directly + output[channel] = interpolated / 65535.0f; + } + else if (endpoint.ValuesAreLns) + { + // Normal HDR block: convert from LNS to FP16, then to float + ushort halfFloatBits = LnsToSf16(interpolated); + output[channel] = (float)BitConverter.UInt16BitsToHalf(halfFloatBits); + } + else + { + // Void extent HDR: values are already FP16 bit patterns + output[channel] = (float)BitConverter.UInt16BitsToHalf(interpolated); + } + } + } + else + { + for (int channel = 0; channel < RgbaColor.BytesPerPixel; ++channel) + { + int channelWeight = (channel == dualPlaneChannel) + ? dualPlaneWeight + : weight; + int p0 = channel switch { 0 => endpoint.LdrLow.R, 1 => endpoint.LdrLow.G, 2 => endpoint.LdrLow.B, _ => endpoint.LdrLow.A }; + int p1 = channel switch { 0 => endpoint.LdrHigh.R, 1 => endpoint.LdrHigh.G, 2 => endpoint.LdrHigh.B, _ => endpoint.LdrHigh.A }; + ushort unorm16 = InterpolateLdrAsUnorm16(p0, p1, channelWeight); + output[channel] = unorm16 / 65535.0f; + } + } + } + + /// + /// Writes all pixels in the block directly to the output buffer in RGBA byte format. + /// Avoids per-pixel method call overhead, type dispatch, and RgbaColor allocation. + /// + public void WriteAllPixelsLdr(Footprint footprint, Span buffer) + { + ref ColorEndpointPair endpoint0 = ref this.endpoints[0]; + + if (!endpoint0.IsHdr && this.partition.PartitionCount == 1) + { + // Fast path: single-partition LDR block (most common case) + int lowR = endpoint0.LdrLow.R, lowG = endpoint0.LdrLow.G, lowB = endpoint0.LdrLow.B, lowA = endpoint0.LdrLow.A; + int highR = endpoint0.LdrHigh.R, highG = endpoint0.LdrHigh.G, highB = endpoint0.LdrHigh.B, highA = endpoint0.LdrHigh.A; + + if (this.dualPlane == null) + { + this.WriteLdrSinglePartition(buffer, footprint, lowR, lowG, lowB, lowA, highR, highG, highB, highA); + } + else + { + int dualPlaneChannel = this.dualPlane.Channel; + int[] dpWeights = this.dualPlane.Weights; + int pixelCount = footprint.PixelCount; + for (int i = 0; i < pixelCount; i++) + { + SimdHelpers.WriteSinglePixelLdrDualPlane( + buffer, + i * 4, + lowR, + lowG, + lowB, + lowA, + highR, + highG, + highB, + highA, + this.weights[i], + dualPlaneChannel, + dpWeights[i]); + } + } + } + else + { + // General path: multi-partition or HDR blocks + this.WriteAllPixelsGeneral(footprint, buffer); + } + } + + public void SetPartition(Partition p) + { + if (!p.Footprint.Equals(this.partition.Footprint)) + { + throw new InvalidOperationException("New partitions may not be for a different footprint"); + } + + this.partition = p; + if (this.endpointCount < p.PartitionCount) + { + ColorEndpointPair[] newEndpoints = new ColorEndpointPair[p.PartitionCount]; + Array.Copy(this.endpoints, newEndpoints, this.endpointCount); + for (int i = this.endpointCount; i < p.PartitionCount; i++) + { + newEndpoints[i] = ColorEndpointPair.Ldr(RgbaColor.Empty, RgbaColor.Empty); + } + + this.endpoints = newEndpoints; + } + + this.endpointCount = p.PartitionCount; + } + + public void SetEndpoints(RgbaColor firstEndpoint, RgbaColor secondEndpoint, int subset) + { + ArgumentOutOfRangeException.ThrowIfNegative(subset); + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(subset, this.partition.PartitionCount); + + this.endpoints[subset] = ColorEndpointPair.Ldr(firstEndpoint, secondEndpoint); + } + + public void SetDualPlaneChannel(int channel) + { + if (channel < 0) + { + this.dualPlane = null; + } + else if (this.dualPlane != null) + { + this.dualPlane.Channel = channel; + } + else + { + this.dualPlane = new DualPlaneData { Channel = channel, Weights = (int[])this.weights.Clone() }; + } + } + + public bool IsDualPlane() => this.dualPlane is not null; + + public static LogicalBlock? UnpackLogicalBlock(Footprint footprint, UInt128 bits, in BlockInfo info) + { + if (!info.IsValid) + { + return null; + } + + if (info.IsVoidExtent) + { + // Void extent blocks are rare; fall back to existing PhysicalBlock path + PhysicalBlock pb = PhysicalBlock.Create(bits); + IntermediateBlock.VoidExtentData? voidExtentData = IntermediateBlock.UnpackVoidExtent(pb); + if (voidExtentData is null) + { + return null; + } + + return new LogicalBlock(footprint, voidExtentData.Value); + } + else + { + return new LogicalBlock(footprint, bits, in info); + } + } + + /// + /// Converts a 16-bit LNS (Log-Normalized Space) value to a 16-bit SF16 (FP16) bit pattern. + /// + /// + /// The LNS value encodes a 5-bit exponent in the upper bits and an 11-bit mantissa + /// in the lower bits. The mantissa is transformed using a piecewise linear function + /// before being combined with the exponent to form the FP16 result. + /// + internal static ushort LnsToSf16(int lns) + { + int mantissaComponent = lns & 0x7FF; // Lower 11 bits: mantissa component + int exponentComponent = (lns >> 11) & 0x1F; // Upper 5 bits: exponent component + + int mantissaTransformed; + if (mantissaComponent < 512) + { + mantissaTransformed = mantissaComponent * 3; + } + else if (mantissaComponent < 1536) + { + mantissaTransformed = (mantissaComponent * 4) - 512; + } + else + { + mantissaTransformed = (mantissaComponent * 5) - 2048; + } + + int result = (exponentComponent << 10) | (mantissaTransformed >> 3); + return (ushort)Math.Min(result, 0x7BFF); // Clamp to max finite FP16 + } + + private static int DecodeEndpoints(in IntermediateBlock.IntermediateBlockData block, ColorEndpointPair[] endpointPair) + { + int endpointRange = block.EndpointRange ?? IntermediateBlock.EndpointRangeForBlock(block); + if (endpointRange <= 0) + { + throw new InvalidOperationException("Invalid endpoint range"); + } + + for (int i = 0; i < block.EndpointCount; i++) + { + IntermediateBlock.IntermediateEndpointData ed = block.Endpoints[i]; + ReadOnlySpan colorSpan = ((ReadOnlySpan)ed.Colors)[..ed.ColorCount]; + endpointPair[i] = EndpointCodec.DecodeColorsForModePolymorphic(colorSpan, endpointRange, ed.Mode); + } + + return block.EndpointCount; + } + + private static int DecodeEndpoints(IntermediateBlock.VoidExtentData block, ColorEndpointPair[] endpointPair) + { + if (block.IsHdr) + { + // HDR void extent: ushort values are FP16 bit patterns (not LNS) + RgbaHdrColor hdrColor = new(block.R, block.G, block.B, block.A); + endpointPair[0] = ColorEndpointPair.Hdr(hdrColor, hdrColor, valuesAreLns: false); + } + else + { + // LDR void extent: ushort values are UNORM16, convert to byte range + RgbaColor ldrColor = new( + (byte)(block.R >> 8), + (byte)(block.G >> 8), + (byte)(block.B >> 8), + (byte)(block.A >> 8)); + endpointPair[0] = ColorEndpointPair.Ldr(ldrColor, ldrColor); + } + + return 1; + } + + private static Partition GenerateSinglePartition(Footprint footprint) => new(footprint, 1, 0) + { + Assignment = new int[footprint.PixelCount] + }; + + private static Partition ComputePartition(Footprint footprint, in IntermediateBlock.IntermediateBlockData block) + => block.PartitionId.HasValue + ? Partition.GetASTCPartition(footprint, block.EndpointCount, block.PartitionId.Value) + : GenerateSinglePartition(footprint); + + private static Partition ComputePartition(Footprint footprint) + => GenerateSinglePartition(footprint); + + private void CalculateWeights(Footprint footprint, in IntermediateBlock.IntermediateBlockData block) + { + int gridSize = block.WeightGridX * block.WeightGridY; + int weightFrequency = block.DualPlaneChannel.HasValue ? 2 : 1; + + // Get decimation info once for both planes + DecimationInfo decimationInfo = DecimationTable.Get(footprint, block.WeightGridX, block.WeightGridY); + + // stackalloc avoids per-block heap allocation (max 12×12 = 144 ints = 576 bytes) + Span unquantized = stackalloc int[gridSize]; + for (int i = 0; i < gridSize; ++i) + { + unquantized[i] = Quantization.UnquantizeWeightFromRange( + block.Weights[i * weightFrequency], block.WeightRange); + } + + DecimationTable.InfillWeights(unquantized, decimationInfo, this.weights); + + if (block.DualPlaneChannel.HasValue) + { + DualPlaneData dualPlane = new() + { + Channel = block.DualPlaneChannel.Value, + Weights = new int[footprint.PixelCount] + }; + this.dualPlane = dualPlane; + for (int i = 0; i < gridSize; ++i) + { + unquantized[i] = Quantization.UnquantizeWeightFromRange( + block.Weights[(i * weightFrequency) + 1], block.WeightRange); + } + + DecimationTable.InfillWeights(unquantized, decimationInfo, this.dualPlane.Weights); + } + } + + private static int InterpolateChannel(int p0, int p1, int weight) + { + int c0 = (p0 << 8) | p0; + int c1 = (p1 << 8) | p1; + int c = ((c0 * (64 - weight)) + (c1 * weight) + 32) / 64; + int quantized = ((c * byte.MaxValue) + short.MaxValue) / (ushort.MaxValue + 1); + return Math.Clamp(quantized, 0, byte.MaxValue); + } + + /// + /// Interpolates an LDR channel value and returns the full 16-bit UNORM result + /// (before reduction to byte). Used by the HDR output path for LDR endpoints. + /// + private static ushort InterpolateLdrAsUnorm16(int p0, int p1, int weight) + { + int c0 = (p0 << 8) | p0; + int c1 = (p1 << 8) | p1; + int c = ((c0 * (64 - weight)) + (c1 * weight) + 32) / 64; + return (ushort)Math.Clamp(c, 0, 0xFFFF); + } + + /// + /// Interpolates an HDR channel value between two endpoints using the specified weight. + /// + /// + /// HDR endpoints are already 16-bit values (FP16 bit patterns). Unlike LDR interpolation + /// which expands 8-bit to 16-bit before interpolating, HDR interpolation operates directly + /// on the 16-bit values + /// + private static ushort InterpolateChannelHdr(int p0, int p1, int weight) + { + int c = ((p0 * (64 - weight)) + (p1 * weight) + 32) / 64; + return (ushort)Math.Clamp(c, 0, 0xFFFF); + } + + private void WriteLdrSinglePartition( + Span buffer, + Footprint footprint, + int lowR, + int lowG, + int lowB, + int lowA, + int highR, + int highG, + int highB, + int highA) + { + int pixelCount = footprint.PixelCount; + for (int i = 0; i < pixelCount; i++) + { + SimdHelpers.WriteSinglePixelLdr( + buffer, + i * 4, + lowR, + lowG, + lowB, + lowA, + highR, + highG, + highB, + highA, + this.weights[i]); + } + } + + private void WriteAllPixelsGeneral(Footprint footprint, Span buffer) + { + int pixelCount = footprint.PixelCount; + for (int i = 0; i < pixelCount; i++) + { + int part = this.partition.Assignment[i]; + ref ColorEndpointPair endpoint = ref this.endpoints[part]; + + int weight = this.weights[i]; + if (!endpoint.IsHdr) + { + if (this.dualPlane is not null) + { + SimdHelpers.WriteSinglePixelLdrDualPlane( + buffer, + i * 4, + endpoint.LdrLow.R, + endpoint.LdrLow.G, + endpoint.LdrLow.B, + endpoint.LdrLow.A, + endpoint.LdrHigh.R, + endpoint.LdrHigh.G, + endpoint.LdrHigh.B, + endpoint.LdrHigh.A, + weight, + this.dualPlane.Channel, + this.dualPlane.Weights[i]); + } + else + { + SimdHelpers.WriteSinglePixelLdr( + buffer, + i * 4, + endpoint.LdrLow.R, + endpoint.LdrLow.G, + endpoint.LdrLow.B, + endpoint.LdrLow.A, + endpoint.LdrHigh.R, + endpoint.LdrHigh.G, + endpoint.LdrHigh.B, + endpoint.LdrHigh.A, + weight); + } + } + else + { + int dualPlaneChannel = this.dualPlane?.Channel ?? -1; + int dualPlaneWeight = this.dualPlane?.Weights[i] ?? weight; + int rWeight = dualPlaneChannel == 0 ? dualPlaneWeight : weight; + int gWeight = dualPlaneChannel == 1 ? dualPlaneWeight : weight; + int bWeight = dualPlaneChannel == 2 ? dualPlaneWeight : weight; + int aWeight = dualPlaneChannel == 3 ? dualPlaneWeight : weight; + buffer[(i * 4) + 0] = (byte)(InterpolateChannelHdr( + endpoint.HdrLow[0], + endpoint.HdrHigh[0], + rWeight) >> 8); + buffer[(i * 4) + 1] = (byte)(InterpolateChannelHdr( + endpoint.HdrLow[1], + endpoint.HdrHigh[1], + gWeight) >> 8); + buffer[(i * 4) + 2] = (byte)(InterpolateChannelHdr( + endpoint.HdrLow[2], + endpoint.HdrHigh[2], + bWeight) >> 8); + buffer[(i * 4) + 3] = (byte)(InterpolateChannelHdr( + endpoint.HdrLow[3], + endpoint.HdrHigh[3], + aWeight) >> 8); + } + } + } + + private class DualPlaneData + { + public int Channel { get; set; } + + public int[] Weights { get; set; } = []; + } +} diff --git a/src/ImageSharp.Textures.Astc/TexelBlock/PhysicalBlock.cs b/src/ImageSharp.Textures.Astc/TexelBlock/PhysicalBlock.cs new file mode 100644 index 00000000..d7b6e2c6 --- /dev/null +++ b/src/ImageSharp.Textures.Astc/TexelBlock/PhysicalBlock.cs @@ -0,0 +1,216 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Textures.Astc.ColorEncoding; +using SixLabors.ImageSharp.Textures.Astc.Core; + +namespace SixLabors.ImageSharp.Textures.Astc.TexelBlock; + +/// +/// A physical ASTC texel block (128 bits). +/// Delegates all block mode decoding to . +/// +internal readonly struct PhysicalBlock +{ + public const int SizeInBytes = 16; + private readonly BlockInfo info; + + private PhysicalBlock(UInt128 bits, BlockInfo info) + { + this.BlockBits = bits; + this.info = info; + } + + public UInt128 BlockBits { get; } + + public bool IsVoidExtent => this.info.IsVoidExtent; + + public bool IsIllegalEncoding => !this.info.IsValid; + + public bool IsDualPlane + => this.info.IsValid && !this.info.IsVoidExtent && this.info.IsDualPlane; + + /// + /// Factory method to create a PhysicalBlock from raw bits + /// + public static PhysicalBlock Create(UInt128 bits) + => new(bits, BlockInfo.Decode(bits)); + + public static PhysicalBlock Create(ulong low) => Create((UInt128)low); + + public static PhysicalBlock Create(ulong low, ulong high) => Create(new UInt128(high, low)); + + internal (int Width, int Height)? GetWeightGridDimensions() + => this.info.IsValid && !this.info.IsVoidExtent + ? (this.info.GridWidth, this.info.GridHeight) + : null; + + internal int? GetWeightRange() + => this.info.IsValid && !this.info.IsVoidExtent + ? this.info.WeightRange + : null; + + internal int[]? GetVoidExtentCoordinates() + { + if (!this.info.IsVoidExtent) + { + return null; + } + + // If void extent coords are all 1's then these are not valid void extent coords + ulong voidExtentMask = 0xFFFFFFFFFFFFFDFFUL; + ulong constBlockMode = 0xFFFFFFFFFFFFFDFCUL; + + return this.info.IsValid && (voidExtentMask & this.BlockBits.Low()) != constBlockMode + ? DecodeVoidExtentCoordinates(this.BlockBits) + : null; + } + + /// + /// Get the dual plane channel if dual plane is enabled + /// + /// The dual plane channel if enabled, otherwise null. + internal int? GetDualPlaneChannel() + => this.info.IsValid && this.info.IsDualPlane + ? this.info.DualPlaneChannel + : null; + + internal string? IdentifyInvalidEncodingIssues() + { + if (this.info.IsValid) + { + return null; + } + + return this.info.IsVoidExtent + ? IdentifyVoidExtentIssues(this.BlockBits) + : "Invalid block encoding"; + } + + internal int? GetWeightBitCount() + => this.info.IsValid && !this.info.IsVoidExtent + ? this.info.WeightBitCount + : null; + + internal int? GetWeightStartBit() + => this.info.IsValid && !this.info.IsVoidExtent + ? 128 - this.info.WeightBitCount + : null; + + internal int? GetPartitionsCount() + => this.info.IsValid && !this.info.IsVoidExtent + ? this.info.PartitionCount + : null; + + internal int? GetPartitionId() + { + if (!this.info.IsValid || this.info.IsVoidExtent || this.info.PartitionCount == 1) + { + return null; + } + + return (int)BitOperations.GetBits(this.BlockBits.Low(), 13, 10); + } + + internal ColorEndpointMode? GetEndpointMode(int partition) + { + if (!this.info.IsValid || this.info.IsVoidExtent) + { + return null; + } + + if (partition < 0 || partition >= this.info.PartitionCount) + { + return null; + } + + return this.info.GetEndpointMode(partition); + } + + internal int? GetColorStartBit() + { + if (this.info.IsVoidExtent) + { + return 64; + } + + return this.info.IsValid + ? this.info.ColorStartBit + : null; + } + + internal int? GetColorValuesCount() + { + if (this.info.IsVoidExtent) + { + return 4; + } + + return this.info.IsValid + ? this.info.ColorValuesCount + : null; + } + + internal int? GetColorBitCount() + { + if (this.info.IsVoidExtent) + { + return 64; + } + + return this.info.IsValid + ? this.info.ColorBitCount + : null; + } + + internal int? GetColorValuesRange() + { + if (this.info.IsVoidExtent) + { + return (1 << 16) - 1; + } + + return this.info.IsValid + ? this.info.ColorValuesRange + : null; + } + + internal static int[] DecodeVoidExtentCoordinates(UInt128 astcBits) + { + ulong lowBits = astcBits.Low(); + int[] coords = new int[4]; + for (int i = 0; i < 4; ++i) + { + coords[i] = (int)BitOperations.GetBits(lowBits, 12 + (13 * i), 13); + } + + return coords; + } + + /// + /// Full error-string version for void extent issues (used for error reporting) + /// + private static string? IdentifyVoidExtentIssues(UInt128 bits) + { + if (BitOperations.GetBits(bits, 10, 2).Low() != 0x3UL) + { + return "Reserved bits set for void extent block"; + } + + ulong lowBits = bits.Low(); + int c0 = (int)BitOperations.GetBits(lowBits, 12, 13); + int c1 = (int)BitOperations.GetBits(lowBits, 25, 13); + int c2 = (int)BitOperations.GetBits(lowBits, 38, 13); + int c3 = (int)BitOperations.GetBits(lowBits, 51, 13); + + const int all1s = (1 << 13) - 1; + bool coordsAll1s = c0 == all1s && c1 == all1s && c2 == all1s && c3 == all1s; + + if (!coordsAll1s && (c0 >= c1 || c2 >= c3)) + { + return "Void extent texture coordinates are invalid"; + } + + return null; + } +} diff --git a/src/ImageSharp.Textures.Astc/TexelBlock/PhysicalBlockMode.cs b/src/ImageSharp.Textures.Astc/TexelBlock/PhysicalBlockMode.cs new file mode 100644 index 00000000..dc414388 --- /dev/null +++ b/src/ImageSharp.Textures.Astc/TexelBlock/PhysicalBlockMode.cs @@ -0,0 +1,23 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Textures.Astc.TexelBlock; + +/// +/// The overall block modes defined in table C.2.8. There are 10 +/// weight grid encoding schemes + void extent. +/// +internal enum PhysicalBlockMode +{ + WidthB4HeightA2, + WidthB8HeightA2, + WidthA2HeightB8, + WidthA2HeightB6, + WidthB2HeightA2, + Width12HeightA2, + WidthA2Height12, + Width6Height10, + Width10Height6, + WidthA6HeightB6, + VoidExtent, +} diff --git a/src/ImageSharp.Textures/Common/Exceptions/TextureFormatException.cs b/src/ImageSharp.Textures/Common/Exceptions/TextureFormatException.cs index 8fd3c014..df73308e 100644 --- a/src/ImageSharp.Textures/Common/Exceptions/TextureFormatException.cs +++ b/src/ImageSharp.Textures/Common/Exceptions/TextureFormatException.cs @@ -1,36 +1,33 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; +namespace SixLabors.ImageSharp.Textures.Common.Exceptions; -namespace SixLabors.ImageSharp.Textures.Common.Exceptions +/// +/// The exception that is thrown when the library tries to load +/// an image, which has an invalid format. +/// +public class TextureFormatException : Exception { /// - /// The exception that is thrown when the library tries to load - /// an image, which has an invalid format. + /// Initializes a new instance of the class with the name of the + /// parameter that causes this exception. /// - public class TextureFormatException : Exception + /// The error message that explains the reason for this exception. + internal TextureFormatException(string errorMessage) + : base(errorMessage) { - /// - /// Initializes a new instance of the class with the name of the - /// parameter that causes this exception. - /// - /// The error message that explains the reason for this exception. - internal TextureFormatException(string errorMessage) - : base(errorMessage) - { - } + } - /// - /// Initializes a new instance of the class with a specified - /// error message and the exception that is the cause of this exception. - /// - /// The error message that explains the reason for this exception. - /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) - /// if no inner exception is specified. - internal TextureFormatException(string errorMessage, Exception innerException) - : base(errorMessage, innerException) - { - } + /// + /// Initializes a new instance of the class with a specified + /// error message and the exception that is the cause of this exception. + /// + /// The error message that explains the reason for this exception. + /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) + /// if no inner exception is specified. + internal TextureFormatException(string errorMessage, Exception innerException) + : base(errorMessage, innerException) + { } } diff --git a/src/ImageSharp.Textures/Common/Exceptions/TextureProcessingException.cs b/src/ImageSharp.Textures/Common/Exceptions/TextureProcessingException.cs index ba360be0..e714fb18 100644 --- a/src/ImageSharp.Textures/Common/Exceptions/TextureProcessingException.cs +++ b/src/ImageSharp.Textures/Common/Exceptions/TextureProcessingException.cs @@ -1,35 +1,32 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; +namespace SixLabors.ImageSharp.Textures.Common.Exceptions; -namespace SixLabors.ImageSharp.Textures.Common.Exceptions +/// +/// The exception that is thrown when an error occurs when applying a process to an image. +/// +public sealed class TextureProcessingException : Exception { /// - /// The exception that is thrown when an error occurs when applying a process to an image. + /// Initializes a new instance of the class with the name of the + /// parameter that causes this exception. /// - public sealed class TextureProcessingException : Exception + /// The error message that explains the reason for this exception. + public TextureProcessingException(string errorMessage) + : base(errorMessage) { - /// - /// Initializes a new instance of the class with the name of the - /// parameter that causes this exception. - /// - /// The error message that explains the reason for this exception. - public TextureProcessingException(string errorMessage) - : base(errorMessage) - { - } + } - /// - /// Initializes a new instance of the class with a specified - /// error message and the exception that is the cause of this exception. - /// - /// The error message that explains the reason for this exception. - /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) - /// if no inner exception is specified. - public TextureProcessingException(string errorMessage, Exception innerException) - : base(errorMessage, innerException) - { - } + /// + /// Initializes a new instance of the class with a specified + /// error message and the exception that is the cause of this exception. + /// + /// The error message that explains the reason for this exception. + /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) + /// if no inner exception is specified. + public TextureProcessingException(string errorMessage, Exception innerException) + : base(errorMessage, innerException) + { } } diff --git a/src/ImageSharp.Textures/Common/Exceptions/UnknownTextureFormatException.cs b/src/ImageSharp.Textures/Common/Exceptions/UnknownTextureFormatException.cs index 05bce3f2..80b093f9 100644 --- a/src/ImageSharp.Textures/Common/Exceptions/UnknownTextureFormatException.cs +++ b/src/ImageSharp.Textures/Common/Exceptions/UnknownTextureFormatException.cs @@ -1,22 +1,21 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.Common.Exceptions +namespace SixLabors.ImageSharp.Textures.Common.Exceptions; + +/// +/// The exception that is thrown when the library tries to load +/// an image which has an unknown format. +/// +public sealed class UnknownTextureFormatException : TextureFormatException { /// - /// The exception that is thrown when the library tries to load - /// an image which has an unknown format. + /// Initializes a new instance of the class with the name of the + /// parameter that causes this exception. /// - public sealed class UnknownTextureFormatException : TextureFormatException + /// The error message that explains the reason for this exception. + public UnknownTextureFormatException(string errorMessage) + : base(errorMessage) { - /// - /// Initializes a new instance of the class with the name of the - /// parameter that causes this exception. - /// - /// The error message that explains the reason for this exception. - public UnknownTextureFormatException(string errorMessage) - : base(errorMessage) - { - } } } diff --git a/src/ImageSharp.Textures/Common/Extensions/StreamExtensions.cs b/src/ImageSharp.Textures/Common/Extensions/StreamExtensions.cs index 274a0227..723927e9 100644 --- a/src/ImageSharp.Textures/Common/Extensions/StreamExtensions.cs +++ b/src/ImageSharp.Textures/Common/Extensions/StreamExtensions.cs @@ -1,105 +1,101 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Buffers; -using System.IO; -using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Textures.Common.Extensions +namespace SixLabors.ImageSharp.Textures.Common.Extensions; + +/// +/// Extension methods for the type. +/// +internal static class StreamExtensions { /// - /// Extension methods for the type. + /// Reads data from a stream into the provided buffer. /// - internal static class StreamExtensions - { - /// - /// Reads data from a stream into the provided buffer. - /// - /// The stream. - /// The buffer.. - /// The offset within the buffer where the bytes are read into. - /// The number of bytes, if available, to read. - /// The actual number of bytes read. - public static int Read(this Stream stream, Span buffer, int offset, int count) => stream.Read(buffer.Slice(offset, count)); + /// The stream. + /// The buffer.. + /// The offset within the buffer where the bytes are read into. + /// The number of bytes, if available, to read. + /// The actual number of bytes read. + public static int Read(this Stream stream, Span buffer, int offset, int count) => stream.Read(buffer.Slice(offset, count)); - /// - /// Skips the number of bytes in the given stream. - /// - /// The stream. - /// The count. - public static void Skip(this Stream stream, int count) + /// + /// Skips the number of bytes in the given stream. + /// + /// The stream. + /// The count. + public static void Skip(this Stream stream, int count) + { + if (count < 1) { - if (count < 1) - { - return; - } + return; + } - if (stream.CanSeek) - { - stream.Seek(count, SeekOrigin.Current); // Position += count; - } - else + if (stream.CanSeek) + { + stream.Seek(count, SeekOrigin.Current); // Position += count; + } + else + { + byte[] foo = new byte[count]; + while (count > 0) { - byte[] foo = new byte[count]; - while (count > 0) + int bytesRead = stream.Read(foo, 0, count); + if (bytesRead == 0) { - int bytesRead = stream.Read(foo, 0, count); - if (bytesRead == 0) - { - break; - } - - count -= bytesRead; + break; } + + count -= bytesRead; } } + } #if NET472 || NETSTANDARD1_3 || NETSTANDARD2_0 - // This is a port of the CoreFX implementation and is MIT Licensed: https://github.com/dotnet/coreclr/blob/c4dca1072d15bdda64c754ad1ea474b1580fa554/src/System.Private.CoreLib/shared/System/IO/Stream.cs#L770 - public static void Write(this Stream stream, ReadOnlySpan buffer) + // This is a port of the CoreFX implementation and is MIT Licensed: https://github.com/dotnet/coreclr/blob/c4dca1072d15bdda64c754ad1ea474b1580fa554/src/System.Private.CoreLib/shared/System/IO/Stream.cs#L770 + public static void Write(this Stream stream, ReadOnlySpan buffer) + { + // This uses ArrayPool.Shared, rather than taking a MemoryAllocator, + // in order to match the signature of the framework method that exists in + // .NET Core. + byte[] sharedBuffer = ArrayPool.Shared.Rent(buffer.Length); + try { - // This uses ArrayPool.Shared, rather than taking a MemoryAllocator, - // in order to match the signature of the framework method that exists in - // .NET Core. - byte[] sharedBuffer = ArrayPool.Shared.Rent(buffer.Length); - try - { - buffer.CopyTo(sharedBuffer); - stream.Write(sharedBuffer, 0, buffer.Length); - } - finally - { - ArrayPool.Shared.Return(sharedBuffer); - } + buffer.CopyTo(sharedBuffer); + stream.Write(sharedBuffer, 0, buffer.Length); + } + finally + { + ArrayPool.Shared.Return(sharedBuffer); } + } #endif #if !SUPPORTS_SPAN_STREAM - // This is a port of the CoreFX implementation and is MIT Licensed: - // https://github.com/dotnet/corefx/blob/17300169760c61a90cab8d913636c1058a30a8c1/src/Common/src/CoreLib/System/IO/Stream.cs#L742 - public static int Read(this Stream stream, Span buffer) + // This is a port of the CoreFX implementation and is MIT Licensed: + // https://github.com/dotnet/corefx/blob/17300169760c61a90cab8d913636c1058a30a8c1/src/Common/src/CoreLib/System/IO/Stream.cs#L742 + public static int Read(this Stream stream, Span buffer) + { + // This uses ArrayPool.Shared, rather than taking a MemoryAllocator, + // in order to match the signature of the framework method that exists in + // .NET Core. + byte[] sharedBuffer = ArrayPool.Shared.Rent(buffer.Length); + try { - // This uses ArrayPool.Shared, rather than taking a MemoryAllocator, - // in order to match the signature of the framework method that exists in - // .NET Core. - byte[] sharedBuffer = ArrayPool.Shared.Rent(buffer.Length); - try + int numRead = stream.Read(sharedBuffer, 0, buffer.Length); + if ((uint)numRead > (uint)buffer.Length) { - int numRead = stream.Read(sharedBuffer, 0, buffer.Length); - if ((uint)numRead > (uint)buffer.Length) - { - throw new IOException("Stream was too long."); - } - - new Span(sharedBuffer, 0, numRead).CopyTo(buffer); - return numRead; - } - finally - { - ArrayPool.Shared.Return(sharedBuffer); + throw new IOException("Stream was too long."); } + + new Span(sharedBuffer, 0, numRead).CopyTo(buffer); + return numRead; + } + finally + { + ArrayPool.Shared.Return(sharedBuffer); } -#endif } +#endif } diff --git a/src/ImageSharp.Textures/Common/Extensions/ToStringExtension.cs b/src/ImageSharp.Textures/Common/Extensions/ToStringExtension.cs index da8ec122..07e0d106 100644 --- a/src/ImageSharp.Textures/Common/Extensions/ToStringExtension.cs +++ b/src/ImageSharp.Textures/Common/Extensions/ToStringExtension.cs @@ -1,24 +1,19 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Text; -namespace SixLabors.ImageSharp.Textures.Common.Extensions +namespace SixLabors.ImageSharp.Textures.Common.Extensions; + +/// +/// To string extension methods. +/// +public static class ToStringExtension { /// - /// To string extension methods. + /// Converts a FourCC value to a string. /// - public static class ToStringExtension - { - /// - /// Converts a FourCC value to a string. - /// - /// The FourCC. - /// A string for the FourCC. - public static string FourCcToString(this uint value) - { - return Encoding.UTF8.GetString(BitConverter.GetBytes(value)); - } - } + /// The FourCC. + /// A string for the FourCC. + public static string FourCcToString(this uint value) => Encoding.UTF8.GetString(BitConverter.GetBytes(value)); } diff --git a/src/ImageSharp.Textures/Common/Helpers/FloatHelper.cs b/src/ImageSharp.Textures/Common/Helpers/FloatHelper.cs index 98b8de50..63e86b8d 100644 --- a/src/ImageSharp.Textures/Common/Helpers/FloatHelper.cs +++ b/src/ImageSharp.Textures/Common/Helpers/FloatHelper.cs @@ -3,70 +3,69 @@ using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.Textures.Common.Helpers +namespace SixLabors.ImageSharp.Textures.Common.Helpers; + +internal static class FloatHelper { - internal static class FloatHelper - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float UnpackFloat32ToFloat(uint value) => Unsafe.As(ref value); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float UnpackFloat32ToFloat(uint value) => Unsafe.As(ref value); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint PackFloatToFloat32(float value) => Unsafe.As(ref value); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint PackFloatToFloat32(float value) => Unsafe.As(ref value); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float UnpackFloat16ToFloat(uint value) - { - uint result = - ((value >> 15) << 31) | - ((((value >> 10) & 0x1f) - 15 + 127) << 23) | - ((value & 0x3ff) << 13); - return Unsafe.As(ref result); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float UnpackFloat16ToFloat(uint value) + { + uint result = + ((value >> 15) << 31) | + ((((value >> 10) & 0x1f) - 15 + 127) << 23) | + ((value & 0x3ff) << 13); + return Unsafe.As(ref result); + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint PackFloatToFloat16(float value) - { - uint temp = Unsafe.As(ref value); - return - ((temp >> 31) << 15) | - ((((temp >> 23) & 0xff) - 127 + 15) << 10) | - ((temp & 0x7fffff) >> 13); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint PackFloatToFloat16(float value) + { + uint temp = Unsafe.As(ref value); + return + ((temp >> 31) << 15) | + ((((temp >> 23) & 0xff) - 127 + 15) << 10) | + ((temp & 0x7fffff) >> 13); + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float UnpackFloat10ToFloat(uint value) - { - uint result = - ((((value >> 5) & 0x1f) - 10 + 127) << 23) | - ((value & 0x1f) << 18); - return Unsafe.As(ref result); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float UnpackFloat10ToFloat(uint value) + { + uint result = + ((((value >> 5) & 0x1f) - 10 + 127) << 23) | + ((value & 0x1f) << 18); + return Unsafe.As(ref result); + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint PackFloatToFloat10(float value) - { - uint temp = Unsafe.As(ref value); - return - ((((temp >> 23) & 0xff) - 127 + 10) << 5) | - ((temp & 0x7fffff) >> 18); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint PackFloatToFloat10(float value) + { + uint temp = Unsafe.As(ref value); + return + ((((temp >> 23) & 0xff) - 127 + 10) << 5) | + ((temp & 0x7fffff) >> 18); + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float UnpackFloat11ToFloat(uint value) - { - uint result = - ((((value >> 6) & 0x1f) - 11 + 127) << 23) | - ((value & 0x3f) << 17); - return Unsafe.As(ref result); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float UnpackFloat11ToFloat(uint value) + { + uint result = + ((((value >> 6) & 0x1f) - 11 + 127) << 23) | + ((value & 0x3f) << 17); + return Unsafe.As(ref result); + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint PackFloatToFloat11(float value) - { - uint temp = Unsafe.As(ref value); - return - ((((temp >> 23) & 0xff) - 127 + 11) << 6) | - ((temp & 0x7fffff) >> 17); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint PackFloatToFloat11(float value) + { + uint temp = Unsafe.As(ref value); + return + ((((temp >> 23) & 0xff) - 127 + 11) << 6) | + ((temp & 0x7fffff) >> 17); } } diff --git a/src/ImageSharp.Textures/Common/Helpers/PixelUtils.cs b/src/ImageSharp.Textures/Common/Helpers/PixelUtils.cs index ed7907b2..d9c789bb 100644 --- a/src/ImageSharp.Textures/Common/Helpers/PixelUtils.cs +++ b/src/ImageSharp.Textures/Common/Helpers/PixelUtils.cs @@ -4,43 +4,42 @@ using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Textures.Common.Helpers +namespace SixLabors.ImageSharp.Textures.Common.Helpers; + +/// +/// Provides methods for calculating pixel values. +/// +internal static class PixelUtils { /// - /// Provides methods for calculating pixel values. + /// Performs final shifting from a 5bit value to an 8bit one. /// - internal static class PixelUtils - { - /// - /// Performs final shifting from a 5bit value to an 8bit one. - /// - /// The masked and shifted value. - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static byte GetBytesFrom5BitValue(int value) => (byte)((value << 3) | (value >> 2)); + /// The masked and shifted value. + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static byte GetBytesFrom5BitValue(int value) => (byte)((value << 3) | (value >> 2)); - /// - /// Performs final shifting from a 6bit value to an 8bit one. - /// - /// The masked and shifted value. - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static byte GetBytesFrom6BitValue(int value) => (byte)((value << 2) | (value >> 4)); + /// + /// Performs final shifting from a 6bit value to an 8bit one. + /// + /// The masked and shifted value. + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static byte GetBytesFrom6BitValue(int value) => (byte)((value << 2) | (value >> 4)); - /// - /// Extracts the R5G6B5 values from a packed ushort pixel in that order. - /// - /// The packed color. - /// The extracted color. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ExtractR5G6B5(ushort color, ref Rgb24 dest) - { - var r = (color & 0xF800) >> 11; - var g = (color & 0x7E0) >> 5; - var b = color & 0x1f; - dest.R = PixelUtils.GetBytesFrom5BitValue(r); - dest.G = PixelUtils.GetBytesFrom6BitValue(g); - dest.B = PixelUtils.GetBytesFrom5BitValue(b); - } + /// + /// Extracts the R5G6B5 values from a packed ushort pixel in that order. + /// + /// The packed color. + /// The extracted color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ExtractR5G6B5(ushort color, ref Rgb24 dest) + { + int r = (color & 0xF800) >> 11; + int g = (color & 0x7E0) >> 5; + int b = color & 0x1f; + dest.R = PixelUtils.GetBytesFrom5BitValue(r); + dest.G = PixelUtils.GetBytesFrom6BitValue(g); + dest.B = PixelUtils.GetBytesFrom5BitValue(b); } } diff --git a/src/ImageSharp.Textures/Configuration.cs b/src/ImageSharp.Textures/Configuration.cs index d68b4726..9e4e9be8 100644 --- a/src/ImageSharp.Textures/Configuration.cs +++ b/src/ImageSharp.Textures/Configuration.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; -using System.Collections.Generic; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Textures.Formats; using SixLabors.ImageSharp.Textures.Formats.Dds; @@ -10,147 +8,146 @@ using SixLabors.ImageSharp.Textures.Formats.Ktx2; using SixLabors.ImageSharp.Textures.IO; -namespace SixLabors.ImageSharp.Textures +namespace SixLabors.ImageSharp.Textures; + +/// +/// Provides configuration code which allows altering default behaviour or extending the library. +/// +public sealed class Configuration { /// - /// Provides configuration code which allows altering default behaviour or extending the library. + /// A lazily initialized configuration default instance. /// - public sealed class Configuration - { - /// - /// A lazily initialized configuration default instance. - /// - private static readonly Lazy Lazy = new(CreateDefaultInstance); + private static readonly Lazy Lazy = new(CreateDefaultInstance); - private int maxDegreeOfParallelism = Environment.ProcessorCount; + private int maxDegreeOfParallelism = Environment.ProcessorCount; - private MemoryAllocator memoryAllocator = MemoryAllocator.Default; + private MemoryAllocator memoryAllocator = MemoryAllocator.Default; - /// - /// Initializes a new instance of the class. - /// - public Configuration() - { - } + /// + /// Initializes a new instance of the class. + /// + public Configuration() + { + } - /// - /// Initializes a new instance of the class. - /// - /// A collection of configuration modules to register - public Configuration(params IConfigurationModule[] configurationModules) + /// + /// Initializes a new instance of the class. + /// + /// A collection of configuration modules to register + public Configuration(params IConfigurationModule[] configurationModules) + { + if (configurationModules != null) { - if (configurationModules != null) + foreach (IConfigurationModule p in configurationModules) { - foreach (IConfigurationModule p in configurationModules) - { - p.Configure(this); - } + p.Configure(this); } } + } - /// - /// Gets the default instance. - /// - public static Configuration Default { get; } = Lazy.Value; - - /// - /// Gets or sets the maximum number of concurrent tasks enabled in ImageSharp algorithms - /// configured with this instance. - /// Initialized with by default. - /// - public int MaxDegreeOfParallelism - { - get => this.maxDegreeOfParallelism; - set - { - if (value == 0 || value < -1) - { - throw new ArgumentOutOfRangeException(nameof(this.MaxDegreeOfParallelism)); - } - - this.maxDegreeOfParallelism = value; - } - } + /// + /// Gets the default instance. + /// + public static Configuration Default { get; } = Lazy.Value; - /// - /// Gets the currently registered s. - /// - public IEnumerable ImageFormats => this.ImageFormatsManager.ImageFormats; - - /// - /// Gets or sets the position in a stream to use for reading when using a seekable stream as an image data source. - /// - public ReadOrigin ReadOrigin { get; set; } = ReadOrigin.Current; - - /// - /// Gets or sets the that is currently in use. - /// - public TextureFormatManager ImageFormatsManager { get; set; } = new(); - - /// - /// Gets or sets the that is currently in use. - /// - public MemoryAllocator MemoryAllocator + /// + /// Gets or sets the maximum number of concurrent tasks enabled in ImageSharp algorithms + /// configured with this instance. + /// Initialized with by default. + /// + public int MaxDegreeOfParallelism + { + get => this.maxDegreeOfParallelism; + set { - get => this.memoryAllocator; - set + if (value is 0 or < (-1)) { - Guard.NotNull(value, nameof(this.MemoryAllocator)); - this.memoryAllocator = value; + throw new ArgumentOutOfRangeException(nameof(this.MaxDegreeOfParallelism)); } + + this.maxDegreeOfParallelism = value; } + } - /// - /// Gets the maximum header size of all the formats. - /// - internal int MaxHeaderSize => this.ImageFormatsManager.MaxHeaderSize; - - /// - /// Gets or sets the filesystem helper for accessing the local file system. - /// - internal IFileSystem FileSystem { get; set; } = new LocalFileSystem(); - - /// - /// Gets or sets the working buffer size hint for image processors. - /// The default value is 1MB. - /// - /// - /// Currently only used by Resize. - /// - internal int WorkingBufferSizeHintInBytes { get; set; } = 1 * 1024 * 1024; - - /// - /// Registers a new format provider. - /// - /// The configuration provider to call configure on. - public void Configure(IConfigurationModule configuration) + /// + /// Gets the currently registered s. + /// + public IEnumerable ImageFormats => this.ImageFormatsManager.ImageFormats; + + /// + /// Gets or sets the position in a stream to use for reading when using a seekable stream as an image data source. + /// + public ReadOrigin ReadOrigin { get; set; } = ReadOrigin.Current; + + /// + /// Gets or sets the that is currently in use. + /// + public TextureFormatManager ImageFormatsManager { get; set; } = new(); + + /// + /// Gets or sets the that is currently in use. + /// + public MemoryAllocator MemoryAllocator + { + get => this.memoryAllocator; + set { - Guard.NotNull(configuration, nameof(configuration)); - configuration.Configure(this); + Guard.NotNull(value, nameof(this.MemoryAllocator)); + this.memoryAllocator = value; } + } - /// - /// Creates a shallow copy of the . - /// - /// A new configuration instance. - public Configuration Clone() => new() - { - MaxDegreeOfParallelism = this.MaxDegreeOfParallelism, - ImageFormatsManager = this.ImageFormatsManager, - MemoryAllocator = this.MemoryAllocator, - ReadOrigin = this.ReadOrigin, - FileSystem = this.FileSystem, - WorkingBufferSizeHintInBytes = this.WorkingBufferSizeHintInBytes, - }; - - /// - /// Creates the default instance with the following s preregistered: - /// - /// - /// The default configuration of . - internal static Configuration CreateDefaultInstance() => new( - new DdsConfigurationModule(), - new KtxConfigurationModule(), - new Ktx2ConfigurationModule()); + /// + /// Gets the maximum header size of all the formats. + /// + internal int MaxHeaderSize => this.ImageFormatsManager.MaxHeaderSize; + + /// + /// Gets or sets the filesystem helper for accessing the local file system. + /// + internal IFileSystem FileSystem { get; set; } = new LocalFileSystem(); + + /// + /// Gets or sets the working buffer size hint for image processors. + /// The default value is 1MB. + /// + /// + /// Currently only used by Resize. + /// + internal int WorkingBufferSizeHintInBytes { get; set; } = 1 * 1024 * 1024; + + /// + /// Registers a new format provider. + /// + /// The configuration provider to call configure on. + public void Configure(IConfigurationModule configuration) + { + Guard.NotNull(configuration, nameof(configuration)); + configuration.Configure(this); } + + /// + /// Creates a shallow copy of the . + /// + /// A new configuration instance. + public Configuration Clone() => new() + { + MaxDegreeOfParallelism = this.MaxDegreeOfParallelism, + ImageFormatsManager = this.ImageFormatsManager, + MemoryAllocator = this.MemoryAllocator, + ReadOrigin = this.ReadOrigin, + FileSystem = this.FileSystem, + WorkingBufferSizeHintInBytes = this.WorkingBufferSizeHintInBytes, + }; + + /// + /// Creates the default instance with the following s preregistered: + /// + /// + /// The default configuration of . + internal static Configuration CreateDefaultInstance() => new( + new DdsConfigurationModule(), + new KtxConfigurationModule(), + new Ktx2ConfigurationModule()); } diff --git a/src/ImageSharp.Textures/Formats/Dds/DdsConfigurationModule.cs b/src/ImageSharp.Textures/Formats/Dds/DdsConfigurationModule.cs index c81d20b7..88085655 100644 --- a/src/ImageSharp.Textures/Formats/Dds/DdsConfigurationModule.cs +++ b/src/ImageSharp.Textures/Formats/Dds/DdsConfigurationModule.cs @@ -1,18 +1,17 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.Formats.Dds +namespace SixLabors.ImageSharp.Textures.Formats.Dds; + +/// +/// Registers the image encoders, decoders and mime type detectors for texture formats. +/// +public sealed class DdsConfigurationModule : IConfigurationModule { - /// - /// Registers the image encoders, decoders and mime type detectors for texture formats. - /// - public sealed class DdsConfigurationModule : IConfigurationModule + /// + public void Configure(Configuration configuration) { - /// - public void Configure(Configuration configuration) - { - configuration.ImageFormatsManager.SetDecoder(DdsFormat.Instance, new DdsDecoder()); - configuration.ImageFormatsManager.AddImageFormatDetector(new DdsImageFormatDetector()); - } + configuration.ImageFormatsManager.SetDecoder(DdsFormat.Instance, new DdsDecoder()); + configuration.ImageFormatsManager.AddImageFormatDetector(new DdsImageFormatDetector()); } } diff --git a/src/ImageSharp.Textures/Formats/Dds/DdsConstants.cs b/src/ImageSharp.Textures/Formats/Dds/DdsConstants.cs index 0caac846..c7ec94dd 100644 --- a/src/ImageSharp.Textures/Formats/Dds/DdsConstants.cs +++ b/src/ImageSharp.Textures/Formats/Dds/DdsConstants.cs @@ -1,35 +1,32 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.Collections.Generic; +namespace SixLabors.ImageSharp.Textures.Formats.Dds; -namespace SixLabors.ImageSharp.Textures.Formats.Dds +internal static class DdsConstants { - internal static class DdsConstants - { - /// - /// The list of mimetypes that equate to a dds file. - /// - public static readonly IEnumerable MimeTypes = new[] { "image/vnd.ms-dds" }; + /// + /// The list of mimetypes that equate to a dds file. + /// + public static readonly IEnumerable MimeTypes = new[] { "image/vnd.ms-dds" }; - /// - /// The list of file extensions that equate to a dds file. - /// - public static readonly IEnumerable FileExtensions = new[] { "dds" }; + /// + /// The list of file extensions that equate to a dds file. + /// + public static readonly IEnumerable FileExtensions = new[] { "dds" }; - /// - /// The dds header size in bytes. - /// - public const int DdsHeaderSize = 124; + /// + /// The dds header size in bytes. + /// + public const int DdsHeaderSize = 124; - /// - /// The dds pixel format size in bytes. - /// - public const int DdsPixelFormatSize = 32; + /// + /// The dds pixel format size in bytes. + /// + public const int DdsPixelFormatSize = 32; - /// - /// The dds dxt10 header size in bytes. - /// - public const int DdsDxt10HeaderSize = 20; - } + /// + /// The dds dxt10 header size in bytes. + /// + public const int DdsDxt10HeaderSize = 20; } diff --git a/src/ImageSharp.Textures/Formats/Dds/DdsDecoder.cs b/src/ImageSharp.Textures/Formats/Dds/DdsDecoder.cs index 6291bf5d..c4983089 100644 --- a/src/ImageSharp.Textures/Formats/Dds/DdsDecoder.cs +++ b/src/ImageSharp.Textures/Formats/Dds/DdsDecoder.cs @@ -1,29 +1,26 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.IO; +namespace SixLabors.ImageSharp.Textures.Formats.Dds; -namespace SixLabors.ImageSharp.Textures.Formats.Dds +/// +/// Image decoder for DDS textures. +/// +public sealed class DdsDecoder : ITextureDecoder, IDdsDecoderOptions, ITextureInfoDetector { - /// - /// Image decoder for DDS textures. - /// - public sealed class DdsDecoder : ITextureDecoder, IDdsDecoderOptions, ITextureInfoDetector + /// + public Texture DecodeTexture(Configuration configuration, Stream stream) { - /// - public Texture DecodeTexture(Configuration configuration, Stream stream) - { - Guard.NotNull(stream, nameof(stream)); + Guard.NotNull(stream, nameof(stream)); - return new DdsDecoderCore(configuration, this).DecodeTexture(stream); - } + return new DdsDecoderCore(configuration, this).DecodeTexture(stream); + } - /// - public ITextureInfo Identify(Configuration configuration, Stream stream) - { - Guard.NotNull(stream, nameof(stream)); + /// + public ITextureInfo Identify(Configuration configuration, Stream stream) + { + Guard.NotNull(stream, nameof(stream)); - return new DdsDecoderCore(configuration, this).Identify(stream); - } + return new DdsDecoderCore(configuration, this).Identify(stream); } } diff --git a/src/ImageSharp.Textures/Formats/Dds/DdsDecoderCore.cs b/src/ImageSharp.Textures/Formats/Dds/DdsDecoderCore.cs index 1bb1d0b5..f99bde8e 100644 --- a/src/ImageSharp.Textures/Formats/Dds/DdsDecoderCore.cs +++ b/src/ImageSharp.Textures/Formats/Dds/DdsDecoderCore.cs @@ -1,9 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Buffers.Binary; -using System.IO; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Textures.Common.Exceptions; using SixLabors.ImageSharp.Textures.Common.Extensions; @@ -12,199 +10,198 @@ using SixLabors.ImageSharp.Textures.Formats.Dds.Processing; using SixLabors.ImageSharp.Textures.TextureFormats; -namespace SixLabors.ImageSharp.Textures.Formats.Dds +namespace SixLabors.ImageSharp.Textures.Formats.Dds; + +/// +/// Performs the dds decoding operation. +/// +internal sealed class DdsDecoderCore { /// - /// Performs the dds decoding operation. + /// The file header containing general information about the texture. + /// + private DdsHeader ddsHeader; + + /// + /// The dxt10 header if available /// - internal sealed class DdsDecoderCore + private DdsHeaderDxt10 ddsDxt10header; + + /// + /// The global configuration. + /// + private readonly Configuration configuration; + + /// + /// Used for allocating memory during processing operations. + /// + private readonly MemoryAllocator memoryAllocator; + + /// + /// The texture decoder options. + /// + private readonly IDdsDecoderOptions options; + + /// + /// Initializes a new instance of the class. + /// + /// The configuration. + /// The options. + public DdsDecoderCore(Configuration configuration, IDdsDecoderOptions options) { - /// - /// The file header containing general information about the texture. - /// - private DdsHeader ddsHeader; - - /// - /// The dxt10 header if available - /// - private DdsHeaderDxt10 ddsDxt10header; - - /// - /// The global configuration. - /// - private readonly Configuration configuration; - - /// - /// Used for allocating memory during processing operations. - /// - private readonly MemoryAllocator memoryAllocator; - - /// - /// The texture decoder options. - /// - private readonly IDdsDecoderOptions options; - - /// - /// Initializes a new instance of the class. - /// - /// The configuration. - /// The options. - public DdsDecoderCore(Configuration configuration, IDdsDecoderOptions options) - { - this.configuration = configuration; - this.memoryAllocator = configuration.MemoryAllocator; - this.options = options; - } + this.configuration = configuration; + this.memoryAllocator = configuration.MemoryAllocator; + this.options = options; + } - /// - /// Decodes the texture from the specified stream. - /// - /// The stream, where the texture should be decoded from. Cannot be null. - /// The decoded image. - public Texture DecodeTexture(Stream stream) + /// + /// Decodes the texture from the specified stream. + /// + /// The stream, where the texture should be decoded from. Cannot be null. + /// The decoded image. + public Texture DecodeTexture(Stream stream) + { + try { - try - { - this.ReadFileHeader(stream); + this.ReadFileHeader(stream); - if (this.ddsHeader.Width == 0 || this.ddsHeader.Height == 0) - { - throw new UnknownTextureFormatException("Width or height cannot be 0"); - } + if (this.ddsHeader.Width == 0 || this.ddsHeader.Height == 0) + { + throw new UnknownTextureFormatException("Width or height cannot be 0"); + } - var ddsProcessor = new DdsProcessor(this.ddsHeader, this.ddsDxt10header); + DdsProcessor ddsProcessor = new DdsProcessor(this.ddsHeader, this.ddsDxt10header); - int width = (int)this.ddsHeader.Width; - int height = (int)this.ddsHeader.Height; - int count = this.ddsHeader.TextureCount(); + int width = (int)this.ddsHeader.Width; + int height = (int)this.ddsHeader.Height; + int count = this.ddsHeader.TextureCount(); - if (this.ddsHeader.IsVolumeTexture()) - { - int depths = this.ddsHeader.ComputeDepth(); + if (this.ddsHeader.IsVolumeTexture()) + { + int depths = this.ddsHeader.ComputeDepth(); - var texture = new VolumeTexture(); - var surfaces = new FlatTexture[depths]; + VolumeTexture texture = new VolumeTexture(); + FlatTexture[] surfaces = new FlatTexture[depths]; - for (int i = 0; i < count; i++) + for (int i = 0; i < count; i++) + { + for (int depth = 0; depth < depths; depth++) { - for (int depth = 0; depth < depths; depth++) + if (i == 0) { - if (i == 0) - { - surfaces[depth] = new FlatTexture(); - } - - MipMap[] mipMaps = ddsProcessor.DecodeDds(stream, width, height, 1); - surfaces[depth].MipMaps.AddRange(mipMaps); + surfaces[depth] = new FlatTexture(); } - depths >>= 1; - width >>= 1; - height >>= 1; + MipMap[] mipMaps = ddsProcessor.DecodeDds(stream, width, height, 1); + surfaces[depth].MipMaps.AddRange(mipMaps); } - texture.Slices.AddRange(surfaces); - return texture; + depths >>= 1; + width >>= 1; + height >>= 1; } - else if (this.ddsHeader.IsCubemap()) - { - DdsSurfaceType[] faces = this.ddsHeader.GetExistingCubemapFaces() ?? Array.Empty(); - var texture = new CubemapTexture(); - for (int face = 0; face < faces.Length; face++) - { - MipMap[] mipMaps = ddsProcessor.DecodeDds(stream, width, height, count); - if (faces[face] == DdsSurfaceType.CubemapPositiveX) - { - texture.PositiveX.MipMaps.AddRange(mipMaps); - } + texture.Slices.AddRange(surfaces); + return texture; + } + else if (this.ddsHeader.IsCubemap()) + { + DdsSurfaceType[] faces = this.ddsHeader.GetExistingCubemapFaces() ?? Array.Empty(); - if (faces[face] == DdsSurfaceType.CubemapNegativeX) - { - texture.NegativeX.MipMaps.AddRange(mipMaps); - } + CubemapTexture texture = new CubemapTexture(); + for (int face = 0; face < faces.Length; face++) + { + MipMap[] mipMaps = ddsProcessor.DecodeDds(stream, width, height, count); + if (faces[face] == DdsSurfaceType.CubemapPositiveX) + { + texture.PositiveX.MipMaps.AddRange(mipMaps); + } - if (faces[face] == DdsSurfaceType.CubemapPositiveY) - { - texture.PositiveY.MipMaps.AddRange(mipMaps); - } + if (faces[face] == DdsSurfaceType.CubemapNegativeX) + { + texture.NegativeX.MipMaps.AddRange(mipMaps); + } - if (faces[face] == DdsSurfaceType.CubemapNegativeY) - { - texture.NegativeY.MipMaps.AddRange(mipMaps); - } + if (faces[face] == DdsSurfaceType.CubemapPositiveY) + { + texture.PositiveY.MipMaps.AddRange(mipMaps); + } - if (faces[face] == DdsSurfaceType.CubemapPositiveZ) - { - texture.PositiveZ.MipMaps.AddRange(mipMaps); - } + if (faces[face] == DdsSurfaceType.CubemapNegativeY) + { + texture.NegativeY.MipMaps.AddRange(mipMaps); + } - if (faces[face] == DdsSurfaceType.CubemapNegativeZ) - { - texture.NegativeZ.MipMaps.AddRange(mipMaps); - } + if (faces[face] == DdsSurfaceType.CubemapPositiveZ) + { + texture.PositiveZ.MipMaps.AddRange(mipMaps); } - return texture; - } - else - { - var texture = new FlatTexture(); - MipMap[] mipMaps = ddsProcessor.DecodeDds(stream, width, height, count); - texture.MipMaps.AddRange(mipMaps); - return texture; + if (faces[face] == DdsSurfaceType.CubemapNegativeZ) + { + texture.NegativeZ.MipMaps.AddRange(mipMaps); + } } + + return texture; } - catch (IndexOutOfRangeException e) + else { - throw new TextureFormatException("Dds image does not have a valid format.", e); + FlatTexture texture = new FlatTexture(); + MipMap[] mipMaps = ddsProcessor.DecodeDds(stream, width, height, count); + texture.MipMaps.AddRange(mipMaps); + return texture; } } - - /// - /// Reads the raw image information from the specified stream. - /// - /// The containing image data. - public ITextureInfo Identify(Stream stream) + catch (IndexOutOfRangeException e) { - this.ReadFileHeader(stream); + throw new TextureFormatException("Dds image does not have a valid format.", e); + } + } - D3dFormat d3dFormat = this.ddsHeader.PixelFormat.GetD3DFormat(); - DxgiFormat dxgiFormat = this.ddsHeader.ShouldHaveDxt10Header() ? this.ddsDxt10header.DxgiFormat : DxgiFormat.Unknown; - int bitsPerPixel = DdsTools.GetBitsPerPixel(d3dFormat, dxgiFormat); + /// + /// Reads the raw image information from the specified stream. + /// + /// The containing image data. + public ITextureInfo Identify(Stream stream) + { + this.ReadFileHeader(stream); - return new TextureInfo( - new TextureTypeInfo(bitsPerPixel), - (int)this.ddsHeader.Width, - (int)this.ddsHeader.Height); - } + D3dFormat d3dFormat = this.ddsHeader.PixelFormat.GetD3DFormat(); + DxgiFormat dxgiFormat = this.ddsHeader.ShouldHaveDxt10Header() ? this.ddsDxt10header.DxgiFormat : DxgiFormat.Unknown; + int bitsPerPixel = DdsTools.GetBitsPerPixel(d3dFormat, dxgiFormat); - /// - /// Reads the dds file header from the stream. - /// - /// The containing texture data. - private void ReadFileHeader(Stream stream) + return new TextureInfo( + new TextureTypeInfo(bitsPerPixel), + (int)this.ddsHeader.Width, + (int)this.ddsHeader.Height); + } + + /// + /// Reads the dds file header from the stream. + /// + /// The containing texture data. + private void ReadFileHeader(Stream stream) + { + Span magicBuffer = stackalloc byte[4]; + stream.Read(magicBuffer, 0, 4); + uint magicValue = BinaryPrimitives.ReadUInt32LittleEndian(magicBuffer); + if (magicValue != DdsFourCc.DdsMagicWord) { - Span magicBuffer = stackalloc byte[4]; - stream.Read(magicBuffer, 0, 4); - uint magicValue = BinaryPrimitives.ReadUInt32LittleEndian(magicBuffer); - if (magicValue != DdsFourCc.DdsMagicWord) - { - throw new NotSupportedException("Invalid DDS magic value."); - } + throw new NotSupportedException("Invalid DDS magic value."); + } - byte[] ddsHeaderBuffer = new byte[DdsConstants.DdsHeaderSize]; + byte[] ddsHeaderBuffer = new byte[DdsConstants.DdsHeaderSize]; - stream.Read(ddsHeaderBuffer, 0, DdsConstants.DdsHeaderSize); - this.ddsHeader = DdsHeader.Parse(ddsHeaderBuffer); - this.ddsHeader.Validate(); + stream.Read(ddsHeaderBuffer, 0, DdsConstants.DdsHeaderSize); + this.ddsHeader = DdsHeader.Parse(ddsHeaderBuffer); + this.ddsHeader.Validate(); - if (this.ddsHeader.ShouldHaveDxt10Header()) - { - byte[] ddsDxt10headerBuffer = new byte[DdsConstants.DdsDxt10HeaderSize]; - stream.Read(ddsDxt10headerBuffer, 0, DdsConstants.DdsDxt10HeaderSize); - this.ddsDxt10header = DdsHeaderDxt10.Parse(ddsDxt10headerBuffer); - } + if (this.ddsHeader.ShouldHaveDxt10Header()) + { + byte[] ddsDxt10headerBuffer = new byte[DdsConstants.DdsDxt10HeaderSize]; + stream.Read(ddsDxt10headerBuffer, 0, DdsConstants.DdsDxt10HeaderSize); + this.ddsDxt10header = DdsHeaderDxt10.Parse(ddsDxt10headerBuffer); } } } diff --git a/src/ImageSharp.Textures/Formats/Dds/DdsFormat.cs b/src/ImageSharp.Textures/Formats/Dds/DdsFormat.cs index 7028ce09..b19c0ab0 100644 --- a/src/ImageSharp.Textures/Formats/Dds/DdsFormat.cs +++ b/src/ImageSharp.Textures/Formats/Dds/DdsFormat.cs @@ -1,37 +1,34 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.Collections.Generic; +namespace SixLabors.ImageSharp.Textures.Formats.Dds; -namespace SixLabors.ImageSharp.Textures.Formats.Dds +/// +/// Registers the texture decoders and mime type detectors for the dds format. +/// +public sealed class DdsFormat : ITextureFormat { /// - /// Registers the texture decoders and mime type detectors for the dds format. + /// Prevents a default instance of the class from being created. /// - public sealed class DdsFormat : ITextureFormat + private DdsFormat() { - /// - /// Prevents a default instance of the class from being created. - /// - private DdsFormat() - { - } + } - /// - /// Gets the current instance. - /// - public static DdsFormat Instance { get; } = new DdsFormat(); + /// + /// Gets the current instance. + /// + public static DdsFormat Instance { get; } = new DdsFormat(); - /// - public string Name => "DDS"; + /// + public string Name => "DDS"; - /// - public string DefaultMimeType => "image/vnd.ms-dds"; + /// + public string DefaultMimeType => "image/vnd.ms-dds"; - /// - public IEnumerable MimeTypes => DdsConstants.MimeTypes; + /// + public IEnumerable MimeTypes => DdsConstants.MimeTypes; - /// - public IEnumerable FileExtensions => DdsConstants.FileExtensions; - } + /// + public IEnumerable FileExtensions => DdsConstants.FileExtensions; } diff --git a/src/ImageSharp.Textures/Formats/Dds/DdsFourCC.cs b/src/ImageSharp.Textures/Formats/Dds/DdsFourCC.cs index 2f922330..e549205b 100644 --- a/src/ImageSharp.Textures/Formats/Dds/DdsFourCC.cs +++ b/src/ImageSharp.Textures/Formats/Dds/DdsFourCC.cs @@ -2,73 +2,72 @@ // Licensed under the Six Labors Split License. // ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Textures.Formats.Dds +namespace SixLabors.ImageSharp.Textures.Formats.Dds; + +/// +/// Four character codes constants used in DDS files. +/// +internal static class DdsFourCc { - /// - /// Four character codes constants used in DDS files. - /// - internal static class DdsFourCc - { - public const uint DdsMagicWord = 'D' | ('D' << 8) | ('S' << 16) | (' ' << 24); + public const uint DdsMagicWord = 'D' | ('D' << 8) | ('S' << 16) | (' ' << 24); - public const uint None = 0; + public const uint None = 0; - public const uint DX10 = 'D' | ('X' << 8) | ('1' << 16) | ('0' << 24); + public const uint DX10 = 'D' | ('X' << 8) | ('1' << 16) | ('0' << 24); - public const uint UYVY = 'U' | ('Y' << 8) | ('V' << 16) | ('Y' << 24); + public const uint UYVY = 'U' | ('Y' << 8) | ('V' << 16) | ('Y' << 24); - public const uint RGBG = 'R' | ('G' << 8) | ('B' << 16) | ('G' << 24); + public const uint RGBG = 'R' | ('G' << 8) | ('B' << 16) | ('G' << 24); - public const uint YUY2 = 'Y' | ('U' << 8) | ('Y' << 16) | ('2' << 24); + public const uint YUY2 = 'Y' | ('U' << 8) | ('Y' << 16) | ('2' << 24); - public const uint GRGB = 'G' | ('R' << 8) | ('G' << 16) | ('B' << 24); + public const uint GRGB = 'G' | ('R' << 8) | ('G' << 16) | ('B' << 24); - public const uint DXT1 = 'D' | ('X' << 8) | ('T' << 16) | ('1' << 24); + public const uint DXT1 = 'D' | ('X' << 8) | ('T' << 16) | ('1' << 24); - public const uint DXT2 = 'D' | ('X' << 8) | ('T' << 16) | ('2' << 24); + public const uint DXT2 = 'D' | ('X' << 8) | ('T' << 16) | ('2' << 24); - public const uint DXT3 = 'D' | ('X' << 8) | ('T' << 16) | ('3' << 24); + public const uint DXT3 = 'D' | ('X' << 8) | ('T' << 16) | ('3' << 24); - public const uint DXT4 = 'D' | ('X' << 8) | ('T' << 16) | ('4' << 24); + public const uint DXT4 = 'D' | ('X' << 8) | ('T' << 16) | ('4' << 24); - public const uint DXT5 = 'D' | ('X' << 8) | ('T' << 16) | ('5' << 24); + public const uint DXT5 = 'D' | ('X' << 8) | ('T' << 16) | ('5' << 24); - public const uint MET1 = 'M' | ('E' << 8) | ('T' << 16) | ('1' << 24); + public const uint MET1 = 'M' | ('E' << 8) | ('T' << 16) | ('1' << 24); - public const uint BC4U = 'B' | ('C' << 8) | ('4' << 16) | ('U' << 24); + public const uint BC4U = 'B' | ('C' << 8) | ('4' << 16) | ('U' << 24); - public const uint BC4S = 'B' | ('C' << 8) | ('4' << 16) | ('S' << 24); + public const uint BC4S = 'B' | ('C' << 8) | ('4' << 16) | ('S' << 24); - public const uint BC5U = 'B' | ('C' << 8) | ('5' << 16) | ('U' << 24); + public const uint BC5U = 'B' | ('C' << 8) | ('5' << 16) | ('U' << 24); - public const uint BC5S = 'B' | ('C' << 8) | ('5' << 16) | ('S' << 24); + public const uint BC5S = 'B' | ('C' << 8) | ('5' << 16) | ('S' << 24); - public const uint ATI1 = 'A' | ('T' << 8) | ('I' << 16) | ('1' << 24); + public const uint ATI1 = 'A' | ('T' << 8) | ('I' << 16) | ('1' << 24); - public const uint ATI2 = 'A' | ('T' << 8) | ('I' << 16) | ('2' << 24); + public const uint ATI2 = 'A' | ('T' << 8) | ('I' << 16) | ('2' << 24); - // DXGI_FORMAT_R16G16B16A16_UNORM - public const uint R16G16B16A16UNORM = 36; + // DXGI_FORMAT_R16G16B16A16_UNORM + public const uint R16G16B16A16UNORM = 36; - // DXGI_FORMAT_R16G16B16A16_SNORM - public const uint R16G16B16A16SNORM = 110; + // DXGI_FORMAT_R16G16B16A16_SNORM + public const uint R16G16B16A16SNORM = 110; - // DXGI_FORMAT_R16_FLOAT - public const uint R16FLOAT = 111; + // DXGI_FORMAT_R16_FLOAT + public const uint R16FLOAT = 111; - // DXGI_FORMAT_R16G16_FLOAT - public const uint R16G16FLOAT = 112; + // DXGI_FORMAT_R16G16_FLOAT + public const uint R16G16FLOAT = 112; - // D3DFMT_A16B16G16R16F - public const uint R16G16B16A16FLOAT = 113; + // D3DFMT_A16B16G16R16F + public const uint R16G16B16A16FLOAT = 113; - // DXGI_FORMAT_R32_FLOAT - public const uint R32FLOAT = 114; + // DXGI_FORMAT_R32_FLOAT + public const uint R32FLOAT = 114; - // DXGI_FORMAT_R32G32_FLOAT - public const uint R32G32FLOAT = 115; + // DXGI_FORMAT_R32G32_FLOAT + public const uint R32G32FLOAT = 115; - // DXGI_FORMAT_R32G32B32A32_FLOAT - public const uint R32G32B32A32FLOAT = 116; - } + // DXGI_FORMAT_R32G32B32A32_FLOAT + public const uint R32G32B32A32FLOAT = 116; } diff --git a/src/ImageSharp.Textures/Formats/Dds/DdsHeader.cs b/src/ImageSharp.Textures/Formats/Dds/DdsHeader.cs index 27e1271e..4a4280ba 100644 --- a/src/ImageSharp.Textures/Formats/Dds/DdsHeader.cs +++ b/src/ImageSharp.Textures/Formats/Dds/DdsHeader.cs @@ -1,236 +1,234 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Buffers.Binary; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Textures.Formats.Dds.Emums; -namespace SixLabors.ImageSharp.Textures.Formats.Dds +namespace SixLabors.ImageSharp.Textures.Formats.Dds; + +/// +/// Describes a DDS file header. +/// +/// +/// Note When you write .dds files, you should set the and +/// flags, and for mipmapped textures you should also set the +/// flag. +/// However, when you read a .dds file, you should not rely on the , +/// , and +/// flags being set because some writers of such a file might not set these flags. +/// Include flags in for the members of the structure that contain valid data. Use this +/// structure in combination with a to store a resource array in a DDS file. +/// For more information, see texture arrays. +/// is identical to the DirectDraw DDSURFACEDESC2 structure without DirectDraw +/// dependencies. +/// +[StructLayout(LayoutKind.Sequential, Pack = 1)] +internal struct DdsHeader { + public DdsHeader( + uint size, + DdsFlags flags, + uint height, + uint width, + uint pitchOrLinearSize, + uint depth, + uint mipMapCount, + uint[] reserved1, + DdsPixelFormat pixelFormat, + DdsCaps1 caps1, + DdsCaps2 caps2, + uint caps3, + uint caps4, + uint reserved2) + { + this.Size = size; + this.Flags = flags; + this.Height = height; + this.Width = width; + this.PitchOrLinearSize = pitchOrLinearSize; + this.Depth = depth; + this.MipMapCount = mipMapCount; + this.Reserved1 = reserved1; + this.PixelFormat = pixelFormat; + this.Caps1 = caps1; + this.Caps2 = caps2; + this.Caps3 = caps3; + this.Caps4 = caps4; + this.Reserved2 = reserved2; + } + /// - /// Describes a DDS file header. + /// Gets size of structure. This member must be set to 124. + /// + public uint Size { get; } + + /// + /// Gets flags to indicate which members contain valid data. /// /// - /// Note When you write .dds files, you should set the and - /// flags, and for mipmapped textures you should also set the - /// flag. + /// When you write .dds files, you should set the and + /// flags, + /// and for mipmapped textures you should also set the flag. /// However, when you read a .dds file, you should not rely on the , /// , and /// flags being set because some writers of such a file might not set these flags. - /// Include flags in for the members of the structure that contain valid data. Use this - /// structure in combination with a to store a resource array in a DDS file. - /// For more information, see texture arrays. - /// is identical to the DirectDraw DDSURFACEDESC2 structure without DirectDraw - /// dependencies. /// - [StructLayout(LayoutKind.Sequential, Pack = 1)] - internal struct DdsHeader + public DdsFlags Flags { get; } + + /// + /// Gets surface height (in pixels). + /// + public uint Height { get; } + + /// + /// Gets surface width (in pixels). + /// + public uint Width { get; } + + /// + /// Gets the pitch or number of bytes per scan line in an uncompressed texture; + /// the total number of bytes in the top level texture for a compressed texture. + /// + public uint PitchOrLinearSize { get; } + + /// + /// Gets depth of a volume texture (in pixels), otherwise unused. + /// + public uint Depth { get; } + + /// + /// Gets number of mipmap levels, otherwise unused. + /// + public uint MipMapCount { get; } + + /// + /// Gets unused. + /// + public uint[] Reserved1 { get; } + + /// + /// Gets the pixel format. + /// + public DdsPixelFormat PixelFormat { get; } + + /// + /// Gets specifies the complexity of the surfaces stored. + /// + /// + /// When you write .dds files, you should set the flag, + /// and for multiple surfaces you should also set the flag. + /// However, when you read a .dds file, you should not rely on the and + /// flags being set because some file writers might not set these flags. + /// + public DdsCaps1 Caps1 { get; } + + /// + /// Gets defines additional capabilities of the surface. + /// + public DdsCaps2 Caps2 { get; } + + /// + /// Gets unused. + /// + public uint Caps3 { get; } + + /// + /// Gets unused. + /// + public uint Caps4 { get; } + + /// + /// Gets unused. + /// + public uint Reserved2 { get; } + + /// + /// Validates the dds header. + /// + /// + /// Thrown if the image does not pass validation. + /// + public readonly void Validate() { - public DdsHeader( - uint size, - DdsFlags flags, - uint height, - uint width, - uint pitchOrLinearSize, - uint depth, - uint mipMapCount, - uint[] reserved1, - DdsPixelFormat pixelFormat, - DdsCaps1 caps1, - DdsCaps2 caps2, - uint caps3, - uint caps4, - uint reserved2) + bool incorrectSize = (this.Size != DdsConstants.DdsHeaderSize) || (this.PixelFormat.Size != DdsConstants.DdsPixelFormatSize); + if (incorrectSize) { - this.Size = size; - this.Flags = flags; - this.Height = height; - this.Width = width; - this.PitchOrLinearSize = pitchOrLinearSize; - this.Depth = depth; - this.MipMapCount = mipMapCount; - this.Reserved1 = reserved1; - this.PixelFormat = pixelFormat; - this.Caps1 = caps1; - this.Caps2 = caps2; - this.Caps3 = caps3; - this.Caps4 = caps4; - this.Reserved2 = reserved2; + throw new NotSupportedException("Invalid structure size."); } - /// - /// Gets size of structure. This member must be set to 124. - /// - public uint Size { get; } - - /// - /// Gets flags to indicate which members contain valid data. - /// - /// - /// When you write .dds files, you should set the and - /// flags, - /// and for mipmapped textures you should also set the flag. - /// However, when you read a .dds file, you should not rely on the , - /// , and - /// flags being set because some writers of such a file might not set these flags. - /// - public DdsFlags Flags { get; } - - /// - /// Gets surface height (in pixels). - /// - public uint Height { get; } - - /// - /// Gets surface width (in pixels). - /// - public uint Width { get; } - - /// - /// Gets the pitch or number of bytes per scan line in an uncompressed texture; - /// the total number of bytes in the top level texture for a compressed texture. - /// - public uint PitchOrLinearSize { get; } - - /// - /// Gets depth of a volume texture (in pixels), otherwise unused. - /// - public uint Depth { get; } - - /// - /// Gets number of mipmap levels, otherwise unused. - /// - public uint MipMapCount { get; } - - /// - /// Gets unused. - /// - public uint[] Reserved1 { get; } - - /// - /// Gets the pixel format. - /// - public DdsPixelFormat PixelFormat { get; } - - /// - /// Gets specifies the complexity of the surfaces stored. - /// - /// - /// When you write .dds files, you should set the flag, - /// and for multiple surfaces you should also set the flag. - /// However, when you read a .dds file, you should not rely on the and - /// flags being set because some file writers might not set these flags. - /// - public DdsCaps1 Caps1 { get; } - - /// - /// Gets defines additional capabilities of the surface. - /// - public DdsCaps2 Caps2 { get; } - - /// - /// Gets unused. - /// - public uint Caps3 { get; } - - /// - /// Gets unused. - /// - public uint Caps4 { get; } - - /// - /// Gets unused. - /// - public uint Reserved2 { get; } - - /// - /// Validates the dds header. - /// - /// - /// Thrown if the image does not pass validation. - /// - public void Validate() + bool requiredFlagsMissing = (this.Flags & DdsFlags.Caps) == 0 || (this.Flags & DdsFlags.PixelFormat) == 0 || (this.Caps1 & DdsCaps1.Texture) == 0; + if (requiredFlagsMissing) { - bool incorrectSize = (this.Size != DdsConstants.DdsHeaderSize) || (this.PixelFormat.Size != DdsConstants.DdsPixelFormatSize); - if (incorrectSize) - { - throw new NotSupportedException("Invalid structure size."); - } - - bool requiredFlagsMissing = (this.Flags & DdsFlags.Caps) == 0 || (this.Flags & DdsFlags.PixelFormat) == 0 || (this.Caps1 & DdsCaps1.Texture) == 0; - if (requiredFlagsMissing) - { - throw new NotSupportedException("Required flags missing."); - } - - bool hasInvalidCompression = (this.Flags & DdsFlags.Pitch) != 0 && (this.Flags & DdsFlags.LinearSize) != 0; - if (hasInvalidCompression) - { - throw new NotSupportedException("Invalid compression."); - } + throw new NotSupportedException("Required flags missing."); } - public void WriteTo(Span buffer) + bool hasInvalidCompression = (this.Flags & DdsFlags.Pitch) != 0 && (this.Flags & DdsFlags.LinearSize) != 0; + if (hasInvalidCompression) { - BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(0, 4), this.Size); - BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(4, 4), (uint)this.Flags); - BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(8, 4), this.Height); - BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(12, 4), this.Width); - BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(16, 4), this.PitchOrLinearSize); - BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(20, 4), this.Depth); - BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(24, 4), this.MipMapCount); - BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(28, 4), this.Reserved1[0]); - BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(32, 4), this.Reserved1[1]); - BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(36, 4), this.Reserved1[2]); - BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(40, 4), this.Reserved1[3]); - BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(44, 4), this.Reserved1[4]); - BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(48, 4), this.Reserved1[5]); - BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(52, 4), this.Reserved1[6]); - BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(56, 4), this.Reserved1[7]); - BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(60, 4), this.Reserved1[8]); - BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(64, 4), this.Reserved1[9]); - BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(68, 4), this.Reserved1[10]); - this.PixelFormat.WriteTo(buffer, 72); - BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(104, 4), (uint)this.Caps1); - BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(108, 4), (uint)this.Caps2); - BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(112, 4), this.Caps3); - BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(116, 4), this.Caps4); - BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(120, 4), this.Reserved2); + throw new NotSupportedException("Invalid compression."); } + } - public static DdsHeader Parse(Span data) - { - uint[] reserved1 = - { - BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(28, 4)), - BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(32, 4)), - BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(36, 4)), - BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(40, 4)), - BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(44, 4)), - BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(48, 4)), - BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(52, 4)), - BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(56, 4)), - BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(60, 4)), - BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(64, 4)), - BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(68, 4)) - }; - - return new DdsHeader( - size: BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(0, 4)), - flags: (DdsFlags)BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(4, 4)), - height: BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(8, 4)), - width: BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(12, 4)), - pitchOrLinearSize: BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(16, 4)), - depth: BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(20, 4)), - mipMapCount: BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(24, 4)), - reserved1: reserved1, - pixelFormat: DdsPixelFormat.Parse(data, 72), - caps1: (DdsCaps1)BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(104, 4)), - caps2: (DdsCaps2)BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(108, 4)), - caps3: BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(112, 4)), - caps4: BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(116, 4)), - reserved2: BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(120, 4))); - } + public readonly void WriteTo(Span buffer) + { + BinaryPrimitives.WriteUInt32LittleEndian(buffer[..4], this.Size); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(4, 4), (uint)this.Flags); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(8, 4), this.Height); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(12, 4), this.Width); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(16, 4), this.PitchOrLinearSize); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(20, 4), this.Depth); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(24, 4), this.MipMapCount); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(28, 4), this.Reserved1[0]); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(32, 4), this.Reserved1[1]); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(36, 4), this.Reserved1[2]); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(40, 4), this.Reserved1[3]); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(44, 4), this.Reserved1[4]); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(48, 4), this.Reserved1[5]); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(52, 4), this.Reserved1[6]); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(56, 4), this.Reserved1[7]); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(60, 4), this.Reserved1[8]); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(64, 4), this.Reserved1[9]); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(68, 4), this.Reserved1[10]); + this.PixelFormat.WriteTo(buffer, 72); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(104, 4), (uint)this.Caps1); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(108, 4), (uint)this.Caps2); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(112, 4), this.Caps3); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(116, 4), this.Caps4); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(120, 4), this.Reserved2); + } + + public static DdsHeader Parse(Span data) + { + uint[] reserved1 = + [ + BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(28, 4)), + BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(32, 4)), + BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(36, 4)), + BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(40, 4)), + BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(44, 4)), + BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(48, 4)), + BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(52, 4)), + BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(56, 4)), + BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(60, 4)), + BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(64, 4)), + BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(68, 4)) + ]; + + return new DdsHeader( + size: BinaryPrimitives.ReadUInt32LittleEndian(data[..4]), + flags: (DdsFlags)BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(4, 4)), + height: BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(8, 4)), + width: BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(12, 4)), + pitchOrLinearSize: BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(16, 4)), + depth: BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(20, 4)), + mipMapCount: BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(24, 4)), + reserved1: reserved1, + pixelFormat: DdsPixelFormat.Parse(data, 72), + caps1: (DdsCaps1)BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(104, 4)), + caps2: (DdsCaps2)BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(108, 4)), + caps3: BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(112, 4)), + caps4: BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(116, 4)), + reserved2: BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(120, 4))); } } diff --git a/src/ImageSharp.Textures/Formats/Dds/DdsHeaderDxt10.cs b/src/ImageSharp.Textures/Formats/Dds/DdsHeaderDxt10.cs index 25494921..c52fa550 100644 --- a/src/ImageSharp.Textures/Formats/Dds/DdsHeaderDxt10.cs +++ b/src/ImageSharp.Textures/Formats/Dds/DdsHeaderDxt10.cs @@ -1,111 +1,109 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Buffers.Binary; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Textures.Formats.Dds.Emums; -namespace SixLabors.ImageSharp.Textures.Formats.Dds +namespace SixLabors.ImageSharp.Textures.Formats.Dds; + +/// +/// DDS header extension to handle resource arrays. +/// +/// +/// Use this structure together with a to store a resource array in a DDS file. +/// For more information, see texture arrays. +/// This header is present if the member of the +/// structure is set to 'DX10'. +/// +[StructLayout(LayoutKind.Sequential, Pack = 1)] +internal struct DdsHeaderDxt10 { - /// - /// DDS header extension to handle resource arrays. - /// - /// - /// Use this structure together with a to store a resource array in a DDS file. - /// For more information, see texture arrays. - /// This header is present if the member of the - /// structure is set to 'DX10'. - /// - [StructLayout(LayoutKind.Sequential, Pack = 1)] - internal struct DdsHeaderDxt10 + public DdsHeaderDxt10( + DxgiFormat dxgiFormat, + D3d10ResourceDimension resourceDimension, + D3d10ResourceMiscFlags miscFlag, + uint arraySize, + uint reserved) { - public DdsHeaderDxt10( - DxgiFormat dxgiFormat, - D3d10ResourceDimension resourceDimension, - D3d10ResourceMiscFlags miscFlag, - uint arraySize, - uint reserved) - { - this.DxgiFormat = dxgiFormat; - this.ResourceDimension = resourceDimension; - this.MiscFlag = miscFlag; - this.ArraySize = arraySize; - this.Reserved = reserved; - } - - /// - /// Gets the surface pixel format. - /// - public DxgiFormat DxgiFormat { get; } + this.DxgiFormat = dxgiFormat; + this.ResourceDimension = resourceDimension; + this.MiscFlag = miscFlag; + this.ArraySize = arraySize; + this.Reserved = reserved; + } - /// - /// Gets identifies the type of resource. - /// The following values for this member are a subset of the values in the - /// enumeration: - /// : - /// Resource is a 1D texture. The member of - /// specifies the size of the texture. Typically, you set the member of - /// to 1; you also must set the flag in - /// the member of . - /// : - /// Resource is a 2D texture with an area specified by the and - /// members of . - /// You can also use this type to identify a cube-map texture. For more information about how to identify a - /// cube-map texture, see and members. - /// : - /// Resource is a 3D texture with a volume specified by the , - /// , and members of - /// . You also must set the flag - /// in the member of . - /// - public D3d10ResourceDimension ResourceDimension { get; } + /// + /// Gets the surface pixel format. + /// + public DxgiFormat DxgiFormat { get; } - /// - /// Gets identifies other, less common options for resources. The following value for this member is a - /// subset of the values in the enumeration: - /// DDS_RESOURCE_MISC_TEXTURECUBE Indicates a 2D texture is a cube-map texture. - /// - public D3d10ResourceMiscFlags MiscFlag { get; } + /// + /// Gets identifies the type of resource. + /// The following values for this member are a subset of the values in the + /// enumeration: + /// : + /// Resource is a 1D texture. The member of + /// specifies the size of the texture. Typically, you set the member of + /// to 1; you also must set the flag in + /// the member of . + /// : + /// Resource is a 2D texture with an area specified by the and + /// members of . + /// You can also use this type to identify a cube-map texture. For more information about how to identify a + /// cube-map texture, see and members. + /// : + /// Resource is a 3D texture with a volume specified by the , + /// , and members of + /// . You also must set the flag + /// in the member of . + /// + public D3d10ResourceDimension ResourceDimension { get; } - /// - /// Gets the number of elements in the array. - /// For a 2D texture that is also a cube-map texture, this number represents the number of cubes. - /// This number is the same as the number in the NumCubes member of D3D10_TEXCUBE_ARRAY_SRV1 or - /// D3D11_TEXCUBE_ARRAY_SRV). In this case, the DDS file contains arraySize*6 2D textures. - /// For more information about this case, see the description. - /// For a 3D texture, you must set this number to 1. - /// - public uint ArraySize { get; } + /// + /// Gets identifies other, less common options for resources. The following value for this member is a + /// subset of the values in the enumeration: + /// DDS_RESOURCE_MISC_TEXTURECUBE Indicates a 2D texture is a cube-map texture. + /// + public D3d10ResourceMiscFlags MiscFlag { get; } - /// - /// Gets reserved for future use. - /// - public uint Reserved { get; } + /// + /// Gets the number of elements in the array. + /// For a 2D texture that is also a cube-map texture, this number represents the number of cubes. + /// This number is the same as the number in the NumCubes member of D3D10_TEXCUBE_ARRAY_SRV1 or + /// D3D11_TEXCUBE_ARRAY_SRV). In this case, the DDS file contains arraySize*6 2D textures. + /// For more information about this case, see the description. + /// For a 3D texture, you must set this number to 1. + /// + public uint ArraySize { get; } - /// - /// Writes the header to the given buffer. - /// - /// The buffer to write to. - public void WriteTo(Span buffer) - { - BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(0, 4), (uint)this.DxgiFormat); - BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(4, 4), (uint)this.ResourceDimension); - BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(8, 4), (uint)this.MiscFlag); - BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(12, 4), this.ArraySize); - BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(16, 4), this.Reserved); - } + /// + /// Gets reserved for future use. + /// + public uint Reserved { get; } - /// - /// Parses the DDS_HEADER_DXT10 from the given data buffer. - /// - /// The data to parse. - /// The parsed DDS_HEADER_DXT10. - public static DdsHeaderDxt10 Parse(Span data) => new DdsHeaderDxt10( - dxgiFormat: (DxgiFormat)BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(0, 4)), - resourceDimension: (D3d10ResourceDimension)BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(4, 4)), - miscFlag: (D3d10ResourceMiscFlags)BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(8, 4)), - arraySize: BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(12, 4)), - reserved: BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(16, 4))); + /// + /// Writes the header to the given buffer. + /// + /// The buffer to write to. + public readonly void WriteTo(Span buffer) + { + BinaryPrimitives.WriteUInt32LittleEndian(buffer[..4], (uint)this.DxgiFormat); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(4, 4), (uint)this.ResourceDimension); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(8, 4), (uint)this.MiscFlag); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(12, 4), this.ArraySize); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(16, 4), this.Reserved); } + + /// + /// Parses the DDS_HEADER_DXT10 from the given data buffer. + /// + /// The data to parse. + /// The parsed DDS_HEADER_DXT10. + public static DdsHeaderDxt10 Parse(Span data) => new( + dxgiFormat: (DxgiFormat)BinaryPrimitives.ReadUInt32LittleEndian(data[..4]), + resourceDimension: (D3d10ResourceDimension)BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(4, 4)), + miscFlag: (D3d10ResourceMiscFlags)BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(8, 4)), + arraySize: BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(12, 4)), + reserved: BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(16, 4))); } diff --git a/src/ImageSharp.Textures/Formats/Dds/DdsImageFormatDetector.cs b/src/ImageSharp.Textures/Formats/Dds/DdsImageFormatDetector.cs index 0a352438..b4248348 100644 --- a/src/ImageSharp.Textures/Formats/Dds/DdsImageFormatDetector.cs +++ b/src/ImageSharp.Textures/Formats/Dds/DdsImageFormatDetector.cs @@ -1,31 +1,29 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Buffers.Binary; -namespace SixLabors.ImageSharp.Textures.Formats.Dds +namespace SixLabors.ImageSharp.Textures.Formats.Dds; + +/// +/// Detects dds file headers. +/// +public sealed class DdsImageFormatDetector : ITextureFormatDetector { - /// - /// Detects dds file headers. - /// - public sealed class DdsImageFormatDetector : ITextureFormatDetector - { - /// - public int HeaderSize => 8; + /// + public int HeaderSize => 8; - /// - public ITextureFormat? DetectFormat(ReadOnlySpan header) => this.IsSupportedFileFormat(header) ? DdsFormat.Instance : null; + /// + public ITextureFormat? DetectFormat(ReadOnlySpan header) => this.IsSupportedFileFormat(header) ? DdsFormat.Instance : null; - private bool IsSupportedFileFormat(ReadOnlySpan header) + private bool IsSupportedFileFormat(ReadOnlySpan header) + { + if (header.Length >= this.HeaderSize) { - if (header.Length >= this.HeaderSize) - { - uint magicValue = BinaryPrimitives.ReadUInt32LittleEndian(header); - return magicValue == DdsFourCc.DdsMagicWord; - } - - return false; + uint magicValue = BinaryPrimitives.ReadUInt32LittleEndian(header); + return magicValue == DdsFourCc.DdsMagicWord; } + + return false; } } diff --git a/src/ImageSharp.Textures/Formats/Dds/DdsPixelFormat.cs b/src/ImageSharp.Textures/Formats/Dds/DdsPixelFormat.cs index 76c4c46c..4d969a89 100644 --- a/src/ImageSharp.Textures/Formats/Dds/DdsPixelFormat.cs +++ b/src/ImageSharp.Textures/Formats/Dds/DdsPixelFormat.cs @@ -1,320 +1,318 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Buffers.Binary; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Textures.Formats.Dds.Emums; -namespace SixLabors.ImageSharp.Textures.Formats.Dds +namespace SixLabors.ImageSharp.Textures.Formats.Dds; + +/// Surface pixel format. +/// +/// To store DXGI formats such as floating-point data, use a of +/// and set to +/// 'D','X','1','0'. Use the extension header to store the DXGI format in the +/// member. +/// Note that there are non-standard variants of DDS files where has +/// and the value +/// is set directly to a D3DFORMAT or enumeration value. It is not possible to +/// disambiguate the D3DFORMAT versus values using this non-standard scheme, so the +/// DX10 extension header is recommended instead. +/// +[StructLayout(LayoutKind.Sequential, Size = 32, Pack = 1)] +internal struct DdsPixelFormat { - /// Surface pixel format. - /// - /// To store DXGI formats such as floating-point data, use a of - /// and set to - /// 'D','X','1','0'. Use the extension header to store the DXGI format in the - /// member. - /// Note that there are non-standard variants of DDS files where has - /// and the value - /// is set directly to a D3DFORMAT or enumeration value. It is not possible to - /// disambiguate the D3DFORMAT versus values using this non-standard scheme, so the - /// DX10 extension header is recommended instead. - /// - [StructLayout(LayoutKind.Sequential, Size = 32, Pack = 1)] - internal struct DdsPixelFormat + public DdsPixelFormat( + uint size, + DdsPixelFormatFlags flags, + uint fourCC, + uint rgbBitCount, + uint rBitMask, + uint gBitMask, + uint bBitMask, + uint aBitMask) { - public DdsPixelFormat( - uint size, - DdsPixelFormatFlags flags, - uint fourCC, - uint rgbBitCount, - uint rBitMask, - uint gBitMask, - uint bBitMask, - uint aBitMask) - { - this.Size = size; - this.Flags = flags; - this.FourCC = fourCC; - this.RGBBitCount = rgbBitCount; - this.RBitMask = rBitMask; - this.GBitMask = gBitMask; - this.BBitMask = bBitMask; - this.ABitMask = aBitMask; - } + this.Size = size; + this.Flags = flags; + this.FourCC = fourCC; + this.RGBBitCount = rgbBitCount; + this.RBitMask = rBitMask; + this.GBitMask = gBitMask; + this.BBitMask = bBitMask; + this.ABitMask = aBitMask; + } - /// - /// Gets Structure size; set to 32 (bytes). - /// - public uint Size { get; } - - /// - /// Gets Values which indicate what type of data is in the surface. - /// - public DdsPixelFormatFlags Flags { get; } - - /// - /// Gets Four-character codes for specifying compressed or custom formats. - /// Possible values include: DXT1, DXT2, DXT3, DXT4, or DXT5. A FOURCC of DX10 indicates the prescense of - /// the extended header, and the - /// member of that structure indicates the - /// true format. When using a four-character code, must include - /// . - /// - public uint FourCC { get; } - - /// - /// Gets Number of bits in an RGB (possibly including alpha) format. Valid when - /// includes , or , - /// or . - /// - public uint RGBBitCount { get; } - - /// - /// Gets Red (or lumiannce or Y) mask for reading color data. For instance, given the A8R8G8B8 format, - /// the red mask would be 0x00ff0000. - /// - public uint RBitMask { get; } - - /// - /// Gets Green (or U) mask for reading color data. For instance, given the A8R8G8B8 format, - /// the green mask would be 0x0000ff00. - /// - public uint GBitMask { get; } - - /// - /// Gets Blue (or V) mask for reading color data. For instance, given the A8R8G8B8 format, - /// the blue mask would be 0x000000ff. - /// - public uint BBitMask { get; } - - /// - /// Gets Alpha mask for reading alpha data. dwFlags must include - /// or . - /// For instance, given the A8R8G8B8 format, the alpha mask would be 0xff000000. - /// - public uint ABitMask { get; } - - /// - /// Writes the header to the given buffer. - /// - /// The buffer to write to. - /// Offset in the buffer. - public void WriteTo(Span buffer, int offset) - { - BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset, 4), this.Size); - BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset + 4, 4), (uint)this.Flags); - BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset + 8, 4), this.FourCC); - BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset + 12, 4), this.RGBBitCount); - BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset + 16, 4), this.RBitMask); - BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset + 20, 4), this.GBitMask); - BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset + 24, 4), this.BBitMask); - BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset + 28, 4), this.ABitMask); - } + /// + /// Gets Structure size; set to 32 (bytes). + /// + public uint Size { get; } + + /// + /// Gets Values which indicate what type of data is in the surface. + /// + public DdsPixelFormatFlags Flags { get; } + + /// + /// Gets Four-character codes for specifying compressed or custom formats. + /// Possible values include: DXT1, DXT2, DXT3, DXT4, or DXT5. A FOURCC of DX10 indicates the prescense of + /// the extended header, and the + /// member of that structure indicates the + /// true format. When using a four-character code, must include + /// . + /// + public uint FourCC { get; } + + /// + /// Gets Number of bits in an RGB (possibly including alpha) format. Valid when + /// includes , or , + /// or . + /// + public uint RGBBitCount { get; } + + /// + /// Gets Red (or lumiannce or Y) mask for reading color data. For instance, given the A8R8G8B8 format, + /// the red mask would be 0x00ff0000. + /// + public uint RBitMask { get; } + + /// + /// Gets Green (or U) mask for reading color data. For instance, given the A8R8G8B8 format, + /// the green mask would be 0x0000ff00. + /// + public uint GBitMask { get; } + + /// + /// Gets Blue (or V) mask for reading color data. For instance, given the A8R8G8B8 format, + /// the blue mask would be 0x000000ff. + /// + public uint BBitMask { get; } + + /// + /// Gets Alpha mask for reading alpha data. dwFlags must include + /// or . + /// For instance, given the A8R8G8B8 format, the alpha mask would be 0xff000000. + /// + public uint ABitMask { get; } + + /// + /// Writes the header to the given buffer. + /// + /// The buffer to write to. + /// Offset in the buffer. + public readonly void WriteTo(Span buffer, int offset) + { + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset, 4), this.Size); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset + 4, 4), (uint)this.Flags); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset + 8, 4), this.FourCC); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset + 12, 4), this.RGBBitCount); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset + 16, 4), this.RBitMask); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset + 20, 4), this.GBitMask); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset + 24, 4), this.BBitMask); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset + 28, 4), this.ABitMask); + } - /// - /// Parses the DdsPixelFormat from the given data buffer. - /// - /// The data to parse. - /// Offset in the buffer. - /// The parsed DdsPixelFormat. - public static DdsPixelFormat Parse(Span data, int offset) => - new DdsPixelFormat( - size: BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(offset, 4)), - flags: (DdsPixelFormatFlags)BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(offset + 4, 4)), - fourCC: BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(offset + 8, 4)), - rgbBitCount: BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(offset + 12, 4)), - rBitMask: BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(offset + 16, 4)), - gBitMask: BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(offset + 20, 4)), - bBitMask: BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(offset + 24, 4)), - aBitMask: BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(offset + 28, 4))); - - /// - /// Gets the represented by this structure. - /// - /// - /// The represented by this structure. - /// - public D3dFormat GetD3DFormat() + /// + /// Parses the DdsPixelFormat from the given data buffer. + /// + /// The data to parse. + /// Offset in the buffer. + /// The parsed DdsPixelFormat. + public static DdsPixelFormat Parse(Span data, int offset) => + new( + size: BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(offset, 4)), + flags: (DdsPixelFormatFlags)BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(offset + 4, 4)), + fourCC: BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(offset + 8, 4)), + rgbBitCount: BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(offset + 12, 4)), + rBitMask: BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(offset + 16, 4)), + gBitMask: BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(offset + 20, 4)), + bBitMask: BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(offset + 24, 4)), + aBitMask: BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(offset + 28, 4))); + + /// + /// Gets the represented by this structure. + /// + /// + /// The represented by this structure. + /// + public D3dFormat GetD3DFormat() + { + if ((this.Flags & DdsPixelFormatFlags.RGBA) == DdsPixelFormatFlags.RGBA) { - if ((this.Flags & DdsPixelFormatFlags.RGBA) == DdsPixelFormatFlags.RGBA) + switch (this.RGBBitCount) { - switch (this.RGBBitCount) - { - case 32: - return this.GetRgba32(); - case 16: - return this.GetRgba16(); - } - } - else if ((this.Flags & DdsPixelFormatFlags.RGB) == DdsPixelFormatFlags.RGB) - { - switch (this.RGBBitCount) - { - case 32: - return this.GetRgb32(); - case 24: - return this.GetRgb24(); - case 16: - return this.GetRgb16(); - } + case 32: + return this.GetRgba32(); + case 16: + return this.GetRgba16(); } - else if ((this.Flags & DdsPixelFormatFlags.Alpha) == DdsPixelFormatFlags.Alpha) + } + else if ((this.Flags & DdsPixelFormatFlags.RGB) == DdsPixelFormatFlags.RGB) + { + switch (this.RGBBitCount) { - if (this.RGBBitCount == 8) - { - if (this.ABitMask == 0xff) - { - return D3dFormat.A8; - } - } + case 32: + return this.GetRgb32(); + case 24: + return this.GetRgb24(); + case 16: + return this.GetRgb16(); } - else if ((this.Flags & DdsPixelFormatFlags.Luminance) == DdsPixelFormatFlags.Luminance) + } + else if ((this.Flags & DdsPixelFormatFlags.Alpha) == DdsPixelFormatFlags.Alpha) + { + if (this.RGBBitCount == 8) { - switch (this.RGBBitCount) + if (this.ABitMask == 0xff) { - case 16: - return this.GetLumi16(); - case 8: - return this.GetLumi8(); + return D3dFormat.A8; } } - else if ((this.Flags & DdsPixelFormatFlags.FourCC) == DdsPixelFormatFlags.FourCC) - { - return (D3dFormat)this.FourCC; - } - - return D3dFormat.Unknown; } - - private D3dFormat GetRgba32() + else if ((this.Flags & DdsPixelFormatFlags.Luminance) == DdsPixelFormatFlags.Luminance) { - if (this.RBitMask == 0xff && this.GBitMask == 0xff00 && this.BBitMask == 0xff0000 && this.ABitMask == 0xff000000) - { - return D3dFormat.A8B8G8R8; - } - - if (this.RBitMask == 0xffff && this.GBitMask == 0xffff0000) + switch (this.RGBBitCount) { - return D3dFormat.G16R16; + case 16: + return this.GetLumi16(); + case 8: + return this.GetLumi8(); } + } + else if ((this.Flags & DdsPixelFormatFlags.FourCC) == DdsPixelFormatFlags.FourCC) + { + return (D3dFormat)this.FourCC; + } - if (this.RBitMask == 0x3ff && this.GBitMask == 0xffc00 && this.BBitMask == 0x3ff00000) - { - return D3dFormat.A2B10G10R10; - } + return D3dFormat.Unknown; + } - if (this.RBitMask == 0xff0000 && this.GBitMask == 0xff00 && this.BBitMask == 0xff && this.ABitMask == 0xff000000) - { - return D3dFormat.A8R8G8B8; - } + private readonly D3dFormat GetRgba32() + { + if (this.RBitMask == 0xff && this.GBitMask == 0xff00 && this.BBitMask == 0xff0000 && this.ABitMask == 0xff000000) + { + return D3dFormat.A8B8G8R8; + } - if (this.RBitMask == 0x3ff00000 && this.GBitMask == 0xffc00 && this.BBitMask == 0x3ff && this.ABitMask == 0xc0000000) - { - return D3dFormat.A2R10G10B10; - } + if (this.RBitMask == 0xffff && this.GBitMask == 0xffff0000) + { + return D3dFormat.G16R16; + } - return D3dFormat.Unknown; + if (this.RBitMask == 0x3ff && this.GBitMask == 0xffc00 && this.BBitMask == 0x3ff00000) + { + return D3dFormat.A2B10G10R10; } - private D3dFormat GetRgba16() + if (this.RBitMask == 0xff0000 && this.GBitMask == 0xff00 && this.BBitMask == 0xff && this.ABitMask == 0xff000000) { - if (this.RBitMask == 0x7c00 && this.GBitMask == 0x3e0 && this.BBitMask == 0x1f && this.ABitMask == 0x8000) - { - return D3dFormat.A1R5G5B5; - } + return D3dFormat.A8R8G8B8; + } - if (this.RBitMask == 0xf00 && this.GBitMask == 0xf0 && this.BBitMask == 0xf && this.ABitMask == 0xf000) - { - return D3dFormat.A4R4G4B4; - } + if (this.RBitMask == 0x3ff00000 && this.GBitMask == 0xffc00 && this.BBitMask == 0x3ff && this.ABitMask == 0xc0000000) + { + return D3dFormat.A2R10G10B10; + } - if (this.RBitMask == 0xe0 && this.GBitMask == 0x1c && this.BBitMask == 0x3 && this.ABitMask == 0xff00) - { - return D3dFormat.A8R3G3B2; - } + return D3dFormat.Unknown; + } - return D3dFormat.Unknown; + private readonly D3dFormat GetRgba16() + { + if (this.RBitMask == 0x7c00 && this.GBitMask == 0x3e0 && this.BBitMask == 0x1f && this.ABitMask == 0x8000) + { + return D3dFormat.A1R5G5B5; } - private D3dFormat GetRgb32() + if (this.RBitMask == 0xf00 && this.GBitMask == 0xf0 && this.BBitMask == 0xf && this.ABitMask == 0xf000) { - if (this.RBitMask == 0xffff && this.GBitMask == 0xffff0000) - { - return D3dFormat.G16R16; - } + return D3dFormat.A4R4G4B4; + } - if (this.RBitMask == 0xff0000 && this.GBitMask == 0xff00 && this.BBitMask == 0xff) - { - return D3dFormat.X8R8G8B8; - } + if (this.RBitMask == 0xe0 && this.GBitMask == 0x1c && this.BBitMask == 0x3 && this.ABitMask == 0xff00) + { + return D3dFormat.A8R3G3B2; + } - if (this.RBitMask == 0xff && this.GBitMask == 0xff00 && this.BBitMask == 0xff0000) - { - return D3dFormat.X8B8G8R8; - } + return D3dFormat.Unknown; + } - return D3dFormat.Unknown; + private readonly D3dFormat GetRgb32() + { + if (this.RBitMask == 0xffff && this.GBitMask == 0xffff0000) + { + return D3dFormat.G16R16; } - private D3dFormat GetRgb24() + if (this.RBitMask == 0xff0000 && this.GBitMask == 0xff00 && this.BBitMask == 0xff) { - if (this.RBitMask == 0xff0000 && this.GBitMask == 0xff00 && this.BBitMask == 0xff) - { - return D3dFormat.R8G8B8; - } + return D3dFormat.X8R8G8B8; + } - return D3dFormat.Unknown; + if (this.RBitMask == 0xff && this.GBitMask == 0xff00 && this.BBitMask == 0xff0000) + { + return D3dFormat.X8B8G8R8; } - private D3dFormat GetRgb16() + return D3dFormat.Unknown; + } + + private readonly D3dFormat GetRgb24() + { + if (this.RBitMask == 0xff0000 && this.GBitMask == 0xff00 && this.BBitMask == 0xff) { - if (this.RBitMask == 0xf800 && this.GBitMask == 0x7e0 && this.BBitMask == 0x1f) - { - return D3dFormat.R5G6B5; - } + return D3dFormat.R8G8B8; + } - if (this.RBitMask == 0x7c00 && this.GBitMask == 0x3e0 && this.BBitMask == 0x1f) - { - return D3dFormat.X1R5G5B5; - } + return D3dFormat.Unknown; + } - if (this.RBitMask == 0xf00 && this.GBitMask == 0xf0 && this.BBitMask == 0xf) - { - return D3dFormat.X4R4G4B4; - } + private readonly D3dFormat GetRgb16() + { + if (this.RBitMask == 0xf800 && this.GBitMask == 0x7e0 && this.BBitMask == 0x1f) + { + return D3dFormat.R5G6B5; + } - return D3dFormat.Unknown; + if (this.RBitMask == 0x7c00 && this.GBitMask == 0x3e0 && this.BBitMask == 0x1f) + { + return D3dFormat.X1R5G5B5; } - private D3dFormat GetLumi16() + if (this.RBitMask == 0xf00 && this.GBitMask == 0xf0 && this.BBitMask == 0xf) { - if (this.RBitMask == 0xff && this.ABitMask == 0xff00) - { - return D3dFormat.A8L8; - } + return D3dFormat.X4R4G4B4; + } - if (this.RBitMask == 0xffff) - { - return D3dFormat.L16; - } + return D3dFormat.Unknown; + } - return D3dFormat.Unknown; + private readonly D3dFormat GetLumi16() + { + if (this.RBitMask == 0xff && this.ABitMask == 0xff00) + { + return D3dFormat.A8L8; } - private D3dFormat GetLumi8() + if (this.RBitMask == 0xffff) { - if (this.RBitMask == 0xf && this.ABitMask == 0xf0) - { - return D3dFormat.A4L4; - } + return D3dFormat.L16; + } - if (this.RBitMask == 0xff) - { - return D3dFormat.L8; - } + return D3dFormat.Unknown; + } - return D3dFormat.Unknown; + private readonly D3dFormat GetLumi8() + { + if (this.RBitMask == 0xf && this.ABitMask == 0xf0) + { + return D3dFormat.A4L4; + } + + if (this.RBitMask == 0xff) + { + return D3dFormat.L8; } + + return D3dFormat.Unknown; } } diff --git a/src/ImageSharp.Textures/Formats/Dds/DdsProcessor.cs b/src/ImageSharp.Textures/Formats/Dds/DdsProcessor.cs index 2c976ff0..15d6a2c4 100644 --- a/src/ImageSharp.Textures/Formats/Dds/DdsProcessor.cs +++ b/src/ImageSharp.Textures/Formats/Dds/DdsProcessor.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; -using System.IO; using SixLabors.ImageSharp.Textures.Common.Exceptions; using SixLabors.ImageSharp.Textures.Common.Extensions; using SixLabors.ImageSharp.Textures.Formats.Dds.Emums; @@ -10,489 +8,488 @@ using Fp32 = SixLabors.ImageSharp.Textures.TextureFormats.Decoding.Fp32; using L32 = SixLabors.ImageSharp.Textures.TextureFormats.Decoding.L32; -namespace SixLabors.ImageSharp.Textures.Formats.Dds +namespace SixLabors.ImageSharp.Textures.Formats.Dds; + +/// +/// Decodes direct draw surfaces. +/// +internal class DdsProcessor { /// - /// Decodes direct draw surfaces. + /// Initializes a new instance of the class. /// - internal class DdsProcessor + /// The DDS header. + /// The DDS header DXT10. + public DdsProcessor(DdsHeader ddsHeader, DdsHeaderDxt10 ddsHeaderDxt10) { - /// - /// Initializes a new instance of the class. - /// - /// The DDS header. - /// The DDS header DXT10. - public DdsProcessor(DdsHeader ddsHeader, DdsHeaderDxt10 ddsHeaderDxt10) - { - this.DdsHeader = ddsHeader; - this.DdsHeaderDxt10 = ddsHeaderDxt10; - } + this.DdsHeader = ddsHeader; + this.DdsHeaderDxt10 = ddsHeaderDxt10; + } + + /// + /// Gets the dds header. + /// + public DdsHeader DdsHeader { get; } + + /// + /// Gets the dxt 10 header. + /// + public DdsHeaderDxt10 DdsHeaderDxt10 { get; } + + /// + /// Decodes the mipmaps of a DDS textures. + /// + /// The stream to read the texture data from. + /// The width of the texture at level 0. + /// The height of the texture at level 0. + /// The mipmap count. + /// The decoded mipmaps. + public MipMap[] DecodeDds(Stream stream, int width, int height, int count) + { + Guard.MustBeGreaterThan(count, 0, nameof(count)); - /// - /// Gets the dds header. - /// - public DdsHeader DdsHeader { get; } - - /// - /// Gets the dxt 10 header. - /// - public DdsHeaderDxt10 DdsHeaderDxt10 { get; } - - /// - /// Decodes the mipmaps of a DDS textures. - /// - /// The stream to read the texture data from. - /// The width of the texture at level 0. - /// The height of the texture at level 0. - /// The mipmap count. - /// The decoded mipmaps. - public MipMap[] DecodeDds(Stream stream, int width, int height, int count) + return this.DdsHeader.PixelFormat.FourCC switch { - Guard.MustBeGreaterThan(count, 0, nameof(count)); + DdsFourCc.None + or DdsFourCc.R16FLOAT + or DdsFourCc.R16G16FLOAT + or DdsFourCc.R16G16B16A16SNORM + or DdsFourCc.R16G16B16A16UNORM + or DdsFourCc.R16G16B16A16FLOAT + or DdsFourCc.R32FLOAT + or DdsFourCc.R32G32FLOAT + or DdsFourCc.R32G32B32A32FLOAT + or DdsFourCc.YUY2 + or DdsFourCc.RGBG + or DdsFourCc.GRGB => this.ProcessUncompressed(stream, width, height, count), + DdsFourCc.DXT1 => AllocateMipMaps(stream, width, height, count), + DdsFourCc.DXT2 or DdsFourCc.DXT4 => throw new NotSupportedException("Due to patents Can, DXT2 or DXT4 cannot be supported."), + DdsFourCc.DXT3 => AllocateMipMaps(stream, width, height, count), + DdsFourCc.DXT5 => AllocateMipMaps(stream, width, height, count), + DdsFourCc.DX10 => this.GetDx10Dds(stream, width, height, count), + DdsFourCc.ATI1 or DdsFourCc.BC4U => AllocateMipMaps(stream, width, height, count), + DdsFourCc.BC4S => AllocateMipMaps(stream, width, height, count), + DdsFourCc.ATI2 or DdsFourCc.BC5U => AllocateMipMaps(stream, width, height, count), + DdsFourCc.BC5S => AllocateMipMaps(stream, width, height, count), + _ => throw new NotSupportedException($"FourCC: {this.DdsHeader.PixelFormat.FourCC.FourCcToString()} not supported."), + }; + } - return this.DdsHeader.PixelFormat.FourCC switch + public MipMap[] ProcessUncompressed(Stream stream, int width, int height, int count) + { + uint bitsPerPixel = this.DdsHeader.PixelFormat.RGBBitCount; + return bitsPerPixel switch + { + 8 => this.EightBitImageFormat(stream, width, height, count), + 16 => this.SixteenBitImageFormat(stream, width, height, count), + 24 => this.TwentyFourBitImageFormat(stream, width, height, count), + 32 => this.ThirtyTwoBitImageFormat(stream, width, height, count), + _ => this.DdsHeader.PixelFormat.FourCC switch { - DdsFourCc.None - or DdsFourCc.R16FLOAT + DdsFourCc.R16FLOAT => this.SixteenBitImageFormat(stream, width, height, count), + DdsFourCc.R32FLOAT or DdsFourCc.R16G16FLOAT - or DdsFourCc.R16G16B16A16SNORM - or DdsFourCc.R16G16B16A16UNORM - or DdsFourCc.R16G16B16A16FLOAT - or DdsFourCc.R32FLOAT - or DdsFourCc.R32G32FLOAT - or DdsFourCc.R32G32B32A32FLOAT or DdsFourCc.YUY2 or DdsFourCc.RGBG - or DdsFourCc.GRGB => this.ProcessUncompressed(stream, width, height, count), - DdsFourCc.DXT1 => AllocateMipMaps(stream, width, height, count), - DdsFourCc.DXT2 or DdsFourCc.DXT4 => throw new NotSupportedException("Due to patents Can, DXT2 or DXT4 cannot be supported."), - DdsFourCc.DXT3 => AllocateMipMaps(stream, width, height, count), - DdsFourCc.DXT5 => AllocateMipMaps(stream, width, height, count), - DdsFourCc.DX10 => this.GetDx10Dds(stream, width, height, count), - DdsFourCc.ATI1 or DdsFourCc.BC4U => AllocateMipMaps(stream, width, height, count), - DdsFourCc.BC4S => AllocateMipMaps(stream, width, height, count), - DdsFourCc.ATI2 or DdsFourCc.BC5U => AllocateMipMaps(stream, width, height, count), - DdsFourCc.BC5S => AllocateMipMaps(stream, width, height, count), - _ => throw new NotSupportedException($"FourCC: {this.DdsHeader.PixelFormat.FourCC.FourCcToString()} not supported."), - }; - } + or DdsFourCc.GRGB => this.ThirtyTwoBitImageFormat(stream, width, height, count), + DdsFourCc.R16G16B16A16SNORM + or DdsFourCc.R16G16B16A16UNORM + or DdsFourCc.R16G16B16A16FLOAT + or DdsFourCc.R32G32FLOAT => this.SixtyFourBitImageFormat(stream, width, height, count), + DdsFourCc.R32G32B32A32FLOAT => this.HundredTwentyEightBitImageFormat(stream, width, height, count), + _ => throw new ArgumentOutOfRangeException($"Unrecognized rgb bit count: {this.DdsHeader.PixelFormat.RGBBitCount}"), + }, // For unknown reason some formats do not have the bitsPerPixel set in the header (its zero). + }; + } - public MipMap[] ProcessUncompressed(Stream stream, int width, int height, int count) - { - uint bitsPerPixel = this.DdsHeader.PixelFormat.RGBBitCount; - return bitsPerPixel switch - { - 8 => this.EightBitImageFormat(stream, width, height, count), - 16 => this.SixteenBitImageFormat(stream, width, height, count), - 24 => this.TwentyFourBitImageFormat(stream, width, height, count), - 32 => this.ThirtyTwoBitImageFormat(stream, width, height, count), - _ => this.DdsHeader.PixelFormat.FourCC switch - { - DdsFourCc.R16FLOAT => this.SixteenBitImageFormat(stream, width, height, count), - DdsFourCc.R32FLOAT - or DdsFourCc.R16G16FLOAT - or DdsFourCc.YUY2 - or DdsFourCc.RGBG - or DdsFourCc.GRGB => this.ThirtyTwoBitImageFormat(stream, width, height, count), - DdsFourCc.R16G16B16A16SNORM - or DdsFourCc.R16G16B16A16UNORM - or DdsFourCc.R16G16B16A16FLOAT - or DdsFourCc.R32G32FLOAT => this.SixtyFourBitImageFormat(stream, width, height, count), - DdsFourCc.R32G32B32A32FLOAT => this.HundredTwentyEightBitImageFormat(stream, width, height, count), - _ => throw new ArgumentOutOfRangeException($"Unrecognized rgb bit count: {this.DdsHeader.PixelFormat.RGBBitCount}"), - }, // For unknown reason some formats do not have the bitsPerPixel set in the header (its zero). - }; - } + /// + /// Allocates and decodes all mipmap levels of a DDS texture. + /// + /// The stream to read the texture data from. + /// The width of the texture at level 0. + /// The height of the texture at level 0. + /// The mipmap count. + /// The decoded mipmaps. + private static MipMap[] AllocateMipMaps(Stream stream, int width, int height, int count) + where TBlock : struct, IBlock + { + TBlock blockFormat = default(TBlock); + + MipMap[] mipMaps = new MipMap[count]; - /// - /// Allocates and decodes all mipmap levels of a DDS texture. - /// - /// The stream to read the texture data from. - /// The width of the texture at level 0. - /// The height of the texture at level 0. - /// The mipmap count. - /// The decoded mipmaps. - private static MipMap[] AllocateMipMaps(Stream stream, int width, int height, int count) - where TBlock : struct, IBlock + for (int i = 0; i < count; i++) { - var blockFormat = default(TBlock); + int widthBlocks = blockFormat.Compressed ? Helper.CalcBlocks(width) : width; + int heightBlocks = blockFormat.Compressed ? Helper.CalcBlocks(height) : height; + int bytesToRead = heightBlocks * widthBlocks * blockFormat.CompressedBytesPerBlock; - var mipMaps = new MipMap[count]; + // Special case for yuv formats with a single pixel. + if (bytesToRead < blockFormat.BitsPerPixel / 8) + { + bytesToRead = blockFormat.BitsPerPixel / 8; + } - for (int i = 0; i < count; i++) + byte[] mipData = new byte[bytesToRead]; + int read = stream.Read(mipData, 0, bytesToRead); + if (read != bytesToRead) { - int widthBlocks = blockFormat.Compressed ? Helper.CalcBlocks(width) : width; - int heightBlocks = blockFormat.Compressed ? Helper.CalcBlocks(height) : height; - int bytesToRead = heightBlocks * widthBlocks * blockFormat.CompressedBytesPerBlock; - - // Special case for yuv formats with a single pixel. - if (bytesToRead < blockFormat.BitsPerPixel / 8) - { - bytesToRead = blockFormat.BitsPerPixel / 8; - } - - byte[] mipData = new byte[bytesToRead]; - int read = stream.Read(mipData, 0, bytesToRead); - if (read != bytesToRead) - { - throw new TextureFormatException("could not read enough texture data from the stream"); - } - - mipMaps[i] = new MipMap(blockFormat, mipData, width, height); - - width >>= 1; - height >>= 1; + throw new TextureFormatException("could not read enough texture data from the stream"); } - return mipMaps; - } + mipMaps[i] = new MipMap(blockFormat, mipData, width, height); - private MipMap[] EightBitImageFormat(Stream stream, int width, int height, int count) - { - DdsPixelFormat pixelFormat = this.DdsHeader.PixelFormat; + width >>= 1; + height >>= 1; + } - bool hasAlpha = pixelFormat.Flags.HasFlag(DdsPixelFormatFlags.AlphaPixels); + return mipMaps; + } - if (pixelFormat.RBitMask == 0x0 && pixelFormat.GBitMask == 0x0 && pixelFormat.BBitMask == 0x0) - { - return AllocateMipMaps(stream, width, height, count); - } + private MipMap[] EightBitImageFormat(Stream stream, int width, int height, int count) + { + DdsPixelFormat pixelFormat = this.DdsHeader.PixelFormat; - if (!hasAlpha && pixelFormat.RBitMask == 0xFF && pixelFormat.GBitMask == 0x0 && pixelFormat.BBitMask == 0x0) - { - return AllocateMipMaps(stream, width, height, count); - } + bool hasAlpha = pixelFormat.Flags.HasFlag(DdsPixelFormatFlags.AlphaPixels); - throw new NotSupportedException("Unsupported 8 bit format"); + if (pixelFormat.RBitMask == 0x0 && pixelFormat.GBitMask == 0x0 && pixelFormat.BBitMask == 0x0) + { + return AllocateMipMaps(stream, width, height, count); } - private MipMap[] SixteenBitImageFormat(Stream stream, int width, int height, int count) + if (!hasAlpha && pixelFormat.RBitMask == 0xFF && pixelFormat.GBitMask == 0x0 && pixelFormat.BBitMask == 0x0) { - DdsPixelFormat pixelFormat = this.DdsHeader.PixelFormat; + return AllocateMipMaps(stream, width, height, count); + } - bool hasAlpha = pixelFormat.Flags.HasFlag(DdsPixelFormatFlags.AlphaPixels); + throw new NotSupportedException("Unsupported 8 bit format"); + } - if (hasAlpha && pixelFormat.RBitMask == 0xF00 && pixelFormat.GBitMask == 0xF0 && pixelFormat.BBitMask == 0xF) - { - return AllocateMipMaps(stream, width, height, count); - } + private MipMap[] SixteenBitImageFormat(Stream stream, int width, int height, int count) + { + DdsPixelFormat pixelFormat = this.DdsHeader.PixelFormat; - if (!hasAlpha && pixelFormat.RBitMask == 0x7C00 && pixelFormat.GBitMask == 0x3E0 && pixelFormat.BBitMask == 0x1F) - { - return AllocateMipMaps(stream, width, height, count); - } + bool hasAlpha = pixelFormat.Flags.HasFlag(DdsPixelFormatFlags.AlphaPixels); - if (hasAlpha && pixelFormat.RBitMask == 0x7C00 && pixelFormat.GBitMask == 0x3E0 && pixelFormat.BBitMask == 0x1F) - { - return AllocateMipMaps(stream, width, height, count); - } + if (hasAlpha && pixelFormat.RBitMask == 0xF00 && pixelFormat.GBitMask == 0xF0 && pixelFormat.BBitMask == 0xF) + { + return AllocateMipMaps(stream, width, height, count); + } - if (!hasAlpha && pixelFormat.RBitMask == 0xF800 && pixelFormat.GBitMask == 0x7E0 && pixelFormat.BBitMask == 0x1F) - { - return AllocateMipMaps(stream, width, height, count); - } + if (!hasAlpha && pixelFormat.RBitMask == 0x7C00 && pixelFormat.GBitMask == 0x3E0 && pixelFormat.BBitMask == 0x1F) + { + return AllocateMipMaps(stream, width, height, count); + } - if (hasAlpha && pixelFormat.RBitMask == 0xFF && pixelFormat.GBitMask == 0x0 && pixelFormat.BBitMask == 0x0) - { - return AllocateMipMaps(stream, width, height, count); - } + if (hasAlpha && pixelFormat.RBitMask == 0x7C00 && pixelFormat.GBitMask == 0x3E0 && pixelFormat.BBitMask == 0x1F) + { + return AllocateMipMaps(stream, width, height, count); + } - if (!hasAlpha && pixelFormat.RBitMask == 0xFFFF && pixelFormat.GBitMask == 0x0 && pixelFormat.BBitMask == 0x0) - { - return AllocateMipMaps(stream, width, height, count); - } + if (!hasAlpha && pixelFormat.RBitMask == 0xF800 && pixelFormat.GBitMask == 0x7E0 && pixelFormat.BBitMask == 0x1F) + { + return AllocateMipMaps(stream, width, height, count); + } - if (!hasAlpha && pixelFormat.RBitMask == 0xFF && pixelFormat.GBitMask == 0xFF00 && pixelFormat.BBitMask == 0x0) - { - return AllocateMipMaps(stream, width, height, count); - } + if (hasAlpha && pixelFormat.RBitMask == 0xFF && pixelFormat.GBitMask == 0x0 && pixelFormat.BBitMask == 0x0) + { + return AllocateMipMaps(stream, width, height, count); + } - if (pixelFormat.FourCC == DdsFourCc.R16FLOAT) - { - return AllocateMipMaps(stream, width, height, count); - } + if (!hasAlpha && pixelFormat.RBitMask == 0xFFFF && pixelFormat.GBitMask == 0x0 && pixelFormat.BBitMask == 0x0) + { + return AllocateMipMaps(stream, width, height, count); + } - throw new NotSupportedException("Unsupported 16 bit format"); + if (!hasAlpha && pixelFormat.RBitMask == 0xFF && pixelFormat.GBitMask == 0xFF00 && pixelFormat.BBitMask == 0x0) + { + return AllocateMipMaps(stream, width, height, count); } - private MipMap[] TwentyFourBitImageFormat(Stream stream, int width, int height, int count) + if (pixelFormat.FourCC == DdsFourCc.R16FLOAT) { - DdsPixelFormat pixelFormat = this.DdsHeader.PixelFormat; + return AllocateMipMaps(stream, width, height, count); + } - bool hasAlpha = pixelFormat.Flags.HasFlag(DdsPixelFormatFlags.AlphaPixels); + throw new NotSupportedException("Unsupported 16 bit format"); + } - if (!hasAlpha && pixelFormat.RBitMask == 0xFF0000 && pixelFormat.GBitMask == 0xFF00 && pixelFormat.BBitMask == 0xFF) - { - return AllocateMipMaps(stream, width, height, count); - } + private MipMap[] TwentyFourBitImageFormat(Stream stream, int width, int height, int count) + { + DdsPixelFormat pixelFormat = this.DdsHeader.PixelFormat; - throw new NotSupportedException("Unsupported 24 bit format"); - } + bool hasAlpha = pixelFormat.Flags.HasFlag(DdsPixelFormatFlags.AlphaPixels); - private MipMap[] ThirtyTwoBitImageFormat(Stream stream, int width, int height, int count) + if (!hasAlpha && pixelFormat.RBitMask == 0xFF0000 && pixelFormat.GBitMask == 0xFF00 && pixelFormat.BBitMask == 0xFF) { - DdsPixelFormat pixelFormat = this.DdsHeader.PixelFormat; + return AllocateMipMaps(stream, width, height, count); + } - bool hasAlpha = pixelFormat.Flags.HasFlag(DdsPixelFormatFlags.AlphaPixels); + throw new NotSupportedException("Unsupported 24 bit format"); + } - if (hasAlpha && pixelFormat.RBitMask == 0xFF0000 && pixelFormat.GBitMask == 0xFF00 && pixelFormat.BBitMask == 0xFF) - { - return AllocateMipMaps(stream, width, height, count); - } + private MipMap[] ThirtyTwoBitImageFormat(Stream stream, int width, int height, int count) + { + DdsPixelFormat pixelFormat = this.DdsHeader.PixelFormat; - if (hasAlpha && pixelFormat.RBitMask == 0xFF && pixelFormat.GBitMask == 0xFF00 && pixelFormat.BBitMask == 0xFF0000) - { - return AllocateMipMaps(stream, width, height, count); - } + bool hasAlpha = pixelFormat.Flags.HasFlag(DdsPixelFormatFlags.AlphaPixels); - if (!hasAlpha && pixelFormat.RBitMask == 0xFF0000 && pixelFormat.GBitMask == 0xFF00 && pixelFormat.BBitMask == 0xFF) - { - return AllocateMipMaps(stream, width, height, count); - } + if (hasAlpha && pixelFormat.RBitMask == 0xFF0000 && pixelFormat.GBitMask == 0xFF00 && pixelFormat.BBitMask == 0xFF) + { + return AllocateMipMaps(stream, width, height, count); + } - if (!hasAlpha && pixelFormat.RBitMask == 0xFF && pixelFormat.GBitMask == 0xFF00 && pixelFormat.BBitMask == 0xFF0000) - { - return AllocateMipMaps(stream, width, height, count); - } + if (hasAlpha && pixelFormat.RBitMask == 0xFF && pixelFormat.GBitMask == 0xFF00 && pixelFormat.BBitMask == 0xFF0000) + { + return AllocateMipMaps(stream, width, height, count); + } - if (!hasAlpha && pixelFormat.RBitMask == 0xFFFF && pixelFormat.GBitMask == 0xFFFF0000 && pixelFormat.BBitMask == 0x0) - { - return AllocateMipMaps(stream, width, height, count); - } + if (!hasAlpha && pixelFormat.RBitMask == 0xFF0000 && pixelFormat.GBitMask == 0xFF00 && pixelFormat.BBitMask == 0xFF) + { + return AllocateMipMaps(stream, width, height, count); + } - if (pixelFormat.FourCC == DdsFourCc.R32FLOAT) - { - return AllocateMipMaps(stream, width, height, count); - } + if (!hasAlpha && pixelFormat.RBitMask == 0xFF && pixelFormat.GBitMask == 0xFF00 && pixelFormat.BBitMask == 0xFF0000) + { + return AllocateMipMaps(stream, width, height, count); + } - if (pixelFormat.FourCC == DdsFourCc.R16G16FLOAT) - { - return AllocateMipMaps(stream, width, height, count); - } + if (!hasAlpha && pixelFormat.RBitMask == 0xFFFF && pixelFormat.GBitMask == 0xFFFF0000 && pixelFormat.BBitMask == 0x0) + { + return AllocateMipMaps(stream, width, height, count); + } - if (pixelFormat.FourCC == DdsFourCc.YUY2) - { - return AllocateMipMaps(stream, width, height, count); - } + if (pixelFormat.FourCC == DdsFourCc.R32FLOAT) + { + return AllocateMipMaps(stream, width, height, count); + } - if (pixelFormat.FourCC == DdsFourCc.RGBG) - { - return AllocateMipMaps(stream, width, height, count); - } + if (pixelFormat.FourCC == DdsFourCc.R16G16FLOAT) + { + return AllocateMipMaps(stream, width, height, count); + } - if (pixelFormat.FourCC == DdsFourCc.GRGB) - { - return AllocateMipMaps(stream, width, height, count); - } + if (pixelFormat.FourCC == DdsFourCc.YUY2) + { + return AllocateMipMaps(stream, width, height, count); + } - throw new NotSupportedException("Unsupported 32 bit format"); + if (pixelFormat.FourCC == DdsFourCc.RGBG) + { + return AllocateMipMaps(stream, width, height, count); } - private MipMap[] SixtyFourBitImageFormat(Stream stream, int width, int height, int count) + if (pixelFormat.FourCC == DdsFourCc.GRGB) { - DdsPixelFormat pixelFormat = this.DdsHeader.PixelFormat; + return AllocateMipMaps(stream, width, height, count); + } - if (pixelFormat.FourCC == DdsFourCc.R16G16B16A16SNORM || pixelFormat.FourCC == DdsFourCc.R16G16B16A16UNORM) - { - return AllocateMipMaps(stream, width, height, count); - } + throw new NotSupportedException("Unsupported 32 bit format"); + } - if (pixelFormat.FourCC == DdsFourCc.R32G32FLOAT) - { - return AllocateMipMaps(stream, width, height, count); - } + private MipMap[] SixtyFourBitImageFormat(Stream stream, int width, int height, int count) + { + DdsPixelFormat pixelFormat = this.DdsHeader.PixelFormat; - if (pixelFormat.FourCC == DdsFourCc.R16G16B16A16FLOAT) - { - return AllocateMipMaps(stream, width, height, count); - } + if (pixelFormat.FourCC is DdsFourCc.R16G16B16A16SNORM or DdsFourCc.R16G16B16A16UNORM) + { + return AllocateMipMaps(stream, width, height, count); + } - throw new NotSupportedException("Unsupported 64 bit format"); + if (pixelFormat.FourCC == DdsFourCc.R32G32FLOAT) + { + return AllocateMipMaps(stream, width, height, count); } - private MipMap[] HundredTwentyEightBitImageFormat(Stream stream, int width, int height, int count) + if (pixelFormat.FourCC == DdsFourCc.R16G16B16A16FLOAT) { - DdsPixelFormat pixelFormat = this.DdsHeader.PixelFormat; + return AllocateMipMaps(stream, width, height, count); + } - if (pixelFormat.FourCC == DdsFourCc.R32G32B32A32FLOAT || pixelFormat.FourCC == DdsFourCc.R32FLOAT) - { - return AllocateMipMaps(stream, width, height, count); - } + throw new NotSupportedException("Unsupported 64 bit format"); + } + + private MipMap[] HundredTwentyEightBitImageFormat(Stream stream, int width, int height, int count) + { + DdsPixelFormat pixelFormat = this.DdsHeader.PixelFormat; - throw new NotSupportedException("Unsupported 128 bit format"); + if (pixelFormat.FourCC is DdsFourCc.R32G32B32A32FLOAT or DdsFourCc.R32FLOAT) + { + return AllocateMipMaps(stream, width, height, count); } - /* - https://docs.microsoft.com/en-us/windows/win32/direct3ddds/dx-graphics-dds-pguide - https://docs.microsoft.com/en-us/windows/win32/api/dxgiformat/ne-dxgiformat-dxgi_format - */ + throw new NotSupportedException("Unsupported 128 bit format"); + } + + /* + https://docs.microsoft.com/en-us/windows/win32/direct3ddds/dx-graphics-dds-pguide + https://docs.microsoft.com/en-us/windows/win32/api/dxgiformat/ne-dxgiformat-dxgi_format + */ - private MipMap[] GetDx10Dds(Stream stream, int width, int height, int count) + private MipMap[] GetDx10Dds(Stream stream, int width, int height, int count) + { + switch (this.DdsHeaderDxt10.DxgiFormat) { - switch (this.DdsHeaderDxt10.DxgiFormat) - { - case DxgiFormat.BC1_Typeless: - case DxgiFormat.BC1_UNorm_SRGB: - case DxgiFormat.BC1_UNorm: - return AllocateMipMaps(stream, width, height, count); - case DxgiFormat.BC2_Typeless: - case DxgiFormat.BC2_UNorm: - case DxgiFormat.BC2_UNorm_SRGB: - return AllocateMipMaps(stream, width, height, count); - case DxgiFormat.BC3_Typeless: - case DxgiFormat.BC3_UNorm: - case DxgiFormat.BC3_UNorm_SRGB: - return AllocateMipMaps(stream, width, height, count); - case DxgiFormat.BC4_Typeless: - case DxgiFormat.BC4_UNorm: - return AllocateMipMaps(stream, width, height, count); - case DxgiFormat.BC4_SNorm: - return AllocateMipMaps(stream, width, height, count); - case DxgiFormat.BC5_Typeless: - case DxgiFormat.BC5_UNorm: - return AllocateMipMaps(stream, width, height, count); - case DxgiFormat.BC5_SNorm: - return AllocateMipMaps(stream, width, height, count); - case DxgiFormat.BC6H_Typeless: - case DxgiFormat.BC6H_UF16: - return AllocateMipMaps(stream, width, height, count); - case DxgiFormat.BC6H_SF16: - return AllocateMipMaps(stream, width, height, count); - case DxgiFormat.BC7_Typeless: - case DxgiFormat.BC7_UNorm: - case DxgiFormat.BC7_UNorm_SRGB: - return AllocateMipMaps(stream, width, height, count); - case DxgiFormat.R8G8B8A8_Typeless: - case DxgiFormat.R8G8B8A8_UNorm: - case DxgiFormat.R8G8B8A8_UNorm_SRGB: - case DxgiFormat.R8G8B8A8_UInt: - case DxgiFormat.R8G8B8A8_SNorm: - case DxgiFormat.R8G8B8A8_SInt: - case DxgiFormat.B8G8R8X8_Typeless: - case DxgiFormat.B8G8R8X8_UNorm: - case DxgiFormat.B8G8R8X8_UNorm_SRGB: - return AllocateMipMaps(stream, width, height, count); - case DxgiFormat.B8G8R8A8_Typeless: - case DxgiFormat.B8G8R8A8_UNorm: - case DxgiFormat.B8G8R8A8_UNorm_SRGB: - return AllocateMipMaps(stream, width, height, count); - case DxgiFormat.R32G32B32A32_Float: - return AllocateMipMaps(stream, width, height, count); - case DxgiFormat.R32G32B32A32_Typeless: - case DxgiFormat.R32G32B32A32_UInt: - case DxgiFormat.R32G32B32A32_SInt: - return AllocateMipMaps(stream, width, height, count); - case DxgiFormat.R32G32B32_Float: - return AllocateMipMaps(stream, width, height, count); - case DxgiFormat.R32G32B32_Typeless: - case DxgiFormat.R32G32B32_UInt: - case DxgiFormat.R32G32B32_SInt: - return AllocateMipMaps(stream, width, height, count); - case DxgiFormat.R16G16B16A16_Typeless: - case DxgiFormat.R16G16B16A16_Float: - case DxgiFormat.R16G16B16A16_UNorm: - case DxgiFormat.R16G16B16A16_UInt: - case DxgiFormat.R16G16B16A16_SNorm: - case DxgiFormat.R16G16B16A16_SInt: - return AllocateMipMaps(stream, width, height, count); - case DxgiFormat.R32G32_Float: - return AllocateMipMaps(stream, width, height, count); - case DxgiFormat.R32G32_Typeless: - case DxgiFormat.R32G32_UInt: - case DxgiFormat.R32G32_SInt: - return AllocateMipMaps(stream, width, height, count); - case DxgiFormat.R10G10B10A2_Typeless: - case DxgiFormat.R10G10B10A2_UNorm: - case DxgiFormat.R10G10B10A2_UInt: - return AllocateMipMaps(stream, width, height, count); - case DxgiFormat.R16G16_Float: - return AllocateMipMaps(stream, width, height, count); - case DxgiFormat.R16G16_Typeless: - case DxgiFormat.R16G16_UNorm: - case DxgiFormat.R16G16_UInt: - case DxgiFormat.R16G16_SNorm: - case DxgiFormat.R16G16_SInt: - return AllocateMipMaps(stream, width, height, count); - case DxgiFormat.R32_Float: - return AllocateMipMaps(stream, width, height, count); - case DxgiFormat.R32_Typeless: - case DxgiFormat.R32_UInt: - case DxgiFormat.R32_SInt: - // Treating single channel format as 32 bit gray image. - return AllocateMipMaps(stream, width, height, count); - case DxgiFormat.R8G8_Typeless: - case DxgiFormat.R8G8_UNorm: - case DxgiFormat.R8G8_UInt: - case DxgiFormat.R8G8_SNorm: - case DxgiFormat.R8G8_SInt: - return AllocateMipMaps(stream, width, height, count); - case DxgiFormat.R16_Float: - return AllocateMipMaps(stream, width, height, count); - case DxgiFormat.R16_Typeless: - case DxgiFormat.R16_UNorm: - case DxgiFormat.R16_UInt: - case DxgiFormat.R16_SNorm: - case DxgiFormat.R16_SInt: - // Treating single channel format as 16 bit gray image. - return AllocateMipMaps(stream, width, height, count); - case DxgiFormat.R8_Typeless: - case DxgiFormat.R8_UNorm: - case DxgiFormat.R8_UInt: - case DxgiFormat.R8_SNorm: - case DxgiFormat.R8_SInt: - // Treating single channel format as 8 bit gray image. - return AllocateMipMaps(stream, width, height, count); - case DxgiFormat.A8_UNorm: - return AllocateMipMaps(stream, width, height, count); - case DxgiFormat.R1_UNorm: - throw new NotImplementedException($"{nameof(DxgiFormat.R1_UNorm)} is currently not implemented"); - case DxgiFormat.R11G11B10_Float: - return AllocateMipMaps(stream, width, height, count); - case DxgiFormat.Y410: - return AllocateMipMaps(stream, width, height, count); - case DxgiFormat.Y416: - return AllocateMipMaps(stream, width, height, count); - case DxgiFormat.Y210: - return AllocateMipMaps(stream, width, height, count); - case DxgiFormat.Y216: - return AllocateMipMaps(stream, width, height, count); - case DxgiFormat.AYUV: - return AllocateMipMaps(stream, width, height, count); - case DxgiFormat.YUY2: - return AllocateMipMaps(stream, width, height, count); - case DxgiFormat.R8G8_B8G8_UNorm: - return AllocateMipMaps(stream, width, height, count); - case DxgiFormat.G8R8_G8B8_UNorm: - return AllocateMipMaps(stream, width, height, count); - case DxgiFormat.R32G8X24_Typeless: - case DxgiFormat.D32_Float_S8X24_UInt: - case DxgiFormat.R32_Float_X8X24_Typeless: - case DxgiFormat.X32_Typeless_G8X24_UInt: - case DxgiFormat.D32_Float: - case DxgiFormat.R24G8_Typeless: - case DxgiFormat.D24_UNorm_S8_UInt: - case DxgiFormat.R24_UNorm_X8_Typeless: - case DxgiFormat.X24_Typeless_G8_UInt: - case DxgiFormat.D16_UNorm: - case DxgiFormat.R9G9B9E5_SharedExp: - case DxgiFormat.R10G10B10_XR_BIAS_A2_UNorm: - case DxgiFormat.NV12: - case DxgiFormat.P010: - case DxgiFormat.P016: - case DxgiFormat.Opaque_420: - case DxgiFormat.NV11: - case DxgiFormat.AI44: - case DxgiFormat.IA44: - case DxgiFormat.P8: - case DxgiFormat.A8P8: - case DxgiFormat.B4G4R4A4_UNorm: - case DxgiFormat.P208: - case DxgiFormat.V208: - case DxgiFormat.V408: - case DxgiFormat.Unknown: - default: - throw new NotSupportedException($"Unsupported format {this.DdsHeaderDxt10.DxgiFormat}"); - } + case DxgiFormat.BC1_Typeless: + case DxgiFormat.BC1_UNorm_SRGB: + case DxgiFormat.BC1_UNorm: + return AllocateMipMaps(stream, width, height, count); + case DxgiFormat.BC2_Typeless: + case DxgiFormat.BC2_UNorm: + case DxgiFormat.BC2_UNorm_SRGB: + return AllocateMipMaps(stream, width, height, count); + case DxgiFormat.BC3_Typeless: + case DxgiFormat.BC3_UNorm: + case DxgiFormat.BC3_UNorm_SRGB: + return AllocateMipMaps(stream, width, height, count); + case DxgiFormat.BC4_Typeless: + case DxgiFormat.BC4_UNorm: + return AllocateMipMaps(stream, width, height, count); + case DxgiFormat.BC4_SNorm: + return AllocateMipMaps(stream, width, height, count); + case DxgiFormat.BC5_Typeless: + case DxgiFormat.BC5_UNorm: + return AllocateMipMaps(stream, width, height, count); + case DxgiFormat.BC5_SNorm: + return AllocateMipMaps(stream, width, height, count); + case DxgiFormat.BC6H_Typeless: + case DxgiFormat.BC6H_UF16: + return AllocateMipMaps(stream, width, height, count); + case DxgiFormat.BC6H_SF16: + return AllocateMipMaps(stream, width, height, count); + case DxgiFormat.BC7_Typeless: + case DxgiFormat.BC7_UNorm: + case DxgiFormat.BC7_UNorm_SRGB: + return AllocateMipMaps(stream, width, height, count); + case DxgiFormat.R8G8B8A8_Typeless: + case DxgiFormat.R8G8B8A8_UNorm: + case DxgiFormat.R8G8B8A8_UNorm_SRGB: + case DxgiFormat.R8G8B8A8_UInt: + case DxgiFormat.R8G8B8A8_SNorm: + case DxgiFormat.R8G8B8A8_SInt: + case DxgiFormat.B8G8R8X8_Typeless: + case DxgiFormat.B8G8R8X8_UNorm: + case DxgiFormat.B8G8R8X8_UNorm_SRGB: + return AllocateMipMaps(stream, width, height, count); + case DxgiFormat.B8G8R8A8_Typeless: + case DxgiFormat.B8G8R8A8_UNorm: + case DxgiFormat.B8G8R8A8_UNorm_SRGB: + return AllocateMipMaps(stream, width, height, count); + case DxgiFormat.R32G32B32A32_Float: + return AllocateMipMaps(stream, width, height, count); + case DxgiFormat.R32G32B32A32_Typeless: + case DxgiFormat.R32G32B32A32_UInt: + case DxgiFormat.R32G32B32A32_SInt: + return AllocateMipMaps(stream, width, height, count); + case DxgiFormat.R32G32B32_Float: + return AllocateMipMaps(stream, width, height, count); + case DxgiFormat.R32G32B32_Typeless: + case DxgiFormat.R32G32B32_UInt: + case DxgiFormat.R32G32B32_SInt: + return AllocateMipMaps(stream, width, height, count); + case DxgiFormat.R16G16B16A16_Typeless: + case DxgiFormat.R16G16B16A16_Float: + case DxgiFormat.R16G16B16A16_UNorm: + case DxgiFormat.R16G16B16A16_UInt: + case DxgiFormat.R16G16B16A16_SNorm: + case DxgiFormat.R16G16B16A16_SInt: + return AllocateMipMaps(stream, width, height, count); + case DxgiFormat.R32G32_Float: + return AllocateMipMaps(stream, width, height, count); + case DxgiFormat.R32G32_Typeless: + case DxgiFormat.R32G32_UInt: + case DxgiFormat.R32G32_SInt: + return AllocateMipMaps(stream, width, height, count); + case DxgiFormat.R10G10B10A2_Typeless: + case DxgiFormat.R10G10B10A2_UNorm: + case DxgiFormat.R10G10B10A2_UInt: + return AllocateMipMaps(stream, width, height, count); + case DxgiFormat.R16G16_Float: + return AllocateMipMaps(stream, width, height, count); + case DxgiFormat.R16G16_Typeless: + case DxgiFormat.R16G16_UNorm: + case DxgiFormat.R16G16_UInt: + case DxgiFormat.R16G16_SNorm: + case DxgiFormat.R16G16_SInt: + return AllocateMipMaps(stream, width, height, count); + case DxgiFormat.R32_Float: + return AllocateMipMaps(stream, width, height, count); + case DxgiFormat.R32_Typeless: + case DxgiFormat.R32_UInt: + case DxgiFormat.R32_SInt: + // Treating single channel format as 32 bit gray image. + return AllocateMipMaps(stream, width, height, count); + case DxgiFormat.R8G8_Typeless: + case DxgiFormat.R8G8_UNorm: + case DxgiFormat.R8G8_UInt: + case DxgiFormat.R8G8_SNorm: + case DxgiFormat.R8G8_SInt: + return AllocateMipMaps(stream, width, height, count); + case DxgiFormat.R16_Float: + return AllocateMipMaps(stream, width, height, count); + case DxgiFormat.R16_Typeless: + case DxgiFormat.R16_UNorm: + case DxgiFormat.R16_UInt: + case DxgiFormat.R16_SNorm: + case DxgiFormat.R16_SInt: + // Treating single channel format as 16 bit gray image. + return AllocateMipMaps(stream, width, height, count); + case DxgiFormat.R8_Typeless: + case DxgiFormat.R8_UNorm: + case DxgiFormat.R8_UInt: + case DxgiFormat.R8_SNorm: + case DxgiFormat.R8_SInt: + // Treating single channel format as 8 bit gray image. + return AllocateMipMaps(stream, width, height, count); + case DxgiFormat.A8_UNorm: + return AllocateMipMaps(stream, width, height, count); + case DxgiFormat.R1_UNorm: + throw new NotImplementedException($"{nameof(DxgiFormat.R1_UNorm)} is currently not implemented"); + case DxgiFormat.R11G11B10_Float: + return AllocateMipMaps(stream, width, height, count); + case DxgiFormat.Y410: + return AllocateMipMaps(stream, width, height, count); + case DxgiFormat.Y416: + return AllocateMipMaps(stream, width, height, count); + case DxgiFormat.Y210: + return AllocateMipMaps(stream, width, height, count); + case DxgiFormat.Y216: + return AllocateMipMaps(stream, width, height, count); + case DxgiFormat.AYUV: + return AllocateMipMaps(stream, width, height, count); + case DxgiFormat.YUY2: + return AllocateMipMaps(stream, width, height, count); + case DxgiFormat.R8G8_B8G8_UNorm: + return AllocateMipMaps(stream, width, height, count); + case DxgiFormat.G8R8_G8B8_UNorm: + return AllocateMipMaps(stream, width, height, count); + case DxgiFormat.R32G8X24_Typeless: + case DxgiFormat.D32_Float_S8X24_UInt: + case DxgiFormat.R32_Float_X8X24_Typeless: + case DxgiFormat.X32_Typeless_G8X24_UInt: + case DxgiFormat.D32_Float: + case DxgiFormat.R24G8_Typeless: + case DxgiFormat.D24_UNorm_S8_UInt: + case DxgiFormat.R24_UNorm_X8_Typeless: + case DxgiFormat.X24_Typeless_G8_UInt: + case DxgiFormat.D16_UNorm: + case DxgiFormat.R9G9B9E5_SharedExp: + case DxgiFormat.R10G10B10_XR_BIAS_A2_UNorm: + case DxgiFormat.NV12: + case DxgiFormat.P010: + case DxgiFormat.P016: + case DxgiFormat.Opaque_420: + case DxgiFormat.NV11: + case DxgiFormat.AI44: + case DxgiFormat.IA44: + case DxgiFormat.P8: + case DxgiFormat.A8P8: + case DxgiFormat.B4G4R4A4_UNorm: + case DxgiFormat.P208: + case DxgiFormat.V208: + case DxgiFormat.V408: + case DxgiFormat.Unknown: + default: + throw new NotSupportedException($"Unsupported format {this.DdsHeaderDxt10.DxgiFormat}"); } } } diff --git a/src/ImageSharp.Textures/Formats/Dds/DdsSurfaceType.cs b/src/ImageSharp.Textures/Formats/Dds/DdsSurfaceType.cs index 187359e6..e949526a 100644 --- a/src/ImageSharp.Textures/Formats/Dds/DdsSurfaceType.cs +++ b/src/ImageSharp.Textures/Formats/Dds/DdsSurfaceType.cs @@ -1,61 +1,60 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.Formats.Dds +namespace SixLabors.ImageSharp.Textures.Formats.Dds; + +/// +/// Shows what kind of surface we're dealing with. +/// +public enum DdsSurfaceType { /// - /// Shows what kind of surface we're dealing with. - /// - public enum DdsSurfaceType - { - /// - /// Default value. - /// - Unknown, - - /// - /// Positive X cube map face. - /// - CubemapPositiveX, - - /// - /// Negative X cube map face. - /// - CubemapNegativeX, - - /// - /// Positive Y cube map face. - /// - CubemapPositiveY, - - /// - /// Negative Y cube map face. - /// - CubemapNegativeY, - - /// - /// Positive Z cube map face. - /// - CubemapPositiveZ, - - /// - /// Negative Z cube map face. - /// - CubemapNegativeZ, - - /// - /// Represents a one-dimensional texture (height == 1). - /// - Texture1D, - - /// - /// Represents a usual two-dimensional texture. - /// - Texture2D, - - /// - /// Represents slices of a volume texture for a one mip-map level. - /// - Texture3D - } + /// Default value. + /// + Unknown, + + /// + /// Positive X cube map face. + /// + CubemapPositiveX, + + /// + /// Negative X cube map face. + /// + CubemapNegativeX, + + /// + /// Positive Y cube map face. + /// + CubemapPositiveY, + + /// + /// Negative Y cube map face. + /// + CubemapNegativeY, + + /// + /// Positive Z cube map face. + /// + CubemapPositiveZ, + + /// + /// Negative Z cube map face. + /// + CubemapNegativeZ, + + /// + /// Represents a one-dimensional texture (height == 1). + /// + Texture1D, + + /// + /// Represents a usual two-dimensional texture. + /// + Texture2D, + + /// + /// Represents slices of a volume texture for a one mip-map level. + /// + Texture3D } diff --git a/src/ImageSharp.Textures/Formats/Dds/DdsTools.cs b/src/ImageSharp.Textures/Formats/Dds/DdsTools.cs index 7402f524..17f590bd 100644 --- a/src/ImageSharp.Textures/Formats/Dds/DdsTools.cs +++ b/src/ImageSharp.Textures/Formats/Dds/DdsTools.cs @@ -1,362 +1,351 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using SixLabors.ImageSharp.Textures.Formats.Dds.Emums; -namespace SixLabors.ImageSharp.Textures.Formats.Dds.Processing +namespace SixLabors.ImageSharp.Textures.Formats.Dds.Processing; + +internal class DdsTools { - internal class DdsTools + public static int GetBitsPerPixel(D3dFormat d3dFormat) { - public static int GetBitsPerPixel(D3dFormat d3dFormat) + switch (d3dFormat) { - switch (d3dFormat) - { - case D3dFormat.A1: - return 1; - case D3dFormat.DXT1: - case D3dFormat.BC4S: - case D3dFormat.BC4U: - case D3dFormat.ATI1: - return 4; - case D3dFormat.R3G3B2: - case D3dFormat.A8: - case D3dFormat.P8: - case D3dFormat.L8: - case D3dFormat.A4L4: - case D3dFormat.S8_Lockable: - case D3dFormat.DXT2: - case D3dFormat.DXT3: - case D3dFormat.DXT4: - case D3dFormat.DXT5: - case D3dFormat.BC5S: - case D3dFormat.BC5U: - case D3dFormat.ATI2: - return 8; - case D3dFormat.R5G6B5: - case D3dFormat.X1R5G5B5: - case D3dFormat.A1R5G5B5: - case D3dFormat.A4R4G4B4: - case D3dFormat.A8R3G3B2: - case D3dFormat.X4R4G4B4: - case D3dFormat.A8P8: - case D3dFormat.A8L8: - case D3dFormat.V8U8: - case D3dFormat.L6V5U5: - case D3dFormat.UYVY: - case D3dFormat.YUY2: - case D3dFormat.R8G8_B8G8: - case D3dFormat.G8R8_G8B8: - case D3dFormat.D16: - case D3dFormat.D16_Lockable: - case D3dFormat.D15S1: - case D3dFormat.L16: - case D3dFormat.Index16: - case D3dFormat.R16F: - case D3dFormat.CxV8U8: - return 16; - case D3dFormat.R8G8B8: - return 24; - case D3dFormat.A8R8G8B8: - case D3dFormat.X8R8G8B8: - case D3dFormat.A2B10G10R10: - case D3dFormat.A8B8G8R8: - case D3dFormat.X8B8G8R8: - case D3dFormat.G16R16: - case D3dFormat.A2R10G10B10: - case D3dFormat.X8L8V8U8: - case D3dFormat.DQ8W8V8U8: - case D3dFormat.V16U16: - case D3dFormat.A2W10V10U10: - case D3dFormat.D32: - case D3dFormat.D32_Lockable: - case D3dFormat.D32F_Lockable: - case D3dFormat.D24S8: - case D3dFormat.D24X8: - case D3dFormat.D24X4S4: - case D3dFormat.D24FS8: - case D3dFormat.Index32: - case D3dFormat.G16R16F: - case D3dFormat.R32F: - case D3dFormat.A2B10G10R10_XR_Bias: - case D3dFormat.Multi2_ARGB8: - return 32; - case D3dFormat.A16B16G16R16: - case D3dFormat.A16B16G16R16F: - case D3dFormat.Q16W16V16U16: - case D3dFormat.G32R32F: - return 64; - case D3dFormat.A32B32G32R32F: - return 128; - case D3dFormat.Unknown: - case D3dFormat.VertexData: - case D3dFormat.BinaryBuffer: - return 0; - default: - return 0; - } + case D3dFormat.A1: + return 1; + case D3dFormat.DXT1: + case D3dFormat.BC4S: + case D3dFormat.BC4U: + case D3dFormat.ATI1: + return 4; + case D3dFormat.R3G3B2: + case D3dFormat.A8: + case D3dFormat.P8: + case D3dFormat.L8: + case D3dFormat.A4L4: + case D3dFormat.S8_Lockable: + case D3dFormat.DXT2: + case D3dFormat.DXT3: + case D3dFormat.DXT4: + case D3dFormat.DXT5: + case D3dFormat.BC5S: + case D3dFormat.BC5U: + case D3dFormat.ATI2: + return 8; + case D3dFormat.R5G6B5: + case D3dFormat.X1R5G5B5: + case D3dFormat.A1R5G5B5: + case D3dFormat.A4R4G4B4: + case D3dFormat.A8R3G3B2: + case D3dFormat.X4R4G4B4: + case D3dFormat.A8P8: + case D3dFormat.A8L8: + case D3dFormat.V8U8: + case D3dFormat.L6V5U5: + case D3dFormat.UYVY: + case D3dFormat.YUY2: + case D3dFormat.R8G8_B8G8: + case D3dFormat.G8R8_G8B8: + case D3dFormat.D16: + case D3dFormat.D16_Lockable: + case D3dFormat.D15S1: + case D3dFormat.L16: + case D3dFormat.Index16: + case D3dFormat.R16F: + case D3dFormat.CxV8U8: + return 16; + case D3dFormat.R8G8B8: + return 24; + case D3dFormat.A8R8G8B8: + case D3dFormat.X8R8G8B8: + case D3dFormat.A2B10G10R10: + case D3dFormat.A8B8G8R8: + case D3dFormat.X8B8G8R8: + case D3dFormat.G16R16: + case D3dFormat.A2R10G10B10: + case D3dFormat.X8L8V8U8: + case D3dFormat.DQ8W8V8U8: + case D3dFormat.V16U16: + case D3dFormat.A2W10V10U10: + case D3dFormat.D32: + case D3dFormat.D32_Lockable: + case D3dFormat.D32F_Lockable: + case D3dFormat.D24S8: + case D3dFormat.D24X8: + case D3dFormat.D24X4S4: + case D3dFormat.D24FS8: + case D3dFormat.Index32: + case D3dFormat.G16R16F: + case D3dFormat.R32F: + case D3dFormat.A2B10G10R10_XR_Bias: + case D3dFormat.Multi2_ARGB8: + return 32; + case D3dFormat.A16B16G16R16: + case D3dFormat.A16B16G16R16F: + case D3dFormat.Q16W16V16U16: + case D3dFormat.G32R32F: + return 64; + case D3dFormat.A32B32G32R32F: + return 128; + case D3dFormat.Unknown: + case D3dFormat.VertexData: + case D3dFormat.BinaryBuffer: + return 0; + default: + return 0; } + } - public static int GetBitsPerPixel(DxgiFormat dxgiFormat) + public static int GetBitsPerPixel(DxgiFormat dxgiFormat) + { + switch (dxgiFormat) { - switch (dxgiFormat) - { - case DxgiFormat.R32G32B32A32_Typeless: - case DxgiFormat.R32G32B32A32_Float: - case DxgiFormat.R32G32B32A32_UInt: - case DxgiFormat.R32G32B32A32_SInt: - return 128; - case DxgiFormat.R32G32B32_Typeless: - case DxgiFormat.R32G32B32_Float: - case DxgiFormat.R32G32B32_UInt: - case DxgiFormat.R32G32B32_SInt: - return 96; - case DxgiFormat.R16G16B16A16_Typeless: - case DxgiFormat.R16G16B16A16_Float: - case DxgiFormat.R16G16B16A16_UNorm: - case DxgiFormat.R16G16B16A16_UInt: - case DxgiFormat.R16G16B16A16_SNorm: - case DxgiFormat.R16G16B16A16_SInt: - case DxgiFormat.R32G32_Typeless: - case DxgiFormat.R32G32_Float: - case DxgiFormat.R32G32_UInt: - case DxgiFormat.R32G32_SInt: - case DxgiFormat.R32G8X24_Typeless: - case DxgiFormat.D32_Float_S8X24_UInt: - case DxgiFormat.R32_Float_X8X24_Typeless: - case DxgiFormat.X32_Typeless_G8X24_UInt: - return 64; - case DxgiFormat.R10G10B10A2_Typeless: - case DxgiFormat.R10G10B10A2_UNorm: - case DxgiFormat.R10G10B10A2_UInt: - case DxgiFormat.R11G11B10_Float: - case DxgiFormat.R8G8B8A8_Typeless: - case DxgiFormat.R8G8B8A8_UNorm: - case DxgiFormat.R8G8B8A8_UNorm_SRGB: - case DxgiFormat.R8G8B8A8_UInt: - case DxgiFormat.R8G8B8A8_SNorm: - case DxgiFormat.R8G8B8A8_SInt: - case DxgiFormat.R16G16_Typeless: - case DxgiFormat.R16G16_Float: - case DxgiFormat.R16G16_UNorm: - case DxgiFormat.R16G16_UInt: - case DxgiFormat.R16G16_SNorm: - case DxgiFormat.R16G16_SInt: - case DxgiFormat.R32_Typeless: - case DxgiFormat.D32_Float: - case DxgiFormat.R32_Float: - case DxgiFormat.R32_UInt: - case DxgiFormat.R32_SInt: - case DxgiFormat.R24G8_Typeless: - case DxgiFormat.D24_UNorm_S8_UInt: - case DxgiFormat.R24_UNorm_X8_Typeless: - case DxgiFormat.X24_Typeless_G8_UInt: - case DxgiFormat.R9G9B9E5_SharedExp: - case DxgiFormat.R8G8_B8G8_UNorm: - case DxgiFormat.G8R8_G8B8_UNorm: - case DxgiFormat.B8G8R8A8_UNorm: - case DxgiFormat.B8G8R8X8_UNorm: - case DxgiFormat.R10G10B10_XR_BIAS_A2_UNorm: - case DxgiFormat.B8G8R8A8_Typeless: - case DxgiFormat.B8G8R8A8_UNorm_SRGB: - case DxgiFormat.B8G8R8X8_Typeless: - case DxgiFormat.B8G8R8X8_UNorm_SRGB: - return 32; - case DxgiFormat.R8G8_Typeless: - case DxgiFormat.R8G8_UNorm: - case DxgiFormat.R8G8_UInt: - case DxgiFormat.R8G8_SNorm: - case DxgiFormat.R8G8_SInt: - case DxgiFormat.R16_Typeless: - case DxgiFormat.R16_Float: - case DxgiFormat.D16_UNorm: - case DxgiFormat.R16_UNorm: - case DxgiFormat.R16_UInt: - case DxgiFormat.R16_SNorm: - case DxgiFormat.R16_SInt: - case DxgiFormat.B5G6R5_UNorm: - case DxgiFormat.B5G5R5A1_UNorm: - case DxgiFormat.B4G4R4A4_UNorm: - return 16; - case DxgiFormat.R8_Typeless: - case DxgiFormat.R8_UNorm: - case DxgiFormat.R8_UInt: - case DxgiFormat.R8_SNorm: - case DxgiFormat.R8_SInt: - case DxgiFormat.A8_UNorm: - return 8; - case DxgiFormat.R1_UNorm: - return 1; - case DxgiFormat.BC1_Typeless: - case DxgiFormat.BC1_UNorm: - case DxgiFormat.BC1_UNorm_SRGB: - case DxgiFormat.BC4_Typeless: - case DxgiFormat.BC4_UNorm: - case DxgiFormat.BC4_SNorm: - return 4; - case DxgiFormat.BC2_Typeless: - case DxgiFormat.BC2_UNorm: - case DxgiFormat.BC2_UNorm_SRGB: - case DxgiFormat.BC3_Typeless: - case DxgiFormat.BC3_UNorm: - case DxgiFormat.BC3_UNorm_SRGB: - case DxgiFormat.BC5_Typeless: - case DxgiFormat.BC5_UNorm: - case DxgiFormat.BC5_SNorm: - case DxgiFormat.BC6H_Typeless: - case DxgiFormat.BC6H_UF16: - case DxgiFormat.BC6H_SF16: - case DxgiFormat.BC7_Typeless: - case DxgiFormat.BC7_UNorm: - case DxgiFormat.BC7_UNorm_SRGB: - return 8; - default: - return 0; - } + case DxgiFormat.R32G32B32A32_Typeless: + case DxgiFormat.R32G32B32A32_Float: + case DxgiFormat.R32G32B32A32_UInt: + case DxgiFormat.R32G32B32A32_SInt: + return 128; + case DxgiFormat.R32G32B32_Typeless: + case DxgiFormat.R32G32B32_Float: + case DxgiFormat.R32G32B32_UInt: + case DxgiFormat.R32G32B32_SInt: + return 96; + case DxgiFormat.R16G16B16A16_Typeless: + case DxgiFormat.R16G16B16A16_Float: + case DxgiFormat.R16G16B16A16_UNorm: + case DxgiFormat.R16G16B16A16_UInt: + case DxgiFormat.R16G16B16A16_SNorm: + case DxgiFormat.R16G16B16A16_SInt: + case DxgiFormat.R32G32_Typeless: + case DxgiFormat.R32G32_Float: + case DxgiFormat.R32G32_UInt: + case DxgiFormat.R32G32_SInt: + case DxgiFormat.R32G8X24_Typeless: + case DxgiFormat.D32_Float_S8X24_UInt: + case DxgiFormat.R32_Float_X8X24_Typeless: + case DxgiFormat.X32_Typeless_G8X24_UInt: + return 64; + case DxgiFormat.R10G10B10A2_Typeless: + case DxgiFormat.R10G10B10A2_UNorm: + case DxgiFormat.R10G10B10A2_UInt: + case DxgiFormat.R11G11B10_Float: + case DxgiFormat.R8G8B8A8_Typeless: + case DxgiFormat.R8G8B8A8_UNorm: + case DxgiFormat.R8G8B8A8_UNorm_SRGB: + case DxgiFormat.R8G8B8A8_UInt: + case DxgiFormat.R8G8B8A8_SNorm: + case DxgiFormat.R8G8B8A8_SInt: + case DxgiFormat.R16G16_Typeless: + case DxgiFormat.R16G16_Float: + case DxgiFormat.R16G16_UNorm: + case DxgiFormat.R16G16_UInt: + case DxgiFormat.R16G16_SNorm: + case DxgiFormat.R16G16_SInt: + case DxgiFormat.R32_Typeless: + case DxgiFormat.D32_Float: + case DxgiFormat.R32_Float: + case DxgiFormat.R32_UInt: + case DxgiFormat.R32_SInt: + case DxgiFormat.R24G8_Typeless: + case DxgiFormat.D24_UNorm_S8_UInt: + case DxgiFormat.R24_UNorm_X8_Typeless: + case DxgiFormat.X24_Typeless_G8_UInt: + case DxgiFormat.R9G9B9E5_SharedExp: + case DxgiFormat.R8G8_B8G8_UNorm: + case DxgiFormat.G8R8_G8B8_UNorm: + case DxgiFormat.B8G8R8A8_UNorm: + case DxgiFormat.B8G8R8X8_UNorm: + case DxgiFormat.R10G10B10_XR_BIAS_A2_UNorm: + case DxgiFormat.B8G8R8A8_Typeless: + case DxgiFormat.B8G8R8A8_UNorm_SRGB: + case DxgiFormat.B8G8R8X8_Typeless: + case DxgiFormat.B8G8R8X8_UNorm_SRGB: + return 32; + case DxgiFormat.R8G8_Typeless: + case DxgiFormat.R8G8_UNorm: + case DxgiFormat.R8G8_UInt: + case DxgiFormat.R8G8_SNorm: + case DxgiFormat.R8G8_SInt: + case DxgiFormat.R16_Typeless: + case DxgiFormat.R16_Float: + case DxgiFormat.D16_UNorm: + case DxgiFormat.R16_UNorm: + case DxgiFormat.R16_UInt: + case DxgiFormat.R16_SNorm: + case DxgiFormat.R16_SInt: + case DxgiFormat.B5G6R5_UNorm: + case DxgiFormat.B5G5R5A1_UNorm: + case DxgiFormat.B4G4R4A4_UNorm: + return 16; + case DxgiFormat.R8_Typeless: + case DxgiFormat.R8_UNorm: + case DxgiFormat.R8_UInt: + case DxgiFormat.R8_SNorm: + case DxgiFormat.R8_SInt: + case DxgiFormat.A8_UNorm: + return 8; + case DxgiFormat.R1_UNorm: + return 1; + case DxgiFormat.BC1_Typeless: + case DxgiFormat.BC1_UNorm: + case DxgiFormat.BC1_UNorm_SRGB: + case DxgiFormat.BC4_Typeless: + case DxgiFormat.BC4_UNorm: + case DxgiFormat.BC4_SNorm: + return 4; + case DxgiFormat.BC2_Typeless: + case DxgiFormat.BC2_UNorm: + case DxgiFormat.BC2_UNorm_SRGB: + case DxgiFormat.BC3_Typeless: + case DxgiFormat.BC3_UNorm: + case DxgiFormat.BC3_UNorm_SRGB: + case DxgiFormat.BC5_Typeless: + case DxgiFormat.BC5_UNorm: + case DxgiFormat.BC5_SNorm: + case DxgiFormat.BC6H_Typeless: + case DxgiFormat.BC6H_UF16: + case DxgiFormat.BC6H_SF16: + case DxgiFormat.BC7_Typeless: + case DxgiFormat.BC7_UNorm: + case DxgiFormat.BC7_UNorm_SRGB: + return 8; + default: + return 0; } + } - public static int GetBitsPerPixel(D3dFormat d3dFormat, DxgiFormat dxgiFormat) + public static int GetBitsPerPixel(D3dFormat d3dFormat, DxgiFormat dxgiFormat) + { + if (dxgiFormat != DxgiFormat.Unknown) { - if (dxgiFormat != DxgiFormat.Unknown) - { - return GetBitsPerPixel(dxgiFormat); - } - - return GetBitsPerPixel(d3dFormat); + return GetBitsPerPixel(dxgiFormat); } - public static long ComputePitch(uint dimension, D3dFormat formatD3D, DxgiFormat formatDxgi, int defaultPitchOrLinearSize = 0) - { - int bitsPerPixel = GetBitsPerPixel(formatD3D, formatDxgi); - if (IsBlockCompressedFormat(formatD3D) || IsBlockCompressedFormat(formatDxgi)) - { - return ComputeBCPitch(dimension, bitsPerPixel * 2); - } - - if (formatD3D == D3dFormat.R8G8_B8G8 || formatD3D == D3dFormat.G8R8_G8B8 || formatD3D == D3dFormat.UYVY || formatD3D == D3dFormat.YUY2) - { - return Math.Max(1, (dimension + 1) >> 1) * 4; - } - - if (IsPackedFormat(formatDxgi)) - { - return defaultPitchOrLinearSize; - } - - return ComputeUncompressedPitch(dimension, bitsPerPixel); - } + return GetBitsPerPixel(d3dFormat); + } - public static bool IsBlockCompressedFormat(D3dFormat format) + public static long ComputePitch(uint dimension, D3dFormat formatD3D, DxgiFormat formatDxgi, int defaultPitchOrLinearSize = 0) + { + int bitsPerPixel = GetBitsPerPixel(formatD3D, formatDxgi); + if (IsBlockCompressedFormat(formatD3D) || IsBlockCompressedFormat(formatDxgi)) { - switch (format) - { - case D3dFormat.DXT1: - case D3dFormat.DXT2: - case D3dFormat.DXT3: - case D3dFormat.DXT4: - case D3dFormat.DXT5: - case D3dFormat.BC4U: - case D3dFormat.BC4S: - case D3dFormat.BC5S: - case D3dFormat.BC5U: - return true; - default: - return false; - } + return ComputeBCPitch(dimension, bitsPerPixel * 2); } - public static bool IsBlockCompressedFormat(DxgiFormat format) + if (formatD3D is D3dFormat.R8G8_B8G8 or D3dFormat.G8R8_G8B8 or D3dFormat.UYVY or D3dFormat.YUY2) { - switch (format) - { - case DxgiFormat.BC1_Typeless: - case DxgiFormat.BC1_UNorm: - case DxgiFormat.BC1_UNorm_SRGB: - case DxgiFormat.BC2_Typeless: - case DxgiFormat.BC2_UNorm: - case DxgiFormat.BC2_UNorm_SRGB: - case DxgiFormat.BC3_Typeless: - case DxgiFormat.BC3_UNorm: - case DxgiFormat.BC3_UNorm_SRGB: - case DxgiFormat.BC4_Typeless: - case DxgiFormat.BC4_UNorm: - case DxgiFormat.BC4_SNorm: - case DxgiFormat.BC5_Typeless: - case DxgiFormat.BC5_UNorm: - case DxgiFormat.BC5_SNorm: - case DxgiFormat.BC6H_Typeless: - case DxgiFormat.BC6H_SF16: - case DxgiFormat.BC6H_UF16: - case DxgiFormat.BC7_Typeless: - case DxgiFormat.BC7_UNorm: - case DxgiFormat.BC7_UNorm_SRGB: - return true; - default: - return false; - } + return Math.Max(1, (dimension + 1) >> 1) * 4; } - internal static int ComputeBCPitch(uint dimension, int bytesPerBlock) + if (IsPackedFormat(formatDxgi)) { - return (int)Math.Max(1, (dimension + 3) / 4) * bytesPerBlock; + return defaultPitchOrLinearSize; } - public static bool IsPackedFormat(DxgiFormat format) + return ComputeUncompressedPitch(dimension, bitsPerPixel); + } + + public static bool IsBlockCompressedFormat(D3dFormat format) + { + switch (format) { - switch (format) - { - case DxgiFormat.YUY2: - case DxgiFormat.Y210: - case DxgiFormat.Y216: - case DxgiFormat.Y410: - case DxgiFormat.Y416: - case DxgiFormat.Opaque_420: - case DxgiFormat.AI44: - case DxgiFormat.AYUV: - case DxgiFormat.IA44: - case DxgiFormat.NV11: - case DxgiFormat.NV12: - case DxgiFormat.P010: - case DxgiFormat.P016: - case DxgiFormat.R8G8_B8G8_UNorm: - case DxgiFormat.G8R8_G8B8_UNorm: - return true; - default: - return false; - } + case D3dFormat.DXT1: + case D3dFormat.DXT2: + case D3dFormat.DXT3: + case D3dFormat.DXT4: + case D3dFormat.DXT5: + case D3dFormat.BC4U: + case D3dFormat.BC4S: + case D3dFormat.BC5S: + case D3dFormat.BC5U: + return true; + default: + return false; } + } - internal static int ComputeUncompressedPitch(uint dimension, int bitsPerPixel) + public static bool IsBlockCompressedFormat(DxgiFormat format) + { + switch (format) { - return (int)Math.Max(1, ((dimension * bitsPerPixel) + 7) / 8); + case DxgiFormat.BC1_Typeless: + case DxgiFormat.BC1_UNorm: + case DxgiFormat.BC1_UNorm_SRGB: + case DxgiFormat.BC2_Typeless: + case DxgiFormat.BC2_UNorm: + case DxgiFormat.BC2_UNorm_SRGB: + case DxgiFormat.BC3_Typeless: + case DxgiFormat.BC3_UNorm: + case DxgiFormat.BC3_UNorm_SRGB: + case DxgiFormat.BC4_Typeless: + case DxgiFormat.BC4_UNorm: + case DxgiFormat.BC4_SNorm: + case DxgiFormat.BC5_Typeless: + case DxgiFormat.BC5_UNorm: + case DxgiFormat.BC5_SNorm: + case DxgiFormat.BC6H_Typeless: + case DxgiFormat.BC6H_SF16: + case DxgiFormat.BC6H_UF16: + case DxgiFormat.BC7_Typeless: + case DxgiFormat.BC7_UNorm: + case DxgiFormat.BC7_UNorm_SRGB: + return true; + default: + return false; } + } - public static long ComputeLinearSize(uint width, uint height, D3dFormat formatD3D, DxgiFormat formatDxgi, int defaultPitchOrLinearSize = 0) + internal static int ComputeBCPitch(uint dimension, int bytesPerBlock) => (int)Math.Max(1, (dimension + 3) / 4) * bytesPerBlock; + + public static bool IsPackedFormat(DxgiFormat format) + { + switch (format) { - int bitsPerPixel = GetBitsPerPixel(formatD3D, formatDxgi); - if (IsBlockCompressedFormat(formatD3D) || IsBlockCompressedFormat(formatDxgi)) - { - return ComputeBCLinearSize(width, height, bitsPerPixel * 2); - } + case DxgiFormat.YUY2: + case DxgiFormat.Y210: + case DxgiFormat.Y216: + case DxgiFormat.Y410: + case DxgiFormat.Y416: + case DxgiFormat.Opaque_420: + case DxgiFormat.AI44: + case DxgiFormat.AYUV: + case DxgiFormat.IA44: + case DxgiFormat.NV11: + case DxgiFormat.NV12: + case DxgiFormat.P010: + case DxgiFormat.P016: + case DxgiFormat.R8G8_B8G8_UNorm: + case DxgiFormat.G8R8_G8B8_UNorm: + return true; + default: + return false; + } + } - // These packed formats get a special handling... - if (formatD3D == D3dFormat.R8G8_B8G8 || formatD3D == D3dFormat.G8R8_G8B8 || - formatD3D == D3dFormat.UYVY || formatD3D == D3dFormat.YUY2) - { - return ((width + 1) >> 1) * 4 * height; - } + internal static int ComputeUncompressedPitch(uint dimension, int bitsPerPixel) => (int)Math.Max(1, ((dimension * bitsPerPixel) + 7) / 8); - if (IsPackedFormat(formatDxgi)) - { - return defaultPitchOrLinearSize * height; - } + public static long ComputeLinearSize(uint width, uint height, D3dFormat formatD3D, DxgiFormat formatDxgi, int defaultPitchOrLinearSize = 0) + { + int bitsPerPixel = GetBitsPerPixel(formatD3D, formatDxgi); + if (IsBlockCompressedFormat(formatD3D) || IsBlockCompressedFormat(formatDxgi)) + { + return ComputeBCLinearSize(width, height, bitsPerPixel * 2); + } - return ComputeUncompressedPitch(width, bitsPerPixel) * height; + // These packed formats get a special handling... + if (formatD3D is D3dFormat.R8G8_B8G8 or D3dFormat.G8R8_G8B8 or + D3dFormat.UYVY or D3dFormat.YUY2) + { + return ((width + 1) >> 1) * 4 * height; } - internal static long ComputeBCLinearSize(uint width, uint height, int bytesPerBlock) + if (IsPackedFormat(formatDxgi)) { - return ComputeBCPitch(width, bytesPerBlock) * Math.Max(1, (height + 3) / 4); + return defaultPitchOrLinearSize * height; } + + return ComputeUncompressedPitch(width, bitsPerPixel) * height; } + + internal static long ComputeBCLinearSize(uint width, uint height, int bytesPerBlock) => ComputeBCPitch(width, bytesPerBlock) * Math.Max(1, (height + 3) / 4); } diff --git a/src/ImageSharp.Textures/Formats/Dds/Enums/D3d10ResourceDimension.cs b/src/ImageSharp.Textures/Formats/Dds/Enums/D3d10ResourceDimension.cs index 056fb472..478fd72e 100644 --- a/src/ImageSharp.Textures/Formats/Dds/Enums/D3d10ResourceDimension.cs +++ b/src/ImageSharp.Textures/Formats/Dds/Enums/D3d10ResourceDimension.cs @@ -1,36 +1,35 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.Formats.Dds.Emums +namespace SixLabors.ImageSharp.Textures.Formats.Dds.Emums; + +/// +/// Identifies the type of resource being used. +/// +internal enum D3d10ResourceDimension : uint { /// - /// Identifies the type of resource being used. + /// Resource is of unknown type. /// - internal enum D3d10ResourceDimension : uint - { - /// - /// Resource is of unknown type. - /// - Unknown = 0, + Unknown = 0, - /// - /// Resource is a buffer. - /// - Buffer = 1, + /// + /// Resource is a buffer. + /// + Buffer = 1, - /// - /// Resource is a 1D Texture. - /// - Texture1D = 2, + /// + /// Resource is a 1D Texture. + /// + Texture1D = 2, - /// - /// Resource is a 2D Texture. - /// - Texture2D = 3, + /// + /// Resource is a 2D Texture. + /// + Texture2D = 3, - /// - /// Resource is a 3D Texture. - /// - Texture3D = 4 - } + /// + /// Resource is a 3D Texture. + /// + Texture3D = 4 } diff --git a/src/ImageSharp.Textures/Formats/Dds/Enums/D3d10ResourceMiscFlags.cs b/src/ImageSharp.Textures/Formats/Dds/Enums/D3d10ResourceMiscFlags.cs index 76dad602..ef155a96 100644 --- a/src/ImageSharp.Textures/Formats/Dds/Enums/D3d10ResourceMiscFlags.cs +++ b/src/ImageSharp.Textures/Formats/Dds/Enums/D3d10ResourceMiscFlags.cs @@ -1,62 +1,59 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; +namespace SixLabors.ImageSharp.Textures.Formats.Dds.Emums; -namespace SixLabors.ImageSharp.Textures.Formats.Dds.Emums +/// +/// Identifies other, less common options for resources. +/// +/// +/// and +/// are mutually exclusive flags: either one may be set in the resource creation calls but not both simultaneously. +/// +[Flags] +internal enum D3d10ResourceMiscFlags : uint { /// - /// Identifies other, less common options for resources. + /// Unknown. /// - /// - /// and - /// are mutually exclusive flags: either one may be set in the resource creation calls but not both simultaneously. - /// - [Flags] - internal enum D3d10ResourceMiscFlags : uint - { - /// - /// Unknown. - /// - Unknown = 0, + Unknown = 0, - /// - /// Enables an application to call ID3D10Device::GenerateMips on a texture resource. - /// The resource must be created with the bind flags that specify that the resource is a render target and a - /// shader resource. - /// - GenerateMips = 0x1, + /// + /// Enables an application to call ID3D10Device::GenerateMips on a texture resource. + /// The resource must be created with the bind flags that specify that the resource is a render target and a + /// shader resource. + /// + GenerateMips = 0x1, - /// - /// Enables the sharing of resource data between two or more Direct3D devices. - /// The only resources that can be shared are 2D non-mipmapped textures. - /// WARP and REF devices do not support shared resources. Attempting to create a resource with this flag on - /// either a WARP or REF device will cause the create method to return an E_OUTOFMEMORY error code. - /// - Shared = 0x2, + /// + /// Enables the sharing of resource data between two or more Direct3D devices. + /// The only resources that can be shared are 2D non-mipmapped textures. + /// WARP and REF devices do not support shared resources. Attempting to create a resource with this flag on + /// either a WARP or REF device will cause the create method to return an E_OUTOFMEMORY error code. + /// + Shared = 0x2, - /// - /// Enables an application to create a cube texture from a Texture2DArray that contains 6 textures. - /// - TextureCube = 0x4, + /// + /// Enables an application to create a cube texture from a Texture2DArray that contains 6 textures. + /// + TextureCube = 0x4, - /// - /// Enables the resource created to be synchronized using the IDXGIKeyedMutex::AcquireSync and ReleaseSync APIs. - /// The following resource creation D3D10 APIs, that all take a D3D10_RESOURCE_MISC_FLAG parameter, have been extended to support the new flag. - /// ID3D10Device1::CreateTexture1D, ID3D10Device1::CreateTexture2D, ID3D10Device1::CreateTexture3D, ID3D10Device1::CreateBuffer - /// If any of the listed functions are called with the D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX flag set, the interface returned can be - /// queried for an IDXGIKeyedMutex interface, which implements AcquireSync and ReleaseSync APIs to synchronize access to the surface. - /// The device creating the surface, and any other device opening the surface (using OpenSharedResource) is required to - /// call IDXGIKeyedMutex::AcquireSync before any rendering commands to the surface, and IDXGIKeyedMutex::ReleaseSync when it is done rendering. - /// WARP and REF devices do not support shared resources. Attempting to create a resource with this flag on either a WARP or REF device will cause the - /// create method to return an E_OUTOFMEMORY error code. - /// - SharedKeyedMutex = 0x10, + /// + /// Enables the resource created to be synchronized using the IDXGIKeyedMutex::AcquireSync and ReleaseSync APIs. + /// The following resource creation D3D10 APIs, that all take a D3D10_RESOURCE_MISC_FLAG parameter, have been extended to support the new flag. + /// ID3D10Device1::CreateTexture1D, ID3D10Device1::CreateTexture2D, ID3D10Device1::CreateTexture3D, ID3D10Device1::CreateBuffer + /// If any of the listed functions are called with the D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX flag set, the interface returned can be + /// queried for an IDXGIKeyedMutex interface, which implements AcquireSync and ReleaseSync APIs to synchronize access to the surface. + /// The device creating the surface, and any other device opening the surface (using OpenSharedResource) is required to + /// call IDXGIKeyedMutex::AcquireSync before any rendering commands to the surface, and IDXGIKeyedMutex::ReleaseSync when it is done rendering. + /// WARP and REF devices do not support shared resources. Attempting to create a resource with this flag on either a WARP or REF device will cause the + /// create method to return an E_OUTOFMEMORY error code. + /// + SharedKeyedMutex = 0x10, - /// - /// Enables a surface to be used for GDI interoperability. Setting this flag enables rendering on - /// the surface via IDXGISurface1::GetDC. - /// - GdiCompatible = 0x20 - } + /// + /// Enables a surface to be used for GDI interoperability. Setting this flag enables rendering on + /// the surface via IDXGISurface1::GetDC. + /// + GdiCompatible = 0x20 } diff --git a/src/ImageSharp.Textures/Formats/Dds/Enums/D3dFormat.cs b/src/ImageSharp.Textures/Formats/Dds/Enums/D3dFormat.cs index 37d63eae..37b544de 100644 --- a/src/ImageSharp.Textures/Formats/Dds/Enums/D3dFormat.cs +++ b/src/ImageSharp.Textures/Formats/Dds/Enums/D3dFormat.cs @@ -1,88 +1,87 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.Formats.Dds.Emums +namespace SixLabors.ImageSharp.Textures.Formats.Dds.Emums; + +/// +/// Defines the various types of surface formats. +/// +/// +/// Values taken from http://msdn.microsoft.com/en-us/library/windows/desktop/bb172558%28v=vs.85%29.aspx +/// +internal enum D3dFormat : uint { - /// - /// Defines the various types of surface formats. - /// - /// - /// Values taken from http://msdn.microsoft.com/en-us/library/windows/desktop/bb172558%28v=vs.85%29.aspx - /// - internal enum D3dFormat : uint - { - Unknown = 0, - R8G8B8 = 20, - A8R8G8B8 = 21, - X8R8G8B8 = 22, - R5G6B5 = 23, - X1R5G5B5 = 24, - A1R5G5B5 = 25, - A4R4G4B4 = 26, - R3G3B2 = 27, - A8 = 28, - A8R3G3B2 = 29, - X4R4G4B4 = 30, - A2B10G10R10 = 31, - A8B8G8R8 = 32, - X8B8G8R8 = 33, - G16R16 = 34, - A2R10G10B10 = 35, - A16B16G16R16 = 36, - A8P8 = 40, - P8 = 41, - L8 = 50, - A8L8 = 51, - A4L4 = 52, - V8U8 = 60, - L6V5U5 = 61, - X8L8V8U8 = 62, - DQ8W8V8U8 = 63, - V16U16 = 64, - A2W10V10U10 = 67, - UYVY = (int)DdsFourCc.UYVY, - YUY2 = (int)DdsFourCc.YUY2, - R8G8_B8G8 = (int)DdsFourCc.RGBG, - G8R8_G8B8 = (int)DdsFourCc.GRGB, - RGBG = (int)DdsFourCc.RGBG, - GRGB = (int)DdsFourCc.GRGB, - Multi2_ARGB8 = (int)DdsFourCc.MET1, - DXT1 = (int)DdsFourCc.DXT1, - DXT2 = (int)DdsFourCc.DXT2, - DXT3 = (int)DdsFourCc.DXT3, - DXT4 = (int)DdsFourCc.DXT4, - DXT5 = (int)DdsFourCc.DXT5, - BC4U = (int)DdsFourCc.BC4U, - BC4S = (int)DdsFourCc.BC4S, - BC5U = (int)DdsFourCc.BC5U, - BC5S = (int)DdsFourCc.BC5S, - ATI1 = (int)DdsFourCc.ATI1, - ATI2 = (int)DdsFourCc.ATI2, - D16_Lockable = 70, - D32 = 71, - D15S1 = 73, - D24S8 = 75, - D24X8 = 77, - D24X4S4 = 79, - D16 = 80, - L16 = 81, - D32F_Lockable = 82, - D24FS8 = 83, - D32_Lockable = 84, - S8_Lockable = 85, - VertexData = 100, - Index16 = 101, - Index32 = 102, - Q16W16V16U16 = 110, - R16F = 111, - G16R16F = 112, - A16B16G16R16F = 113, - R32F = 114, - G32R32F = 115, - A32B32G32R32F = 116, - CxV8U8 = 117, - A1 = 118, - A2B10G10R10_XR_Bias = 119, - BinaryBuffer = 199, - } + Unknown = 0, + R8G8B8 = 20, + A8R8G8B8 = 21, + X8R8G8B8 = 22, + R5G6B5 = 23, + X1R5G5B5 = 24, + A1R5G5B5 = 25, + A4R4G4B4 = 26, + R3G3B2 = 27, + A8 = 28, + A8R3G3B2 = 29, + X4R4G4B4 = 30, + A2B10G10R10 = 31, + A8B8G8R8 = 32, + X8B8G8R8 = 33, + G16R16 = 34, + A2R10G10B10 = 35, + A16B16G16R16 = 36, + A8P8 = 40, + P8 = 41, + L8 = 50, + A8L8 = 51, + A4L4 = 52, + V8U8 = 60, + L6V5U5 = 61, + X8L8V8U8 = 62, + DQ8W8V8U8 = 63, + V16U16 = 64, + A2W10V10U10 = 67, + UYVY = (int)DdsFourCc.UYVY, + YUY2 = (int)DdsFourCc.YUY2, + R8G8_B8G8 = (int)DdsFourCc.RGBG, + G8R8_G8B8 = (int)DdsFourCc.GRGB, + RGBG = (int)DdsFourCc.RGBG, + GRGB = (int)DdsFourCc.GRGB, + Multi2_ARGB8 = (int)DdsFourCc.MET1, + DXT1 = (int)DdsFourCc.DXT1, + DXT2 = (int)DdsFourCc.DXT2, + DXT3 = (int)DdsFourCc.DXT3, + DXT4 = (int)DdsFourCc.DXT4, + DXT5 = (int)DdsFourCc.DXT5, + BC4U = (int)DdsFourCc.BC4U, + BC4S = (int)DdsFourCc.BC4S, + BC5U = (int)DdsFourCc.BC5U, + BC5S = (int)DdsFourCc.BC5S, + ATI1 = (int)DdsFourCc.ATI1, + ATI2 = (int)DdsFourCc.ATI2, + D16_Lockable = 70, + D32 = 71, + D15S1 = 73, + D24S8 = 75, + D24X8 = 77, + D24X4S4 = 79, + D16 = 80, + L16 = 81, + D32F_Lockable = 82, + D24FS8 = 83, + D32_Lockable = 84, + S8_Lockable = 85, + VertexData = 100, + Index16 = 101, + Index32 = 102, + Q16W16V16U16 = 110, + R16F = 111, + G16R16F = 112, + A16B16G16R16F = 113, + R32F = 114, + G32R32F = 115, + A32B32G32R32F = 116, + CxV8U8 = 117, + A1 = 118, + A2B10G10R10_XR_Bias = 119, + BinaryBuffer = 199, } diff --git a/src/ImageSharp.Textures/Formats/Dds/Enums/DdsCaps1.cs b/src/ImageSharp.Textures/Formats/Dds/Enums/DdsCaps1.cs index abd4d7ea..6efa6831 100644 --- a/src/ImageSharp.Textures/Formats/Dds/Enums/DdsCaps1.cs +++ b/src/ImageSharp.Textures/Formats/Dds/Enums/DdsCaps1.cs @@ -1,41 +1,38 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; +namespace SixLabors.ImageSharp.Textures.Formats.Dds.Emums; -namespace SixLabors.ImageSharp.Textures.Formats.Dds.Emums +/// Specifies the complexity of the surfaces stored. +/// +/// The DDS_SURFACE_FLAGS_MIPMAP flag, which is defined in Dds.h, is a bitwise-OR combination of the +/// and flags. +/// The DDS_SURFACE_FLAGS_TEXTURE flag, which is defined in Dds.h, is equal to the +/// flag. +/// The DDS_SURFACE_FLAGS_CUBEMAP flag, which is defined in Dds.h, is equal to the +/// flag. +/// +[Flags] +internal enum DdsCaps1 : uint { - /// Specifies the complexity of the surfaces stored. - /// - /// The DDS_SURFACE_FLAGS_MIPMAP flag, which is defined in Dds.h, is a bitwise-OR combination of the - /// and flags. - /// The DDS_SURFACE_FLAGS_TEXTURE flag, which is defined in Dds.h, is equal to the - /// flag. - /// The DDS_SURFACE_FLAGS_CUBEMAP flag, which is defined in Dds.h, is equal to the - /// flag. - /// - [Flags] - internal enum DdsCaps1 : uint - { - /// - /// Unknown. - /// - Unknown = 0, + /// + /// Unknown. + /// + Unknown = 0, - /// - /// Optional; must be used on any file that contains more than one surface - /// (a mipmap, a cubic environment map, or mipmapped volume texture). - /// - Complex = 0x8, + /// + /// Optional; must be used on any file that contains more than one surface + /// (a mipmap, a cubic environment map, or mipmapped volume texture). + /// + Complex = 0x8, - /// - /// Optional; should be used for a mipmap. - /// - MipMap = 0x400000, + /// + /// Optional; should be used for a mipmap. + /// + MipMap = 0x400000, - /// - /// Required. - /// - Texture = 0x1000 - } + /// + /// Required. + /// + Texture = 0x1000 } diff --git a/src/ImageSharp.Textures/Formats/Dds/Enums/DdsCaps2.cs b/src/ImageSharp.Textures/Formats/Dds/Enums/DdsCaps2.cs index ae88ac0f..a46bdf59 100644 --- a/src/ImageSharp.Textures/Formats/Dds/Enums/DdsCaps2.cs +++ b/src/ImageSharp.Textures/Formats/Dds/Enums/DdsCaps2.cs @@ -1,63 +1,60 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; - -namespace SixLabors.ImageSharp.Textures.Formats.Dds.Emums +namespace SixLabors.ImageSharp.Textures.Formats.Dds.Emums; + +/// +/// Additional detail about the surfaces stored. +/// +/// +/// Although Direct3D 9 supports partial cube-maps, Direct3D 10, 10.1, and 11 require that you +/// define all six cube-map faces (that is, you must set DDS_Cubemap_ALLFACES). +/// +[Flags] +internal enum DdsCaps2 : uint { /// - /// Additional detail about the surfaces stored. - /// - /// - /// Although Direct3D 9 supports partial cube-maps, Direct3D 10, 10.1, and 11 require that you - /// define all six cube-map faces (that is, you must set DDS_Cubemap_ALLFACES). - /// - [Flags] - internal enum DdsCaps2 : uint - { - /// - /// Unknown. - /// - Unknown = 0, - - /// - /// Required for a cube map. - /// - Cubemap = 0x200, - - /// - /// Required when these surfaces are stored in a cube map. - /// - CubemapPositiveX = 0x400, - - /// - /// Required when these surfaces are stored in a cube map. - /// - CubemapNegativeX = 0x800, - - /// - /// Required when these surfaces are stored in a cube map. - /// - CubemapPositiveY = 0x1000, - - /// - /// Required when these surfaces are stored in a cube map. - /// - CubemapNegativeY = 0x2000, - - /// - /// Required when these surfaces are stored in a cube map. - /// - CubemapPositiveZ = 0x4000, - - /// - /// Required when these surfaces are stored in a cube map. - /// - CubemapNegativeZ = 0x8000, - - /// - /// Required for a volume texture. - /// - Volume = 0x200000 - } + /// Unknown. + /// + Unknown = 0, + + /// + /// Required for a cube map. + /// + Cubemap = 0x200, + + /// + /// Required when these surfaces are stored in a cube map. + /// + CubemapPositiveX = 0x400, + + /// + /// Required when these surfaces are stored in a cube map. + /// + CubemapNegativeX = 0x800, + + /// + /// Required when these surfaces are stored in a cube map. + /// + CubemapPositiveY = 0x1000, + + /// + /// Required when these surfaces are stored in a cube map. + /// + CubemapNegativeY = 0x2000, + + /// + /// Required when these surfaces are stored in a cube map. + /// + CubemapPositiveZ = 0x4000, + + /// + /// Required when these surfaces are stored in a cube map. + /// + CubemapNegativeZ = 0x8000, + + /// + /// Required for a volume texture. + /// + Volume = 0x200000 } diff --git a/src/ImageSharp.Textures/Formats/Dds/Enums/DdsFlags.cs b/src/ImageSharp.Textures/Formats/Dds/Enums/DdsFlags.cs index ae311d09..78be2076 100644 --- a/src/ImageSharp.Textures/Formats/Dds/Enums/DdsFlags.cs +++ b/src/ImageSharp.Textures/Formats/Dds/Enums/DdsFlags.cs @@ -1,72 +1,69 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; +namespace SixLabors.ImageSharp.Textures.Formats.Dds.Emums; -namespace SixLabors.ImageSharp.Textures.Formats.Dds.Emums +/// +/// Flags to indicate which members contain valid data. +/// +/// +/// The DDS_HEADER_FLAGS_TEXTURE flag, which is defined in Dds.h, is a bitwise-OR combination of the +/// , , +/// , and flags. +/// The DDS_HEADER_FLAGS_MIPMAP flag, which is defined in Dds.h, is equal to the +/// flag. +/// The DDS_HEADER_FLAGS_VOLUME flag, which is defined in Dds.h, is equal to the +/// flag. +/// The DDS_HEADER_FLAGS_PITCH flag, which is defined in Dds.h, is equal to the +/// flag. +/// The DDS_HEADER_FLAGS_LINEARSIZE flag, which is defined in Dds.h, is equal to the +/// flag. +/// +[Flags] +internal enum DdsFlags : uint { /// - /// Flags to indicate which members contain valid data. + /// Unknown. /// - /// - /// The DDS_HEADER_FLAGS_TEXTURE flag, which is defined in Dds.h, is a bitwise-OR combination of the - /// , , - /// , and flags. - /// The DDS_HEADER_FLAGS_MIPMAP flag, which is defined in Dds.h, is equal to the - /// flag. - /// The DDS_HEADER_FLAGS_VOLUME flag, which is defined in Dds.h, is equal to the - /// flag. - /// The DDS_HEADER_FLAGS_PITCH flag, which is defined in Dds.h, is equal to the - /// flag. - /// The DDS_HEADER_FLAGS_LINEARSIZE flag, which is defined in Dds.h, is equal to the - /// flag. - /// - [Flags] - internal enum DdsFlags : uint - { - /// - /// Unknown. - /// - Unknown = 0, + Unknown = 0, - /// - /// Required in every .dds file. - /// - Caps = 0x1, + /// + /// Required in every .dds file. + /// + Caps = 0x1, - /// - /// Required in every .dds file. - /// - Height = 0x2, + /// + /// Required in every .dds file. + /// + Height = 0x2, - /// - /// Required in every .dds file. - /// - Width = 0x4, + /// + /// Required in every .dds file. + /// + Width = 0x4, - /// - /// Required when pitch is provided for an uncompressed texture. - /// - Pitch = 0x8, + /// + /// Required when pitch is provided for an uncompressed texture. + /// + Pitch = 0x8, - /// - /// Required in every .dds file. - /// - PixelFormat = 0x1000, + /// + /// Required in every .dds file. + /// + PixelFormat = 0x1000, - /// - /// Required in a mipmapped texture. - /// - MipMapCount = 0x20000, + /// + /// Required in a mipmapped texture. + /// + MipMapCount = 0x20000, - /// - /// Required when pitch is provided for a compressed texture. - /// - LinearSize = 0x80000, + /// + /// Required when pitch is provided for a compressed texture. + /// + LinearSize = 0x80000, - /// - /// Required in a depth texture. - /// - Depth = 0x800000 - } + /// + /// Required in a depth texture. + /// + Depth = 0x800000 } diff --git a/src/ImageSharp.Textures/Formats/Dds/Enums/DdsPixelFormatFlags.cs b/src/ImageSharp.Textures/Formats/Dds/Enums/DdsPixelFormatFlags.cs index 4fc301c9..c49cdd63 100644 --- a/src/ImageSharp.Textures/Formats/Dds/Enums/DdsPixelFormatFlags.cs +++ b/src/ImageSharp.Textures/Formats/Dds/Enums/DdsPixelFormatFlags.cs @@ -1,61 +1,58 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; +namespace SixLabors.ImageSharp.Textures.Formats.Dds.Emums; -namespace SixLabors.ImageSharp.Textures.Formats.Dds.Emums +/// +/// Values which indicate what type of data is in the surface. +/// +[Flags] +internal enum DdsPixelFormatFlags : uint { /// - /// Values which indicate what type of data is in the surface. - /// - [Flags] - internal enum DdsPixelFormatFlags : uint - { - /// - /// Unknown. - /// - Unknown = 0, - - /// - /// Texture contains alpha data; dwRGBAlphaBitMask contains valid data. - /// - AlphaPixels = 0x1, - - /// - /// Used in some older DDS files for alpha channel only uncompressed data - /// (dwRGBBitCount contains the alpha channel bitcount; dwABitMask contains valid data). - /// - Alpha = 0x2, - - /// - /// Texture contains compressed RGB data; dwFourCC contains valid data. - /// - FourCC = 0x4, - - /// - /// Texture contains uncompressed RGB data; dwRGBBitCount and the RGB masks - /// (dwRBitMask, dwRBitMask, dwRBitMask) contain valid data. - /// - RGB = 0x40, - - /// - /// Texture contains uncompressed RGB and alpha data; dwRGBBitCount and all of the masks - /// (dwRBitMask, dwRBitMask, dwRBitMask, dwRGBAlphaBitMask) contain valid data. - /// - RGBA = RGB | AlphaPixels, - - /// - /// Used in some older DDS files for YUV uncompressed data - /// (dwRGBBitCount contains the YUV bit count; dwRBitMask contains the Y mask, dwGBitMask contains the U mask, - /// dwBBitMask contains the V mask). - /// - YUV = 0x200, - - /// - /// Used in some older DDS files for single channel color uncompressed data - /// (dwRGBBitCount contains the luminance channel bit count; dwRBitMask contains the channel mask). - /// Can be combined with DDPF_ALPHAPIXELS for a two channel DDS file. - /// - Luminance = 0x20000 - } + /// Unknown. + /// + Unknown = 0, + + /// + /// Texture contains alpha data; dwRGBAlphaBitMask contains valid data. + /// + AlphaPixels = 0x1, + + /// + /// Used in some older DDS files for alpha channel only uncompressed data + /// (dwRGBBitCount contains the alpha channel bitcount; dwABitMask contains valid data). + /// + Alpha = 0x2, + + /// + /// Texture contains compressed RGB data; dwFourCC contains valid data. + /// + FourCC = 0x4, + + /// + /// Texture contains uncompressed RGB data; dwRGBBitCount and the RGB masks + /// (dwRBitMask, dwRBitMask, dwRBitMask) contain valid data. + /// + RGB = 0x40, + + /// + /// Texture contains uncompressed RGB and alpha data; dwRGBBitCount and all of the masks + /// (dwRBitMask, dwRBitMask, dwRBitMask, dwRGBAlphaBitMask) contain valid data. + /// + RGBA = RGB | AlphaPixels, + + /// + /// Used in some older DDS files for YUV uncompressed data + /// (dwRGBBitCount contains the YUV bit count; dwRBitMask contains the Y mask, dwGBitMask contains the U mask, + /// dwBBitMask contains the V mask). + /// + YUV = 0x200, + + /// + /// Used in some older DDS files for single channel color uncompressed data + /// (dwRGBBitCount contains the luminance channel bit count; dwRBitMask contains the channel mask). + /// Can be combined with DDPF_ALPHAPIXELS for a two channel DDS file. + /// + Luminance = 0x20000 } diff --git a/src/ImageSharp.Textures/Formats/Dds/Enums/DxgiFormat.cs b/src/ImageSharp.Textures/Formats/Dds/Enums/DxgiFormat.cs index dc4be9ac..37db737f 100644 --- a/src/ImageSharp.Textures/Formats/Dds/Enums/DxgiFormat.cs +++ b/src/ImageSharp.Textures/Formats/Dds/Enums/DxgiFormat.cs @@ -2,648 +2,647 @@ // Licensed under the Six Labors Split License. // ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Textures.Formats.Dds.Emums +namespace SixLabors.ImageSharp.Textures.Formats.Dds.Emums; + +/// +/// Resource data formats which includes fully-typed and typeless formats. +/// +/// +/// Values taken from http://msdn.microsoft.com/en-us/library/windows/desktop/bb173059%28v=vs.85%29.aspx +/// +internal enum DxgiFormat : uint { + Unknown = 0, + + /// + /// A four-component, 128-bit typeless format that supports 32 bits per channel including alpha. + /// + R32G32B32A32_Typeless = 1, + + /// + /// A four-component, 128-bit floating-point format that supports 32 bits per channel including alpha. + /// + R32G32B32A32_Float = 2, + + /// + /// A four-component, 128-bit unsigned-integer format that supports 32 bits per channel including alpha. + /// + R32G32B32A32_UInt = 3, + /// - /// Resource data formats which includes fully-typed and typeless formats. - /// - /// - /// Values taken from http://msdn.microsoft.com/en-us/library/windows/desktop/bb173059%28v=vs.85%29.aspx - /// - internal enum DxgiFormat : uint - { - Unknown = 0, - - /// - /// A four-component, 128-bit typeless format that supports 32 bits per channel including alpha. - /// - R32G32B32A32_Typeless = 1, - - /// - /// A four-component, 128-bit floating-point format that supports 32 bits per channel including alpha. - /// - R32G32B32A32_Float = 2, - - /// - /// A four-component, 128-bit unsigned-integer format that supports 32 bits per channel including alpha. - /// - R32G32B32A32_UInt = 3, - - /// - /// A four-component, 128-bit signed-integer format that supports 32 bits per channel including alpha. - /// - R32G32B32A32_SInt = 4, - - /// - /// A three-component, 96-bit typeless format that supports 32 bits per color channel. - /// - R32G32B32_Typeless = 5, - - /// - /// A three-component, 96-bit floating-point format that supports 32 bits per color channel. - /// - R32G32B32_Float = 6, - - /// - /// A three-component, 96-bit unsigned-integer format that supports 32 bits per color channel. - /// - R32G32B32_UInt = 7, - - /// - /// A three-component, 96-bit signed-integer format that supports 32 bits per color channel. - /// - R32G32B32_SInt = 8, - - /// - /// A four-component, 64-bit typeless format that supports 16 bits per channel including alpha. - /// - R16G16B16A16_Typeless = 9, - - /// - /// A four-component, 64-bit floating-point format that supports 16 bits per channel including alpha. - /// - R16G16B16A16_Float = 10, - - /// - /// A four-component, 64-bit unsigned-normalized-integer format that supports 16 bits per channel including alpha. - /// - R16G16B16A16_UNorm = 11, - - /// - /// A four-component, 64-bit unsigned-integer format that supports 16 bits per channel including alpha. - /// - R16G16B16A16_UInt = 12, - - /// - /// A four-component, 64-bit signed-normalized-integer format that supports 16 bits per channel including alpha. - /// - R16G16B16A16_SNorm = 13, - - /// - /// A four-component, 64-bit signed-integer format that supports 16 bits per channel including alpha. - /// - R16G16B16A16_SInt = 14, - - /// - /// A two-component, 64-bit typeless format that supports 32 bits for the red channel and 32 bits for the green channel. - /// - R32G32_Typeless = 15, - - /// - /// A two-component, 64-bit floating-point format that supports 32 bits for the red channel and 32 bits for the green channel. - /// - R32G32_Float = 16, - - /// - /// A two-component, 64-bit unsigned-integer format that supports 32 bits for the red channel and 32 bits for the green channel. - /// - R32G32_UInt = 17, - - /// - /// A two-component, 64-bit signed-integer format that supports 32 bits for the red channel and 32 bits for the green channel. - /// - R32G32_SInt = 18, - - /// - /// A two-component, 64-bit typeless format that supports 32 bits for the red channel, 8 bits for the green channel, and 24 bits are unused. - /// - R32G8X24_Typeless = 19, - - /// - /// A 32-bit floating-point component, and two unsigned-integer components (with an additional 32 bits). This format supports 32-bit depth, 8-bit stencil, and 24 bits are unused. - /// - D32_Float_S8X24_UInt = 20, - - /// - /// A 32-bit floating-point component, and two typeless components (with an additional 32 bits). This format supports 32-bit red channel, 8 bits are unused, and 24 bits are unused. - /// - R32_Float_X8X24_Typeless = 21, - - /// - /// A 32-bit typeless component, and two unsigned-integer components (with an additional 32 bits). This format has 32 bits unused, 8 bits for green channel, and 24 bits are unused. - /// - X32_Typeless_G8X24_UInt = 22, - - /// - /// A four-component, 32-bit typeless format that supports 10 bits for each color and 2 bits for alpha. - /// - R10G10B10A2_Typeless = 23, - - /// - /// A four-component, 32-bit unsigned-normalized-integer format that supports 10 bits for each color and 2 bits for alpha. - /// - R10G10B10A2_UNorm = 24, - - /// - /// A four-component, 32-bit unsigned-integer format that supports 10 bits for each color and 2 bits for alpha. - /// - R10G10B10A2_UInt = 25, - - /// - /// Three partial-precision floating-point numbers encoded into a single 32-bit value (a variant of s10e5, which is sign bit, 10-bit mantissa, and 5-bit biased (15) exponent). - /// There are no sign bits, and there is a 5-bit biased (15) exponent for each channel, 6-bit mantissa for R and G, and a 5-bit mantissa for B. - /// - R11G11B10_Float = 26, - - /// - /// A four-component, 32-bit typeless format that supports 8 bits per channel including alpha. - /// - R8G8B8A8_Typeless = 27, - - /// - /// A four-component, 32-bit unsigned-normalized-integer format that supports 8 bits per channel including alpha. - /// - R8G8B8A8_UNorm = 28, - - /// - /// A four-component, 32-bit unsigned-normalized integer sRGB format that supports 8 bits per channel including alpha. - /// - R8G8B8A8_UNorm_SRGB = 29, - - /// - /// A four-component, 32-bit unsigned-integer format that supports 8 bits per channel including alpha. - /// - R8G8B8A8_UInt = 30, - - /// - /// A four-component, 32-bit signed-normalized-integer format that supports 8 bits per channel including alpha. - /// - R8G8B8A8_SNorm = 31, - - /// - /// A four-component, 32-bit signed-integer format that supports 8 bits per channel including alpha. - /// - R8G8B8A8_SInt = 32, - - /// - /// A two-component, 32-bit typeless format that supports 16 bits for the red channel and 16 bits for the green channel. - /// - R16G16_Typeless = 33, - - /// - /// A two-component, 32-bit floating-point format that supports 16 bits for the red channel and 16 bits for the green channel. - /// - R16G16_Float = 34, - - /// - /// A two-component, 32-bit unsigned-normalized-integer format that supports 16 bits each for the green and red channels. - /// - R16G16_UNorm = 35, - - /// - /// A two-component, 32-bit unsigned-integer format that supports 16 bits for the red channel and 16 bits for the green channel. - /// - R16G16_UInt = 36, - - /// - /// A two-component, 32-bit signed-normalized-integer format that supports 16 bits for the red channel and 16 bits for the green channel. - /// - R16G16_SNorm = 37, - - /// - /// A two-component, 32-bit signed-integer format that supports 16 bits for the red channel and 16 bits for the green channel. - /// - R16G16_SInt = 38, - - /// - /// A single-component, 32-bit typeless format that supports 32 bits for the red channel. - /// - R32_Typeless = 39, - - /// - /// A single-component, 32-bit floating-point format that supports 32 bits for depth. - /// - D32_Float = 40, - - /// - /// A single-component, 32-bit floating-point format that supports 32 bits for the red channel. - /// - R32_Float = 41, - - /// - /// A single-component, 32-bit unsigned-integer format that supports 32 bits for the red channel. - /// - R32_UInt = 42, - - /// - /// A single-component, 32-bit signed-integer format that supports 32 bits for the red channel. - /// - R32_SInt = 43, - - /// - /// A two-component, 32-bit typeless format that supports 24 bits for the red channel and 8 bits for the green channel. - /// - R24G8_Typeless = 44, - - /// - /// A 32-bit z-buffer format that supports 24 bits for depth and 8 bits for stencil. - /// - D24_UNorm_S8_UInt = 45, - - /// - /// A 32-bit format, that contains a 24 bit, single-component, unsigned-normalized integer, with an additional typeless 8 bits. This format has 24 bits red channel and 8 bits unused. - /// - R24_UNorm_X8_Typeless = 46, - - /// - /// A 32-bit format, that contains a 24 bit, single-component, typeless format, with an additional 8 bit unsigned integer component. This format has 24 bits unused and 8 bits green channel. - /// - X24_Typeless_G8_UInt = 47, - - /// - /// A two-component, 16-bit typeless format that supports 8 bits for the red channel and 8 bits for the green channel. - /// - R8G8_Typeless = 48, - - /// - /// A two-component, 16-bit unsigned-normalized-integer format that supports 8 bits for the red channel and 8 bits for the green channel. - /// - R8G8_UNorm = 49, - - /// - /// A two-component, 16-bit unsigned-integer format that supports 8 bits for the red channel and 8 bits for the green channel. - /// - R8G8_UInt = 50, - - /// - /// A two-component, 16-bit signed-normalized-integer format that supports 8 bits for the red channel and 8 bits for the green channel. - /// - R8G8_SNorm = 51, - - /// - /// A two-component, 16-bit signed-integer format that supports 8 bits for the red channel and 8 bits for the green channel. - /// - R8G8_SInt = 52, - - /// - /// A single-component, 16-bit typeless format that supports 16 bits for the red channel. - /// - R16_Typeless = 53, - - /// - /// A single-component, 16-bit floating-point format that supports 16 bits for the red channel. - /// - R16_Float = 54, - - /// - /// A single-component, 16-bit unsigned-normalized-integer format that supports 16 bits for depth. - /// - D16_UNorm = 55, - - /// - /// A single-component, 16-bit unsigned-normalized-integer format that supports 16 bits for the red channel. - /// - R16_UNorm = 56, - - /// - /// A single-component, 16-bit unsigned-integer format that supports 16 bits for the red channel. - /// - R16_UInt = 57, - - /// - /// A single-component, 16-bit signed-normalized-integer format that supports 16 bits for the red channel. - /// - R16_SNorm = 58, - - /// - /// A single-component, 16-bit signed-integer format that supports 16 bits for the red channel. - /// - R16_SInt = 59, - - /// - /// A single-component, 8-bit typeless format that supports 8 bits for the red channel. - /// - R8_Typeless = 60, - - /// - /// A single-component, 8-bit unsigned-normalized-integer format that supports 8 bits for the red channel. - /// - R8_UNorm = 61, - - /// - /// A single-component, 8-bit unsigned-integer format that supports 8 bits for the red channel. - /// - R8_UInt = 62, - - /// - /// A single-component, 8-bit signed-normalized-integer format that supports 8 bits for the red channel. - /// - R8_SNorm = 63, - - /// - /// A single-component, 8-bit signed-integer format that supports 8 bits for the red channel. - /// - R8_SInt = 64, - - /// - /// A single-component, 8-bit unsigned-normalized-integer format for alpha only. - /// - A8_UNorm = 65, - - /// - /// A single-component, 1-bit unsigned-normalized integer format that supports 1 bit for the red channel. - /// - R1_UNorm = 66, - - /// - /// Three partial-precision floating-point numbers encoded into a single 32-bit value all sharing the same 5-bit exponent (variant of s10e5, which is sign bit, 10-bit mantissa, and 5-bit biased (15) exponent). - /// There is no sign bit, and there is a shared 5-bit biased (15) exponent and a 9-bit mantissa for each channel. - /// - R9G9B9E5_SharedExp = 67, - - /// - /// A four-component, 32-bit unsigned-normalized-integer format. This packed RGB format is analogous to the UYVY format. Each 32-bit block describes a pair of pixels: (R8, G8, B8) and (R8, G8, B8) where the R8/B8 values are repeated, - /// and the G8 values are unique to each pixel. Width must be even. - /// - R8G8_B8G8_UNorm = 68, - - /// - /// A four-component, 32-bit unsigned-normalized-integer format. This packed RGB format is analogous to the YUY2 format. Each 32-bit block describes a pair of pixels: (R8, G8, B8) and (R8, G8, B8) where the R8/B8 values are repeated, - /// and the G8 values are unique to each pixel. Width must be even. - /// - G8R8_G8B8_UNorm = 69, - - /// - /// Four-component typeless block-compression format. - /// - BC1_Typeless = 70, - - /// - /// Four-component block-compression format. - /// - BC1_UNorm = 71, - - /// - /// Four-component block-compression format for sRGB data. - /// - BC1_UNorm_SRGB = 72, - - /// - /// Four-component typeless block-compression format. - /// - BC2_Typeless = 73, - - /// - /// Four-component block-compression format. - /// - BC2_UNorm = 74, - - /// - /// Four-component block-compression format for sRGB data. - /// - BC2_UNorm_SRGB = 75, - - /// - /// Four-component typeless block-compression format. - /// - BC3_Typeless = 76, - - /// - /// Four-component block-compression format. - /// - BC3_UNorm = 77, - - /// - /// Four-component block-compression format for sRGB data. - /// - BC3_UNorm_SRGB = 78, - - /// - /// One-component typeless block-compression format. - /// - BC4_Typeless = 79, - - /// - /// One-component block-compression format. - /// - BC4_UNorm = 80, - - /// - /// One-component block-compression format. - /// - BC4_SNorm = 81, - - /// - /// Two-component typeless block-compression format. - /// - BC5_Typeless = 82, - - /// - /// Two-component block-compression format. - /// - BC5_UNorm = 83, - - /// - /// Two-component block-compression format. - /// - BC5_SNorm = 84, - - /// - /// A three-component, 16-bit unsigned-normalized-integer format that supports 5 bits for blue, 6 bits for green, and 5 bits for red. - /// Direct3D 10 through Direct3D 11: This value is defined for DXGI. However, Direct3D 10, 10.1, or 11 devices do not support this format. - /// Direct3D 11.1: This value is not supported until Windows 8. - /// - B5G6R5_UNorm = 85, - - /// - /// A four-component, 16-bit unsigned-normalized-integer format that supports 5 bits for each color channel and 1-bit alpha. - /// Direct3D 10 through Direct3D 11: This value is defined for DXGI. However, Direct3D 10, 10.1, or 11 devices do not support this format. - /// Direct3D 11.1: This value is not supported until Windows 8. - /// - B5G5R5A1_UNorm = 86, - - /// - /// A four-component, 32-bit unsigned-normalized-integer format that supports 8 bits for each color channel and 8-bit alpha. - /// - B8G8R8A8_UNorm = 87, - - /// - /// A four-component, 32-bit unsigned-normalized-integer format that supports 8 bits for each color channel and 8 bits unused. - /// - B8G8R8X8_UNorm = 88, - - /// - /// A four-component, 32-bit 2.8-biased fixed-point format that supports 10 bits for each color channel and 2-bit alpha. - /// - R10G10B10_XR_BIAS_A2_UNorm = 89, - - /// - /// A four-component, 32-bit typeless format that supports 8 bits for each channel including alpha. - /// - B8G8R8A8_Typeless = 90, - - /// - /// A four-component, 32-bit unsigned-normalized standard RGB format that supports 8 bits for each channel including alpha. - /// - B8G8R8A8_UNorm_SRGB = 91, - - /// - /// A four-component, 32-bit typeless format that supports 8 bits for each color channel, and 8 bits are unused. - /// - B8G8R8X8_Typeless = 92, - - /// - /// A four-component, 32-bit unsigned-normalized standard RGB format that supports 8 bits for each color channel, and 8 bits are unused. - /// - B8G8R8X8_UNorm_SRGB = 93, - - /// - /// A typeless block-compression format. - /// - BC6H_Typeless = 94, - - /// - /// A block-compression format. - /// - BC6H_UF16 = 95, - - /// - /// A block-compression format. - /// - BC6H_SF16 = 96, - - /// - /// A typeless block-compression format. - /// - BC7_Typeless = 97, - - /// - /// A block-compression format. - /// - BC7_UNorm = 98, - - /// - /// A block-compression format. - /// - BC7_UNorm_SRGB = 99, - - /// - /// Most common YUV 4:4:4 video resource format. Valid view formats for this video resource format are DXGI_FORMAT_R8G8B8A8_UNORM and DXGI_FORMAT_R8G8B8A8_UINT. - /// For UAVs, an additional valid view format is DXGI_FORMAT_R32_UINT. - /// By using DXGI_FORMAT_R32_UINT for UAVs, you can both read and write as opposed to just write for DXGI_FORMAT_R8G8B8A8_UNORM and DXGI_FORMAT_R8G8B8A8_UINT. - /// Supported view types are SRV, RTV, and UAV. One view provides a straightforward mapping of the entire surface. - /// The mapping to the view channel is V->R8, U->G8, Y->B8, and A->A8. - /// - AYUV = 100, - - /// - /// 10-bit per channel packed YUV 4:4:4 video resource format. Valid view formats for this video resource format are DXGI_FORMAT_R10G10B10A2_UNORM and DXGI_FORMAT_R10G10B10A2_UINT. - /// For UAVs, an additional valid view format is DXGI_FORMAT_R32_UINT. - /// By using DXGI_FORMAT_R32_UINT for UAVs, you can both read and write as opposed to just write for DXGI_FORMAT_R10G10B10A2_UNORM and DXGI_FORMAT_R10G10B10A2_UINT. - /// Supported view types are SRV and UAV. One view provides a straightforward mapping of the entire surface. - /// The mapping to the view channel is U->R10, Y->G10, V->B10, and A->A2. - /// - Y410 = 101, - - /// - /// 16-bit per channel packed YUV 4:4:4 video resource format. Valid view formats for this video resource format are DXGI_FORMAT_R16G16B16A16_UNORM and DXGI_FORMAT_R16G16B16A16_UINT. - /// Supported view types are SRV and UAV. One view provides a straightforward mapping of the entire surface. - /// The mapping to the view channel is U->R16, Y->G16, V->B16, and A->A16. - /// - Y416 = 102, - - /// - /// Most common YUV 4:2:0 video resource format. Valid luminance data view formats for this video resource format are DXGI_FORMAT_R8_UNORM and DXGI_FORMAT_R8_UINT. - /// Valid chrominance data view formats (width and height are each 1/2 of luminance view) for this video resource format are DXGI_FORMAT_R8G8_UNORM and DXGI_FORMAT_R8G8_UINT. - /// Supported view types are SRV, RTV, and UAV. For luminance data view, the mapping to the view channel is Y->R8. - /// For chrominance data view, the mapping to the view channel is U->R8 and V->G8. - /// - NV12 = 103, - - /// - /// 10-bit per channel planar YUV 4:2:0 video resource format. Valid luminance data view formats for this video resource format are DXGI_FORMAT_R16_UNORM and DXGI_FORMAT_R16_UINT. - /// The runtime does not enforce whether the lowest 6 bits are 0 (given that this video resource format is a 10-bit format that uses 16 bits). - /// If required, application shader code would have to enforce this manually. From the runtime's point of view, DXGI_FORMAT_P010 is no different than DXGI_FORMAT_P016. - /// Valid chrominance data view formats (width and height are each 1/2 of luminance view) for this video resource format are DXGI_FORMAT_R16G16_UNORM and DXGI_FORMAT_R16G16_UINT. - /// For UAVs, an additional valid chrominance data view format is DXGI_FORMAT_R32_UINT. By using DXGI_FORMAT_R32_UINT for UAVs, you can both read and write as opposed to just write for DXGI_FORMAT_R16G16_UNORM and DXGI_FORMAT_R16G16_UINT. Supported view types are SRV, RTV, and UAV. For luminance data view, the mapping to the view channel is Y->R16. - /// For chrominance data view, the mapping to the view channel is U->R16 and V->G16. - /// - P010 = 104, - - /// - /// 16-bit per channel planar YUV 4:2:0 video resource format. Valid luminance data view formats for this video resource format are DXGI_FORMAT_R16_UNORM and DXGI_FORMAT_R16_UINT. - /// Valid chrominance data view formats (width and height are each 1/2 of luminance view) for this video resource format are DXGI_FORMAT_R16G16_UNORM and DXGI_FORMAT_R16G16_UINT. - /// For UAVs, an additional valid chrominance data view format is DXGI_FORMAT_R32_UINT. By using DXGI_FORMAT_R32_UINT for UAVs, you can both read and write as opposed to just write for DXGI_FORMAT_R16G16_UNORM and DXGI_FORMAT_R16G16_UINT. Supported view types are SRV, RTV, and UAV. For luminance data view, the mapping to the view channel is Y->R16. - /// For chrominance data view, the mapping to the view channel is U->R16 and V->G16. - /// - P016 = 105, - - /// - /// 8-bit per channel planar YUV 4:2:0 video resource format. This format is subsampled where each pixel has its own Y value, but each 2x2 pixel block shares a single U and V value. - /// The runtime requires that the width and height of all resources that are created with this format are multiples of 2. The runtime also requires that the left, right, top, and bottom members of any RECT that are used for this format are multiples of 2. - /// This format differs from DXGI_FORMAT_NV12 in that the layout of the data within the resource is completely opaque to applications. - /// Applications cannot use the CPU to map the resource and then access the data within the resource. You cannot use shaders with this format. - /// Because of this behavior, legacy hardware that supports a non-NV12 4:2:0 layout (for example, YV12, and so on) can be used. - /// Also, new hardware that has a 4:2:0 implementation better than NV12 can be used when the application does not need the data to be in a standard layout. - /// - Opaque_420 = 106, - - /// - /// Most common YUV 4:2:2 video resource format. Valid view formats for this video resource format are DXGI_FORMAT_R8G8B8A8_UNORM and DXGI_FORMAT_R8G8B8A8_UINT. - /// For UAVs, an additional valid view format is DXGI_FORMAT_R32_UINT. By using DXGI_FORMAT_R32_UINT for UAVs, you can both read and write as opposed to just write for DXGI_FORMAT_R8G8B8A8_UNORM and DXGI_FORMAT_R8G8B8A8_UINT. - /// Supported view types are SRV and UAV. One view provides a straightforward mapping of the entire surface. The mapping to the view channel is Y0->R8, U0->G8,Y1->B8,and V0->A8. - /// - YUY2 = 107, - - /// - /// 10-bit per channel packed YUV 4:2:2 video resource format. Valid view formats for this video resource format are DXGI_FORMAT_R16G16B16A16_UNORM and DXGI_FORMAT_R16G16B16A16_UINT. - /// The runtime does not enforce whether the lowest 6 bits are 0 (given that this video resource format is a 10-bit format that uses 16 bits). - /// If required, application shader code would have to enforce this manually. From the runtime's point of view, DXGI_FORMAT_Y210 is no different than DXGI_FORMAT_Y216. - /// Supported view types are SRV and UAV. One view provides a straightforward mapping of the entire surface. The mapping to the view channel is Y0->R16, U->G16, Y1->B16, and V->A16. - /// - Y210 = 108, - - /// - /// 16-bit per channel packed YUV 4:2:2 video resource format. Valid view formats for this video resource format are DXGI_FORMAT_R16G16B16A16_UNORM and DXGI_FORMAT_R16G16B16A16_UINT. - /// Supported view types are SRV and UAV. One view provides a straightforward mapping of the entire surface. The mapping to the view channel is Y0->R16, U->G16, Y1->B16, and V->A16. - /// - Y216 = 109, - - /// - /// Most common planar YUV 4:1:1 video resource format. Valid luminance data view formats for this video resource format are DXGI_FORMAT_R8_UNORM and DXGI_FORMAT_R8_UINT. - /// Valid chrominance data view formats (width and height are each 1/4 of luminance view) for this video resource format are DXGI_FORMAT_R8G8_UNORM and DXGI_FORMAT_R8G8_UINT. - /// Supported view types are SRV, RTV, and UAV. For luminance data view, the mapping to the view channel is Y->R8. For chrominance data view, the mapping to the view channel is U->R8 and V->G8. - /// - NV11 = 110, - - /// - /// 4-bit palletized YUV format that is commonly used for DVD subpicture. - /// - AI44 = 111, - - /// - /// 4-bit palletized YUV format that is commonly used for DVD subpicture. - /// - IA44 = 112, - - /// - /// 8-bit palletized format that is used for palletized RGB data when the processor processes ISDB-T data and for palletized YUV data when the processor processes BluRay data. - /// - P8 = 113, - - /// - /// 8-bit palletized format with 8 bits of alpha that is used for palletized YUV data when the processor processes BluRay data. - /// - A8P8 = 114, - - /// - /// A four-component, 16-bit unsigned-normalized integer format that supports 4 bits for each channel including alpha. - /// - B4G4R4A4_UNorm = 115, - - /// - /// A video format; an 8-bit version of a hybrid planar 4:2:2 format. - /// - P208 = 130, - - /// - /// An 8 bit YCbCrA 4:4 rendering format. - /// - V208 = 131, - - /// - /// An 8 bit YCbCrA 4:4:4:4 rendering format. - /// - V408 = 132, - } + /// A four-component, 128-bit signed-integer format that supports 32 bits per channel including alpha. + /// + R32G32B32A32_SInt = 4, + + /// + /// A three-component, 96-bit typeless format that supports 32 bits per color channel. + /// + R32G32B32_Typeless = 5, + + /// + /// A three-component, 96-bit floating-point format that supports 32 bits per color channel. + /// + R32G32B32_Float = 6, + + /// + /// A three-component, 96-bit unsigned-integer format that supports 32 bits per color channel. + /// + R32G32B32_UInt = 7, + + /// + /// A three-component, 96-bit signed-integer format that supports 32 bits per color channel. + /// + R32G32B32_SInt = 8, + + /// + /// A four-component, 64-bit typeless format that supports 16 bits per channel including alpha. + /// + R16G16B16A16_Typeless = 9, + + /// + /// A four-component, 64-bit floating-point format that supports 16 bits per channel including alpha. + /// + R16G16B16A16_Float = 10, + + /// + /// A four-component, 64-bit unsigned-normalized-integer format that supports 16 bits per channel including alpha. + /// + R16G16B16A16_UNorm = 11, + + /// + /// A four-component, 64-bit unsigned-integer format that supports 16 bits per channel including alpha. + /// + R16G16B16A16_UInt = 12, + + /// + /// A four-component, 64-bit signed-normalized-integer format that supports 16 bits per channel including alpha. + /// + R16G16B16A16_SNorm = 13, + + /// + /// A four-component, 64-bit signed-integer format that supports 16 bits per channel including alpha. + /// + R16G16B16A16_SInt = 14, + + /// + /// A two-component, 64-bit typeless format that supports 32 bits for the red channel and 32 bits for the green channel. + /// + R32G32_Typeless = 15, + + /// + /// A two-component, 64-bit floating-point format that supports 32 bits for the red channel and 32 bits for the green channel. + /// + R32G32_Float = 16, + + /// + /// A two-component, 64-bit unsigned-integer format that supports 32 bits for the red channel and 32 bits for the green channel. + /// + R32G32_UInt = 17, + + /// + /// A two-component, 64-bit signed-integer format that supports 32 bits for the red channel and 32 bits for the green channel. + /// + R32G32_SInt = 18, + + /// + /// A two-component, 64-bit typeless format that supports 32 bits for the red channel, 8 bits for the green channel, and 24 bits are unused. + /// + R32G8X24_Typeless = 19, + + /// + /// A 32-bit floating-point component, and two unsigned-integer components (with an additional 32 bits). This format supports 32-bit depth, 8-bit stencil, and 24 bits are unused. + /// + D32_Float_S8X24_UInt = 20, + + /// + /// A 32-bit floating-point component, and two typeless components (with an additional 32 bits). This format supports 32-bit red channel, 8 bits are unused, and 24 bits are unused. + /// + R32_Float_X8X24_Typeless = 21, + + /// + /// A 32-bit typeless component, and two unsigned-integer components (with an additional 32 bits). This format has 32 bits unused, 8 bits for green channel, and 24 bits are unused. + /// + X32_Typeless_G8X24_UInt = 22, + + /// + /// A four-component, 32-bit typeless format that supports 10 bits for each color and 2 bits for alpha. + /// + R10G10B10A2_Typeless = 23, + + /// + /// A four-component, 32-bit unsigned-normalized-integer format that supports 10 bits for each color and 2 bits for alpha. + /// + R10G10B10A2_UNorm = 24, + + /// + /// A four-component, 32-bit unsigned-integer format that supports 10 bits for each color and 2 bits for alpha. + /// + R10G10B10A2_UInt = 25, + + /// + /// Three partial-precision floating-point numbers encoded into a single 32-bit value (a variant of s10e5, which is sign bit, 10-bit mantissa, and 5-bit biased (15) exponent). + /// There are no sign bits, and there is a 5-bit biased (15) exponent for each channel, 6-bit mantissa for R and G, and a 5-bit mantissa for B. + /// + R11G11B10_Float = 26, + + /// + /// A four-component, 32-bit typeless format that supports 8 bits per channel including alpha. + /// + R8G8B8A8_Typeless = 27, + + /// + /// A four-component, 32-bit unsigned-normalized-integer format that supports 8 bits per channel including alpha. + /// + R8G8B8A8_UNorm = 28, + + /// + /// A four-component, 32-bit unsigned-normalized integer sRGB format that supports 8 bits per channel including alpha. + /// + R8G8B8A8_UNorm_SRGB = 29, + + /// + /// A four-component, 32-bit unsigned-integer format that supports 8 bits per channel including alpha. + /// + R8G8B8A8_UInt = 30, + + /// + /// A four-component, 32-bit signed-normalized-integer format that supports 8 bits per channel including alpha. + /// + R8G8B8A8_SNorm = 31, + + /// + /// A four-component, 32-bit signed-integer format that supports 8 bits per channel including alpha. + /// + R8G8B8A8_SInt = 32, + + /// + /// A two-component, 32-bit typeless format that supports 16 bits for the red channel and 16 bits for the green channel. + /// + R16G16_Typeless = 33, + + /// + /// A two-component, 32-bit floating-point format that supports 16 bits for the red channel and 16 bits for the green channel. + /// + R16G16_Float = 34, + + /// + /// A two-component, 32-bit unsigned-normalized-integer format that supports 16 bits each for the green and red channels. + /// + R16G16_UNorm = 35, + + /// + /// A two-component, 32-bit unsigned-integer format that supports 16 bits for the red channel and 16 bits for the green channel. + /// + R16G16_UInt = 36, + + /// + /// A two-component, 32-bit signed-normalized-integer format that supports 16 bits for the red channel and 16 bits for the green channel. + /// + R16G16_SNorm = 37, + + /// + /// A two-component, 32-bit signed-integer format that supports 16 bits for the red channel and 16 bits for the green channel. + /// + R16G16_SInt = 38, + + /// + /// A single-component, 32-bit typeless format that supports 32 bits for the red channel. + /// + R32_Typeless = 39, + + /// + /// A single-component, 32-bit floating-point format that supports 32 bits for depth. + /// + D32_Float = 40, + + /// + /// A single-component, 32-bit floating-point format that supports 32 bits for the red channel. + /// + R32_Float = 41, + + /// + /// A single-component, 32-bit unsigned-integer format that supports 32 bits for the red channel. + /// + R32_UInt = 42, + + /// + /// A single-component, 32-bit signed-integer format that supports 32 bits for the red channel. + /// + R32_SInt = 43, + + /// + /// A two-component, 32-bit typeless format that supports 24 bits for the red channel and 8 bits for the green channel. + /// + R24G8_Typeless = 44, + + /// + /// A 32-bit z-buffer format that supports 24 bits for depth and 8 bits for stencil. + /// + D24_UNorm_S8_UInt = 45, + + /// + /// A 32-bit format, that contains a 24 bit, single-component, unsigned-normalized integer, with an additional typeless 8 bits. This format has 24 bits red channel and 8 bits unused. + /// + R24_UNorm_X8_Typeless = 46, + + /// + /// A 32-bit format, that contains a 24 bit, single-component, typeless format, with an additional 8 bit unsigned integer component. This format has 24 bits unused and 8 bits green channel. + /// + X24_Typeless_G8_UInt = 47, + + /// + /// A two-component, 16-bit typeless format that supports 8 bits for the red channel and 8 bits for the green channel. + /// + R8G8_Typeless = 48, + + /// + /// A two-component, 16-bit unsigned-normalized-integer format that supports 8 bits for the red channel and 8 bits for the green channel. + /// + R8G8_UNorm = 49, + + /// + /// A two-component, 16-bit unsigned-integer format that supports 8 bits for the red channel and 8 bits for the green channel. + /// + R8G8_UInt = 50, + + /// + /// A two-component, 16-bit signed-normalized-integer format that supports 8 bits for the red channel and 8 bits for the green channel. + /// + R8G8_SNorm = 51, + + /// + /// A two-component, 16-bit signed-integer format that supports 8 bits for the red channel and 8 bits for the green channel. + /// + R8G8_SInt = 52, + + /// + /// A single-component, 16-bit typeless format that supports 16 bits for the red channel. + /// + R16_Typeless = 53, + + /// + /// A single-component, 16-bit floating-point format that supports 16 bits for the red channel. + /// + R16_Float = 54, + + /// + /// A single-component, 16-bit unsigned-normalized-integer format that supports 16 bits for depth. + /// + D16_UNorm = 55, + + /// + /// A single-component, 16-bit unsigned-normalized-integer format that supports 16 bits for the red channel. + /// + R16_UNorm = 56, + + /// + /// A single-component, 16-bit unsigned-integer format that supports 16 bits for the red channel. + /// + R16_UInt = 57, + + /// + /// A single-component, 16-bit signed-normalized-integer format that supports 16 bits for the red channel. + /// + R16_SNorm = 58, + + /// + /// A single-component, 16-bit signed-integer format that supports 16 bits for the red channel. + /// + R16_SInt = 59, + + /// + /// A single-component, 8-bit typeless format that supports 8 bits for the red channel. + /// + R8_Typeless = 60, + + /// + /// A single-component, 8-bit unsigned-normalized-integer format that supports 8 bits for the red channel. + /// + R8_UNorm = 61, + + /// + /// A single-component, 8-bit unsigned-integer format that supports 8 bits for the red channel. + /// + R8_UInt = 62, + + /// + /// A single-component, 8-bit signed-normalized-integer format that supports 8 bits for the red channel. + /// + R8_SNorm = 63, + + /// + /// A single-component, 8-bit signed-integer format that supports 8 bits for the red channel. + /// + R8_SInt = 64, + + /// + /// A single-component, 8-bit unsigned-normalized-integer format for alpha only. + /// + A8_UNorm = 65, + + /// + /// A single-component, 1-bit unsigned-normalized integer format that supports 1 bit for the red channel. + /// + R1_UNorm = 66, + + /// + /// Three partial-precision floating-point numbers encoded into a single 32-bit value all sharing the same 5-bit exponent (variant of s10e5, which is sign bit, 10-bit mantissa, and 5-bit biased (15) exponent). + /// There is no sign bit, and there is a shared 5-bit biased (15) exponent and a 9-bit mantissa for each channel. + /// + R9G9B9E5_SharedExp = 67, + + /// + /// A four-component, 32-bit unsigned-normalized-integer format. This packed RGB format is analogous to the UYVY format. Each 32-bit block describes a pair of pixels: (R8, G8, B8) and (R8, G8, B8) where the R8/B8 values are repeated, + /// and the G8 values are unique to each pixel. Width must be even. + /// + R8G8_B8G8_UNorm = 68, + + /// + /// A four-component, 32-bit unsigned-normalized-integer format. This packed RGB format is analogous to the YUY2 format. Each 32-bit block describes a pair of pixels: (R8, G8, B8) and (R8, G8, B8) where the R8/B8 values are repeated, + /// and the G8 values are unique to each pixel. Width must be even. + /// + G8R8_G8B8_UNorm = 69, + + /// + /// Four-component typeless block-compression format. + /// + BC1_Typeless = 70, + + /// + /// Four-component block-compression format. + /// + BC1_UNorm = 71, + + /// + /// Four-component block-compression format for sRGB data. + /// + BC1_UNorm_SRGB = 72, + + /// + /// Four-component typeless block-compression format. + /// + BC2_Typeless = 73, + + /// + /// Four-component block-compression format. + /// + BC2_UNorm = 74, + + /// + /// Four-component block-compression format for sRGB data. + /// + BC2_UNorm_SRGB = 75, + + /// + /// Four-component typeless block-compression format. + /// + BC3_Typeless = 76, + + /// + /// Four-component block-compression format. + /// + BC3_UNorm = 77, + + /// + /// Four-component block-compression format for sRGB data. + /// + BC3_UNorm_SRGB = 78, + + /// + /// One-component typeless block-compression format. + /// + BC4_Typeless = 79, + + /// + /// One-component block-compression format. + /// + BC4_UNorm = 80, + + /// + /// One-component block-compression format. + /// + BC4_SNorm = 81, + + /// + /// Two-component typeless block-compression format. + /// + BC5_Typeless = 82, + + /// + /// Two-component block-compression format. + /// + BC5_UNorm = 83, + + /// + /// Two-component block-compression format. + /// + BC5_SNorm = 84, + + /// + /// A three-component, 16-bit unsigned-normalized-integer format that supports 5 bits for blue, 6 bits for green, and 5 bits for red. + /// Direct3D 10 through Direct3D 11: This value is defined for DXGI. However, Direct3D 10, 10.1, or 11 devices do not support this format. + /// Direct3D 11.1: This value is not supported until Windows 8. + /// + B5G6R5_UNorm = 85, + + /// + /// A four-component, 16-bit unsigned-normalized-integer format that supports 5 bits for each color channel and 1-bit alpha. + /// Direct3D 10 through Direct3D 11: This value is defined for DXGI. However, Direct3D 10, 10.1, or 11 devices do not support this format. + /// Direct3D 11.1: This value is not supported until Windows 8. + /// + B5G5R5A1_UNorm = 86, + + /// + /// A four-component, 32-bit unsigned-normalized-integer format that supports 8 bits for each color channel and 8-bit alpha. + /// + B8G8R8A8_UNorm = 87, + + /// + /// A four-component, 32-bit unsigned-normalized-integer format that supports 8 bits for each color channel and 8 bits unused. + /// + B8G8R8X8_UNorm = 88, + + /// + /// A four-component, 32-bit 2.8-biased fixed-point format that supports 10 bits for each color channel and 2-bit alpha. + /// + R10G10B10_XR_BIAS_A2_UNorm = 89, + + /// + /// A four-component, 32-bit typeless format that supports 8 bits for each channel including alpha. + /// + B8G8R8A8_Typeless = 90, + + /// + /// A four-component, 32-bit unsigned-normalized standard RGB format that supports 8 bits for each channel including alpha. + /// + B8G8R8A8_UNorm_SRGB = 91, + + /// + /// A four-component, 32-bit typeless format that supports 8 bits for each color channel, and 8 bits are unused. + /// + B8G8R8X8_Typeless = 92, + + /// + /// A four-component, 32-bit unsigned-normalized standard RGB format that supports 8 bits for each color channel, and 8 bits are unused. + /// + B8G8R8X8_UNorm_SRGB = 93, + + /// + /// A typeless block-compression format. + /// + BC6H_Typeless = 94, + + /// + /// A block-compression format. + /// + BC6H_UF16 = 95, + + /// + /// A block-compression format. + /// + BC6H_SF16 = 96, + + /// + /// A typeless block-compression format. + /// + BC7_Typeless = 97, + + /// + /// A block-compression format. + /// + BC7_UNorm = 98, + + /// + /// A block-compression format. + /// + BC7_UNorm_SRGB = 99, + + /// + /// Most common YUV 4:4:4 video resource format. Valid view formats for this video resource format are DXGI_FORMAT_R8G8B8A8_UNORM and DXGI_FORMAT_R8G8B8A8_UINT. + /// For UAVs, an additional valid view format is DXGI_FORMAT_R32_UINT. + /// By using DXGI_FORMAT_R32_UINT for UAVs, you can both read and write as opposed to just write for DXGI_FORMAT_R8G8B8A8_UNORM and DXGI_FORMAT_R8G8B8A8_UINT. + /// Supported view types are SRV, RTV, and UAV. One view provides a straightforward mapping of the entire surface. + /// The mapping to the view channel is V->R8, U->G8, Y->B8, and A->A8. + /// + AYUV = 100, + + /// + /// 10-bit per channel packed YUV 4:4:4 video resource format. Valid view formats for this video resource format are DXGI_FORMAT_R10G10B10A2_UNORM and DXGI_FORMAT_R10G10B10A2_UINT. + /// For UAVs, an additional valid view format is DXGI_FORMAT_R32_UINT. + /// By using DXGI_FORMAT_R32_UINT for UAVs, you can both read and write as opposed to just write for DXGI_FORMAT_R10G10B10A2_UNORM and DXGI_FORMAT_R10G10B10A2_UINT. + /// Supported view types are SRV and UAV. One view provides a straightforward mapping of the entire surface. + /// The mapping to the view channel is U->R10, Y->G10, V->B10, and A->A2. + /// + Y410 = 101, + + /// + /// 16-bit per channel packed YUV 4:4:4 video resource format. Valid view formats for this video resource format are DXGI_FORMAT_R16G16B16A16_UNORM and DXGI_FORMAT_R16G16B16A16_UINT. + /// Supported view types are SRV and UAV. One view provides a straightforward mapping of the entire surface. + /// The mapping to the view channel is U->R16, Y->G16, V->B16, and A->A16. + /// + Y416 = 102, + + /// + /// Most common YUV 4:2:0 video resource format. Valid luminance data view formats for this video resource format are DXGI_FORMAT_R8_UNORM and DXGI_FORMAT_R8_UINT. + /// Valid chrominance data view formats (width and height are each 1/2 of luminance view) for this video resource format are DXGI_FORMAT_R8G8_UNORM and DXGI_FORMAT_R8G8_UINT. + /// Supported view types are SRV, RTV, and UAV. For luminance data view, the mapping to the view channel is Y->R8. + /// For chrominance data view, the mapping to the view channel is U->R8 and V->G8. + /// + NV12 = 103, + + /// + /// 10-bit per channel planar YUV 4:2:0 video resource format. Valid luminance data view formats for this video resource format are DXGI_FORMAT_R16_UNORM and DXGI_FORMAT_R16_UINT. + /// The runtime does not enforce whether the lowest 6 bits are 0 (given that this video resource format is a 10-bit format that uses 16 bits). + /// If required, application shader code would have to enforce this manually. From the runtime's point of view, DXGI_FORMAT_P010 is no different than DXGI_FORMAT_P016. + /// Valid chrominance data view formats (width and height are each 1/2 of luminance view) for this video resource format are DXGI_FORMAT_R16G16_UNORM and DXGI_FORMAT_R16G16_UINT. + /// For UAVs, an additional valid chrominance data view format is DXGI_FORMAT_R32_UINT. By using DXGI_FORMAT_R32_UINT for UAVs, you can both read and write as opposed to just write for DXGI_FORMAT_R16G16_UNORM and DXGI_FORMAT_R16G16_UINT. Supported view types are SRV, RTV, and UAV. For luminance data view, the mapping to the view channel is Y->R16. + /// For chrominance data view, the mapping to the view channel is U->R16 and V->G16. + /// + P010 = 104, + + /// + /// 16-bit per channel planar YUV 4:2:0 video resource format. Valid luminance data view formats for this video resource format are DXGI_FORMAT_R16_UNORM and DXGI_FORMAT_R16_UINT. + /// Valid chrominance data view formats (width and height are each 1/2 of luminance view) for this video resource format are DXGI_FORMAT_R16G16_UNORM and DXGI_FORMAT_R16G16_UINT. + /// For UAVs, an additional valid chrominance data view format is DXGI_FORMAT_R32_UINT. By using DXGI_FORMAT_R32_UINT for UAVs, you can both read and write as opposed to just write for DXGI_FORMAT_R16G16_UNORM and DXGI_FORMAT_R16G16_UINT. Supported view types are SRV, RTV, and UAV. For luminance data view, the mapping to the view channel is Y->R16. + /// For chrominance data view, the mapping to the view channel is U->R16 and V->G16. + /// + P016 = 105, + + /// + /// 8-bit per channel planar YUV 4:2:0 video resource format. This format is subsampled where each pixel has its own Y value, but each 2x2 pixel block shares a single U and V value. + /// The runtime requires that the width and height of all resources that are created with this format are multiples of 2. The runtime also requires that the left, right, top, and bottom members of any RECT that are used for this format are multiples of 2. + /// This format differs from DXGI_FORMAT_NV12 in that the layout of the data within the resource is completely opaque to applications. + /// Applications cannot use the CPU to map the resource and then access the data within the resource. You cannot use shaders with this format. + /// Because of this behavior, legacy hardware that supports a non-NV12 4:2:0 layout (for example, YV12, and so on) can be used. + /// Also, new hardware that has a 4:2:0 implementation better than NV12 can be used when the application does not need the data to be in a standard layout. + /// + Opaque_420 = 106, + + /// + /// Most common YUV 4:2:2 video resource format. Valid view formats for this video resource format are DXGI_FORMAT_R8G8B8A8_UNORM and DXGI_FORMAT_R8G8B8A8_UINT. + /// For UAVs, an additional valid view format is DXGI_FORMAT_R32_UINT. By using DXGI_FORMAT_R32_UINT for UAVs, you can both read and write as opposed to just write for DXGI_FORMAT_R8G8B8A8_UNORM and DXGI_FORMAT_R8G8B8A8_UINT. + /// Supported view types are SRV and UAV. One view provides a straightforward mapping of the entire surface. The mapping to the view channel is Y0->R8, U0->G8,Y1->B8,and V0->A8. + /// + YUY2 = 107, + + /// + /// 10-bit per channel packed YUV 4:2:2 video resource format. Valid view formats for this video resource format are DXGI_FORMAT_R16G16B16A16_UNORM and DXGI_FORMAT_R16G16B16A16_UINT. + /// The runtime does not enforce whether the lowest 6 bits are 0 (given that this video resource format is a 10-bit format that uses 16 bits). + /// If required, application shader code would have to enforce this manually. From the runtime's point of view, DXGI_FORMAT_Y210 is no different than DXGI_FORMAT_Y216. + /// Supported view types are SRV and UAV. One view provides a straightforward mapping of the entire surface. The mapping to the view channel is Y0->R16, U->G16, Y1->B16, and V->A16. + /// + Y210 = 108, + + /// + /// 16-bit per channel packed YUV 4:2:2 video resource format. Valid view formats for this video resource format are DXGI_FORMAT_R16G16B16A16_UNORM and DXGI_FORMAT_R16G16B16A16_UINT. + /// Supported view types are SRV and UAV. One view provides a straightforward mapping of the entire surface. The mapping to the view channel is Y0->R16, U->G16, Y1->B16, and V->A16. + /// + Y216 = 109, + + /// + /// Most common planar YUV 4:1:1 video resource format. Valid luminance data view formats for this video resource format are DXGI_FORMAT_R8_UNORM and DXGI_FORMAT_R8_UINT. + /// Valid chrominance data view formats (width and height are each 1/4 of luminance view) for this video resource format are DXGI_FORMAT_R8G8_UNORM and DXGI_FORMAT_R8G8_UINT. + /// Supported view types are SRV, RTV, and UAV. For luminance data view, the mapping to the view channel is Y->R8. For chrominance data view, the mapping to the view channel is U->R8 and V->G8. + /// + NV11 = 110, + + /// + /// 4-bit palletized YUV format that is commonly used for DVD subpicture. + /// + AI44 = 111, + + /// + /// 4-bit palletized YUV format that is commonly used for DVD subpicture. + /// + IA44 = 112, + + /// + /// 8-bit palletized format that is used for palletized RGB data when the processor processes ISDB-T data and for palletized YUV data when the processor processes BluRay data. + /// + P8 = 113, + + /// + /// 8-bit palletized format with 8 bits of alpha that is used for palletized YUV data when the processor processes BluRay data. + /// + A8P8 = 114, + + /// + /// A four-component, 16-bit unsigned-normalized integer format that supports 4 bits for each channel including alpha. + /// + B4G4R4A4_UNorm = 115, + + /// + /// A video format; an 8-bit version of a hybrid planar 4:2:2 format. + /// + P208 = 130, + + /// + /// An 8 bit YCbCrA 4:4 rendering format. + /// + V208 = 131, + + /// + /// An 8 bit YCbCrA 4:4:4:4 rendering format. + /// + V408 = 132, } diff --git a/src/ImageSharp.Textures/Formats/Dds/Extensions/DdsHeaderExtensions.cs b/src/ImageSharp.Textures/Formats/Dds/Extensions/DdsHeaderExtensions.cs index faf92f89..91777dc5 100644 --- a/src/ImageSharp.Textures/Formats/Dds/Extensions/DdsHeaderExtensions.cs +++ b/src/ImageSharp.Textures/Formats/Dds/Extensions/DdsHeaderExtensions.cs @@ -3,199 +3,174 @@ using SixLabors.ImageSharp.Textures.Formats.Dds.Emums; -namespace SixLabors.ImageSharp.Textures.Formats.Dds.Extensions +namespace SixLabors.ImageSharp.Textures.Formats.Dds.Extensions; + +internal static class DdsHeaderExtensions { - internal static class DdsHeaderExtensions + /// + /// Gets a value indicating whether determines whether this resource is a cubemap. + /// + /// + /// true if this resource is a cubemap; otherwise, false. + /// + public static bool IsCubemap(this DdsHeader ddsHeader) => (ddsHeader.Caps2 & DdsCaps2.Cubemap) != 0; + + /// + /// Gets a value indicating whether determines whether this resource is a volume texture. + /// + /// + /// true if this resource is a volume texture; otherwise, false. + /// + public static bool IsVolumeTexture(this DdsHeader ddsHeader) => (ddsHeader.Caps2 & DdsCaps2.Volume) != 0; + + /// + /// Gets a value indicating whether determines whether this resource contains compressed surface data. + /// + /// + /// true if this resource contains compressed surface data; otherwise, false. + /// + public static bool IsCompressed(this DdsHeader ddsHeader) => (ddsHeader.Flags & DdsFlags.LinearSize) != 0; + + /// + /// Gets a value indicating whether determines whether this resource contains alpha data. + /// + /// + /// true if this resource contains alpha data; otherwise, false. + /// + public static bool HasAlpha(this DdsHeader ddsHeader) => (ddsHeader.PixelFormat.Flags & DdsPixelFormatFlags.AlphaPixels) != 0; + + /// + /// Gets a value indicating whether determines whether this resource contains mipmap data. + /// + /// + /// true if this resource contains mipmap data; otherwise, false. + /// + public static bool HasMipmaps(this DdsHeader ddsHeader) => (ddsHeader.Caps1 & DdsCaps1.MipMap) != 0 && (ddsHeader.Flags & DdsFlags.MipMapCount) != 0; + + /// + /// Gets the number of textures. + /// + /// + /// Number of textures. + public static int TextureCount(this DdsHeader ddsHeader) => ddsHeader.HasMipmaps() ? (int)ddsHeader.MipMapCount : 1; + + /// + /// Checks if dds resource should have the header. + /// + /// + /// true if dds resource should have the header; + /// otherwise false. + /// + public static bool ShouldHaveDxt10Header(this DdsHeader ddsHeader) => (ddsHeader.PixelFormat.Flags == DdsPixelFormatFlags.FourCC) && (ddsHeader.PixelFormat.FourCC == DdsFourCc.DX10); + + /// + /// Determines whether width and height flags are set, so and + /// contain valid values. + /// + /// + /// true if dimensions flags are set; otherwise, false. + /// + public static bool AreDimensionsSet(this DdsHeader ddsHeader) => (ddsHeader.Flags & DdsFlags.Width) != 0 && (ddsHeader.Flags & DdsFlags.Height) != 0; + + /// + /// Returns either depth of a volume texture in pixels, amount of faces in a cubemap or + /// a 1 for a flat resource. + /// + /// + /// Actual depth of a resource. + /// + public static int ComputeDepth(this DdsHeader ddsHeader) { - /// - /// Gets a value indicating whether determines whether this resource is a cubemap. - /// - /// - /// true if this resource is a cubemap; otherwise, false. - /// - public static bool IsCubemap(this DdsHeader ddsHeader) + int result = 1; + if (ddsHeader.IsVolumeTexture()) { - return (ddsHeader.Caps2 & DdsCaps2.Cubemap) != 0; + result = (int)ddsHeader.Depth; } - - /// - /// Gets a value indicating whether determines whether this resource is a volume texture. - /// - /// - /// true if this resource is a volume texture; otherwise, false. - /// - public static bool IsVolumeTexture(this DdsHeader ddsHeader) + else if (ddsHeader.IsCubemap()) { - return (ddsHeader.Caps2 & DdsCaps2.Volume) != 0; - } + result = 0; - /// - /// Gets a value indicating whether determines whether this resource contains compressed surface data. - /// - /// - /// true if this resource contains compressed surface data; otherwise, false. - /// - public static bool IsCompressed(this DdsHeader ddsHeader) - { - return (ddsHeader.Flags & DdsFlags.LinearSize) != 0; - } + // Partial cubemaps are not supported by Direct3D >= 11, but lets support them for the legacy sake. + // So cubemaps can store up to 6 faces: + if ((ddsHeader.Caps2 & DdsCaps2.CubemapPositiveX) != 0) + { + result++; + } - /// - /// Gets a value indicating whether determines whether this resource contains alpha data. - /// - /// - /// true if this resource contains alpha data; otherwise, false. - /// - public static bool HasAlpha(this DdsHeader ddsHeader) - { - return (ddsHeader.PixelFormat.Flags & DdsPixelFormatFlags.AlphaPixels) != 0; - } + if ((ddsHeader.Caps2 & DdsCaps2.CubemapNegativeX) != 0) + { + result++; + } - /// - /// Gets a value indicating whether determines whether this resource contains mipmap data. - /// - /// - /// true if this resource contains mipmap data; otherwise, false. - /// - public static bool HasMipmaps(this DdsHeader ddsHeader) - { - return (ddsHeader.Caps1 & DdsCaps1.MipMap) != 0 && (ddsHeader.Flags & DdsFlags.MipMapCount) != 0; - } + if ((ddsHeader.Caps2 & DdsCaps2.CubemapPositiveY) != 0) + { + result++; + } - /// - /// Gets the number of textures. - /// - /// - /// Number of textures. - public static int TextureCount(this DdsHeader ddsHeader) - { - return ddsHeader.HasMipmaps() ? (int)ddsHeader.MipMapCount : 1; - } + if ((ddsHeader.Caps2 & DdsCaps2.CubemapNegativeY) != 0) + { + result++; + } - /// - /// Checks if dds resource should have the header. - /// - /// - /// true if dds resource should have the header; - /// otherwise false. - /// - public static bool ShouldHaveDxt10Header(this DdsHeader ddsHeader) - { - return (ddsHeader.PixelFormat.Flags == DdsPixelFormatFlags.FourCC) && (ddsHeader.PixelFormat.FourCC == DdsFourCc.DX10); - } + if ((ddsHeader.Caps2 & DdsCaps2.CubemapPositiveZ) != 0) + { + result++; + } - /// - /// Determines whether width and height flags are set, so and - /// contain valid values. - /// - /// - /// true if dimensions flags are set; otherwise, false. - /// - public static bool AreDimensionsSet(this DdsHeader ddsHeader) - { - return (ddsHeader.Flags & DdsFlags.Width) != 0 && (ddsHeader.Flags & DdsFlags.Height) != 0; + if ((ddsHeader.Caps2 & DdsCaps2.CubemapNegativeZ) != 0) + { + result++; + } } - /// - /// Returns either depth of a volume texture in pixels, amount of faces in a cubemap or - /// a 1 for a flat resource. - /// - /// - /// Actual depth of a resource. - /// - public static int ComputeDepth(this DdsHeader ddsHeader) + return result; + } + + /// + /// Gets the existing cube map faces, if this header represents a cube map. + /// + /// + /// Types of cube map faces stored in this cube map or null if this is not a cubemap. + /// + public static DdsSurfaceType[]? GetExistingCubemapFaces(this DdsHeader ddsHeader) + { + int depth = ddsHeader.ComputeDepth(); + DdsSurfaceType[] result = new DdsSurfaceType[depth]; + int index = 0; + + if (depth > 0) { - int result = 1; - if (ddsHeader.IsVolumeTexture()) + if ((ddsHeader.Caps2 & DdsCaps2.CubemapPositiveX) != 0) { - result = (int)ddsHeader.Depth; + result[index++] = DdsSurfaceType.CubemapPositiveX; } - else if (ddsHeader.IsCubemap()) + + if ((ddsHeader.Caps2 & DdsCaps2.CubemapNegativeX) != 0) { - result = 0; - - // Partial cubemaps are not supported by Direct3D >= 11, but lets support them for the legacy sake. - // So cubemaps can store up to 6 faces: - if ((ddsHeader.Caps2 & DdsCaps2.CubemapPositiveX) != 0) - { - result++; - } - - if ((ddsHeader.Caps2 & DdsCaps2.CubemapNegativeX) != 0) - { - result++; - } - - if ((ddsHeader.Caps2 & DdsCaps2.CubemapPositiveY) != 0) - { - result++; - } - - if ((ddsHeader.Caps2 & DdsCaps2.CubemapNegativeY) != 0) - { - result++; - } - - if ((ddsHeader.Caps2 & DdsCaps2.CubemapPositiveZ) != 0) - { - result++; - } - - if ((ddsHeader.Caps2 & DdsCaps2.CubemapNegativeZ) != 0) - { - result++; - } + result[index++] = DdsSurfaceType.CubemapNegativeX; } - return result; - } + if ((ddsHeader.Caps2 & DdsCaps2.CubemapPositiveY) != 0) + { + result[index++] = DdsSurfaceType.CubemapPositiveY; + } - /// - /// Gets the existing cube map faces, if this header represents a cube map. - /// - /// - /// Types of cube map faces stored in this cube map or null if this is not a cubemap. - /// - public static DdsSurfaceType[]? GetExistingCubemapFaces(this DdsHeader ddsHeader) - { - int depth = ddsHeader.ComputeDepth(); - var result = new DdsSurfaceType[depth]; - int index = 0; + if ((ddsHeader.Caps2 & DdsCaps2.CubemapNegativeY) != 0) + { + result[index++] = DdsSurfaceType.CubemapNegativeY; + } - if (depth > 0) + if ((ddsHeader.Caps2 & DdsCaps2.CubemapPositiveZ) != 0) { - if ((ddsHeader.Caps2 & DdsCaps2.CubemapPositiveX) != 0) - { - result[index++] = DdsSurfaceType.CubemapPositiveX; - } - - if ((ddsHeader.Caps2 & DdsCaps2.CubemapNegativeX) != 0) - { - result[index++] = DdsSurfaceType.CubemapNegativeX; - } - - if ((ddsHeader.Caps2 & DdsCaps2.CubemapPositiveY) != 0) - { - result[index++] = DdsSurfaceType.CubemapPositiveY; - } - - if ((ddsHeader.Caps2 & DdsCaps2.CubemapNegativeY) != 0) - { - result[index++] = DdsSurfaceType.CubemapNegativeY; - } - - if ((ddsHeader.Caps2 & DdsCaps2.CubemapPositiveZ) != 0) - { - result[index++] = DdsSurfaceType.CubemapPositiveZ; - } - - if ((ddsHeader.Caps2 & DdsCaps2.CubemapNegativeZ) != 0) - { - result[index++] = DdsSurfaceType.CubemapNegativeZ; - } + result[index++] = DdsSurfaceType.CubemapPositiveZ; } - return (index > 0) ? result : null; + if ((ddsHeader.Caps2 & DdsCaps2.CubemapNegativeZ) != 0) + { + result[index++] = DdsSurfaceType.CubemapNegativeZ; + } } + + return (index > 0) ? result : null; } } diff --git a/src/ImageSharp.Textures/Formats/Dds/IDdsDecoderOptions.cs b/src/ImageSharp.Textures/Formats/Dds/IDdsDecoderOptions.cs index 40a9b0cf..d48326d7 100644 --- a/src/ImageSharp.Textures/Formats/Dds/IDdsDecoderOptions.cs +++ b/src/ImageSharp.Textures/Formats/Dds/IDdsDecoderOptions.cs @@ -1,12 +1,11 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.Formats.Dds +namespace SixLabors.ImageSharp.Textures.Formats.Dds; + +/// +/// The options for decoding dds textures. Currently empty, but this may change in the future. +/// +internal interface IDdsDecoderOptions { - /// - /// The options for decoding dds textures. Currently empty, but this may change in the future. - /// - internal interface IDdsDecoderOptions - { - } } diff --git a/src/ImageSharp.Textures/Formats/ITextureDecoder.cs b/src/ImageSharp.Textures/Formats/ITextureDecoder.cs index c1e34985..cdf05b4f 100644 --- a/src/ImageSharp.Textures/Formats/ITextureDecoder.cs +++ b/src/ImageSharp.Textures/Formats/ITextureDecoder.cs @@ -1,21 +1,20 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.Formats -{ - using System.IO; +namespace SixLabors.ImageSharp.Textures.Formats; + +using System.IO; +/// +/// Encapsulates properties and methods required for decoding an image from a stream. +/// +public interface ITextureDecoder +{ /// - /// Encapsulates properties and methods required for decoding an image from a stream. + /// Decodes the image from the specified stream to an . /// - public interface ITextureDecoder - { - /// - /// Decodes the image from the specified stream to an . - /// - /// The configuration for the image. - /// The containing image data. - /// The . - Texture DecodeTexture(Configuration configuration, Stream stream); - } + /// The configuration for the image. + /// The containing image data. + /// The . + Texture DecodeTexture(Configuration configuration, Stream stream); } diff --git a/src/ImageSharp.Textures/Formats/ITextureEncoder.cs b/src/ImageSharp.Textures/Formats/ITextureEncoder.cs index a58ad860..a3ff2022 100644 --- a/src/ImageSharp.Textures/Formats/ITextureEncoder.cs +++ b/src/ImageSharp.Textures/Formats/ITextureEncoder.cs @@ -1,20 +1,19 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.Formats -{ - using System.IO; +namespace SixLabors.ImageSharp.Textures.Formats; + +using System.IO; +/// +/// Encapsulates properties and methods required for encoding an image to a stream. +/// +public interface ITextureEncoder +{ /// - /// Encapsulates properties and methods required for encoding an image to a stream. + /// Encodes the image to the specified stream from the . /// - public interface ITextureEncoder - { - /// - /// Encodes the image to the specified stream from the . - /// - /// The to encode from. - /// The to encode the image data to. - void Encode(Texture texture, Stream stream); - } + /// The to encode from. + /// The to encode the image data to. + void Encode(Texture texture, Stream stream); } diff --git a/src/ImageSharp.Textures/Formats/ITextureFormat.cs b/src/ImageSharp.Textures/Formats/ITextureFormat.cs index bfd52970..81253715 100644 --- a/src/ImageSharp.Textures/Formats/ITextureFormat.cs +++ b/src/ImageSharp.Textures/Formats/ITextureFormat.cs @@ -1,63 +1,60 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.Collections.Generic; +namespace SixLabors.ImageSharp.Textures.Formats; -namespace SixLabors.ImageSharp.Textures.Formats +/// +/// Defines the contract for an image format. +/// +public interface ITextureFormat { /// - /// Defines the contract for an image format. + /// Gets the name that describes this image format. /// - public interface ITextureFormat - { - /// - /// Gets the name that describes this image format. - /// - string Name { get; } + string Name { get; } - /// - /// Gets the default mimetype that the image format uses - /// - string DefaultMimeType { get; } + /// + /// Gets the default mimetype that the image format uses + /// + string DefaultMimeType { get; } - /// - /// Gets all the mimetypes that have been used by this image format. - /// - IEnumerable MimeTypes { get; } + /// + /// Gets all the mimetypes that have been used by this image format. + /// + IEnumerable MimeTypes { get; } - /// - /// Gets the file extensions this image format commonly uses. - /// - IEnumerable FileExtensions { get; } - } + /// + /// Gets the file extensions this image format commonly uses. + /// + IEnumerable FileExtensions { get; } +} +/// +/// Defines the contract for an image format containing metadata. +/// +/// The type of format metadata. +public interface IImageFormat : ITextureFormat + where TFormatMetadata : class +{ /// - /// Defines the contract for an image format containing metadata. + /// Creates a default instance of the format metadata. /// - /// The type of format metadata. - public interface IImageFormat : ITextureFormat - where TFormatMetadata : class - { - /// - /// Creates a default instance of the format metadata. - /// - /// The . - TFormatMetadata CreateDefaultFormatMetadata(); - } + /// The . + TFormatMetadata CreateDefaultFormatMetadata(); +} +/// +/// Defines the contract for an image format containing metadata with multiple frames. +/// +/// The type of format metadata. +/// The type of format frame metadata. +public interface IImageFormat : IImageFormat + where TFormatMetadata : class + where TFormatFrameMetadata : class +{ /// - /// Defines the contract for an image format containing metadata with multiple frames. + /// Creates a default instance of the format frame metadata. /// - /// The type of format metadata. - /// The type of format frame metadata. - public interface IImageFormat : IImageFormat - where TFormatMetadata : class - where TFormatFrameMetadata : class - { - /// - /// Creates a default instance of the format frame metadata. - /// - /// The . - TFormatFrameMetadata CreateDefaultFormatFrameMetadata(); - } + /// The . + TFormatFrameMetadata CreateDefaultFormatFrameMetadata(); } diff --git a/src/ImageSharp.Textures/Formats/ITextureFormatDetector.cs b/src/ImageSharp.Textures/Formats/ITextureFormatDetector.cs index 3e24a6f2..c1b3ffed 100644 --- a/src/ImageSharp.Textures/Formats/ITextureFormatDetector.cs +++ b/src/ImageSharp.Textures/Formats/ITextureFormatDetector.cs @@ -1,26 +1,23 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; +namespace SixLabors.ImageSharp.Textures.Formats; -namespace SixLabors.ImageSharp.Textures.Formats +/// +/// Used for detecting mime types from a file header +/// +public interface ITextureFormatDetector { /// - /// Used for detecting mime types from a file header + /// Gets the size of the header for this image type. /// - public interface ITextureFormatDetector - { - /// - /// Gets the size of the header for this image type. - /// - /// The size of the header. - int HeaderSize { get; } + /// The size of the header. + int HeaderSize { get; } - /// - /// Detect mimetype - /// - /// The containing the file header. - /// returns the mime type of detected otherwise returns null - ITextureFormat? DetectFormat(ReadOnlySpan header); - } + /// + /// Detect mimetype + /// + /// The containing the file header. + /// returns the mime type of detected otherwise returns null + ITextureFormat? DetectFormat(ReadOnlySpan header); } diff --git a/src/ImageSharp.Textures/Formats/ITextureFormatManager.cs b/src/ImageSharp.Textures/Formats/ITextureFormatManager.cs index f551e7cd..e8b30604 100644 --- a/src/ImageSharp.Textures/Formats/ITextureFormatManager.cs +++ b/src/ImageSharp.Textures/Formats/ITextureFormatManager.cs @@ -1,192 +1,185 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -namespace SixLabors.ImageSharp.Textures.Formats +namespace SixLabors.ImageSharp.Textures.Formats; + +/// +/// Collection of Image Formats to be used in class. +/// +public class ITextureFormatManager { /// - /// Collection of Image Formats to be used in class. + /// Used for locking against as there is no ConcurrentSet type. + /// + /// + private static readonly object HashLock = new(); + + /// + /// The list of supported keyed to mime types. /// - public class ITextureFormatManager + private readonly ConcurrentDictionary mimeTypeEncoders = new(); + + /// + /// The list of supported keyed to mime types. + /// + private readonly ConcurrentDictionary mimeTypeDecoders = new(); + + /// + /// The list of supported s. + /// + private readonly HashSet imageFormats = []; + + /// + /// The list of supported s. + /// + private ConcurrentBag imageFormatDetectors = []; + + /// + /// Initializes a new instance of the class. + /// + public ITextureFormatManager() { - /// - /// Used for locking against as there is no ConcurrentSet type. - /// - /// - private static readonly object HashLock = new object(); - - /// - /// The list of supported keyed to mime types. - /// - private readonly ConcurrentDictionary mimeTypeEncoders = new ConcurrentDictionary(); - - /// - /// The list of supported keyed to mime types. - /// - private readonly ConcurrentDictionary mimeTypeDecoders = new ConcurrentDictionary(); - - /// - /// The list of supported s. - /// - private readonly HashSet imageFormats = new HashSet(); - - /// - /// The list of supported s. - /// - private ConcurrentBag imageFormatDetectors = new ConcurrentBag(); - - /// - /// Initializes a new instance of the class. - /// - public ITextureFormatManager() - { - } + } - /// - /// Gets the maximum header size of all the formats. - /// - internal int MaxHeaderSize { get; private set; } - - /// - /// Gets the currently registered s. - /// - public IEnumerable ImageFormats => this.imageFormats; - - /// - /// Gets the currently registered s. - /// - internal IEnumerable FormatDetectors => this.imageFormatDetectors; - - /// - /// Gets the currently registered s. - /// - internal IEnumerable> ImageDecoders => this.mimeTypeDecoders; - - /// - /// Gets the currently registered s. - /// - internal IEnumerable> ImageEncoders => this.mimeTypeEncoders; - - /// - /// Registers a new format provider. - /// - /// The format to register as a known format. - public void AddImageFormat(ITextureFormat format) - { - Guard.NotNull(format, nameof(format)); - Guard.NotNull(format.MimeTypes, nameof(format.MimeTypes)); - Guard.NotNull(format.FileExtensions, nameof(format.FileExtensions)); - - lock (HashLock) - { - _ = this.imageFormats.Add(format); - } - } + /// + /// Gets the maximum header size of all the formats. + /// + internal int MaxHeaderSize { get; private set; } - /// - /// For the specified file extensions type find the e . - /// - /// The extension to discover - /// The if found otherwise null - public ITextureFormat? FindFormatByFileExtension(string extension) - { - Guard.NotNullOrWhiteSpace(extension, nameof(extension)); + /// + /// Gets the currently registered s. + /// + public IEnumerable ImageFormats => this.imageFormats; - if (extension[0] == '.') - { - extension = extension.Substring(1); - } + /// + /// Gets the currently registered s. + /// + internal IEnumerable FormatDetectors => this.imageFormatDetectors; - return this.imageFormats.FirstOrDefault(x => x.FileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)); - } + /// + /// Gets the currently registered s. + /// + internal IEnumerable> ImageDecoders => this.mimeTypeDecoders; - /// - /// For the specified mime type find the . - /// - /// The mime-type to discover - /// The if found; otherwise null - public ITextureFormat? FindFormatByMimeType(string mimeType) => this.imageFormats.FirstOrDefault(x => x.MimeTypes.Contains(mimeType, StringComparer.OrdinalIgnoreCase)); - - /// - /// Sets a specific image encoder as the encoder for a specific image format. - /// - /// The image format to register the encoder for. - /// The encoder to use, - public void SetEncoder(ITextureFormat imageFormat, ITextureEncoder encoder) - { - Guard.NotNull(imageFormat, nameof(imageFormat)); - Guard.NotNull(encoder, nameof(encoder)); - this.AddImageFormat(imageFormat); - this.mimeTypeEncoders.AddOrUpdate(imageFormat, encoder, (s, e) => encoder); - } + /// + /// Gets the currently registered s. + /// + internal IEnumerable> ImageEncoders => this.mimeTypeEncoders; + + /// + /// Registers a new format provider. + /// + /// The format to register as a known format. + public void AddImageFormat(ITextureFormat format) + { + Guard.NotNull(format, nameof(format)); + Guard.NotNull(format.MimeTypes, nameof(format.MimeTypes)); + Guard.NotNull(format.FileExtensions, nameof(format.FileExtensions)); - /// - /// Sets a specific image decoder as the decoder for a specific image format. - /// - /// The image format to register the encoder for. - /// The decoder to use, - public void SetDecoder(ITextureFormat imageFormat, ITextureDecoder decoder) + lock (HashLock) { - Guard.NotNull(imageFormat, nameof(imageFormat)); - Guard.NotNull(decoder, nameof(decoder)); - this.AddImageFormat(imageFormat); - this.mimeTypeDecoders.AddOrUpdate(imageFormat, decoder, (s, e) => decoder); + _ = this.imageFormats.Add(format); } + } - /// - /// Removes all the registered image format detectors. - /// - public void ClearImageFormatDetectors() => this.imageFormatDetectors = new ConcurrentBag(); + /// + /// For the specified file extensions type find the e . + /// + /// The extension to discover + /// The if found otherwise null + public ITextureFormat? FindFormatByFileExtension(string extension) + { + Guard.NotNullOrWhiteSpace(extension, nameof(extension)); - /// - /// Adds a new detector for detecting mime types. - /// - /// The detector to add - public void AddImageFormatDetector(ITextureFormatDetector detector) + if (extension[0] == '.') { - Guard.NotNull(detector, nameof(detector)); - this.imageFormatDetectors.Add(detector); - this.SetMaxHeaderSize(); + extension = extension[1..]; } - /// - /// For the specified mime type find the decoder. - /// - /// The format to discover - /// The if found otherwise null - public ITextureDecoder? FindDecoder(ITextureFormat format) - { - Guard.NotNull(format, nameof(format)); + return this.imageFormats.FirstOrDefault(x => x.FileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)); + } - return this.mimeTypeDecoders.TryGetValue(format, out ITextureDecoder? decoder) - ? decoder - : null; - } + /// + /// For the specified mime type find the . + /// + /// The mime-type to discover + /// The if found; otherwise null + public ITextureFormat? FindFormatByMimeType(string mimeType) => this.imageFormats.FirstOrDefault(x => x.MimeTypes.Contains(mimeType, StringComparer.OrdinalIgnoreCase)); - /// - /// For the specified mime type find the encoder. - /// - /// The format to discover - /// The if found otherwise null - public ITextureEncoder? FindEncoder(ITextureFormat format) - { - Guard.NotNull(format, nameof(format)); + /// + /// Sets a specific image encoder as the encoder for a specific image format. + /// + /// The image format to register the encoder for. + /// The encoder to use, + public void SetEncoder(ITextureFormat imageFormat, ITextureEncoder encoder) + { + Guard.NotNull(imageFormat, nameof(imageFormat)); + Guard.NotNull(encoder, nameof(encoder)); + this.AddImageFormat(imageFormat); + this.mimeTypeEncoders.AddOrUpdate(imageFormat, encoder, (s, e) => encoder); + } - return this.mimeTypeEncoders.TryGetValue(format, out ITextureEncoder? encoder) - ? encoder - : null; - } + /// + /// Sets a specific image decoder as the decoder for a specific image format. + /// + /// The image format to register the encoder for. + /// The decoder to use, + public void SetDecoder(ITextureFormat imageFormat, ITextureDecoder decoder) + { + Guard.NotNull(imageFormat, nameof(imageFormat)); + Guard.NotNull(decoder, nameof(decoder)); + this.AddImageFormat(imageFormat); + this.mimeTypeDecoders.AddOrUpdate(imageFormat, decoder, (s, e) => decoder); + } - /// - /// Sets the max header size. - /// - private void SetMaxHeaderSize() - { - this.MaxHeaderSize = this.imageFormatDetectors.Max(x => x.HeaderSize); - } + /// + /// Removes all the registered image format detectors. + /// + public void ClearImageFormatDetectors() => this.imageFormatDetectors = []; + + /// + /// Adds a new detector for detecting mime types. + /// + /// The detector to add + public void AddImageFormatDetector(ITextureFormatDetector detector) + { + Guard.NotNull(detector, nameof(detector)); + this.imageFormatDetectors.Add(detector); + this.SetMaxHeaderSize(); + } + + /// + /// For the specified mime type find the decoder. + /// + /// The format to discover + /// The if found otherwise null + public ITextureDecoder? FindDecoder(ITextureFormat format) + { + Guard.NotNull(format, nameof(format)); + + return this.mimeTypeDecoders.TryGetValue(format, out ITextureDecoder? decoder) + ? decoder + : null; + } + + /// + /// For the specified mime type find the encoder. + /// + /// The format to discover + /// The if found otherwise null + public ITextureEncoder? FindEncoder(ITextureFormat format) + { + Guard.NotNull(format, nameof(format)); + + return this.mimeTypeEncoders.TryGetValue(format, out ITextureEncoder? encoder) + ? encoder + : null; } + + /// + /// Sets the max header size. + /// + private void SetMaxHeaderSize() => this.MaxHeaderSize = this.imageFormatDetectors.Max(x => x.HeaderSize); } diff --git a/src/ImageSharp.Textures/Formats/ITextureInfoDetector.cs b/src/ImageSharp.Textures/Formats/ITextureInfoDetector.cs index 3865e173..0a582987 100644 --- a/src/ImageSharp.Textures/Formats/ITextureInfoDetector.cs +++ b/src/ImageSharp.Textures/Formats/ITextureInfoDetector.cs @@ -1,21 +1,18 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.IO; +namespace SixLabors.ImageSharp.Textures.Formats; -namespace SixLabors.ImageSharp.Textures.Formats +/// +/// Encapsulates methods used for detecting the raw image information without fully decoding it. +/// +public interface ITextureInfoDetector { /// - /// Encapsulates methods used for detecting the raw image information without fully decoding it. + /// Reads the raw image information from the specified stream. /// - public interface ITextureInfoDetector - { - /// - /// Reads the raw image information from the specified stream. - /// - /// The configuration for the image. - /// The containing image data. - /// The object - ITextureInfo Identify(Configuration configuration, Stream stream); - } + /// The configuration for the image. + /// The containing image data. + /// The object + ITextureInfo Identify(Configuration configuration, Stream stream); } diff --git a/src/ImageSharp.Textures/Formats/Ktx/Enums/GlBaseInternalPixelFormat.cs b/src/ImageSharp.Textures/Formats/Ktx/Enums/GlBaseInternalPixelFormat.cs index bd585fe3..946ceff9 100644 --- a/src/ImageSharp.Textures/Formats/Ktx/Enums/GlBaseInternalPixelFormat.cs +++ b/src/ImageSharp.Textures/Formats/Ktx/Enums/GlBaseInternalPixelFormat.cs @@ -1,34 +1,33 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.Formats.Ktx.Enums +namespace SixLabors.ImageSharp.Textures.Formats.Ktx.Enums; + +internal enum GlBaseInternalPixelFormat : uint { - internal enum GlBaseInternalPixelFormat : uint - { - Red = 0x1903, + Red = 0x1903, - Green = 0x1904, + Green = 0x1904, - Blue = 0x1905, + Blue = 0x1905, - Alpha = 0x1906, + Alpha = 0x1906, - Rgb = 0x1907, + Rgb = 0x1907, - Rgba = 0x1908, + Rgba = 0x1908, - Luminance = 0x1909, + Luminance = 0x1909, - LuminanceAlpha = 0x190A, + LuminanceAlpha = 0x190A, - Intensity = 0x8049, + Intensity = 0x8049, - RedGreen = 0x8227, + RedGreen = 0x8227, - Bgr = 0x80E0, + Bgr = 0x80E0, - Bgra = 0x80E1, + Bgra = 0x80E1, - Rg = 0x8228 - } + Rg = 0x8228 } diff --git a/src/ImageSharp.Textures/Formats/Ktx/Enums/GlInternalPixelFormat.cs b/src/ImageSharp.Textures/Formats/Ktx/Enums/GlInternalPixelFormat.cs index 0597c744..18f65a78 100644 --- a/src/ImageSharp.Textures/Formats/Ktx/Enums/GlInternalPixelFormat.cs +++ b/src/ImageSharp.Textures/Formats/Ktx/Enums/GlInternalPixelFormat.cs @@ -1,162 +1,161 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.Formats.Ktx +namespace SixLabors.ImageSharp.Textures.Formats.Ktx; + +internal enum GlInternalPixelFormat : uint { - internal enum GlInternalPixelFormat : uint - { - Luminance4 = 0x803F, + Luminance4 = 0x803F, - Luminance8 = 0x8040, + Luminance8 = 0x8040, - Luminance4Alpha4 = 0x8043, + Luminance4Alpha4 = 0x8043, - Luminance6Alpha2 = 0x8044, + Luminance6Alpha2 = 0x8044, - Luminance7Alpha8 = 0x8045, + Luminance7Alpha8 = 0x8045, - Rgb4 = 0x804F, + Rgb4 = 0x804F, - Rgb5 = 0x8050, + Rgb5 = 0x8050, - Rgb8 = 0x8051, + Rgb8 = 0x8051, - Rgb16 = 0x8054, + Rgb16 = 0x8054, - Rgba8 = 0x8058, + Rgba8 = 0x8058, - Rgb565 = 0x8D62, + Rgb565 = 0x8D62, - Rgb10 = 0x8052, + Rgb10 = 0x8052, - Rgb12 = 0x8053, + Rgb12 = 0x8053, - Rgba2 = 0x8055, + Rgba2 = 0x8055, - Rgba4 = 0x8056, + Rgba4 = 0x8056, - Rgba12 = 0x805A, + Rgba12 = 0x805A, - Rgb5A1 = 0x8057, + Rgb5A1 = 0x8057, - Rgb10A2 = 0x8059, + Rgb10A2 = 0x8059, - Rgb9E5 = 0x8C3D, + Rgb9E5 = 0x8C3D, - Rgba16 = 0x805B, + Rgba16 = 0x805B, - R8 = 0x8229, + R8 = 0x8229, - R8UnsignedInt = 0x8232, + R8UnsignedInt = 0x8232, - Rg8UnsignedInt = 0x8238, + Rg8UnsignedInt = 0x8238, - Rgb8UnsignedInt = 0x8D7D, + Rgb8UnsignedInt = 0x8D7D, - RgbaUnsignedInt = 0x8D7C, + RgbaUnsignedInt = 0x8D7C, - R32UnsignedInt = 0x8236, + R32UnsignedInt = 0x8236, - Rg32UnsignedInt = 0x823C, + Rg32UnsignedInt = 0x823C, - Rgb32UnsignedInt = 0x8D71, + Rgb32UnsignedInt = 0x8D71, - Rgba32UnsignedInt = 0x8D70, + Rgba32UnsignedInt = 0x8D70, - R16 = 0x822A, + R16 = 0x822A, - Rg8 = 0x822B, + Rg8 = 0x822B, - Rg16 = 0x822C, + Rg16 = 0x822C, - R8SNorm = 0x8F94, + R8SNorm = 0x8F94, - Rg8SNorm = 0x8F95, + Rg8SNorm = 0x8F95, - Rgb8SNorm = 0x8F96, + Rgb8SNorm = 0x8F96, - RgbaSNorm = 0x8F97, + RgbaSNorm = 0x8F97, - Etc1Rgb8Oes = 0x8D64, + Etc1Rgb8Oes = 0x8D64, - RedRgtc1 = 0x8DBB, + RedRgtc1 = 0x8DBB, - SignedRedRgtc1 = 0x8DBC, + SignedRedRgtc1 = 0x8DBC, - RedGreenRgtc2 = 0x8DBD, + RedGreenRgtc2 = 0x8DBD, - SignedRedGreenRgtc2 = 0x8DBE, + SignedRedGreenRgtc2 = 0x8DBE, - RgbDxt1 = 0x83F0, + RgbDxt1 = 0x83F0, - RgbaDxt1 = 0x83F1, + RgbaDxt1 = 0x83F1, - RgbaDxt3 = 0x83F2, + RgbaDxt3 = 0x83F2, - RgbaDxt5 = 0x83F3, + RgbaDxt5 = 0x83F3, - Sr8 = 0x8FBD, + Sr8 = 0x8FBD, - Srg8 = 0x8FBE, + Srg8 = 0x8FBE, - Srgb8 = 0x8C41, + Srgb8 = 0x8C41, - Srgb8Alpha8 = 0x8C43, + Srgb8Alpha8 = 0x8C43, - SrgbDxt1 = 0x8C4C, + SrgbDxt1 = 0x8C4C, - SrgbAlphaDxt1 = 0x8C4D, + SrgbAlphaDxt1 = 0x8C4D, - SrgbAlphaDxt3 = 0x8C4E, + SrgbAlphaDxt3 = 0x8C4E, - SrgbAlphaDxt5 = 0x8C4F, + SrgbAlphaDxt5 = 0x8C4F, - CompressedRed11Eac = 0x9270, + CompressedRed11Eac = 0x9270, - CompressedRedGreen11Eac = 0x9272, + CompressedRedGreen11Eac = 0x9272, - CompressedRedSignedRedEac = 0x9271, + CompressedRedSignedRedEac = 0x9271, - CompressedRedGreenSignedEac = 0x9273, + CompressedRedGreenSignedEac = 0x9273, - CompressedRgb8Etc2 = 0x9274, + CompressedRgb8Etc2 = 0x9274, - CompressedSrgb8Etc2 = 0x9275, + CompressedSrgb8Etc2 = 0x9275, - CompressedRgb8PunchthroughAlpa1Etc2 = 0x9276, + CompressedRgb8PunchthroughAlpa1Etc2 = 0x9276, - CompressedSrgb8PunchthroughAlpa1Etc2 = 0x9277, + CompressedSrgb8PunchthroughAlpa1Etc2 = 0x9277, - CompressedRgb8Etc2Eac = 0x9278, + CompressedRgb8Etc2Eac = 0x9278, - CompressedSrgb8Alpha8Etc2Eac = 0x9279, + CompressedSrgb8Alpha8Etc2Eac = 0x9279, - CompressedRgbaAstc4x4Khr = 0x93B0, + CompressedRgbaAstc4x4Khr = 0x93B0, - CompressedRgbaAstc5x4Khr = 0x93B1, + CompressedRgbaAstc5x4Khr = 0x93B1, - CompressedRgbaAstc5x5Khr = 0x93B2, + CompressedRgbaAstc5x5Khr = 0x93B2, - CompressedRgbaAstc6x5Khr = 0x93B3, + CompressedRgbaAstc6x5Khr = 0x93B3, - CompressedRgbaAstc6x6Khr = 0x93B4, + CompressedRgbaAstc6x6Khr = 0x93B4, - CompressedRgbaAstc8x5Khr = 0x93B5, + CompressedRgbaAstc8x5Khr = 0x93B5, - CompressedRgbaAstc8x6Khr = 0x93B6, + CompressedRgbaAstc8x6Khr = 0x93B6, - CompressedRgbaAstc8x8Khr = 0x93B7, + CompressedRgbaAstc8x8Khr = 0x93B7, - CompressedRgbaAstc10x5Khr = 0x93B8, + CompressedRgbaAstc10x5Khr = 0x93B8, - CompressedRgbaAstc10x6Khr = 0x93B9, + CompressedRgbaAstc10x6Khr = 0x93B9, - CompressedRgbaAstc10x8Khr = 0x93BA, + CompressedRgbaAstc10x8Khr = 0x93BA, - CompressedRgbaAstc10x10Khr = 0x93BB, + CompressedRgbaAstc10x10Khr = 0x93BB, - CompressedRgbaAstc12x10Khr = 0x93BC, + CompressedRgbaAstc12x10Khr = 0x93BC, - CompressedRgbaAstc12x12Khr = 0x93BD, - } + CompressedRgbaAstc12x12Khr = 0x93BD, } diff --git a/src/ImageSharp.Textures/Formats/Ktx/Enums/GlPixelFormat.cs b/src/ImageSharp.Textures/Formats/Ktx/Enums/GlPixelFormat.cs index c709c886..1dad8007 100644 --- a/src/ImageSharp.Textures/Formats/Ktx/Enums/GlPixelFormat.cs +++ b/src/ImageSharp.Textures/Formats/Ktx/Enums/GlPixelFormat.cs @@ -1,76 +1,75 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.Formats.Ktx +namespace SixLabors.ImageSharp.Textures.Formats.Ktx; + +/// +/// Enum for the different OpenGl pixel formats. +/// +internal enum GlPixelFormat : uint { /// - /// Enum for the different OpenGl pixel formats. + /// Zero indicates, that the texture is compressed. /// - internal enum GlPixelFormat : uint - { - /// - /// Zero indicates, that the texture is compressed. - /// - Compressed = 0, + Compressed = 0, - /// - /// Only the red channel. - /// - Red = 0x1903, + /// + /// Only the red channel. + /// + Red = 0x1903, - /// - /// Only the green channel. - /// - Green = 0x1904, + /// + /// Only the green channel. + /// + Green = 0x1904, - /// - /// Only the blue channel. - /// - Blue = 0x1905, + /// + /// Only the blue channel. + /// + Blue = 0x1905, - /// - /// Only the alpha channel. - /// - Alpha = 0x1906, + /// + /// Only the alpha channel. + /// + Alpha = 0x1906, - /// - /// Only luminance. - /// - Luminance = 0x1909, + /// + /// Only luminance. + /// + Luminance = 0x1909, - /// - /// Luminance and alpha. - /// - LuminanceAlpha = 0x190A, + /// + /// Luminance and alpha. + /// + LuminanceAlpha = 0x190A, - /// - /// Pixels are stored only with the red and green channel present. - /// - Rg = 0x8227, + /// + /// Pixels are stored only with the red and green channel present. + /// + Rg = 0x8227, - /// - /// Pixels are stored only with the red and green channel present. - /// - RgInteger = 0x8228, + /// + /// Pixels are stored only with the red and green channel present. + /// + RgInteger = 0x8228, - /// - /// Pixels are stored as RGB. - /// - Rgb = 0x1907, + /// + /// Pixels are stored as RGB. + /// + Rgb = 0x1907, - /// - /// Pixels are stored as RGBA. - /// - Rgba = 0x1908, + /// + /// Pixels are stored as RGBA. + /// + Rgba = 0x1908, - /// - /// Pixels are stored as BGR. - /// - Bgr = 0x80E0, + /// + /// Pixels are stored as BGR. + /// + Bgr = 0x80E0, - /// - /// Pixels are stored as BGRA. - /// - Bgra = 0x80E1 - } + /// + /// Pixels are stored as BGRA. + /// + Bgra = 0x80E1 } diff --git a/src/ImageSharp.Textures/Formats/Ktx/Enums/GlType.cs b/src/ImageSharp.Textures/Formats/Ktx/Enums/GlType.cs index db933289..4abcacce 100644 --- a/src/ImageSharp.Textures/Formats/Ktx/Enums/GlType.cs +++ b/src/ImageSharp.Textures/Formats/Ktx/Enums/GlType.cs @@ -1,51 +1,50 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.Formats.Ktx.Enums +namespace SixLabors.ImageSharp.Textures.Formats.Ktx.Enums; + +internal enum GlType : uint { - internal enum GlType : uint - { - /// - /// Zero indicates, that the texture is compressed. - /// - Compressed = 0, + /// + /// Zero indicates, that the texture is compressed. + /// + Compressed = 0, - Byte = 0x1400, + Byte = 0x1400, - UnsignedByte = 0x1401, + UnsignedByte = 0x1401, - Short = 0x1402, + Short = 0x1402, - UnsignedShort = 0x1403, + UnsignedShort = 0x1403, - Int = 0x1404, + Int = 0x1404, - UnsignedInt = 0x1405, + UnsignedInt = 0x1405, - Int64 = 0x140E, + Int64 = 0x140E, - UnsignedInt64 = 0x140F, + UnsignedInt64 = 0x140F, - HalfFloat = 0x140B, + HalfFloat = 0x140B, - HalfFloatOes = 0x8D61, + HalfFloatOes = 0x8D61, - Float = 0x1406, + Float = 0x1406, - Double = 0x140A, + Double = 0x140A, - UsignedByte332 = 0x8032, + UsignedByte332 = 0x8032, - UnsignedByte233 = 0x8362, + UnsignedByte233 = 0x8362, - UnsignedShort565 = 0x8363, + UnsignedShort565 = 0x8363, - UnsignedShort4444 = 0x8033, + UnsignedShort4444 = 0x8033, - UnsignedShort5551 = 0x8034, + UnsignedShort5551 = 0x8034, - UnsignedInt8888 = 0x8035, + UnsignedInt8888 = 0x8035, - UnsignedInt1010102 = 0x8036, - } + UnsignedInt1010102 = 0x8036, } diff --git a/src/ImageSharp.Textures/Formats/Ktx/Enums/KtxEndianness.cs b/src/ImageSharp.Textures/Formats/Ktx/Enums/KtxEndianness.cs index 4864d1ac..b3211310 100644 --- a/src/ImageSharp.Textures/Formats/Ktx/Enums/KtxEndianness.cs +++ b/src/ImageSharp.Textures/Formats/Ktx/Enums/KtxEndianness.cs @@ -1,18 +1,17 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.Formats.Ktx +namespace SixLabors.ImageSharp.Textures.Formats.Ktx; + +internal enum KtxEndianness : uint { - internal enum KtxEndianness : uint - { - /// - /// Texture data is little endian. - /// - LittleEndian = 0x04030201, + /// + /// Texture data is little endian. + /// + LittleEndian = 0x04030201, - /// - /// Texture data is big endian. - /// - BigEndian = 0x01020304, - } + /// + /// Texture data is big endian. + /// + BigEndian = 0x01020304, } diff --git a/src/ImageSharp.Textures/Formats/Ktx/IKtxDecoderOptions.cs b/src/ImageSharp.Textures/Formats/Ktx/IKtxDecoderOptions.cs index e2b1c398..1ef51ad6 100644 --- a/src/ImageSharp.Textures/Formats/Ktx/IKtxDecoderOptions.cs +++ b/src/ImageSharp.Textures/Formats/Ktx/IKtxDecoderOptions.cs @@ -1,12 +1,11 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.Formats.Ktx +namespace SixLabors.ImageSharp.Textures.Formats.Ktx; + +/// +/// The options for decoding ktx textures. Currently empty, but this may change in the future. +/// +internal interface IKtxDecoderOptions { - /// - /// The options for decoding ktx textures. Currently empty, but this may change in the future. - /// - internal interface IKtxDecoderOptions - { - } } diff --git a/src/ImageSharp.Textures/Formats/Ktx/KtxConfigurationModule.cs b/src/ImageSharp.Textures/Formats/Ktx/KtxConfigurationModule.cs index 81d2de60..3963acd1 100644 --- a/src/ImageSharp.Textures/Formats/Ktx/KtxConfigurationModule.cs +++ b/src/ImageSharp.Textures/Formats/Ktx/KtxConfigurationModule.cs @@ -1,18 +1,17 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.Formats.Ktx +namespace SixLabors.ImageSharp.Textures.Formats.Ktx; + +/// +/// Registers the image encoders, decoders and mime type detectors for the ktx format. +/// +public class KtxConfigurationModule : IConfigurationModule { - /// - /// Registers the image encoders, decoders and mime type detectors for the ktx format. - /// - public class KtxConfigurationModule : IConfigurationModule + /// + public void Configure(Configuration configuration) { - /// - public void Configure(Configuration configuration) - { - configuration.ImageFormatsManager.SetDecoder(KtxFormat.Instance, new KtxDecoder()); - configuration.ImageFormatsManager.AddImageFormatDetector(new KtxImageFormatDetector()); - } + configuration.ImageFormatsManager.SetDecoder(KtxFormat.Instance, new KtxDecoder()); + configuration.ImageFormatsManager.AddImageFormatDetector(new KtxImageFormatDetector()); } } diff --git a/src/ImageSharp.Textures/Formats/Ktx/KtxConstants.cs b/src/ImageSharp.Textures/Formats/Ktx/KtxConstants.cs index 4b2fd316..9c3415dd 100644 --- a/src/ImageSharp.Textures/Formats/Ktx/KtxConstants.cs +++ b/src/ImageSharp.Textures/Formats/Ktx/KtxConstants.cs @@ -1,45 +1,41 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; -using System.Collections.Generic; +namespace SixLabors.ImageSharp.Textures.Formats.Ktx; -namespace SixLabors.ImageSharp.Textures.Formats.Ktx +internal static class KtxConstants { - internal static class KtxConstants - { - /// - /// The size of a KTX header in bytes. - /// - public const int KtxHeaderSize = 52; + /// + /// The size of a KTX header in bytes. + /// + public const int KtxHeaderSize = 52; - /// - /// The list of mimetypes that equate to a ktx file. - /// - public static readonly IEnumerable MimeTypes = new[] { "image/ktx" }; + /// + /// The list of mimetypes that equate to a ktx file. + /// + public static readonly IEnumerable MimeTypes = new[] { "image/ktx" }; - /// - /// The list of file extensions that equate to a ktx file. - /// - public static readonly IEnumerable FileExtensions = new[] { "ktx" }; + /// + /// The list of file extensions that equate to a ktx file. + /// + public static readonly IEnumerable FileExtensions = new[] { "ktx" }; - /// - /// Gets the magic bytes identifying a ktx texture. - /// - public static ReadOnlySpan MagicBytes => new byte[] - { - 0xAB, // « - 0x4B, // K - 0x54, // T - 0x58, // X - 0x20, // " " - 0x31, // 1 - 0x31, // 1 - 0xBB, // » - 0x0D, // \r - 0x0A, // \n - 0x1A, - 0x0A, // \n - }; - } + /// + /// Gets the magic bytes identifying a ktx texture. + /// + public static ReadOnlySpan MagicBytes => + [ + 0xAB, // « + 0x4B, // K + 0x54, // T + 0x58, // X + 0x20, // " " + 0x31, // 1 + 0x31, // 1 + 0xBB, // » + 0x0D, // \r + 0x0A, // \n + 0x1A, + 0x0A, // \n + ]; } diff --git a/src/ImageSharp.Textures/Formats/Ktx/KtxDecoder.cs b/src/ImageSharp.Textures/Formats/Ktx/KtxDecoder.cs index 4f7aa560..7df4b267 100644 --- a/src/ImageSharp.Textures/Formats/Ktx/KtxDecoder.cs +++ b/src/ImageSharp.Textures/Formats/Ktx/KtxDecoder.cs @@ -1,29 +1,26 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.IO; +namespace SixLabors.ImageSharp.Textures.Formats.Ktx; -namespace SixLabors.ImageSharp.Textures.Formats.Ktx +/// +/// Image decoder for KTX textures. +/// +public sealed class KtxDecoder : ITextureDecoder, IKtxDecoderOptions, ITextureInfoDetector { - /// - /// Image decoder for KTX textures. - /// - public sealed class KtxDecoder : ITextureDecoder, IKtxDecoderOptions, ITextureInfoDetector + /// + public Texture DecodeTexture(Configuration configuration, Stream stream) { - /// - public Texture DecodeTexture(Configuration configuration, Stream stream) - { - Guard.NotNull(stream, nameof(stream)); + Guard.NotNull(stream, nameof(stream)); - return new KtxDecoderCore(configuration, this).DecodeTexture(stream); - } + return new KtxDecoderCore(configuration, this).DecodeTexture(stream); + } - /// - public ITextureInfo Identify(Configuration configuration, Stream stream) - { - Guard.NotNull(stream, nameof(stream)); + /// + public ITextureInfo Identify(Configuration configuration, Stream stream) + { + Guard.NotNull(stream, nameof(stream)); - return new KtxDecoderCore(configuration, this).Identify(stream); - } + return new KtxDecoderCore(configuration, this).Identify(stream); } } diff --git a/src/ImageSharp.Textures/Formats/Ktx/KtxDecoderCore.cs b/src/ImageSharp.Textures/Formats/Ktx/KtxDecoderCore.cs index 8a6255bf..a1e7487e 100644 --- a/src/ImageSharp.Textures/Formats/Ktx/KtxDecoderCore.cs +++ b/src/ImageSharp.Textures/Formats/Ktx/KtxDecoderCore.cs @@ -1,110 +1,108 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.IO; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Textures.Common.Exceptions; using SixLabors.ImageSharp.Textures.TextureFormats; -namespace SixLabors.ImageSharp.Textures.Formats.Ktx +namespace SixLabors.ImageSharp.Textures.Formats.Ktx; + +/// +/// Performs the ktx decoding operation. +/// +internal sealed class KtxDecoderCore { /// - /// Performs the ktx decoding operation. + /// The global configuration. /// - internal sealed class KtxDecoderCore - { - /// - /// The global configuration. - /// - private readonly Configuration configuration; - - /// - /// Used for allocating memory during processing operations. - /// - private readonly MemoryAllocator memoryAllocator; - - /// - /// The file header containing general information about the texture. - /// - private KtxHeader ktxHeader; - - /// - /// The texture decoder options. - /// - private readonly IKtxDecoderOptions options; - - /// - /// Initializes a new instance of the class. - /// - /// The configuration. - /// The options. - public KtxDecoderCore(Configuration configuration, IKtxDecoderOptions options) - { - this.configuration = configuration; - this.memoryAllocator = configuration.MemoryAllocator; - this.options = options; - } - - /// - /// Decodes the texture from the specified stream. - /// - /// The stream, where the texture should be decoded from. Cannot be null. - /// The decoded image. - public Texture DecodeTexture(Stream stream) - { - this.ReadFileHeader(stream); + private readonly Configuration configuration; - if (this.ktxHeader.Width == 0) - { - throw new UnknownTextureFormatException("Width cannot be 0"); - } + /// + /// Used for allocating memory during processing operations. + /// + private readonly MemoryAllocator memoryAllocator; - int width = (int)this.ktxHeader.Width; - int height = (int)this.ktxHeader.Height; + /// + /// The file header containing general information about the texture. + /// + private KtxHeader ktxHeader; - // Skip over bytesOfKeyValueData, if any is present. - stream.Position += this.ktxHeader.BytesOfKeyValueData; + /// + /// The texture decoder options. + /// + private readonly IKtxDecoderOptions options; - var ktxProcessor = new KtxProcessor(this.ktxHeader); + /// + /// Initializes a new instance of the class. + /// + /// The configuration. + /// The options. + public KtxDecoderCore(Configuration configuration, IKtxDecoderOptions options) + { + this.configuration = configuration; + this.memoryAllocator = configuration.MemoryAllocator; + this.options = options; + } - if (this.ktxHeader.NumberOfFaces == 6) - { - CubemapTexture cubeMapTexture = ktxProcessor.DecodeCubeMap(stream, width, height); - return cubeMapTexture; - } + /// + /// Decodes the texture from the specified stream. + /// + /// The stream, where the texture should be decoded from. Cannot be null. + /// The decoded image. + public Texture DecodeTexture(Stream stream) + { + this.ReadFileHeader(stream); - var texture = new FlatTexture(); - MipMap[] mipMaps = ktxProcessor.DecodeMipMaps(stream, width, height, this.ktxHeader.NumberOfMipmapLevels); - texture.MipMaps.AddRange(mipMaps); - return texture; + if (this.ktxHeader.Width == 0) + { + throw new UnknownTextureFormatException("Width cannot be 0"); } - /// - /// Reads the raw texture information from the specified stream. - /// - /// The containing texture data. - public ITextureInfo Identify(Stream currentStream) - { - this.ReadFileHeader(currentStream); + int width = (int)this.ktxHeader.Width; + int height = (int)this.ktxHeader.Height; - var textureInfo = new TextureInfo(new TextureTypeInfo((int)this.ktxHeader.PixelDepth), (int)this.ktxHeader.Width, (int)this.ktxHeader.Height); + // Skip over bytesOfKeyValueData, if any is present. + stream.Position += this.ktxHeader.BytesOfKeyValueData; - return textureInfo; - } + KtxProcessor ktxProcessor = new KtxProcessor(this.ktxHeader); - /// - /// Reads the dds file header from the stream. - /// - /// The containing texture data. - private void ReadFileHeader(Stream stream) + if (this.ktxHeader.NumberOfFaces == 6) { - // Discard the magic bytes, we already know at this point its a ktx file. - stream.Position += KtxConstants.MagicBytes.Length; + CubemapTexture cubeMapTexture = ktxProcessor.DecodeCubeMap(stream, width, height); + return cubeMapTexture; + } - byte[] ktxHeaderBuffer = new byte[KtxConstants.KtxHeaderSize]; - stream.Read(ktxHeaderBuffer, 0, KtxConstants.KtxHeaderSize); + FlatTexture texture = new FlatTexture(); + MipMap[] mipMaps = ktxProcessor.DecodeMipMaps(stream, width, height, this.ktxHeader.NumberOfMipmapLevels); + texture.MipMaps.AddRange(mipMaps); + return texture; + } - this.ktxHeader = KtxHeader.Parse(ktxHeaderBuffer); - } + /// + /// Reads the raw texture information from the specified stream. + /// + /// The containing texture data. + public ITextureInfo Identify(Stream currentStream) + { + this.ReadFileHeader(currentStream); + + TextureInfo textureInfo = new TextureInfo(new TextureTypeInfo((int)this.ktxHeader.PixelDepth), (int)this.ktxHeader.Width, (int)this.ktxHeader.Height); + + return textureInfo; + } + + /// + /// Reads the dds file header from the stream. + /// + /// The containing texture data. + private void ReadFileHeader(Stream stream) + { + // Discard the magic bytes, we already know at this point its a ktx file. + stream.Position += KtxConstants.MagicBytes.Length; + + byte[] ktxHeaderBuffer = new byte[KtxConstants.KtxHeaderSize]; + stream.Read(ktxHeaderBuffer, 0, KtxConstants.KtxHeaderSize); + + this.ktxHeader = KtxHeader.Parse(ktxHeaderBuffer); } } diff --git a/src/ImageSharp.Textures/Formats/Ktx/KtxFormat.cs b/src/ImageSharp.Textures/Formats/Ktx/KtxFormat.cs index a46ba1af..0679564a 100644 --- a/src/ImageSharp.Textures/Formats/Ktx/KtxFormat.cs +++ b/src/ImageSharp.Textures/Formats/Ktx/KtxFormat.cs @@ -1,37 +1,34 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.Collections.Generic; +namespace SixLabors.ImageSharp.Textures.Formats.Ktx; -namespace SixLabors.ImageSharp.Textures.Formats.Ktx +/// +/// Registers the texture decoders and mime type detectors for the ktx format. +/// +public sealed class KtxFormat : ITextureFormat { /// - /// Registers the texture decoders and mime type detectors for the ktx format. + /// Prevents a default instance of the class from being created. /// - public sealed class KtxFormat : ITextureFormat + private KtxFormat() { - /// - /// Prevents a default instance of the class from being created. - /// - private KtxFormat() - { - } + } - /// - /// Gets the current instance. - /// - public static KtxFormat Instance { get; } = new KtxFormat(); + /// + /// Gets the current instance. + /// + public static KtxFormat Instance { get; } = new KtxFormat(); - /// - public string Name => "KTX"; + /// + public string Name => "KTX"; - /// - public string DefaultMimeType => "image/ktx"; + /// + public string DefaultMimeType => "image/ktx"; - /// - public IEnumerable MimeTypes => KtxConstants.MimeTypes; + /// + public IEnumerable MimeTypes => KtxConstants.MimeTypes; - /// - public IEnumerable FileExtensions => KtxConstants.FileExtensions; - } + /// + public IEnumerable FileExtensions => KtxConstants.FileExtensions; } diff --git a/src/ImageSharp.Textures/Formats/Ktx/KtxHeader.cs b/src/ImageSharp.Textures/Formats/Ktx/KtxHeader.cs index 67ccc7c1..51ea641e 100644 --- a/src/ImageSharp.Textures/Formats/Ktx/KtxHeader.cs +++ b/src/ImageSharp.Textures/Formats/Ktx/KtxHeader.cs @@ -1,183 +1,181 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Buffers.Binary; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Textures.Common.Exceptions; using SixLabors.ImageSharp.Textures.Formats.Ktx.Enums; -namespace SixLabors.ImageSharp.Textures.Formats.Ktx +namespace SixLabors.ImageSharp.Textures.Formats.Ktx; + +/// +/// Describes a KTX file header. +/// +[StructLayout(LayoutKind.Sequential, Pack = 1)] +internal struct KtxHeader { + public KtxHeader( + KtxEndianness endianness, + GlType glType, + uint glTypeSize, + GlPixelFormat glFormat, + GlInternalPixelFormat glInternalFormat, + GlBaseInternalPixelFormat glBaseInternalFormat, + uint width, + uint height, + uint pixelDepth, + uint numberOfArrayElements, + uint numberOfFaces, + uint numberOfMipmapLevels, + uint bytesOfKeyValueData) + { + this.Endianness = endianness; + this.GlTypeParameter = glType; + this.GlTypeSize = glTypeSize; + this.GlFormat = glFormat; + this.GlInternalFormat = glInternalFormat; + this.GlBaseInternalFormat = glBaseInternalFormat; + this.Width = width; + this.Height = height; + this.PixelDepth = pixelDepth; + this.NumberOfArrayElements = numberOfArrayElements; + this.NumberOfFaces = numberOfFaces; + this.NumberOfMipmapLevels = numberOfMipmapLevels; + this.BytesOfKeyValueData = bytesOfKeyValueData; + } + + /// + /// Gets the endianness. + /// endianness contains the number 0x04030201 written as a 32 bit integer. If the file is little endian then this is represented as the bytes 0x01 0x02 0x03 0x04. + /// If the file is big endian then this is represented as the bytes 0x04 0x03 0x02 0x01. + /// + public KtxEndianness Endianness { get; } + + /// + /// Gets the glType of the texture. + /// For compressed textures, glType must equal 0. For uncompressed textures, glType specifies the type parameter passed to glTex{,Sub}Image*D, + /// usually one of the values from table 8.2 of the OpenGL 4.4 specification + /// + public GlType GlTypeParameter { get; } + + /// + /// Gets the glTypeSize. + /// glTypeSize specifies the data type size that should be used when endianness conversion is required for the texture data stored in the file. + /// If glType is not 0, this should be the size in bytes corresponding to glType. For texture data which does not depend on platform endianness, + /// including compressed texture data, glTypeSize must equal 1. + /// + public uint GlTypeSize { get; } + + /// + /// Gets the glFormat. + /// For compressed textures, glFormat must equal 0. For uncompressed textures, glFormat specifies the format parameter passed to glTex{,Sub}Image*D, + /// usually one of the values from table 8.3 of the OpenGL 4.4 specification. (RGB, RGBA, BGRA, etc.) + /// + public GlPixelFormat GlFormat { get; } + + /// + /// Gets the internal format. + /// For compressed textures, glInternalFormat must equal the compressed internal format, usually one of the values from table 8.14 of the OpenGL 4.4 specification. + /// For uncompressed textures, glInternalFormat specifies the internalformat parameter passed to glTexStorage*D or glTexImage*D, + /// usually one of the sized internal formats from tables 8.12 and 8.13 of the OpenGL 4.4 specification. + /// + public GlInternalPixelFormat GlInternalFormat { get; } + + /// + /// Gets the base internal format. + /// For both compressed and uncompressed textures, glBaseInternalFormat specifies the base internal format of the texture, + /// usually one of the values from table 8.11 of the OpenGL 4.4 specification (RGB, RGBA, ALPHA, etc.). + /// For uncompressed textures, this value will be the same as glFormat and is used as the internalformat parameter when loading into a context that does not support sized formats, + /// such as an unextended OpenGL ES 2.0 context. + /// + public GlBaseInternalPixelFormat GlBaseInternalFormat { get; } + /// - /// Describes a KTX file header. + /// Gets the width in pixels of the texture at level 0. /// - [StructLayout(LayoutKind.Sequential, Pack = 1)] - internal struct KtxHeader + public uint Width { get; } + + /// + /// Gets the height in pixels of the texture at level 0. + /// For 1D textures pixelHeight must be 0. + /// + public uint Height { get; } + + /// + /// Gets the pixel depth. + /// For 1D textures pixelDepth must be 0. For 2D and cube textures pixelDepth must be 0. + /// + public uint PixelDepth { get; } + + /// + /// Gets the number of array elements. + /// If the texture is not an array texture, numberOfArrayElements must equal 0. + /// + public uint NumberOfArrayElements { get; } + + /// + /// Gets the number of faces. + /// numberOfFaces specifies the number of cubemap faces. For cubemaps and cubemap arrays this should be 6. For non cubemaps this should be 1. + /// Cube map faces are stored in the order: +X, -X, +Y, -Y, +Z, -Z. + /// + public uint NumberOfFaces { get; } + + /// + /// Gets the number of mipmap levels. + /// numberOfMipmapLevels must equal 1 for non-mipmapped textures. + /// + public uint NumberOfMipmapLevels { get; } + + /// + /// Gets the BytesOfKeyValueData. + /// An arbitrary number of key/value pairs may follow the header. This can be used to encode any arbitrary data. + /// + public uint BytesOfKeyValueData { get; } + + public static KtxHeader Parse(ReadOnlySpan data) { - public KtxHeader( - KtxEndianness endianness, - GlType glType, - uint glTypeSize, - GlPixelFormat glFormat, - GlInternalPixelFormat glInternalFormat, - GlBaseInternalPixelFormat glBaseInternalFormat, - uint width, - uint height, - uint pixelDepth, - uint numberOfArrayElements, - uint numberOfFaces, - uint numberOfMipmapLevels, - uint bytesOfKeyValueData) + if (data.Length < KtxConstants.KtxHeaderSize) { - this.Endianness = endianness; - this.GlTypeParameter = glType; - this.GlTypeSize = glTypeSize; - this.GlFormat = glFormat; - this.GlInternalFormat = glInternalFormat; - this.GlBaseInternalFormat = glBaseInternalFormat; - this.Width = width; - this.Height = height; - this.PixelDepth = pixelDepth; - this.NumberOfArrayElements = numberOfArrayElements; - this.NumberOfFaces = numberOfFaces; - this.NumberOfMipmapLevels = numberOfMipmapLevels; - this.BytesOfKeyValueData = bytesOfKeyValueData; + throw new ArgumentException($"Ktx header must be {KtxConstants.KtxHeaderSize} bytes. Was {data.Length} bytes.", nameof(data)); } - /// - /// Gets the endianness. - /// endianness contains the number 0x04030201 written as a 32 bit integer. If the file is little endian then this is represented as the bytes 0x01 0x02 0x03 0x04. - /// If the file is big endian then this is represented as the bytes 0x04 0x03 0x02 0x01. - /// - public KtxEndianness Endianness { get; } - - /// - /// Gets the glType of the texture. - /// For compressed textures, glType must equal 0. For uncompressed textures, glType specifies the type parameter passed to glTex{,Sub}Image*D, - /// usually one of the values from table 8.2 of the OpenGL 4.4 specification - /// - public GlType GlTypeParameter { get; } - - /// - /// Gets the glTypeSize. - /// glTypeSize specifies the data type size that should be used when endianness conversion is required for the texture data stored in the file. - /// If glType is not 0, this should be the size in bytes corresponding to glType. For texture data which does not depend on platform endianness, - /// including compressed texture data, glTypeSize must equal 1. - /// - public uint GlTypeSize { get; } - - /// - /// Gets the glFormat. - /// For compressed textures, glFormat must equal 0. For uncompressed textures, glFormat specifies the format parameter passed to glTex{,Sub}Image*D, - /// usually one of the values from table 8.3 of the OpenGL 4.4 specification. (RGB, RGBA, BGRA, etc.) - /// - public GlPixelFormat GlFormat { get; } - - /// - /// Gets the internal format. - /// For compressed textures, glInternalFormat must equal the compressed internal format, usually one of the values from table 8.14 of the OpenGL 4.4 specification. - /// For uncompressed textures, glInternalFormat specifies the internalformat parameter passed to glTexStorage*D or glTexImage*D, - /// usually one of the sized internal formats from tables 8.12 and 8.13 of the OpenGL 4.4 specification. - /// - public GlInternalPixelFormat GlInternalFormat { get; } - - /// - /// Gets the base internal format. - /// For both compressed and uncompressed textures, glBaseInternalFormat specifies the base internal format of the texture, - /// usually one of the values from table 8.11 of the OpenGL 4.4 specification (RGB, RGBA, ALPHA, etc.). - /// For uncompressed textures, this value will be the same as glFormat and is used as the internalformat parameter when loading into a context that does not support sized formats, - /// such as an unextended OpenGL ES 2.0 context. - /// - public GlBaseInternalPixelFormat GlBaseInternalFormat { get; } - - /// - /// Gets the width in pixels of the texture at level 0. - /// - public uint Width { get; } - - /// - /// Gets the height in pixels of the texture at level 0. - /// For 1D textures pixelHeight must be 0. - /// - public uint Height { get; } - - /// - /// Gets the pixel depth. - /// For 1D textures pixelDepth must be 0. For 2D and cube textures pixelDepth must be 0. - /// - public uint PixelDepth { get; } - - /// - /// Gets the number of array elements. - /// If the texture is not an array texture, numberOfArrayElements must equal 0. - /// - public uint NumberOfArrayElements { get; } - - /// - /// Gets the number of faces. - /// numberOfFaces specifies the number of cubemap faces. For cubemaps and cubemap arrays this should be 6. For non cubemaps this should be 1. - /// Cube map faces are stored in the order: +X, -X, +Y, -Y, +Z, -Z. - /// - public uint NumberOfFaces { get; } - - /// - /// Gets the number of mipmap levels. - /// numberOfMipmapLevels must equal 1 for non-mipmapped textures. - /// - public uint NumberOfMipmapLevels { get; } - - /// - /// Gets the BytesOfKeyValueData. - /// An arbitrary number of key/value pairs may follow the header. This can be used to encode any arbitrary data. - /// - public uint BytesOfKeyValueData { get; } - - public static KtxHeader Parse(ReadOnlySpan data) + KtxEndianness endianness = (KtxEndianness)BinaryPrimitives.ReadUInt32LittleEndian(data); + if (endianness is not KtxEndianness.BigEndian and not KtxEndianness.LittleEndian) { - if (data.Length < KtxConstants.KtxHeaderSize) - { - throw new ArgumentException($"Ktx header must be {KtxConstants.KtxHeaderSize} bytes. Was {data.Length} bytes.", nameof(data)); - } - - var endianness = (KtxEndianness)BinaryPrimitives.ReadUInt32LittleEndian(data); - if (endianness != KtxEndianness.BigEndian && endianness != KtxEndianness.LittleEndian) - { - throw new TextureFormatException("ktx file header has an invalid value for endianness"); - } - - if (endianness == KtxEndianness.LittleEndian) - { - return new KtxHeader( - (KtxEndianness)BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(0, 4)), - (GlType)BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(4, 4)), - BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(8, 4)), - (GlPixelFormat)BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(12, 4)), - (GlInternalPixelFormat)BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(16, 4)), - (GlBaseInternalPixelFormat)BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(20, 4)), - BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(24, 4)), - BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(28, 4)), - BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(32, 4)), - BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(36, 4)), - BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(40, 4)), - BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(44, 4)), - BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(48, 4))); - } + throw new TextureFormatException("ktx file header has an invalid value for endianness"); + } + if (endianness == KtxEndianness.LittleEndian) + { return new KtxHeader( - (KtxEndianness)BinaryPrimitives.ReadUInt32BigEndian(data.Slice(0, 4)), - (GlType)BinaryPrimitives.ReadUInt32BigEndian(data.Slice(4, 4)), - BinaryPrimitives.ReadUInt32BigEndian(data.Slice(8, 4)), - (GlPixelFormat)BinaryPrimitives.ReadUInt32BigEndian(data.Slice(12, 4)), - (GlInternalPixelFormat)BinaryPrimitives.ReadUInt32BigEndian(data.Slice(16, 4)), - (GlBaseInternalPixelFormat)BinaryPrimitives.ReadUInt32BigEndian(data.Slice(20, 4)), - BinaryPrimitives.ReadUInt32BigEndian(data.Slice(24, 4)), - BinaryPrimitives.ReadUInt32BigEndian(data.Slice(28, 4)), - BinaryPrimitives.ReadUInt32BigEndian(data.Slice(32, 4)), - BinaryPrimitives.ReadUInt32BigEndian(data.Slice(36, 4)), - BinaryPrimitives.ReadUInt32BigEndian(data.Slice(40, 4)), - BinaryPrimitives.ReadUInt32BigEndian(data.Slice(44, 4)), - BinaryPrimitives.ReadUInt32BigEndian(data.Slice(48, 4))); + (KtxEndianness)BinaryPrimitives.ReadUInt32LittleEndian(data[..4]), + (GlType)BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(4, 4)), + BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(8, 4)), + (GlPixelFormat)BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(12, 4)), + (GlInternalPixelFormat)BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(16, 4)), + (GlBaseInternalPixelFormat)BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(20, 4)), + BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(24, 4)), + BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(28, 4)), + BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(32, 4)), + BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(36, 4)), + BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(40, 4)), + BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(44, 4)), + BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(48, 4))); } + + return new KtxHeader( + (KtxEndianness)BinaryPrimitives.ReadUInt32BigEndian(data[..4]), + (GlType)BinaryPrimitives.ReadUInt32BigEndian(data.Slice(4, 4)), + BinaryPrimitives.ReadUInt32BigEndian(data.Slice(8, 4)), + (GlPixelFormat)BinaryPrimitives.ReadUInt32BigEndian(data.Slice(12, 4)), + (GlInternalPixelFormat)BinaryPrimitives.ReadUInt32BigEndian(data.Slice(16, 4)), + (GlBaseInternalPixelFormat)BinaryPrimitives.ReadUInt32BigEndian(data.Slice(20, 4)), + BinaryPrimitives.ReadUInt32BigEndian(data.Slice(24, 4)), + BinaryPrimitives.ReadUInt32BigEndian(data.Slice(28, 4)), + BinaryPrimitives.ReadUInt32BigEndian(data.Slice(32, 4)), + BinaryPrimitives.ReadUInt32BigEndian(data.Slice(36, 4)), + BinaryPrimitives.ReadUInt32BigEndian(data.Slice(40, 4)), + BinaryPrimitives.ReadUInt32BigEndian(data.Slice(44, 4)), + BinaryPrimitives.ReadUInt32BigEndian(data.Slice(48, 4))); } } diff --git a/src/ImageSharp.Textures/Formats/Ktx/KtxImageFormatDetector.cs b/src/ImageSharp.Textures/Formats/Ktx/KtxImageFormatDetector.cs index 4d4f9c7f..e83eaac6 100644 --- a/src/ImageSharp.Textures/Formats/Ktx/KtxImageFormatDetector.cs +++ b/src/ImageSharp.Textures/Formats/Ktx/KtxImageFormatDetector.cs @@ -1,30 +1,27 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; +namespace SixLabors.ImageSharp.Textures.Formats.Ktx; -namespace SixLabors.ImageSharp.Textures.Formats.Ktx +/// +/// Detects ktx file headers. +/// +public sealed class KtxImageFormatDetector : ITextureFormatDetector { - /// - /// Detects ktx file headers. - /// - public sealed class KtxImageFormatDetector : ITextureFormatDetector - { - /// - public int HeaderSize => 12; + /// + public int HeaderSize => 12; - /// - public ITextureFormat? DetectFormat(ReadOnlySpan header) => this.IsSupportedFileFormat(header) ? KtxFormat.Instance : null; + /// + public ITextureFormat? DetectFormat(ReadOnlySpan header) => this.IsSupportedFileFormat(header) ? KtxFormat.Instance : null; - private bool IsSupportedFileFormat(ReadOnlySpan header) + private bool IsSupportedFileFormat(ReadOnlySpan header) + { + if (header.Length >= this.HeaderSize) { - if (header.Length >= this.HeaderSize) - { - ReadOnlySpan magicBytes = header.Slice(0, 12); - return magicBytes.SequenceEqual(KtxConstants.MagicBytes); - } - - return false; + ReadOnlySpan magicBytes = header[..12]; + return magicBytes.SequenceEqual(KtxConstants.MagicBytes); } + + return false; } } diff --git a/src/ImageSharp.Textures/Formats/Ktx/KtxProcessor.cs b/src/ImageSharp.Textures/Formats/Ktx/KtxProcessor.cs index 88ac7f20..908747b1 100644 --- a/src/ImageSharp.Textures/Formats/Ktx/KtxProcessor.cs +++ b/src/ImageSharp.Textures/Formats/Ktx/KtxProcessor.cs @@ -1,300 +1,353 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Buffers.Binary; -using System.IO; using SixLabors.ImageSharp.Textures.Common.Exceptions; using SixLabors.ImageSharp.Textures.TextureFormats; using SixLabors.ImageSharp.Textures.TextureFormats.Decoding; -namespace SixLabors.ImageSharp.Textures.Formats.Ktx +namespace SixLabors.ImageSharp.Textures.Formats.Ktx; + +/// +/// Decodes ktx textures. +/// +internal class KtxProcessor { /// - /// Decodes ktx textures. + /// A scratch buffer to reduce allocations. /// - internal class KtxProcessor - { - /// - /// A scratch buffer to reduce allocations. - /// - private readonly byte[] buffer = new byte[4]; - - /// - /// Initializes a new instance of the class. - /// - /// The KTX header. - public KtxProcessor(KtxHeader ktxHeader) => this.KtxHeader = ktxHeader; - - /// - /// Gets the KTX header. - /// - public KtxHeader KtxHeader { get; } - - /// - /// Decodes the mipmaps of a KTX textures. - /// - /// The stream to read the texture data from. - /// The width of the texture at level 0. - /// The height of the texture at level 0. - /// The mipmap count. - /// The decoded mipmaps. - public MipMap[] DecodeMipMaps(Stream stream, int width, int height, uint count) - { - if (this.KtxHeader.GlTypeSize == 1) - { - switch (this.KtxHeader.GlFormat) - { - case GlPixelFormat.Red: - return this.AllocateMipMaps(stream, width, height, count); - case GlPixelFormat.Rg: - case GlPixelFormat.RgInteger: - return this.AllocateMipMaps(stream, width, height, count); - case GlPixelFormat.Rgb: - return this.AllocateMipMaps(stream, width, height, count); - case GlPixelFormat.Rgba: - return this.AllocateMipMaps(stream, width, height, count); - case GlPixelFormat.Bgr: - return this.AllocateMipMaps(stream, width, height, count); - case GlPixelFormat.Bgra: - return this.AllocateMipMaps(stream, width, height, count); - case GlPixelFormat.LuminanceAlpha: - return this.AllocateMipMaps(stream, width, height, count); - case GlPixelFormat.Luminance: - return this.AllocateMipMaps(stream, width, height, count); - case GlPixelFormat.Alpha: - return this.AllocateMipMaps(stream, width, height, count); - case GlPixelFormat.Compressed: - switch (this.KtxHeader.GlInternalFormat) - { - case GlInternalPixelFormat.RgbaDxt1: - case GlInternalPixelFormat.RgbDxt1: - return this.AllocateMipMaps(stream, width, height, count); - case GlInternalPixelFormat.RgbaDxt3: - return this.AllocateMipMaps(stream, width, height, count); - case GlInternalPixelFormat.RgbaDxt5: - return this.AllocateMipMaps(stream, width, height, count); - case GlInternalPixelFormat.RedRgtc1: - return this.AllocateMipMaps(stream, width, height, count); - case GlInternalPixelFormat.SignedRedRgtc1: - return this.AllocateMipMaps(stream, width, height, count); - case GlInternalPixelFormat.RedGreenRgtc2: - return this.AllocateMipMaps(stream, width, height, count); - case GlInternalPixelFormat.SignedRedGreenRgtc2: - return this.AllocateMipMaps(stream, width, height, count); - case GlInternalPixelFormat.Etc1Rgb8Oes: - return this.AllocateMipMaps(stream, width, height, count); - case GlInternalPixelFormat.CompressedRgb8Etc2: - return this.AllocateMipMaps(stream, width, height, count); - } + private readonly byte[] buffer = new byte[4]; - break; - } - } - - if (this.KtxHeader.GlTypeSize == 2 || this.KtxHeader.GlTypeSize == 4) - { - // TODO: endianess is not respected here. Use stream reader which respects endianess. - switch (this.KtxHeader.GlInternalFormat) - { - case GlInternalPixelFormat.Rgb5A1: - return this.AllocateMipMaps(stream, width, height, count); - case GlInternalPixelFormat.Rgb10A2: - return this.AllocateMipMaps(stream, width, height, count); - case GlInternalPixelFormat.Rgb16: - return this.AllocateMipMaps(stream, width, height, count); - case GlInternalPixelFormat.Rgba16: - return this.AllocateMipMaps(stream, width, height, count); - case GlInternalPixelFormat.Rgba32UnsignedInt: - return this.AllocateMipMaps(stream, width, height, count); - } - } + /// + /// Initializes a new instance of the class. + /// + /// The KTX header. + public KtxProcessor(KtxHeader ktxHeader) => this.KtxHeader = ktxHeader; - throw new NotSupportedException("The pixel format is not supported"); - } + /// + /// Gets the KTX header. + /// + public KtxHeader KtxHeader { get; } - /// - /// Decodes the a KTX cube map texture. - /// - /// The stream to read the texture data from. - /// The width of a texture face. - /// The height of a texture face. - /// A decoded cubemap texture. - /// The pixel format is not supported - public CubemapTexture DecodeCubeMap(Stream stream, int width, int height) + /// + /// Decodes the mipmaps of a KTX textures. + /// + /// The stream to read the texture data from. + /// The width of the texture at level 0. + /// The height of the texture at level 0. + /// The mipmap count. + /// The decoded mipmaps. + public MipMap[] DecodeMipMaps(Stream stream, int width, int height, uint count) + { + if (this.KtxHeader.GlTypeSize is 0 or 1) { switch (this.KtxHeader.GlFormat) { case GlPixelFormat.Red: - return this.AllocateCubeMap(stream, width, height); + return this.AllocateMipMaps(stream, width, height, count); case GlPixelFormat.Rg: case GlPixelFormat.RgInteger: - return this.AllocateCubeMap(stream, width, height); + return this.AllocateMipMaps(stream, width, height, count); case GlPixelFormat.Rgb: - return this.AllocateCubeMap(stream, width, height); + return this.AllocateMipMaps(stream, width, height, count); case GlPixelFormat.Rgba: - return this.AllocateCubeMap(stream, width, height); + return this.AllocateMipMaps(stream, width, height, count); case GlPixelFormat.Bgr: - return this.AllocateCubeMap(stream, width, height); + return this.AllocateMipMaps(stream, width, height, count); case GlPixelFormat.Bgra: - return this.AllocateCubeMap(stream, width, height); + return this.AllocateMipMaps(stream, width, height, count); case GlPixelFormat.LuminanceAlpha: - return this.AllocateCubeMap(stream, width, height); + return this.AllocateMipMaps(stream, width, height, count); case GlPixelFormat.Luminance: - return this.AllocateCubeMap(stream, width, height); + return this.AllocateMipMaps(stream, width, height, count); case GlPixelFormat.Alpha: - return this.AllocateCubeMap(stream, width, height); + return this.AllocateMipMaps(stream, width, height, count); case GlPixelFormat.Compressed: switch (this.KtxHeader.GlInternalFormat) { case GlInternalPixelFormat.RgbaDxt1: case GlInternalPixelFormat.RgbDxt1: - return this.AllocateCubeMap(stream, width, height); + return this.AllocateMipMaps(stream, width, height, count); case GlInternalPixelFormat.RgbaDxt3: - return this.AllocateCubeMap(stream, width, height); + return this.AllocateMipMaps(stream, width, height, count); case GlInternalPixelFormat.RgbaDxt5: - return this.AllocateCubeMap(stream, width, height); + return this.AllocateMipMaps(stream, width, height, count); case GlInternalPixelFormat.RedRgtc1: - return this.AllocateCubeMap(stream, width, height); + return this.AllocateMipMaps(stream, width, height, count); case GlInternalPixelFormat.SignedRedRgtc1: - return this.AllocateCubeMap(stream, width, height); + return this.AllocateMipMaps(stream, width, height, count); case GlInternalPixelFormat.RedGreenRgtc2: - return this.AllocateCubeMap(stream, width, height); + return this.AllocateMipMaps(stream, width, height, count); case GlInternalPixelFormat.SignedRedGreenRgtc2: - return this.AllocateCubeMap(stream, width, height); + return this.AllocateMipMaps(stream, width, height, count); case GlInternalPixelFormat.Etc1Rgb8Oes: - return this.AllocateCubeMap(stream, width, height); + return this.AllocateMipMaps(stream, width, height, count); case GlInternalPixelFormat.CompressedRgb8Etc2: - return this.AllocateCubeMap(stream, width, height); + return this.AllocateMipMaps(stream, width, height, count); + case GlInternalPixelFormat.CompressedRgbaAstc4x4Khr: + return this.AllocateMipMaps(stream, width, height, count); + case GlInternalPixelFormat.CompressedRgbaAstc5x4Khr: + return this.AllocateMipMaps(stream, width, height, count); + case GlInternalPixelFormat.CompressedRgbaAstc5x5Khr: + return this.AllocateMipMaps(stream, width, height, count); + case GlInternalPixelFormat.CompressedRgbaAstc6x5Khr: + return this.AllocateMipMaps(stream, width, height, count); + case GlInternalPixelFormat.CompressedRgbaAstc6x6Khr: + return this.AllocateMipMaps(stream, width, height, count); + case GlInternalPixelFormat.CompressedRgbaAstc8x5Khr: + return this.AllocateMipMaps(stream, width, height, count); + case GlInternalPixelFormat.CompressedRgbaAstc8x6Khr: + return this.AllocateMipMaps(stream, width, height, count); + case GlInternalPixelFormat.CompressedRgbaAstc8x8Khr: + return this.AllocateMipMaps(stream, width, height, count); + case GlInternalPixelFormat.CompressedRgbaAstc10x5Khr: + return this.AllocateMipMaps(stream, width, height, count); + case GlInternalPixelFormat.CompressedRgbaAstc10x6Khr: + return this.AllocateMipMaps(stream, width, height, count); + case GlInternalPixelFormat.CompressedRgbaAstc10x8Khr: + return this.AllocateMipMaps(stream, width, height, count); + case GlInternalPixelFormat.CompressedRgbaAstc10x10Khr: + return this.AllocateMipMaps(stream, width, height, count); + case GlInternalPixelFormat.CompressedRgbaAstc12x10Khr: + return this.AllocateMipMaps(stream, width, height, count); + case GlInternalPixelFormat.CompressedRgbaAstc12x12Khr: + return this.AllocateMipMaps(stream, width, height, count); } break; } + } - if (this.KtxHeader.GlTypeSize == 2 || this.KtxHeader.GlTypeSize == 4) + if (this.KtxHeader.GlTypeSize is 2 or 4) + { + // TODO: endianess is not respected here. Use stream reader which respects endianess. + switch (this.KtxHeader.GlInternalFormat) { - // TODO: endianess is not respected here. Use stream reader which respects endianess. + case GlInternalPixelFormat.Rgb5A1: + return this.AllocateMipMaps(stream, width, height, count); + case GlInternalPixelFormat.Rgb10A2: + return this.AllocateMipMaps(stream, width, height, count); + case GlInternalPixelFormat.Rgb16: + return this.AllocateMipMaps(stream, width, height, count); + case GlInternalPixelFormat.Rgba16: + return this.AllocateMipMaps(stream, width, height, count); + case GlInternalPixelFormat.Rgba32UnsignedInt: + return this.AllocateMipMaps(stream, width, height, count); + } + } + + throw new NotSupportedException("The pixel format is not supported"); + } + + /// + /// Decodes the a KTX cube map texture. + /// + /// The stream to read the texture data from. + /// The width of a texture face. + /// The height of a texture face. + /// A decoded cubemap texture. + /// The pixel format is not supported + public CubemapTexture DecodeCubeMap(Stream stream, int width, int height) + { + switch (this.KtxHeader.GlFormat) + { + case GlPixelFormat.Red: + return this.AllocateCubeMap(stream, width, height); + case GlPixelFormat.Rg: + case GlPixelFormat.RgInteger: + return this.AllocateCubeMap(stream, width, height); + case GlPixelFormat.Rgb: + return this.AllocateCubeMap(stream, width, height); + case GlPixelFormat.Rgba: + return this.AllocateCubeMap(stream, width, height); + case GlPixelFormat.Bgr: + return this.AllocateCubeMap(stream, width, height); + case GlPixelFormat.Bgra: + return this.AllocateCubeMap(stream, width, height); + case GlPixelFormat.LuminanceAlpha: + return this.AllocateCubeMap(stream, width, height); + case GlPixelFormat.Luminance: + return this.AllocateCubeMap(stream, width, height); + case GlPixelFormat.Alpha: + return this.AllocateCubeMap(stream, width, height); + case GlPixelFormat.Compressed: switch (this.KtxHeader.GlInternalFormat) { - case GlInternalPixelFormat.Rgb5A1: - return this.AllocateCubeMap(stream, width, height); - case GlInternalPixelFormat.Rgb10A2: - return this.AllocateCubeMap(stream, width, height); - case GlInternalPixelFormat.Rgb16: - return this.AllocateCubeMap(stream, width, height); - case GlInternalPixelFormat.Rgba16: - return this.AllocateCubeMap(stream, width, height); - case GlInternalPixelFormat.Rgba32UnsignedInt: - return this.AllocateCubeMap(stream, width, height); + case GlInternalPixelFormat.RgbaDxt1: + case GlInternalPixelFormat.RgbDxt1: + return this.AllocateCubeMap(stream, width, height); + case GlInternalPixelFormat.RgbaDxt3: + return this.AllocateCubeMap(stream, width, height); + case GlInternalPixelFormat.RgbaDxt5: + return this.AllocateCubeMap(stream, width, height); + case GlInternalPixelFormat.RedRgtc1: + return this.AllocateCubeMap(stream, width, height); + case GlInternalPixelFormat.SignedRedRgtc1: + return this.AllocateCubeMap(stream, width, height); + case GlInternalPixelFormat.RedGreenRgtc2: + return this.AllocateCubeMap(stream, width, height); + case GlInternalPixelFormat.SignedRedGreenRgtc2: + return this.AllocateCubeMap(stream, width, height); + case GlInternalPixelFormat.Etc1Rgb8Oes: + return this.AllocateCubeMap(stream, width, height); + case GlInternalPixelFormat.CompressedRgb8Etc2: + return this.AllocateCubeMap(stream, width, height); + case GlInternalPixelFormat.CompressedRgbaAstc4x4Khr: + return this.AllocateCubeMap(stream, width, height); + case GlInternalPixelFormat.CompressedRgbaAstc5x4Khr: + return this.AllocateCubeMap(stream, width, height); + case GlInternalPixelFormat.CompressedRgbaAstc5x5Khr: + return this.AllocateCubeMap(stream, width, height); + case GlInternalPixelFormat.CompressedRgbaAstc6x5Khr: + return this.AllocateCubeMap(stream, width, height); + case GlInternalPixelFormat.CompressedRgbaAstc6x6Khr: + return this.AllocateCubeMap(stream, width, height); + case GlInternalPixelFormat.CompressedRgbaAstc8x5Khr: + return this.AllocateCubeMap(stream, width, height); + case GlInternalPixelFormat.CompressedRgbaAstc8x6Khr: + return this.AllocateCubeMap(stream, width, height); + case GlInternalPixelFormat.CompressedRgbaAstc8x8Khr: + return this.AllocateCubeMap(stream, width, height); + case GlInternalPixelFormat.CompressedRgbaAstc10x5Khr: + return this.AllocateCubeMap(stream, width, height); + case GlInternalPixelFormat.CompressedRgbaAstc10x6Khr: + return this.AllocateCubeMap(stream, width, height); + case GlInternalPixelFormat.CompressedRgbaAstc10x8Khr: + return this.AllocateCubeMap(stream, width, height); + case GlInternalPixelFormat.CompressedRgbaAstc10x10Khr: + return this.AllocateCubeMap(stream, width, height); + case GlInternalPixelFormat.CompressedRgbaAstc12x10Khr: + return this.AllocateCubeMap(stream, width, height); + case GlInternalPixelFormat.CompressedRgbaAstc12x12Khr: + return this.AllocateCubeMap(stream, width, height); } - } - throw new NotSupportedException("The pixel format is not supported"); + break; } - /// - /// Allocates and decodes the a KTX cube map texture. - /// - /// The stream to read the texture data from. - /// The width of a texture face. - /// The height of a texture face. - /// A decoded cubemap texture. - /// The pixel format is not supported - private CubemapTexture AllocateCubeMap(Stream stream, int width, int height) - where TBlock : struct, IBlock + if (this.KtxHeader.GlTypeSize is 2 or 4) { - var numberOfMipMaps = this.KtxHeader.NumberOfMipmapLevels != 0 ? this.KtxHeader.NumberOfMipmapLevels : 1; - - var cubeMapTexture = new CubemapTexture(); - var blockFormat = default(TBlock); - for (int i = 0; i < numberOfMipMaps; i++) + // TODO: endianess is not respected here. Use stream reader which respects endianess. + switch (this.KtxHeader.GlInternalFormat) { - var dataForEachFace = this.ReadTextureDataSize(stream); - cubeMapTexture.PositiveX.MipMaps.Add(ReadFaceTexture(stream, width, height, blockFormat, dataForEachFace)); - cubeMapTexture.NegativeX.MipMaps.Add(ReadFaceTexture(stream, width, height, blockFormat, dataForEachFace)); - cubeMapTexture.PositiveY.MipMaps.Add(ReadFaceTexture(stream, width, height, blockFormat, dataForEachFace)); - cubeMapTexture.NegativeY.MipMaps.Add(ReadFaceTexture(stream, width, height, blockFormat, dataForEachFace)); - cubeMapTexture.PositiveZ.MipMaps.Add(ReadFaceTexture(stream, width, height, blockFormat, dataForEachFace)); - cubeMapTexture.NegativeZ.MipMaps.Add(ReadFaceTexture(stream, width, height, blockFormat, dataForEachFace)); - - width >>= 1; - height >>= 1; + case GlInternalPixelFormat.Rgb5A1: + return this.AllocateCubeMap(stream, width, height); + case GlInternalPixelFormat.Rgb10A2: + return this.AllocateCubeMap(stream, width, height); + case GlInternalPixelFormat.Rgb16: + return this.AllocateCubeMap(stream, width, height); + case GlInternalPixelFormat.Rgba16: + return this.AllocateCubeMap(stream, width, height); + case GlInternalPixelFormat.Rgba32UnsignedInt: + return this.AllocateCubeMap(stream, width, height); } - - return cubeMapTexture; } - private static MipMap ReadFaceTexture(Stream stream, int width, int height, TBlock blockFormat, uint dataForEachFace) - where TBlock : struct, IBlock - { - byte[] faceData = new byte[dataForEachFace]; - ReadTextureData(stream, faceData); - return new MipMap(blockFormat, faceData, width, height); - } + throw new NotSupportedException("The pixel format is not supported"); + } + + /// + /// Allocates and decodes the a KTX cube map texture. + /// + /// The stream to read the texture data from. + /// The width of a texture face. + /// The height of a texture face. + /// A decoded cubemap texture. + /// The pixel format is not supported + private CubemapTexture AllocateCubeMap(Stream stream, int width, int height) + where TBlock : struct, IBlock + { + uint numberOfMipMaps = this.KtxHeader.NumberOfMipmapLevels != 0 ? this.KtxHeader.NumberOfMipmapLevels : 1; - /// - /// Allocates and decodes all mipmap levels of a ktx texture. - /// - /// The stream to read the texture data from. - /// The width of the texture at level 0. - /// The height of the texture at level 0. - /// The mipmap count. - /// The decoded mipmaps. - private MipMap[] AllocateMipMaps(Stream stream, int width, int height, uint count) - where TBlock : struct, IBlock + CubemapTexture cubeMapTexture = new CubemapTexture(); + TBlock blockFormat = default(TBlock); + for (int i = 0; i < numberOfMipMaps; i++) { - MipMap[] mipMaps = this.ReadMipMaps(stream, width, height, count); + uint dataForEachFace = this.ReadTextureDataSize(stream); + cubeMapTexture.PositiveX.MipMaps.Add(ReadFaceTexture(stream, width, height, blockFormat, dataForEachFace)); + cubeMapTexture.NegativeX.MipMaps.Add(ReadFaceTexture(stream, width, height, blockFormat, dataForEachFace)); + cubeMapTexture.PositiveY.MipMaps.Add(ReadFaceTexture(stream, width, height, blockFormat, dataForEachFace)); + cubeMapTexture.NegativeY.MipMaps.Add(ReadFaceTexture(stream, width, height, blockFormat, dataForEachFace)); + cubeMapTexture.PositiveZ.MipMaps.Add(ReadFaceTexture(stream, width, height, blockFormat, dataForEachFace)); + cubeMapTexture.NegativeZ.MipMaps.Add(ReadFaceTexture(stream, width, height, blockFormat, dataForEachFace)); - return mipMaps; + width >>= 1; + height >>= 1; } - private MipMap[] ReadMipMaps(Stream stream, int width, int height, uint count) - where TBlock : struct, IBlock - { - // If numberOfMipmapLevels equals 0, it indicates that a full mipmap pyramid should be generated from level 0 at load time. - // TODO: generate mipmap pyramid. For now only the first image is loaded. - if (count == 0) - { - count = 1; - } + return cubeMapTexture; + } - var blockFormat = default(TBlock); - var mipMaps = new MipMap[count]; - for (int i = 0; i < count; i++) - { - var pixelDataSize = this.ReadTextureDataSize(stream); - byte[] mipMapData = new byte[pixelDataSize]; - ReadTextureData(stream, mipMapData); + private static MipMap ReadFaceTexture(Stream stream, int width, int height, TBlock blockFormat, uint dataForEachFace) + where TBlock : struct, IBlock + { + byte[] faceData = new byte[dataForEachFace]; + ReadTextureData(stream, faceData); + return new MipMap(blockFormat, faceData, width, height); + } - mipMaps[i] = new MipMap(blockFormat, mipMapData, width, height); + /// + /// Allocates and decodes all mipmap levels of a ktx texture. + /// + /// The stream to read the texture data from. + /// The width of the texture at level 0. + /// The height of the texture at level 0. + /// The mipmap count. + /// The decoded mipmaps. + private MipMap[] AllocateMipMaps(Stream stream, int width, int height, uint count) + where TBlock : struct, IBlock + { + MipMap[] mipMaps = this.ReadMipMaps(stream, width, height, count); - width >>= 1; - height >>= 1; - } + return mipMaps; + } - return mipMaps; + private MipMap[] ReadMipMaps(Stream stream, int width, int height, uint count) + where TBlock : struct, IBlock + { + // If numberOfMipmapLevels equals 0, it indicates that a full mipmap pyramid should be generated from level 0 at load time. + // TODO: generate mipmap pyramid. For now only the first image is loaded. + if (count == 0) + { + count = 1; } - private static void ReadTextureData(Stream stream, byte[] mipMapData) + TBlock blockFormat = default(TBlock); + MipMap[] mipMaps = new MipMap[count]; + for (int i = 0; i < count; i++) { - int bytesRead = stream.Read(mipMapData, 0, mipMapData.Length); - if (bytesRead != mipMapData.Length) - { - throw new TextureFormatException("could not read enough texture data from the stream"); - } + uint pixelDataSize = this.ReadTextureDataSize(stream); + byte[] mipMapData = new byte[pixelDataSize]; + ReadTextureData(stream, mipMapData); + + mipMaps[i] = new MipMap(blockFormat, mipMapData, width, height); + + width >>= 1; + height >>= 1; } - private uint ReadTextureDataSize(Stream stream) - { - int bytesRead = stream.Read(this.buffer, 0, 4); - if (bytesRead != 4) - { - throw new TextureFormatException("could not read texture data length from the stream"); - } + return mipMaps; + } - var pixelDataSize = BinaryPrimitives.ReadUInt32LittleEndian(this.buffer); + private static void ReadTextureData(Stream stream, byte[] mipMapData) + { + int bytesRead = stream.Read(mipMapData, 0, mipMapData.Length); + if (bytesRead != mipMapData.Length) + { + throw new TextureFormatException("could not read enough texture data from the stream"); + } + } - return pixelDataSize; + private uint ReadTextureDataSize(Stream stream) + { + int bytesRead = stream.Read(this.buffer, 0, 4); + if (bytesRead != 4) + { + throw new TextureFormatException("could not read texture data length from the stream"); } + + uint pixelDataSize = BinaryPrimitives.ReadUInt32LittleEndian(this.buffer); + + return pixelDataSize; } } diff --git a/src/ImageSharp.Textures/Formats/Ktx2/Enums/VkFormat.cs b/src/ImageSharp.Textures/Formats/Ktx2/Enums/VkFormat.cs index a3e89738..044b7287 100644 --- a/src/ImageSharp.Textures/Formats/Ktx2/Enums/VkFormat.cs +++ b/src/ImageSharp.Textures/Formats/Ktx2/Enums/VkFormat.cs @@ -2,1089 +2,1088 @@ // Licensed under the Six Labors Split License. // ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Textures.Formats.Ktx2.Enums +namespace SixLabors.ImageSharp.Textures.Formats.Ktx2.Enums; + +/// +/// Vulkan pixel formats. +/// +internal enum VkFormat { /// - /// Vulkan pixel formats. - /// - internal enum VkFormat - { - /// - /// The format is not specified. - /// - VK_FORMAT_UNDEFINED = 0, - - /// - /// Specifies a two-component, 8-bit packed unsigned normalized format that has a 4-bit R component in bits 4..7, and a 4-bit G component in bits 0..3. - /// - VK_FORMAT_R4G4_UNORM_PACK8 = 1, - - /// - /// specifies a four-component, 16-bit packed unsigned normalized format that has a 4-bit R component in bits 12..15, a 4-bit G component in bits 8..11, - /// a 4-bit B component in bits 4..7, and a 4-bit A component in bits 0..3. - /// - VK_FORMAT_R4G4B4A4_UNORM_PACK16 = 2, - - /// - /// Specifies a four-component, 16-bit packed unsigned normalized format that has a 4-bit B component in bits 12..15, a 4-bit G component in bits 8..11, - /// a 4-bit R component in bits 4..7, and a 4-bit A component in bits 0..3. - /// - VK_FORMAT_B4G4R4A4_UNORM_PACK16 = 3, - - /// - /// specifies a three-component, 16-bit packed unsigned normalized format that has a 5-bit R component in bits 11..15, a 6-bit G component in bits 5..10, - /// and a 5-bit B component in bits 0..4. - /// - VK_FORMAT_R5G6B5_UNORM_PACK16 = 4, - - /// - /// specifies a three-component, 16-bit packed unsigned normalized format that has a 5-bit B component in bits 11..15, a 6-bit G component in bits 5..10, - /// and a 5-bit R component in bits 0..4. - /// - VK_FORMAT_B5G6R5_UNORM_PACK16 = 5, - - /// - /// Specifies a four-component, 16-bit packed unsigned normalized format that has a 5-bit R component in bits 11..15, a 5-bit G component in bits 6..10, - /// a 5-bit B component in bits 1..5, and a 1-bit A component in bit 0. - /// - VK_FORMAT_R5G5B5A1_UNORM_PACK16 = 6, - - /// - /// specifies a four-component, 16-bit packed unsigned normalized format that has a 5-bit B component in bits 11..15, a 5-bit G component in bits 6..10, - /// a 5-bit R component in bits 1..5, and a 1-bit A component in bit 0. - /// - VK_FORMAT_B5G5R5A1_UNORM_PACK16 = 7, - - /// - /// Specifies a four-component, 16-bit packed unsigned normalized format that has a 1-bit A component in bit 15, a 5-bit R component in bits 10..14, - /// a 5-bit G component in bits 5..9, and a 5-bit B component in bits 0..4. - /// - VK_FORMAT_A1R5G5B5_UNORM_PACK16 = 8, - - /// - /// Specifies a one-component, 8-bit unsigned normalized format that has a single 8-bit R component. - /// - VK_FORMAT_R8_UNORM = 9, - - /// - /// Specifies a one-component, 8-bit signed normalized format that has a single 8-bit R component. - /// - VK_FORMAT_R8_SNORM = 10, - - /// - /// Specifies a one-component, 8-bit unsigned scaled integer format that has a single 8-bit R component. - /// - VK_FORMAT_R8_USCALED = 11, - - /// - /// Specifies a one-component, 8-bit signed scaled integer format that has a single 8-bit R component. - /// - VK_FORMAT_R8_SSCALED = 12, - - /// - /// Specifies a one-component, 8-bit unsigned integer format that has a single 8-bit R component. - /// - VK_FORMAT_R8_UINT = 13, - - /// - /// Specifies a one-component, 8-bit signed integer format that has a single 8-bit R component. - /// - VK_FORMAT_R8_SINT = 14, - - /// - /// Specifies a one-component, 8-bit unsigned normalized format that has a single 8-bit R component stored with sRGB nonlinear encoding. - /// - VK_FORMAT_R8_SRGB = 15, - - /// - /// Specifies a two-component, 16-bit unsigned normalized format that has an 8-bit R component in byte 0, and an 8-bit G component in byte 1. - /// - VK_FORMAT_R8G8_UNORM = 16, - - /// - /// Specifies a two-component, 16-bit signed normalized format that has an 8-bit R component in byte 0, and an 8-bit G component in byte 1. - /// - VK_FORMAT_R8G8_SNORM = 17, - - /// - /// Specifies a two-component, 16-bit unsigned scaled integer format that has an 8-bit R component in byte 0, and an 8-bit G component in byte 1. - /// - VK_FORMAT_R8G8_USCALED = 18, - - /// - /// Specifies a two-component, 16-bit signed scaled integer format that has an 8-bit R component in byte 0, and an 8-bit G component in byte 1. - /// - VK_FORMAT_R8G8_SSCALED = 19, - - /// - /// Specifies a two-component, 16-bit unsigned integer format that has an 8-bit R component in byte 0, and an 8-bit G component in byte 1. - /// - VK_FORMAT_R8G8_UINT = 20, - - /// - /// Specifies a two-component, 16-bit signed integer format that has an 8-bit R component in byte 0, and an 8-bit G component in byte 1. - /// - VK_FORMAT_R8G8_SINT = 21, - - /// - /// Specifies a two-component, 16-bit unsigned normalized format that has an 8-bit R component stored with sRGB nonlinear encoding in byte 0, - /// and an 8-bit G component stored with sRGB nonlinear encoding in byte 1. - /// - VK_FORMAT_R8G8_SRGB = 22, - - /// - /// Specifies a three-component, 24-bit unsigned normalized format that has an 8-bit R component in byte 0, an 8-bit G component in byte 1, and an 8-bit B component in byte 2. - /// - VK_FORMAT_R8G8B8_UNORM = 23, - - /// - /// Specifies a three-component, 24-bit signed normalized format that has an 8-bit R component in byte 0, an 8-bit G component in byte 1, and an 8-bit B component in byte 2. - /// - VK_FORMAT_R8G8B8_SNORM = 24, - - /// - /// Specifies a three-component, 24-bit unsigned scaled format that has an 8-bit R component in byte 0, an 8-bit G component in byte 1, and an 8-bit B component in byte 2. - /// - VK_FORMAT_R8G8B8_USCALED = 25, - - /// - /// Specifies a three-component, 24-bit signed scaled format that has an 8-bit R component in byte 0, an 8-bit G component in byte 1, and an 8-bit B component in byte 2. - /// - VK_FORMAT_R8G8B8_SSCALED = 26, - - /// - /// Specifies a three-component, 24-bit unsigned integer format that has an 8-bit R component in byte 0, an 8-bit G component in byte 1, and an 8-bit B component in byte 2. - /// - VK_FORMAT_R8G8B8_UINT = 27, - - /// - /// Specifies a three-component, 24-bit signed integer format that has an 8-bit R component in byte 0, an 8-bit G component in byte 1, and an 8-bit B component in byte 2. - /// - VK_FORMAT_R8G8B8_SINT = 28, - - /// - /// Specifies a three-component, 24-bit unsigned normalized format that has an 8-bit R component stored with sRGB nonlinear encoding in byte 0, - /// an 8-bit G component stored with sRGB nonlinear encoding in byte 1, and an 8-bit B component stored with sRGB nonlinear encoding in byte 2. - /// - VK_FORMAT_R8G8B8_SRGB = 29, - - /// - /// Specifies a three-component, 24-bit unsigned normalized format that has an 8-bit B component in byte 0, an 8-bit G component in byte 1, and an 8-bit R component in byte 2. - /// - VK_FORMAT_B8G8R8_UNORM = 30, - - /// - /// Specifies a three-component, 24-bit signed normalized format that has an 8-bit B component in byte 0, an 8-bit G component in byte 1, and an 8-bit R component in byte 2. - /// - VK_FORMAT_B8G8R8_SNORM = 31, - - /// - /// Specifies a three-component, 24-bit unsigned scaled format that has an 8-bit B component in byte 0, an 8-bit G component in byte 1, and an 8-bit R component in byte 2. - /// - VK_FORMAT_B8G8R8_USCALED = 32, - - /// - /// Specifies a three-component, 24-bit signed scaled format that has an 8-bit B component in byte 0, an 8-bit G component in byte 1, and an 8-bit R component in byte 2. - /// - VK_FORMAT_B8G8R8_SSCALED = 33, - - /// - /// Specifies a three-component, 24-bit unsigned integer format that has an 8-bit B component in byte 0, an 8-bit G component in byte 1, and an 8-bit R component in byte 2. - /// - VK_FORMAT_B8G8R8_UINT = 34, - - /// - /// Specifies a three-component, 24-bit signed integer format that has an 8-bit B component in byte 0, an 8-bit G component in byte 1, and an 8-bit R component in byte 2. - /// - VK_FORMAT_B8G8R8_SINT = 35, - - /// - /// Specifies a three-component, 24-bit unsigned normalized format that has an 8-bit B component stored with sRGB nonlinear encoding in byte 0, - /// an 8-bit G component stored with sRGB nonlinear encoding in byte 1, and an 8-bit R component stored with sRGB nonlinear encoding in byte 2. - /// - VK_FORMAT_B8G8R8_SRGB = 36, - - /// - /// Specifies a four-component, 32-bit unsigned normalized format that has an 8-bit R component in byte 0, an 8-bit G component in byte 1, - /// an 8-bit B component in byte 2, and an 8-bit A component in byte 3. - /// - VK_FORMAT_R8G8B8A8_UNORM = 37, - - /// - /// Specifies a four-component, 32-bit signed normalized format that has an 8-bit R component in byte 0, an 8-bit G component in byte 1, - /// an 8-bit B component in byte 2, and an 8-bit A component in byte 3. - /// - VK_FORMAT_R8G8B8A8_SNORM = 38, - - /// - /// Specifies a four-component, 32-bit unsigned scaled format that has an 8-bit R component in byte 0, an 8-bit G component in byte 1, - /// an 8-bit B component in byte 2, and an 8-bit A component in byte 3. - /// - VK_FORMAT_R8G8B8A8_USCALED = 39, - - /// - /// Specifies a four-component, 32-bit signed scaled format that has an 8-bit R component in byte 0, an 8-bit G component in byte 1, - /// an 8-bit B component in byte 2, and an 8-bit A component in byte 3. - /// - VK_FORMAT_R8G8B8A8_SSCALED = 40, - - /// - /// Specifies a four-component, 32-bit unsigned integer format that has an 8-bit R component in byte 0, an 8-bit G component in byte 1, - /// an 8-bit B component in byte 2, and an 8-bit A component in byte 3. - /// - VK_FORMAT_R8G8B8A8_UINT = 41, - - /// - /// Specifies a four-component, 32-bit signed integer format that has an 8-bit R component in byte 0, an 8-bit G component in byte 1, - /// an 8-bit B component in byte 2, and an 8-bit A component in byte 3. - /// - VK_FORMAT_R8G8B8A8_SINT = 42, - - /// - /// Specifies a four-component, 32-bit unsigned normalized format that has an 8-bit R component stored with sRGB nonlinear encoding in byte 0, - /// an 8-bit G component stored with sRGB nonlinear encoding in byte 1, an 8-bit B component stored with sRGB nonlinear encoding in byte 2, and an 8-bit A component in byte 3. - /// - VK_FORMAT_R8G8B8A8_SRGB = 43, - - /// - /// Specifies a four-component, 32-bit unsigned normalized format that has an 8-bit B component in byte 0, an 8-bit G component in byte 1, an 8-bit R component in byte 2, and an 8-bit A component in byte 3. - /// - VK_FORMAT_B8G8R8A8_UNORM = 44, - - /// - /// Specifies a four-component, 32-bit signed normalized format that has an 8-bit B component in byte 0, an 8-bit G component in byte 1, an 8-bit R component in byte 2, and an 8-bit A component in byte 3. - /// - VK_FORMAT_B8G8R8A8_SNORM = 45, - - /// - /// Specifies a four-component, 32-bit unsigned scaled format that has an 8-bit B component in byte 0, an 8-bit G component in byte 1, an 8-bit R component in byte 2, and an 8-bit A component in byte 3. - /// - VK_FORMAT_B8G8R8A8_USCALED = 46, - - /// - /// Specifies a four-component, 32-bit signed scaled format that has an 8-bit B component in byte 0, an 8-bit G component in byte 1, an 8-bit R component in byte 2, and an 8-bit A component in byte 3. - /// - VK_FORMAT_B8G8R8A8_SSCALED = 47, - - /// - /// Specifies a four-component, 32-bit unsigned integer format that has an 8-bit B component in byte 0, an 8-bit G component in byte 1, an 8-bit R component in byte 2, and an 8-bit A component in byte 3. - /// - VK_FORMAT_B8G8R8A8_UINT = 48, - - /// - /// Specifies a four-component, 32-bit signed integer format that has an 8-bit B component in byte 0, an 8-bit G component in byte 1, an 8-bit R component in byte 2, and an 8-bit A component in byte 3. - /// - VK_FORMAT_B8G8R8A8_SINT = 49, - - /// - /// Specifies a four-component, 32-bit unsigned normalized format that has an 8-bit B component stored with sRGB nonlinear encoding in byte 0, - /// an 8-bit G component stored with sRGB nonlinear encoding in byte 1, an 8-bit R component stored with sRGB nonlinear encoding in byte 2, and an 8-bit A component in byte 3. - /// - VK_FORMAT_B8G8R8A8_SRGB = 50, - - /// - /// Specifies a four-component, 32-bit packed unsigned normalized format that has an 8-bit A component in bits 24..31, an 8-bit B component in bits 16..23, - /// an 8-bit G component in bits 8..15, and an 8-bit R component in bits 0..7. - /// - VK_FORMAT_A8B8G8R8_UNORM_PACK32 = 51, - - /// - /// specifies a four-component, 32-bit packed signed normalized format that has an 8-bit A component in bits 24..31, an 8-bit B component in bits 16..23, - /// an 8-bit G component in bits 8..15, and an 8-bit R component in bits 0..7. - /// - VK_FORMAT_A8B8G8R8_SNORM_PACK32 = 52, - - /// - /// Specifies a four-component, 32-bit packed unsigned scaled integer format that has an 8-bit A component in bits 24..31, an 8-bit B component in bits 16..23, - /// an 8-bit G component in bits 8..15, and an 8-bit R component in bits 0..7. - /// - VK_FORMAT_A8B8G8R8_USCALED_PACK32 = 53, - - /// - /// Specifies a four-component, 32-bit packed signed scaled integer format that has an 8-bit A component in bits 24..31, an 8-bit B component in bits 16..23, - /// an 8-bit G component in bits 8..15, and an 8-bit R component in bits 0..7. - /// - VK_FORMAT_A8B8G8R8_SSCALED_PACK32 = 54, - - /// - /// Specifies a four-component, 32-bit packed unsigned integer format that has an 8-bit A component in bits 24..31, an 8-bit B component in bits 16..23, - /// an 8-bit G component in bits 8..15, and an 8-bit R component in bits 0..7. - /// - VK_FORMAT_A8B8G8R8_UINT_PACK32 = 55, - - /// - /// Specifies a four-component, 32-bit packed signed integer format that has an 8-bit A component in bits 24..31, an 8-bit B component in bits 16..23, - /// an 8-bit G component in bits 8..15, and an 8-bit R component in bits 0..7. - /// - VK_FORMAT_A8B8G8R8_SINT_PACK32 = 56, - - /// - /// Specifies a four-component, 32-bit packed unsigned normalized format that has an 8-bit A component in bits 24..31, - /// an 8-bit B component stored with sRGB nonlinear encoding in bits 16..23, an 8-bit G component stored with sRGB nonlinear encoding in bits 8..15, and an 8-bit R component stored with sRGB nonlinear encoding in bits 0..7. - /// - VK_FORMAT_A8B8G8R8_SRGB_PACK32 = 57, - - /// - /// Specifies a four-component, 32-bit packed unsigned normalized format that has a 2-bit A component in bits 30..31, a 10-bit R component in bits 20..29, - /// a 10-bit G component in bits 10..19, and a 10-bit B component in bits 0..9. - /// - VK_FORMAT_A2R10G10B10_UNORM_PACK32 = 58, - - /// - /// Specifies a four-component, 32-bit packed signed normalized format that has a 2-bit A component in bits 30..31, a 10-bit R component in bits 20..29, - /// a 10-bit G component in bits 10..19, and a 10-bit B component in bits 0..9. - /// - VK_FORMAT_A2R10G10B10_SNORM_PACK32 = 59, - - /// - /// Specifies a four-component, 32-bit packed unsigned scaled integer format that has a 2-bit A component in bits 30..31, a 10-bit R component in bits 20..29, - /// a 10-bit G component in bits 10..19, and a 10-bit B component in bits 0..9. - /// - VK_FORMAT_A2R10G10B10_USCALED_PACK32 = 60, - - /// - /// Specifies a four-component, 32-bit packed signed scaled integer format that has a 2-bit A component in bits 30..31, a 10-bit R component in bits 20..29, - /// a 10-bit G component in bits 10..19, and a 10-bit B component in bits 0..9. - /// - VK_FORMAT_A2R10G10B10_SSCALED_PACK32 = 61, - - /// - /// Specifies a four-component, 32-bit packed unsigned integer format that has a 2-bit A component in bits 30..31, a 10-bit R component in bits 20..29, - /// a 10-bit G component in bits 10..19, and a 10-bit B component in bits 0..9. - /// - VK_FORMAT_A2R10G10B10_UINT_PACK32 = 62, - - /// - /// Specifies a four-component, 32-bit packed signed integer format that has a 2-bit A component in bits 30..31, a 10-bit R component in bits 20..29, - /// a 10-bit G component in bits 10..19, and a 10-bit B component in bits 0..9. - /// - VK_FORMAT_A2R10G10B10_SINT_PACK32 = 63, - - /// - /// Specifies a four-component, 32-bit packed unsigned normalized format that has a 2-bit A component in bits 30..31, a 10-bit B component in bits 20..29, - /// a 10-bit G component in bits 10..19, and a 10-bit R component in bits 0..9. - /// - VK_FORMAT_A2B10G10R10_UNORM_PACK32 = 64, - - /// - /// Specifies a four-component, 32-bit packed signed normalized format that has a 2-bit A component in bits 30..31, a 10-bit B component in bits 20..29, - /// a 10-bit G component in bits 10..19, and a 10-bit R component in bits 0..9. - /// - VK_FORMAT_A2B10G10R10_SNORM_PACK32 = 65, - - /// - /// Specifies a four-component, 32-bit packed unsigned scaled integer format that has a 2-bit A component in bits 30..31, a 10-bit B component in bits 20..29, - /// a 10-bit G component in bits 10..19, and a 10-bit R component in bits 0..9. - /// - VK_FORMAT_A2B10G10R10_USCALED_PACK32 = 66, - - /// - /// Specifies a four-component, 32-bit packed signed scaled integer format that has a 2-bit A component in bits 30..31, a 10-bit B component in bits 20..29, - /// a 10-bit G component in bits 10..19, and a 10-bit R component in bits 0..9. - /// - VK_FORMAT_A2B10G10R10_SSCALED_PACK32 = 67, - - /// - /// Specifies a four-component, 32-bit packed unsigned integer format that has a 2-bit A component in bits 30..31, a 10-bit B component in bits 20..29, - /// a 10-bit G component in bits 10..19, and a 10-bit R component in bits 0..9. - /// - VK_FORMAT_A2B10G10R10_UINT_PACK32 = 68, - - /// - /// Specifies a four-component, 32-bit packed signed integer format that has a 2-bit A component in bits 30..31, a 10-bit B component in bits 20..29, - /// a 10-bit G component in bits 10..19, and a 10-bit R component in bits 0..9. - /// - VK_FORMAT_A2B10G10R10_SINT_PACK32 = 69, - - /// - /// Specifies a one-component, 16-bit unsigned normalized format that has a single 16-bit R component. - /// - VK_FORMAT_R16_UNORM = 70, - - /// - /// Specifies a one-component, 16-bit signed normalized format that has a single 16-bit R component. - /// - VK_FORMAT_R16_SNORM = 71, - - /// - /// Specifies a one-component, 16-bit unsigned scaled integer format that has a single 16-bit R component. - /// - VK_FORMAT_R16_USCALED = 72, - - /// - /// Specifies a one-component, 16-bit signed scaled integer format that has a single 16-bit R component. - /// - VK_FORMAT_R16_SSCALED = 73, - - /// - /// Specifies a one-component, 16-bit unsigned integer format that has a single 16-bit R component. - /// - VK_FORMAT_R16_UINT = 74, - - /// - /// Specifies a one-component, 16-bit signed integer format that has a single 16-bit R component. - /// - VK_FORMAT_R16_SINT = 75, - - /// - /// Specifies a one-component, 16-bit signed floating-point format that has a single 16-bit R component. - /// - VK_FORMAT_R16_SFLOAT = 76, - - /// - /// Specifies a two-component, 32-bit unsigned normalized format that has a 16-bit R component in bytes 0..1, and a 16-bit G component in bytes 2..3. - /// - VK_FORMAT_R16G16_UNORM = 77, - - /// - /// Specifies a two-component, 32-bit signed normalized format that has a 16-bit R component in bytes 0..1, and a 16-bit G component in bytes 2..3. - /// - VK_FORMAT_R16G16_SNORM = 78, - - /// - /// Specifies a two-component, 32-bit unsigned scaled integer format that has a 16-bit R component in bytes 0..1, and a 16-bit G component in bytes 2..3. - /// - VK_FORMAT_R16G16_USCALED = 79, - - /// - /// Specifies a two-component, 32-bit signed scaled integer format that has a 16-bit R component in bytes 0..1, and a 16-bit G component in bytes 2..3. - /// - VK_FORMAT_R16G16_SSCALED = 80, - - /// - /// Specifies a two-component, 32-bit unsigned integer format that has a 16-bit R component in bytes 0..1, and a 16-bit G component in bytes 2..3. - /// - VK_FORMAT_R16G16_UINT = 81, - - /// - /// Specifies a two-component, 32-bit signed integer format that has a 16-bit R component in bytes 0..1, and a 16-bit G component in bytes 2..3. - /// - VK_FORMAT_R16G16_SINT = 82, - - /// - /// Specifies a two-component, 32-bit signed floating-point format that has a 16-bit R component in bytes 0..1, and a 16-bit G component in bytes 2..3. - /// - VK_FORMAT_R16G16_SFLOAT = 83, - - /// - /// Specifies a three-component, 48-bit unsigned normalized format that has a 16-bit R component in bytes 0..1, a 16-bit G component in bytes 2..3, - /// and a 16-bit B component in bytes 4..5. - /// - VK_FORMAT_R16G16B16_UNORM = 84, - - /// - /// Specifies a three-component, 48-bit signed normalized format that has a 16-bit R component in bytes 0..1, a 16-bit G component in bytes 2..3, - /// and a 16-bit B component in bytes 4..5. - /// - VK_FORMAT_R16G16B16_SNORM = 85, - - /// - /// Specifies a three-component, 48-bit unsigned scaled integer format that has a 16-bit R component in bytes 0..1, a 16-bit G component in bytes 2..3, - /// and a 16-bit B component in bytes 4..5. - /// - VK_FORMAT_R16G16B16_USCALED = 86, - - /// - /// Specifies a three-component, 48-bit signed scaled integer format that has a 16-bit R component in bytes 0..1, a 16-bit G component in bytes 2..3, - /// and a 16-bit B component in bytes 4..5. - /// - VK_FORMAT_R16G16B16_SSCALED = 87, - - /// - /// Specifies a three-component, 48-bit unsigned integer format that has a 16-bit R component in bytes 0..1, a 16-bit G component in bytes 2..3, - /// and a 16-bit B component in bytes 4..5. - /// - VK_FORMAT_R16G16B16_UINT = 88, - - /// - /// Specifies a three-component, 48-bit signed integer format that has a 16-bit R component in bytes 0..1, a 16-bit G component in bytes 2..3, - /// and a 16-bit B component in bytes 4..5. - /// - VK_FORMAT_R16G16B16_SINT = 89, - - /// - /// Specifies a three-component, 48-bit signed floating-point format that has a 16-bit R component in bytes 0..1, a 16-bit G component in bytes 2..3, - /// and a 16-bit B component in bytes 4..5. - /// - VK_FORMAT_R16G16B16_SFLOAT = 90, - - /// - /// Specifies a four-component, 64-bit unsigned normalized format that has a 16-bit R component in bytes 0..1, a 16-bit G component in bytes 2..3, - /// a 16-bit B component in bytes 4..5, and a 16-bit A component in bytes 6..7. - /// - VK_FORMAT_R16G16B16A16_UNORM = 91, - - /// - /// Specifies a four-component, 64-bit signed normalized format that has a 16-bit R component in bytes 0..1, a 16-bit G component in bytes 2..3, - /// a 16-bit B component in bytes 4..5, and a 16-bit A component in bytes 6..7. - /// - VK_FORMAT_R16G16B16A16_SNORM = 92, - - /// - /// Specifies a four-component, 64-bit unsigned scaled integer format that has a 16-bit R component in bytes 0..1, a 16-bit G component in bytes 2..3, - /// a 16-bit B component in bytes 4..5, and a 16-bit A component in bytes 6..7. - /// - VK_FORMAT_R16G16B16A16_USCALED = 93, - - /// - /// Specifies a four-component, 64-bit signed scaled integer format that has a 16-bit R component in bytes 0..1, a 16-bit G component in bytes 2..3, - /// a 16-bit B component in bytes 4..5, and a 16-bit A component in bytes 6..7. - /// - VK_FORMAT_R16G16B16A16_SSCALED = 94, - - /// - /// Specifies a four-component, 64-bit unsigned integer format that has a 16-bit R component in bytes 0..1, a 16-bit G component in bytes 2..3, - /// a 16-bit B component in bytes 4..5, and a 16-bit A component in bytes 6..7. - /// - VK_FORMAT_R16G16B16A16_UINT = 95, - - /// - /// Specifies a four-component, 64-bit signed integer format that has a 16-bit R component in bytes 0..1, a 16-bit G component in bytes 2..3, - /// a 16-bit B component in bytes 4..5, and a 16-bit A component in bytes 6..7. - /// - VK_FORMAT_R16G16B16A16_SINT = 96, - - /// - /// Specifies a four-component, 64-bit signed floating-point format that has a 16-bit R component in bytes 0..1, a 16-bit G component in bytes 2..3, - /// a 16-bit B component in bytes 4..5, and a 16-bit A component in bytes 6..7. - /// - VK_FORMAT_R16G16B16A16_SFLOAT = 97, - - /// - /// Specifies a one-component, 32-bit unsigned integer format that has a single 32-bit R component. - /// - VK_FORMAT_R32_UINT = 98, - - /// - /// Specifies a one-component, 32-bit signed integer format that has a single 32-bit R component. - /// - VK_FORMAT_R32_SINT = 99, - - /// - /// Specifies a one-component, 32-bit signed floating-point format that has a single 32-bit R component. - /// - VK_FORMAT_R32_SFLOAT = 100, - - /// - /// Specifies a two-component, 64-bit unsigned integer format that has a 32-bit R component in bytes 0..3, and a 32-bit G component in bytes 4..7. - /// - VK_FORMAT_R32G32_UINT = 101, - - /// - /// Specifies a two-component, 64-bit signed integer format that has a 32-bit R component in bytes 0..3, and a 32-bit G component in bytes 4..7. - /// - VK_FORMAT_R32G32_SINT = 102, - - /// - /// Specifies a two-component, 64-bit signed floating-point format that has a 32-bit R component in bytes 0..3, and a 32-bit G component in bytes 4..7. - /// - VK_FORMAT_R32G32_SFLOAT = 103, - - /// - /// Specifies a three-component, 96-bit unsigned integer format that has a 32-bit R component in bytes 0..3, a 32-bit G component in bytes 4..7, and a 32-bit B component in bytes 8..11. - /// - VK_FORMAT_R32G32B32_UINT = 104, - - /// - /// Specifies a three-component, 96-bit signed integer format that has a 32-bit R component in bytes 0..3, a 32-bit G component in bytes 4..7, and a 32-bit B component in bytes 8..11. - /// - VK_FORMAT_R32G32B32_SINT = 105, - - /// - /// Specifies a three-component, 96-bit signed floating-point format that has a 32-bit R component in bytes 0..3, a 32-bit G component in bytes 4..7, and a 32-bit B component in bytes 8..11. - /// - VK_FORMAT_R32G32B32_SFLOAT = 106, - - /// - /// Specifies a four-component, 128-bit unsigned integer format that has a 32-bit R component in bytes 0..3, a 32-bit G component in bytes 4..7, a 32-bit B component in bytes 8..11, and a 32-bit A component in bytes 12..15. - /// - VK_FORMAT_R32G32B32A32_UINT = 107, - - /// - /// Specifies a four-component, 128-bit signed integer format that has a 32-bit R component in bytes 0..3, a 32-bit G component in bytes 4..7, a 32-bit B component in bytes 8..11, and a 32-bit A component in bytes 12..15. - /// - VK_FORMAT_R32G32B32A32_SINT = 108, - - /// - /// Specifies a four-component, 128-bit signed floating-point format that has a 32-bit R component in bytes 0..3, a 32-bit G component in bytes 4..7, a 32-bit B component in bytes 8..11, and a 32-bit A component in bytes 12..15. - /// - VK_FORMAT_R32G32B32A32_SFLOAT = 109, - - /// - /// Specifies a one-component, 64-bit unsigned integer format that has a single 64-bit R component. - /// - VK_FORMAT_R64_UINT = 110, - - /// - /// Specifies a one-component, 64-bit signed integer format that has a single 64-bit R component. - /// - VK_FORMAT_R64_SINT = 111, - - /// - /// Specifies a one-component, 64-bit signed floating-point format that has a single 64-bit R component. - /// - VK_FORMAT_R64_SFLOAT = 112, - - /// - /// Specifies a two-component, 128-bit unsigned integer format that has a 64-bit R component in bytes 0..7, and a 64-bit G component in bytes 8..15. - /// - VK_FORMAT_R64G64_UINT = 113, - - /// - /// Specifies a two-component, 128-bit signed integer format that has a 64-bit R component in bytes 0..7, and a 64-bit G component in bytes 8..15. - /// - VK_FORMAT_R64G64_SINT = 114, - - /// - /// Specifies a two-component, 128-bit signed floating-point format that has a 64-bit R component in bytes 0..7, and a 64-bit G component in bytes 8..15. - /// - VK_FORMAT_R64G64_SFLOAT = 115, - - /// - /// Specifies a three-component, 192-bit unsigned integer format that has a 64-bit R component in bytes 0..7, a 64-bit G component in bytes 8..15, and a 64-bit B component in bytes 16..23. - /// - VK_FORMAT_R64G64B64_UINT = 116, - - /// - /// Specifies a three-component, 192-bit signed integer format that has a 64-bit R component in bytes 0..7, a 64-bit G component in bytes 8..15, and a 64-bit B component in bytes 16..23. - /// - VK_FORMAT_R64G64B64_SINT = 117, - - /// - /// specifies a three-component, 192-bit signed floating-point format that has a 64-bit R component in bytes 0..7, a 64-bit G component in bytes 8..15, and a 64-bit B component in bytes 16..23. - /// - VK_FORMAT_R64G64B64_SFLOAT = 118, - - /// - /// Specifies a four-component, 256-bit unsigned integer format that has a 64-bit R component in bytes 0..7, a 64-bit G component in bytes 8..15, a 64-bit B component in bytes 16..23, and a 64-bit A component in bytes 24..31. - /// - VK_FORMAT_R64G64B64A64_UINT = 119, - - /// - /// Specifies a four-component, 256-bit signed integer format that has a 64-bit R component in bytes 0..7, a 64-bit G component in bytes 8..15, a 64-bit B component in bytes 16..23, and a 64-bit A component in bytes 24..31. - /// - VK_FORMAT_R64G64B64A64_SINT = 120, - - /// - /// Specifies a four-component, 256-bit signed floating-point format that has a 64-bit R component in bytes 0..7, a 64-bit G component in bytes 8..15, a 64-bit B component in bytes 16..23, and a 64-bit A component in bytes 24..31. - /// - VK_FORMAT_R64G64B64A64_SFLOAT = 121, - - /// - /// Specifies a three-component, 32-bit packed unsigned floating-point format that has a 10-bit B component in bits 22..31, an 11-bit G component in bits 11..21, an 11-bit R component in bits 0..10. - /// - VK_FORMAT_B10G11R11_UFLOAT_PACK32 = 122, - - /// - /// Specifies a three-component, 32-bit packed unsigned floating-point format that has a 5-bit shared exponent in bits 27..31, a 9-bit B component mantissa in bits 18..26, a 9-bit G component mantissa in bits 9..17, and a 9-bit R component mantissa in bits 0..8. - /// - VK_FORMAT_E5B9G9R9_UFLOAT_PACK32 = 123, - - /// - /// Specifies a one-component, 16-bit unsigned normalized format that has a single 16-bit depth component. - /// - VK_FORMAT_D16_UNORM = 124, - - /// - /// Specifies a two-component, 32-bit format that has 24 unsigned normalized bits in the depth component and, optionally:, 8 bits that are unused. - /// - VK_FORMAT_X8_D24_UNORM_PACK32 = 125, - - /// - /// Specifies a one-component, 32-bit signed floating-point format that has 32-bits in the depth component. - /// - VK_FORMAT_D32_SFLOAT = 126, - - /// - /// Specifies a one-component, 8-bit unsigned integer format that has 8-bits in the stencil component. - /// - VK_FORMAT_S8_UINT = 127, - - /// - /// Specifies a two-component, 24-bit format that has 16 unsigned normalized bits in the depth component and 8 unsigned integer bits in the stencil component. - /// - VK_FORMAT_D16_UNORM_S8_UINT = 128, - - /// - /// Specifies a two-component, 32-bit packed format that has 8 unsigned integer bits in the stencil component, and 24 unsigned normalized bits in the depth component. - /// - VK_FORMAT_D24_UNORM_S8_UINT = 129, - - /// - /// Specifies a two-component format that has 32 signed float bits in the depth component and 8 unsigned integer bits in the stencil component. There are optionally: 24-bits that are unused. - /// - VK_FORMAT_D32_SFLOAT_S8_UINT = 130, - - /// - /// Specifies a three-component, block-compressed format where each 64-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGB texel data. This format has no alpha and is considered opaque. - /// - VK_FORMAT_BC1_RGB_UNORM_BLOCK = 131, - - /// - /// Specifies a three-component, block-compressed format where each 64-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGB texel data with sRGB nonlinear encoding. This format has no alpha and is considered opaque. - /// - VK_FORMAT_BC1_RGB_SRGB_BLOCK = 132, - - /// - /// Specifies a four-component, block-compressed format where each 64-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGB texel data, and provides 1 bit of alpha. - /// - VK_FORMAT_BC1_RGBA_UNORM_BLOCK = 133, - - /// - /// Specifies a four-component, block-compressed format where each 64-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGB texel data with sRGB nonlinear encoding, and provides 1 bit of alpha. - /// - VK_FORMAT_BC1_RGBA_SRGB_BLOCK = 134, - - /// - /// Specifies a four-component, block-compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGBA texel data with the first 64 bits encoding alpha values followed by 64 bits encoding RGB values. - /// - VK_FORMAT_BC2_UNORM_BLOCK = 135, - - /// - /// Specifies a four-component, block-compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGBA texel data with the first 64 bits encoding alpha values followed by 64 bits encoding RGB values with sRGB nonlinear encoding. - /// - VK_FORMAT_BC2_SRGB_BLOCK = 136, - - /// - /// Specifies a four-component, block-compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGBA texel data with the first 64 bits encoding alpha values followed by 64 bits encoding RGB values. - /// - VK_FORMAT_BC3_UNORM_BLOCK = 137, - - /// - /// Specifies a four-component, block-compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGBA texel data with the first 64 bits encoding alpha values followed by 64 bits encoding RGB values with sRGB nonlinear encoding. - /// - VK_FORMAT_BC3_SRGB_BLOCK = 138, - - /// - /// Specifies a one-component, block-compressed format where each 64-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized red texel data. - /// - VK_FORMAT_BC4_UNORM_BLOCK = 139, - - /// - /// Specifies a one-component, block-compressed format where each 64-bit compressed texel block encodes a 4×4 rectangle of signed normalized red texel data. - /// - VK_FORMAT_BC4_SNORM_BLOCK = 140, - - /// - /// Specifies a two-component, block-compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RG texel data with the first 64 bits encoding red values followed by 64 bits encoding green values. - /// - VK_FORMAT_BC5_UNORM_BLOCK = 141, - - /// - /// Specifies a two-component, block-compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of signed normalized RG texel data with the first 64 bits encoding red values followed by 64 bits encoding green values. - /// - VK_FORMAT_BC5_SNORM_BLOCK = 142, - - /// - /// Specifies a three-component, block-compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of unsigned floating-point RGB texel data. - /// - VK_FORMAT_BC6H_UFLOAT_BLOCK = 143, - - /// - /// Specifies a three-component, block-compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of signed floating-point RGB texel data. - /// - VK_FORMAT_BC6H_SFLOAT_BLOCK = 144, - - /// - /// Specifies a four-component, block-compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGBA texel data - /// - VK_FORMAT_BC7_UNORM_BLOCK = 145, - - /// - /// Specifies a four-component, block-compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGBA texel data with sRGB nonlinear encoding applied to the RGB components. - /// - VK_FORMAT_BC7_SRGB_BLOCK = 146, - - /// - /// Specifies a three-component, ETC2 compressed format where each 64-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGB texel data. This format has no alpha and is considered opaque. - /// - VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK = 147, - - /// - /// Specifies a three-component, ETC2 compressed format where each 64-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGB texel data with sRGB nonlinear encoding. This format has no alpha and is considered opaque. - /// - VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK = 148, - - /// - /// Specifies a four-component, ETC2 compressed format where each 64-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGB texel data, and provides 1 bit of alpha. - /// - VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK = 149, - - /// - /// Specifies a four-component, ETC2 compressed format where each 64-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGB texel data with sRGB nonlinear encoding, and provides 1 bit of alpha. - /// - VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK = 150, - - /// - /// Specifies a four-component, ETC2 compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGBA texel data with the first 64 bits encoding alpha values followed by 64 bits encoding RGB values. - /// - VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK = 151, - - /// - /// Specifies a four-component, ETC2 compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGBA texel data with the first 64 bits encoding alpha values followed by 64 bits encoding RGB values with sRGB nonlinear encoding applied. - /// - VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK = 152, - - /// - /// Specifies a one-component, ETC2 compressed format where each 64-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized red texel data. - /// - VK_FORMAT_EAC_R11_UNORM_BLOCK = 153, - - /// - /// Specifies a one-component, ETC2 compressed format where each 64-bit compressed texel block encodes a 4×4 rectangle of signed normalized red texel data. - /// - VK_FORMAT_EAC_R11_SNORM_BLOCK = 154, - - /// - /// Specifies a two-component, ETC2 compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RG texel data with the first 64 bits encoding red values followed by 64 bits encoding green values. - /// - VK_FORMAT_EAC_R11G11_UNORM_BLOCK = 155, - - /// - /// Specifies a two-component, ETC2 compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of signed normalized RG texel data with the first 64 bits encoding red values followed by 64 bits encoding green values. - /// - VK_FORMAT_EAC_R11G11_SNORM_BLOCK = 156, - - /// - /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGBA texel data. - /// - VK_FORMAT_ASTC_4x4_UNORM_BLOCK = 157, - - /// - /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGBA texel data with sRGB nonlinear encoding applied to the RGB components. - /// - VK_FORMAT_ASTC_4x4_SRGB_BLOCK = 158, - - /// - /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 5×4 rectangle of unsigned normalized RGBA texel data. - /// - VK_FORMAT_ASTC_5x4_UNORM_BLOCK = 159, - - /// - /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 5×4 rectangle of unsigned normalized RGBA texel data with sRGB nonlinear encoding applied to the RGB components. - /// - VK_FORMAT_ASTC_5x4_SRGB_BLOCK = 160, - - /// - /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 5×5 rectangle of unsigned normalized RGBA texel data. - /// - VK_FORMAT_ASTC_5x5_UNORM_BLOCK = 161, - - /// - /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 5×5 rectangle of unsigned normalized RGBA texel data with sRGB nonlinear encoding applied to the RGB components. - /// - VK_FORMAT_ASTC_5x5_SRGB_BLOCK = 162, - - /// - /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 6×5 rectangle of unsigned normalized RGBA texel data. - /// - VK_FORMAT_ASTC_6x5_UNORM_BLOCK = 163, - - /// - /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 6×5 rectangle of unsigned normalized RGBA texel data with sRGB nonlinear encoding applied to the RGB components. - /// - VK_FORMAT_ASTC_6x5_SRGB_BLOCK = 164, - - /// - /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 6×6 rectangle of unsigned normalized RGBA texel data. - /// - VK_FORMAT_ASTC_6x6_UNORM_BLOCK = 165, - - /// - /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 6×6 rectangle of unsigned normalized RGBA texel data with sRGB nonlinear encoding applied to the RGB components. - /// - VK_FORMAT_ASTC_6x6_SRGB_BLOCK = 166, - - /// - /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes an 8×5 rectangle of unsigned normalized RGBA texel data. - /// - VK_FORMAT_ASTC_8x5_UNORM_BLOCK = 167, - - /// - /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes an 8×5 rectangle of unsigned normalized RGBA texel data with sRGB nonlinear encoding applied to the RGB components. - /// - VK_FORMAT_ASTC_8x5_SRGB_BLOCK = 168, - - /// - /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes an 8×6 rectangle of unsigned normalized RGBA texel data. - /// - VK_FORMAT_ASTC_8x6_UNORM_BLOCK = 169, - - /// - /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes an 8×6 rectangle of unsigned normalized RGBA texel data with sRGB nonlinear encoding applied to the RGB components. - /// - VK_FORMAT_ASTC_8x6_SRGB_BLOCK = 170, - - /// - /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes an 8×8 rectangle of unsigned normalized RGBA texel data. - /// - VK_FORMAT_ASTC_8x8_UNORM_BLOCK = 171, - - /// - /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes an 8×8 rectangle of unsigned normalized RGBA texel data with sRGB nonlinear encoding applied to the RGB components. - /// - VK_FORMAT_ASTC_8x8_SRGB_BLOCK = 172, - - /// - /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 10×5 rectangle of unsigned normalized RGBA texel data. - /// - VK_FORMAT_ASTC_10x5_UNORM_BLOCK = 173, - - /// - /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 10×5 rectangle of unsigned normalized RGBA texel data with sRGB nonlinear encoding applied to the RGB components. - /// - VK_FORMAT_ASTC_10x5_SRGB_BLOCK = 174, - - /// - /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 10×6 rectangle of unsigned normalized RGBA texel data. - /// - VK_FORMAT_ASTC_10x6_UNORM_BLOCK = 175, - - /// - /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 10×6 rectangle of unsigned normalized RGBA texel data with sRGB nonlinear encoding applied to the RGB components. - /// - VK_FORMAT_ASTC_10x6_SRGB_BLOCK = 176, - - /// - /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 10×8 rectangle of unsigned normalized RGBA texel data. - /// - VK_FORMAT_ASTC_10x8_UNORM_BLOCK = 177, - - /// - /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 10×8 rectangle of unsigned normalized RGBA texel data with sRGB nonlinear encoding applied to the RGB components. - /// - VK_FORMAT_ASTC_10x8_SRGB_BLOCK = 178, + /// The format is not specified. + /// + VK_FORMAT_UNDEFINED = 0, + + /// + /// Specifies a two-component, 8-bit packed unsigned normalized format that has a 4-bit R component in bits 4..7, and a 4-bit G component in bits 0..3. + /// + VK_FORMAT_R4G4_UNORM_PACK8 = 1, + + /// + /// specifies a four-component, 16-bit packed unsigned normalized format that has a 4-bit R component in bits 12..15, a 4-bit G component in bits 8..11, + /// a 4-bit B component in bits 4..7, and a 4-bit A component in bits 0..3. + /// + VK_FORMAT_R4G4B4A4_UNORM_PACK16 = 2, + + /// + /// Specifies a four-component, 16-bit packed unsigned normalized format that has a 4-bit B component in bits 12..15, a 4-bit G component in bits 8..11, + /// a 4-bit R component in bits 4..7, and a 4-bit A component in bits 0..3. + /// + VK_FORMAT_B4G4R4A4_UNORM_PACK16 = 3, + + /// + /// specifies a three-component, 16-bit packed unsigned normalized format that has a 5-bit R component in bits 11..15, a 6-bit G component in bits 5..10, + /// and a 5-bit B component in bits 0..4. + /// + VK_FORMAT_R5G6B5_UNORM_PACK16 = 4, + + /// + /// specifies a three-component, 16-bit packed unsigned normalized format that has a 5-bit B component in bits 11..15, a 6-bit G component in bits 5..10, + /// and a 5-bit R component in bits 0..4. + /// + VK_FORMAT_B5G6R5_UNORM_PACK16 = 5, + + /// + /// Specifies a four-component, 16-bit packed unsigned normalized format that has a 5-bit R component in bits 11..15, a 5-bit G component in bits 6..10, + /// a 5-bit B component in bits 1..5, and a 1-bit A component in bit 0. + /// + VK_FORMAT_R5G5B5A1_UNORM_PACK16 = 6, + + /// + /// specifies a four-component, 16-bit packed unsigned normalized format that has a 5-bit B component in bits 11..15, a 5-bit G component in bits 6..10, + /// a 5-bit R component in bits 1..5, and a 1-bit A component in bit 0. + /// + VK_FORMAT_B5G5R5A1_UNORM_PACK16 = 7, + + /// + /// Specifies a four-component, 16-bit packed unsigned normalized format that has a 1-bit A component in bit 15, a 5-bit R component in bits 10..14, + /// a 5-bit G component in bits 5..9, and a 5-bit B component in bits 0..4. + /// + VK_FORMAT_A1R5G5B5_UNORM_PACK16 = 8, + + /// + /// Specifies a one-component, 8-bit unsigned normalized format that has a single 8-bit R component. + /// + VK_FORMAT_R8_UNORM = 9, + + /// + /// Specifies a one-component, 8-bit signed normalized format that has a single 8-bit R component. + /// + VK_FORMAT_R8_SNORM = 10, + + /// + /// Specifies a one-component, 8-bit unsigned scaled integer format that has a single 8-bit R component. + /// + VK_FORMAT_R8_USCALED = 11, + + /// + /// Specifies a one-component, 8-bit signed scaled integer format that has a single 8-bit R component. + /// + VK_FORMAT_R8_SSCALED = 12, + + /// + /// Specifies a one-component, 8-bit unsigned integer format that has a single 8-bit R component. + /// + VK_FORMAT_R8_UINT = 13, + + /// + /// Specifies a one-component, 8-bit signed integer format that has a single 8-bit R component. + /// + VK_FORMAT_R8_SINT = 14, + + /// + /// Specifies a one-component, 8-bit unsigned normalized format that has a single 8-bit R component stored with sRGB nonlinear encoding. + /// + VK_FORMAT_R8_SRGB = 15, + + /// + /// Specifies a two-component, 16-bit unsigned normalized format that has an 8-bit R component in byte 0, and an 8-bit G component in byte 1. + /// + VK_FORMAT_R8G8_UNORM = 16, + + /// + /// Specifies a two-component, 16-bit signed normalized format that has an 8-bit R component in byte 0, and an 8-bit G component in byte 1. + /// + VK_FORMAT_R8G8_SNORM = 17, + + /// + /// Specifies a two-component, 16-bit unsigned scaled integer format that has an 8-bit R component in byte 0, and an 8-bit G component in byte 1. + /// + VK_FORMAT_R8G8_USCALED = 18, + + /// + /// Specifies a two-component, 16-bit signed scaled integer format that has an 8-bit R component in byte 0, and an 8-bit G component in byte 1. + /// + VK_FORMAT_R8G8_SSCALED = 19, + + /// + /// Specifies a two-component, 16-bit unsigned integer format that has an 8-bit R component in byte 0, and an 8-bit G component in byte 1. + /// + VK_FORMAT_R8G8_UINT = 20, + + /// + /// Specifies a two-component, 16-bit signed integer format that has an 8-bit R component in byte 0, and an 8-bit G component in byte 1. + /// + VK_FORMAT_R8G8_SINT = 21, + + /// + /// Specifies a two-component, 16-bit unsigned normalized format that has an 8-bit R component stored with sRGB nonlinear encoding in byte 0, + /// and an 8-bit G component stored with sRGB nonlinear encoding in byte 1. + /// + VK_FORMAT_R8G8_SRGB = 22, + + /// + /// Specifies a three-component, 24-bit unsigned normalized format that has an 8-bit R component in byte 0, an 8-bit G component in byte 1, and an 8-bit B component in byte 2. + /// + VK_FORMAT_R8G8B8_UNORM = 23, + + /// + /// Specifies a three-component, 24-bit signed normalized format that has an 8-bit R component in byte 0, an 8-bit G component in byte 1, and an 8-bit B component in byte 2. + /// + VK_FORMAT_R8G8B8_SNORM = 24, + + /// + /// Specifies a three-component, 24-bit unsigned scaled format that has an 8-bit R component in byte 0, an 8-bit G component in byte 1, and an 8-bit B component in byte 2. + /// + VK_FORMAT_R8G8B8_USCALED = 25, + + /// + /// Specifies a three-component, 24-bit signed scaled format that has an 8-bit R component in byte 0, an 8-bit G component in byte 1, and an 8-bit B component in byte 2. + /// + VK_FORMAT_R8G8B8_SSCALED = 26, + + /// + /// Specifies a three-component, 24-bit unsigned integer format that has an 8-bit R component in byte 0, an 8-bit G component in byte 1, and an 8-bit B component in byte 2. + /// + VK_FORMAT_R8G8B8_UINT = 27, + + /// + /// Specifies a three-component, 24-bit signed integer format that has an 8-bit R component in byte 0, an 8-bit G component in byte 1, and an 8-bit B component in byte 2. + /// + VK_FORMAT_R8G8B8_SINT = 28, + + /// + /// Specifies a three-component, 24-bit unsigned normalized format that has an 8-bit R component stored with sRGB nonlinear encoding in byte 0, + /// an 8-bit G component stored with sRGB nonlinear encoding in byte 1, and an 8-bit B component stored with sRGB nonlinear encoding in byte 2. + /// + VK_FORMAT_R8G8B8_SRGB = 29, + + /// + /// Specifies a three-component, 24-bit unsigned normalized format that has an 8-bit B component in byte 0, an 8-bit G component in byte 1, and an 8-bit R component in byte 2. + /// + VK_FORMAT_B8G8R8_UNORM = 30, + + /// + /// Specifies a three-component, 24-bit signed normalized format that has an 8-bit B component in byte 0, an 8-bit G component in byte 1, and an 8-bit R component in byte 2. + /// + VK_FORMAT_B8G8R8_SNORM = 31, + + /// + /// Specifies a three-component, 24-bit unsigned scaled format that has an 8-bit B component in byte 0, an 8-bit G component in byte 1, and an 8-bit R component in byte 2. + /// + VK_FORMAT_B8G8R8_USCALED = 32, + + /// + /// Specifies a three-component, 24-bit signed scaled format that has an 8-bit B component in byte 0, an 8-bit G component in byte 1, and an 8-bit R component in byte 2. + /// + VK_FORMAT_B8G8R8_SSCALED = 33, + + /// + /// Specifies a three-component, 24-bit unsigned integer format that has an 8-bit B component in byte 0, an 8-bit G component in byte 1, and an 8-bit R component in byte 2. + /// + VK_FORMAT_B8G8R8_UINT = 34, + + /// + /// Specifies a three-component, 24-bit signed integer format that has an 8-bit B component in byte 0, an 8-bit G component in byte 1, and an 8-bit R component in byte 2. + /// + VK_FORMAT_B8G8R8_SINT = 35, + + /// + /// Specifies a three-component, 24-bit unsigned normalized format that has an 8-bit B component stored with sRGB nonlinear encoding in byte 0, + /// an 8-bit G component stored with sRGB nonlinear encoding in byte 1, and an 8-bit R component stored with sRGB nonlinear encoding in byte 2. + /// + VK_FORMAT_B8G8R8_SRGB = 36, + + /// + /// Specifies a four-component, 32-bit unsigned normalized format that has an 8-bit R component in byte 0, an 8-bit G component in byte 1, + /// an 8-bit B component in byte 2, and an 8-bit A component in byte 3. + /// + VK_FORMAT_R8G8B8A8_UNORM = 37, + + /// + /// Specifies a four-component, 32-bit signed normalized format that has an 8-bit R component in byte 0, an 8-bit G component in byte 1, + /// an 8-bit B component in byte 2, and an 8-bit A component in byte 3. + /// + VK_FORMAT_R8G8B8A8_SNORM = 38, + + /// + /// Specifies a four-component, 32-bit unsigned scaled format that has an 8-bit R component in byte 0, an 8-bit G component in byte 1, + /// an 8-bit B component in byte 2, and an 8-bit A component in byte 3. + /// + VK_FORMAT_R8G8B8A8_USCALED = 39, + + /// + /// Specifies a four-component, 32-bit signed scaled format that has an 8-bit R component in byte 0, an 8-bit G component in byte 1, + /// an 8-bit B component in byte 2, and an 8-bit A component in byte 3. + /// + VK_FORMAT_R8G8B8A8_SSCALED = 40, + + /// + /// Specifies a four-component, 32-bit unsigned integer format that has an 8-bit R component in byte 0, an 8-bit G component in byte 1, + /// an 8-bit B component in byte 2, and an 8-bit A component in byte 3. + /// + VK_FORMAT_R8G8B8A8_UINT = 41, + + /// + /// Specifies a four-component, 32-bit signed integer format that has an 8-bit R component in byte 0, an 8-bit G component in byte 1, + /// an 8-bit B component in byte 2, and an 8-bit A component in byte 3. + /// + VK_FORMAT_R8G8B8A8_SINT = 42, + + /// + /// Specifies a four-component, 32-bit unsigned normalized format that has an 8-bit R component stored with sRGB nonlinear encoding in byte 0, + /// an 8-bit G component stored with sRGB nonlinear encoding in byte 1, an 8-bit B component stored with sRGB nonlinear encoding in byte 2, and an 8-bit A component in byte 3. + /// + VK_FORMAT_R8G8B8A8_SRGB = 43, + + /// + /// Specifies a four-component, 32-bit unsigned normalized format that has an 8-bit B component in byte 0, an 8-bit G component in byte 1, an 8-bit R component in byte 2, and an 8-bit A component in byte 3. + /// + VK_FORMAT_B8G8R8A8_UNORM = 44, + + /// + /// Specifies a four-component, 32-bit signed normalized format that has an 8-bit B component in byte 0, an 8-bit G component in byte 1, an 8-bit R component in byte 2, and an 8-bit A component in byte 3. + /// + VK_FORMAT_B8G8R8A8_SNORM = 45, + + /// + /// Specifies a four-component, 32-bit unsigned scaled format that has an 8-bit B component in byte 0, an 8-bit G component in byte 1, an 8-bit R component in byte 2, and an 8-bit A component in byte 3. + /// + VK_FORMAT_B8G8R8A8_USCALED = 46, + + /// + /// Specifies a four-component, 32-bit signed scaled format that has an 8-bit B component in byte 0, an 8-bit G component in byte 1, an 8-bit R component in byte 2, and an 8-bit A component in byte 3. + /// + VK_FORMAT_B8G8R8A8_SSCALED = 47, + + /// + /// Specifies a four-component, 32-bit unsigned integer format that has an 8-bit B component in byte 0, an 8-bit G component in byte 1, an 8-bit R component in byte 2, and an 8-bit A component in byte 3. + /// + VK_FORMAT_B8G8R8A8_UINT = 48, + + /// + /// Specifies a four-component, 32-bit signed integer format that has an 8-bit B component in byte 0, an 8-bit G component in byte 1, an 8-bit R component in byte 2, and an 8-bit A component in byte 3. + /// + VK_FORMAT_B8G8R8A8_SINT = 49, + + /// + /// Specifies a four-component, 32-bit unsigned normalized format that has an 8-bit B component stored with sRGB nonlinear encoding in byte 0, + /// an 8-bit G component stored with sRGB nonlinear encoding in byte 1, an 8-bit R component stored with sRGB nonlinear encoding in byte 2, and an 8-bit A component in byte 3. + /// + VK_FORMAT_B8G8R8A8_SRGB = 50, + + /// + /// Specifies a four-component, 32-bit packed unsigned normalized format that has an 8-bit A component in bits 24..31, an 8-bit B component in bits 16..23, + /// an 8-bit G component in bits 8..15, and an 8-bit R component in bits 0..7. + /// + VK_FORMAT_A8B8G8R8_UNORM_PACK32 = 51, + + /// + /// specifies a four-component, 32-bit packed signed normalized format that has an 8-bit A component in bits 24..31, an 8-bit B component in bits 16..23, + /// an 8-bit G component in bits 8..15, and an 8-bit R component in bits 0..7. + /// + VK_FORMAT_A8B8G8R8_SNORM_PACK32 = 52, + + /// + /// Specifies a four-component, 32-bit packed unsigned scaled integer format that has an 8-bit A component in bits 24..31, an 8-bit B component in bits 16..23, + /// an 8-bit G component in bits 8..15, and an 8-bit R component in bits 0..7. + /// + VK_FORMAT_A8B8G8R8_USCALED_PACK32 = 53, + + /// + /// Specifies a four-component, 32-bit packed signed scaled integer format that has an 8-bit A component in bits 24..31, an 8-bit B component in bits 16..23, + /// an 8-bit G component in bits 8..15, and an 8-bit R component in bits 0..7. + /// + VK_FORMAT_A8B8G8R8_SSCALED_PACK32 = 54, + + /// + /// Specifies a four-component, 32-bit packed unsigned integer format that has an 8-bit A component in bits 24..31, an 8-bit B component in bits 16..23, + /// an 8-bit G component in bits 8..15, and an 8-bit R component in bits 0..7. + /// + VK_FORMAT_A8B8G8R8_UINT_PACK32 = 55, + + /// + /// Specifies a four-component, 32-bit packed signed integer format that has an 8-bit A component in bits 24..31, an 8-bit B component in bits 16..23, + /// an 8-bit G component in bits 8..15, and an 8-bit R component in bits 0..7. + /// + VK_FORMAT_A8B8G8R8_SINT_PACK32 = 56, + + /// + /// Specifies a four-component, 32-bit packed unsigned normalized format that has an 8-bit A component in bits 24..31, + /// an 8-bit B component stored with sRGB nonlinear encoding in bits 16..23, an 8-bit G component stored with sRGB nonlinear encoding in bits 8..15, and an 8-bit R component stored with sRGB nonlinear encoding in bits 0..7. + /// + VK_FORMAT_A8B8G8R8_SRGB_PACK32 = 57, + + /// + /// Specifies a four-component, 32-bit packed unsigned normalized format that has a 2-bit A component in bits 30..31, a 10-bit R component in bits 20..29, + /// a 10-bit G component in bits 10..19, and a 10-bit B component in bits 0..9. + /// + VK_FORMAT_A2R10G10B10_UNORM_PACK32 = 58, + + /// + /// Specifies a four-component, 32-bit packed signed normalized format that has a 2-bit A component in bits 30..31, a 10-bit R component in bits 20..29, + /// a 10-bit G component in bits 10..19, and a 10-bit B component in bits 0..9. + /// + VK_FORMAT_A2R10G10B10_SNORM_PACK32 = 59, + + /// + /// Specifies a four-component, 32-bit packed unsigned scaled integer format that has a 2-bit A component in bits 30..31, a 10-bit R component in bits 20..29, + /// a 10-bit G component in bits 10..19, and a 10-bit B component in bits 0..9. + /// + VK_FORMAT_A2R10G10B10_USCALED_PACK32 = 60, + + /// + /// Specifies a four-component, 32-bit packed signed scaled integer format that has a 2-bit A component in bits 30..31, a 10-bit R component in bits 20..29, + /// a 10-bit G component in bits 10..19, and a 10-bit B component in bits 0..9. + /// + VK_FORMAT_A2R10G10B10_SSCALED_PACK32 = 61, + + /// + /// Specifies a four-component, 32-bit packed unsigned integer format that has a 2-bit A component in bits 30..31, a 10-bit R component in bits 20..29, + /// a 10-bit G component in bits 10..19, and a 10-bit B component in bits 0..9. + /// + VK_FORMAT_A2R10G10B10_UINT_PACK32 = 62, + + /// + /// Specifies a four-component, 32-bit packed signed integer format that has a 2-bit A component in bits 30..31, a 10-bit R component in bits 20..29, + /// a 10-bit G component in bits 10..19, and a 10-bit B component in bits 0..9. + /// + VK_FORMAT_A2R10G10B10_SINT_PACK32 = 63, + + /// + /// Specifies a four-component, 32-bit packed unsigned normalized format that has a 2-bit A component in bits 30..31, a 10-bit B component in bits 20..29, + /// a 10-bit G component in bits 10..19, and a 10-bit R component in bits 0..9. + /// + VK_FORMAT_A2B10G10R10_UNORM_PACK32 = 64, + + /// + /// Specifies a four-component, 32-bit packed signed normalized format that has a 2-bit A component in bits 30..31, a 10-bit B component in bits 20..29, + /// a 10-bit G component in bits 10..19, and a 10-bit R component in bits 0..9. + /// + VK_FORMAT_A2B10G10R10_SNORM_PACK32 = 65, + + /// + /// Specifies a four-component, 32-bit packed unsigned scaled integer format that has a 2-bit A component in bits 30..31, a 10-bit B component in bits 20..29, + /// a 10-bit G component in bits 10..19, and a 10-bit R component in bits 0..9. + /// + VK_FORMAT_A2B10G10R10_USCALED_PACK32 = 66, + + /// + /// Specifies a four-component, 32-bit packed signed scaled integer format that has a 2-bit A component in bits 30..31, a 10-bit B component in bits 20..29, + /// a 10-bit G component in bits 10..19, and a 10-bit R component in bits 0..9. + /// + VK_FORMAT_A2B10G10R10_SSCALED_PACK32 = 67, + + /// + /// Specifies a four-component, 32-bit packed unsigned integer format that has a 2-bit A component in bits 30..31, a 10-bit B component in bits 20..29, + /// a 10-bit G component in bits 10..19, and a 10-bit R component in bits 0..9. + /// + VK_FORMAT_A2B10G10R10_UINT_PACK32 = 68, + + /// + /// Specifies a four-component, 32-bit packed signed integer format that has a 2-bit A component in bits 30..31, a 10-bit B component in bits 20..29, + /// a 10-bit G component in bits 10..19, and a 10-bit R component in bits 0..9. + /// + VK_FORMAT_A2B10G10R10_SINT_PACK32 = 69, + + /// + /// Specifies a one-component, 16-bit unsigned normalized format that has a single 16-bit R component. + /// + VK_FORMAT_R16_UNORM = 70, + + /// + /// Specifies a one-component, 16-bit signed normalized format that has a single 16-bit R component. + /// + VK_FORMAT_R16_SNORM = 71, + + /// + /// Specifies a one-component, 16-bit unsigned scaled integer format that has a single 16-bit R component. + /// + VK_FORMAT_R16_USCALED = 72, + + /// + /// Specifies a one-component, 16-bit signed scaled integer format that has a single 16-bit R component. + /// + VK_FORMAT_R16_SSCALED = 73, + + /// + /// Specifies a one-component, 16-bit unsigned integer format that has a single 16-bit R component. + /// + VK_FORMAT_R16_UINT = 74, + + /// + /// Specifies a one-component, 16-bit signed integer format that has a single 16-bit R component. + /// + VK_FORMAT_R16_SINT = 75, + + /// + /// Specifies a one-component, 16-bit signed floating-point format that has a single 16-bit R component. + /// + VK_FORMAT_R16_SFLOAT = 76, + + /// + /// Specifies a two-component, 32-bit unsigned normalized format that has a 16-bit R component in bytes 0..1, and a 16-bit G component in bytes 2..3. + /// + VK_FORMAT_R16G16_UNORM = 77, + + /// + /// Specifies a two-component, 32-bit signed normalized format that has a 16-bit R component in bytes 0..1, and a 16-bit G component in bytes 2..3. + /// + VK_FORMAT_R16G16_SNORM = 78, + + /// + /// Specifies a two-component, 32-bit unsigned scaled integer format that has a 16-bit R component in bytes 0..1, and a 16-bit G component in bytes 2..3. + /// + VK_FORMAT_R16G16_USCALED = 79, + + /// + /// Specifies a two-component, 32-bit signed scaled integer format that has a 16-bit R component in bytes 0..1, and a 16-bit G component in bytes 2..3. + /// + VK_FORMAT_R16G16_SSCALED = 80, + + /// + /// Specifies a two-component, 32-bit unsigned integer format that has a 16-bit R component in bytes 0..1, and a 16-bit G component in bytes 2..3. + /// + VK_FORMAT_R16G16_UINT = 81, - /// - /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 10×10 rectangle of unsigned normalized RGBA texel data. - /// - VK_FORMAT_ASTC_10x10_UNORM_BLOCK = 179, + /// + /// Specifies a two-component, 32-bit signed integer format that has a 16-bit R component in bytes 0..1, and a 16-bit G component in bytes 2..3. + /// + VK_FORMAT_R16G16_SINT = 82, - /// - /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 10×10 rectangle of unsigned normalized RGBA texel data with sRGB nonlinear encoding applied to the RGB components. - /// - VK_FORMAT_ASTC_10x10_SRGB_BLOCK = 180, + /// + /// Specifies a two-component, 32-bit signed floating-point format that has a 16-bit R component in bytes 0..1, and a 16-bit G component in bytes 2..3. + /// + VK_FORMAT_R16G16_SFLOAT = 83, - /// - /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 12×10 rectangle of unsigned normalized RGBA texel data. - /// - VK_FORMAT_ASTC_12x10_UNORM_BLOCK = 181, + /// + /// Specifies a three-component, 48-bit unsigned normalized format that has a 16-bit R component in bytes 0..1, a 16-bit G component in bytes 2..3, + /// and a 16-bit B component in bytes 4..5. + /// + VK_FORMAT_R16G16B16_UNORM = 84, - /// - /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 12×10 rectangle of unsigned normalized RGBA texel data with sRGB nonlinear encoding applied to the RGB components. - /// - VK_FORMAT_ASTC_12x10_SRGB_BLOCK = 182, + /// + /// Specifies a three-component, 48-bit signed normalized format that has a 16-bit R component in bytes 0..1, a 16-bit G component in bytes 2..3, + /// and a 16-bit B component in bytes 4..5. + /// + VK_FORMAT_R16G16B16_SNORM = 85, - /// - /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 12×12 rectangle of unsigned normalized RGBA texel data. - /// - VK_FORMAT_ASTC_12x12_UNORM_BLOCK = 183, + /// + /// Specifies a three-component, 48-bit unsigned scaled integer format that has a 16-bit R component in bytes 0..1, a 16-bit G component in bytes 2..3, + /// and a 16-bit B component in bytes 4..5. + /// + VK_FORMAT_R16G16B16_USCALED = 86, - /// - /// specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 12×12 rectangle of unsigned normalized RGBA texel data with sRGB nonlinear encoding applied to the RGB components. - /// - VK_FORMAT_ASTC_12x12_SRGB_BLOCK = 184, + /// + /// Specifies a three-component, 48-bit signed scaled integer format that has a 16-bit R component in bytes 0..1, a 16-bit G component in bytes 2..3, + /// and a 16-bit B component in bytes 4..5. + /// + VK_FORMAT_R16G16B16_SSCALED = 87, + + /// + /// Specifies a three-component, 48-bit unsigned integer format that has a 16-bit R component in bytes 0..1, a 16-bit G component in bytes 2..3, + /// and a 16-bit B component in bytes 4..5. + /// + VK_FORMAT_R16G16B16_UINT = 88, + + /// + /// Specifies a three-component, 48-bit signed integer format that has a 16-bit R component in bytes 0..1, a 16-bit G component in bytes 2..3, + /// and a 16-bit B component in bytes 4..5. + /// + VK_FORMAT_R16G16B16_SINT = 89, + + /// + /// Specifies a three-component, 48-bit signed floating-point format that has a 16-bit R component in bytes 0..1, a 16-bit G component in bytes 2..3, + /// and a 16-bit B component in bytes 4..5. + /// + VK_FORMAT_R16G16B16_SFLOAT = 90, + + /// + /// Specifies a four-component, 64-bit unsigned normalized format that has a 16-bit R component in bytes 0..1, a 16-bit G component in bytes 2..3, + /// a 16-bit B component in bytes 4..5, and a 16-bit A component in bytes 6..7. + /// + VK_FORMAT_R16G16B16A16_UNORM = 91, + + /// + /// Specifies a four-component, 64-bit signed normalized format that has a 16-bit R component in bytes 0..1, a 16-bit G component in bytes 2..3, + /// a 16-bit B component in bytes 4..5, and a 16-bit A component in bytes 6..7. + /// + VK_FORMAT_R16G16B16A16_SNORM = 92, + + /// + /// Specifies a four-component, 64-bit unsigned scaled integer format that has a 16-bit R component in bytes 0..1, a 16-bit G component in bytes 2..3, + /// a 16-bit B component in bytes 4..5, and a 16-bit A component in bytes 6..7. + /// + VK_FORMAT_R16G16B16A16_USCALED = 93, + + /// + /// Specifies a four-component, 64-bit signed scaled integer format that has a 16-bit R component in bytes 0..1, a 16-bit G component in bytes 2..3, + /// a 16-bit B component in bytes 4..5, and a 16-bit A component in bytes 6..7. + /// + VK_FORMAT_R16G16B16A16_SSCALED = 94, + + /// + /// Specifies a four-component, 64-bit unsigned integer format that has a 16-bit R component in bytes 0..1, a 16-bit G component in bytes 2..3, + /// a 16-bit B component in bytes 4..5, and a 16-bit A component in bytes 6..7. + /// + VK_FORMAT_R16G16B16A16_UINT = 95, + + /// + /// Specifies a four-component, 64-bit signed integer format that has a 16-bit R component in bytes 0..1, a 16-bit G component in bytes 2..3, + /// a 16-bit B component in bytes 4..5, and a 16-bit A component in bytes 6..7. + /// + VK_FORMAT_R16G16B16A16_SINT = 96, + + /// + /// Specifies a four-component, 64-bit signed floating-point format that has a 16-bit R component in bytes 0..1, a 16-bit G component in bytes 2..3, + /// a 16-bit B component in bytes 4..5, and a 16-bit A component in bytes 6..7. + /// + VK_FORMAT_R16G16B16A16_SFLOAT = 97, + + /// + /// Specifies a one-component, 32-bit unsigned integer format that has a single 32-bit R component. + /// + VK_FORMAT_R32_UINT = 98, + + /// + /// Specifies a one-component, 32-bit signed integer format that has a single 32-bit R component. + /// + VK_FORMAT_R32_SINT = 99, + + /// + /// Specifies a one-component, 32-bit signed floating-point format that has a single 32-bit R component. + /// + VK_FORMAT_R32_SFLOAT = 100, + + /// + /// Specifies a two-component, 64-bit unsigned integer format that has a 32-bit R component in bytes 0..3, and a 32-bit G component in bytes 4..7. + /// + VK_FORMAT_R32G32_UINT = 101, + + /// + /// Specifies a two-component, 64-bit signed integer format that has a 32-bit R component in bytes 0..3, and a 32-bit G component in bytes 4..7. + /// + VK_FORMAT_R32G32_SINT = 102, + + /// + /// Specifies a two-component, 64-bit signed floating-point format that has a 32-bit R component in bytes 0..3, and a 32-bit G component in bytes 4..7. + /// + VK_FORMAT_R32G32_SFLOAT = 103, + + /// + /// Specifies a three-component, 96-bit unsigned integer format that has a 32-bit R component in bytes 0..3, a 32-bit G component in bytes 4..7, and a 32-bit B component in bytes 8..11. + /// + VK_FORMAT_R32G32B32_UINT = 104, + + /// + /// Specifies a three-component, 96-bit signed integer format that has a 32-bit R component in bytes 0..3, a 32-bit G component in bytes 4..7, and a 32-bit B component in bytes 8..11. + /// + VK_FORMAT_R32G32B32_SINT = 105, + + /// + /// Specifies a three-component, 96-bit signed floating-point format that has a 32-bit R component in bytes 0..3, a 32-bit G component in bytes 4..7, and a 32-bit B component in bytes 8..11. + /// + VK_FORMAT_R32G32B32_SFLOAT = 106, + + /// + /// Specifies a four-component, 128-bit unsigned integer format that has a 32-bit R component in bytes 0..3, a 32-bit G component in bytes 4..7, a 32-bit B component in bytes 8..11, and a 32-bit A component in bytes 12..15. + /// + VK_FORMAT_R32G32B32A32_UINT = 107, + + /// + /// Specifies a four-component, 128-bit signed integer format that has a 32-bit R component in bytes 0..3, a 32-bit G component in bytes 4..7, a 32-bit B component in bytes 8..11, and a 32-bit A component in bytes 12..15. + /// + VK_FORMAT_R32G32B32A32_SINT = 108, + + /// + /// Specifies a four-component, 128-bit signed floating-point format that has a 32-bit R component in bytes 0..3, a 32-bit G component in bytes 4..7, a 32-bit B component in bytes 8..11, and a 32-bit A component in bytes 12..15. + /// + VK_FORMAT_R32G32B32A32_SFLOAT = 109, + + /// + /// Specifies a one-component, 64-bit unsigned integer format that has a single 64-bit R component. + /// + VK_FORMAT_R64_UINT = 110, + + /// + /// Specifies a one-component, 64-bit signed integer format that has a single 64-bit R component. + /// + VK_FORMAT_R64_SINT = 111, + + /// + /// Specifies a one-component, 64-bit signed floating-point format that has a single 64-bit R component. + /// + VK_FORMAT_R64_SFLOAT = 112, + + /// + /// Specifies a two-component, 128-bit unsigned integer format that has a 64-bit R component in bytes 0..7, and a 64-bit G component in bytes 8..15. + /// + VK_FORMAT_R64G64_UINT = 113, + + /// + /// Specifies a two-component, 128-bit signed integer format that has a 64-bit R component in bytes 0..7, and a 64-bit G component in bytes 8..15. + /// + VK_FORMAT_R64G64_SINT = 114, + + /// + /// Specifies a two-component, 128-bit signed floating-point format that has a 64-bit R component in bytes 0..7, and a 64-bit G component in bytes 8..15. + /// + VK_FORMAT_R64G64_SFLOAT = 115, + + /// + /// Specifies a three-component, 192-bit unsigned integer format that has a 64-bit R component in bytes 0..7, a 64-bit G component in bytes 8..15, and a 64-bit B component in bytes 16..23. + /// + VK_FORMAT_R64G64B64_UINT = 116, + + /// + /// Specifies a three-component, 192-bit signed integer format that has a 64-bit R component in bytes 0..7, a 64-bit G component in bytes 8..15, and a 64-bit B component in bytes 16..23. + /// + VK_FORMAT_R64G64B64_SINT = 117, + + /// + /// specifies a three-component, 192-bit signed floating-point format that has a 64-bit R component in bytes 0..7, a 64-bit G component in bytes 8..15, and a 64-bit B component in bytes 16..23. + /// + VK_FORMAT_R64G64B64_SFLOAT = 118, + + /// + /// Specifies a four-component, 256-bit unsigned integer format that has a 64-bit R component in bytes 0..7, a 64-bit G component in bytes 8..15, a 64-bit B component in bytes 16..23, and a 64-bit A component in bytes 24..31. + /// + VK_FORMAT_R64G64B64A64_UINT = 119, + + /// + /// Specifies a four-component, 256-bit signed integer format that has a 64-bit R component in bytes 0..7, a 64-bit G component in bytes 8..15, a 64-bit B component in bytes 16..23, and a 64-bit A component in bytes 24..31. + /// + VK_FORMAT_R64G64B64A64_SINT = 120, + + /// + /// Specifies a four-component, 256-bit signed floating-point format that has a 64-bit R component in bytes 0..7, a 64-bit G component in bytes 8..15, a 64-bit B component in bytes 16..23, and a 64-bit A component in bytes 24..31. + /// + VK_FORMAT_R64G64B64A64_SFLOAT = 121, + + /// + /// Specifies a three-component, 32-bit packed unsigned floating-point format that has a 10-bit B component in bits 22..31, an 11-bit G component in bits 11..21, an 11-bit R component in bits 0..10. + /// + VK_FORMAT_B10G11R11_UFLOAT_PACK32 = 122, + + /// + /// Specifies a three-component, 32-bit packed unsigned floating-point format that has a 5-bit shared exponent in bits 27..31, a 9-bit B component mantissa in bits 18..26, a 9-bit G component mantissa in bits 9..17, and a 9-bit R component mantissa in bits 0..8. + /// + VK_FORMAT_E5B9G9R9_UFLOAT_PACK32 = 123, + + /// + /// Specifies a one-component, 16-bit unsigned normalized format that has a single 16-bit depth component. + /// + VK_FORMAT_D16_UNORM = 124, + + /// + /// Specifies a two-component, 32-bit format that has 24 unsigned normalized bits in the depth component and, optionally:, 8 bits that are unused. + /// + VK_FORMAT_X8_D24_UNORM_PACK32 = 125, + + /// + /// Specifies a one-component, 32-bit signed floating-point format that has 32-bits in the depth component. + /// + VK_FORMAT_D32_SFLOAT = 126, + + /// + /// Specifies a one-component, 8-bit unsigned integer format that has 8-bits in the stencil component. + /// + VK_FORMAT_S8_UINT = 127, + + /// + /// Specifies a two-component, 24-bit format that has 16 unsigned normalized bits in the depth component and 8 unsigned integer bits in the stencil component. + /// + VK_FORMAT_D16_UNORM_S8_UINT = 128, + + /// + /// Specifies a two-component, 32-bit packed format that has 8 unsigned integer bits in the stencil component, and 24 unsigned normalized bits in the depth component. + /// + VK_FORMAT_D24_UNORM_S8_UINT = 129, + + /// + /// Specifies a two-component format that has 32 signed float bits in the depth component and 8 unsigned integer bits in the stencil component. There are optionally: 24-bits that are unused. + /// + VK_FORMAT_D32_SFLOAT_S8_UINT = 130, + + /// + /// Specifies a three-component, block-compressed format where each 64-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGB texel data. This format has no alpha and is considered opaque. + /// + VK_FORMAT_BC1_RGB_UNORM_BLOCK = 131, + + /// + /// Specifies a three-component, block-compressed format where each 64-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGB texel data with sRGB nonlinear encoding. This format has no alpha and is considered opaque. + /// + VK_FORMAT_BC1_RGB_SRGB_BLOCK = 132, + + /// + /// Specifies a four-component, block-compressed format where each 64-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGB texel data, and provides 1 bit of alpha. + /// + VK_FORMAT_BC1_RGBA_UNORM_BLOCK = 133, + + /// + /// Specifies a four-component, block-compressed format where each 64-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGB texel data with sRGB nonlinear encoding, and provides 1 bit of alpha. + /// + VK_FORMAT_BC1_RGBA_SRGB_BLOCK = 134, + + /// + /// Specifies a four-component, block-compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGBA texel data with the first 64 bits encoding alpha values followed by 64 bits encoding RGB values. + /// + VK_FORMAT_BC2_UNORM_BLOCK = 135, + + /// + /// Specifies a four-component, block-compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGBA texel data with the first 64 bits encoding alpha values followed by 64 bits encoding RGB values with sRGB nonlinear encoding. + /// + VK_FORMAT_BC2_SRGB_BLOCK = 136, + + /// + /// Specifies a four-component, block-compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGBA texel data with the first 64 bits encoding alpha values followed by 64 bits encoding RGB values. + /// + VK_FORMAT_BC3_UNORM_BLOCK = 137, + + /// + /// Specifies a four-component, block-compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGBA texel data with the first 64 bits encoding alpha values followed by 64 bits encoding RGB values with sRGB nonlinear encoding. + /// + VK_FORMAT_BC3_SRGB_BLOCK = 138, + + /// + /// Specifies a one-component, block-compressed format where each 64-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized red texel data. + /// + VK_FORMAT_BC4_UNORM_BLOCK = 139, + + /// + /// Specifies a one-component, block-compressed format where each 64-bit compressed texel block encodes a 4×4 rectangle of signed normalized red texel data. + /// + VK_FORMAT_BC4_SNORM_BLOCK = 140, + + /// + /// Specifies a two-component, block-compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RG texel data with the first 64 bits encoding red values followed by 64 bits encoding green values. + /// + VK_FORMAT_BC5_UNORM_BLOCK = 141, + + /// + /// Specifies a two-component, block-compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of signed normalized RG texel data with the first 64 bits encoding red values followed by 64 bits encoding green values. + /// + VK_FORMAT_BC5_SNORM_BLOCK = 142, + + /// + /// Specifies a three-component, block-compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of unsigned floating-point RGB texel data. + /// + VK_FORMAT_BC6H_UFLOAT_BLOCK = 143, + + /// + /// Specifies a three-component, block-compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of signed floating-point RGB texel data. + /// + VK_FORMAT_BC6H_SFLOAT_BLOCK = 144, + + /// + /// Specifies a four-component, block-compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGBA texel data + /// + VK_FORMAT_BC7_UNORM_BLOCK = 145, + + /// + /// Specifies a four-component, block-compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGBA texel data with sRGB nonlinear encoding applied to the RGB components. + /// + VK_FORMAT_BC7_SRGB_BLOCK = 146, + + /// + /// Specifies a three-component, ETC2 compressed format where each 64-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGB texel data. This format has no alpha and is considered opaque. + /// + VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK = 147, + + /// + /// Specifies a three-component, ETC2 compressed format where each 64-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGB texel data with sRGB nonlinear encoding. This format has no alpha and is considered opaque. + /// + VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK = 148, + + /// + /// Specifies a four-component, ETC2 compressed format where each 64-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGB texel data, and provides 1 bit of alpha. + /// + VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK = 149, + + /// + /// Specifies a four-component, ETC2 compressed format where each 64-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGB texel data with sRGB nonlinear encoding, and provides 1 bit of alpha. + /// + VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK = 150, + + /// + /// Specifies a four-component, ETC2 compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGBA texel data with the first 64 bits encoding alpha values followed by 64 bits encoding RGB values. + /// + VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK = 151, + + /// + /// Specifies a four-component, ETC2 compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGBA texel data with the first 64 bits encoding alpha values followed by 64 bits encoding RGB values with sRGB nonlinear encoding applied. + /// + VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK = 152, + + /// + /// Specifies a one-component, ETC2 compressed format where each 64-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized red texel data. + /// + VK_FORMAT_EAC_R11_UNORM_BLOCK = 153, + + /// + /// Specifies a one-component, ETC2 compressed format where each 64-bit compressed texel block encodes a 4×4 rectangle of signed normalized red texel data. + /// + VK_FORMAT_EAC_R11_SNORM_BLOCK = 154, + + /// + /// Specifies a two-component, ETC2 compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RG texel data with the first 64 bits encoding red values followed by 64 bits encoding green values. + /// + VK_FORMAT_EAC_R11G11_UNORM_BLOCK = 155, + + /// + /// Specifies a two-component, ETC2 compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of signed normalized RG texel data with the first 64 bits encoding red values followed by 64 bits encoding green values. + /// + VK_FORMAT_EAC_R11G11_SNORM_BLOCK = 156, + + /// + /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGBA texel data. + /// + VK_FORMAT_ASTC_4x4_UNORM_BLOCK = 157, + + /// + /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGBA texel data with sRGB nonlinear encoding applied to the RGB components. + /// + VK_FORMAT_ASTC_4x4_SRGB_BLOCK = 158, + + /// + /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 5×4 rectangle of unsigned normalized RGBA texel data. + /// + VK_FORMAT_ASTC_5x4_UNORM_BLOCK = 159, + + /// + /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 5×4 rectangle of unsigned normalized RGBA texel data with sRGB nonlinear encoding applied to the RGB components. + /// + VK_FORMAT_ASTC_5x4_SRGB_BLOCK = 160, + + /// + /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 5×5 rectangle of unsigned normalized RGBA texel data. + /// + VK_FORMAT_ASTC_5x5_UNORM_BLOCK = 161, + + /// + /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 5×5 rectangle of unsigned normalized RGBA texel data with sRGB nonlinear encoding applied to the RGB components. + /// + VK_FORMAT_ASTC_5x5_SRGB_BLOCK = 162, + + /// + /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 6×5 rectangle of unsigned normalized RGBA texel data. + /// + VK_FORMAT_ASTC_6x5_UNORM_BLOCK = 163, + + /// + /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 6×5 rectangle of unsigned normalized RGBA texel data with sRGB nonlinear encoding applied to the RGB components. + /// + VK_FORMAT_ASTC_6x5_SRGB_BLOCK = 164, + + /// + /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 6×6 rectangle of unsigned normalized RGBA texel data. + /// + VK_FORMAT_ASTC_6x6_UNORM_BLOCK = 165, + + /// + /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 6×6 rectangle of unsigned normalized RGBA texel data with sRGB nonlinear encoding applied to the RGB components. + /// + VK_FORMAT_ASTC_6x6_SRGB_BLOCK = 166, + + /// + /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes an 8×5 rectangle of unsigned normalized RGBA texel data. + /// + VK_FORMAT_ASTC_8x5_UNORM_BLOCK = 167, + + /// + /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes an 8×5 rectangle of unsigned normalized RGBA texel data with sRGB nonlinear encoding applied to the RGB components. + /// + VK_FORMAT_ASTC_8x5_SRGB_BLOCK = 168, + + /// + /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes an 8×6 rectangle of unsigned normalized RGBA texel data. + /// + VK_FORMAT_ASTC_8x6_UNORM_BLOCK = 169, + + /// + /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes an 8×6 rectangle of unsigned normalized RGBA texel data with sRGB nonlinear encoding applied to the RGB components. + /// + VK_FORMAT_ASTC_8x6_SRGB_BLOCK = 170, + + /// + /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes an 8×8 rectangle of unsigned normalized RGBA texel data. + /// + VK_FORMAT_ASTC_8x8_UNORM_BLOCK = 171, + + /// + /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes an 8×8 rectangle of unsigned normalized RGBA texel data with sRGB nonlinear encoding applied to the RGB components. + /// + VK_FORMAT_ASTC_8x8_SRGB_BLOCK = 172, + + /// + /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 10×5 rectangle of unsigned normalized RGBA texel data. + /// + VK_FORMAT_ASTC_10x5_UNORM_BLOCK = 173, + + /// + /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 10×5 rectangle of unsigned normalized RGBA texel data with sRGB nonlinear encoding applied to the RGB components. + /// + VK_FORMAT_ASTC_10x5_SRGB_BLOCK = 174, + + /// + /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 10×6 rectangle of unsigned normalized RGBA texel data. + /// + VK_FORMAT_ASTC_10x6_UNORM_BLOCK = 175, + + /// + /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 10×6 rectangle of unsigned normalized RGBA texel data with sRGB nonlinear encoding applied to the RGB components. + /// + VK_FORMAT_ASTC_10x6_SRGB_BLOCK = 176, + + /// + /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 10×8 rectangle of unsigned normalized RGBA texel data. + /// + VK_FORMAT_ASTC_10x8_UNORM_BLOCK = 177, + + /// + /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 10×8 rectangle of unsigned normalized RGBA texel data with sRGB nonlinear encoding applied to the RGB components. + /// + VK_FORMAT_ASTC_10x8_SRGB_BLOCK = 178, + + /// + /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 10×10 rectangle of unsigned normalized RGBA texel data. + /// + VK_FORMAT_ASTC_10x10_UNORM_BLOCK = 179, + + /// + /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 10×10 rectangle of unsigned normalized RGBA texel data with sRGB nonlinear encoding applied to the RGB components. + /// + VK_FORMAT_ASTC_10x10_SRGB_BLOCK = 180, + + /// + /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 12×10 rectangle of unsigned normalized RGBA texel data. + /// + VK_FORMAT_ASTC_12x10_UNORM_BLOCK = 181, + + /// + /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 12×10 rectangle of unsigned normalized RGBA texel data with sRGB nonlinear encoding applied to the RGB components. + /// + VK_FORMAT_ASTC_12x10_SRGB_BLOCK = 182, + + /// + /// Specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 12×12 rectangle of unsigned normalized RGBA texel data. + /// + VK_FORMAT_ASTC_12x12_UNORM_BLOCK = 183, + + /// + /// specifies a four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 12×12 rectangle of unsigned normalized RGBA texel data with sRGB nonlinear encoding applied to the RGB components. + /// + VK_FORMAT_ASTC_12x12_SRGB_BLOCK = 184, - // Provided by VK_VERSION_1_1 - VK_FORMAT_G8B8G8R8_422_UNORM = 1000156000, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G8B8G8R8_422_UNORM = 1000156000, - // Provided by VK_VERSION_1_1 - VK_FORMAT_B8G8R8G8_422_UNORM = 1000156001, + // Provided by VK_VERSION_1_1 + VK_FORMAT_B8G8R8G8_422_UNORM = 1000156001, - // Provided by VK_VERSION_1_1 - VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM = 1000156002, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM = 1000156002, - // Provided by VK_VERSION_1_1 - VK_FORMAT_G8_B8R8_2PLANE_420_UNORM = 1000156003, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G8_B8R8_2PLANE_420_UNORM = 1000156003, - // Provided by VK_VERSION_1_1 - VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM = 1000156004, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM = 1000156004, - // Provided by VK_VERSION_1_1 - VK_FORMAT_G8_B8R8_2PLANE_422_UNORM = 1000156005, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G8_B8R8_2PLANE_422_UNORM = 1000156005, - // Provided by VK_VERSION_1_1 - VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM = 1000156006, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM = 1000156006, - // Provided by VK_VERSION_1_1 - VK_FORMAT_R10X6_UNORM_PACK16 = 1000156007, + // Provided by VK_VERSION_1_1 + VK_FORMAT_R10X6_UNORM_PACK16 = 1000156007, - // Provided by VK_VERSION_1_1 - VK_FORMAT_R10X6G10X6_UNORM_2PACK16 = 1000156008, + // Provided by VK_VERSION_1_1 + VK_FORMAT_R10X6G10X6_UNORM_2PACK16 = 1000156008, - // Provided by VK_VERSION_1_1 - VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16 = 1000156009, + // Provided by VK_VERSION_1_1 + VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16 = 1000156009, - // Provided by VK_VERSION_1_1 - VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16 = 1000156010, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16 = 1000156010, - // Provided by VK_VERSION_1_1 - VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16 = 1000156011, + // Provided by VK_VERSION_1_1 + VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16 = 1000156011, - // Provided by VK_VERSION_1_1 - VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16 = 1000156012, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16 = 1000156012, - // Provided by VK_VERSION_1_1 - VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16 = 1000156013, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16 = 1000156013, - // Provided by VK_VERSION_1_1 - VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16 = 1000156014, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16 = 1000156014, - // Provided by VK_VERSION_1_1 - VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16 = 1000156015, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16 = 1000156015, - // Provided by VK_VERSION_1_1 - VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16 = 1000156016, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16 = 1000156016, - // Provided by VK_VERSION_1_1 - VK_FORMAT_R12X4_UNORM_PACK16 = 1000156017, + // Provided by VK_VERSION_1_1 + VK_FORMAT_R12X4_UNORM_PACK16 = 1000156017, - // Provided by VK_VERSION_1_1 - VK_FORMAT_R12X4G12X4_UNORM_2PACK16 = 1000156018, + // Provided by VK_VERSION_1_1 + VK_FORMAT_R12X4G12X4_UNORM_2PACK16 = 1000156018, - // Provided by VK_VERSION_1_1 - VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16 = 1000156019, + // Provided by VK_VERSION_1_1 + VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16 = 1000156019, - // Provided by VK_VERSION_1_1 - VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16 = 1000156020, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16 = 1000156020, - // Provided by VK_VERSION_1_1 - VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16 = 1000156021, + // Provided by VK_VERSION_1_1 + VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16 = 1000156021, - // Provided by VK_VERSION_1_1 - VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16 = 1000156022, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16 = 1000156022, - // Provided by VK_VERSION_1_1 - VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16 = 1000156023, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16 = 1000156023, - // Provided by VK_VERSION_1_1 - VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16 = 1000156024, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16 = 1000156024, - // Provided by VK_VERSION_1_1 - VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16 = 1000156025, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16 = 1000156025, - // Provided by VK_VERSION_1_1 - VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16 = 1000156026, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16 = 1000156026, - // Provided by VK_VERSION_1_1 - VK_FORMAT_G16B16G16R16_422_UNORM = 1000156027, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G16B16G16R16_422_UNORM = 1000156027, - // Provided by VK_VERSION_1_1 - VK_FORMAT_B16G16R16G16_422_UNORM = 1000156028, + // Provided by VK_VERSION_1_1 + VK_FORMAT_B16G16R16G16_422_UNORM = 1000156028, - // Provided by VK_VERSION_1_1 - VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM = 1000156029, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM = 1000156029, - // Provided by VK_VERSION_1_1 - VK_FORMAT_G16_B16R16_2PLANE_420_UNORM = 1000156030, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G16_B16R16_2PLANE_420_UNORM = 1000156030, - // Provided by VK_VERSION_1_1 - VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM = 1000156031, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM = 1000156031, - // Provided by VK_VERSION_1_1 - VK_FORMAT_G16_B16R16_2PLANE_422_UNORM = 1000156032, + // Provided by VK_VERSION_1_1 + VK_FORMAT_G16_B16R16_2PLANE_422_UNORM = 1000156032, - // Provided by VK_VERSION_1_1 - VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM = 1000156033, - } + // Provided by VK_VERSION_1_1 + VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM = 1000156033, } diff --git a/src/ImageSharp.Textures/Formats/Ktx2/IKtx2DecoderOptions.cs b/src/ImageSharp.Textures/Formats/Ktx2/IKtx2DecoderOptions.cs index b534ff26..60e5d27e 100644 --- a/src/ImageSharp.Textures/Formats/Ktx2/IKtx2DecoderOptions.cs +++ b/src/ImageSharp.Textures/Formats/Ktx2/IKtx2DecoderOptions.cs @@ -1,12 +1,11 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.Formats.Ktx2 +namespace SixLabors.ImageSharp.Textures.Formats.Ktx2; + +/// +/// The options for decoding ktx version 2 textures. Currently empty, but this may change in the future. +/// +internal interface IKtx2DecoderOptions { - /// - /// The options for decoding ktx version 2 textures. Currently empty, but this may change in the future. - /// - internal interface IKtx2DecoderOptions - { - } } diff --git a/src/ImageSharp.Textures/Formats/Ktx2/Ktx2ConfigurationModule.cs b/src/ImageSharp.Textures/Formats/Ktx2/Ktx2ConfigurationModule.cs index 93e05c59..135b6e04 100644 --- a/src/ImageSharp.Textures/Formats/Ktx2/Ktx2ConfigurationModule.cs +++ b/src/ImageSharp.Textures/Formats/Ktx2/Ktx2ConfigurationModule.cs @@ -1,18 +1,17 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.Formats.Ktx2 +namespace SixLabors.ImageSharp.Textures.Formats.Ktx2; + +/// +/// Registers the image encoders, decoders and mime type detectors for the ktx format. +/// +public class Ktx2ConfigurationModule : IConfigurationModule { - /// - /// Registers the image encoders, decoders and mime type detectors for the ktx format. - /// - public class Ktx2ConfigurationModule : IConfigurationModule + /// + public void Configure(Configuration configuration) { - /// - public void Configure(Configuration configuration) - { - configuration.ImageFormatsManager.SetDecoder(Ktx2Format.Instance, new Ktx2Decoder()); - configuration.ImageFormatsManager.AddImageFormatDetector(new Ktx2ImageFormatDetector()); - } + configuration.ImageFormatsManager.SetDecoder(Ktx2Format.Instance, new Ktx2Decoder()); + configuration.ImageFormatsManager.AddImageFormatDetector(new Ktx2ImageFormatDetector()); } } diff --git a/src/ImageSharp.Textures/Formats/Ktx2/Ktx2Constants.cs b/src/ImageSharp.Textures/Formats/Ktx2/Ktx2Constants.cs index 0305799b..a0df683a 100644 --- a/src/ImageSharp.Textures/Formats/Ktx2/Ktx2Constants.cs +++ b/src/ImageSharp.Textures/Formats/Ktx2/Ktx2Constants.cs @@ -1,48 +1,44 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; -using System.Collections.Generic; +namespace SixLabors.ImageSharp.Textures.Formats.Ktx2; -namespace SixLabors.ImageSharp.Textures.Formats.Ktx2 +/// +/// Constants for ktx version 2 textures. +/// +internal static class Ktx2Constants { /// - /// Constants for ktx version 2 textures. + /// The size of a KTX header in bytes. /// - internal static class Ktx2Constants - { - /// - /// The size of a KTX header in bytes. - /// - public const int KtxHeaderSize = 68; + public const int KtxHeaderSize = 68; - /// - /// The list of mimetypes that equate to a ktx2 file. - /// - public static readonly IEnumerable MimeTypes = new[] { "image/ktx2" }; + /// + /// The list of mimetypes that equate to a ktx2 file. + /// + public static readonly IEnumerable MimeTypes = new[] { "image/ktx2" }; - /// - /// The list of file extensions that equate to a ktx2 file. - /// - public static readonly IEnumerable FileExtensions = new[] { "ktx2" }; + /// + /// The list of file extensions that equate to a ktx2 file. + /// + public static readonly IEnumerable FileExtensions = new[] { "ktx2" }; - /// - /// Gets the magic bytes identifying a ktx2 texture. - /// - public static ReadOnlySpan MagicBytes => new byte[] - { - 0xAB, // « - 0x4B, // K - 0x54, // T - 0x58, // X - 0x20, // " " - 0x32, // 2 - 0x30, // 0 - 0xBB, // » - 0x0D, // \r - 0x0A, // \n - 0x1A, - 0x0A, // \n - }; - } + /// + /// Gets the magic bytes identifying a ktx2 texture. + /// + public static ReadOnlySpan MagicBytes => + [ + 0xAB, // « + 0x4B, // K + 0x54, // T + 0x58, // X + 0x20, // " " + 0x32, // 2 + 0x30, // 0 + 0xBB, // » + 0x0D, // \r + 0x0A, // \n + 0x1A, + 0x0A, // \n + ]; } diff --git a/src/ImageSharp.Textures/Formats/Ktx2/Ktx2Decoder.cs b/src/ImageSharp.Textures/Formats/Ktx2/Ktx2Decoder.cs index 0442a622..f0c9969d 100644 --- a/src/ImageSharp.Textures/Formats/Ktx2/Ktx2Decoder.cs +++ b/src/ImageSharp.Textures/Formats/Ktx2/Ktx2Decoder.cs @@ -1,29 +1,26 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.IO; +namespace SixLabors.ImageSharp.Textures.Formats.Ktx2; -namespace SixLabors.ImageSharp.Textures.Formats.Ktx2 +/// +/// Image decoder for KTX2 textures. +/// +public sealed class Ktx2Decoder : ITextureDecoder, IKtx2DecoderOptions, ITextureInfoDetector { - /// - /// Image decoder for KTX2 textures. - /// - public sealed class Ktx2Decoder : ITextureDecoder, IKtx2DecoderOptions, ITextureInfoDetector + /// + public Texture DecodeTexture(Configuration configuration, Stream stream) { - /// - public Texture DecodeTexture(Configuration configuration, Stream stream) - { - Guard.NotNull(stream, nameof(stream)); + Guard.NotNull(stream, nameof(stream)); - return new Ktx2DecoderCore(configuration, this).DecodeTexture(stream); - } + return new Ktx2DecoderCore(configuration, this).DecodeTexture(stream); + } - /// - public ITextureInfo Identify(Configuration configuration, Stream stream) - { - Guard.NotNull(stream, nameof(stream)); + /// + public ITextureInfo Identify(Configuration configuration, Stream stream) + { + Guard.NotNull(stream, nameof(stream)); - return new Ktx2DecoderCore(configuration, this).Identify(stream); - } + return new Ktx2DecoderCore(configuration, this).Identify(stream); } } diff --git a/src/ImageSharp.Textures/Formats/Ktx2/Ktx2DecoderCore.cs b/src/ImageSharp.Textures/Formats/Ktx2/Ktx2DecoderCore.cs index 6decbef3..74fbb48c 100644 --- a/src/ImageSharp.Textures/Formats/Ktx2/Ktx2DecoderCore.cs +++ b/src/ImageSharp.Textures/Formats/Ktx2/Ktx2DecoderCore.cs @@ -1,128 +1,150 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; -using System.IO; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Textures.Common.Exceptions; using SixLabors.ImageSharp.Textures.TextureFormats; -namespace SixLabors.ImageSharp.Textures.Formats.Ktx2 +namespace SixLabors.ImageSharp.Textures.Formats.Ktx2; + +/// +/// Performs the ktx decoding operation. +/// +internal sealed class Ktx2DecoderCore { /// - /// Performs the ktx decoding operation. + /// A scratch buffer to reduce allocations. + /// + private readonly byte[] buffer = new byte[24]; + + /// + /// The global configuration. + /// + private readonly Configuration configuration; + + /// + /// Used for allocating memory during processing operations. + /// + private readonly MemoryAllocator memoryAllocator; + + /// + /// The file header containing general information about the texture. + /// + private Ktx2Header ktxHeader; + + /// + /// The texture decoder options. + /// + private readonly IKtx2DecoderOptions options; + + /// + /// Initializes a new instance of the class. + /// + /// The configuration. + /// The options. + public Ktx2DecoderCore(Configuration configuration, IKtx2DecoderOptions options) + { + this.configuration = configuration; + this.memoryAllocator = configuration.MemoryAllocator; + this.options = options; + } + + /// + /// Decodes the texture from the specified stream. /// - internal sealed class Ktx2DecoderCore + /// The stream, where the texture should be decoded from. Cannot be null. + /// The decoded image. + public Texture DecodeTexture(Stream stream) { - /// - /// A scratch buffer to reduce allocations. - /// - private readonly byte[] buffer = new byte[24]; - - /// - /// The global configuration. - /// - private readonly Configuration configuration; - - /// - /// Used for allocating memory during processing operations. - /// - private readonly MemoryAllocator memoryAllocator; - - /// - /// The file header containing general information about the texture. - /// - private Ktx2Header ktxHeader; - - /// - /// The texture decoder options. - /// - private readonly IKtx2DecoderOptions options; - - /// - /// Initializes a new instance of the class. - /// - /// The configuration. - /// The options. - public Ktx2DecoderCore(Configuration configuration, IKtx2DecoderOptions options) + this.ReadFileHeader(stream); + + if (this.ktxHeader.PixelWidth == 0) { - this.configuration = configuration; - this.memoryAllocator = configuration.MemoryAllocator; - this.options = options; + throw new UnknownTextureFormatException("Width cannot be 0"); } - /// - /// Decodes the texture from the specified stream. - /// - /// The stream, where the texture should be decoded from. Cannot be null. - /// The decoded image. - public Texture DecodeTexture(Stream stream) + int width = (int)this.ktxHeader.PixelWidth; + int height = (int)this.ktxHeader.PixelHeight; + + // Level indices start immediately after the header + LevelIndex[] levelIndices = new LevelIndex[this.ktxHeader.LevelCount]; + for (int i = 0; i < levelIndices.Length; i++) { - this.ReadFileHeader(stream); + stream.Read(this.buffer, 0, 24); + LevelIndex levelIndex = MemoryMarshal.Cast(this.buffer)[0]; + levelIndices[i] = levelIndex; + } - if (this.ktxHeader.PixelWidth == 0) - { - throw new UnknownTextureFormatException("Width cannot be 0"); - } + if (this.ktxHeader.SupercompressionScheme != 0) + { + throw new NotSupportedException("SupercompressionSchemes are not yet supported"); + } - int width = (int)this.ktxHeader.PixelWidth; - int height = (int)this.ktxHeader.PixelHeight; + Ktx2Processor ktxProcessor = new Ktx2Processor(this.ktxHeader); - var levelIndices = new LevelIndex[this.ktxHeader.LevelCount]; - for (int i = 0; i < levelIndices.Length; i++) - { - stream.Read(this.buffer, 0, 24); - LevelIndex levelIndex = MemoryMarshal.Cast(this.buffer)[0]; - levelIndices[i] = levelIndex; - } + Texture texture; + if (this.ktxHeader.FaceCount == 6) + { + texture = ktxProcessor.DecodeCubeMap(stream, width, height, levelIndices); + } + else + { + FlatTexture flatTexture = new FlatTexture(); + MipMap[] mipMaps = ktxProcessor.DecodeMipMaps(stream, width, height, levelIndices); + flatTexture.MipMaps.AddRange(mipMaps); + texture = flatTexture; + } - if (this.ktxHeader.SupercompressionScheme != 0) + // Seek to the end of the file to ensure the entire stream is consumed. + // KTX2 files use byte offsets for mipmap data, so the stream position may not + // be at the end after reading. We need to find the furthest point read. + if (levelIndices.Length > 0) + { + long maxEndPosition = 0; + for (int i = 0; i < levelIndices.Length; i++) { - throw new NotSupportedException("SupercompressionSchemes are not yet supported"); + long endPosition = (long)(levelIndices[i].ByteOffset + levelIndices[i].UncompressedByteLength); + if (endPosition > maxEndPosition) + { + maxEndPosition = endPosition; + } } - var ktxProcessor = new Ktx2Processor(this.ktxHeader); - - if (this.ktxHeader.FaceCount == 6) + if (stream.Position < maxEndPosition && stream.CanSeek) { - CubemapTexture cubeMapTexture = ktxProcessor.DecodeCubeMap(stream, width, height, levelIndices); - return cubeMapTexture; + stream.Position = maxEndPosition; } - - var texture = new FlatTexture(); - MipMap[] mipMaps = ktxProcessor.DecodeMipMaps(stream, width, height, levelIndices); - texture.MipMaps.AddRange(mipMaps); - - return texture; } - /// - /// Reads the raw texture information from the specified stream. - /// - /// The containing texture data. - public ITextureInfo Identify(Stream currentStream) - { - this.ReadFileHeader(currentStream); + return texture; + } - var textureInfo = new TextureInfo(new TextureTypeInfo((int)this.ktxHeader.PixelDepth), (int)this.ktxHeader.PixelWidth, (int)this.ktxHeader.PixelHeight); + /// + /// Reads the raw texture information from the specified stream. + /// + /// The containing texture data. + public ITextureInfo Identify(Stream currentStream) + { + this.ReadFileHeader(currentStream); - return textureInfo; - } + TextureInfo textureInfo = new TextureInfo(new TextureTypeInfo((int)this.ktxHeader.PixelDepth), (int)this.ktxHeader.PixelWidth, (int)this.ktxHeader.PixelHeight); - /// - /// Reads the dds file header from the stream. - /// - /// The containing texture data. - private void ReadFileHeader(Stream stream) - { - // Discard the magic bytes, we already know at this point its a ktx2 file. - stream.Position += Ktx2Constants.MagicBytes.Length; + return textureInfo; + } + + /// + /// Reads the dds file header from the stream. + /// + /// The containing texture data. + private void ReadFileHeader(Stream stream) + { + // Discard the magic bytes, we already know at this point its a ktx2 file. + stream.Position += Ktx2Constants.MagicBytes.Length; - byte[] ktxHeaderBuffer = new byte[Ktx2Constants.KtxHeaderSize]; - stream.Read(ktxHeaderBuffer, 0, Ktx2Constants.KtxHeaderSize); + byte[] ktxHeaderBuffer = new byte[Ktx2Constants.KtxHeaderSize]; + stream.Read(ktxHeaderBuffer, 0, Ktx2Constants.KtxHeaderSize); - this.ktxHeader = Ktx2Header.Parse(ktxHeaderBuffer); - } + this.ktxHeader = Ktx2Header.Parse(ktxHeaderBuffer); } } diff --git a/src/ImageSharp.Textures/Formats/Ktx2/Ktx2Format.cs b/src/ImageSharp.Textures/Formats/Ktx2/Ktx2Format.cs index 84a6ddf8..9b9338d8 100644 --- a/src/ImageSharp.Textures/Formats/Ktx2/Ktx2Format.cs +++ b/src/ImageSharp.Textures/Formats/Ktx2/Ktx2Format.cs @@ -1,37 +1,34 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.Collections.Generic; +namespace SixLabors.ImageSharp.Textures.Formats.Ktx2; -namespace SixLabors.ImageSharp.Textures.Formats.Ktx2 +/// +/// Registers the texture decoders and mime type detectors for the ktx2 format. +/// +public sealed class Ktx2Format : ITextureFormat { /// - /// Registers the texture decoders and mime type detectors for the ktx2 format. + /// Prevents a default instance of the class from being created. /// - public sealed class Ktx2Format : ITextureFormat + private Ktx2Format() { - /// - /// Prevents a default instance of the class from being created. - /// - private Ktx2Format() - { - } + } - /// - /// Gets the current instance. - /// - public static Ktx2Format Instance { get; } = new Ktx2Format(); + /// + /// Gets the current instance. + /// + public static Ktx2Format Instance { get; } = new Ktx2Format(); - /// - public string Name => "KTX2"; + /// + public string Name => "KTX2"; - /// - public string DefaultMimeType => "image/ktx2"; + /// + public string DefaultMimeType => "image/ktx2"; - /// - public IEnumerable MimeTypes => Ktx2Constants.MimeTypes; + /// + public IEnumerable MimeTypes => Ktx2Constants.MimeTypes; - /// - public IEnumerable FileExtensions => Ktx2Constants.FileExtensions; - } + /// + public IEnumerable FileExtensions => Ktx2Constants.FileExtensions; } diff --git a/src/ImageSharp.Textures/Formats/Ktx2/Ktx2Header.cs b/src/ImageSharp.Textures/Formats/Ktx2/Ktx2Header.cs index 341757fa..60aa1161 100644 --- a/src/ImageSharp.Textures/Formats/Ktx2/Ktx2Header.cs +++ b/src/ImageSharp.Textures/Formats/Ktx2/Ktx2Header.cs @@ -1,171 +1,169 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Buffers.Binary; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Textures.Formats.Ktx2.Enums; -namespace SixLabors.ImageSharp.Textures.Formats.Ktx2 +namespace SixLabors.ImageSharp.Textures.Formats.Ktx2; + +/// +/// Describes a KTX2 file header. +/// +[StructLayout(LayoutKind.Sequential, Pack = 1)] +internal struct Ktx2Header { + public Ktx2Header( + VkFormat vkFormat, + uint typeSize, + uint pixelWidth, + uint pixelHeight, + uint pixelDepth, + uint layerCount, + uint faceCount, + uint levelCount, + uint supercompressionScheme, + uint dfdByteOffset, + uint dfdByteLength, + uint kvdByteOffset, + uint kvdByteLength, + ulong sgdByteOffset, + ulong sgdByteLength) + { + this.VkFormat = vkFormat; + this.TypeSize = typeSize; + this.PixelWidth = pixelWidth; + this.PixelHeight = pixelHeight; + this.PixelDepth = pixelDepth; + this.LayerCount = layerCount; + this.FaceCount = faceCount; + this.LevelCount = levelCount; + this.SupercompressionScheme = supercompressionScheme; + this.DfdByteOffset = dfdByteOffset; + this.DfdByteLength = dfdByteLength; + this.KvdByteOffset = kvdByteOffset; + this.KvdByteLength = kvdByteLength; + this.SgdByteOffset = sgdByteOffset; + this.SgdByteLength = sgdByteLength; + } + + /// + /// Gets the vkFormat. + /// vkFormat specifies the image format using Vulkan VkFormat enum values. It can be any value defined in core Vulkan 1.2. + /// + public VkFormat VkFormat { get; } + + /// + /// Gets the type size. + /// typeSize specifies the size of the data type in bytes used to upload the data to a graphics API. + /// When typeSize is greater than 1, software on big-endian systems must endian convert all image data since it is little-endian. + /// + public uint TypeSize { get; } + + /// + /// Gets the width of the texture image for level 0, in pixels. + /// pixelWidth cannot be 0. + /// + public uint PixelWidth { get; } + + /// + /// Gets the height of the texture image for level 0, in pixels. + /// For 1D textures, pixelHeight and pixelDepth must be 0. + /// + public uint PixelHeight { get; } + + /// + /// Gets the pixel depth. + /// For 1D textures pixelDepth must be 0. + /// For 2D and cubemap textures, pixelDepth must be 0. + /// pixelDepth must be 0 for depth or stencil formats. + /// + public uint PixelDepth { get; } + + /// + /// Gets the layer count. + /// layerCount specifies the number of array elements. If the texture is not an array texture, layerCount must equal 0. + /// + public uint LayerCount { get; } + + /// + /// Gets the face count. + /// faceCount specifies the number of cubemap faces. For cubemaps and cubemap arrays this must be 6. For non cubemaps this must be 1. + /// Cubemap faces are stored in the order: +X, -X, +Y, -Y, +Z, -Z. + /// + public uint FaceCount { get; } + /// - /// Describes a KTX2 file header. + /// Gets the level count. + /// levelCount specifies the number of levels in the Mip Level Array and, by extension, the number of indices in the Level Index array. + /// levelCount=0 is allowed, except for block-compressed formats, and means that a file contains only the base level and consumers, + /// particularly loaders, should generate other levels if needed. /// - [StructLayout(LayoutKind.Sequential, Pack = 1)] - internal struct Ktx2Header + public uint LevelCount { get; } + + /// + /// Gets the supercompression scheme. + /// supercompressionScheme indicates if a supercompression scheme has been applied to the data in levelImages. + /// It must be one of the values from Table 2, “Supercompression Schemes”. A value of 0 indicates no supercompression. + /// + public uint SupercompressionScheme { get; } + + /// + /// Gets the DFD byte offset. + /// The offset from the start of the file of the dfdTotalSize field of the Data Format Descriptor. + /// + public uint DfdByteOffset { get; } + + /// + /// Gets the total number of bytes in the Data Format Descriptor including the dfdTotalSize field. dfdByteLength must equal dfdTotalSize. + /// + public uint DfdByteLength { get; } + + /// + /// Gets the key value pair offsets. + /// An arbitrary number of key/value pairs may follow the Index. These can be used to encode any arbitrary data. + /// The kvdByteOffset field gives the offset of this data, i.e. that of first key/value pair, from the start of the file. The value must be 0 when kvdByteLength = 0. + /// + public uint KvdByteOffset { get; } + + /// + /// Gets the total number of bytes of key/value data including all keyAndValueByteLength fields, all keyAndValue fields and all valuePadding fields. + /// + public uint KvdByteLength { get; } + + /// + /// Gets the offset from the start of the file of supercompressionGlobalData. The value must be 0 when sgdByteLength = 0. + /// + public ulong SgdByteOffset { get; } + + /// + /// Gets the number of bytes of supercompressionGlobalData. + /// For supercompression schemes for which no reference is provided in the Global Data Format column of Table 2, “Supercompression Schemes”. the value must be 0. + /// + public ulong SgdByteLength { get; } + + public static Ktx2Header Parse(ReadOnlySpan data) { - public Ktx2Header( - VkFormat vkFormat, - uint typeSize, - uint pixelWidth, - uint pixelHeight, - uint pixelDepth, - uint layerCount, - uint faceCount, - uint levelCount, - uint supercompressionScheme, - uint dfdByteOffset, - uint dfdByteLength, - uint kvdByteOffset, - uint kvdByteLength, - ulong sgdByteOffset, - ulong sgdByteLength) + if (data.Length < Ktx2Constants.KtxHeaderSize) { - this.VkFormat = vkFormat; - this.TypeSize = typeSize; - this.PixelWidth = pixelWidth; - this.PixelHeight = pixelHeight; - this.PixelDepth = pixelDepth; - this.LayerCount = layerCount; - this.FaceCount = faceCount; - this.LevelCount = levelCount; - this.SupercompressionScheme = supercompressionScheme; - this.DfdByteOffset = dfdByteOffset; - this.DfdByteLength = dfdByteLength; - this.KvdByteOffset = kvdByteOffset; - this.KvdByteLength = kvdByteLength; - this.SgdByteOffset = sgdByteOffset; - this.SgdByteLength = sgdByteLength; + throw new ArgumentException($"Ktx2 header must be {Ktx2Constants.KtxHeaderSize} bytes. Was {data.Length} bytes.", nameof(data)); } - /// - /// Gets the vkFormat. - /// vkFormat specifies the image format using Vulkan VkFormat enum values. It can be any value defined in core Vulkan 1.2. - /// - public VkFormat VkFormat { get; } - - /// - /// Gets the type size. - /// typeSize specifies the size of the data type in bytes used to upload the data to a graphics API. - /// When typeSize is greater than 1, software on big-endian systems must endian convert all image data since it is little-endian. - /// - public uint TypeSize { get; } - - /// - /// Gets the width of the texture image for level 0, in pixels. - /// pixelWidth cannot be 0. - /// - public uint PixelWidth { get; } - - /// - /// Gets the height of the texture image for level 0, in pixels. - /// For 1D textures, pixelHeight and pixelDepth must be 0. - /// - public uint PixelHeight { get; } - - /// - /// Gets the pixel depth. - /// For 1D textures pixelDepth must be 0. - /// For 2D and cubemap textures, pixelDepth must be 0. - /// pixelDepth must be 0 for depth or stencil formats. - /// - public uint PixelDepth { get; } - - /// - /// Gets the layer count. - /// layerCount specifies the number of array elements. If the texture is not an array texture, layerCount must equal 0. - /// - public uint LayerCount { get; } - - /// - /// Gets the face count. - /// faceCount specifies the number of cubemap faces. For cubemaps and cubemap arrays this must be 6. For non cubemaps this must be 1. - /// Cubemap faces are stored in the order: +X, -X, +Y, -Y, +Z, -Z. - /// - public uint FaceCount { get; } - - /// - /// Gets the level count. - /// levelCount specifies the number of levels in the Mip Level Array and, by extension, the number of indices in the Level Index array. - /// levelCount=0 is allowed, except for block-compressed formats, and means that a file contains only the base level and consumers, - /// particularly loaders, should generate other levels if needed. - /// - public uint LevelCount { get; } - - /// - /// Gets the supercompression scheme. - /// supercompressionScheme indicates if a supercompression scheme has been applied to the data in levelImages. - /// It must be one of the values from Table 2, “Supercompression Schemes”. A value of 0 indicates no supercompression. - /// - public uint SupercompressionScheme { get; } - - /// - /// Gets the DFD byte offset. - /// The offset from the start of the file of the dfdTotalSize field of the Data Format Descriptor. - /// - public uint DfdByteOffset { get; } - - /// - /// Gets the total number of bytes in the Data Format Descriptor including the dfdTotalSize field. dfdByteLength must equal dfdTotalSize. - /// - public uint DfdByteLength { get; } - - /// - /// Gets the key value pair offsets. - /// An arbitrary number of key/value pairs may follow the Index. These can be used to encode any arbitrary data. - /// The kvdByteOffset field gives the offset of this data, i.e. that of first key/value pair, from the start of the file. The value must be 0 when kvdByteLength = 0. - /// - public uint KvdByteOffset { get; } - - /// - /// Gets the total number of bytes of key/value data including all keyAndValueByteLength fields, all keyAndValue fields and all valuePadding fields. - /// - public uint KvdByteLength { get; } - - /// - /// Gets the offset from the start of the file of supercompressionGlobalData. The value must be 0 when sgdByteLength = 0. - /// - public ulong SgdByteOffset { get; } - - /// - /// Gets the number of bytes of supercompressionGlobalData. - /// For supercompression schemes for which no reference is provided in the Global Data Format column of Table 2, “Supercompression Schemes”. the value must be 0. - /// - public ulong SgdByteLength { get; } - - public static Ktx2Header Parse(ReadOnlySpan data) - { - if (data.Length < Ktx2Constants.KtxHeaderSize) - { - throw new ArgumentException($"Ktx2 header must be {Ktx2Constants.KtxHeaderSize} bytes. Was {data.Length} bytes.", nameof(data)); - } - - return new Ktx2Header( - (VkFormat)BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(0, 4)), - BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(4, 4)), - BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(8, 4)), - BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(12, 4)), - BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(16, 4)), - BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(20, 4)), - BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(24, 4)), - BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(28, 4)), - BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(32, 4)), - BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(36, 4)), - BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(40, 4)), - BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(44, 4)), - BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(48, 4)), - BinaryPrimitives.ReadUInt64LittleEndian(data.Slice(52, 8)), - BinaryPrimitives.ReadUInt64LittleEndian(data.Slice(60, 8))); - } + return new Ktx2Header( + (VkFormat)BinaryPrimitives.ReadUInt32LittleEndian(data[..4]), + BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(4, 4)), + BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(8, 4)), + BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(12, 4)), + BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(16, 4)), + BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(20, 4)), + BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(24, 4)), + BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(28, 4)), + BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(32, 4)), + BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(36, 4)), + BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(40, 4)), + BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(44, 4)), + BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(48, 4)), + BinaryPrimitives.ReadUInt64LittleEndian(data.Slice(52, 8)), + BinaryPrimitives.ReadUInt64LittleEndian(data.Slice(60, 8))); } } diff --git a/src/ImageSharp.Textures/Formats/Ktx2/Ktx2ImageFormatDetector.cs b/src/ImageSharp.Textures/Formats/Ktx2/Ktx2ImageFormatDetector.cs index c1ab0878..33ebfdf8 100644 --- a/src/ImageSharp.Textures/Formats/Ktx2/Ktx2ImageFormatDetector.cs +++ b/src/ImageSharp.Textures/Formats/Ktx2/Ktx2ImageFormatDetector.cs @@ -1,30 +1,27 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; +namespace SixLabors.ImageSharp.Textures.Formats.Ktx2; -namespace SixLabors.ImageSharp.Textures.Formats.Ktx2 +/// +/// Detects ktx version 2 texture file headers. +/// +public sealed class Ktx2ImageFormatDetector : ITextureFormatDetector { - /// - /// Detects ktx version 2 texture file headers. - /// - public sealed class Ktx2ImageFormatDetector : ITextureFormatDetector - { - /// - public int HeaderSize => 12; + /// + public int HeaderSize => 12; - /// - public ITextureFormat? DetectFormat(ReadOnlySpan header) => this.IsSupportedFileFormat(header) ? Ktx2Format.Instance : null; + /// + public ITextureFormat? DetectFormat(ReadOnlySpan header) => this.IsSupportedFileFormat(header) ? Ktx2Format.Instance : null; - private bool IsSupportedFileFormat(ReadOnlySpan header) + private bool IsSupportedFileFormat(ReadOnlySpan header) + { + if (header.Length >= this.HeaderSize) { - if (header.Length >= this.HeaderSize) - { - ReadOnlySpan magicBytes = header.Slice(0, 12); - return magicBytes.SequenceEqual(Ktx2Constants.MagicBytes); - } - - return false; + ReadOnlySpan magicBytes = header[..12]; + return magicBytes.SequenceEqual(Ktx2Constants.MagicBytes); } + + return false; } } diff --git a/src/ImageSharp.Textures/Formats/Ktx2/Ktx2Processor.cs b/src/ImageSharp.Textures/Formats/Ktx2/Ktx2Processor.cs index 4dbd30d2..a02160e4 100644 --- a/src/ImageSharp.Textures/Formats/Ktx2/Ktx2Processor.cs +++ b/src/ImageSharp.Textures/Formats/Ktx2/Ktx2Processor.cs @@ -1,392 +1,473 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; -using System.IO; using SixLabors.ImageSharp.Textures.Common.Exceptions; using SixLabors.ImageSharp.Textures.Formats.Ktx2.Enums; using SixLabors.ImageSharp.Textures.TextureFormats; using SixLabors.ImageSharp.Textures.TextureFormats.Decoding; -namespace SixLabors.ImageSharp.Textures.Formats.Ktx2 +namespace SixLabors.ImageSharp.Textures.Formats.Ktx2; + +/// +/// Decodes ktx textures. +/// +internal class Ktx2Processor { /// - /// Decodes ktx textures. + /// Initializes a new instance of the class. /// - internal class Ktx2Processor - { - /// - /// Initializes a new instance of the class. - /// - /// The KTX header. - public Ktx2Processor(Ktx2Header ktxHeader) => this.KtxHeader = ktxHeader; - - /// - /// Gets the KTX header. - /// - public Ktx2Header KtxHeader { get; } + /// The KTX header. + public Ktx2Processor(Ktx2Header ktxHeader) => this.KtxHeader = ktxHeader; - /// - /// Decodes the mipmaps of a KTX2 textures. - /// - /// The stream to read the texture data from. - /// The width of the texture at level 0. - /// The height of the texture at level 0. - /// The start offsets and byte length of each texture. - /// The decoded mipmaps. - public MipMap[] DecodeMipMaps(Stream stream, int width, int height, LevelIndex[] levelIndices) - { - DebugGuard.MustBeGreaterThan(width, 0, nameof(width)); - DebugGuard.MustBeGreaterThan(height, 0, nameof(height)); - DebugGuard.MustBeGreaterThan(levelIndices.Length, 0, nameof(levelIndices.Length)); + /// + /// Gets the KTX header. + /// + public Ktx2Header KtxHeader { get; } - var allMipMapBytes = ReadAllMipMapBytes(stream, levelIndices); - using var memoryStream = new MemoryStream(allMipMapBytes); + /// + /// Decodes the mipmaps of a KTX2 textures. + /// + /// The stream to read the texture data from. + /// The width of the texture at level 0. + /// The height of the texture at level 0. + /// The start offsets and byte length of each texture. + /// The decoded mipmaps. + public MipMap[] DecodeMipMaps(Stream stream, int width, int height, LevelIndex[] levelIndices) + { + DebugGuard.MustBeGreaterThan(width, 0, nameof(width)); + DebugGuard.MustBeGreaterThan(height, 0, nameof(height)); + DebugGuard.MustBeGreaterThan(levelIndices.Length, 0, nameof(levelIndices.Length)); - switch (this.KtxHeader.VkFormat) - { - case VkFormat.VK_FORMAT_R8_UNORM: - case VkFormat.VK_FORMAT_R8_SNORM: - case VkFormat.VK_FORMAT_R8_UINT: - case VkFormat.VK_FORMAT_R8_SINT: - case VkFormat.VK_FORMAT_R8_SRGB: - // Single channel textures will be decoded to luminance image. - return AllocateMipMaps(memoryStream, width, height, levelIndices); - case VkFormat.VK_FORMAT_R16_UNORM: - case VkFormat.VK_FORMAT_R16_SNORM: - case VkFormat.VK_FORMAT_R16_UINT: - case VkFormat.VK_FORMAT_R16_SINT: - // Single channel textures will be decoded to luminance image. - return AllocateMipMaps(memoryStream, width, height, levelIndices); - case VkFormat.VK_FORMAT_R16_SFLOAT: - return AllocateMipMaps(memoryStream, width, height, levelIndices); - case VkFormat.VK_FORMAT_R8G8_UNORM: - case VkFormat.VK_FORMAT_R8G8_SNORM: - case VkFormat.VK_FORMAT_R8G8_UINT: - case VkFormat.VK_FORMAT_R8G8_SINT: - case VkFormat.VK_FORMAT_R8G8_SRGB: - return AllocateMipMaps(memoryStream, width, height, levelIndices); - case VkFormat.VK_FORMAT_R16G16_UNORM: - case VkFormat.VK_FORMAT_R16G16_SNORM: - case VkFormat.VK_FORMAT_R16G16_UINT: - case VkFormat.VK_FORMAT_R16G16_SINT: - return AllocateMipMaps(memoryStream, width, height, levelIndices); - case VkFormat.VK_FORMAT_R32G32_UINT: - case VkFormat.VK_FORMAT_R32G32_SINT: - return AllocateMipMaps(memoryStream, width, height, levelIndices); - case VkFormat.VK_FORMAT_R32G32_SFLOAT: - return AllocateMipMaps(memoryStream, width, height, levelIndices); - case VkFormat.VK_FORMAT_R16G16B16_UNORM: - case VkFormat.VK_FORMAT_R16G16B16_SNORM: - case VkFormat.VK_FORMAT_R16G16B16_UINT: - case VkFormat.VK_FORMAT_R16G16B16_SINT: - return AllocateMipMaps(memoryStream, width, height, levelIndices); - case VkFormat.VK_FORMAT_R16G16B16A16_UNORM: - case VkFormat.VK_FORMAT_R16G16B16A16_SNORM: - case VkFormat.VK_FORMAT_R16G16B16A16_UINT: - case VkFormat.VK_FORMAT_R16G16B16A16_SINT: - return AllocateMipMaps(memoryStream, width, height, levelIndices); - case VkFormat.VK_FORMAT_R16G16B16A16_SFLOAT: - return AllocateMipMaps(memoryStream, width, height, levelIndices); - case VkFormat.VK_FORMAT_R32G32B32_UINT: - case VkFormat.VK_FORMAT_R32G32B32_SINT: - return AllocateMipMaps(memoryStream, width, height, levelIndices); - case VkFormat.VK_FORMAT_R32G32B32_SFLOAT: - return AllocateMipMaps(memoryStream, width, height, levelIndices); - case VkFormat.VK_FORMAT_R32G32B32A32_UINT: - case VkFormat.VK_FORMAT_R32G32B32A32_SINT: - return AllocateMipMaps(memoryStream, width, height, levelIndices); - case VkFormat.VK_FORMAT_R32G32B32A32_SFLOAT: - return AllocateMipMaps(memoryStream, width, height, levelIndices); - case VkFormat.VK_FORMAT_B8G8R8_UNORM: - case VkFormat.VK_FORMAT_B8G8R8_SNORM: - case VkFormat.VK_FORMAT_B8G8R8_UINT: - case VkFormat.VK_FORMAT_B8G8R8_SINT: - case VkFormat.VK_FORMAT_B8G8R8_SRGB: - return AllocateMipMaps(memoryStream, width, height, levelIndices); - case VkFormat.VK_FORMAT_R8G8B8_UNORM: - case VkFormat.VK_FORMAT_R8G8B8_SNORM: - case VkFormat.VK_FORMAT_R8G8B8_UINT: - case VkFormat.VK_FORMAT_R8G8B8_SINT: - case VkFormat.VK_FORMAT_R8G8B8_SRGB: - return AllocateMipMaps(memoryStream, width, height, levelIndices); - case VkFormat.VK_FORMAT_R5G6B5_UNORM_PACK16: - return AllocateMipMaps(memoryStream, width, height, levelIndices); - case VkFormat.VK_FORMAT_R4G4B4A4_UNORM_PACK16: - return AllocateMipMaps(memoryStream, width, height, levelIndices); - case VkFormat.VK_FORMAT_B8G8R8A8_UNORM: - case VkFormat.VK_FORMAT_B8G8R8A8_SNORM: - case VkFormat.VK_FORMAT_B8G8R8A8_UINT: - case VkFormat.VK_FORMAT_B8G8R8A8_SINT: - case VkFormat.VK_FORMAT_B8G8R8A8_SRGB: - return AllocateMipMaps(memoryStream, width, height, levelIndices); - case VkFormat.VK_FORMAT_B5G5R5A1_UNORM_PACK16: - return AllocateMipMaps(memoryStream, width, height, levelIndices); - case VkFormat.VK_FORMAT_B5G6R5_UNORM_PACK16: - return AllocateMipMaps(memoryStream, width, height, levelIndices); - case VkFormat.VK_FORMAT_B4G4R4A4_UNORM_PACK16: - return AllocateMipMaps(memoryStream, width, height, levelIndices); - case VkFormat.VK_FORMAT_R8G8B8A8_UNORM: - case VkFormat.VK_FORMAT_R8G8B8A8_SNORM: - case VkFormat.VK_FORMAT_R8G8B8A8_UINT: - case VkFormat.VK_FORMAT_R8G8B8A8_SINT: - case VkFormat.VK_FORMAT_R8G8B8A8_SRGB: - return AllocateMipMaps(memoryStream, width, height, levelIndices); - case VkFormat.VK_FORMAT_R5G5B5A1_UNORM_PACK16: - return AllocateMipMaps(memoryStream, width, height, levelIndices); - case VkFormat.VK_FORMAT_BC1_RGB_UNORM_BLOCK: - case VkFormat.VK_FORMAT_BC1_RGBA_UNORM_BLOCK: - return AllocateMipMaps(memoryStream, width, height, levelIndices); - case VkFormat.VK_FORMAT_BC2_UNORM_BLOCK: - return AllocateMipMaps(memoryStream, width, height, levelIndices); - case VkFormat.VK_FORMAT_BC3_UNORM_BLOCK: - return AllocateMipMaps(memoryStream, width, height, levelIndices); - case VkFormat.VK_FORMAT_BC4_UNORM_BLOCK: - return AllocateMipMaps(memoryStream, width, height, levelIndices); - case VkFormat.VK_FORMAT_BC4_SNORM_BLOCK: - return AllocateMipMaps(memoryStream, width, height, levelIndices); - case VkFormat.VK_FORMAT_BC5_UNORM_BLOCK: - return AllocateMipMaps(memoryStream, width, height, levelIndices); - case VkFormat.VK_FORMAT_BC5_SNORM_BLOCK: - return AllocateMipMaps(memoryStream, width, height, levelIndices); - case VkFormat.VK_FORMAT_BC6H_UFLOAT_BLOCK: - return AllocateMipMaps(memoryStream, width, height, levelIndices); - case VkFormat.VK_FORMAT_BC6H_SFLOAT_BLOCK: - return AllocateMipMaps(memoryStream, width, height, levelIndices); - case VkFormat.VK_FORMAT_BC7_UNORM_BLOCK: - return AllocateMipMaps(memoryStream, width, height, levelIndices); - case VkFormat.VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK: - return AllocateMipMaps(memoryStream, width, height, levelIndices); - } + byte[] allMipMapBytes = ReadAllMipMapBytes(stream, levelIndices); + using MemoryStream memoryStream = new MemoryStream(allMipMapBytes); - throw new NotSupportedException("The pixel format is not supported"); + switch (this.KtxHeader.VkFormat) + { + case VkFormat.VK_FORMAT_R8_UNORM: + case VkFormat.VK_FORMAT_R8_SNORM: + case VkFormat.VK_FORMAT_R8_UINT: + case VkFormat.VK_FORMAT_R8_SINT: + case VkFormat.VK_FORMAT_R8_SRGB: + // Single channel textures will be decoded to luminance image. + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_R16_UNORM: + case VkFormat.VK_FORMAT_R16_SNORM: + case VkFormat.VK_FORMAT_R16_UINT: + case VkFormat.VK_FORMAT_R16_SINT: + // Single channel textures will be decoded to luminance image. + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_R16_SFLOAT: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_R8G8_UNORM: + case VkFormat.VK_FORMAT_R8G8_SNORM: + case VkFormat.VK_FORMAT_R8G8_UINT: + case VkFormat.VK_FORMAT_R8G8_SINT: + case VkFormat.VK_FORMAT_R8G8_SRGB: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_R16G16_UNORM: + case VkFormat.VK_FORMAT_R16G16_SNORM: + case VkFormat.VK_FORMAT_R16G16_UINT: + case VkFormat.VK_FORMAT_R16G16_SINT: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_R32G32_UINT: + case VkFormat.VK_FORMAT_R32G32_SINT: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_R32G32_SFLOAT: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_R16G16B16_UNORM: + case VkFormat.VK_FORMAT_R16G16B16_SNORM: + case VkFormat.VK_FORMAT_R16G16B16_UINT: + case VkFormat.VK_FORMAT_R16G16B16_SINT: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_R16G16B16A16_UNORM: + case VkFormat.VK_FORMAT_R16G16B16A16_SNORM: + case VkFormat.VK_FORMAT_R16G16B16A16_UINT: + case VkFormat.VK_FORMAT_R16G16B16A16_SINT: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_R16G16B16A16_SFLOAT: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_R32G32B32_UINT: + case VkFormat.VK_FORMAT_R32G32B32_SINT: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_R32G32B32_SFLOAT: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_R32G32B32A32_UINT: + case VkFormat.VK_FORMAT_R32G32B32A32_SINT: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_R32G32B32A32_SFLOAT: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_B8G8R8_UNORM: + case VkFormat.VK_FORMAT_B8G8R8_SNORM: + case VkFormat.VK_FORMAT_B8G8R8_UINT: + case VkFormat.VK_FORMAT_B8G8R8_SINT: + case VkFormat.VK_FORMAT_B8G8R8_SRGB: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_R8G8B8_UNORM: + case VkFormat.VK_FORMAT_R8G8B8_SNORM: + case VkFormat.VK_FORMAT_R8G8B8_UINT: + case VkFormat.VK_FORMAT_R8G8B8_SINT: + case VkFormat.VK_FORMAT_R8G8B8_SRGB: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_R5G6B5_UNORM_PACK16: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_R4G4B4A4_UNORM_PACK16: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_B8G8R8A8_UNORM: + case VkFormat.VK_FORMAT_B8G8R8A8_SNORM: + case VkFormat.VK_FORMAT_B8G8R8A8_UINT: + case VkFormat.VK_FORMAT_B8G8R8A8_SINT: + case VkFormat.VK_FORMAT_B8G8R8A8_SRGB: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_B5G5R5A1_UNORM_PACK16: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_B5G6R5_UNORM_PACK16: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_B4G4R4A4_UNORM_PACK16: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_R8G8B8A8_UNORM: + case VkFormat.VK_FORMAT_R8G8B8A8_SNORM: + case VkFormat.VK_FORMAT_R8G8B8A8_UINT: + case VkFormat.VK_FORMAT_R8G8B8A8_SINT: + case VkFormat.VK_FORMAT_R8G8B8A8_SRGB: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_R5G5B5A1_UNORM_PACK16: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_BC1_RGB_UNORM_BLOCK: + case VkFormat.VK_FORMAT_BC1_RGBA_UNORM_BLOCK: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_BC2_UNORM_BLOCK: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_BC3_UNORM_BLOCK: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_BC4_UNORM_BLOCK: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_BC4_SNORM_BLOCK: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_BC5_UNORM_BLOCK: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_BC5_SNORM_BLOCK: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_BC6H_UFLOAT_BLOCK: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_BC6H_SFLOAT_BLOCK: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_BC7_UNORM_BLOCK: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_ASTC_4x4_UNORM_BLOCK: + case VkFormat.VK_FORMAT_ASTC_4x4_SRGB_BLOCK: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_ASTC_5x4_UNORM_BLOCK: + case VkFormat.VK_FORMAT_ASTC_5x4_SRGB_BLOCK: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_ASTC_5x5_UNORM_BLOCK: + case VkFormat.VK_FORMAT_ASTC_5x5_SRGB_BLOCK: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_ASTC_6x5_UNORM_BLOCK: + case VkFormat.VK_FORMAT_ASTC_6x5_SRGB_BLOCK: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_ASTC_6x6_UNORM_BLOCK: + case VkFormat.VK_FORMAT_ASTC_6x6_SRGB_BLOCK: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_ASTC_8x5_UNORM_BLOCK: + case VkFormat.VK_FORMAT_ASTC_8x5_SRGB_BLOCK: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_ASTC_8x6_UNORM_BLOCK: + case VkFormat.VK_FORMAT_ASTC_8x6_SRGB_BLOCK: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_ASTC_8x8_UNORM_BLOCK: + case VkFormat.VK_FORMAT_ASTC_8x8_SRGB_BLOCK: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_ASTC_10x5_UNORM_BLOCK: + case VkFormat.VK_FORMAT_ASTC_10x5_SRGB_BLOCK: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_ASTC_10x6_UNORM_BLOCK: + case VkFormat.VK_FORMAT_ASTC_10x6_SRGB_BLOCK: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_ASTC_10x8_UNORM_BLOCK: + case VkFormat.VK_FORMAT_ASTC_10x8_SRGB_BLOCK: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_ASTC_10x10_UNORM_BLOCK: + case VkFormat.VK_FORMAT_ASTC_10x10_SRGB_BLOCK: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_ASTC_12x10_UNORM_BLOCK: + case VkFormat.VK_FORMAT_ASTC_12x10_SRGB_BLOCK: + return AllocateMipMaps(memoryStream, width, height, levelIndices); + case VkFormat.VK_FORMAT_ASTC_12x12_UNORM_BLOCK: + case VkFormat.VK_FORMAT_ASTC_12x12_SRGB_BLOCK: + return AllocateMipMaps(memoryStream, width, height, levelIndices); } - /// - /// Allocates and decodes the a KTX2 cube map texture. - /// - /// The stream to read the texture data from. - /// The width of a texture face. - /// The height of a texture face. - /// The start offsets and byte length of each texture. - /// A decoded cubemap texture. - /// The pixel format is not supported - public CubemapTexture DecodeCubeMap(Stream stream, int width, int height, LevelIndex[] levelIndices) - { - DebugGuard.MustBeGreaterThan(width, 0, nameof(width)); - DebugGuard.MustBeGreaterThan(height, 0, nameof(height)); + throw new NotSupportedException("The pixel format is not supported"); + } - switch (this.KtxHeader.VkFormat) - { - case VkFormat.VK_FORMAT_R8_UNORM: - case VkFormat.VK_FORMAT_R8_SNORM: - case VkFormat.VK_FORMAT_R8_UINT: - case VkFormat.VK_FORMAT_R8_SINT: - case VkFormat.VK_FORMAT_R8_SRGB: - return AllocateCubeMap(stream, width, height, levelIndices); - case VkFormat.VK_FORMAT_R16_UNORM: - case VkFormat.VK_FORMAT_R16_SNORM: - case VkFormat.VK_FORMAT_R16_UINT: - case VkFormat.VK_FORMAT_R16_SINT: - return AllocateCubeMap(stream, width, height, levelIndices); - case VkFormat.VK_FORMAT_R8G8_UNORM: - case VkFormat.VK_FORMAT_R8G8_SNORM: - case VkFormat.VK_FORMAT_R8G8_UINT: - case VkFormat.VK_FORMAT_R8G8_SINT: - case VkFormat.VK_FORMAT_R8G8_SRGB: - return AllocateCubeMap(stream, width, height, levelIndices); - case VkFormat.VK_FORMAT_R16G16_UNORM: - case VkFormat.VK_FORMAT_R16G16_SNORM: - case VkFormat.VK_FORMAT_R16G16_UINT: - case VkFormat.VK_FORMAT_R16G16_SINT: - return AllocateCubeMap(stream, width, height, levelIndices); - case VkFormat.VK_FORMAT_R16G16B16_UNORM: - case VkFormat.VK_FORMAT_R16G16B16_SNORM: - case VkFormat.VK_FORMAT_R16G16B16_UINT: - case VkFormat.VK_FORMAT_R16G16B16_SINT: - return AllocateCubeMap(stream, width, height, levelIndices); - case VkFormat.VK_FORMAT_R16G16B16A16_UNORM: - case VkFormat.VK_FORMAT_R16G16B16A16_SNORM: - case VkFormat.VK_FORMAT_R16G16B16A16_UINT: - case VkFormat.VK_FORMAT_R16G16B16A16_SINT: - return AllocateCubeMap(stream, width, height, levelIndices); - case VkFormat.VK_FORMAT_R16_SFLOAT: - return AllocateCubeMap(stream, width, height, levelIndices); - case VkFormat.VK_FORMAT_R16G16B16A16_SFLOAT: - return AllocateCubeMap(stream, width, height, levelIndices); - case VkFormat.VK_FORMAT_R32G32_UINT: - case VkFormat.VK_FORMAT_R32G32_SINT: - return AllocateCubeMap(stream, width, height, levelIndices); - case VkFormat.VK_FORMAT_R32G32_SFLOAT: - return AllocateCubeMap(stream, width, height, levelIndices); - case VkFormat.VK_FORMAT_R32G32B32_UINT: - case VkFormat.VK_FORMAT_R32G32B32_SINT: - return AllocateCubeMap(stream, width, height, levelIndices); - case VkFormat.VK_FORMAT_R32G32B32_SFLOAT: - return AllocateCubeMap(stream, width, height, levelIndices); - case VkFormat.VK_FORMAT_R32G32B32A32_UINT: - case VkFormat.VK_FORMAT_R32G32B32A32_SINT: - return AllocateCubeMap(stream, width, height, levelIndices); - case VkFormat.VK_FORMAT_R32G32B32A32_SFLOAT: - return AllocateCubeMap(stream, width, height, levelIndices); - case VkFormat.VK_FORMAT_B8G8R8_UNORM: - case VkFormat.VK_FORMAT_B8G8R8_SNORM: - case VkFormat.VK_FORMAT_B8G8R8_UINT: - case VkFormat.VK_FORMAT_B8G8R8_SINT: - case VkFormat.VK_FORMAT_B8G8R8_SRGB: - return AllocateCubeMap(stream, width, height, levelIndices); - case VkFormat.VK_FORMAT_R8G8B8_UNORM: - case VkFormat.VK_FORMAT_R8G8B8_SNORM: - case VkFormat.VK_FORMAT_R8G8B8_UINT: - case VkFormat.VK_FORMAT_R8G8B8_SINT: - case VkFormat.VK_FORMAT_R8G8B8_SRGB: - return AllocateCubeMap(stream, width, height, levelIndices); - case VkFormat.VK_FORMAT_R5G6B5_UNORM_PACK16: - return AllocateCubeMap(stream, width, height, levelIndices); - case VkFormat.VK_FORMAT_R4G4B4A4_UNORM_PACK16: - return AllocateCubeMap(stream, width, height, levelIndices); - case VkFormat.VK_FORMAT_B8G8R8A8_UNORM: - case VkFormat.VK_FORMAT_B8G8R8A8_SNORM: - case VkFormat.VK_FORMAT_B8G8R8A8_UINT: - case VkFormat.VK_FORMAT_B8G8R8A8_SINT: - case VkFormat.VK_FORMAT_B8G8R8A8_SRGB: - return AllocateCubeMap(stream, width, height, levelIndices); - case VkFormat.VK_FORMAT_B5G5R5A1_UNORM_PACK16: - return AllocateCubeMap(stream, width, height, levelIndices); - case VkFormat.VK_FORMAT_B5G6R5_UNORM_PACK16: - return AllocateCubeMap(stream, width, height, levelIndices); - case VkFormat.VK_FORMAT_B4G4R4A4_UNORM_PACK16: - return AllocateCubeMap(stream, width, height, levelIndices); - case VkFormat.VK_FORMAT_R8G8B8A8_UNORM: - case VkFormat.VK_FORMAT_R8G8B8A8_SNORM: - case VkFormat.VK_FORMAT_R8G8B8A8_UINT: - case VkFormat.VK_FORMAT_R8G8B8A8_SINT: - case VkFormat.VK_FORMAT_R8G8B8A8_SRGB: - return AllocateCubeMap(stream, width, height, levelIndices); - case VkFormat.VK_FORMAT_R5G5B5A1_UNORM_PACK16: - return AllocateCubeMap(stream, width, height, levelIndices); - case VkFormat.VK_FORMAT_BC1_RGB_UNORM_BLOCK: - case VkFormat.VK_FORMAT_BC1_RGBA_UNORM_BLOCK: - return AllocateCubeMap(stream, width, height, levelIndices); - case VkFormat.VK_FORMAT_BC2_UNORM_BLOCK: - return AllocateCubeMap(stream, width, height, levelIndices); - case VkFormat.VK_FORMAT_BC4_UNORM_BLOCK: - return AllocateCubeMap(stream, width, height, levelIndices); - case VkFormat.VK_FORMAT_BC4_SNORM_BLOCK: - return AllocateCubeMap(stream, width, height, levelIndices); - case VkFormat.VK_FORMAT_BC5_UNORM_BLOCK: - return AllocateCubeMap(stream, width, height, levelIndices); - case VkFormat.VK_FORMAT_BC5_SNORM_BLOCK: - return AllocateCubeMap(stream, width, height, levelIndices); - case VkFormat.VK_FORMAT_BC6H_UFLOAT_BLOCK: - return AllocateCubeMap(stream, width, height, levelIndices); - case VkFormat.VK_FORMAT_BC6H_SFLOAT_BLOCK: - return AllocateCubeMap(stream, width, height, levelIndices); - case VkFormat.VK_FORMAT_BC7_UNORM_BLOCK: - return AllocateCubeMap(stream, width, height, levelIndices); - case VkFormat.VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK: - return AllocateCubeMap(stream, width, height, levelIndices); - } + /// + /// Allocates and decodes the a KTX2 cube map texture. + /// + /// The stream to read the texture data from. + /// The width of a texture face. + /// The height of a texture face. + /// The start offsets and byte length of each texture. + /// A decoded cubemap texture. + /// The pixel format is not supported + public CubemapTexture DecodeCubeMap(Stream stream, int width, int height, LevelIndex[] levelIndices) + { + DebugGuard.MustBeGreaterThan(width, 0, nameof(width)); + DebugGuard.MustBeGreaterThan(height, 0, nameof(height)); - throw new NotSupportedException("The pixel format is not supported"); + switch (this.KtxHeader.VkFormat) + { + case VkFormat.VK_FORMAT_R8_UNORM: + case VkFormat.VK_FORMAT_R8_SNORM: + case VkFormat.VK_FORMAT_R8_UINT: + case VkFormat.VK_FORMAT_R8_SINT: + case VkFormat.VK_FORMAT_R8_SRGB: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_R16_UNORM: + case VkFormat.VK_FORMAT_R16_SNORM: + case VkFormat.VK_FORMAT_R16_UINT: + case VkFormat.VK_FORMAT_R16_SINT: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_R8G8_UNORM: + case VkFormat.VK_FORMAT_R8G8_SNORM: + case VkFormat.VK_FORMAT_R8G8_UINT: + case VkFormat.VK_FORMAT_R8G8_SINT: + case VkFormat.VK_FORMAT_R8G8_SRGB: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_R16G16_UNORM: + case VkFormat.VK_FORMAT_R16G16_SNORM: + case VkFormat.VK_FORMAT_R16G16_UINT: + case VkFormat.VK_FORMAT_R16G16_SINT: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_R16G16B16_UNORM: + case VkFormat.VK_FORMAT_R16G16B16_SNORM: + case VkFormat.VK_FORMAT_R16G16B16_UINT: + case VkFormat.VK_FORMAT_R16G16B16_SINT: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_R16G16B16A16_UNORM: + case VkFormat.VK_FORMAT_R16G16B16A16_SNORM: + case VkFormat.VK_FORMAT_R16G16B16A16_UINT: + case VkFormat.VK_FORMAT_R16G16B16A16_SINT: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_R16_SFLOAT: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_R16G16B16A16_SFLOAT: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_R32G32_UINT: + case VkFormat.VK_FORMAT_R32G32_SINT: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_R32G32_SFLOAT: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_R32G32B32_UINT: + case VkFormat.VK_FORMAT_R32G32B32_SINT: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_R32G32B32_SFLOAT: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_R32G32B32A32_UINT: + case VkFormat.VK_FORMAT_R32G32B32A32_SINT: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_R32G32B32A32_SFLOAT: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_B8G8R8_UNORM: + case VkFormat.VK_FORMAT_B8G8R8_SNORM: + case VkFormat.VK_FORMAT_B8G8R8_UINT: + case VkFormat.VK_FORMAT_B8G8R8_SINT: + case VkFormat.VK_FORMAT_B8G8R8_SRGB: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_R8G8B8_UNORM: + case VkFormat.VK_FORMAT_R8G8B8_SNORM: + case VkFormat.VK_FORMAT_R8G8B8_UINT: + case VkFormat.VK_FORMAT_R8G8B8_SINT: + case VkFormat.VK_FORMAT_R8G8B8_SRGB: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_R5G6B5_UNORM_PACK16: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_R4G4B4A4_UNORM_PACK16: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_B8G8R8A8_UNORM: + case VkFormat.VK_FORMAT_B8G8R8A8_SNORM: + case VkFormat.VK_FORMAT_B8G8R8A8_UINT: + case VkFormat.VK_FORMAT_B8G8R8A8_SINT: + case VkFormat.VK_FORMAT_B8G8R8A8_SRGB: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_B5G5R5A1_UNORM_PACK16: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_B5G6R5_UNORM_PACK16: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_B4G4R4A4_UNORM_PACK16: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_R8G8B8A8_UNORM: + case VkFormat.VK_FORMAT_R8G8B8A8_SNORM: + case VkFormat.VK_FORMAT_R8G8B8A8_UINT: + case VkFormat.VK_FORMAT_R8G8B8A8_SINT: + case VkFormat.VK_FORMAT_R8G8B8A8_SRGB: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_R5G5B5A1_UNORM_PACK16: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_BC1_RGB_UNORM_BLOCK: + case VkFormat.VK_FORMAT_BC1_RGBA_UNORM_BLOCK: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_BC2_UNORM_BLOCK: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_BC4_UNORM_BLOCK: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_BC4_SNORM_BLOCK: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_BC5_UNORM_BLOCK: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_BC5_SNORM_BLOCK: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_BC6H_UFLOAT_BLOCK: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_BC6H_SFLOAT_BLOCK: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_BC7_UNORM_BLOCK: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_ASTC_4x4_UNORM_BLOCK: + case VkFormat.VK_FORMAT_ASTC_4x4_SRGB_BLOCK: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_ASTC_5x4_UNORM_BLOCK: + case VkFormat.VK_FORMAT_ASTC_5x4_SRGB_BLOCK: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_ASTC_5x5_UNORM_BLOCK: + case VkFormat.VK_FORMAT_ASTC_5x5_SRGB_BLOCK: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_ASTC_6x5_UNORM_BLOCK: + case VkFormat.VK_FORMAT_ASTC_6x5_SRGB_BLOCK: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_ASTC_6x6_UNORM_BLOCK: + case VkFormat.VK_FORMAT_ASTC_6x6_SRGB_BLOCK: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_ASTC_8x5_UNORM_BLOCK: + case VkFormat.VK_FORMAT_ASTC_8x5_SRGB_BLOCK: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_ASTC_8x6_UNORM_BLOCK: + case VkFormat.VK_FORMAT_ASTC_8x6_SRGB_BLOCK: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_ASTC_8x8_UNORM_BLOCK: + case VkFormat.VK_FORMAT_ASTC_8x8_SRGB_BLOCK: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_ASTC_10x5_UNORM_BLOCK: + case VkFormat.VK_FORMAT_ASTC_10x5_SRGB_BLOCK: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_ASTC_10x6_UNORM_BLOCK: + case VkFormat.VK_FORMAT_ASTC_10x6_SRGB_BLOCK: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_ASTC_10x8_UNORM_BLOCK: + case VkFormat.VK_FORMAT_ASTC_10x8_SRGB_BLOCK: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_ASTC_10x10_UNORM_BLOCK: + case VkFormat.VK_FORMAT_ASTC_10x10_SRGB_BLOCK: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_ASTC_12x10_UNORM_BLOCK: + case VkFormat.VK_FORMAT_ASTC_12x10_SRGB_BLOCK: + return AllocateCubeMap(stream, width, height, levelIndices); + case VkFormat.VK_FORMAT_ASTC_12x12_UNORM_BLOCK: + case VkFormat.VK_FORMAT_ASTC_12x12_SRGB_BLOCK: + return AllocateCubeMap(stream, width, height, levelIndices); } - /// - /// Allocates and decodes all mipmap levels of a ktx texture. - /// - /// The stream to read the texture data from. - /// The width of the texture at level 0. - /// The height of the texture at level 0. - /// The start offsets and byte length of each texture. - /// The decoded mipmaps. - private static MipMap[] AllocateMipMaps(Stream stream, int width, int height, LevelIndex[] levelIndices) - where TBlock : struct, IBlock - { - var blockFormat = default(TBlock); - var mipMaps = new MipMap[levelIndices.Length]; - for (int i = 0; i < levelIndices.Length; i++) - { - var pixelDataSize = levelIndices[i].UncompressedByteLength; - byte[] mipMapData = new byte[pixelDataSize]; - ReadTextureData(stream, mipMapData); - mipMaps[i] = new MipMap(blockFormat, mipMapData, width, height); + throw new NotSupportedException("The pixel format is not supported"); + } - width >>= 1; - height >>= 1; - } + /// + /// Allocates and decodes all mipmap levels of a ktx texture. + /// + /// The stream to read the texture data from. + /// The width of the texture at level 0. + /// The height of the texture at level 0. + /// The start offsets and byte length of each texture. + /// The decoded mipmaps. + private static MipMap[] AllocateMipMaps(Stream stream, int width, int height, LevelIndex[] levelIndices) + where TBlock : struct, IBlock + { + TBlock blockFormat = default(TBlock); + MipMap[] mipMaps = new MipMap[levelIndices.Length]; + for (int i = 0; i < levelIndices.Length; i++) + { + ulong pixelDataSize = levelIndices[i].UncompressedByteLength; + byte[] mipMapData = new byte[pixelDataSize]; + ReadTextureData(stream, mipMapData); + mipMaps[i] = new MipMap(blockFormat, mipMapData, width, height); - return mipMaps; + width >>= 1; + height >>= 1; } - private static CubemapTexture AllocateCubeMap(Stream stream, int width, int height, LevelIndex[] levelIndices) - where TBlock : struct, IBlock - { - var numberOfMipMaps = levelIndices.Length; + return mipMaps; + } - var cubeMapTexture = new CubemapTexture(); - var blockFormat = default(TBlock); - for (int i = 0; i < numberOfMipMaps; i++) - { - stream.Position = (long)levelIndices[i].ByteOffset; - var uncompressedDataLength = levelIndices[i].UncompressedByteLength; - var dataForEachFace = (uint)uncompressedDataLength / 6; + private static CubemapTexture AllocateCubeMap(Stream stream, int width, int height, LevelIndex[] levelIndices) + where TBlock : struct, IBlock + { + int numberOfMipMaps = levelIndices.Length; - cubeMapTexture.PositiveX.MipMaps.Add(ReadFaceTexture(stream, width, height, blockFormat, dataForEachFace)); - cubeMapTexture.NegativeX.MipMaps.Add(ReadFaceTexture(stream, width, height, blockFormat, dataForEachFace)); - cubeMapTexture.PositiveY.MipMaps.Add(ReadFaceTexture(stream, width, height, blockFormat, dataForEachFace)); - cubeMapTexture.NegativeY.MipMaps.Add(ReadFaceTexture(stream, width, height, blockFormat, dataForEachFace)); - cubeMapTexture.PositiveZ.MipMaps.Add(ReadFaceTexture(stream, width, height, blockFormat, dataForEachFace)); - cubeMapTexture.NegativeZ.MipMaps.Add(ReadFaceTexture(stream, width, height, blockFormat, dataForEachFace)); + CubemapTexture cubeMapTexture = new CubemapTexture(); + TBlock blockFormat = default(TBlock); + for (int i = 0; i < numberOfMipMaps; i++) + { + stream.Position = (long)levelIndices[i].ByteOffset; + ulong uncompressedDataLength = levelIndices[i].UncompressedByteLength; + uint dataForEachFace = (uint)uncompressedDataLength / 6; - width >>= 1; - height >>= 1; - } + cubeMapTexture.PositiveX.MipMaps.Add(ReadFaceTexture(stream, width, height, blockFormat, dataForEachFace)); + cubeMapTexture.NegativeX.MipMaps.Add(ReadFaceTexture(stream, width, height, blockFormat, dataForEachFace)); + cubeMapTexture.PositiveY.MipMaps.Add(ReadFaceTexture(stream, width, height, blockFormat, dataForEachFace)); + cubeMapTexture.NegativeY.MipMaps.Add(ReadFaceTexture(stream, width, height, blockFormat, dataForEachFace)); + cubeMapTexture.PositiveZ.MipMaps.Add(ReadFaceTexture(stream, width, height, blockFormat, dataForEachFace)); + cubeMapTexture.NegativeZ.MipMaps.Add(ReadFaceTexture(stream, width, height, blockFormat, dataForEachFace)); - return cubeMapTexture; + width >>= 1; + height >>= 1; } - /// - /// Read all mip maps and store them in a byte array so the level 0 mipmap will be at the beginning - /// followed by all other mip map levels. - /// - /// The stream to read the mipmap data from. - /// The level indices. - /// A byte array containing all the mipmaps. - private static byte[] ReadAllMipMapBytes(Stream stream, LevelIndex[] levelIndices) - { - ulong totalBytes = 0; - for (int i = 0; i < levelIndices.Length; i++) - { - totalBytes += levelIndices[i].UncompressedByteLength; - } - - byte[] allMipMapBytes = new byte[totalBytes]; - var idx = 0; - for (int i = 0; i < levelIndices.Length; i++) - { - stream.Position = (long)levelIndices[i].ByteOffset; - stream.Read(allMipMapBytes, idx, (int)levelIndices[i].UncompressedByteLength); - idx += (int)levelIndices[i].UncompressedByteLength; - } + return cubeMapTexture; + } - return allMipMapBytes; + /// + /// Read all mip maps and store them in a byte array so the level 0 mipmap will be at the beginning + /// followed by all other mip map levels. + /// + /// The stream to read the mipmap data from. + /// The level indices. + /// A byte array containing all the mipmaps. + private static byte[] ReadAllMipMapBytes(Stream stream, LevelIndex[] levelIndices) + { + ulong totalBytes = 0; + for (int i = 0; i < levelIndices.Length; i++) + { + totalBytes += levelIndices[i].UncompressedByteLength; } - private static MipMap ReadFaceTexture(Stream stream, int width, int height, TBlock blockFormat, uint dataForEachFace) - where TBlock : struct, IBlock + byte[] allMipMapBytes = new byte[totalBytes]; + int idx = 0; + for (int i = 0; i < levelIndices.Length; i++) { - byte[] faceData = new byte[dataForEachFace]; - ReadTextureData(stream, faceData); - return new MipMap(blockFormat, faceData, width, height); + stream.Position = (long)levelIndices[i].ByteOffset; + stream.Read(allMipMapBytes, idx, (int)levelIndices[i].UncompressedByteLength); + idx += (int)levelIndices[i].UncompressedByteLength; } - private static void ReadTextureData(Stream stream, byte[] mipMapData) + return allMipMapBytes; + } + + private static MipMap ReadFaceTexture(Stream stream, int width, int height, TBlock blockFormat, uint dataForEachFace) + where TBlock : struct, IBlock + { + byte[] faceData = new byte[dataForEachFace]; + ReadTextureData(stream, faceData); + return new MipMap(blockFormat, faceData, width, height); + } + + private static void ReadTextureData(Stream stream, byte[] mipMapData) + { + int bytesRead = stream.Read(mipMapData, 0, mipMapData.Length); + if (bytesRead != mipMapData.Length) { - int bytesRead = stream.Read(mipMapData, 0, mipMapData.Length); - if (bytesRead != mipMapData.Length) - { - throw new TextureFormatException("could not read enough texture data from the stream"); - } + throw new TextureFormatException("could not read enough texture data from the stream"); } } } diff --git a/src/ImageSharp.Textures/Formats/Ktx2/LevelIndex.cs b/src/ImageSharp.Textures/Formats/Ktx2/LevelIndex.cs index 80d38f6f..02a79a0c 100644 --- a/src/ImageSharp.Textures/Formats/Ktx2/LevelIndex.cs +++ b/src/ImageSharp.Textures/Formats/Ktx2/LevelIndex.cs @@ -3,15 +3,14 @@ using System.Runtime.InteropServices; -namespace SixLabors.ImageSharp.Textures.Formats.Ktx2 +namespace SixLabors.ImageSharp.Textures.Formats.Ktx2; + +[StructLayout(LayoutKind.Sequential, Pack = 1)] +internal struct LevelIndex { - [StructLayout(LayoutKind.Sequential, Pack = 1)] - internal struct LevelIndex - { - public readonly ulong ByteOffset; + public readonly ulong ByteOffset; - public readonly ulong ByteLength; + public readonly ulong ByteLength; - public readonly ulong UncompressedByteLength; - } + public readonly ulong UncompressedByteLength; } diff --git a/src/ImageSharp.Textures/Formats/TextureFormatManager.cs b/src/ImageSharp.Textures/Formats/TextureFormatManager.cs index 11d36fb7..4dd5e248 100644 --- a/src/ImageSharp.Textures/Formats/TextureFormatManager.cs +++ b/src/ImageSharp.Textures/Formats/TextureFormatManager.cs @@ -99,7 +99,7 @@ public void AddImageFormat(ITextureFormat format) if (extension[0] == '.') { - extension = extension.Substring(1); + extension = extension[1..]; } return this.imageFormats.FirstOrDefault(x => x.FileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)); @@ -141,7 +141,7 @@ public void SetDecoder(ITextureFormat imageFormat, ITextureDecoder decoder) /// /// Removes all the registered image format detectors. /// - public void ClearImageFormatDetectors() => this.imageFormatDetectors = new ConcurrentBag(); + public void ClearImageFormatDetectors() => this.imageFormatDetectors = []; /// /// Adds a new detector for detecting mime types. diff --git a/src/ImageSharp.Textures/Formats/TextureTypeInfo.cs b/src/ImageSharp.Textures/Formats/TextureTypeInfo.cs index ab632015..6a5182bb 100644 --- a/src/ImageSharp.Textures/Formats/TextureTypeInfo.cs +++ b/src/ImageSharp.Textures/Formats/TextureTypeInfo.cs @@ -1,25 +1,21 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.Formats +namespace SixLabors.ImageSharp.Textures.Formats; + +/// +/// Contains information about the pixels that make up an images visual data. +/// +public class TextureTypeInfo { /// - /// Contains information about the pixels that make up an images visual data. + /// Initializes a new instance of the class. /// - public class TextureTypeInfo - { - /// - /// Initializes a new instance of the class. - /// - /// Color depth, in number of bits per pixel. - internal TextureTypeInfo(int bitsPerPixel) - { - this.BitsPerPixel = bitsPerPixel; - } + /// Color depth, in number of bits per pixel. + internal TextureTypeInfo(int bitsPerPixel) => this.BitsPerPixel = bitsPerPixel; - /// - /// Gets color depth, in number of bits per pixel. - /// - public int BitsPerPixel { get; } - } + /// + /// Gets color depth, in number of bits per pixel. + /// + public int BitsPerPixel { get; } } diff --git a/src/ImageSharp.Textures/IConfigurationModule.cs b/src/ImageSharp.Textures/IConfigurationModule.cs index 13362391..10622f72 100644 --- a/src/ImageSharp.Textures/IConfigurationModule.cs +++ b/src/ImageSharp.Textures/IConfigurationModule.cs @@ -1,17 +1,16 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures +namespace SixLabors.ImageSharp.Textures; + +/// +/// Represents an interface that can register image encoders, decoders and image format detectors. +/// +public interface IConfigurationModule { /// - /// Represents an interface that can register image encoders, decoders and image format detectors. + /// Called when loaded into a configuration object so the module can register items into the configuration. /// - public interface IConfigurationModule - { - /// - /// Called when loaded into a configuration object so the module can register items into the configuration. - /// - /// The configuration that will retain the encoders, decodes and mime type detectors. - void Configure(Configuration configuration); - } + /// The configuration that will retain the encoders, decodes and mime type detectors. + void Configure(Configuration configuration); } diff --git a/src/ImageSharp.Textures/IO/IFileSystem.cs b/src/ImageSharp.Textures/IO/IFileSystem.cs index a36fd7eb..79656b16 100644 --- a/src/ImageSharp.Textures/IO/IFileSystem.cs +++ b/src/ImageSharp.Textures/IO/IFileSystem.cs @@ -1,27 +1,26 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.IO -{ - using System.IO; +namespace SixLabors.ImageSharp.Textures.IO; + +using System.IO; +/// +/// A simple interface representing the filesystem. +/// +internal interface IFileSystem +{ /// - /// A simple interface representing the filesystem. + /// Returns a readable stream as defined by the path. /// - internal interface IFileSystem - { - /// - /// Returns a readable stream as defined by the path. - /// - /// Path to the file to open. - /// A stream representing the file to open. - Stream OpenRead(string path); + /// Path to the file to open. + /// A stream representing the file to open. + Stream OpenRead(string path); - /// - /// Creates or opens a file and returns it as a writable stream as defined by the path. - /// - /// Path to the file to open. - /// A stream representing the file to open. - Stream Create(string path); - } + /// + /// Creates or opens a file and returns it as a writable stream as defined by the path. + /// + /// Path to the file to open. + /// A stream representing the file to open. + Stream Create(string path); } diff --git a/src/ImageSharp.Textures/IO/LocalFileSystem.cs b/src/ImageSharp.Textures/IO/LocalFileSystem.cs index 1ff7fb9d..522486dc 100644 --- a/src/ImageSharp.Textures/IO/LocalFileSystem.cs +++ b/src/ImageSharp.Textures/IO/LocalFileSystem.cs @@ -1,19 +1,16 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.IO -{ - using System.IO; +namespace SixLabors.ImageSharp.Textures.IO; - /// - /// A wrapper around the local File apis. - /// - internal sealed class LocalFileSystem : IFileSystem - { - /// - public Stream OpenRead(string path) => File.OpenRead(path); +/// +/// A wrapper around the local File apis. +/// +internal sealed class LocalFileSystem : IFileSystem +{ + /// + public Stream OpenRead(string path) => File.OpenRead(path); - /// - public Stream Create(string path) => File.Create(path); - } + /// + public Stream Create(string path) => File.Create(path); } diff --git a/src/ImageSharp.Textures/ITexture.cs b/src/ImageSharp.Textures/ITexture.cs index 8c978350..d7ae516e 100644 --- a/src/ImageSharp.Textures/ITexture.cs +++ b/src/ImageSharp.Textures/ITexture.cs @@ -1,15 +1,12 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; +namespace SixLabors.ImageSharp.Textures; -namespace SixLabors.ImageSharp.Textures +/// +/// Interface for a texture. +/// +/// +public interface ITexture : IDisposable { - /// - /// Interface for a texture. - /// - /// - public interface ITexture : IDisposable - { - } } diff --git a/src/ImageSharp.Textures/ITextureInfo.cs b/src/ImageSharp.Textures/ITextureInfo.cs index 35443f67..76ad1ef4 100644 --- a/src/ImageSharp.Textures/ITextureInfo.cs +++ b/src/ImageSharp.Textures/ITextureInfo.cs @@ -3,27 +3,26 @@ using SixLabors.ImageSharp.Textures.Formats; -namespace SixLabors.ImageSharp.Textures +namespace SixLabors.ImageSharp.Textures; + +/// +/// Encapsulates properties that describe basic image information including dimensions, pixel type information +/// and additional metadata. +/// +public interface ITextureInfo { /// - /// Encapsulates properties that describe basic image information including dimensions, pixel type information - /// and additional metadata. + /// Gets information about the image pixels. /// - public interface ITextureInfo - { - /// - /// Gets information about the image pixels. - /// - TextureTypeInfo PixelType { get; } + TextureTypeInfo PixelType { get; } - /// - /// Gets the width. - /// - int Width { get; } + /// + /// Gets the width. + /// + int Width { get; } - /// - /// Gets the height. - /// - int Height { get; } - } + /// + /// Gets the height. + /// + int Height { get; } } diff --git a/src/ImageSharp.Textures/ImageSharp.Textures.csproj b/src/ImageSharp.Textures/ImageSharp.Textures.csproj index 38c21b95..c9948470 100644 --- a/src/ImageSharp.Textures/ImageSharp.Textures.csproj +++ b/src/ImageSharp.Textures/ImageSharp.Textures.csproj @@ -39,6 +39,7 @@ + diff --git a/src/ImageSharp.Textures/MipMap.cs b/src/ImageSharp.Textures/MipMap.cs index c0e5a518..6ca31f8e 100644 --- a/src/ImageSharp.Textures/MipMap.cs +++ b/src/ImageSharp.Textures/MipMap.cs @@ -1,17 +1,16 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures +namespace SixLabors.ImageSharp.Textures; + +/// +/// Represents a MipMap. +/// +public abstract class MipMap { /// - /// Represents a MipMap. + /// Gets the image at a given mipmap level. /// - public abstract class MipMap - { - /// - /// Gets the image at a given mipmap level. - /// - /// The image. - public abstract Image GetImage(); - } + /// The image. + public abstract Image GetImage(); } diff --git a/src/ImageSharp.Textures/MipMap{TBlock}.cs b/src/ImageSharp.Textures/MipMap{TBlock}.cs index 9d51b590..eb48a78c 100644 --- a/src/ImageSharp.Textures/MipMap{TBlock}.cs +++ b/src/ImageSharp.Textures/MipMap{TBlock}.cs @@ -3,52 +3,51 @@ using SixLabors.ImageSharp.Textures.TextureFormats.Decoding; -namespace SixLabors.ImageSharp.Textures +namespace SixLabors.ImageSharp.Textures; + +/// +/// Represents a mipmap for a specific texture format. +/// +/// The type of the texture block. +/// +public sealed class MipMap : MipMap + where TBlock : struct, IBlock { /// - /// Represents a mipmap for a specific texture format. + /// Initializes a new instance of the class. /// - /// The type of the texture block. - /// - public sealed class MipMap : MipMap - where TBlock : struct, IBlock + /// The block format. + /// The block data. + /// The width of the texture. + /// The height of the texture. + public MipMap(TBlock blockFormat, byte[] blockData, int width, int height) { - /// - /// Initializes a new instance of the class. - /// - /// The block format. - /// The block data. - /// The width of the texture. - /// The height of the texture. - public MipMap(TBlock blockFormat, byte[] blockData, int width, int height) - { - this.BlockFormat = blockFormat; - this.BlockData = blockData; - this.Width = width; - this.Height = height; - } - - /// - /// Gets the block format. - /// - public TBlock BlockFormat { get; } - - /// - /// Gets or sets the byte data for the mipmap. - /// - public byte[] BlockData { get; set; } - - /// - /// Gets the width of the mipmap. - /// - public int Width { get; } - - /// - /// Gets the height of the mipmap. - /// - public int Height { get; } - - /// - public override Image GetImage() => this.BlockFormat.GetImage(this.BlockData, this.Width, this.Height); + this.BlockFormat = blockFormat; + this.BlockData = blockData; + this.Width = width; + this.Height = height; } + + /// + /// Gets the block format. + /// + public TBlock BlockFormat { get; } + + /// + /// Gets or sets the byte data for the mipmap. + /// + public byte[] BlockData { get; set; } + + /// + /// Gets the width of the mipmap. + /// + public int Width { get; } + + /// + /// Gets the height of the mipmap. + /// + public int Height { get; } + + /// + public override Image GetImage() => this.BlockFormat.GetImage(this.BlockData, this.Width, this.Height); } diff --git a/src/ImageSharp.Textures/PixelFormats/Ayuv.cs b/src/ImageSharp.Textures/PixelFormats/Ayuv.cs index f31800b9..9b52a341 100644 --- a/src/ImageSharp.Textures/PixelFormats/Ayuv.cs +++ b/src/ImageSharp.Textures/PixelFormats/Ayuv.cs @@ -1,152 +1,149 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; -using System.Diagnostics.CodeAnalysis; using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Textures.PixelFormats +namespace SixLabors.ImageSharp.Textures.PixelFormats; + +/// +/// Pixel format for YUV 4:4:4 data. +/// +public struct Ayuv : IPixel, IPackedVector { + /// + public uint PackedValue { get; set; } + /// - /// Pixel format for YUV 4:4:4 data. + /// Gets or sets the packed representation of the Ayuv struct. /// - public struct Ayuv : IPixel, IPackedVector + public uint Yuv { - /// - public uint PackedValue { get; set; } - - /// - /// Gets or sets the packed representation of the Ayuv struct. - /// - public uint Yuv - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - readonly get => Unsafe.As(ref Unsafe.AsRef(in this)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - set => Unsafe.As(ref this) = value; - } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the parameter is equal to the parameter; otherwise, false. - /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Ayuv left, Ayuv right) => left.Equals(right); - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// + readonly get => Unsafe.As(ref Unsafe.AsRef(in this)); + [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Ayuv left, Ayuv right) => !left.Equals(right); + set => Unsafe.As(ref this) = value; + } - /// - public override readonly string ToString() - { - var vector = this.ToVector4(); - return FormattableString.Invariant($"Ayuv({vector.X:#0.##}, {vector.Y:#0.##}, {vector.Z:#0.##}, {vector.W:#0.##})"); - } + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Ayuv left, Ayuv right) => left.Equals(right); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public PixelOperations CreatePixelOperations() => new(); + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Ayuv left, Ayuv right) => !left.Equals(right); + + /// + public override readonly string ToString() + { + Vector4 vector = this.ToVector4(); + return FormattableString.Invariant($"Ayuv({vector.X:#0.##}, {vector.Y:#0.##}, {vector.Z:#0.##}, {vector.W:#0.##})"); + } - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override readonly int GetHashCode() => this.Yuv.GetHashCode(); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly PixelOperations CreatePixelOperations() => new(); - /// - public void FromArgb32(Argb32 source) => throw new NotImplementedException(); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override readonly int GetHashCode() => this.Yuv.GetHashCode(); - /// - public void FromBgr24(Bgr24 source) => throw new NotImplementedException(); + /// + public void FromArgb32(Argb32 source) => throw new NotImplementedException(); - /// - public void FromBgra32(Bgra32 source) => throw new NotImplementedException(); + /// + public void FromBgr24(Bgr24 source) => throw new NotImplementedException(); - /// - public void FromAbgr32(Abgr32 source) => throw new NotImplementedException(); + /// + public void FromBgra32(Bgra32 source) => throw new NotImplementedException(); - /// - public void FromBgra5551(Bgra5551 source) => throw new NotImplementedException(); + /// + public void FromAbgr32(Abgr32 source) => throw new NotImplementedException(); - /// - public void FromL16(L16 source) => throw new NotImplementedException(); + /// + public void FromBgra5551(Bgra5551 source) => throw new NotImplementedException(); - /// - public void FromL8(L8 source) => throw new NotImplementedException(); + /// + public void FromL16(L16 source) => throw new NotImplementedException(); - /// - public void FromLa16(La16 source) => throw new NotImplementedException(); + /// + public void FromL8(L8 source) => throw new NotImplementedException(); - /// - public void FromLa32(La32 source) => throw new NotImplementedException(); + /// + public void FromLa16(La16 source) => throw new NotImplementedException(); - /// - public void FromRgb24(Rgb24 source) => throw new NotImplementedException(); + /// + public void FromLa32(La32 source) => throw new NotImplementedException(); - /// - public void FromRgb48(Rgb48 source) => throw new NotImplementedException(); + /// + public void FromRgb24(Rgb24 source) => throw new NotImplementedException(); - /// - public void FromRgba32(Rgba32 source) => throw new NotImplementedException(); + /// + public void FromRgb48(Rgb48 source) => throw new NotImplementedException(); - /// - public void FromRgba64(Rgba64 source) => throw new NotImplementedException(); + /// + public void FromRgba32(Rgba32 source) => throw new NotImplementedException(); - /// - public void FromScaledVector4(Vector4 vector) => throw new NotImplementedException(); + /// + public void FromRgba64(Rgba64 source) => throw new NotImplementedException(); - /// - public void FromVector4(Vector4 vector) => throw new NotImplementedException(); + /// + public void FromScaledVector4(Vector4 vector) => throw new NotImplementedException(); - /// - public void ToRgba32(ref Rgba32 dest) => throw new NotImplementedException(); + /// + public void FromVector4(Vector4 vector) => throw new NotImplementedException(); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly Vector4 ToScaledVector4() => this.ToVector4(); + /// + public void ToRgba32(ref Rgba32 dest) => throw new NotImplementedException(); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly Vector4 ToVector4() - { - int v = (int)(this.PackedValue & 0xFF); - int u = (int)((this.PackedValue >> 8) & 0xFF); - int y = (int)((this.PackedValue >> 16) & 0xFF); - int a = (int)((this.PackedValue >> 24) & 0xFF); - - // http://msdn.microsoft.com/en-us/library/windows/desktop/dd206750.aspx - - // Y' = Y - 16 - // Cb' = Cb - 128 - // Cr' = Cr - 128 - y -= 16; - u -= 128; - v -= 128; - - // R = 1.1644Y' + 1.5960Cr' - // G = 1.1644Y' - 0.3917Cb' - 0.8128Cr' - // B = 1.1644Y' + 2.0172Cb' - return ColorSpaceConversion.YuvToRgba8Bit(y, u, v, a); - } - - /// - public override readonly bool Equals(object? obj) => obj is Ayuv ayuv && this.Equals(ayuv); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly bool Equals(Ayuv other) => this.Yuv.Equals(other.Yuv); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() + { + int v = (int)(this.PackedValue & 0xFF); + int u = (int)((this.PackedValue >> 8) & 0xFF); + int y = (int)((this.PackedValue >> 16) & 0xFF); + int a = (int)((this.PackedValue >> 24) & 0xFF); + + // http://msdn.microsoft.com/en-us/library/windows/desktop/dd206750.aspx + + // Y' = Y - 16 + // Cb' = Cb - 128 + // Cr' = Cr - 128 + y -= 16; + u -= 128; + v -= 128; + + // R = 1.1644Y' + 1.5960Cr' + // G = 1.1644Y' - 0.3917Cb' - 0.8128Cr' + // B = 1.1644Y' + 2.0172Cb' + return ColorSpaceConversion.YuvToRgba8Bit(y, u, v, a); } + + /// + public override readonly bool Equals(object? obj) => obj is Ayuv ayuv && this.Equals(ayuv); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly bool Equals(Ayuv other) => this.Yuv.Equals(other.Yuv); } diff --git a/src/ImageSharp.Textures/PixelFormats/ColorSpaceConversion.cs b/src/ImageSharp.Textures/PixelFormats/ColorSpaceConversion.cs index 36f78ee4..462562e3 100644 --- a/src/ImageSharp.Textures/PixelFormats/ColorSpaceConversion.cs +++ b/src/ImageSharp.Textures/PixelFormats/ColorSpaceConversion.cs @@ -1,72 +1,70 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Numerics; using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.Textures.PixelFormats +namespace SixLabors.ImageSharp.Textures.PixelFormats; + +internal static class ColorSpaceConversion { - internal static class ColorSpaceConversion + private const float Max8Bit = 256F; + + private const float Max10Bit = 1023F; + + private const float Max16Bit = 65535F; + + private static readonly Vector4 Multiplier16Bit = new(Max16Bit, Max16Bit, Max16Bit, Max16Bit); + + private static readonly Vector4 Multiplier10Bit = new(Max10Bit, Max10Bit, Max10Bit, 3F); + + private static readonly Vector4 Multiplier8Bit = new(Max8Bit, Max8Bit, Max8Bit, Max8Bit); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 YuvToRgba16Bit(uint y, uint u, uint v, uint a = ushort.MaxValue) + { + // http://msdn.microsoft.com/en-us/library/windows/desktop/bb970578.aspx + // R = 1.1689Y' + 1.6023Cr' + // G = 1.1689Y' - 0.3933Cb' - 0.8160Cr' + // B = 1.1689Y'+ 2.0251Cb' + uint r = ((76607 * y) + (105006 * v) + 32768) >> 16; + uint g = ((76607 * y) - (25772 * u) - (53477 * v) + 32768) >> 16; + uint b = ((76607 * y) + (132718 * u) + 32768) >> 16; + + return new Vector4(Clamp(r, 0, ushort.MaxValue), Clamp(g, 0, ushort.MaxValue), Clamp(b, 0, ushort.MaxValue), Clamp(a, 0, ushort.MaxValue)) / Multiplier16Bit; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 YuvToRgba10Bit(uint y, uint u, uint v, uint a = 1023) { - private const float Max8Bit = 256F; - - private const float Max10Bit = 1023F; - - private const float Max16Bit = 65535F; - - private static readonly Vector4 Multiplier16Bit = new Vector4(Max16Bit, Max16Bit, Max16Bit, Max16Bit); - - private static readonly Vector4 Multiplier10Bit = new Vector4(Max10Bit, Max10Bit, Max10Bit, 3F); - - private static readonly Vector4 Multiplier8Bit = new Vector4(Max8Bit, Max8Bit, Max8Bit, Max8Bit); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 YuvToRgba16Bit(uint y, uint u, uint v, uint a = ushort.MaxValue) - { - // http://msdn.microsoft.com/en-us/library/windows/desktop/bb970578.aspx - // R = 1.1689Y' + 1.6023Cr' - // G = 1.1689Y' - 0.3933Cb' - 0.8160Cr' - // B = 1.1689Y'+ 2.0251Cb' - uint r = ((76607 * y) + (105006 * v) + 32768) >> 16; - uint g = ((76607 * y) - (25772 * u) - (53477 * v) + 32768) >> 16; - uint b = ((76607 * y) + (132718 * u) + 32768) >> 16; - - return new Vector4(Clamp(r, 0, ushort.MaxValue), Clamp(g, 0, ushort.MaxValue), Clamp(b, 0, ushort.MaxValue), Clamp(a, 0, ushort.MaxValue)) / Multiplier16Bit; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 YuvToRgba10Bit(uint y, uint u, uint v, uint a = 1023) - { - // http://msdn.microsoft.com/en-us/library/windows/desktop/bb970578.aspx - // R = 1.1678Y' + 1.6007Cr' - // G = 1.1678Y' - 0.3929Cb' - 0.8152Cr' - // B = 1.1678Y' + 2.0232Cb' - uint r = ((76533 * y) + (104905 * v) + 32768) >> 16; - uint g = ((76533 * y) - (25747 * u) - (53425 * v) + 32768) >> 16; - uint b = ((76533 * y) + (132590 * u) + 32768) >> 16; - - return new Vector4(Clamp(r, 0, 1023), Clamp(g, 0, 1023), Clamp(b, 0, 1023), Clamp(a, 0, 1023)) / Multiplier10Bit; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 YuvToRgba8Bit(int y, int u, int v, int a = 256) - { - // http://msdn.microsoft.com/en-us/library/windows/desktop/bb970578.aspx - // R = 1.1644Y' + 1.5960Cr' - // G = 1.1644Y' - 0.3917Cb' - 0.8128Cr' - // B = 1.1644Y' + 2.0172Cb' - int r = ((298 * y) + (409 * v) + 128) >> 8; - int g = ((298 * y) - (100 * u) - (208 * v) + 128) >> 8; - int b = ((298 * y) + (516 * u) + 128) >> 8; - - return new Vector4(Clamp(r, 0, 256), Clamp(g, 0, 256), Clamp(b, 0, 256), Clamp(a, 0, 256)) / Multiplier8Bit; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint Clamp(uint val, uint min, uint max) => Math.Min(Math.Max(val, min), max); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int Clamp(int val, int min, int max) => Math.Min(Math.Max(val, min), max); + // http://msdn.microsoft.com/en-us/library/windows/desktop/bb970578.aspx + // R = 1.1678Y' + 1.6007Cr' + // G = 1.1678Y' - 0.3929Cb' - 0.8152Cr' + // B = 1.1678Y' + 2.0232Cb' + uint r = ((76533 * y) + (104905 * v) + 32768) >> 16; + uint g = ((76533 * y) - (25747 * u) - (53425 * v) + 32768) >> 16; + uint b = ((76533 * y) + (132590 * u) + 32768) >> 16; + + return new Vector4(Clamp(r, 0, 1023), Clamp(g, 0, 1023), Clamp(b, 0, 1023), Clamp(a, 0, 1023)) / Multiplier10Bit; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 YuvToRgba8Bit(int y, int u, int v, int a = 256) + { + // http://msdn.microsoft.com/en-us/library/windows/desktop/bb970578.aspx + // R = 1.1644Y' + 1.5960Cr' + // G = 1.1644Y' - 0.3917Cb' - 0.8128Cr' + // B = 1.1644Y' + 2.0172Cb' + int r = ((298 * y) + (409 * v) + 128) >> 8; + int g = ((298 * y) - (100 * u) - (208 * v) + 128) >> 8; + int b = ((298 * y) + (516 * u) + 128) >> 8; + + return new Vector4(Clamp(r, 0, 256), Clamp(g, 0, 256), Clamp(b, 0, 256), Clamp(a, 0, 256)) / Multiplier8Bit; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint Clamp(uint val, uint min, uint max) => Math.Min(Math.Max(val, min), max); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int Clamp(int val, int min, int max) => Math.Min(Math.Max(val, min), max); } diff --git a/src/ImageSharp.Textures/PixelFormats/Fp32.cs b/src/ImageSharp.Textures/PixelFormats/Fp32.cs index 828dd0f4..1f025556 100644 --- a/src/ImageSharp.Textures/PixelFormats/Fp32.cs +++ b/src/ImageSharp.Textures/PixelFormats/Fp32.cs @@ -1,159 +1,157 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Textures.PixelFormats +namespace SixLabors.ImageSharp.Textures.PixelFormats; + +/// +/// Pixel where each pixel is represented by a 32 bit float value. +/// +public struct Fp32 : IPixel, IPackedVector { /// - /// Pixel where each pixel is represented by a 32 bit float value. + /// Initializes a new instance of the struct. /// - public struct Fp32 : IPixel, IPackedVector + /// The x-component. + public Fp32(float x) + : this(new Vector(x)) { - /// - /// Initializes a new instance of the struct. - /// - /// The x-component. - public Fp32(float x) - : this(new Vector(x)) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector containing the component values. - public Fp32(Vector vector) => this.PackedValue = Pack(vector); - - /// - public float PackedValue { get; set; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the parameter is equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Fp32 left, Fp32 right) => left.Equals(right); - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Fp32 left, Fp32 right) => !left.Equals(right); - - /// - public PixelOperations CreatePixelOperations() => new(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() => this.ToVector4(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromVector4(Vector4 vector) => this.PackedValue = (ushort)Pack(new Vector(vector.X)); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() => new(this.PackedValue); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - public void ToRgba32(ref Rgba32 dest) => throw new NotImplementedException(); - - /// - /// Expands the packed representation into a . - /// The vector components are typically expanded in least to greatest significance order. - /// - /// The . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector ToVector() => new Vector(this.PackedValue); - - /// - public override readonly bool Equals(object? obj) => obj is Fp32 other && this.Equals(other); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly bool Equals(Fp32 other) => this.PackedValue.Equals(other.PackedValue); - - /// - public override string ToString() - { - var vector = this.ToVector(); - return FormattableString.Invariant($"Fp32({vector:#0.##}"); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); + } + + /// + /// Initializes a new instance of the struct. + /// + /// The vector containing the component values. + public Fp32(Vector vector) => this.PackedValue = Pack(vector); + + /// + public float PackedValue { get; set; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Fp32 left, Fp32 right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Fp32 left, Fp32 right) => !left.Equals(right); + + /// + public readonly PixelOperations CreatePixelOperations() => new(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromVector4(Vector4 vector) => this.PackedValue = (ushort)Pack(new Vector(vector.X)); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new(this.PackedValue); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static float Pack(Vector vector) => vector[0]; + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + public void ToRgba32(ref Rgba32 dest) => throw new NotImplementedException(); + + /// + /// Expands the packed representation into a . + /// The vector components are typically expanded in least to greatest significance order. + /// + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector ToVector() => new(this.PackedValue); + + /// + public override readonly bool Equals(object? obj) => obj is Fp32 other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly bool Equals(Fp32 other) => this.PackedValue.Equals(other.PackedValue); + + /// + public override string ToString() + { + Vector vector = this.ToVector(); + return FormattableString.Invariant($"Fp32({vector:#0.##}"); } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float Pack(Vector vector) => vector[0]; } diff --git a/src/ImageSharp.Textures/PixelFormats/Generated/Bgr32.cs b/src/ImageSharp.Textures/PixelFormats/Generated/Bgr32.cs index bac1a29c..d88d9841 100644 --- a/src/ImageSharp.Textures/PixelFormats/Generated/Bgr32.cs +++ b/src/ImageSharp.Textures/PixelFormats/Generated/Bgr32.cs @@ -1,175 +1,173 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Textures.PixelFormats +namespace SixLabors.ImageSharp.Textures.PixelFormats; + +/// +/// Packed pixel type containing unsigned normalized values ranging from 0 to 1. +/// The x, y and z components use 8 bits. +/// +/// Ranges from [0, 0, 0] to [1, 1, 1] in vector form. +/// +/// +public partial struct Bgr32 : IPixel, IPackedVector { /// - /// Packed pixel type containing unsigned normalized values ranging from 0 to 1. - /// The x, y and z components use 8 bits. - /// - /// Ranges from [0, 0, 0] to [1, 1, 1] in vector form. - /// + /// Initializes a new instance of the struct. + /// + /// The x-component + /// The y-component + /// The z-component + public Bgr32(float x, float y, float z) + : this(new Vector3(x, y, z)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// + /// The vector containing the components for the packed vector. + /// + public Bgr32(Vector3 vector) => this.PackedValue = Pack(ref vector); + + /// + public uint PackedValue { get; set; } + + /// + /// Compares two objects for equality. /// - public partial struct Bgr32 : IPixel, IPackedVector + /// The on the left side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + /// The on the right side of the operand. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Bgr32 left, Bgr32 right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Bgr32 left, Bgr32 right) => !left.Equals(right); + + /// + public readonly PixelOperations CreatePixelOperations() => new(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromVector4(Vector4 vector) + { + Vector3 vector3 = new Vector3(vector.X, vector.Y, vector.Z); + this.PackedValue = Pack(ref vector3); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new( + ((this.PackedValue >> 16) & 255) / 255F, + ((this.PackedValue >> 8) & 255) / 255F, + (this.PackedValue & 255) / 255F, + 1.0f); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + public override bool Equals(object? obj) => obj is Bgr32 other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly bool Equals(Bgr32 other) => this.PackedValue.Equals(other.PackedValue); + + /// + public override string ToString() + { + Vector4 vector = this.ToVector4(); + return FormattableString.Invariant($"Bgr32({vector.Z:#0.##}, {vector.Y:#0.##}, {vector.X:#0.##})"); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint Pack(ref Vector3 vector) { - /// - /// Initializes a new instance of the struct. - /// - /// The x-component - /// The y-component - /// The z-component - public Bgr32(float x, float y, float z) - : this(new Vector3(x, y, z)) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// - /// The vector containing the components for the packed vector. - /// - public Bgr32(Vector3 vector) => this.PackedValue = Pack(ref vector); - - /// - public uint PackedValue { get; set; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// - /// True if the parameter is equal to the parameter; otherwise, false. - /// - /// The on the right side of the operand. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Bgr32 left, Bgr32 right) => left.Equals(right); - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Bgr32 left, Bgr32 right) => !left.Equals(right); - - /// - public PixelOperations CreatePixelOperations() => new(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() => this.ToVector4(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromVector4(Vector4 vector) - { - var vector3 = new Vector3(vector.X, vector.Y, vector.Z); - this.PackedValue = Pack(ref vector3); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() => new( - ((this.PackedValue >> 16) & 255) / 255F, - ((this.PackedValue >> 8) & 255) / 255F, - (this.PackedValue & 255) / 255F, - 1.0f); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - public override bool Equals(object? obj) => obj is Bgr32 other && this.Equals(other); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Bgr32 other) => this.PackedValue.Equals(other.PackedValue); - - /// - public override string ToString() - { - var vector = this.ToVector4(); - return FormattableString.Invariant($"Bgr32({vector.Z:#0.##}, {vector.Y:#0.##}, {vector.X:#0.##})"); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => this.PackedValue.GetHashCode(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint Pack(ref Vector3 vector) - { - vector = Vector3.Clamp(vector, Vector3.Zero, Vector3.One); - return (uint)( - (((uint)Math.Round(vector.X * 255F) & 255) << 16) - | (((uint)Math.Round(vector.Y * 255F) & 255) << 8) - | ((uint)Math.Round(vector.Z * 255F) & 255)); - } + vector = Vector3.Clamp(vector, Vector3.Zero, Vector3.One); + return + (((uint)Math.Round(vector.X * 255F) & 255) << 16) + | (((uint)Math.Round(vector.Y * 255F) & 255) << 8) + | ((uint)Math.Round(vector.Z * 255F) & 255); } } diff --git a/src/ImageSharp.Textures/PixelFormats/Generated/Bgr555.cs b/src/ImageSharp.Textures/PixelFormats/Generated/Bgr555.cs index 3eb78bc8..9dc52e75 100644 --- a/src/ImageSharp.Textures/PixelFormats/Generated/Bgr555.cs +++ b/src/ImageSharp.Textures/PixelFormats/Generated/Bgr555.cs @@ -1,175 +1,173 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Textures.PixelFormats +namespace SixLabors.ImageSharp.Textures.PixelFormats; + +/// +/// Packed pixel type containing unsigned normalized values ranging from 0 to 1. +/// The x, y and z components use 5 bits. +/// +/// Ranges from [0, 0, 0] to [1, 1, 1] in vector form. +/// +/// +public partial struct Bgr555 : IPixel, IPackedVector { /// - /// Packed pixel type containing unsigned normalized values ranging from 0 to 1. - /// The x, y and z components use 5 bits. - /// - /// Ranges from [0, 0, 0] to [1, 1, 1] in vector form. - /// + /// Initializes a new instance of the struct. + /// + /// The x-component + /// The y-component + /// The z-component + public Bgr555(float x, float y, float z) + : this(new Vector3(x, y, z)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// + /// The vector containing the components for the packed vector. + /// + public Bgr555(Vector3 vector) => this.PackedValue = Pack(ref vector); + + /// + public ushort PackedValue { get; set; } + + /// + /// Compares two objects for equality. /// - public partial struct Bgr555 : IPixel, IPackedVector + /// The on the left side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + /// The on the right side of the operand. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Bgr555 left, Bgr555 right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Bgr555 left, Bgr555 right) => !left.Equals(right); + + /// + public readonly PixelOperations CreatePixelOperations() => new(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromVector4(Vector4 vector) + { + Vector3 vector3 = new Vector3(vector.X, vector.Y, vector.Z); + this.PackedValue = Pack(ref vector3); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new( + ((this.PackedValue >> 10) & 31) / 31F, + ((this.PackedValue >> 5) & 31) / 31F, + (this.PackedValue & 31) / 31F, + 1.0f); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + public override bool Equals(object? obj) => obj is Bgr555 other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly bool Equals(Bgr555 other) => this.PackedValue.Equals(other.PackedValue); + + /// + public override string ToString() + { + Vector4 vector = this.ToVector4(); + return FormattableString.Invariant($"Bgr555({vector.Z:#0.##}, {vector.Y:#0.##}, {vector.X:#0.##})"); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ushort Pack(ref Vector3 vector) { - /// - /// Initializes a new instance of the struct. - /// - /// The x-component - /// The y-component - /// The z-component - public Bgr555(float x, float y, float z) - : this(new Vector3(x, y, z)) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// - /// The vector containing the components for the packed vector. - /// - public Bgr555(Vector3 vector) => this.PackedValue = Pack(ref vector); - - /// - public ushort PackedValue { get; set; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// - /// True if the parameter is equal to the parameter; otherwise, false. - /// - /// The on the right side of the operand. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Bgr555 left, Bgr555 right) => left.Equals(right); - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Bgr555 left, Bgr555 right) => !left.Equals(right); - - /// - public PixelOperations CreatePixelOperations() => new(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() => this.ToVector4(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromVector4(Vector4 vector) - { - var vector3 = new Vector3(vector.X, vector.Y, vector.Z); - this.PackedValue = Pack(ref vector3); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() => new( - ((this.PackedValue >> 10) & 31) / 31F, - ((this.PackedValue >> 5) & 31) / 31F, - (this.PackedValue & 31) / 31F, - 1.0f); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - public override bool Equals(object? obj) => obj is Bgr555 other && this.Equals(other); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Bgr555 other) => this.PackedValue.Equals(other.PackedValue); - - /// - public override string ToString() - { - var vector = this.ToVector4(); - return FormattableString.Invariant($"Bgr555({vector.Z:#0.##}, {vector.Y:#0.##}, {vector.X:#0.##})"); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => this.PackedValue.GetHashCode(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ushort Pack(ref Vector3 vector) - { - vector = Vector3.Clamp(vector, Vector3.Zero, Vector3.One); - return (ushort)( - (((uint)Math.Round(vector.X * 31F) & 31) << 10) - | (((uint)Math.Round(vector.Y * 31F) & 31) << 5) - | ((uint)Math.Round(vector.Z * 31F) & 31)); - } + vector = Vector3.Clamp(vector, Vector3.Zero, Vector3.One); + return (ushort)( + (((uint)Math.Round(vector.X * 31F) & 31) << 10) + | (((uint)Math.Round(vector.Y * 31F) & 31) << 5) + | ((uint)Math.Round(vector.Z * 31F) & 31)); } } diff --git a/src/ImageSharp.Textures/PixelFormats/Generated/D32_FLOAT_S8X24_UINT.cs b/src/ImageSharp.Textures/PixelFormats/Generated/D32_FLOAT_S8X24_UINT.cs index 5c8c2fb6..50e8ce7e 100644 --- a/src/ImageSharp.Textures/PixelFormats/Generated/D32_FLOAT_S8X24_UINT.cs +++ b/src/ImageSharp.Textures/PixelFormats/Generated/D32_FLOAT_S8X24_UINT.cs @@ -1,179 +1,174 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Textures.Common.Helpers; -namespace SixLabors.ImageSharp.Textures.PixelFormats +namespace SixLabors.ImageSharp.Textures.PixelFormats; + +/// +/// Packed pixel type containing unsigned normalized values ranging from 0 to 1. +/// The x and y components uses 32 and 8 bits respectively. +/// +/// Ranges from [0, 0] to [1, 1] in vector form. +/// +/// +#pragma warning disable CA1707 // Identifiers should not contain underscores +public partial struct D32_FLOAT_S8X24_UINT : IPixel, IPackedVector +#pragma warning restore CA1707 // Identifiers should not contain underscores { /// - /// Packed pixel type containing unsigned normalized values ranging from 0 to 1. - /// The x and y components uses 32 and 8 bits respectively. - /// - /// Ranges from [0, 0] to [1, 1] in vector form. - /// + /// Initializes a new instance of the struct. /// -#pragma warning disable CA1707 // Identifiers should not contain underscores - public partial struct D32_FLOAT_S8X24_UINT : IPixel, IPackedVector -#pragma warning restore CA1707 // Identifiers should not contain underscores + /// The x-component + /// The y-component + public D32_FLOAT_S8X24_UINT(float x, float y) + : this(new Vector2(x, y)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// + /// The vector containing the components for the packed vector. + /// + public D32_FLOAT_S8X24_UINT(Vector2 vector) => this.PackedValue = Pack(ref vector); + + /// + public ulong PackedValue { get; set; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + /// The on the right side of the operand. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(D32_FLOAT_S8X24_UINT left, D32_FLOAT_S8X24_UINT right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(D32_FLOAT_S8X24_UINT left, D32_FLOAT_S8X24_UINT right) => !left.Equals(right); + + /// + public readonly PixelOperations CreatePixelOperations() => new(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromVector4(Vector4 vector) + { + Vector2 vector2 = new Vector2(vector.X, vector.Y); + this.PackedValue = Pack(ref vector2); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new( + FloatHelper.UnpackFloat32ToFloat((uint)(this.PackedValue & 4294967295)), + ((this.PackedValue >> 32) & 255) / 255F, + 0.0f, + 1.0f); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + public override bool Equals(object? obj) => obj is D32_FLOAT_S8X24_UINT other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly bool Equals(D32_FLOAT_S8X24_UINT other) => this.PackedValue.Equals(other.PackedValue); + + /// + public override string ToString() + { + Vector4 vector = this.ToVector4(); + return FormattableString.Invariant($"D32_FLOAT_S8X24_UINT({vector.X:#0.##}, {vector.Y:#0.##})"); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong Pack(ref Vector2 vector) { - /// - /// Initializes a new instance of the struct. - /// - /// The x-component - /// The y-component - public D32_FLOAT_S8X24_UINT(float x, float y) - : this(new Vector2(x, y)) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// - /// The vector containing the components for the packed vector. - /// - public D32_FLOAT_S8X24_UINT(Vector2 vector) => this.PackedValue = Pack(ref vector); - - /// - public ulong PackedValue { get; set; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// - /// True if the parameter is equal to the parameter; otherwise, false. - /// - /// The on the right side of the operand. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(D32_FLOAT_S8X24_UINT left, D32_FLOAT_S8X24_UINT right) => left.Equals(right); - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(D32_FLOAT_S8X24_UINT left, D32_FLOAT_S8X24_UINT right) => !left.Equals(right); - - /// - public PixelOperations CreatePixelOperations() => new PixelOperations(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() => this.ToVector4(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromVector4(Vector4 vector) - { - var vector2 = new Vector2(vector.X, vector.Y); - this.PackedValue = Pack(ref vector2); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() - { - return new Vector4( - FloatHelper.UnpackFloat32ToFloat((uint)(this.PackedValue & 4294967295)), - ((this.PackedValue >> 32) & 255) / 255F, - 0.0f, - 1.0f); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - public override bool Equals(object? obj) => obj is D32_FLOAT_S8X24_UINT other && this.Equals(other); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(D32_FLOAT_S8X24_UINT other) => this.PackedValue.Equals(other.PackedValue); - - /// - public override string ToString() - { - var vector = this.ToVector4(); - return FormattableString.Invariant($"D32_FLOAT_S8X24_UINT({vector.X:#0.##}, {vector.Y:#0.##})"); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => this.PackedValue.GetHashCode(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ulong Pack(ref Vector2 vector) - { - vector = Vector2.Clamp(vector, Vector2.Zero, Vector2.One); - return (ulong)( - (uint)FloatHelper.PackFloatToFloat32(vector.X) - | (((uint)Math.Round(vector.Y * 255F) & 255) << 32)); - } + vector = Vector2.Clamp(vector, Vector2.Zero, Vector2.One); + return + FloatHelper.PackFloatToFloat32(vector.X) + | (((uint)Math.Round(vector.Y * 255F) & 255) << 32); } } diff --git a/src/ImageSharp.Textures/PixelFormats/Generated/R11G11B10Float.cs b/src/ImageSharp.Textures/PixelFormats/Generated/R11G11B10Float.cs index 0be3301f..c7c6c1a8 100644 --- a/src/ImageSharp.Textures/PixelFormats/Generated/R11G11B10Float.cs +++ b/src/ImageSharp.Textures/PixelFormats/Generated/R11G11B10Float.cs @@ -1,182 +1,174 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Textures.Common.Helpers; -namespace SixLabors.ImageSharp.Textures.PixelFormats +namespace SixLabors.ImageSharp.Textures.PixelFormats; + +/// +/// Packed pixel type containing unsigned normalized values ranging from 0 to 1. +/// The x, y and z components uses 11, 11 and 10 bits respectively. +/// +/// Ranges from [0, 0, 0] to [1, 1, 1] in vector form. +/// +/// +public partial struct R11G11B10Float : IPixel, IPackedVector { /// - /// Packed pixel type containing unsigned normalized values ranging from 0 to 1. - /// The x, y and z components uses 11, 11 and 10 bits respectively. - /// - /// Ranges from [0, 0, 0] to [1, 1, 1] in vector form. - /// + /// Initializes a new instance of the struct. + /// + /// The x-component + /// The y-component + /// The z-component + public R11G11B10Float(float x, float y, float z) + : this(new Vector3(x, y, z)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// + /// The vector containing the components for the packed vector. + /// + public R11G11B10Float(Vector3 vector) => this.PackedValue = Pack(ref vector); + + /// + public uint PackedValue { get; set; } + + /// + /// Compares two objects for equality. /// - public partial struct R11G11B10Float : IPixel, IPackedVector + /// The on the left side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + /// The on the right side of the operand. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(R11G11B10Float left, R11G11B10Float right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(R11G11B10Float left, R11G11B10Float right) => !left.Equals(right); + + /// + public readonly PixelOperations CreatePixelOperations() => new(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromVector4(Vector4 vector) + { + Vector3 vector3 = new Vector3(vector.X, vector.Y, vector.Z); + this.PackedValue = Pack(ref vector3); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new( + FloatHelper.UnpackFloat11ToFloat((ushort)(this.PackedValue & 2047)), + FloatHelper.UnpackFloat11ToFloat((ushort)((this.PackedValue >> 11) & 2047)), + FloatHelper.UnpackFloat10ToFloat((ushort)((this.PackedValue >> 22) & 1023)), + 1.0f); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + public override bool Equals(object? obj) => obj is R11G11B10Float other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly bool Equals(R11G11B10Float other) => this.PackedValue.Equals(other.PackedValue); + + /// + public override string ToString() + { + Vector4 vector = this.ToVector4(); + return FormattableString.Invariant($"R11G11B10Float({vector.X:#0.##}, {vector.Y:#0.##}, {vector.Z:#0.##})"); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint Pack(ref Vector3 vector) { - /// - /// Initializes a new instance of the struct. - /// - /// The x-component - /// The y-component - /// The z-component - public R11G11B10Float(float x, float y, float z) - : this(new Vector3(x, y, z)) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// - /// The vector containing the components for the packed vector. - /// - public R11G11B10Float(Vector3 vector) => this.PackedValue = Pack(ref vector); - - /// - public uint PackedValue { get; set; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// - /// True if the parameter is equal to the parameter; otherwise, false. - /// - /// The on the right side of the operand. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(R11G11B10Float left, R11G11B10Float right) => left.Equals(right); - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(R11G11B10Float left, R11G11B10Float right) => !left.Equals(right); - - /// - public PixelOperations CreatePixelOperations() => new PixelOperations(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() => this.ToVector4(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromVector4(Vector4 vector) - { - var vector3 = new Vector3(vector.X, vector.Y, vector.Z); - this.PackedValue = Pack(ref vector3); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() - { - return new Vector4( - FloatHelper.UnpackFloat11ToFloat((ushort)(this.PackedValue & 2047)), - FloatHelper.UnpackFloat11ToFloat((ushort)((this.PackedValue >> 11) & 2047)), - FloatHelper.UnpackFloat10ToFloat((ushort)((this.PackedValue >> 22) & 1023)), - 1.0f); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba32(ref Rgba32 dest) - { - dest.FromScaledVector4(this.ToScaledVector4()); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - public override bool Equals(object? obj) => obj is R11G11B10Float other && this.Equals(other); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(R11G11B10Float other) => this.PackedValue.Equals(other.PackedValue); - - /// - public override string ToString() - { - var vector = this.ToVector4(); - return FormattableString.Invariant($"R11G11B10Float({vector.X:#0.##}, {vector.Y:#0.##}, {vector.Z:#0.##})"); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => this.PackedValue.GetHashCode(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint Pack(ref Vector3 vector) - { - vector = Vector3.Clamp(vector, Vector3.Zero, Vector3.One); - return (uint)( - (uint)FloatHelper.PackFloatToFloat11(vector.X) - | ((uint)FloatHelper.PackFloatToFloat11(vector.Y) << 11) - | ((uint)FloatHelper.PackFloatToFloat10(vector.Z) << 22)); - } + vector = Vector3.Clamp(vector, Vector3.Zero, Vector3.One); + return + FloatHelper.PackFloatToFloat11(vector.X) + | (FloatHelper.PackFloatToFloat11(vector.Y) << 11) + | (FloatHelper.PackFloatToFloat10(vector.Z) << 22); } } diff --git a/src/ImageSharp.Textures/PixelFormats/Generated/Rg32Float.cs b/src/ImageSharp.Textures/PixelFormats/Generated/Rg32Float.cs index 36100cf1..d493f509 100644 --- a/src/ImageSharp.Textures/PixelFormats/Generated/Rg32Float.cs +++ b/src/ImageSharp.Textures/PixelFormats/Generated/Rg32Float.cs @@ -1,174 +1,172 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Textures.Common.Helpers; -namespace SixLabors.ImageSharp.Textures.PixelFormats +namespace SixLabors.ImageSharp.Textures.PixelFormats; + +/// +/// Packed pixel type containing unsigned normalized values ranging from 0 to 1. +/// The x and y components use 16 bits. +/// +/// Ranges from [0, 0] to [1, 1] in vector form. +/// +/// +public partial struct Rg32Float : IPixel, IPackedVector { /// - /// Packed pixel type containing unsigned normalized values ranging from 0 to 1. - /// The x and y components use 16 bits. - /// - /// Ranges from [0, 0] to [1, 1] in vector form. - /// + /// Initializes a new instance of the struct. + /// + /// The x-component + /// The y-component + public Rg32Float(float x, float y) + : this(new Vector2(x, y)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// + /// The vector containing the components for the packed vector. + /// + public Rg32Float(Vector2 vector) => this.PackedValue = Pack(ref vector); + + /// + public uint PackedValue { get; set; } + + /// + /// Compares two objects for equality. /// - public partial struct Rg32Float : IPixel, IPackedVector + /// The on the left side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + /// The on the right side of the operand. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Rg32Float left, Rg32Float right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Rg32Float left, Rg32Float right) => !left.Equals(right); + + /// + public readonly PixelOperations CreatePixelOperations() => new(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromVector4(Vector4 vector) + { + Vector2 vector2 = new Vector2(vector.X, vector.Y); + this.PackedValue = Pack(ref vector2); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new( + FloatHelper.UnpackFloat16ToFloat((ushort)(this.PackedValue & 65535)), + FloatHelper.UnpackFloat16ToFloat((ushort)((this.PackedValue >> 16) & 65535)), + 0.0f, + 1.0f); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + public override bool Equals(object? obj) => obj is Rg32Float other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly bool Equals(Rg32Float other) => this.PackedValue.Equals(other.PackedValue); + + /// + public override string ToString() + { + Vector4 vector = this.ToVector4(); + return FormattableString.Invariant($"Rg32Float({vector.X:#0.##}, {vector.Y:#0.##})"); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint Pack(ref Vector2 vector) { - /// - /// Initializes a new instance of the struct. - /// - /// The x-component - /// The y-component - public Rg32Float(float x, float y) - : this(new Vector2(x, y)) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// - /// The vector containing the components for the packed vector. - /// - public Rg32Float(Vector2 vector) => this.PackedValue = Pack(ref vector); - - /// - public uint PackedValue { get; set; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// - /// True if the parameter is equal to the parameter; otherwise, false. - /// - /// The on the right side of the operand. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Rg32Float left, Rg32Float right) => left.Equals(right); - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Rg32Float left, Rg32Float right) => !left.Equals(right); - - /// - public PixelOperations CreatePixelOperations() => new(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() => this.ToVector4(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromVector4(Vector4 vector) - { - var vector2 = new Vector2(vector.X, vector.Y); - this.PackedValue = Pack(ref vector2); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() => new( - FloatHelper.UnpackFloat16ToFloat((ushort)(this.PackedValue & 65535)), - FloatHelper.UnpackFloat16ToFloat((ushort)((this.PackedValue >> 16) & 65535)), - 0.0f, - 1.0f); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - public override bool Equals(object? obj) => obj is Rg32Float other && this.Equals(other); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Rg32Float other) => this.PackedValue.Equals(other.PackedValue); - - /// - public override string ToString() - { - var vector = this.ToVector4(); - return FormattableString.Invariant($"Rg32Float({vector.X:#0.##}, {vector.Y:#0.##})"); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => this.PackedValue.GetHashCode(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint Pack(ref Vector2 vector) - { - vector = Vector2.Clamp(vector, Vector2.Zero, Vector2.One); - return (uint)( - (uint)FloatHelper.PackFloatToFloat16(vector.X) - | ((uint)FloatHelper.PackFloatToFloat16(vector.Y) << 16)); - } + vector = Vector2.Clamp(vector, Vector2.Zero, Vector2.One); + return + FloatHelper.PackFloatToFloat16(vector.X) + | (FloatHelper.PackFloatToFloat16(vector.Y) << 16); } } diff --git a/src/ImageSharp.Textures/PixelFormats/Generated/Rg64.cs b/src/ImageSharp.Textures/PixelFormats/Generated/Rg64.cs index fd444284..e85aa415 100644 --- a/src/ImageSharp.Textures/PixelFormats/Generated/Rg64.cs +++ b/src/ImageSharp.Textures/PixelFormats/Generated/Rg64.cs @@ -1,165 +1,163 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Textures.PixelFormats +namespace SixLabors.ImageSharp.Textures.PixelFormats; + +/// +/// Pixel type containing two 32-bit unsigned normalized values ranging from 0 to 4294967295. +/// The color components are stored in red, green +/// +/// Ranges from [0, 0] to [1, 1] in vector form. +/// +/// +[StructLayout(LayoutKind.Explicit)] +public partial struct Rg64 : IPixel { /// - /// Pixel type containing two 32-bit unsigned normalized values ranging from 0 to 4294967295. - /// The color components are stored in red, green - /// - /// Ranges from [0, 0] to [1, 1] in vector form. - /// + /// Gets or sets the red component. /// - [StructLayout(LayoutKind.Explicit)] - public partial struct Rg64 : IPixel + [FieldOffset(0)] + public uint R; + + /// + /// Gets or sets the green component. + /// + [FieldOffset(4)] + public uint G; + + /// + /// Initializes a new instance of the struct. + /// + /// The red component. + /// The green component. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Rg64(uint r, uint g) { - /// - /// Gets or sets the red component. - /// - [FieldOffset(0)] - public uint R; - - /// - /// Gets or sets the green component. - /// - [FieldOffset(4)] - public uint G; - - /// - /// Initializes a new instance of the struct. - /// - /// The red component. - /// The green component. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Rg64(uint r, uint g) - { - this.R = r; - this.G = g; - } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// - /// True if the parameter is equal to the parameter; otherwise, false. - /// - /// The on the right side of the operand. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Rg64 left, Rg64 right) => left.Equals(right); - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Rg64 left, Rg64 right) => !left.Equals(right); - - /// - public PixelOperations CreatePixelOperations() => new(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() => this.ToVector4(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromVector4(Vector4 vector) - { - this.R = (uint)(vector.X * 4294967295); - this.G = (uint)(vector.Y * 4294967295); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() => new( - this.R / 4294967295F, - this.G / 4294967295F, - 0.0f, - 1.0f); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - public override bool Equals(object? obj) => obj is Rg64 other && this.Equals(other); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Rg64 other) => this.R.Equals(other.R) && this.G.Equals(other.G); - - /// - public override string ToString() => FormattableString.Invariant($"Rg64({this.R}, {this.G})"); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => HashCode.Combine(this.R, this.G); + this.R = r; + this.G = g; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + /// The on the right side of the operand. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Rg64 left, Rg64 right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Rg64 left, Rg64 right) => !left.Equals(right); + + /// + public readonly PixelOperations CreatePixelOperations() => new(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromVector4(Vector4 vector) + { + this.R = (uint)(vector.X * 4294967295); + this.G = (uint)(vector.Y * 4294967295); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new( + this.R / 4294967295F, + this.G / 4294967295F, + 0.0f, + 1.0f); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + public override bool Equals(object? obj) => obj is Rg64 other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly bool Equals(Rg64 other) => this.R.Equals(other.R) && this.G.Equals(other.G); + + /// + public override readonly string ToString() => FormattableString.Invariant($"Rg64({this.R}, {this.G})"); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override readonly int GetHashCode() => HashCode.Combine(this.R, this.G); } diff --git a/src/ImageSharp.Textures/PixelFormats/Generated/Rg64Float.cs b/src/ImageSharp.Textures/PixelFormats/Generated/Rg64Float.cs index 7eb39b56..8dcfc88a 100644 --- a/src/ImageSharp.Textures/PixelFormats/Generated/Rg64Float.cs +++ b/src/ImageSharp.Textures/PixelFormats/Generated/Rg64Float.cs @@ -1,165 +1,163 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Textures.PixelFormats +namespace SixLabors.ImageSharp.Textures.PixelFormats; + +/// +/// Pixel type containing two 32-bit unsigned normalized values ranging from 0 to 4294967295. +/// The color components are stored in red, green +/// +/// Ranges from [0, 0] to [1, 1] in vector form. +/// +/// +[StructLayout(LayoutKind.Explicit)] +public partial struct Rg64Float : IPixel { /// - /// Pixel type containing two 32-bit unsigned normalized values ranging from 0 to 4294967295. - /// The color components are stored in red, green - /// - /// Ranges from [0, 0] to [1, 1] in vector form. - /// + /// Gets or sets the red component. /// - [StructLayout(LayoutKind.Explicit)] - public partial struct Rg64Float : IPixel + [FieldOffset(0)] + public float R; + + /// + /// Gets or sets the green component. + /// + [FieldOffset(4)] + public float G; + + /// + /// Initializes a new instance of the struct. + /// + /// The red component. + /// The green component. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Rg64Float(float r, float g) { - /// - /// Gets or sets the red component. - /// - [FieldOffset(0)] - public float R; - - /// - /// Gets or sets the green component. - /// - [FieldOffset(4)] - public float G; - - /// - /// Initializes a new instance of the struct. - /// - /// The red component. - /// The green component. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Rg64Float(float r, float g) - { - this.R = r; - this.G = g; - } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// - /// True if the parameter is equal to the parameter; otherwise, false. - /// - /// The on the right side of the operand. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Rg64Float left, Rg64Float right) => left.Equals(right); - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Rg64Float left, Rg64Float right) => !left.Equals(right); - - /// - public PixelOperations CreatePixelOperations() => new(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() => this.ToVector4(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromVector4(Vector4 vector) - { - this.R = vector.X; - this.G = vector.Y; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() => new( - this.R, - this.G, - 0.0f, - 1.0f); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - public override bool Equals(object? obj) => obj is Rg64Float other && this.Equals(other); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Rg64Float other) => this.R.Equals(other.R) && this.G.Equals(other.G); - - /// - public override string ToString() => FormattableString.Invariant($"Rg64Float({this.R}, {this.G})"); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => HashCode.Combine(this.R, this.G); + this.R = r; + this.G = g; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + /// The on the right side of the operand. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Rg64Float left, Rg64Float right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Rg64Float left, Rg64Float right) => !left.Equals(right); + + /// + public readonly PixelOperations CreatePixelOperations() => new(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromVector4(Vector4 vector) + { + this.R = vector.X; + this.G = vector.Y; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new( + this.R, + this.G, + 0.0f, + 1.0f); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + public override bool Equals(object? obj) => obj is Rg64Float other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly bool Equals(Rg64Float other) => this.R.Equals(other.R) && this.G.Equals(other.G); + + /// + public override readonly string ToString() => FormattableString.Invariant($"Rg64Float({this.R}, {this.G})"); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override readonly int GetHashCode() => HashCode.Combine(this.R, this.G); } diff --git a/src/ImageSharp.Textures/PixelFormats/Generated/Rgb32.cs b/src/ImageSharp.Textures/PixelFormats/Generated/Rgb32.cs index 4791df50..25aa16e6 100644 --- a/src/ImageSharp.Textures/PixelFormats/Generated/Rgb32.cs +++ b/src/ImageSharp.Textures/PixelFormats/Generated/Rgb32.cs @@ -1,175 +1,173 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Textures.PixelFormats +namespace SixLabors.ImageSharp.Textures.PixelFormats; + +/// +/// Packed pixel type containing unsigned normalized values ranging from 0 to 1. +/// The x, y and z components use 8 bits. +/// +/// Ranges from [0, 0, 0] to [1, 1, 1] in vector form. +/// +/// +public partial struct Rgb32 : IPixel, IPackedVector { /// - /// Packed pixel type containing unsigned normalized values ranging from 0 to 1. - /// The x, y and z components use 8 bits. - /// - /// Ranges from [0, 0, 0] to [1, 1, 1] in vector form. - /// + /// Initializes a new instance of the struct. + /// + /// The x-component + /// The y-component + /// The z-component + public Rgb32(float x, float y, float z) + : this(new Vector3(x, y, z)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// + /// The vector containing the components for the packed vector. + /// + public Rgb32(Vector3 vector) => this.PackedValue = Pack(ref vector); + + /// + public uint PackedValue { get; set; } + + /// + /// Compares two objects for equality. /// - public partial struct Rgb32 : IPixel, IPackedVector + /// The on the left side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + /// The on the right side of the operand. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Rgb32 left, Rgb32 right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Rgb32 left, Rgb32 right) => !left.Equals(right); + + /// + public readonly PixelOperations CreatePixelOperations() => new(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromVector4(Vector4 vector) + { + Vector3 vector3 = new Vector3(vector.X, vector.Y, vector.Z); + this.PackedValue = Pack(ref vector3); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new( + (this.PackedValue & 255) / 255F, + ((this.PackedValue >> 8) & 255) / 255F, + ((this.PackedValue >> 16) & 255) / 255F, + 1.0f); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + public override bool Equals(object? obj) => obj is Rgb32 other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly bool Equals(Rgb32 other) => this.PackedValue.Equals(other.PackedValue); + + /// + public override string ToString() + { + Vector4 vector = this.ToVector4(); + return FormattableString.Invariant($"Rgb32({vector.X:#0.##}, {vector.Y:#0.##}, {vector.Z:#0.##})"); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint Pack(ref Vector3 vector) { - /// - /// Initializes a new instance of the struct. - /// - /// The x-component - /// The y-component - /// The z-component - public Rgb32(float x, float y, float z) - : this(new Vector3(x, y, z)) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// - /// The vector containing the components for the packed vector. - /// - public Rgb32(Vector3 vector) => this.PackedValue = Pack(ref vector); - - /// - public uint PackedValue { get; set; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// - /// True if the parameter is equal to the parameter; otherwise, false. - /// - /// The on the right side of the operand. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Rgb32 left, Rgb32 right) => left.Equals(right); - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Rgb32 left, Rgb32 right) => !left.Equals(right); - - /// - public PixelOperations CreatePixelOperations() => new(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() => this.ToVector4(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromVector4(Vector4 vector) - { - var vector3 = new Vector3(vector.X, vector.Y, vector.Z); - this.PackedValue = Pack(ref vector3); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() => new( - (this.PackedValue & 255) / 255F, - ((this.PackedValue >> 8) & 255) / 255F, - ((this.PackedValue >> 16) & 255) / 255F, - 1.0f); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - public override bool Equals(object? obj) => obj is Rgb32 other && this.Equals(other); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Rgb32 other) => this.PackedValue.Equals(other.PackedValue); - - /// - public override string ToString() - { - var vector = this.ToVector4(); - return FormattableString.Invariant($"Rgb32({vector.X:#0.##}, {vector.Y:#0.##}, {vector.Z:#0.##})"); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => this.PackedValue.GetHashCode(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint Pack(ref Vector3 vector) - { - vector = Vector3.Clamp(vector, Vector3.Zero, Vector3.One); - return (uint)( - ((uint)Math.Round(vector.X * 255F) & 255) - | (((uint)Math.Round(vector.Y * 255F) & 255) << 8) - | (((uint)Math.Round(vector.Z * 255F) & 255) << 16)); - } + vector = Vector3.Clamp(vector, Vector3.Zero, Vector3.One); + return + ((uint)Math.Round(vector.X * 255F) & 255) + | (((uint)Math.Round(vector.Y * 255F) & 255) << 8) + | (((uint)Math.Round(vector.Z * 255F) & 255) << 16); } } diff --git a/src/ImageSharp.Textures/PixelFormats/Generated/Rgb565.cs b/src/ImageSharp.Textures/PixelFormats/Generated/Rgb565.cs index 1eff83a7..22572e97 100644 --- a/src/ImageSharp.Textures/PixelFormats/Generated/Rgb565.cs +++ b/src/ImageSharp.Textures/PixelFormats/Generated/Rgb565.cs @@ -1,178 +1,173 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Textures.PixelFormats +namespace SixLabors.ImageSharp.Textures.PixelFormats; + +/// +/// Packed pixel type containing unsigned normalized values ranging from 0 to 1. +/// The x, y and z components uses 5, 6 and 5 bits respectively. +/// +/// Ranges from [0, 0, 0] to [1, 1, 1] in vector form. +/// +/// +public partial struct Rgb565 : IPixel, IPackedVector { /// - /// Packed pixel type containing unsigned normalized values ranging from 0 to 1. - /// The x, y and z components uses 5, 6 and 5 bits respectively. - /// - /// Ranges from [0, 0, 0] to [1, 1, 1] in vector form. - /// + /// Initializes a new instance of the struct. + /// + /// The x-component + /// The y-component + /// The z-component + public Rgb565(float x, float y, float z) + : this(new Vector3(x, y, z)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// + /// The vector containing the components for the packed vector. + /// + public Rgb565(Vector3 vector) => this.PackedValue = Pack(ref vector); + + /// + public ushort PackedValue { get; set; } + + /// + /// Compares two objects for equality. /// - public partial struct Rgb565 : IPixel, IPackedVector + /// The on the left side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + /// The on the right side of the operand. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Rgb565 left, Rgb565 right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Rgb565 left, Rgb565 right) => !left.Equals(right); + + /// + public readonly PixelOperations CreatePixelOperations() => new(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromVector4(Vector4 vector) + { + Vector3 vector3 = new Vector3(vector.X, vector.Y, vector.Z); + this.PackedValue = Pack(ref vector3); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new( + (this.PackedValue & 31) / 31F, + ((this.PackedValue >> 5) & 63) / 63F, + ((this.PackedValue >> 11) & 31) / 31F, + 1.0f); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + public override bool Equals(object? obj) => obj is Rgb565 other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly bool Equals(Rgb565 other) => this.PackedValue.Equals(other.PackedValue); + + /// + public override string ToString() + { + Vector4 vector = this.ToVector4(); + return FormattableString.Invariant($"Rgb565({vector.X:#0.##}, {vector.Y:#0.##}, {vector.Z:#0.##})"); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ushort Pack(ref Vector3 vector) { - /// - /// Initializes a new instance of the struct. - /// - /// The x-component - /// The y-component - /// The z-component - public Rgb565(float x, float y, float z) - : this(new Vector3(x, y, z)) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// - /// The vector containing the components for the packed vector. - /// - public Rgb565(Vector3 vector) => this.PackedValue = Pack(ref vector); - - /// - public ushort PackedValue { get; set; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// - /// True if the parameter is equal to the parameter; otherwise, false. - /// - /// The on the right side of the operand. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Rgb565 left, Rgb565 right) => left.Equals(right); - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Rgb565 left, Rgb565 right) => !left.Equals(right); - - /// - public PixelOperations CreatePixelOperations() => new(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() => this.ToVector4(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromVector4(Vector4 vector) - { - var vector3 = new Vector3(vector.X, vector.Y, vector.Z); - this.PackedValue = Pack(ref vector3); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() => new( - (this.PackedValue & 31) / 31F, - ((this.PackedValue >> 5) & 63) / 63F, - ((this.PackedValue >> 11) & 31) / 31F, - 1.0f); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba32(ref Rgba32 dest) - { - dest.FromScaledVector4(this.ToScaledVector4()); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - public override bool Equals(object? obj) => obj is Rgb565 other && this.Equals(other); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Rgb565 other) => this.PackedValue.Equals(other.PackedValue); - - /// - public override string ToString() - { - var vector = this.ToVector4(); - return FormattableString.Invariant($"Rgb565({vector.X:#0.##}, {vector.Y:#0.##}, {vector.Z:#0.##})"); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => this.PackedValue.GetHashCode(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ushort Pack(ref Vector3 vector) - { - vector = Vector3.Clamp(vector, Vector3.Zero, Vector3.One); - return (ushort)( - ((uint)Math.Round(vector.X * 31F) & 31) - | (((uint)Math.Round(vector.Y * 63F) & 63) << 5) - | (((uint)Math.Round(vector.Z * 31F) & 31) << 11)); - } + vector = Vector3.Clamp(vector, Vector3.Zero, Vector3.One); + return (ushort)( + ((uint)Math.Round(vector.X * 31F) & 31) + | (((uint)Math.Round(vector.Y * 63F) & 63) << 5) + | (((uint)Math.Round(vector.Z * 31F) & 31) << 11)); } } diff --git a/src/ImageSharp.Textures/PixelFormats/Generated/Rgb96.cs b/src/ImageSharp.Textures/PixelFormats/Generated/Rgb96.cs index 13fac465..297e02ea 100644 --- a/src/ImageSharp.Textures/PixelFormats/Generated/Rgb96.cs +++ b/src/ImageSharp.Textures/PixelFormats/Generated/Rgb96.cs @@ -1,177 +1,172 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Textures.PixelFormats +namespace SixLabors.ImageSharp.Textures.PixelFormats; + +/// +/// Pixel type containing three 32-bit unsigned normalized values ranging from 0 to 4294967295. +/// The color components are stored in red, green, blue +/// +/// Ranges from [0, 0, 0] to [1, 1, 1] in vector form. +/// +/// +[StructLayout(LayoutKind.Explicit)] +public partial struct Rgb96 : IPixel { /// - /// Pixel type containing three 32-bit unsigned normalized values ranging from 0 to 4294967295. - /// The color components are stored in red, green, blue - /// - /// Ranges from [0, 0, 0] to [1, 1, 1] in vector form. - /// + /// Gets or sets the red component. /// - [StructLayout(LayoutKind.Explicit)] - public partial struct Rgb96 : IPixel + [FieldOffset(0)] + public uint R; + + /// + /// Gets or sets the green component. + /// + [FieldOffset(4)] + public uint G; + + /// + /// Gets or sets the blue component. + /// + [FieldOffset(8)] + public uint B; + + /// + /// Initializes a new instance of the struct. + /// + /// The red component. + /// The green component. + /// The blue component. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Rgb96(uint r, uint g, uint b) { - /// - /// Gets or sets the red component. - /// - [FieldOffset(0)] - public uint R; - - /// - /// Gets or sets the green component. - /// - [FieldOffset(4)] - public uint G; - - /// - /// Gets or sets the blue component. - /// - [FieldOffset(8)] - public uint B; - - /// - /// Initializes a new instance of the struct. - /// - /// The red component. - /// The green component. - /// The blue component. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Rgb96(uint r, uint g, uint b) - { - this.R = r; - this.G = g; - this.B = b; - } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// - /// True if the parameter is equal to the parameter; otherwise, false. - /// - /// The on the right side of the operand. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Rgb96 left, Rgb96 right) => left.Equals(right); - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Rgb96 left, Rgb96 right) => !left.Equals(right); - - /// - public PixelOperations CreatePixelOperations() => new(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() => this.ToVector4(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromVector4(Vector4 vector) - { - this.R = (uint)(vector.X * 4294967295); - this.G = (uint)(vector.Y * 4294967295); - this.B = (uint)(vector.Z * 4294967295); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() => new( - this.R / 4294967295F, - this.G / 4294967295F, - this.B / 4294967295F, - 1.0f); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - public override bool Equals(object? obj) => obj is Rgb96 other && this.Equals(other); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Rgb96 other) => this.R.Equals(other.R) && this.G.Equals(other.G) && this.B.Equals(other.B); - - /// - public override string ToString() - { - return FormattableString.Invariant($"Rgb96({this.R}, {this.G}, {this.B})"); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B); + this.R = r; + this.G = g; + this.B = b; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + /// The on the right side of the operand. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Rgb96 left, Rgb96 right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Rgb96 left, Rgb96 right) => !left.Equals(right); + + /// + public readonly PixelOperations CreatePixelOperations() => new(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromVector4(Vector4 vector) + { + this.R = (uint)(vector.X * 4294967295); + this.G = (uint)(vector.Y * 4294967295); + this.B = (uint)(vector.Z * 4294967295); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new( + this.R / 4294967295F, + this.G / 4294967295F, + this.B / 4294967295F, + 1.0f); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + public override bool Equals(object? obj) => obj is Rgb96 other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly bool Equals(Rgb96 other) => this.R.Equals(other.R) && this.G.Equals(other.G) && this.B.Equals(other.B); + + /// + public override readonly string ToString() => FormattableString.Invariant($"Rgb96({this.R}, {this.G}, {this.B})"); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override readonly int GetHashCode() => HashCode.Combine(this.R, this.G, this.B); } diff --git a/src/ImageSharp.Textures/PixelFormats/Generated/Rgb96Float.cs b/src/ImageSharp.Textures/PixelFormats/Generated/Rgb96Float.cs index ab1cd9d5..467a3035 100644 --- a/src/ImageSharp.Textures/PixelFormats/Generated/Rgb96Float.cs +++ b/src/ImageSharp.Textures/PixelFormats/Generated/Rgb96Float.cs @@ -1,174 +1,172 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Textures.PixelFormats +namespace SixLabors.ImageSharp.Textures.PixelFormats; + +/// +/// Pixel type containing three 32-bit unsigned normalized values ranging from 0 to 4294967295. +/// The color components are stored in red, green, blue +/// +/// Ranges from [0, 0, 0] to [1, 1, 1] in vector form. +/// +/// +[StructLayout(LayoutKind.Explicit)] +public partial struct Rgb96Float : IPixel { /// - /// Pixel type containing three 32-bit unsigned normalized values ranging from 0 to 4294967295. - /// The color components are stored in red, green, blue - /// - /// Ranges from [0, 0, 0] to [1, 1, 1] in vector form. - /// + /// Gets or sets the red component. /// - [StructLayout(LayoutKind.Explicit)] - public partial struct Rgb96Float : IPixel + [FieldOffset(0)] + public float R; + + /// + /// Gets or sets the green component. + /// + [FieldOffset(4)] + public float G; + + /// + /// Gets or sets the blue component. + /// + [FieldOffset(8)] + public float B; + + /// + /// Initializes a new instance of the struct. + /// + /// The red component. + /// The green component. + /// The blue component. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Rgb96Float(float r, float g, float b) { - /// - /// Gets or sets the red component. - /// - [FieldOffset(0)] - public float R; - - /// - /// Gets or sets the green component. - /// - [FieldOffset(4)] - public float G; - - /// - /// Gets or sets the blue component. - /// - [FieldOffset(8)] - public float B; - - /// - /// Initializes a new instance of the struct. - /// - /// The red component. - /// The green component. - /// The blue component. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Rgb96Float(float r, float g, float b) - { - this.R = r; - this.G = g; - this.B = b; - } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// - /// True if the parameter is equal to the parameter; otherwise, false. - /// - /// The on the right side of the operand. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Rgb96Float left, Rgb96Float right) => left.Equals(right); - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Rgb96Float left, Rgb96Float right) => !left.Equals(right); - - /// - public PixelOperations CreatePixelOperations() => new(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() => this.ToVector4(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromVector4(Vector4 vector) - { - this.R = vector.X; - this.G = vector.Y; - this.B = vector.Z; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() => new( - this.R, - this.G, - this.B, - 1.0f); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - public override bool Equals(object? obj) => obj is Rgb96Float other && this.Equals(other); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Rgb96Float other) => this.R.Equals(other.R) && this.G.Equals(other.G) && this.B.Equals(other.B); - - /// - public override string ToString() => FormattableString.Invariant($"Rgb96Float({this.R}, {this.G}, {this.B})"); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B); + this.R = r; + this.G = g; + this.B = b; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + /// The on the right side of the operand. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Rgb96Float left, Rgb96Float right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Rgb96Float left, Rgb96Float right) => !left.Equals(right); + + /// + public readonly PixelOperations CreatePixelOperations() => new(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromVector4(Vector4 vector) + { + this.R = vector.X; + this.G = vector.Y; + this.B = vector.Z; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new( + this.R, + this.G, + this.B, + 1.0f); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + public override bool Equals(object? obj) => obj is Rgb96Float other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly bool Equals(Rgb96Float other) => this.R.Equals(other.R) && this.G.Equals(other.G) && this.B.Equals(other.B); + + /// + public override readonly string ToString() => FormattableString.Invariant($"Rgb96Float({this.R}, {this.G}, {this.B})"); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override readonly int GetHashCode() => HashCode.Combine(this.R, this.G, this.B); } diff --git a/src/ImageSharp.Textures/PixelFormats/Generated/Rgba128.cs b/src/ImageSharp.Textures/PixelFormats/Generated/Rgba128.cs index c4728c3f..bbc719d4 100644 --- a/src/ImageSharp.Textures/PixelFormats/Generated/Rgba128.cs +++ b/src/ImageSharp.Textures/PixelFormats/Generated/Rgba128.cs @@ -1,183 +1,181 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Textures.PixelFormats +namespace SixLabors.ImageSharp.Textures.PixelFormats; + +/// +/// Pixel type containing four 32-bit unsigned normalized values ranging from 0 to 4294967295. +/// The color components are stored in red, green, blue, alpha +/// +/// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form. +/// +/// +[StructLayout(LayoutKind.Explicit)] +public partial struct Rgba128 : IPixel { /// - /// Pixel type containing four 32-bit unsigned normalized values ranging from 0 to 4294967295. - /// The color components are stored in red, green, blue, alpha - /// - /// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form. - /// + /// Gets or sets the red component. /// - [StructLayout(LayoutKind.Explicit)] - public partial struct Rgba128 : IPixel + [FieldOffset(0)] + public uint R; + + /// + /// Gets or sets the green component. + /// + [FieldOffset(4)] + public uint G; + + /// + /// Gets or sets the blue component. + /// + [FieldOffset(8)] + public uint B; + + /// + /// Gets or sets the alpha component. + /// + [FieldOffset(12)] + public uint A; + + /// + /// Initializes a new instance of the struct. + /// + /// The red component. + /// The green component. + /// The blue component. + /// The alpha component. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Rgba128(uint r, uint g, uint b, uint a) + { + this.R = r; + this.G = g; + this.B = b; + this.A = a; + } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + /// The on the right side of the operand. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Rgba128 left, Rgba128 right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Rgba128 left, Rgba128 right) => !left.Equals(right); + + /// + public readonly PixelOperations CreatePixelOperations() => new(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromVector4(Vector4 vector) { - /// - /// Gets or sets the red component. - /// - [FieldOffset(0)] - public uint R; - - /// - /// Gets or sets the green component. - /// - [FieldOffset(4)] - public uint G; - - /// - /// Gets or sets the blue component. - /// - [FieldOffset(8)] - public uint B; - - /// - /// Gets or sets the alpha component. - /// - [FieldOffset(12)] - public uint A; - - /// - /// Initializes a new instance of the struct. - /// - /// The red component. - /// The green component. - /// The blue component. - /// The alpha component. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Rgba128(uint r, uint g, uint b, uint a) - { - this.R = r; - this.G = g; - this.B = b; - this.A = a; - } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// - /// True if the parameter is equal to the parameter; otherwise, false. - /// - /// The on the right side of the operand. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Rgba128 left, Rgba128 right) => left.Equals(right); - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Rgba128 left, Rgba128 right) => !left.Equals(right); - - /// - public PixelOperations CreatePixelOperations() => new(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() => this.ToVector4(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromVector4(Vector4 vector) - { - this.R = (uint)(vector.X * 4294967295); - this.G = (uint)(vector.Y * 4294967295); - this.B = (uint)(vector.Z * 4294967295); - this.A = (uint)(vector.W * 4294967295); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() => new( - this.R / 4294967295F, - this.G / 4294967295F, - this.B / 4294967295F, - this.A / 4294967295F); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - public override bool Equals(object? obj) => obj is Rgba128 other && this.Equals(other); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Rgba128 other) => this.R.Equals(other.R) && this.G.Equals(other.G) && this.B.Equals(other.B) && this.A.Equals(other.A); - - /// - public override string ToString() => FormattableString.Invariant($"Rgba128({this.R}, {this.G}, {this.B}, {this.A})"); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B, this.A); + this.R = (uint)(vector.X * 4294967295); + this.G = (uint)(vector.Y * 4294967295); + this.B = (uint)(vector.Z * 4294967295); + this.A = (uint)(vector.W * 4294967295); } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new( + this.R / 4294967295F, + this.G / 4294967295F, + this.B / 4294967295F, + this.A / 4294967295F); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + public override bool Equals(object? obj) => obj is Rgba128 other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly bool Equals(Rgba128 other) => this.R.Equals(other.R) && this.G.Equals(other.G) && this.B.Equals(other.B) && this.A.Equals(other.A); + + /// + public override readonly string ToString() => FormattableString.Invariant($"Rgba128({this.R}, {this.G}, {this.B}, {this.A})"); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override readonly int GetHashCode() => HashCode.Combine(this.R, this.G, this.B, this.A); } diff --git a/src/ImageSharp.Textures/PixelFormats/Generated/Rgba128Float.cs b/src/ImageSharp.Textures/PixelFormats/Generated/Rgba128Float.cs index f21a8641..7557d7b9 100644 --- a/src/ImageSharp.Textures/PixelFormats/Generated/Rgba128Float.cs +++ b/src/ImageSharp.Textures/PixelFormats/Generated/Rgba128Float.cs @@ -1,183 +1,181 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Textures.PixelFormats +namespace SixLabors.ImageSharp.Textures.PixelFormats; + +/// +/// Pixel type containing four 32-bit unsigned normalized values ranging from 0 to 4294967295. +/// The color components are stored in red, green, blue, alpha +/// +/// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form. +/// +/// +[StructLayout(LayoutKind.Explicit)] +public partial struct Rgba128Float : IPixel { /// - /// Pixel type containing four 32-bit unsigned normalized values ranging from 0 to 4294967295. - /// The color components are stored in red, green, blue, alpha - /// - /// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form. - /// + /// Gets or sets the red component. /// - [StructLayout(LayoutKind.Explicit)] - public partial struct Rgba128Float : IPixel + [FieldOffset(0)] + public float R; + + /// + /// Gets or sets the green component. + /// + [FieldOffset(4)] + public float G; + + /// + /// Gets or sets the blue component. + /// + [FieldOffset(8)] + public float B; + + /// + /// Gets or sets the alpha component. + /// + [FieldOffset(12)] + public float A; + + /// + /// Initializes a new instance of the struct. + /// + /// The red component. + /// The green component. + /// The blue component. + /// The alpha component. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Rgba128Float(float r, float g, float b, float a) + { + this.R = r; + this.G = g; + this.B = b; + this.A = a; + } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + /// The on the right side of the operand. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Rgba128Float left, Rgba128Float right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Rgba128Float left, Rgba128Float right) => !left.Equals(right); + + /// + public readonly PixelOperations CreatePixelOperations() => new(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromVector4(Vector4 vector) { - /// - /// Gets or sets the red component. - /// - [FieldOffset(0)] - public float R; - - /// - /// Gets or sets the green component. - /// - [FieldOffset(4)] - public float G; - - /// - /// Gets or sets the blue component. - /// - [FieldOffset(8)] - public float B; - - /// - /// Gets or sets the alpha component. - /// - [FieldOffset(12)] - public float A; - - /// - /// Initializes a new instance of the struct. - /// - /// The red component. - /// The green component. - /// The blue component. - /// The alpha component. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Rgba128Float(float r, float g, float b, float a) - { - this.R = r; - this.G = g; - this.B = b; - this.A = a; - } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// - /// True if the parameter is equal to the parameter; otherwise, false. - /// - /// The on the right side of the operand. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Rgba128Float left, Rgba128Float right) => left.Equals(right); - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Rgba128Float left, Rgba128Float right) => !left.Equals(right); - - /// - public PixelOperations CreatePixelOperations() => new(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() => this.ToVector4(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromVector4(Vector4 vector) - { - this.R = vector.X; - this.G = vector.Y; - this.B = vector.Z; - this.A = vector.W; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() => new( - this.R, - this.G, - this.B, - this.A); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - public override bool Equals(object? obj) => obj is Rgba128Float other && this.Equals(other); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Rgba128Float other) => this.R.Equals(other.R) && this.G.Equals(other.G) && this.B.Equals(other.B) && this.A.Equals(other.A); - - /// - public override string ToString() => FormattableString.Invariant($"Rgba128Float({this.R}, {this.G}, {this.B}, {this.A})"); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B, this.A); + this.R = vector.X; + this.G = vector.Y; + this.B = vector.Z; + this.A = vector.W; } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new( + this.R, + this.G, + this.B, + this.A); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + public override bool Equals(object? obj) => obj is Rgba128Float other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly bool Equals(Rgba128Float other) => this.R.Equals(other.R) && this.G.Equals(other.G) && this.B.Equals(other.B) && this.A.Equals(other.A); + + /// + public override readonly string ToString() => FormattableString.Invariant($"Rgba128Float({this.R}, {this.G}, {this.B}, {this.A})"); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override readonly int GetHashCode() => HashCode.Combine(this.R, this.G, this.B, this.A); } diff --git a/src/ImageSharp.Textures/PixelFormats/Generated/Rgba4444.cs b/src/ImageSharp.Textures/PixelFormats/Generated/Rgba4444.cs index 64b7d7df..f844636e 100644 --- a/src/ImageSharp.Textures/PixelFormats/Generated/Rgba4444.cs +++ b/src/ImageSharp.Textures/PixelFormats/Generated/Rgba4444.cs @@ -1,177 +1,175 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Textures.PixelFormats +namespace SixLabors.ImageSharp.Textures.PixelFormats; + +/// +/// Packed pixel type containing unsigned normalized values ranging from 0 to 1. +/// The x, y, z and w components use 4 bits. +/// +/// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form. +/// +/// +public partial struct Rgba4444 : IPixel, IPackedVector { /// - /// Packed pixel type containing unsigned normalized values ranging from 0 to 1. - /// The x, y, z and w components use 4 bits. - /// - /// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form. - /// + /// Initializes a new instance of the struct. + /// + /// The x-component + /// The y-component + /// The z-component + /// The w-component + public Rgba4444(float x, float y, float z, float w) + : this(new Vector4(x, y, z, w)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// + /// The vector containing the components for the packed vector. + /// + public Rgba4444(Vector4 vector) => this.PackedValue = Pack(ref vector); + + /// + public ushort PackedValue { get; set; } + + /// + /// Compares two objects for equality. /// - public partial struct Rgba4444 : IPixel, IPackedVector + /// The on the left side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + /// The on the right side of the operand. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Rgba4444 left, Rgba4444 right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Rgba4444 left, Rgba4444 right) => !left.Equals(right); + + /// + public readonly PixelOperations CreatePixelOperations() => new(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromVector4(Vector4 vector) + { + Vector4 vector4 = new Vector4(vector.X, vector.Y, vector.Z, vector.W); + this.PackedValue = Pack(ref vector4); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new( + (this.PackedValue & 15) / 15F, + ((this.PackedValue >> 4) & 15) / 15F, + ((this.PackedValue >> 8) & 15) / 15F, + ((this.PackedValue >> 12) & 15) / 15F); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + public override bool Equals(object? obj) => obj is Rgba4444 other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly bool Equals(Rgba4444 other) => this.PackedValue.Equals(other.PackedValue); + + /// + public override string ToString() + { + Vector4 vector = this.ToVector4(); + return FormattableString.Invariant($"Rgba4444({vector.X:#0.##}, {vector.Y:#0.##}, {vector.Z:#0.##}, {vector.W:#0.##})"); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ushort Pack(ref Vector4 vector) { - /// - /// Initializes a new instance of the struct. - /// - /// The x-component - /// The y-component - /// The z-component - /// The w-component - public Rgba4444(float x, float y, float z, float w) - : this(new Vector4(x, y, z, w)) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// - /// The vector containing the components for the packed vector. - /// - public Rgba4444(Vector4 vector) => this.PackedValue = Pack(ref vector); - - /// - public ushort PackedValue { get; set; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// - /// True if the parameter is equal to the parameter; otherwise, false. - /// - /// The on the right side of the operand. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Rgba4444 left, Rgba4444 right) => left.Equals(right); - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Rgba4444 left, Rgba4444 right) => !left.Equals(right); - - /// - public PixelOperations CreatePixelOperations() => new(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() => this.ToVector4(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromVector4(Vector4 vector) - { - var vector4 = new Vector4(vector.X, vector.Y, vector.Z, vector.W); - this.PackedValue = Pack(ref vector4); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() => new( - (this.PackedValue & 15) / 15F, - ((this.PackedValue >> 4) & 15) / 15F, - ((this.PackedValue >> 8) & 15) / 15F, - ((this.PackedValue >> 12) & 15) / 15F); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - public override bool Equals(object? obj) => obj is Rgba4444 other && this.Equals(other); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Rgba4444 other) => this.PackedValue.Equals(other.PackedValue); - - /// - public override string ToString() - { - var vector = this.ToVector4(); - return FormattableString.Invariant($"Rgba4444({vector.X:#0.##}, {vector.Y:#0.##}, {vector.Z:#0.##}, {vector.W:#0.##})"); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => this.PackedValue.GetHashCode(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ushort Pack(ref Vector4 vector) - { - vector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One); - return (ushort)( - ((uint)Math.Round(vector.X * 15F) & 15) - | (((uint)Math.Round(vector.Y * 15F) & 15) << 4) - | (((uint)Math.Round(vector.Z * 15F) & 15) << 8) - | (((uint)Math.Round(vector.W * 15F) & 15) << 12)); - } + vector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One); + return (ushort)( + ((uint)Math.Round(vector.X * 15F) & 15) + | (((uint)Math.Round(vector.Y * 15F) & 15) << 4) + | (((uint)Math.Round(vector.Z * 15F) & 15) << 8) + | (((uint)Math.Round(vector.W * 15F) & 15) << 12)); } } diff --git a/src/ImageSharp.Textures/PixelFormats/Generated/Rgba5551.cs b/src/ImageSharp.Textures/PixelFormats/Generated/Rgba5551.cs index 7c99ac48..99f6ada6 100644 --- a/src/ImageSharp.Textures/PixelFormats/Generated/Rgba5551.cs +++ b/src/ImageSharp.Textures/PixelFormats/Generated/Rgba5551.cs @@ -1,177 +1,175 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Textures.PixelFormats +namespace SixLabors.ImageSharp.Textures.PixelFormats; + +/// +/// Packed pixel type containing unsigned normalized values ranging from 0 to 1. +/// The x, y, z and w components uses 5, 5, 5 and 1 bits respectively. +/// +/// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form. +/// +/// +public partial struct Rgba5551 : IPixel, IPackedVector { /// - /// Packed pixel type containing unsigned normalized values ranging from 0 to 1. - /// The x, y, z and w components uses 5, 5, 5 and 1 bits respectively. - /// - /// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form. - /// + /// Initializes a new instance of the struct. + /// + /// The x-component + /// The y-component + /// The z-component + /// The w-component + public Rgba5551(float x, float y, float z, float w) + : this(new Vector4(x, y, z, w)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// + /// The vector containing the components for the packed vector. + /// + public Rgba5551(Vector4 vector) => this.PackedValue = Pack(ref vector); + + /// + public ushort PackedValue { get; set; } + + /// + /// Compares two objects for equality. /// - public partial struct Rgba5551 : IPixel, IPackedVector + /// The on the left side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + /// The on the right side of the operand. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Rgba5551 left, Rgba5551 right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Rgba5551 left, Rgba5551 right) => !left.Equals(right); + + /// + public readonly PixelOperations CreatePixelOperations() => new(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromVector4(Vector4 vector) + { + Vector4 vector4 = new Vector4(vector.X, vector.Y, vector.Z, vector.W); + this.PackedValue = Pack(ref vector4); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new( + (this.PackedValue & 31) / 31F, + ((this.PackedValue >> 5) & 31) / 31F, + ((this.PackedValue >> 10) & 31) / 31F, + ((this.PackedValue >> 15) & 1) / 1F); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + public override bool Equals(object? obj) => obj is Rgba5551 other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly bool Equals(Rgba5551 other) => this.PackedValue.Equals(other.PackedValue); + + /// + public override string ToString() + { + Vector4 vector = this.ToVector4(); + return FormattableString.Invariant($"Rgba5551({vector.X:#0.##}, {vector.Y:#0.##}, {vector.Z:#0.##}, {vector.W:#0.##})"); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ushort Pack(ref Vector4 vector) { - /// - /// Initializes a new instance of the struct. - /// - /// The x-component - /// The y-component - /// The z-component - /// The w-component - public Rgba5551(float x, float y, float z, float w) - : this(new Vector4(x, y, z, w)) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// - /// The vector containing the components for the packed vector. - /// - public Rgba5551(Vector4 vector) => this.PackedValue = Pack(ref vector); - - /// - public ushort PackedValue { get; set; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// - /// True if the parameter is equal to the parameter; otherwise, false. - /// - /// The on the right side of the operand. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Rgba5551 left, Rgba5551 right) => left.Equals(right); - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Rgba5551 left, Rgba5551 right) => !left.Equals(right); - - /// - public PixelOperations CreatePixelOperations() => new(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() => this.ToVector4(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromVector4(Vector4 vector) - { - var vector4 = new Vector4(vector.X, vector.Y, vector.Z, vector.W); - this.PackedValue = Pack(ref vector4); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() => new( - (this.PackedValue & 31) / 31F, - ((this.PackedValue >> 5) & 31) / 31F, - ((this.PackedValue >> 10) & 31) / 31F, - ((this.PackedValue >> 15) & 1) / 1F); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - public override bool Equals(object? obj) => obj is Rgba5551 other && this.Equals(other); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Rgba5551 other) => this.PackedValue.Equals(other.PackedValue); - - /// - public override string ToString() - { - var vector = this.ToVector4(); - return FormattableString.Invariant($"Rgba5551({vector.X:#0.##}, {vector.Y:#0.##}, {vector.Z:#0.##}, {vector.W:#0.##})"); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => this.PackedValue.GetHashCode(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ushort Pack(ref Vector4 vector) - { - vector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One); - return (ushort)( - ((uint)Math.Round(vector.X * 31F) & 31) - | (((uint)Math.Round(vector.Y * 31F) & 31) << 5) - | (((uint)Math.Round(vector.Z * 31F) & 31) << 10) - | (((uint)Math.Round(vector.W * 1F) & 1) << 15)); - } + vector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One); + return (ushort)( + ((uint)Math.Round(vector.X * 31F) & 31) + | (((uint)Math.Round(vector.Y * 31F) & 31) << 5) + | (((uint)Math.Round(vector.Z * 31F) & 31) << 10) + | (((uint)Math.Round(vector.W * 1F) & 1) << 15)); } } diff --git a/src/ImageSharp.Textures/PixelFormats/Generated/Rgba64Float.cs b/src/ImageSharp.Textures/PixelFormats/Generated/Rgba64Float.cs index 8cd8db4f..c989e7b8 100644 --- a/src/ImageSharp.Textures/PixelFormats/Generated/Rgba64Float.cs +++ b/src/ImageSharp.Textures/PixelFormats/Generated/Rgba64Float.cs @@ -1,181 +1,176 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Textures.Common.Helpers; -namespace SixLabors.ImageSharp.Textures.PixelFormats +namespace SixLabors.ImageSharp.Textures.PixelFormats; + +/// +/// Packed pixel type containing unsigned normalized values ranging from 0 to 1. +/// The x, y, z and w components use 16 bits. +/// +/// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form. +/// +/// +public partial struct Rgba64Float : IPixel, IPackedVector { /// - /// Packed pixel type containing unsigned normalized values ranging from 0 to 1. - /// The x, y, z and w components use 16 bits. - /// - /// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form. - /// + /// Initializes a new instance of the struct. + /// + /// The x-component + /// The y-component + /// The z-component + /// The w-component + public Rgba64Float(float x, float y, float z, float w) + : this(new Vector4(x, y, z, w)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// + /// The vector containing the components for the packed vector. + /// + public Rgba64Float(Vector4 vector) => this.PackedValue = Pack(ref vector); + + /// + public ulong PackedValue { get; set; } + + /// + /// Compares two objects for equality. /// - public partial struct Rgba64Float : IPixel, IPackedVector + /// The on the left side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + /// The on the right side of the operand. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Rgba64Float left, Rgba64Float right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Rgba64Float left, Rgba64Float right) => !left.Equals(right); + + /// + public readonly PixelOperations CreatePixelOperations() => new(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromVector4(Vector4 vector) + { + Vector4 vector4 = new Vector4(vector.X, vector.Y, vector.Z, vector.W); + this.PackedValue = Pack(ref vector4); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new( + FloatHelper.UnpackFloat16ToFloat((ushort)(this.PackedValue & 65535)), + FloatHelper.UnpackFloat16ToFloat((ushort)((this.PackedValue >> 16) & 65535)), + FloatHelper.UnpackFloat16ToFloat((ushort)((this.PackedValue >> 32) & 65535)), + FloatHelper.UnpackFloat16ToFloat((ushort)((this.PackedValue >> 48) & 65535))); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + public override bool Equals(object? obj) => obj is Rgba64Float other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly bool Equals(Rgba64Float other) => this.PackedValue.Equals(other.PackedValue); + + /// + public override string ToString() + { + Vector4 vector = this.ToVector4(); + return FormattableString.Invariant($"Rgba64Float({vector.X:#0.##}, {vector.Y:#0.##}, {vector.Z:#0.##}, {vector.W:#0.##})"); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong Pack(ref Vector4 vector) { - /// - /// Initializes a new instance of the struct. - /// - /// The x-component - /// The y-component - /// The z-component - /// The w-component - public Rgba64Float(float x, float y, float z, float w) - : this(new Vector4(x, y, z, w)) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// - /// The vector containing the components for the packed vector. - /// - public Rgba64Float(Vector4 vector) => this.PackedValue = Pack(ref vector); - - /// - public ulong PackedValue { get; set; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// - /// True if the parameter is equal to the parameter; otherwise, false. - /// - /// The on the right side of the operand. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Rgba64Float left, Rgba64Float right) => left.Equals(right); - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Rgba64Float left, Rgba64Float right) => !left.Equals(right); - - /// - public PixelOperations CreatePixelOperations() => new(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() => this.ToVector4(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromVector4(Vector4 vector) - { - var vector4 = new Vector4(vector.X, vector.Y, vector.Z, vector.W); - this.PackedValue = Pack(ref vector4); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() => new( - FloatHelper.UnpackFloat16ToFloat((ushort)(this.PackedValue & 65535)), - FloatHelper.UnpackFloat16ToFloat((ushort)((this.PackedValue >> 16) & 65535)), - FloatHelper.UnpackFloat16ToFloat((ushort)((this.PackedValue >> 32) & 65535)), - FloatHelper.UnpackFloat16ToFloat((ushort)((this.PackedValue >> 48) & 65535))); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba32(ref Rgba32 dest) - { - dest.FromScaledVector4(this.ToScaledVector4()); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - public override bool Equals(object? obj) => obj is Rgba64Float other && this.Equals(other); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Rgba64Float other) => this.PackedValue.Equals(other.PackedValue); - - /// - public override string ToString() - { - var vector = this.ToVector4(); - return FormattableString.Invariant($"Rgba64Float({vector.X:#0.##}, {vector.Y:#0.##}, {vector.Z:#0.##}, {vector.W:#0.##})"); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => this.PackedValue.GetHashCode(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ulong Pack(ref Vector4 vector) - { - vector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One); - return (ulong)( - (uint)FloatHelper.PackFloatToFloat16(vector.X) - | ((uint)FloatHelper.PackFloatToFloat16(vector.Y) << 16) - | ((uint)FloatHelper.PackFloatToFloat16(vector.Z) << 32) - | ((uint)FloatHelper.PackFloatToFloat16(vector.W) << 48)); - } + vector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One); + return + FloatHelper.PackFloatToFloat16(vector.X) + | (FloatHelper.PackFloatToFloat16(vector.Y) << 16) + | (FloatHelper.PackFloatToFloat16(vector.Z) << 32) + | (FloatHelper.PackFloatToFloat16(vector.W) << 48); } } diff --git a/src/ImageSharp.Textures/PixelFormats/L32.cs b/src/ImageSharp.Textures/PixelFormats/L32.cs index 97caeefe..c930f082 100644 --- a/src/ImageSharp.Textures/PixelFormats/L32.cs +++ b/src/ImageSharp.Textures/PixelFormats/L32.cs @@ -5,260 +5,257 @@ using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Textures.PixelFormats +namespace SixLabors.ImageSharp.Textures.PixelFormats; + +/// +/// Packed pixel type containing a single 32-bit normalized luminance value. +/// +/// Ranges from [0, 0, 0, 1] to [1, 1, 1, 1] in vector form. +/// +/// +public struct L32 : IPixel, IPackedVector { + private const float Max = uint.MaxValue; + + /// + /// Initializes a new instance of the struct. + /// + /// The luminance component + public L32(uint luminance) => this.PackedValue = luminance; + + /// + public uint PackedValue { get; set; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(L32 left, L32 right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(L32 left, L32 right) => !left.Equals(right); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromScaledVector4(Vector4 vector) => this.ConvertFromRgbaScaledVector4(vector); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromVector4(Vector4 vector) => this.ConvertFromRgbaScaledVector4(vector); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() + { + float scaled = this.PackedValue / Max; + return new Vector4(scaled, scaled, scaled, 1F); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromArgb32(Argb32 source) => this.PackedValue = Get16BitBT709Luminance( + UpscaleFrom8BitTo16Bit(source.R), + UpscaleFrom8BitTo16Bit(source.G), + UpscaleFrom8BitTo16Bit(source.B)); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgr24(Bgr24 source) => this.PackedValue = Get16BitBT709Luminance( + UpscaleFrom8BitTo16Bit(source.R), + UpscaleFrom8BitTo16Bit(source.G), + UpscaleFrom8BitTo16Bit(source.B)); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgra32(Bgra32 source) => this.PackedValue = Get16BitBT709Luminance( + UpscaleFrom8BitTo16Bit(source.R), + UpscaleFrom8BitTo16Bit(source.G), + UpscaleFrom8BitTo16Bit(source.B)); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromAbgr32(Abgr32 source) => throw new System.NotImplementedException(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromL8(L8 source) => this.PackedValue = UpscaleFrom8BitTo16Bit(source.PackedValue); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromL16(L16 source) => this = new L32(source.PackedValue); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromLa16(La16 source) => this.PackedValue = UpscaleFrom8BitTo16Bit(source.L); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromLa32(La32 source) => this.PackedValue = source.L; + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgb24(Rgb24 source) => this.PackedValue = Get16BitBT709Luminance( + UpscaleFrom8BitTo16Bit(source.R), + UpscaleFrom8BitTo16Bit(source.G), + UpscaleFrom8BitTo16Bit(source.B)); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba32(Rgba32 source) => this.PackedValue = Get16BitBT709Luminance( + UpscaleFrom8BitTo16Bit(source.R), + UpscaleFrom8BitTo16Bit(source.G), + UpscaleFrom8BitTo16Bit(source.B)); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly void ToRgba32(ref Rgba32 dest) + { + byte rgb = DownScaleFrom32BitTo8Bit(this.PackedValue); + dest.R = rgb; + dest.G = rgb; + dest.B = rgb; + dest.A = byte.MaxValue; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgb48(Rgb48 source) => this.PackedValue = Get16BitBT709Luminance(source.R, source.G, source.B); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba64(Rgba64 source) => this.PackedValue = Get16BitBT709Luminance(source.R, source.G, source.B); + + /// + public readonly PixelOperations CreatePixelOperations() => new(); + + /// + public override readonly bool Equals(object? obj) => obj is L16 other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly bool Equals(L32 other) => this.PackedValue.Equals(other.PackedValue); + + /// + public override readonly string ToString() => $"L32({this.PackedValue})"; + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void ConvertFromRgbaScaledVector4(Vector4 vector) + { + vector = Clamp(vector, Vector4.Zero, Vector4.One) * Max; + this.PackedValue = Get16BitBT709Luminance( + vector.X, + vector.Y, + vector.Z); + } + + /// + /// Gets the luminance from the rgb components using the formula as + /// specified by ITU-R Recommendation BT.709. + /// + /// The red component. + /// The green component. + /// The blue component. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ushort Get16BitBT709Luminance(ushort r, ushort g, ushort b) + => (ushort)((r * .2126F) + (g * .7152F) + (b * .0722F) + 0.5F); + + /// + /// Gets the luminance from the rgb components using the formula as specified + /// by ITU-R Recommendation BT.709. + /// + /// The red component. + /// The green component. + /// The blue component. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ushort Get16BitBT709Luminance(float r, float g, float b) + => (ushort)((r * .2126F) + (g * .7152F) + (b * .0722F) + 0.5F); + /// - /// Packed pixel type containing a single 32-bit normalized luminance value. - /// - /// Ranges from [0, 0, 0, 1] to [1, 1, 1, 1] in vector form. - /// + /// Scales a value from an 8 bit to + /// an 16 bit equivalent. /// - public struct L32 : IPixel, IPackedVector + /// The 8 bit component value. + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ushort UpscaleFrom8BitTo16Bit(byte component) + => (ushort)(component * 257); + + /// + /// Returns the value clamped to the inclusive range of min and max. + /// 5x Faster than + /// on platforms < NET 5. + /// + /// The value to clamp. + /// The minimum inclusive value. + /// The maximum inclusive value. + /// The clamped . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector4 Clamp(Vector4 value, Vector4 min, Vector4 max) + => Vector4.Min(Vector4.Max(value, min), max); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static byte DownScaleFrom32BitTo8Bit(uint component) { - private const float Max = uint.MaxValue; - - /// - /// Initializes a new instance of the struct. - /// - /// The luminance component - public L32(uint luminance) => this.PackedValue = luminance; - - /// - public uint PackedValue { get; set; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the parameter is equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(L32 left, L32 right) => left.Equals(right); - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(L32 left, L32 right) => !left.Equals(right); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromScaledVector4(Vector4 vector) => this.ConvertFromRgbaScaledVector4(vector); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly Vector4 ToScaledVector4() => this.ToVector4(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromVector4(Vector4 vector) => this.ConvertFromRgbaScaledVector4(vector); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly Vector4 ToVector4() - { - float scaled = this.PackedValue / Max; - return new Vector4(scaled, scaled, scaled, 1F); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromArgb32(Argb32 source) => this.PackedValue = Get16BitBT709Luminance( - UpscaleFrom8BitTo16Bit(source.R), - UpscaleFrom8BitTo16Bit(source.G), - UpscaleFrom8BitTo16Bit(source.B)); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgr24(Bgr24 source) => this.PackedValue = Get16BitBT709Luminance( - UpscaleFrom8BitTo16Bit(source.R), - UpscaleFrom8BitTo16Bit(source.G), - UpscaleFrom8BitTo16Bit(source.B)); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra32(Bgra32 source) => this.PackedValue = Get16BitBT709Luminance( - UpscaleFrom8BitTo16Bit(source.R), - UpscaleFrom8BitTo16Bit(source.G), - UpscaleFrom8BitTo16Bit(source.B)); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromAbgr32(Abgr32 source) => throw new System.NotImplementedException(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL8(L8 source) => this.PackedValue = UpscaleFrom8BitTo16Bit(source.PackedValue); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL16(L16 source) => this = new L32(source.PackedValue); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa16(La16 source) => this.PackedValue = UpscaleFrom8BitTo16Bit(source.L); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa32(La32 source) => this.PackedValue = source.L; - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb24(Rgb24 source) => this.PackedValue = Get16BitBT709Luminance( - UpscaleFrom8BitTo16Bit(source.R), - UpscaleFrom8BitTo16Bit(source.G), - UpscaleFrom8BitTo16Bit(source.B)); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba32(Rgba32 source) => this.PackedValue = Get16BitBT709Luminance( - UpscaleFrom8BitTo16Bit(source.R), - UpscaleFrom8BitTo16Bit(source.G), - UpscaleFrom8BitTo16Bit(source.B)); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba32(ref Rgba32 dest) - { - byte rgb = DownScaleFrom32BitTo8Bit(this.PackedValue); - dest.R = rgb; - dest.G = rgb; - dest.B = rgb; - dest.A = byte.MaxValue; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb48(Rgb48 source) => this.PackedValue = Get16BitBT709Luminance(source.R, source.G, source.B); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba64(Rgba64 source) => this.PackedValue = Get16BitBT709Luminance(source.R, source.G, source.B); - - /// - public PixelOperations CreatePixelOperations() => new(); - - /// - public override readonly bool Equals(object? obj) => obj is L16 other && this.Equals(other); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly bool Equals(L32 other) => this.PackedValue.Equals(other.PackedValue); - - /// - public override readonly string ToString() => $"L32({this.PackedValue})"; - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void ConvertFromRgbaScaledVector4(Vector4 vector) - { - vector = Clamp(vector, Vector4.Zero, Vector4.One) * Max; - this.PackedValue = Get16BitBT709Luminance( - vector.X, - vector.Y, - vector.Z); - } - - /// - /// Gets the luminance from the rgb components using the formula as - /// specified by ITU-R Recommendation BT.709. - /// - /// The red component. - /// The green component. - /// The blue component. - /// The . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ushort Get16BitBT709Luminance(ushort r, ushort g, ushort b) - => (ushort)((r * .2126F) + (g * .7152F) + (b * .0722F) + 0.5F); - - /// - /// Gets the luminance from the rgb components using the formula as specified - /// by ITU-R Recommendation BT.709. - /// - /// The red component. - /// The green component. - /// The blue component. - /// The . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ushort Get16BitBT709Luminance(float r, float g, float b) - => (ushort)((r * .2126F) + (g * .7152F) + (b * .0722F) + 0.5F); - - /// - /// Scales a value from an 8 bit to - /// an 16 bit equivalent. - /// - /// The 8 bit component value. - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ushort UpscaleFrom8BitTo16Bit(byte component) - => (ushort)(component * 257); - - /// - /// Returns the value clamped to the inclusive range of min and max. - /// 5x Faster than - /// on platforms < NET 5. - /// - /// The value to clamp. - /// The minimum inclusive value. - /// The maximum inclusive value. - /// The clamped . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector4 Clamp(Vector4 value, Vector4 min, Vector4 max) - => Vector4.Min(Vector4.Max(value, min), max); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static byte DownScaleFrom32BitTo8Bit(uint component) - { - ushort componentAsShort = (ushort)(component >> 16); - return DownScaleFrom16BitTo8Bit(componentAsShort); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static byte DownScaleFrom16BitTo8Bit(ushort component) - { - // To scale to 8 bits From a 16-bit value V the required value (from the PNG specification) is: - // - // (V * 255) / 65535 - // - // This reduces to round(V / 257), or floor((V + 128.5)/257) - // - // Represent V as the two byte value vhi.vlo. Make a guess that the - // result is the top byte of V, vhi, then the correction to this value - // is: - // - // error = floor(((V-vhi.vhi) + 128.5) / 257) - // = floor(((vlo-vhi) + 128.5) / 257) - // - // This can be approximated using integer arithmetic (and a signed - // shift): - // - // error = (vlo-vhi+128) >> 8; - // - // The approximate differs from the exact answer only when (vlo-vhi) is - // 128; it then gives a correction of +1 when the exact correction is - // 0. This gives 128 errors. The exact answer (correct for all 16-bit - // input values) is: - // - // error = (vlo-vhi+128)*65535 >> 24; - // - // An alternative arithmetic calculation which also gives no errors is: - // - // (V * 255 + 32895) >> 16 - return (byte)(((component * 255) + 32895) >> 16); - } + ushort componentAsShort = (ushort)(component >> 16); + return DownScaleFrom16BitTo8Bit(componentAsShort); } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static byte DownScaleFrom16BitTo8Bit(ushort component) => + // To scale to 8 bits From a 16-bit value V the required value (from the PNG specification) is: + // + // (V * 255) / 65535 + // + // This reduces to round(V / 257), or floor((V + 128.5)/257) + // + // Represent V as the two byte value vhi.vlo. Make a guess that the + // result is the top byte of V, vhi, then the correction to this value + // is: + // + // error = floor(((V-vhi.vhi) + 128.5) / 257) + // = floor(((vlo-vhi) + 128.5) / 257) + // + // This can be approximated using integer arithmetic (and a signed + // shift): + // + // error = (vlo-vhi+128) >> 8; + // + // The approximate differs from the exact answer only when (vlo-vhi) is + // 128; it then gives a correction of +1 when the exact correction is + // 0. This gives 128 errors. The exact answer (correct for all 16-bit + // input values) is: + // + // error = (vlo-vhi+128)*65535 >> 24; + // + // An alternative arithmetic calculation which also gives no errors is: + // + // (V * 255 + 32895) >> 16 + (byte)(((component * 255) + 32895) >> 16); } diff --git a/src/ImageSharp.Textures/PixelFormats/R16Float.cs b/src/ImageSharp.Textures/PixelFormats/R16Float.cs index d951de29..719fbe20 100644 --- a/src/ImageSharp.Textures/PixelFormats/R16Float.cs +++ b/src/ImageSharp.Textures/PixelFormats/R16Float.cs @@ -1,154 +1,152 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Textures.Common.Helpers; -namespace SixLabors.ImageSharp.Textures.PixelFormats +namespace SixLabors.ImageSharp.Textures.PixelFormats; + +/// +/// Packed pixel type containing unsigned normalized values only for the red channel ranging from 0 to 1. +/// The x component uses 16 bits. +/// +/// Ranges from [0] to [1] in vector form. +/// +/// +public struct R16Float : IPixel, IPackedVector { + /// + public ushort PackedValue { get; set; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + /// The on the right side of the operand. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(R16Float left, R16Float right) => left.Equals(right); + /// - /// Packed pixel type containing unsigned normalized values only for the red channel ranging from 0 to 1. - /// The x component uses 16 bits. - /// - /// Ranges from [0] to [1] in vector form. - /// + /// Compares two objects for equality. /// - public struct R16Float : IPixel, IPackedVector + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(R16Float left, R16Float right) => !left.Equals(right); + + /// + public readonly PixelOperations CreatePixelOperations() => new(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromVector4(Vector4 vector) + { + Vector2 vector2 = new Vector2(vector.X, vector.Y); + this.PackedValue = Pack(ref vector2); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new( + FloatHelper.UnpackFloat16ToFloat(this.PackedValue), + 0.0f, + 0.0f, + 1.0f); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + public override bool Equals(object? obj) => obj is R16Float other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly bool Equals(R16Float other) => this.PackedValue.Equals(other.PackedValue); + + /// + public override string ToString() + { + Vector4 vector = this.ToVector4(); + return FormattableString.Invariant($"R16f({vector.X:#0.##})"); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ushort Pack(ref Vector2 vector) { - /// - public ushort PackedValue { get; set; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// - /// True if the parameter is equal to the parameter; otherwise, false. - /// - /// The on the right side of the operand. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(R16Float left, R16Float right) => left.Equals(right); - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(R16Float left, R16Float right) => !left.Equals(right); - - /// - public PixelOperations CreatePixelOperations() => new(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() => this.ToVector4(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromVector4(Vector4 vector) - { - var vector2 = new Vector2(vector.X, vector.Y); - this.PackedValue = Pack(ref vector2); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() => new( - FloatHelper.UnpackFloat16ToFloat(this.PackedValue), - 0.0f, - 0.0f, - 1.0f); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - public override bool Equals(object? obj) => obj is R16Float other && this.Equals(other); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(R16Float other) => this.PackedValue.Equals(other.PackedValue); - - /// - public override string ToString() - { - var vector = this.ToVector4(); - return FormattableString.Invariant($"R16f({vector.X:#0.##})"); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => this.PackedValue.GetHashCode(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ushort Pack(ref Vector2 vector) - { - vector = Vector2.Clamp(vector, Vector2.Zero, Vector2.One); - return (ushort)FloatHelper.PackFloatToFloat16(vector.X); - } + vector = Vector2.Clamp(vector, Vector2.Zero, Vector2.One); + return (ushort)FloatHelper.PackFloatToFloat16(vector.X); } } diff --git a/src/ImageSharp.Textures/PixelFormats/Rg16.cs b/src/ImageSharp.Textures/PixelFormats/Rg16.cs index 70a4ef6f..ff347d22 100644 --- a/src/ImageSharp.Textures/PixelFormats/Rg16.cs +++ b/src/ImageSharp.Textures/PixelFormats/Rg16.cs @@ -1,174 +1,172 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Textures.PixelFormats +namespace SixLabors.ImageSharp.Textures.PixelFormats; + +/// +/// Packed pixel type containing two 8-bit unsigned normalized values ranging from 0 to 1. +/// +/// Ranges from [0, 0, 0, 1] to [1, 1, 0, 1] in vector form. +/// +/// +public struct Rg16 : IPixel, IPackedVector { + private static readonly Vector2 Max = new(byte.MaxValue); + + /// + /// Initializes a new instance of the struct. + /// + /// The x-component + /// The y-component + public Rg16(float x, float y) + : this(new Vector2(x, y)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The vector containing the component values. + public Rg16(Vector2 vector) => this.PackedValue = (ushort)Pack(vector); + + /// + public ushort PackedValue { get; set; } + /// - /// Packed pixel type containing two 8-bit unsigned normalized values ranging from 0 to 1. - /// - /// Ranges from [0, 0, 0, 1] to [1, 1, 0, 1] in vector form. - /// + /// Compares two objects for equality. /// - public struct Rg16 : IPixel, IPackedVector + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Rg16 left, Rg16 right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Rg16 left, Rg16 right) => !left.Equals(right); + + /// + public readonly PixelOperations CreatePixelOperations() => new(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromVector4(Vector4 vector) + { + Vector2 vector2 = new Vector2(vector.X, vector.Y); + this.PackedValue = (ushort)Pack(vector2); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 ToVector4() => new(this.ToVector2(), 0F, 1F); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + /// Expands the packed representation into a . + /// The vector components are typically expanded in least to greatest significance order. + /// + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector2 ToVector2() => new Vector2(this.PackedValue & 0xFF, (this.PackedValue >> 8) & 0xFF) / Max; + + /// + public override bool Equals(object? obj) => obj is Rg16 other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly bool Equals(Rg16 other) => this.PackedValue.Equals(other.PackedValue); + + /// + public override string ToString() + { + Vector2 vector = this.ToVector2(); + return FormattableString.Invariant($"Rg16({vector.X:#0.##}, {vector.Y:#0.##})"); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint Pack(Vector2 vector) { - private static readonly Vector2 Max = new(byte.MaxValue); - - /// - /// Initializes a new instance of the struct. - /// - /// The x-component - /// The y-component - public Rg16(float x, float y) - : this(new Vector2(x, y)) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector containing the component values. - public Rg16(Vector2 vector) => this.PackedValue = (ushort)Pack(vector); - - /// - public ushort PackedValue { get; set; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the parameter is equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Rg16 left, Rg16 right) => left.Equals(right); - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Rg16 left, Rg16 right) => !left.Equals(right); - - /// - public PixelOperations CreatePixelOperations() => new(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() => this.ToVector4(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromVector4(Vector4 vector) - { - var vector2 = new Vector2(vector.X, vector.Y); - this.PackedValue = (ushort)Pack(vector2); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() => new(this.ToVector2(), 0F, 1F); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - /// Expands the packed representation into a . - /// The vector components are typically expanded in least to greatest significance order. - /// - /// The . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector2 ToVector2() => new Vector2(this.PackedValue & 0xFF, (this.PackedValue >> 8) & 0xFF) / Max; - - /// - public override bool Equals(object? obj) => obj is Rg16 other && this.Equals(other); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Rg16 other) => this.PackedValue.Equals(other.PackedValue); - - /// - public override string ToString() - { - var vector = this.ToVector2(); - return FormattableString.Invariant($"Rg16({vector.X:#0.##}, {vector.Y:#0.##})"); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => this.PackedValue.GetHashCode(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint Pack(Vector2 vector) - { - vector = Vector2.Clamp(vector, Vector2.Zero, Vector2.One) * Max; - return (uint)(((int)Math.Round(vector.X) & 0xFF) | (((int)Math.Round(vector.Y) & 0xFF) << 8)); - } + vector = Vector2.Clamp(vector, Vector2.Zero, Vector2.One) * Max; + return (uint)(((int)Math.Round(vector.X) & 0xFF) | (((int)Math.Round(vector.Y) & 0xFF) << 8)); } } diff --git a/src/ImageSharp.Textures/PixelFormats/Rg32.cs b/src/ImageSharp.Textures/PixelFormats/Rg32.cs index d87dc6b2..372e7a82 100644 --- a/src/ImageSharp.Textures/PixelFormats/Rg32.cs +++ b/src/ImageSharp.Textures/PixelFormats/Rg32.cs @@ -1,174 +1,172 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Textures.PixelFormats +namespace SixLabors.ImageSharp.Textures.PixelFormats; + +/// +/// Packed pixel type containing two 16-bit unsigned normalized values ranging from 0 to 1. +/// +/// Ranges from [0, 0, 0, 1] to [1, 1, 0, 1] in vector form. +/// +/// +public struct Rg32 : IPixel, IPackedVector { + private static readonly Vector2 Max = new(byte.MaxValue); + + /// + /// Initializes a new instance of the struct. + /// + /// The x-component + /// The y-component + public Rg32(float x, float y) + : this(new Vector2(x, y)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The vector containing the component values. + public Rg32(Vector2 vector) => this.PackedValue = Pack(vector); + + /// + public uint PackedValue { get; set; } + /// - /// Packed pixel type containing two 16-bit unsigned normalized values ranging from 0 to 1. - /// - /// Ranges from [0, 0, 0, 1] to [1, 1, 0, 1] in vector form. - /// + /// Compares two objects for equality. /// - public struct Rg32 : IPixel, IPackedVector + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Rg32 left, Rg32 right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Rg32 left, Rg32 right) => !left.Equals(right); + + /// + public readonly PixelOperations CreatePixelOperations() => new(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromVector4(Vector4 vector) + { + Vector2 vector2 = new Vector2(vector.X, vector.Y); + this.PackedValue = (ushort)Pack(vector2); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 ToVector4() => new(this.ToVector2(), 0F, 1F); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + /// Expands the packed representation into a . + /// The vector components are typically expanded in least to greatest significance order. + /// + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector2 ToVector2() => new Vector2(this.PackedValue & 0xFF, (this.PackedValue >> 8) & 0xFF) / Max; + + /// + public override bool Equals(object? obj) => obj is Rg32 other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly bool Equals(Rg32 other) => this.PackedValue.Equals(other.PackedValue); + + /// + public override string ToString() + { + Vector2 vector = this.ToVector2(); + return FormattableString.Invariant($"Rg32({vector.X:#0.##}, {vector.Y:#0.##})"); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override readonly int GetHashCode() => this.PackedValue.GetHashCode(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint Pack(Vector2 vector) { - private static readonly Vector2 Max = new(byte.MaxValue); - - /// - /// Initializes a new instance of the struct. - /// - /// The x-component - /// The y-component - public Rg32(float x, float y) - : this(new Vector2(x, y)) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector containing the component values. - public Rg32(Vector2 vector) => this.PackedValue = Pack(vector); - - /// - public uint PackedValue { get; set; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the parameter is equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Rg32 left, Rg32 right) => left.Equals(right); - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Rg32 left, Rg32 right) => !left.Equals(right); - - /// - public PixelOperations CreatePixelOperations() => new(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() => this.ToVector4(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromVector4(Vector4 vector) - { - var vector2 = new Vector2(vector.X, vector.Y); - this.PackedValue = (ushort)Pack(vector2); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() => new(this.ToVector2(), 0F, 1F); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); - - /// - /// Expands the packed representation into a . - /// The vector components are typically expanded in least to greatest significance order. - /// - /// The . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector2 ToVector2() => new Vector2(this.PackedValue & 0xFF, (this.PackedValue >> 8) & 0xFF) / Max; - - /// - public override bool Equals(object? obj) => obj is Rg32 other && this.Equals(other); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Rg32 other) => this.PackedValue.Equals(other.PackedValue); - - /// - public override string ToString() - { - var vector = this.ToVector2(); - return FormattableString.Invariant($"Rg32({vector.X:#0.##}, {vector.Y:#0.##})"); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => this.PackedValue.GetHashCode(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint Pack(Vector2 vector) - { - vector = Vector2.Clamp(vector, Vector2.Zero, Vector2.One) * Max; - return (uint)(((int)Math.Round(vector.X) & 0xFF) | (((int)Math.Round(vector.Y) & 0xFF) << 8)); - } + vector = Vector2.Clamp(vector, Vector2.Zero, Vector2.One) * Max; + return (uint)(((int)Math.Round(vector.X) & 0xFF) | (((int)Math.Round(vector.Y) & 0xFF) << 8)); } } diff --git a/src/ImageSharp.Textures/PixelFormats/Y410.cs b/src/ImageSharp.Textures/PixelFormats/Y410.cs index 701a1456..20dc0591 100644 --- a/src/ImageSharp.Textures/PixelFormats/Y410.cs +++ b/src/ImageSharp.Textures/PixelFormats/Y410.cs @@ -1,150 +1,148 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Textures.PixelFormats +namespace SixLabors.ImageSharp.Textures.PixelFormats; + +/// +/// Pixel format based on 16-bit per channel packed YUV 4:4:4 data. +/// +public struct Y410 : IPixel, IPackedVector { + /// + public uint PackedValue { get; set; } + /// - /// Pixel format based on 16-bit per channel packed YUV 4:4:4 data. + /// Gets or sets the packed representation of the Y410 struct. /// - public struct Y410 : IPixel, IPackedVector + public uint Yuv { - /// - public uint PackedValue { get; set; } - - /// - /// Gets or sets the packed representation of the Y410 struct. - /// - public uint Yuv - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - readonly get => Unsafe.As(ref Unsafe.AsRef(in this)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - set => Unsafe.As(ref this) = value; - } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the parameter is equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Y410 left, Y410 right) => left.Equals(right); - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Y410 left, Y410 right) => !left.Equals(right); + readonly get => Unsafe.As(ref Unsafe.AsRef(in this)); - /// - public override readonly bool Equals(object? obj) => obj is Y410 other && this.Equals(other); - - /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly bool Equals(Y410 other) => this.Yuv.Equals(other.Yuv); + set => Unsafe.As(ref this) = value; + } - /// - public override readonly string ToString() - { - var vector = this.ToVector4(); - return FormattableString.Invariant($"Y416({vector.X:#0.##}, {vector.Y:#0.##}, {vector.Z:#0.##}, {vector.W:#0.##})"); - } + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Y410 left, Y410 right) => left.Equals(right); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public PixelOperations CreatePixelOperations() => new(); + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Y410 left, Y410 right) => !left.Equals(right); + + /// + public override readonly bool Equals(object? obj) => obj is Y410 other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly bool Equals(Y410 other) => this.Yuv.Equals(other.Yuv); + + /// + public override readonly string ToString() + { + Vector4 vector = this.ToVector4(); + return FormattableString.Invariant($"Y416({vector.X:#0.##}, {vector.Y:#0.##}, {vector.Z:#0.##}, {vector.W:#0.##})"); + } - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override readonly int GetHashCode() => this.Yuv.GetHashCode(); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly PixelOperations CreatePixelOperations() => new(); - /// - public void FromArgb32(Argb32 source) => throw new NotImplementedException(); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override readonly int GetHashCode() => this.Yuv.GetHashCode(); - /// - public void FromBgr24(Bgr24 source) => throw new NotImplementedException(); + /// + public void FromArgb32(Argb32 source) => throw new NotImplementedException(); - /// - public void FromBgra32(Bgra32 source) => throw new NotImplementedException(); + /// + public void FromBgr24(Bgr24 source) => throw new NotImplementedException(); - /// - public void FromAbgr32(Abgr32 source) => throw new NotImplementedException(); + /// + public void FromBgra32(Bgra32 source) => throw new NotImplementedException(); - /// - public void FromBgra5551(Bgra5551 source) => throw new NotImplementedException(); + /// + public void FromAbgr32(Abgr32 source) => throw new NotImplementedException(); - /// - public void FromL16(L16 source) => throw new NotImplementedException(); + /// + public void FromBgra5551(Bgra5551 source) => throw new NotImplementedException(); - /// - public void FromL8(L8 source) => throw new NotImplementedException(); + /// + public void FromL16(L16 source) => throw new NotImplementedException(); - /// - public void FromLa16(La16 source) => throw new NotImplementedException(); + /// + public void FromL8(L8 source) => throw new NotImplementedException(); - /// - public void FromLa32(La32 source) => throw new NotImplementedException(); + /// + public void FromLa16(La16 source) => throw new NotImplementedException(); - /// - public void FromRgb24(Rgb24 source) => throw new NotImplementedException(); + /// + public void FromLa32(La32 source) => throw new NotImplementedException(); - /// - public void FromRgb48(Rgb48 source) => throw new NotImplementedException(); + /// + public void FromRgb24(Rgb24 source) => throw new NotImplementedException(); - /// - public void FromRgba32(Rgba32 source) => throw new NotImplementedException(); + /// + public void FromRgb48(Rgb48 source) => throw new NotImplementedException(); - /// - public void FromRgba64(Rgba64 source) => throw new NotImplementedException(); + /// + public void FromRgba32(Rgba32 source) => throw new NotImplementedException(); - /// - public void FromScaledVector4(Vector4 vector) => throw new NotImplementedException(); + /// + public void FromRgba64(Rgba64 source) => throw new NotImplementedException(); - /// - public void FromVector4(Vector4 vector) => throw new NotImplementedException(); + /// + public void FromScaledVector4(Vector4 vector) => throw new NotImplementedException(); - /// - public void ToRgba32(ref Rgba32 dest) => throw new NotImplementedException(); + /// + public void FromVector4(Vector4 vector) => throw new NotImplementedException(); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly Vector4 ToScaledVector4() => this.ToVector4(); + /// + public void ToRgba32(ref Rgba32 dest) => throw new NotImplementedException(); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly Vector4 ToVector4() - { - uint u = (this.PackedValue >> 0) & 0x03FF; - uint y = (this.PackedValue >> 10) & 0x03FF; - uint v = (this.PackedValue >> 20) & 0x03FF; - uint a = (this.PackedValue >> 30) & 0x03; - - // http://msdn.microsoft.com/en-us/library/windows/desktop/bb970578.aspx - // Y' = Y - 64 - // Cb' = Cb - 512 - // Cr' = Cr - 512 - y -= 64; - u -= 512; - v -= 512; - - // R = 1.1678Y' + 1.6007Cr' - // G = 1.1678Y' - 0.3929Cb' - 0.8152Cr' - // B = 1.1678Y' + 2.0232Cb' - return ColorSpaceConversion.YuvToRgba10Bit(y, u, v, a); - } + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() + { + uint u = (this.PackedValue >> 0) & 0x03FF; + uint y = (this.PackedValue >> 10) & 0x03FF; + uint v = (this.PackedValue >> 20) & 0x03FF; + uint a = (this.PackedValue >> 30) & 0x03; + + // http://msdn.microsoft.com/en-us/library/windows/desktop/bb970578.aspx + // Y' = Y - 64 + // Cb' = Cb - 512 + // Cr' = Cr - 512 + y -= 64; + u -= 512; + v -= 512; + + // R = 1.1678Y' + 1.6007Cr' + // G = 1.1678Y' - 0.3929Cb' - 0.8152Cr' + // B = 1.1678Y' + 2.0232Cb' + return ColorSpaceConversion.YuvToRgba10Bit(y, u, v, a); } } diff --git a/src/ImageSharp.Textures/PixelFormats/Y416.cs b/src/ImageSharp.Textures/PixelFormats/Y416.cs index 33887f04..ecc42eb4 100644 --- a/src/ImageSharp.Textures/PixelFormats/Y416.cs +++ b/src/ImageSharp.Textures/PixelFormats/Y416.cs @@ -1,148 +1,146 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Textures.PixelFormats +namespace SixLabors.ImageSharp.Textures.PixelFormats; + +/// +/// Pixel format for 16-bit per channel packed YUV 4:4:4 data. +/// +public struct Y416 : IPixel, IPackedVector { + /// + public ulong PackedValue { get; set; } + /// - /// Pixel format for 16-bit per channel packed YUV 4:4:4 data. + /// Gets or sets the packed representation of the Y416 struct. /// - public struct Y416 : IPixel, IPackedVector + public ulong Yuv { - /// - public ulong PackedValue { get; set; } - - /// - /// Gets or sets the packed representation of the Y416 struct. - /// - public ulong Yuv - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - readonly get => Unsafe.As(ref Unsafe.AsRef(in this)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - set => Unsafe.As(ref this) = value; - } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the parameter is equal to the parameter; otherwise, false. - /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Y416 left, Y416 right) => left.Equals(right); - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// + readonly get => Unsafe.As(ref Unsafe.AsRef(in this)); + [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Y416 left, Y416 right) => !left.Equals(right); + set => Unsafe.As(ref this) = value; + } - /// - public override readonly bool Equals(object? obj) => obj is Y416 other && this.Equals(other); + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Y416 left, Y416 right) => left.Equals(right); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly bool Equals(Y416 other) => this.Yuv.Equals(other.Yuv); + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Y416 left, Y416 right) => !left.Equals(right); + + /// + public override readonly bool Equals(object? obj) => obj is Y416 other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly bool Equals(Y416 other) => this.Yuv.Equals(other.Yuv); + + /// + public override readonly string ToString() + { + Vector4 vector = this.ToVector4(); + return FormattableString.Invariant($"Y416({vector.X:#0.##}, {vector.Y:#0.##}, {vector.Z:#0.##}, {vector.W:#0.##})"); + } - /// - public override readonly string ToString() - { - var vector = this.ToVector4(); - return FormattableString.Invariant($"Y416({vector.X:#0.##}, {vector.Y:#0.##}, {vector.Z:#0.##}, {vector.W:#0.##})"); - } + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly PixelOperations CreatePixelOperations() => new(); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public PixelOperations CreatePixelOperations() => new(); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override readonly int GetHashCode() => this.Yuv.GetHashCode(); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override readonly int GetHashCode() => this.Yuv.GetHashCode(); + /// + public void FromArgb32(Argb32 source) => throw new NotImplementedException(); - /// - public void FromArgb32(Argb32 source) => throw new NotImplementedException(); + /// + public void FromBgr24(Bgr24 source) => throw new NotImplementedException(); - /// - public void FromBgr24(Bgr24 source) => throw new NotImplementedException(); + /// + public void FromBgra32(Bgra32 source) => throw new NotImplementedException(); - /// - public void FromBgra32(Bgra32 source) => throw new NotImplementedException(); + /// + public void FromAbgr32(Abgr32 source) => throw new NotImplementedException(); - /// - public void FromAbgr32(Abgr32 source) => throw new NotImplementedException(); + /// + public void FromBgra5551(Bgra5551 source) => throw new NotImplementedException(); - /// - public void FromBgra5551(Bgra5551 source) => throw new NotImplementedException(); + /// + public void FromL16(L16 source) => throw new NotImplementedException(); - /// - public void FromL16(L16 source) => throw new NotImplementedException(); + /// + public void FromL8(L8 source) => throw new NotImplementedException(); - /// - public void FromL8(L8 source) => throw new NotImplementedException(); + /// + public void FromLa16(La16 source) => throw new NotImplementedException(); - /// - public void FromLa16(La16 source) => throw new NotImplementedException(); + /// + public void FromLa32(La32 source) => throw new NotImplementedException(); - /// - public void FromLa32(La32 source) => throw new NotImplementedException(); + /// + public void FromRgb24(Rgb24 source) => throw new NotImplementedException(); - /// - public void FromRgb24(Rgb24 source) => throw new NotImplementedException(); + /// + public void FromRgb48(Rgb48 source) => throw new NotImplementedException(); - /// - public void FromRgb48(Rgb48 source) => throw new NotImplementedException(); + /// + public void FromRgba32(Rgba32 source) => throw new NotImplementedException(); - /// - public void FromRgba32(Rgba32 source) => throw new NotImplementedException(); + /// + public void FromRgba64(Rgba64 source) => throw new NotImplementedException(); - /// - public void FromRgba64(Rgba64 source) => throw new NotImplementedException(); + /// + public void FromScaledVector4(Vector4 vector) => throw new NotImplementedException(); - /// - public void FromScaledVector4(Vector4 vector) => throw new NotImplementedException(); + /// + public void FromVector4(Vector4 vector) => throw new NotImplementedException(); - /// - public void FromVector4(Vector4 vector) => throw new NotImplementedException(); + /// + public void ToRgba32(ref Rgba32 dest) => throw new NotImplementedException(); - /// - public void ToRgba32(ref Rgba32 dest) => throw new NotImplementedException(); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToScaledVector4() => this.ToVector4(); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly Vector4 ToScaledVector4() => this.ToVector4(); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() + { + uint u = (uint)(this.PackedValue & 0xFFFF); + uint y = (uint)((this.PackedValue >> 16) & 0xFFFF); + uint v = (uint)((this.PackedValue >> 32) & 0xFFFF); + uint a = (uint)((this.PackedValue >> 48) & 0xFFFF); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly Vector4 ToVector4() - { - uint u = (uint)(this.PackedValue & 0xFFFF); - uint y = (uint)((this.PackedValue >> 16) & 0xFFFF); - uint v = (uint)((this.PackedValue >> 32) & 0xFFFF); - uint a = (uint)((this.PackedValue >> 48) & 0xFFFF); - - // http://msdn.microsoft.com/en-us/library/windows/desktop/bb970578.aspx - - // Y' = Y - 4096 - // Cb' = Cb - 32768 - // Cr' = Cr - 32768 - y -= 4096; - u -= 32768; - v -= 32768; - - return ColorSpaceConversion.YuvToRgba16Bit(y, u, v, a); - } + // http://msdn.microsoft.com/en-us/library/windows/desktop/bb970578.aspx + + // Y' = Y - 4096 + // Cb' = Cb - 32768 + // Cr' = Cr - 32768 + y -= 4096; + u -= 32768; + v -= 32768; + + return ColorSpaceConversion.YuvToRgba16Bit(y, u, v, a); } } diff --git a/src/ImageSharp.Textures/Texture.FromStream.cs b/src/ImageSharp.Textures/Texture.FromStream.cs index fcd7dfd1..36d2a454 100644 --- a/src/ImageSharp.Textures/Texture.FromStream.cs +++ b/src/ImageSharp.Textures/Texture.FromStream.cs @@ -181,7 +181,7 @@ private static T WithSeekableStream(Configuration config, Stream stream, Func } // We want to be able to load images from things like HttpContext.Request.Body - using var memoryStream = new MemoryStream(); + using MemoryStream memoryStream = new MemoryStream(); stream.CopyTo(memoryStream); memoryStream.Position = 0; diff --git a/src/ImageSharp.Textures/Texture.cs b/src/ImageSharp.Textures/Texture.cs index c4f97e47..f6ddce89 100644 --- a/src/ImageSharp.Textures/Texture.cs +++ b/src/ImageSharp.Textures/Texture.cs @@ -1,31 +1,28 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; +namespace SixLabors.ImageSharp.Textures; -namespace SixLabors.ImageSharp.Textures +/// +/// Represents a texture. +/// +public abstract partial class Texture : IDisposable { - /// - /// Represents a texture. - /// - public abstract partial class Texture : IDisposable + /// + public void Dispose() { - /// - public void Dispose() - { - this.Dispose(true); - GC.SuppressFinalize(this); - } + this.Dispose(true); + GC.SuppressFinalize(this); + } - /// - /// Throws if the image is disposed. - /// - internal abstract void EnsureNotDisposed(); + /// + /// Throws if the image is disposed. + /// + internal abstract void EnsureNotDisposed(); - /// - /// Disposes the object and frees resources for the Garbage Collector. - /// - /// Whether to dispose of managed and unmanaged objects. - protected abstract void Dispose(bool disposing); - } + /// + /// Disposes the object and frees resources for the Garbage Collector. + /// + /// Whether to dispose of managed and unmanaged objects. + protected abstract void Dispose(bool disposing); } diff --git a/src/ImageSharp.Textures/TextureFormats/CubemapTexture.cs b/src/ImageSharp.Textures/TextureFormats/CubemapTexture.cs index fc51a3d1..5a1d1864 100644 --- a/src/ImageSharp.Textures/TextureFormats/CubemapTexture.cs +++ b/src/ImageSharp.Textures/TextureFormats/CubemapTexture.cs @@ -1,84 +1,81 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; +namespace SixLabors.ImageSharp.Textures.TextureFormats; -namespace SixLabors.ImageSharp.Textures.TextureFormats +/// +/// Represents a cube map texture. +/// +/// +public class CubemapTexture : Texture { + private bool isDisposed; + /// - /// Represents a cube map texture. + /// Initializes a new instance of the class. /// - /// - public class CubemapTexture : Texture + public CubemapTexture() { - private bool isDisposed; - - /// - /// Initializes a new instance of the class. - /// - public CubemapTexture() - { - this.PositiveX = new FlatTexture(); - this.NegativeX = new FlatTexture(); - this.PositiveY = new FlatTexture(); - this.NegativeY = new FlatTexture(); - this.PositiveZ = new FlatTexture(); - this.NegativeZ = new FlatTexture(); - } + this.PositiveX = new FlatTexture(); + this.NegativeX = new FlatTexture(); + this.PositiveY = new FlatTexture(); + this.NegativeY = new FlatTexture(); + this.PositiveZ = new FlatTexture(); + this.NegativeZ = new FlatTexture(); + } - /// - /// Gets the positive x texture. - /// - public FlatTexture PositiveX { get; } + /// + /// Gets the positive x texture. + /// + public FlatTexture PositiveX { get; } - /// - /// Gets the negative x texture. - /// - public FlatTexture NegativeX { get; } + /// + /// Gets the negative x texture. + /// + public FlatTexture NegativeX { get; } - /// - /// Gets the positive y texture. - /// - public FlatTexture PositiveY { get; } + /// + /// Gets the positive y texture. + /// + public FlatTexture PositiveY { get; } - /// - /// Gets the negative y texture. - /// - public FlatTexture NegativeY { get; } + /// + /// Gets the negative y texture. + /// + public FlatTexture NegativeY { get; } - /// - /// Gets the positive z texture. - /// - public FlatTexture PositiveZ { get; } + /// + /// Gets the positive z texture. + /// + public FlatTexture PositiveZ { get; } - /// - /// Gets the negative z texture. - /// - public FlatTexture NegativeZ { get; } + /// + /// Gets the negative z texture. + /// + public FlatTexture NegativeZ { get; } - /// - protected override void Dispose(bool disposing) + /// + protected override void Dispose(bool disposing) + { + if (this.isDisposed) { - if (this.isDisposed) - { - return; - } - - if (disposing) - { - this.PositiveX.Dispose(); - this.NegativeX.Dispose(); - this.PositiveY.Dispose(); - this.NegativeY.Dispose(); - this.PositiveZ.Dispose(); - this.NegativeZ.Dispose(); - } + return; + } - this.isDisposed = true; + if (disposing) + { + this.PositiveX.Dispose(); + this.NegativeX.Dispose(); + this.PositiveY.Dispose(); + this.NegativeY.Dispose(); + this.PositiveZ.Dispose(); + this.NegativeZ.Dispose(); } - /// - internal override void EnsureNotDisposed() - => ObjectDisposedException.ThrowIf(this.isDisposed, "Trying to execute an operation on a disposed image."); + this.isDisposed = true; } + + /// + internal override void EnsureNotDisposed() + => ObjectDisposedException.ThrowIf(this.isDisposed, "Trying to execute an operation on a disposed image."); } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/A8.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/A8.cs index 98be4a8d..3269fe4b 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/A8.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/A8.cs @@ -1,36 +1,35 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding -{ - /// - /// Texture for pixel data which only contains the alpha value. - /// - internal struct A8 : IBlock - { - /// - public int BitsPerPixel => 8; +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; - /// - public byte PixelDepthBytes => 1; +/// +/// Texture for pixel data which only contains the alpha value. +/// +internal struct A8 : IBlock +{ + /// + public readonly int BitsPerPixel => 8; - /// - public byte DivSize => 1; + /// + public readonly byte PixelDepthBytes => 1; - /// - public byte CompressedBytesPerBlock => 1; + /// + public readonly byte DivSize => 1; - /// - public bool Compressed => false; + /// + public readonly byte CompressedBytesPerBlock => 1; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public readonly bool Compressed => false; - /// - public byte[] Decompress(byte[] blockData, int width, int height) => blockData; + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); } + + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) => blockData; } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/AstcDecoder.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/AstcDecoder.cs new file mode 100644 index 00000000..f0d42dd2 --- /dev/null +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/AstcDecoder.cs @@ -0,0 +1,146 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Textures.Astc.Core; + +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; + +/// +/// ASTC (Adaptive scalable texture compression) decoder for all valid block footprints. +/// +internal static class AstcDecoder +{ + internal const int AstcBlockSize = 16; + + /// + /// Decodes an ASTC block into RGBA pixels. + /// + /// The 16-byte ASTC block data. + /// The width of the block footprint (4-12). + /// The height of the block footprint (4-12). + /// The output span for decoded RGBA pixels. + /// Thrown if blockData is not 16 bytes or decodedPixels is the wrong size. + /// Thrown if the block dimensions are invalid. + public static void DecodeBlock(ReadOnlySpan blockData, int blockWidth, int blockHeight, Span decodedPixels) + { + if (blockData.Length != AstcBlockSize) + { + throw new ArgumentException($"ASTC block data must be exactly {AstcBlockSize} bytes. Received {blockData.Length} bytes.", nameof(blockData)); + } + + int expectedDecodedSize = blockWidth * blockHeight * 4; + if (decodedPixels.Length < expectedDecodedSize) + { + throw new ArgumentException($"Output buffer must be at least {expectedDecodedSize} bytes for {blockWidth}x{blockHeight} block. Received {decodedPixels.Length} bytes.", nameof(decodedPixels)); + } + + Footprint footprint = Footprint.FromFootprintType(FootprintFromDimensions(blockWidth, blockHeight)); + + Astc.AstcDecoder.DecompressBlock(blockData, footprint, decodedPixels); + } + + /// + /// Decompresses ASTC-compressed image data to RGBA pixels. + /// + /// The compressed block data. + /// The width of the texture. + /// The height of the texture. + /// The width of the block footprint. + /// The height of the block footprint. + /// The number of compressed bytes per block. + /// The decompressed RGBA pixel data. + /// Thrown if blockData is null. + /// Thrown if dimensions or block parameters are invalid. + /// Thrown if blockData length is invalid. + public static byte[] DecompressImage( + byte[] blockData, + int width, + int height, + int blockWidth, + int blockHeight, + byte compressedBytesPerBlock) + { + ArgumentNullException.ThrowIfNull(blockData); + ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(width, 0); + ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(height, 0); + + if (compressedBytesPerBlock != AstcBlockSize) + { + throw new ArgumentOutOfRangeException(nameof(compressedBytesPerBlock), compressedBytesPerBlock, $"ASTC blocks must be {AstcBlockSize} bytes."); + } + + // Validate block dimensions (will throw if invalid) + _ = FootprintFromDimensions(blockWidth, blockHeight); + + int blocksWide = (width + blockWidth - 1) / blockWidth; + int blocksHigh = (height + blockHeight - 1) / blockHeight; + int totalBlocks = blocksWide * blocksHigh; + int expectedDataLength = totalBlocks * compressedBytesPerBlock; + + if (blockData.Length < expectedDataLength) + { + throw new ArgumentException($"Block data is too small. Expected at least {expectedDataLength} bytes for {width}x{height} texture with {blockWidth}x{blockHeight} blocks, but received {blockData.Length} bytes.", nameof(blockData)); + } + + byte[] decompressedData = new byte[width * height * 4]; + byte[] decodedBlock = new byte[blockWidth * blockHeight * 4]; + + int blockIndex = 0; + + for (int by = 0; by < blocksHigh; by++) + { + for (int bx = 0; bx < blocksWide; bx++) + { + int blockDataOffset = blockIndex * compressedBytesPerBlock; + if (blockDataOffset + compressedBytesPerBlock <= blockData.Length) + { + DecodeBlock( + blockData.AsSpan(blockDataOffset, compressedBytesPerBlock), + blockWidth, + blockHeight, + decodedBlock); + + for (int py = 0; py < blockHeight && ((by * blockHeight) + py) < height; py++) + { + for (int px = 0; px < blockWidth && ((bx * blockWidth) + px) < width; px++) + { + int srcIndex = ((py * blockWidth) + px) * 4; + int dstX = (bx * blockWidth) + px; + int dstY = (by * blockHeight) + py; + int dstIndex = ((dstY * width) + dstX) * 4; + + decompressedData[dstIndex] = decodedBlock[srcIndex]; + decompressedData[dstIndex + 1] = decodedBlock[srcIndex + 1]; + decompressedData[dstIndex + 2] = decodedBlock[srcIndex + 2]; + decompressedData[dstIndex + 3] = decodedBlock[srcIndex + 3]; + } + } + } + + blockIndex++; + } + } + + return decompressedData; + } + + private static FootprintType FootprintFromDimensions(int width, int height) + => (width, height) switch + { + (4, 4) => FootprintType.Footprint4x4, + (5, 4) => FootprintType.Footprint5x4, + (5, 5) => FootprintType.Footprint5x5, + (6, 5) => FootprintType.Footprint6x5, + (6, 6) => FootprintType.Footprint6x6, + (8, 5) => FootprintType.Footprint8x5, + (8, 6) => FootprintType.Footprint8x6, + (8, 8) => FootprintType.Footprint8x8, + (10, 5) => FootprintType.Footprint10x5, + (10, 6) => FootprintType.Footprint10x6, + (10, 8) => FootprintType.Footprint10x8, + (10, 10) => FootprintType.Footprint10x10, + (12, 10) => FootprintType.Footprint12x10, + (12, 12) => FootprintType.Footprint12x12, + _ => throw new ArgumentOutOfRangeException(nameof(width), $"Invalid ASTC block dimensions: {width}x{height}. Valid sizes are 4x4, 5x4, 5x5, 6x5, 6x6, 8x5, 8x6, 8x8, 10x5, 10x6, 10x8, 10x10, 12x10, 12x12."), + }; +} diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Ayuv.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Ayuv.cs index 7277cba0..ceff2887 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Ayuv.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Ayuv.cs @@ -1,36 +1,35 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding -{ - /// - /// Texture for YUV 4:4:4 pixel data. - /// - internal struct Ayuv : IBlock - { - /// - public int BitsPerPixel => 32; +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; - /// - public byte PixelDepthBytes => 4; +/// +/// Texture for YUV 4:4:4 pixel data. +/// +internal struct Ayuv : IBlock +{ + /// + public readonly int BitsPerPixel => 32; - /// - public byte DivSize => 1; + /// + public readonly byte PixelDepthBytes => 4; - /// - public byte CompressedBytesPerBlock => 4; + /// + public readonly byte DivSize => 1; - /// - public bool Compressed => false; + /// + public readonly byte CompressedBytesPerBlock => 4; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public readonly bool Compressed => false; - /// - public byte[] Decompress(byte[] blockData, int width, int height) => blockData; + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); } + + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) => blockData; } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Bc4.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Bc4.cs index 9fc80456..3ff52e04 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Bc4.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Bc4.cs @@ -3,106 +3,105 @@ using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; + +/// +/// Texture compressed with BC4 with one color channel (8 bits). +/// +internal struct Bc4 : IBlock { - /// - /// Texture compressed with BC4 with one color channel (8 bits). - /// - internal struct Bc4 : IBlock - { - /// - public int BitsPerPixel => 8; + /// + public readonly int BitsPerPixel => 8; - /// - public byte PixelDepthBytes => 1; + /// + public readonly byte PixelDepthBytes => 1; - /// - public byte DivSize => 4; + /// + public readonly byte DivSize => 4; - /// - public byte CompressedBytesPerBlock => 8; + /// + public readonly byte CompressedBytesPerBlock => 8; - /// - public bool Compressed => true; + /// + public readonly bool Compressed => true; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); + } - /// - public byte[] Decompress(byte[] blockData, int width, int height) + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) + { + Bc4 self = this; + + return Helper.InMemoryDecode(blockData, width, height, (stream, data, streamIndex, dataIndex, stride) => { - Bc4 self = this; + byte red0 = blockData[streamIndex++]; + byte red1 = blockData[streamIndex++]; + ulong rIndex = blockData[streamIndex++]; + rIndex |= (ulong)blockData[streamIndex++] << 8; + rIndex |= (ulong)blockData[streamIndex++] << 16; + rIndex |= (ulong)blockData[streamIndex++] << 24; + rIndex |= (ulong)blockData[streamIndex++] << 32; + rIndex |= (ulong)blockData[streamIndex++] << 40; - return Helper.InMemoryDecode(blockData, width, height, (stream, data, streamIndex, dataIndex, stride) => + for (int i = 0; i < 16; ++i) { - byte red0 = blockData[streamIndex++]; - byte red1 = blockData[streamIndex++]; - ulong rIndex = blockData[streamIndex++]; - rIndex |= (ulong)blockData[streamIndex++] << 8; - rIndex |= (ulong)blockData[streamIndex++] << 16; - rIndex |= (ulong)blockData[streamIndex++] << 24; - rIndex |= (ulong)blockData[streamIndex++] << 32; - rIndex |= (ulong)blockData[streamIndex++] << 40; - - for (int i = 0; i < 16; ++i) - { - byte index = (byte)((uint)(rIndex >> (3 * i)) & 0x07); + byte index = (byte)((uint)(rIndex >> (3 * i)) & 0x07); - data[dataIndex++] = InterpolateColor(index, red0, red1); + data[dataIndex++] = InterpolateColor(index, red0, red1); - // Is mult 4? - if (((i + 1) & 0x3) == 0) - { - dataIndex += self.PixelDepthBytes * (stride - self.DivSize); - } + // Is mult 4? + if (((i + 1) & 0x3) == 0) + { + dataIndex += self.PixelDepthBytes * (stride - self.DivSize); } + } - return streamIndex; - }); - } + return streamIndex; + }); + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static byte InterpolateColor(byte index, byte red0, byte red1) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static byte InterpolateColor(byte index, byte red0, byte red1) + { + byte red; + if (index == 0) { - byte red; - if (index == 0) - { - red = red0; - } - else if (index == 1) + red = red0; + } + else if (index == 1) + { + red = red1; + } + else + { + if (red0 > red1) { - red = red1; + index -= 1; + red = (byte)((((red0 * (7 - index)) + (red1 * index)) / 7.0f) + 0.5f); } else { - if (red0 > red1) + if (index == 6) { - index -= 1; - red = (byte)((((red0 * (7 - index)) + (red1 * index)) / 7.0f) + 0.5f); + red = 0; + } + else if (index == 7) + { + red = 255; } else { - if (index == 6) - { - red = 0; - } - else if (index == 7) - { - red = 255; - } - else - { - index -= 1; - red = (byte)((((red0 * (5 - index)) + (red1 * index)) / 5.0f) + 0.5f); - } + index -= 1; + red = (byte)((((red0 * (5 - index)) + (red1 * index)) / 5.0f) + 0.5f); } } - - return red; } + + return red; } } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Bc4s.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Bc4s.cs index e70f1575..cba718b9 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Bc4s.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Bc4s.cs @@ -3,111 +3,110 @@ using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; + +/// +/// Texture compressed with BC4S with one color channel (8 bits). +/// +internal struct Bc4s : IBlock { - /// - /// Texture compressed with BC4S with one color channel (8 bits). - /// - internal struct Bc4s : IBlock - { - private const float Multiplier = 255.0f / 254.0f; + private const float Multiplier = 255.0f / 254.0f; - /// - public int BitsPerPixel => 8; + /// + public readonly int BitsPerPixel => 8; - /// - public byte PixelDepthBytes => 1; + /// + public readonly byte PixelDepthBytes => 1; - /// - public byte DivSize => 4; + /// + public readonly byte DivSize => 4; - /// - public byte CompressedBytesPerBlock => 8; + /// + public readonly byte CompressedBytesPerBlock => 8; - /// - public bool Compressed => true; + /// + public readonly bool Compressed => true; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); + } - /// - public byte[] Decompress(byte[] blockData, int width, int height) - { - Bc4s self = this; + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) + { + Bc4s self = this; - return Helper.InMemoryDecode(blockData, width, height, (stream, data, streamIndex, dataIndex, stride) => + return Helper.InMemoryDecode(blockData, width, height, (stream, data, streamIndex, dataIndex, stride) => + { + sbyte red0 = (sbyte)blockData[streamIndex++]; + sbyte red1 = (sbyte)blockData[streamIndex++]; + red0 = red0 == -128 ? (sbyte)-127 : red0; + red1 = red1 == -128 ? (sbyte)-127 : red1; + + ulong rIndex = blockData[streamIndex++]; + rIndex |= (ulong)blockData[streamIndex++] << 8; + rIndex |= (ulong)blockData[streamIndex++] << 16; + rIndex |= (ulong)blockData[streamIndex++] << 24; + rIndex |= (ulong)blockData[streamIndex++] << 32; + rIndex |= (ulong)blockData[streamIndex++] << 40; + + for (int i = 0; i < 16; ++i) { - sbyte red0 = (sbyte)blockData[streamIndex++]; - sbyte red1 = (sbyte)blockData[streamIndex++]; - red0 = red0 == -128 ? (sbyte)-127 : red0; - red1 = red1 == -128 ? (sbyte)-127 : red1; - - ulong rIndex = blockData[streamIndex++]; - rIndex |= (ulong)blockData[streamIndex++] << 8; - rIndex |= (ulong)blockData[streamIndex++] << 16; - rIndex |= (ulong)blockData[streamIndex++] << 24; - rIndex |= (ulong)blockData[streamIndex++] << 32; - rIndex |= (ulong)blockData[streamIndex++] << 40; - - for (int i = 0; i < 16; ++i) - { - uint index = (byte)((uint)(rIndex >> (3 * i)) & 0x07); + uint index = (byte)((uint)(rIndex >> (3 * i)) & 0x07); - data[dataIndex++] = InterpolateColor((byte)index, red0, red1); + data[dataIndex++] = InterpolateColor((byte)index, red0, red1); - // Is mult 4? - if (((i + 1) & 0x3) == 0) - { - dataIndex += self.PixelDepthBytes * (stride - self.DivSize); - } + // Is mult 4? + if (((i + 1) & 0x3) == 0) + { + dataIndex += self.PixelDepthBytes * (stride - self.DivSize); } + } - return streamIndex; - }); - } + return streamIndex; + }); + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static byte InterpolateColor(byte index, sbyte red0, sbyte red1) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static byte InterpolateColor(byte index, sbyte red0, sbyte red1) + { + float red; + if (index == 0) { - float red; - if (index == 0) - { - red = red0; - } - else if (index == 1) + red = red0; + } + else if (index == 1) + { + red = red1; + } + else + { + if (red0 > red1) { - red = red1; + index -= 1; + red = ((red0 * (7 - index)) + (red1 * index)) / 7.0f; } else { - if (red0 > red1) + if (index == 6) { - index -= 1; - red = ((red0 * (7 - index)) + (red1 * index)) / 7.0f; + red = -127.0f; + } + else if (index == 7) + { + red = 127.0f; } else { - if (index == 6) - { - red = -127.0f; - } - else if (index == 7) - { - red = 127.0f; - } - else - { - index -= 1; - red = ((red0 * (5 - index)) + (red1 * index)) / 5.0f; - } + index -= 1; + red = ((red0 * (5 - index)) + (red1 * index)) / 5.0f; } } - - return (byte)(((red + 127) * Multiplier) + 0.5f); } + + return (byte)(((red + 127) * Multiplier) + 0.5f); } } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Bc5.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Bc5.cs index d1f535fc..990d4ab7 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Bc5.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Bc5.cs @@ -1,112 +1,110 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; + +/// +/// Texture compressed with BC5 with two color channels, red and green. +/// +internal struct Bc5 : IBlock { - /// - /// Texture compressed with BC5 with two color channels, red and green. - /// - internal struct Bc5 : IBlock - { - /// - public int BitsPerPixel => 24; + /// + public readonly int BitsPerPixel => 24; - /// - public byte PixelDepthBytes => 3; + /// + public readonly byte PixelDepthBytes => 3; - /// - public byte DivSize => 4; + /// + public readonly byte DivSize => 4; - /// - public byte CompressedBytesPerBlock => 16; + /// + public readonly byte CompressedBytesPerBlock => 16; - /// - public bool Compressed => true; + /// + public readonly bool Compressed => true; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); - // Should RG format be used instead RGB24? - return Image.LoadPixelData(decompressedData, width, height); - } + // Should RG format be used instead RGB24? + return Image.LoadPixelData(decompressedData, width, height); + } - /// - public byte[] Decompress(byte[] blockData, int width, int height) - { - Bc5 self = this; + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) + { + Bc5 self = this; - byte[] firstGradient = new byte[8]; - byte[] secondGradient = new byte[8]; + byte[] firstGradient = new byte[8]; + byte[] secondGradient = new byte[8]; - return Helper.InMemoryDecode(blockData, width, height, (stream, data, streamIndex, dataIndex, stride) => + return Helper.InMemoryDecode(blockData, width, height, (stream, data, streamIndex, dataIndex, stride) => + { + streamIndex = ExtractGradient(firstGradient, blockData, streamIndex); + ulong firstCodes = blockData[streamIndex++]; + firstCodes |= (ulong)blockData[streamIndex++] << 8; + firstCodes |= (ulong)blockData[streamIndex++] << 16; + firstCodes |= (ulong)blockData[streamIndex++] << 24; + firstCodes |= (ulong)blockData[streamIndex++] << 32; + firstCodes |= (ulong)blockData[streamIndex++] << 40; + + streamIndex = ExtractGradient(secondGradient, blockData, streamIndex); + ulong secondCodes = blockData[streamIndex++]; + secondCodes |= (ulong)blockData[streamIndex++] << 8; + secondCodes |= (ulong)blockData[streamIndex++] << 16; + secondCodes |= (ulong)blockData[streamIndex++] << 24; + secondCodes |= (ulong)blockData[streamIndex++] << 32; + secondCodes |= (ulong)blockData[streamIndex++] << 40; + + for (int alphaShift = 0; alphaShift < 48; alphaShift += 12) { - streamIndex = ExtractGradient(firstGradient, blockData, streamIndex); - ulong firstCodes = blockData[streamIndex++]; - firstCodes |= (ulong)blockData[streamIndex++] << 8; - firstCodes |= (ulong)blockData[streamIndex++] << 16; - firstCodes |= (ulong)blockData[streamIndex++] << 24; - firstCodes |= (ulong)blockData[streamIndex++] << 32; - firstCodes |= (ulong)blockData[streamIndex++] << 40; - - streamIndex = ExtractGradient(secondGradient, blockData, streamIndex); - ulong secondCodes = blockData[streamIndex++]; - secondCodes |= (ulong)blockData[streamIndex++] << 8; - secondCodes |= (ulong)blockData[streamIndex++] << 16; - secondCodes |= (ulong)blockData[streamIndex++] << 24; - secondCodes |= (ulong)blockData[streamIndex++] << 32; - secondCodes |= (ulong)blockData[streamIndex++] << 40; - - for (int alphaShift = 0; alphaShift < 48; alphaShift += 12) + for (int j = 0; j < 4; j++) { - for (int j = 0; j < 4; j++) - { - // 3 bits determine alpha index to use. - byte firstIndex = (byte)((firstCodes >> (alphaShift + (3 * j))) & 0x07); - byte secondIndex = (byte)((secondCodes >> (alphaShift + (3 * j))) & 0x07); - data[dataIndex++] = firstGradient[firstIndex]; - data[dataIndex++] = secondGradient[secondIndex]; - data[dataIndex++] = 0; // Skip blue. - } - - dataIndex += self.PixelDepthBytes * (stride - self.DivSize); + // 3 bits determine alpha index to use. + byte firstIndex = (byte)((firstCodes >> (alphaShift + (3 * j))) & 0x07); + byte secondIndex = (byte)((secondCodes >> (alphaShift + (3 * j))) & 0x07); + data[dataIndex++] = firstGradient[firstIndex]; + data[dataIndex++] = secondGradient[secondIndex]; + data[dataIndex++] = 0; // Skip blue. } - return streamIndex; - }); - } + dataIndex += self.PixelDepthBytes * (stride - self.DivSize); + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static int ExtractGradient(Span gradient, Span stream, int bIndex) - { - byte endpoint0; - byte endpoint1; - gradient[0] = endpoint0 = stream[bIndex++]; - gradient[1] = endpoint1 = stream[bIndex++]; + return streamIndex; + }); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static int ExtractGradient(Span gradient, Span stream, int bIndex) + { + byte endpoint0; + byte endpoint1; + gradient[0] = endpoint0 = stream[bIndex++]; + gradient[1] = endpoint1 = stream[bIndex++]; - if (endpoint0 > endpoint1) + if (endpoint0 > endpoint1) + { + for (int i = 1; i < 7; i++) { - for (int i = 1; i < 7; i++) - { - gradient[1 + i] = (byte)((((7 - i) * endpoint0) + (i * endpoint1)) / 7); - } + gradient[1 + i] = (byte)((((7 - i) * endpoint0) + (i * endpoint1)) / 7); } - else + } + else + { + for (int i = 1; i < 5; ++i) { - for (int i = 1; i < 5; ++i) - { - gradient[1 + i] = (byte)((((5 - i) * endpoint0) + (i * endpoint1)) / 5); - } - - gradient[6] = 0; - gradient[7] = 255; + gradient[1 + i] = (byte)((((5 - i) * endpoint0) + (i * endpoint1)) / 5); } - return bIndex; + gradient[6] = 0; + gradient[7] = 255; } + + return bIndex; } } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Bc5s.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Bc5s.cs index abd1b526..92f8472c 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Bc5s.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Bc5s.cs @@ -1,84 +1,83 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; + +/// +/// Texture compressed with BC5S with two color channels, red and green. +/// +internal struct Bc5s : IBlock { - /// - /// Texture compressed with BC5S with two color channels, red and green. - /// - internal struct Bc5s : IBlock - { - /// - public int BitsPerPixel => 24; + /// + public readonly int BitsPerPixel => 24; - /// - public byte PixelDepthBytes => 3; + /// + public readonly byte PixelDepthBytes => 3; - /// - public byte DivSize => 4; + /// + public readonly byte DivSize => 4; - /// - public byte CompressedBytesPerBlock => 16; + /// + public readonly byte CompressedBytesPerBlock => 16; - /// - public bool Compressed => true; + /// + public readonly bool Compressed => true; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + + // Should RG format be used instead RGB24? + return Image.LoadPixelData(decompressedData, width, height); + } - // Should RG format be used instead RGB24? - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) + { + Bc5s self = this; - /// - public byte[] Decompress(byte[] blockData, int width, int height) + return Helper.InMemoryDecode(blockData, width, height, (stream, data, streamIndex, dataIndex, stride) => { - Bc5s self = this; + sbyte red0 = (sbyte)blockData[streamIndex++]; + sbyte red1 = (sbyte)blockData[streamIndex++]; + red0 = red0 == -128 ? (sbyte)-127 : red0; + red1 = red1 == -128 ? (sbyte)-127 : red1; + ulong rIndex = blockData[streamIndex++]; + rIndex |= (ulong)blockData[streamIndex++] << 8; + rIndex |= (ulong)blockData[streamIndex++] << 16; + rIndex |= (ulong)blockData[streamIndex++] << 24; + rIndex |= (ulong)blockData[streamIndex++] << 32; + rIndex |= (ulong)blockData[streamIndex++] << 40; + + sbyte green0 = (sbyte)blockData[streamIndex++]; + sbyte green1 = (sbyte)blockData[streamIndex++]; + green0 = green0 == -128 ? (sbyte)-127 : green0; + green1 = green1 == -128 ? (sbyte)-127 : green1; + ulong gIndex = blockData[streamIndex++]; + gIndex |= (ulong)blockData[streamIndex++] << 8; + gIndex |= (ulong)blockData[streamIndex++] << 16; + gIndex |= (ulong)blockData[streamIndex++] << 24; + gIndex |= (ulong)blockData[streamIndex++] << 32; + gIndex |= (ulong)blockData[streamIndex++] << 40; - return Helper.InMemoryDecode(blockData, width, height, (stream, data, streamIndex, dataIndex, stride) => + for (int i = 0; i < 16; ++i) { - sbyte red0 = (sbyte)blockData[streamIndex++]; - sbyte red1 = (sbyte)blockData[streamIndex++]; - red0 = red0 == -128 ? (sbyte)-127 : red0; - red1 = red1 == -128 ? (sbyte)-127 : red1; - ulong rIndex = blockData[streamIndex++]; - rIndex |= (ulong)blockData[streamIndex++] << 8; - rIndex |= (ulong)blockData[streamIndex++] << 16; - rIndex |= (ulong)blockData[streamIndex++] << 24; - rIndex |= (ulong)blockData[streamIndex++] << 32; - rIndex |= (ulong)blockData[streamIndex++] << 40; + byte rSel = (byte)((uint)(rIndex >> (3 * i)) & 0x07); + byte gSel = (byte)((uint)(gIndex >> (3 * i)) & 0x07); - sbyte green0 = (sbyte)blockData[streamIndex++]; - sbyte green1 = (sbyte)blockData[streamIndex++]; - green0 = green0 == -128 ? (sbyte)-127 : green0; - green1 = green1 == -128 ? (sbyte)-127 : green1; - ulong gIndex = blockData[streamIndex++]; - gIndex |= (ulong)blockData[streamIndex++] << 8; - gIndex |= (ulong)blockData[streamIndex++] << 16; - gIndex |= (ulong)blockData[streamIndex++] << 24; - gIndex |= (ulong)blockData[streamIndex++] << 32; - gIndex |= (ulong)blockData[streamIndex++] << 40; + data[dataIndex++] = Bc4s.InterpolateColor(rSel, red0, red1); + data[dataIndex++] = Bc4s.InterpolateColor(gSel, green0, green1); + data[dataIndex++] = 0; // Skip blue. - for (int i = 0; i < 16; ++i) + // Is mult 4? + if (((i + 1) & 0x3) == 0) { - byte rSel = (byte)((uint)(rIndex >> (3 * i)) & 0x07); - byte gSel = (byte)((uint)(gIndex >> (3 * i)) & 0x07); - - data[dataIndex++] = Bc4s.InterpolateColor(rSel, red0, red1); - data[dataIndex++] = Bc4s.InterpolateColor(gSel, green0, green1); - data[dataIndex++] = 0; // Skip blue. - - // Is mult 4? - if (((i + 1) & 0x3) == 0) - { - dataIndex += self.PixelDepthBytes * (stride - self.DivSize); - } + dataIndex += self.PixelDepthBytes * (stride - self.DivSize); } + } - return streamIndex; - }); - } + return streamIndex; + }); } } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Bc6h.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Bc6h.cs index 6636c9f0..68e17853 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Bc6h.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Bc6h.cs @@ -1,583 +1,567 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Diagnostics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Textures.Common.Helpers; using SixLabors.ImageSharp.Textures.TextureFormats.Decoding.PixelFormats; -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; + +/// +/// Texture compressed with BC6H, three color channels (16 bits:16 bits:16 bits) in "half" floating point. +/// +internal struct Bc6h : IBlock { - /// - /// Texture compressed with BC6H, three color channels (16 bits:16 bits:16 bits) in "half" floating point. - /// - internal struct Bc6h : IBlock + // Code based on commit 138efff1b9c53fd9a5dd34b8c865e8f5ae798030 2019/10/24 in DirectXTex C++ library + private static readonly Bc6HModeDescriptor[][] ModeDescriptors = + [ + [ + // Mode 1 (0x00) - 10 5 5 5 + new Bc6HModeDescriptor(Bc6hEField.M, 0), new Bc6HModeDescriptor(Bc6hEField.M, 1), new Bc6HModeDescriptor(Bc6hEField.GY, 4), new Bc6HModeDescriptor(Bc6hEField.BY, 4), new Bc6HModeDescriptor(Bc6hEField.BZ, 4), new Bc6HModeDescriptor(Bc6hEField.RW, 0), new Bc6HModeDescriptor(Bc6hEField.RW, 1), new Bc6HModeDescriptor(Bc6hEField.RW, 2), new Bc6HModeDescriptor(Bc6hEField.RW, 3), new Bc6HModeDescriptor(Bc6hEField.RW, 4), + new Bc6HModeDescriptor(Bc6hEField.RW, 5), new Bc6HModeDescriptor(Bc6hEField.RW, 6), new Bc6HModeDescriptor(Bc6hEField.RW, 7), new Bc6HModeDescriptor(Bc6hEField.RW, 8), new Bc6HModeDescriptor(Bc6hEField.RW, 9), new Bc6HModeDescriptor(Bc6hEField.GW, 0), new Bc6HModeDescriptor(Bc6hEField.GW, 1), new Bc6HModeDescriptor(Bc6hEField.GW, 2), new Bc6HModeDescriptor(Bc6hEField.GW, 3), new Bc6HModeDescriptor(Bc6hEField.GW, 4), + new Bc6HModeDescriptor(Bc6hEField.GW, 5), new Bc6HModeDescriptor(Bc6hEField.GW, 6), new Bc6HModeDescriptor(Bc6hEField.GW, 7), new Bc6HModeDescriptor(Bc6hEField.GW, 8), new Bc6HModeDescriptor(Bc6hEField.GW, 9), new Bc6HModeDescriptor(Bc6hEField.BW, 0), new Bc6HModeDescriptor(Bc6hEField.BW, 1), new Bc6HModeDescriptor(Bc6hEField.BW, 2), new Bc6HModeDescriptor(Bc6hEField.BW, 3), new Bc6HModeDescriptor(Bc6hEField.BW, 4), + new Bc6HModeDescriptor(Bc6hEField.BW, 5), new Bc6HModeDescriptor(Bc6hEField.BW, 6), new Bc6HModeDescriptor(Bc6hEField.BW, 7), new Bc6HModeDescriptor(Bc6hEField.BW, 8), new Bc6HModeDescriptor(Bc6hEField.BW, 9), new Bc6HModeDescriptor(Bc6hEField.RX, 0), new Bc6HModeDescriptor(Bc6hEField.RX, 1), new Bc6HModeDescriptor(Bc6hEField.RX, 2), new Bc6HModeDescriptor(Bc6hEField.RX, 3), new Bc6HModeDescriptor(Bc6hEField.RX, 4), + new Bc6HModeDescriptor(Bc6hEField.GZ, 4), new Bc6HModeDescriptor(Bc6hEField.GY, 0), new Bc6HModeDescriptor(Bc6hEField.GY, 1), new Bc6HModeDescriptor(Bc6hEField.GY, 2), new Bc6HModeDescriptor(Bc6hEField.GY, 3), new Bc6HModeDescriptor(Bc6hEField.GX, 0), new Bc6HModeDescriptor(Bc6hEField.GX, 1), new Bc6HModeDescriptor(Bc6hEField.GX, 2), new Bc6HModeDescriptor(Bc6hEField.GX, 3), new Bc6HModeDescriptor(Bc6hEField.GX, 4), + new Bc6HModeDescriptor(Bc6hEField.BZ, 0), new Bc6HModeDescriptor(Bc6hEField.GZ, 0), new Bc6HModeDescriptor(Bc6hEField.GZ, 1), new Bc6HModeDescriptor(Bc6hEField.GZ, 2), new Bc6HModeDescriptor(Bc6hEField.GZ, 3), new Bc6HModeDescriptor(Bc6hEField.BX, 0), new Bc6HModeDescriptor(Bc6hEField.BX, 1), new Bc6HModeDescriptor(Bc6hEField.BX, 2), new Bc6HModeDescriptor(Bc6hEField.BX, 3), new Bc6HModeDescriptor(Bc6hEField.BX, 4), + new Bc6HModeDescriptor(Bc6hEField.BZ, 1), new Bc6HModeDescriptor(Bc6hEField.BY, 0), new Bc6HModeDescriptor(Bc6hEField.BY, 1), new Bc6HModeDescriptor(Bc6hEField.BY, 2), new Bc6HModeDescriptor(Bc6hEField.BY, 3), new Bc6HModeDescriptor(Bc6hEField.RY, 0), new Bc6HModeDescriptor(Bc6hEField.RY, 1), new Bc6HModeDescriptor(Bc6hEField.RY, 2), new Bc6HModeDescriptor(Bc6hEField.RY, 3), new Bc6HModeDescriptor(Bc6hEField.RY, 4), + new Bc6HModeDescriptor(Bc6hEField.BZ, 2), new Bc6HModeDescriptor(Bc6hEField.RZ, 0), new Bc6HModeDescriptor(Bc6hEField.RZ, 1), new Bc6HModeDescriptor(Bc6hEField.RZ, 2), new Bc6HModeDescriptor(Bc6hEField.RZ, 3), new Bc6HModeDescriptor(Bc6hEField.RZ, 4), new Bc6HModeDescriptor(Bc6hEField.BZ, 3), new Bc6HModeDescriptor(Bc6hEField.D, 0), new Bc6HModeDescriptor(Bc6hEField.D, 1), new Bc6HModeDescriptor(Bc6hEField.D, 2), + new Bc6HModeDescriptor(Bc6hEField.D, 3), new Bc6HModeDescriptor(Bc6hEField.D, 4) + ], + + [ + // Mode 2 (0x01) - 7 6 6 6 + new Bc6HModeDescriptor(Bc6hEField.M, 0), new Bc6HModeDescriptor(Bc6hEField.M, 1), new Bc6HModeDescriptor(Bc6hEField.GY, 5), new Bc6HModeDescriptor(Bc6hEField.GZ, 4), new Bc6HModeDescriptor(Bc6hEField.GZ, 5), new Bc6HModeDescriptor(Bc6hEField.RW, 0), new Bc6HModeDescriptor(Bc6hEField.RW, 1), new Bc6HModeDescriptor(Bc6hEField.RW, 2), new Bc6HModeDescriptor(Bc6hEField.RW, 3), new Bc6HModeDescriptor(Bc6hEField.RW, 4), + new Bc6HModeDescriptor(Bc6hEField.RW, 5), new Bc6HModeDescriptor(Bc6hEField.RW, 6), new Bc6HModeDescriptor(Bc6hEField.BZ, 0), new Bc6HModeDescriptor(Bc6hEField.BZ, 1), new Bc6HModeDescriptor(Bc6hEField.BY, 4), new Bc6HModeDescriptor(Bc6hEField.GW, 0), new Bc6HModeDescriptor(Bc6hEField.GW, 1), new Bc6HModeDescriptor(Bc6hEField.GW, 2), new Bc6HModeDescriptor(Bc6hEField.GW, 3), new Bc6HModeDescriptor(Bc6hEField.GW, 4), + new Bc6HModeDescriptor(Bc6hEField.GW, 5), new Bc6HModeDescriptor(Bc6hEField.GW, 6), new Bc6HModeDescriptor(Bc6hEField.BY, 5), new Bc6HModeDescriptor(Bc6hEField.BZ, 2), new Bc6HModeDescriptor(Bc6hEField.GY, 4), new Bc6HModeDescriptor(Bc6hEField.BW, 0), new Bc6HModeDescriptor(Bc6hEField.BW, 1), new Bc6HModeDescriptor(Bc6hEField.BW, 2), new Bc6HModeDescriptor(Bc6hEField.BW, 3), new Bc6HModeDescriptor(Bc6hEField.BW, 4), + new Bc6HModeDescriptor(Bc6hEField.BW, 5), new Bc6HModeDescriptor(Bc6hEField.BW, 6), new Bc6HModeDescriptor(Bc6hEField.BZ, 3), new Bc6HModeDescriptor(Bc6hEField.BZ, 5), new Bc6HModeDescriptor(Bc6hEField.BZ, 4), new Bc6HModeDescriptor(Bc6hEField.RX, 0), new Bc6HModeDescriptor(Bc6hEField.RX, 1), new Bc6HModeDescriptor(Bc6hEField.RX, 2), new Bc6HModeDescriptor(Bc6hEField.RX, 3), new Bc6HModeDescriptor(Bc6hEField.RX, 4), + new Bc6HModeDescriptor(Bc6hEField.RX, 5), new Bc6HModeDescriptor(Bc6hEField.GY, 0), new Bc6HModeDescriptor(Bc6hEField.GY, 1), new Bc6HModeDescriptor(Bc6hEField.GY, 2), new Bc6HModeDescriptor(Bc6hEField.GY, 3), new Bc6HModeDescriptor(Bc6hEField.GX, 0), new Bc6HModeDescriptor(Bc6hEField.GX, 1), new Bc6HModeDescriptor(Bc6hEField.GX, 2), new Bc6HModeDescriptor(Bc6hEField.GX, 3), new Bc6HModeDescriptor(Bc6hEField.GX, 4), + new Bc6HModeDescriptor(Bc6hEField.GX, 5), new Bc6HModeDescriptor(Bc6hEField.GZ, 0), new Bc6HModeDescriptor(Bc6hEField.GZ, 1), new Bc6HModeDescriptor(Bc6hEField.GZ, 2), new Bc6HModeDescriptor(Bc6hEField.GZ, 3), new Bc6HModeDescriptor(Bc6hEField.BX, 0), new Bc6HModeDescriptor(Bc6hEField.BX, 1), new Bc6HModeDescriptor(Bc6hEField.BX, 2), new Bc6HModeDescriptor(Bc6hEField.BX, 3), new Bc6HModeDescriptor(Bc6hEField.BX, 4), + new Bc6HModeDescriptor(Bc6hEField.BX, 5), new Bc6HModeDescriptor(Bc6hEField.BY, 0), new Bc6HModeDescriptor(Bc6hEField.BY, 1), new Bc6HModeDescriptor(Bc6hEField.BY, 2), new Bc6HModeDescriptor(Bc6hEField.BY, 3), new Bc6HModeDescriptor(Bc6hEField.RY, 0), new Bc6HModeDescriptor(Bc6hEField.RY, 1), new Bc6HModeDescriptor(Bc6hEField.RY, 2), new Bc6HModeDescriptor(Bc6hEField.RY, 3), new Bc6HModeDescriptor(Bc6hEField.RY, 4), + new Bc6HModeDescriptor(Bc6hEField.RY, 5), new Bc6HModeDescriptor(Bc6hEField.RZ, 0), new Bc6HModeDescriptor(Bc6hEField.RZ, 1), new Bc6HModeDescriptor(Bc6hEField.RZ, 2), new Bc6HModeDescriptor(Bc6hEField.RZ, 3), new Bc6HModeDescriptor(Bc6hEField.RZ, 4), new Bc6HModeDescriptor(Bc6hEField.RZ, 5), new Bc6HModeDescriptor(Bc6hEField.D, 0), new Bc6HModeDescriptor(Bc6hEField.D, 1), new Bc6HModeDescriptor(Bc6hEField.D, 2), + new Bc6HModeDescriptor(Bc6hEField.D, 3), new Bc6HModeDescriptor(Bc6hEField.D, 4) + ], + + [ + // Mode 3 (0x02) - 11 5 4 4 + new Bc6HModeDescriptor(Bc6hEField.M, 0), new Bc6HModeDescriptor(Bc6hEField.M, 1), new Bc6HModeDescriptor(Bc6hEField.M, 2), new Bc6HModeDescriptor(Bc6hEField.M, 3), new Bc6HModeDescriptor(Bc6hEField.M, 4), new Bc6HModeDescriptor(Bc6hEField.RW, 0), new Bc6HModeDescriptor(Bc6hEField.RW, 1), new Bc6HModeDescriptor(Bc6hEField.RW, 2), new Bc6HModeDescriptor(Bc6hEField.RW, 3), new Bc6HModeDescriptor(Bc6hEField.RW, 4), + new Bc6HModeDescriptor(Bc6hEField.RW, 5), new Bc6HModeDescriptor(Bc6hEField.RW, 6), new Bc6HModeDescriptor(Bc6hEField.RW, 7), new Bc6HModeDescriptor(Bc6hEField.RW, 8), new Bc6HModeDescriptor(Bc6hEField.RW, 9), new Bc6HModeDescriptor(Bc6hEField.GW, 0), new Bc6HModeDescriptor(Bc6hEField.GW, 1), new Bc6HModeDescriptor(Bc6hEField.GW, 2), new Bc6HModeDescriptor(Bc6hEField.GW, 3), new Bc6HModeDescriptor(Bc6hEField.GW, 4), + new Bc6HModeDescriptor(Bc6hEField.GW, 5), new Bc6HModeDescriptor(Bc6hEField.GW, 6), new Bc6HModeDescriptor(Bc6hEField.GW, 7), new Bc6HModeDescriptor(Bc6hEField.GW, 8), new Bc6HModeDescriptor(Bc6hEField.GW, 9), new Bc6HModeDescriptor(Bc6hEField.BW, 0), new Bc6HModeDescriptor(Bc6hEField.BW, 1), new Bc6HModeDescriptor(Bc6hEField.BW, 2), new Bc6HModeDescriptor(Bc6hEField.BW, 3), new Bc6HModeDescriptor(Bc6hEField.BW, 4), + new Bc6HModeDescriptor(Bc6hEField.BW, 5), new Bc6HModeDescriptor(Bc6hEField.BW, 6), new Bc6HModeDescriptor(Bc6hEField.BW, 7), new Bc6HModeDescriptor(Bc6hEField.BW, 8), new Bc6HModeDescriptor(Bc6hEField.BW, 9), new Bc6HModeDescriptor(Bc6hEField.RX, 0), new Bc6HModeDescriptor(Bc6hEField.RX, 1), new Bc6HModeDescriptor(Bc6hEField.RX, 2), new Bc6HModeDescriptor(Bc6hEField.RX, 3), new Bc6HModeDescriptor(Bc6hEField.RX, 4), + new Bc6HModeDescriptor(Bc6hEField.RW, 10), new Bc6HModeDescriptor(Bc6hEField.GY, 0), new Bc6HModeDescriptor(Bc6hEField.GY, 1), new Bc6HModeDescriptor(Bc6hEField.GY, 2), new Bc6HModeDescriptor(Bc6hEField.GY, 3), new Bc6HModeDescriptor(Bc6hEField.GX, 0), new Bc6HModeDescriptor(Bc6hEField.GX, 1), new Bc6HModeDescriptor(Bc6hEField.GX, 2), new Bc6HModeDescriptor(Bc6hEField.GX, 3), new Bc6HModeDescriptor(Bc6hEField.GW, 10), + new Bc6HModeDescriptor(Bc6hEField.BZ, 0), new Bc6HModeDescriptor(Bc6hEField.GZ, 0), new Bc6HModeDescriptor(Bc6hEField.GZ, 1), new Bc6HModeDescriptor(Bc6hEField.GZ, 2), new Bc6HModeDescriptor(Bc6hEField.GZ, 3), new Bc6HModeDescriptor(Bc6hEField.BX, 0), new Bc6HModeDescriptor(Bc6hEField.BX, 1), new Bc6HModeDescriptor(Bc6hEField.BX, 2), new Bc6HModeDescriptor(Bc6hEField.BX, 3), new Bc6HModeDescriptor(Bc6hEField.BW, 10), + new Bc6HModeDescriptor(Bc6hEField.BZ, 1), new Bc6HModeDescriptor(Bc6hEField.BY, 0), new Bc6HModeDescriptor(Bc6hEField.BY, 1), new Bc6HModeDescriptor(Bc6hEField.BY, 2), new Bc6HModeDescriptor(Bc6hEField.BY, 3), new Bc6HModeDescriptor(Bc6hEField.RY, 0), new Bc6HModeDescriptor(Bc6hEField.RY, 1), new Bc6HModeDescriptor(Bc6hEField.RY, 2), new Bc6HModeDescriptor(Bc6hEField.RY, 3), new Bc6HModeDescriptor(Bc6hEField.RY, 4), + new Bc6HModeDescriptor(Bc6hEField.BZ, 2), new Bc6HModeDescriptor(Bc6hEField.RZ, 0), new Bc6HModeDescriptor(Bc6hEField.RZ, 1), new Bc6HModeDescriptor(Bc6hEField.RZ, 2), new Bc6HModeDescriptor(Bc6hEField.RZ, 3), new Bc6HModeDescriptor(Bc6hEField.RZ, 4), new Bc6HModeDescriptor(Bc6hEField.BZ, 3), new Bc6HModeDescriptor(Bc6hEField.D, 0), new Bc6HModeDescriptor(Bc6hEField.D, 1), new Bc6HModeDescriptor(Bc6hEField.D, 2), + new Bc6HModeDescriptor(Bc6hEField.D, 3), new Bc6HModeDescriptor(Bc6hEField.D, 4) + ], + + [ + // Mode 4 (0x06) - 11 4 5 4 + new Bc6HModeDescriptor(Bc6hEField.M, 0), new Bc6HModeDescriptor(Bc6hEField.M, 1), new Bc6HModeDescriptor(Bc6hEField.M, 2), new Bc6HModeDescriptor(Bc6hEField.M, 3), new Bc6HModeDescriptor(Bc6hEField.M, 4), new Bc6HModeDescriptor(Bc6hEField.RW, 0), new Bc6HModeDescriptor(Bc6hEField.RW, 1), new Bc6HModeDescriptor(Bc6hEField.RW, 2), new Bc6HModeDescriptor(Bc6hEField.RW, 3), new Bc6HModeDescriptor(Bc6hEField.RW, 4), + new Bc6HModeDescriptor(Bc6hEField.RW, 5), new Bc6HModeDescriptor(Bc6hEField.RW, 6), new Bc6HModeDescriptor(Bc6hEField.RW, 7), new Bc6HModeDescriptor(Bc6hEField.RW, 8), new Bc6HModeDescriptor(Bc6hEField.RW, 9), new Bc6HModeDescriptor(Bc6hEField.GW, 0), new Bc6HModeDescriptor(Bc6hEField.GW, 1), new Bc6HModeDescriptor(Bc6hEField.GW, 2), new Bc6HModeDescriptor(Bc6hEField.GW, 3), new Bc6HModeDescriptor(Bc6hEField.GW, 4), + new Bc6HModeDescriptor(Bc6hEField.GW, 5), new Bc6HModeDescriptor(Bc6hEField.GW, 6), new Bc6HModeDescriptor(Bc6hEField.GW, 7), new Bc6HModeDescriptor(Bc6hEField.GW, 8), new Bc6HModeDescriptor(Bc6hEField.GW, 9), new Bc6HModeDescriptor(Bc6hEField.BW, 0), new Bc6HModeDescriptor(Bc6hEField.BW, 1), new Bc6HModeDescriptor(Bc6hEField.BW, 2), new Bc6HModeDescriptor(Bc6hEField.BW, 3), new Bc6HModeDescriptor(Bc6hEField.BW, 4), + new Bc6HModeDescriptor(Bc6hEField.BW, 5), new Bc6HModeDescriptor(Bc6hEField.BW, 6), new Bc6HModeDescriptor(Bc6hEField.BW, 7), new Bc6HModeDescriptor(Bc6hEField.BW, 8), new Bc6HModeDescriptor(Bc6hEField.BW, 9), new Bc6HModeDescriptor(Bc6hEField.RX, 0), new Bc6HModeDescriptor(Bc6hEField.RX, 1), new Bc6HModeDescriptor(Bc6hEField.RX, 2), new Bc6HModeDescriptor(Bc6hEField.RX, 3), new Bc6HModeDescriptor(Bc6hEField.RW, 10), + new Bc6HModeDescriptor(Bc6hEField.GZ, 4), new Bc6HModeDescriptor(Bc6hEField.GY, 0), new Bc6HModeDescriptor(Bc6hEField.GY, 1), new Bc6HModeDescriptor(Bc6hEField.GY, 2), new Bc6HModeDescriptor(Bc6hEField.GY, 3), new Bc6HModeDescriptor(Bc6hEField.GX, 0), new Bc6HModeDescriptor(Bc6hEField.GX, 1), new Bc6HModeDescriptor(Bc6hEField.GX, 2), new Bc6HModeDescriptor(Bc6hEField.GX, 3), new Bc6HModeDescriptor(Bc6hEField.GX, 4), + new Bc6HModeDescriptor(Bc6hEField.GW, 10), new Bc6HModeDescriptor(Bc6hEField.GZ, 0), new Bc6HModeDescriptor(Bc6hEField.GZ, 1), new Bc6HModeDescriptor(Bc6hEField.GZ, 2), new Bc6HModeDescriptor(Bc6hEField.GZ, 3), new Bc6HModeDescriptor(Bc6hEField.BX, 0), new Bc6HModeDescriptor(Bc6hEField.BX, 1), new Bc6HModeDescriptor(Bc6hEField.BX, 2), new Bc6HModeDescriptor(Bc6hEField.BX, 3), new Bc6HModeDescriptor(Bc6hEField.BW, 10), + new Bc6HModeDescriptor(Bc6hEField.BZ, 1), new Bc6HModeDescriptor(Bc6hEField.BY, 0), new Bc6HModeDescriptor(Bc6hEField.BY, 1), new Bc6HModeDescriptor(Bc6hEField.BY, 2), new Bc6HModeDescriptor(Bc6hEField.BY, 3), new Bc6HModeDescriptor(Bc6hEField.RY, 0), new Bc6HModeDescriptor(Bc6hEField.RY, 1), new Bc6HModeDescriptor(Bc6hEField.RY, 2), new Bc6HModeDescriptor(Bc6hEField.RY, 3), new Bc6HModeDescriptor(Bc6hEField.BZ, 0), + new Bc6HModeDescriptor(Bc6hEField.BZ, 2), new Bc6HModeDescriptor(Bc6hEField.RZ, 0), new Bc6HModeDescriptor(Bc6hEField.RZ, 1), new Bc6HModeDescriptor(Bc6hEField.RZ, 2), new Bc6HModeDescriptor(Bc6hEField.RZ, 3), new Bc6HModeDescriptor(Bc6hEField.GY, 4), new Bc6HModeDescriptor(Bc6hEField.BZ, 3), new Bc6HModeDescriptor(Bc6hEField.D, 0), new Bc6HModeDescriptor(Bc6hEField.D, 1), new Bc6HModeDescriptor(Bc6hEField.D, 2), + new Bc6HModeDescriptor(Bc6hEField.D, 3), new Bc6HModeDescriptor(Bc6hEField.D, 4) + ], + + [ + // Mode 5 (0x0a) - 11 4 4 5 + new Bc6HModeDescriptor(Bc6hEField.M, 0), new Bc6HModeDescriptor(Bc6hEField.M, 1), new Bc6HModeDescriptor(Bc6hEField.M, 2), new Bc6HModeDescriptor(Bc6hEField.M, 3), new Bc6HModeDescriptor(Bc6hEField.M, 4), new Bc6HModeDescriptor(Bc6hEField.RW, 0), new Bc6HModeDescriptor(Bc6hEField.RW, 1), new Bc6HModeDescriptor(Bc6hEField.RW, 2), new Bc6HModeDescriptor(Bc6hEField.RW, 3), new Bc6HModeDescriptor(Bc6hEField.RW, 4), + new Bc6HModeDescriptor(Bc6hEField.RW, 5), new Bc6HModeDescriptor(Bc6hEField.RW, 6), new Bc6HModeDescriptor(Bc6hEField.RW, 7), new Bc6HModeDescriptor(Bc6hEField.RW, 8), new Bc6HModeDescriptor(Bc6hEField.RW, 9), new Bc6HModeDescriptor(Bc6hEField.GW, 0), new Bc6HModeDescriptor(Bc6hEField.GW, 1), new Bc6HModeDescriptor(Bc6hEField.GW, 2), new Bc6HModeDescriptor(Bc6hEField.GW, 3), new Bc6HModeDescriptor(Bc6hEField.GW, 4), + new Bc6HModeDescriptor(Bc6hEField.GW, 5), new Bc6HModeDescriptor(Bc6hEField.GW, 6), new Bc6HModeDescriptor(Bc6hEField.GW, 7), new Bc6HModeDescriptor(Bc6hEField.GW, 8), new Bc6HModeDescriptor(Bc6hEField.GW, 9), new Bc6HModeDescriptor(Bc6hEField.BW, 0), new Bc6HModeDescriptor(Bc6hEField.BW, 1), new Bc6HModeDescriptor(Bc6hEField.BW, 2), new Bc6HModeDescriptor(Bc6hEField.BW, 3), new Bc6HModeDescriptor(Bc6hEField.BW, 4), + new Bc6HModeDescriptor(Bc6hEField.BW, 5), new Bc6HModeDescriptor(Bc6hEField.BW, 6), new Bc6HModeDescriptor(Bc6hEField.BW, 7), new Bc6HModeDescriptor(Bc6hEField.BW, 8), new Bc6HModeDescriptor(Bc6hEField.BW, 9), new Bc6HModeDescriptor(Bc6hEField.RX, 0), new Bc6HModeDescriptor(Bc6hEField.RX, 1), new Bc6HModeDescriptor(Bc6hEField.RX, 2), new Bc6HModeDescriptor(Bc6hEField.RX, 3), new Bc6HModeDescriptor(Bc6hEField.RW, 10), + new Bc6HModeDescriptor(Bc6hEField.BY, 4), new Bc6HModeDescriptor(Bc6hEField.GY, 0), new Bc6HModeDescriptor(Bc6hEField.GY, 1), new Bc6HModeDescriptor(Bc6hEField.GY, 2), new Bc6HModeDescriptor(Bc6hEField.GY, 3), new Bc6HModeDescriptor(Bc6hEField.GX, 0), new Bc6HModeDescriptor(Bc6hEField.GX, 1), new Bc6HModeDescriptor(Bc6hEField.GX, 2), new Bc6HModeDescriptor(Bc6hEField.GX, 3), new Bc6HModeDescriptor(Bc6hEField.GW, 10), + new Bc6HModeDescriptor(Bc6hEField.BZ, 0), new Bc6HModeDescriptor(Bc6hEField.GZ, 0), new Bc6HModeDescriptor(Bc6hEField.GZ, 1), new Bc6HModeDescriptor(Bc6hEField.GZ, 2), new Bc6HModeDescriptor(Bc6hEField.GZ, 3), new Bc6HModeDescriptor(Bc6hEField.BX, 0), new Bc6HModeDescriptor(Bc6hEField.BX, 1), new Bc6HModeDescriptor(Bc6hEField.BX, 2), new Bc6HModeDescriptor(Bc6hEField.BX, 3), new Bc6HModeDescriptor(Bc6hEField.BX, 4), + new Bc6HModeDescriptor(Bc6hEField.BW, 10), new Bc6HModeDescriptor(Bc6hEField.BY, 0), new Bc6HModeDescriptor(Bc6hEField.BY, 1), new Bc6HModeDescriptor(Bc6hEField.BY, 2), new Bc6HModeDescriptor(Bc6hEField.BY, 3), new Bc6HModeDescriptor(Bc6hEField.RY, 0), new Bc6HModeDescriptor(Bc6hEField.RY, 1), new Bc6HModeDescriptor(Bc6hEField.RY, 2), new Bc6HModeDescriptor(Bc6hEField.RY, 3), new Bc6HModeDescriptor(Bc6hEField.BZ, 1), + new Bc6HModeDescriptor(Bc6hEField.BZ, 2), new Bc6HModeDescriptor(Bc6hEField.RZ, 0), new Bc6HModeDescriptor(Bc6hEField.RZ, 1), new Bc6HModeDescriptor(Bc6hEField.RZ, 2), new Bc6HModeDescriptor(Bc6hEField.RZ, 3), new Bc6HModeDescriptor(Bc6hEField.BZ, 4), new Bc6HModeDescriptor(Bc6hEField.BZ, 3), new Bc6HModeDescriptor(Bc6hEField.D, 0), new Bc6HModeDescriptor(Bc6hEField.D, 1), new Bc6HModeDescriptor(Bc6hEField.D, 2), + new Bc6HModeDescriptor(Bc6hEField.D, 3), new Bc6HModeDescriptor(Bc6hEField.D, 4) + ], + + [ + // Mode 6 (0x0e) - 9 5 5 5 + new Bc6HModeDescriptor(Bc6hEField.M, 0), new Bc6HModeDescriptor(Bc6hEField.M, 1), new Bc6HModeDescriptor(Bc6hEField.M, 2), new Bc6HModeDescriptor(Bc6hEField.M, 3), new Bc6HModeDescriptor(Bc6hEField.M, 4), new Bc6HModeDescriptor(Bc6hEField.RW, 0), new Bc6HModeDescriptor(Bc6hEField.RW, 1), new Bc6HModeDescriptor(Bc6hEField.RW, 2), new Bc6HModeDescriptor(Bc6hEField.RW, 3), new Bc6HModeDescriptor(Bc6hEField.RW, 4), + new Bc6HModeDescriptor(Bc6hEField.RW, 5), new Bc6HModeDescriptor(Bc6hEField.RW, 6), new Bc6HModeDescriptor(Bc6hEField.RW, 7), new Bc6HModeDescriptor(Bc6hEField.RW, 8), new Bc6HModeDescriptor(Bc6hEField.BY, 4), new Bc6HModeDescriptor(Bc6hEField.GW, 0), new Bc6HModeDescriptor(Bc6hEField.GW, 1), new Bc6HModeDescriptor(Bc6hEField.GW, 2), new Bc6HModeDescriptor(Bc6hEField.GW, 3), new Bc6HModeDescriptor(Bc6hEField.GW, 4), + new Bc6HModeDescriptor(Bc6hEField.GW, 5), new Bc6HModeDescriptor(Bc6hEField.GW, 6), new Bc6HModeDescriptor(Bc6hEField.GW, 7), new Bc6HModeDescriptor(Bc6hEField.GW, 8), new Bc6HModeDescriptor(Bc6hEField.GY, 4), new Bc6HModeDescriptor(Bc6hEField.BW, 0), new Bc6HModeDescriptor(Bc6hEField.BW, 1), new Bc6HModeDescriptor(Bc6hEField.BW, 2), new Bc6HModeDescriptor(Bc6hEField.BW, 3), new Bc6HModeDescriptor(Bc6hEField.BW, 4), + new Bc6HModeDescriptor(Bc6hEField.BW, 5), new Bc6HModeDescriptor(Bc6hEField.BW, 6), new Bc6HModeDescriptor(Bc6hEField.BW, 7), new Bc6HModeDescriptor(Bc6hEField.BW, 8), new Bc6HModeDescriptor(Bc6hEField.BZ, 4), new Bc6HModeDescriptor(Bc6hEField.RX, 0), new Bc6HModeDescriptor(Bc6hEField.RX, 1), new Bc6HModeDescriptor(Bc6hEField.RX, 2), new Bc6HModeDescriptor(Bc6hEField.RX, 3), new Bc6HModeDescriptor(Bc6hEField.RX, 4), + new Bc6HModeDescriptor(Bc6hEField.GZ, 4), new Bc6HModeDescriptor(Bc6hEField.GY, 0), new Bc6HModeDescriptor(Bc6hEField.GY, 1), new Bc6HModeDescriptor(Bc6hEField.GY, 2), new Bc6HModeDescriptor(Bc6hEField.GY, 3), new Bc6HModeDescriptor(Bc6hEField.GX, 0), new Bc6HModeDescriptor(Bc6hEField.GX, 1), new Bc6HModeDescriptor(Bc6hEField.GX, 2), new Bc6HModeDescriptor(Bc6hEField.GX, 3), new Bc6HModeDescriptor(Bc6hEField.GX, 4), + new Bc6HModeDescriptor(Bc6hEField.BZ, 0), new Bc6HModeDescriptor(Bc6hEField.GZ, 0), new Bc6HModeDescriptor(Bc6hEField.GZ, 1), new Bc6HModeDescriptor(Bc6hEField.GZ, 2), new Bc6HModeDescriptor(Bc6hEField.GZ, 3), new Bc6HModeDescriptor(Bc6hEField.BX, 0), new Bc6HModeDescriptor(Bc6hEField.BX, 1), new Bc6HModeDescriptor(Bc6hEField.BX, 2), new Bc6HModeDescriptor(Bc6hEField.BX, 3), new Bc6HModeDescriptor(Bc6hEField.BX, 4), + new Bc6HModeDescriptor(Bc6hEField.BZ, 1), new Bc6HModeDescriptor(Bc6hEField.BY, 0), new Bc6HModeDescriptor(Bc6hEField.BY, 1), new Bc6HModeDescriptor(Bc6hEField.BY, 2), new Bc6HModeDescriptor(Bc6hEField.BY, 3), new Bc6HModeDescriptor(Bc6hEField.RY, 0), new Bc6HModeDescriptor(Bc6hEField.RY, 1), new Bc6HModeDescriptor(Bc6hEField.RY, 2), new Bc6HModeDescriptor(Bc6hEField.RY, 3), new Bc6HModeDescriptor(Bc6hEField.RY, 4), + new Bc6HModeDescriptor(Bc6hEField.BZ, 2), new Bc6HModeDescriptor(Bc6hEField.RZ, 0), new Bc6HModeDescriptor(Bc6hEField.RZ, 1), new Bc6HModeDescriptor(Bc6hEField.RZ, 2), new Bc6HModeDescriptor(Bc6hEField.RZ, 3), new Bc6HModeDescriptor(Bc6hEField.RZ, 4), new Bc6HModeDescriptor(Bc6hEField.BZ, 3), new Bc6HModeDescriptor(Bc6hEField.D, 0), new Bc6HModeDescriptor(Bc6hEField.D, 1), new Bc6HModeDescriptor(Bc6hEField.D, 2), + new Bc6HModeDescriptor(Bc6hEField.D, 3), new Bc6HModeDescriptor(Bc6hEField.D, 4) + ], + + [ + // Mode 7 (0x12) - 8 6 5 5 + new Bc6HModeDescriptor(Bc6hEField.M, 0), new Bc6HModeDescriptor(Bc6hEField.M, 1), new Bc6HModeDescriptor(Bc6hEField.M, 2), new Bc6HModeDescriptor(Bc6hEField.M, 3), new Bc6HModeDescriptor(Bc6hEField.M, 4), new Bc6HModeDescriptor(Bc6hEField.RW, 0), new Bc6HModeDescriptor(Bc6hEField.RW, 1), new Bc6HModeDescriptor(Bc6hEField.RW, 2), new Bc6HModeDescriptor(Bc6hEField.RW, 3), new Bc6HModeDescriptor(Bc6hEField.RW, 4), + new Bc6HModeDescriptor(Bc6hEField.RW, 5), new Bc6HModeDescriptor(Bc6hEField.RW, 6), new Bc6HModeDescriptor(Bc6hEField.RW, 7), new Bc6HModeDescriptor(Bc6hEField.GZ, 4), new Bc6HModeDescriptor(Bc6hEField.BY, 4), new Bc6HModeDescriptor(Bc6hEField.GW, 0), new Bc6HModeDescriptor(Bc6hEField.GW, 1), new Bc6HModeDescriptor(Bc6hEField.GW, 2), new Bc6HModeDescriptor(Bc6hEField.GW, 3), new Bc6HModeDescriptor(Bc6hEField.GW, 4), + new Bc6HModeDescriptor(Bc6hEField.GW, 5), new Bc6HModeDescriptor(Bc6hEField.GW, 6), new Bc6HModeDescriptor(Bc6hEField.GW, 7), new Bc6HModeDescriptor(Bc6hEField.BZ, 2), new Bc6HModeDescriptor(Bc6hEField.GY, 4), new Bc6HModeDescriptor(Bc6hEField.BW, 0), new Bc6HModeDescriptor(Bc6hEField.BW, 1), new Bc6HModeDescriptor(Bc6hEField.BW, 2), new Bc6HModeDescriptor(Bc6hEField.BW, 3), new Bc6HModeDescriptor(Bc6hEField.BW, 4), + new Bc6HModeDescriptor(Bc6hEField.BW, 5), new Bc6HModeDescriptor(Bc6hEField.BW, 6), new Bc6HModeDescriptor(Bc6hEField.BW, 7), new Bc6HModeDescriptor(Bc6hEField.BZ, 3), new Bc6HModeDescriptor(Bc6hEField.BZ, 4), new Bc6HModeDescriptor(Bc6hEField.RX, 0), new Bc6HModeDescriptor(Bc6hEField.RX, 1), new Bc6HModeDescriptor(Bc6hEField.RX, 2), new Bc6HModeDescriptor(Bc6hEField.RX, 3), new Bc6HModeDescriptor(Bc6hEField.RX, 4), + new Bc6HModeDescriptor(Bc6hEField.RX, 5), new Bc6HModeDescriptor(Bc6hEField.GY, 0), new Bc6HModeDescriptor(Bc6hEField.GY, 1), new Bc6HModeDescriptor(Bc6hEField.GY, 2), new Bc6HModeDescriptor(Bc6hEField.GY, 3), new Bc6HModeDescriptor(Bc6hEField.GX, 0), new Bc6HModeDescriptor(Bc6hEField.GX, 1), new Bc6HModeDescriptor(Bc6hEField.GX, 2), new Bc6HModeDescriptor(Bc6hEField.GX, 3), new Bc6HModeDescriptor(Bc6hEField.GX, 4), + new Bc6HModeDescriptor(Bc6hEField.BZ, 0), new Bc6HModeDescriptor(Bc6hEField.GZ, 0), new Bc6HModeDescriptor(Bc6hEField.GZ, 1), new Bc6HModeDescriptor(Bc6hEField.GZ, 2), new Bc6HModeDescriptor(Bc6hEField.GZ, 3), new Bc6HModeDescriptor(Bc6hEField.BX, 0), new Bc6HModeDescriptor(Bc6hEField.BX, 1), new Bc6HModeDescriptor(Bc6hEField.BX, 2), new Bc6HModeDescriptor(Bc6hEField.BX, 3), new Bc6HModeDescriptor(Bc6hEField.BX, 4), + new Bc6HModeDescriptor(Bc6hEField.BZ, 1), new Bc6HModeDescriptor(Bc6hEField.BY, 0), new Bc6HModeDescriptor(Bc6hEField.BY, 1), new Bc6HModeDescriptor(Bc6hEField.BY, 2), new Bc6HModeDescriptor(Bc6hEField.BY, 3), new Bc6HModeDescriptor(Bc6hEField.RY, 0), new Bc6HModeDescriptor(Bc6hEField.RY, 1), new Bc6HModeDescriptor(Bc6hEField.RY, 2), new Bc6HModeDescriptor(Bc6hEField.RY, 3), new Bc6HModeDescriptor(Bc6hEField.RY, 4), + new Bc6HModeDescriptor(Bc6hEField.RY, 5), new Bc6HModeDescriptor(Bc6hEField.RZ, 0), new Bc6HModeDescriptor(Bc6hEField.RZ, 1), new Bc6HModeDescriptor(Bc6hEField.RZ, 2), new Bc6HModeDescriptor(Bc6hEField.RZ, 3), new Bc6HModeDescriptor(Bc6hEField.RZ, 4), new Bc6HModeDescriptor(Bc6hEField.RZ, 5), new Bc6HModeDescriptor(Bc6hEField.D, 0), new Bc6HModeDescriptor(Bc6hEField.D, 1), new Bc6HModeDescriptor(Bc6hEField.D, 2), + new Bc6HModeDescriptor(Bc6hEField.D, 3), new Bc6HModeDescriptor(Bc6hEField.D, 4) + ], + + [ + // Mode 8 (0x16) - 8 5 6 5 + new Bc6HModeDescriptor(Bc6hEField.M, 0), new Bc6HModeDescriptor(Bc6hEField.M, 1), new Bc6HModeDescriptor(Bc6hEField.M, 2), new Bc6HModeDescriptor(Bc6hEField.M, 3), new Bc6HModeDescriptor(Bc6hEField.M, 4), new Bc6HModeDescriptor(Bc6hEField.RW, 0), new Bc6HModeDescriptor(Bc6hEField.RW, 1), new Bc6HModeDescriptor(Bc6hEField.RW, 2), new Bc6HModeDescriptor(Bc6hEField.RW, 3), new Bc6HModeDescriptor(Bc6hEField.RW, 4), + new Bc6HModeDescriptor(Bc6hEField.RW, 5), new Bc6HModeDescriptor(Bc6hEField.RW, 6), new Bc6HModeDescriptor(Bc6hEField.RW, 7), new Bc6HModeDescriptor(Bc6hEField.BZ, 0), new Bc6HModeDescriptor(Bc6hEField.BY, 4), new Bc6HModeDescriptor(Bc6hEField.GW, 0), new Bc6HModeDescriptor(Bc6hEField.GW, 1), new Bc6HModeDescriptor(Bc6hEField.GW, 2), new Bc6HModeDescriptor(Bc6hEField.GW, 3), new Bc6HModeDescriptor(Bc6hEField.GW, 4), + new Bc6HModeDescriptor(Bc6hEField.GW, 5), new Bc6HModeDescriptor(Bc6hEField.GW, 6), new Bc6HModeDescriptor(Bc6hEField.GW, 7), new Bc6HModeDescriptor(Bc6hEField.GY, 5), new Bc6HModeDescriptor(Bc6hEField.GY, 4), new Bc6HModeDescriptor(Bc6hEField.BW, 0), new Bc6HModeDescriptor(Bc6hEField.BW, 1), new Bc6HModeDescriptor(Bc6hEField.BW, 2), new Bc6HModeDescriptor(Bc6hEField.BW, 3), new Bc6HModeDescriptor(Bc6hEField.BW, 4), + new Bc6HModeDescriptor(Bc6hEField.BW, 5), new Bc6HModeDescriptor(Bc6hEField.BW, 6), new Bc6HModeDescriptor(Bc6hEField.BW, 7), new Bc6HModeDescriptor(Bc6hEField.GZ, 5), new Bc6HModeDescriptor(Bc6hEField.BZ, 4), new Bc6HModeDescriptor(Bc6hEField.RX, 0), new Bc6HModeDescriptor(Bc6hEField.RX, 1), new Bc6HModeDescriptor(Bc6hEField.RX, 2), new Bc6HModeDescriptor(Bc6hEField.RX, 3), new Bc6HModeDescriptor(Bc6hEField.RX, 4), + new Bc6HModeDescriptor(Bc6hEField.GZ, 4), new Bc6HModeDescriptor(Bc6hEField.GY, 0), new Bc6HModeDescriptor(Bc6hEField.GY, 1), new Bc6HModeDescriptor(Bc6hEField.GY, 2), new Bc6HModeDescriptor(Bc6hEField.GY, 3), new Bc6HModeDescriptor(Bc6hEField.GX, 0), new Bc6HModeDescriptor(Bc6hEField.GX, 1), new Bc6HModeDescriptor(Bc6hEField.GX, 2), new Bc6HModeDescriptor(Bc6hEField.GX, 3), new Bc6HModeDescriptor(Bc6hEField.GX, 4), + new Bc6HModeDescriptor(Bc6hEField.GX, 5), new Bc6HModeDescriptor(Bc6hEField.GZ, 0), new Bc6HModeDescriptor(Bc6hEField.GZ, 1), new Bc6HModeDescriptor(Bc6hEField.GZ, 2), new Bc6HModeDescriptor(Bc6hEField.GZ, 3), new Bc6HModeDescriptor(Bc6hEField.BX, 0), new Bc6HModeDescriptor(Bc6hEField.BX, 1), new Bc6HModeDescriptor(Bc6hEField.BX, 2), new Bc6HModeDescriptor(Bc6hEField.BX, 3), new Bc6HModeDescriptor(Bc6hEField.BX, 4), + new Bc6HModeDescriptor(Bc6hEField.BZ, 1), new Bc6HModeDescriptor(Bc6hEField.BY, 0), new Bc6HModeDescriptor(Bc6hEField.BY, 1), new Bc6HModeDescriptor(Bc6hEField.BY, 2), new Bc6HModeDescriptor(Bc6hEField.BY, 3), new Bc6HModeDescriptor(Bc6hEField.RY, 0), new Bc6HModeDescriptor(Bc6hEField.RY, 1), new Bc6HModeDescriptor(Bc6hEField.RY, 2), new Bc6HModeDescriptor(Bc6hEField.RY, 3), new Bc6HModeDescriptor(Bc6hEField.RY, 4), + new Bc6HModeDescriptor(Bc6hEField.BZ, 2), new Bc6HModeDescriptor(Bc6hEField.RZ, 0), new Bc6HModeDescriptor(Bc6hEField.RZ, 1), new Bc6HModeDescriptor(Bc6hEField.RZ, 2), new Bc6HModeDescriptor(Bc6hEField.RZ, 3), new Bc6HModeDescriptor(Bc6hEField.RZ, 4), new Bc6HModeDescriptor(Bc6hEField.BZ, 3), new Bc6HModeDescriptor(Bc6hEField.D, 0), new Bc6HModeDescriptor(Bc6hEField.D, 1), new Bc6HModeDescriptor(Bc6hEField.D, 2), + new Bc6HModeDescriptor(Bc6hEField.D, 3), new Bc6HModeDescriptor(Bc6hEField.D, 4) + ], + + [ + // Mode 9 (0x1a) - 8 5 5 6 + new Bc6HModeDescriptor(Bc6hEField.M, 0), new Bc6HModeDescriptor(Bc6hEField.M, 1), new Bc6HModeDescriptor(Bc6hEField.M, 2), new Bc6HModeDescriptor(Bc6hEField.M, 3), new Bc6HModeDescriptor(Bc6hEField.M, 4), new Bc6HModeDescriptor(Bc6hEField.RW, 0), new Bc6HModeDescriptor(Bc6hEField.RW, 1), new Bc6HModeDescriptor(Bc6hEField.RW, 2), new Bc6HModeDescriptor(Bc6hEField.RW, 3), new Bc6HModeDescriptor(Bc6hEField.RW, 4), + new Bc6HModeDescriptor(Bc6hEField.RW, 5), new Bc6HModeDescriptor(Bc6hEField.RW, 6), new Bc6HModeDescriptor(Bc6hEField.RW, 7), new Bc6HModeDescriptor(Bc6hEField.BZ, 1), new Bc6HModeDescriptor(Bc6hEField.BY, 4), new Bc6HModeDescriptor(Bc6hEField.GW, 0), new Bc6HModeDescriptor(Bc6hEField.GW, 1), new Bc6HModeDescriptor(Bc6hEField.GW, 2), new Bc6HModeDescriptor(Bc6hEField.GW, 3), new Bc6HModeDescriptor(Bc6hEField.GW, 4), + new Bc6HModeDescriptor(Bc6hEField.GW, 5), new Bc6HModeDescriptor(Bc6hEField.GW, 6), new Bc6HModeDescriptor(Bc6hEField.GW, 7), new Bc6HModeDescriptor(Bc6hEField.BY, 5), new Bc6HModeDescriptor(Bc6hEField.GY, 4), new Bc6HModeDescriptor(Bc6hEField.BW, 0), new Bc6HModeDescriptor(Bc6hEField.BW, 1), new Bc6HModeDescriptor(Bc6hEField.BW, 2), new Bc6HModeDescriptor(Bc6hEField.BW, 3), new Bc6HModeDescriptor(Bc6hEField.BW, 4), + new Bc6HModeDescriptor(Bc6hEField.BW, 5), new Bc6HModeDescriptor(Bc6hEField.BW, 6), new Bc6HModeDescriptor(Bc6hEField.BW, 7), new Bc6HModeDescriptor(Bc6hEField.BZ, 5), new Bc6HModeDescriptor(Bc6hEField.BZ, 4), new Bc6HModeDescriptor(Bc6hEField.RX, 0), new Bc6HModeDescriptor(Bc6hEField.RX, 1), new Bc6HModeDescriptor(Bc6hEField.RX, 2), new Bc6HModeDescriptor(Bc6hEField.RX, 3), new Bc6HModeDescriptor(Bc6hEField.RX, 4), + new Bc6HModeDescriptor(Bc6hEField.GZ, 4), new Bc6HModeDescriptor(Bc6hEField.GY, 0), new Bc6HModeDescriptor(Bc6hEField.GY, 1), new Bc6HModeDescriptor(Bc6hEField.GY, 2), new Bc6HModeDescriptor(Bc6hEField.GY, 3), new Bc6HModeDescriptor(Bc6hEField.GX, 0), new Bc6HModeDescriptor(Bc6hEField.GX, 1), new Bc6HModeDescriptor(Bc6hEField.GX, 2), new Bc6HModeDescriptor(Bc6hEField.GX, 3), new Bc6HModeDescriptor(Bc6hEField.GX, 4), + new Bc6HModeDescriptor(Bc6hEField.BZ, 0), new Bc6HModeDescriptor(Bc6hEField.GZ, 0), new Bc6HModeDescriptor(Bc6hEField.GZ, 1), new Bc6HModeDescriptor(Bc6hEField.GZ, 2), new Bc6HModeDescriptor(Bc6hEField.GZ, 3), new Bc6HModeDescriptor(Bc6hEField.BX, 0), new Bc6HModeDescriptor(Bc6hEField.BX, 1), new Bc6HModeDescriptor(Bc6hEField.BX, 2), new Bc6HModeDescriptor(Bc6hEField.BX, 3), new Bc6HModeDescriptor(Bc6hEField.BX, 4), + new Bc6HModeDescriptor(Bc6hEField.BX, 5), new Bc6HModeDescriptor(Bc6hEField.BY, 0), new Bc6HModeDescriptor(Bc6hEField.BY, 1), new Bc6HModeDescriptor(Bc6hEField.BY, 2), new Bc6HModeDescriptor(Bc6hEField.BY, 3), new Bc6HModeDescriptor(Bc6hEField.RY, 0), new Bc6HModeDescriptor(Bc6hEField.RY, 1), new Bc6HModeDescriptor(Bc6hEField.RY, 2), new Bc6HModeDescriptor(Bc6hEField.RY, 3), new Bc6HModeDescriptor(Bc6hEField.RY, 4), + new Bc6HModeDescriptor(Bc6hEField.BZ, 2), new Bc6HModeDescriptor(Bc6hEField.RZ, 0), new Bc6HModeDescriptor(Bc6hEField.RZ, 1), new Bc6HModeDescriptor(Bc6hEField.RZ, 2), new Bc6HModeDescriptor(Bc6hEField.RZ, 3), new Bc6HModeDescriptor(Bc6hEField.RZ, 4), new Bc6HModeDescriptor(Bc6hEField.BZ, 3), new Bc6HModeDescriptor(Bc6hEField.D, 0), new Bc6HModeDescriptor(Bc6hEField.D, 1), new Bc6HModeDescriptor(Bc6hEField.D, 2), + new Bc6HModeDescriptor(Bc6hEField.D, 3), new Bc6HModeDescriptor(Bc6hEField.D, 4) + ], + + [ + // Mode 10 (0x1e) - 6 6 6 6 + new Bc6HModeDescriptor(Bc6hEField.M, 0), new Bc6HModeDescriptor(Bc6hEField.M, 1), new Bc6HModeDescriptor(Bc6hEField.M, 2), new Bc6HModeDescriptor(Bc6hEField.M, 3), new Bc6HModeDescriptor(Bc6hEField.M, 4), new Bc6HModeDescriptor(Bc6hEField.RW, 0), new Bc6HModeDescriptor(Bc6hEField.RW, 1), new Bc6HModeDescriptor(Bc6hEField.RW, 2), new Bc6HModeDescriptor(Bc6hEField.RW, 3), new Bc6HModeDescriptor(Bc6hEField.RW, 4), + new Bc6HModeDescriptor(Bc6hEField.RW, 5), new Bc6HModeDescriptor(Bc6hEField.GZ, 4), new Bc6HModeDescriptor(Bc6hEField.BZ, 0), new Bc6HModeDescriptor(Bc6hEField.BZ, 1), new Bc6HModeDescriptor(Bc6hEField.BY, 4), new Bc6HModeDescriptor(Bc6hEField.GW, 0), new Bc6HModeDescriptor(Bc6hEField.GW, 1), new Bc6HModeDescriptor(Bc6hEField.GW, 2), new Bc6HModeDescriptor(Bc6hEField.GW, 3), new Bc6HModeDescriptor(Bc6hEField.GW, 4), + new Bc6HModeDescriptor(Bc6hEField.GW, 5), new Bc6HModeDescriptor(Bc6hEField.GY, 5), new Bc6HModeDescriptor(Bc6hEField.BY, 5), new Bc6HModeDescriptor(Bc6hEField.BZ, 2), new Bc6HModeDescriptor(Bc6hEField.GY, 4), new Bc6HModeDescriptor(Bc6hEField.BW, 0), new Bc6HModeDescriptor(Bc6hEField.BW, 1), new Bc6HModeDescriptor(Bc6hEField.BW, 2), new Bc6HModeDescriptor(Bc6hEField.BW, 3), new Bc6HModeDescriptor(Bc6hEField.BW, 4), + new Bc6HModeDescriptor(Bc6hEField.BW, 5), new Bc6HModeDescriptor(Bc6hEField.GZ, 5), new Bc6HModeDescriptor(Bc6hEField.BZ, 3), new Bc6HModeDescriptor(Bc6hEField.BZ, 5), new Bc6HModeDescriptor(Bc6hEField.BZ, 4), new Bc6HModeDescriptor(Bc6hEField.RX, 0), new Bc6HModeDescriptor(Bc6hEField.RX, 1), new Bc6HModeDescriptor(Bc6hEField.RX, 2), new Bc6HModeDescriptor(Bc6hEField.RX, 3), new Bc6HModeDescriptor(Bc6hEField.RX, 4), + new Bc6HModeDescriptor(Bc6hEField.RX, 5), new Bc6HModeDescriptor(Bc6hEField.GY, 0), new Bc6HModeDescriptor(Bc6hEField.GY, 1), new Bc6HModeDescriptor(Bc6hEField.GY, 2), new Bc6HModeDescriptor(Bc6hEField.GY, 3), new Bc6HModeDescriptor(Bc6hEField.GX, 0), new Bc6HModeDescriptor(Bc6hEField.GX, 1), new Bc6HModeDescriptor(Bc6hEField.GX, 2), new Bc6HModeDescriptor(Bc6hEField.GX, 3), new Bc6HModeDescriptor(Bc6hEField.GX, 4), + new Bc6HModeDescriptor(Bc6hEField.GX, 5), new Bc6HModeDescriptor(Bc6hEField.GZ, 0), new Bc6HModeDescriptor(Bc6hEField.GZ, 1), new Bc6HModeDescriptor(Bc6hEField.GZ, 2), new Bc6HModeDescriptor(Bc6hEField.GZ, 3), new Bc6HModeDescriptor(Bc6hEField.BX, 0), new Bc6HModeDescriptor(Bc6hEField.BX, 1), new Bc6HModeDescriptor(Bc6hEField.BX, 2), new Bc6HModeDescriptor(Bc6hEField.BX, 3), new Bc6HModeDescriptor(Bc6hEField.BX, 4), + new Bc6HModeDescriptor(Bc6hEField.BX, 5), new Bc6HModeDescriptor(Bc6hEField.BY, 0), new Bc6HModeDescriptor(Bc6hEField.BY, 1), new Bc6HModeDescriptor(Bc6hEField.BY, 2), new Bc6HModeDescriptor(Bc6hEField.BY, 3), new Bc6HModeDescriptor(Bc6hEField.RY, 0), new Bc6HModeDescriptor(Bc6hEField.RY, 1), new Bc6HModeDescriptor(Bc6hEField.RY, 2), new Bc6HModeDescriptor(Bc6hEField.RY, 3), new Bc6HModeDescriptor(Bc6hEField.RY, 4), + new Bc6HModeDescriptor(Bc6hEField.RY, 5), new Bc6HModeDescriptor(Bc6hEField.RZ, 0), new Bc6HModeDescriptor(Bc6hEField.RZ, 1), new Bc6HModeDescriptor(Bc6hEField.RZ, 2), new Bc6HModeDescriptor(Bc6hEField.RZ, 3), new Bc6HModeDescriptor(Bc6hEField.RZ, 4), new Bc6HModeDescriptor(Bc6hEField.RZ, 5), new Bc6HModeDescriptor(Bc6hEField.D, 0), new Bc6HModeDescriptor(Bc6hEField.D, 1), new Bc6HModeDescriptor(Bc6hEField.D, 2), + new Bc6HModeDescriptor(Bc6hEField.D, 3), new Bc6HModeDescriptor(Bc6hEField.D, 4) + ], + + [ + // Mode 11 (0x03) - 10 10 + new Bc6HModeDescriptor(Bc6hEField.M, 0), new Bc6HModeDescriptor(Bc6hEField.M, 1), new Bc6HModeDescriptor(Bc6hEField.M, 2), new Bc6HModeDescriptor(Bc6hEField.M, 3), new Bc6HModeDescriptor(Bc6hEField.M, 4), new Bc6HModeDescriptor(Bc6hEField.RW, 0), new Bc6HModeDescriptor(Bc6hEField.RW, 1), new Bc6HModeDescriptor(Bc6hEField.RW, 2), new Bc6HModeDescriptor(Bc6hEField.RW, 3), new Bc6HModeDescriptor(Bc6hEField.RW, 4), + new Bc6HModeDescriptor(Bc6hEField.RW, 5), new Bc6HModeDescriptor(Bc6hEField.RW, 6), new Bc6HModeDescriptor(Bc6hEField.RW, 7), new Bc6HModeDescriptor(Bc6hEField.RW, 8), new Bc6HModeDescriptor(Bc6hEField.RW, 9), new Bc6HModeDescriptor(Bc6hEField.GW, 0), new Bc6HModeDescriptor(Bc6hEField.GW, 1), new Bc6HModeDescriptor(Bc6hEField.GW, 2), new Bc6HModeDescriptor(Bc6hEField.GW, 3), new Bc6HModeDescriptor(Bc6hEField.GW, 4), + new Bc6HModeDescriptor(Bc6hEField.GW, 5), new Bc6HModeDescriptor(Bc6hEField.GW, 6), new Bc6HModeDescriptor(Bc6hEField.GW, 7), new Bc6HModeDescriptor(Bc6hEField.GW, 8), new Bc6HModeDescriptor(Bc6hEField.GW, 9), new Bc6HModeDescriptor(Bc6hEField.BW, 0), new Bc6HModeDescriptor(Bc6hEField.BW, 1), new Bc6HModeDescriptor(Bc6hEField.BW, 2), new Bc6HModeDescriptor(Bc6hEField.BW, 3), new Bc6HModeDescriptor(Bc6hEField.BW, 4), + new Bc6HModeDescriptor(Bc6hEField.BW, 5), new Bc6HModeDescriptor(Bc6hEField.BW, 6), new Bc6HModeDescriptor(Bc6hEField.BW, 7), new Bc6HModeDescriptor(Bc6hEField.BW, 8), new Bc6HModeDescriptor(Bc6hEField.BW, 9), new Bc6HModeDescriptor(Bc6hEField.RX, 0), new Bc6HModeDescriptor(Bc6hEField.RX, 1), new Bc6HModeDescriptor(Bc6hEField.RX, 2), new Bc6HModeDescriptor(Bc6hEField.RX, 3), new Bc6HModeDescriptor(Bc6hEField.RX, 4), + new Bc6HModeDescriptor(Bc6hEField.RX, 5), new Bc6HModeDescriptor(Bc6hEField.RX, 6), new Bc6HModeDescriptor(Bc6hEField.RX, 7), new Bc6HModeDescriptor(Bc6hEField.RX, 8), new Bc6HModeDescriptor(Bc6hEField.RX, 9), new Bc6HModeDescriptor(Bc6hEField.GX, 0), new Bc6HModeDescriptor(Bc6hEField.GX, 1), new Bc6HModeDescriptor(Bc6hEField.GX, 2), new Bc6HModeDescriptor(Bc6hEField.GX, 3), new Bc6HModeDescriptor(Bc6hEField.GX, 4), + new Bc6HModeDescriptor(Bc6hEField.GX, 5), new Bc6HModeDescriptor(Bc6hEField.GX, 6), new Bc6HModeDescriptor(Bc6hEField.GX, 7), new Bc6HModeDescriptor(Bc6hEField.GX, 8), new Bc6HModeDescriptor(Bc6hEField.GX, 9), new Bc6HModeDescriptor(Bc6hEField.BX, 0), new Bc6HModeDescriptor(Bc6hEField.BX, 1), new Bc6HModeDescriptor(Bc6hEField.BX, 2), new Bc6HModeDescriptor(Bc6hEField.BX, 3), new Bc6HModeDescriptor(Bc6hEField.BX, 4), + new Bc6HModeDescriptor(Bc6hEField.BX, 5), new Bc6HModeDescriptor(Bc6hEField.BX, 6), new Bc6HModeDescriptor(Bc6hEField.BX, 7), new Bc6HModeDescriptor(Bc6hEField.BX, 8), new Bc6HModeDescriptor(Bc6hEField.BX, 9), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), + new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), + new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0) + ], + + [ + // Mode 12 (0x07) - 11 9 + new Bc6HModeDescriptor(Bc6hEField.M, 0), new Bc6HModeDescriptor(Bc6hEField.M, 1), new Bc6HModeDescriptor(Bc6hEField.M, 2), new Bc6HModeDescriptor(Bc6hEField.M, 3), new Bc6HModeDescriptor(Bc6hEField.M, 4), new Bc6HModeDescriptor(Bc6hEField.RW, 0), new Bc6HModeDescriptor(Bc6hEField.RW, 1), new Bc6HModeDescriptor(Bc6hEField.RW, 2), new Bc6HModeDescriptor(Bc6hEField.RW, 3), new Bc6HModeDescriptor(Bc6hEField.RW, 4), + new Bc6HModeDescriptor(Bc6hEField.RW, 5), new Bc6HModeDescriptor(Bc6hEField.RW, 6), new Bc6HModeDescriptor(Bc6hEField.RW, 7), new Bc6HModeDescriptor(Bc6hEField.RW, 8), new Bc6HModeDescriptor(Bc6hEField.RW, 9), new Bc6HModeDescriptor(Bc6hEField.GW, 0), new Bc6HModeDescriptor(Bc6hEField.GW, 1), new Bc6HModeDescriptor(Bc6hEField.GW, 2), new Bc6HModeDescriptor(Bc6hEField.GW, 3), new Bc6HModeDescriptor(Bc6hEField.GW, 4), + new Bc6HModeDescriptor(Bc6hEField.GW, 5), new Bc6HModeDescriptor(Bc6hEField.GW, 6), new Bc6HModeDescriptor(Bc6hEField.GW, 7), new Bc6HModeDescriptor(Bc6hEField.GW, 8), new Bc6HModeDescriptor(Bc6hEField.GW, 9), new Bc6HModeDescriptor(Bc6hEField.BW, 0), new Bc6HModeDescriptor(Bc6hEField.BW, 1), new Bc6HModeDescriptor(Bc6hEField.BW, 2), new Bc6HModeDescriptor(Bc6hEField.BW, 3), new Bc6HModeDescriptor(Bc6hEField.BW, 4), + new Bc6HModeDescriptor(Bc6hEField.BW, 5), new Bc6HModeDescriptor(Bc6hEField.BW, 6), new Bc6HModeDescriptor(Bc6hEField.BW, 7), new Bc6HModeDescriptor(Bc6hEField.BW, 8), new Bc6HModeDescriptor(Bc6hEField.BW, 9), new Bc6HModeDescriptor(Bc6hEField.RX, 0), new Bc6HModeDescriptor(Bc6hEField.RX, 1), new Bc6HModeDescriptor(Bc6hEField.RX, 2), new Bc6HModeDescriptor(Bc6hEField.RX, 3), new Bc6HModeDescriptor(Bc6hEField.RX, 4), + new Bc6HModeDescriptor(Bc6hEField.RX, 5), new Bc6HModeDescriptor(Bc6hEField.RX, 6), new Bc6HModeDescriptor(Bc6hEField.RX, 7), new Bc6HModeDescriptor(Bc6hEField.RX, 8), new Bc6HModeDescriptor(Bc6hEField.RW, 10), new Bc6HModeDescriptor(Bc6hEField.GX, 0), new Bc6HModeDescriptor(Bc6hEField.GX, 1), new Bc6HModeDescriptor(Bc6hEField.GX, 2), new Bc6HModeDescriptor(Bc6hEField.GX, 3), new Bc6HModeDescriptor(Bc6hEField.GX, 4), + new Bc6HModeDescriptor(Bc6hEField.GX, 5), new Bc6HModeDescriptor(Bc6hEField.GX, 6), new Bc6HModeDescriptor(Bc6hEField.GX, 7), new Bc6HModeDescriptor(Bc6hEField.GX, 8), new Bc6HModeDescriptor(Bc6hEField.GW, 10), new Bc6HModeDescriptor(Bc6hEField.BX, 0), new Bc6HModeDescriptor(Bc6hEField.BX, 1), new Bc6HModeDescriptor(Bc6hEField.BX, 2), new Bc6HModeDescriptor(Bc6hEField.BX, 3), new Bc6HModeDescriptor(Bc6hEField.BX, 4), + new Bc6HModeDescriptor(Bc6hEField.BX, 5), new Bc6HModeDescriptor(Bc6hEField.BX, 6), new Bc6HModeDescriptor(Bc6hEField.BX, 7), new Bc6HModeDescriptor(Bc6hEField.BX, 8), new Bc6HModeDescriptor(Bc6hEField.BW, 10), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), + new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), + new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0) + ], + + [ + // Mode 13 (0x0b) - 12 8 + new Bc6HModeDescriptor(Bc6hEField.M, 0), new Bc6HModeDescriptor(Bc6hEField.M, 1), new Bc6HModeDescriptor(Bc6hEField.M, 2), new Bc6HModeDescriptor(Bc6hEField.M, 3), new Bc6HModeDescriptor(Bc6hEField.M, 4), new Bc6HModeDescriptor(Bc6hEField.RW, 0), new Bc6HModeDescriptor(Bc6hEField.RW, 1), new Bc6HModeDescriptor(Bc6hEField.RW, 2), new Bc6HModeDescriptor(Bc6hEField.RW, 3), new Bc6HModeDescriptor(Bc6hEField.RW, 4), + new Bc6HModeDescriptor(Bc6hEField.RW, 5), new Bc6HModeDescriptor(Bc6hEField.RW, 6), new Bc6HModeDescriptor(Bc6hEField.RW, 7), new Bc6HModeDescriptor(Bc6hEField.RW, 8), new Bc6HModeDescriptor(Bc6hEField.RW, 9), new Bc6HModeDescriptor(Bc6hEField.GW, 0), new Bc6HModeDescriptor(Bc6hEField.GW, 1), new Bc6HModeDescriptor(Bc6hEField.GW, 2), new Bc6HModeDescriptor(Bc6hEField.GW, 3), new Bc6HModeDescriptor(Bc6hEField.GW, 4), + new Bc6HModeDescriptor(Bc6hEField.GW, 5), new Bc6HModeDescriptor(Bc6hEField.GW, 6), new Bc6HModeDescriptor(Bc6hEField.GW, 7), new Bc6HModeDescriptor(Bc6hEField.GW, 8), new Bc6HModeDescriptor(Bc6hEField.GW, 9), new Bc6HModeDescriptor(Bc6hEField.BW, 0), new Bc6HModeDescriptor(Bc6hEField.BW, 1), new Bc6HModeDescriptor(Bc6hEField.BW, 2), new Bc6HModeDescriptor(Bc6hEField.BW, 3), new Bc6HModeDescriptor(Bc6hEField.BW, 4), + new Bc6HModeDescriptor(Bc6hEField.BW, 5), new Bc6HModeDescriptor(Bc6hEField.BW, 6), new Bc6HModeDescriptor(Bc6hEField.BW, 7), new Bc6HModeDescriptor(Bc6hEField.BW, 8), new Bc6HModeDescriptor(Bc6hEField.BW, 9), new Bc6HModeDescriptor(Bc6hEField.RX, 0), new Bc6HModeDescriptor(Bc6hEField.RX, 1), new Bc6HModeDescriptor(Bc6hEField.RX, 2), new Bc6HModeDescriptor(Bc6hEField.RX, 3), new Bc6HModeDescriptor(Bc6hEField.RX, 4), + new Bc6HModeDescriptor(Bc6hEField.RX, 5), new Bc6HModeDescriptor(Bc6hEField.RX, 6), new Bc6HModeDescriptor(Bc6hEField.RX, 7), new Bc6HModeDescriptor(Bc6hEField.RW, 11), new Bc6HModeDescriptor(Bc6hEField.RW, 10), new Bc6HModeDescriptor(Bc6hEField.GX, 0), new Bc6HModeDescriptor(Bc6hEField.GX, 1), new Bc6HModeDescriptor(Bc6hEField.GX, 2), new Bc6HModeDescriptor(Bc6hEField.GX, 3), new Bc6HModeDescriptor(Bc6hEField.GX, 4), + new Bc6HModeDescriptor(Bc6hEField.GX, 5), new Bc6HModeDescriptor(Bc6hEField.GX, 6), new Bc6HModeDescriptor(Bc6hEField.GX, 7), new Bc6HModeDescriptor(Bc6hEField.GW, 11), new Bc6HModeDescriptor(Bc6hEField.GW, 10), new Bc6HModeDescriptor(Bc6hEField.BX, 0), new Bc6HModeDescriptor(Bc6hEField.BX, 1), new Bc6HModeDescriptor(Bc6hEField.BX, 2), new Bc6HModeDescriptor(Bc6hEField.BX, 3), new Bc6HModeDescriptor(Bc6hEField.BX, 4), + new Bc6HModeDescriptor(Bc6hEField.BX, 5), new Bc6HModeDescriptor(Bc6hEField.BX, 6), new Bc6HModeDescriptor(Bc6hEField.BX, 7), new Bc6HModeDescriptor(Bc6hEField.BW, 11), new Bc6HModeDescriptor(Bc6hEField.BW, 10), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), + new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), + new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0) + ], + + [ + // Mode 14 (0x0f) - 16 4 + new Bc6HModeDescriptor(Bc6hEField.M, 0), new Bc6HModeDescriptor(Bc6hEField.M, 1), new Bc6HModeDescriptor(Bc6hEField.M, 2), new Bc6HModeDescriptor(Bc6hEField.M, 3), new Bc6HModeDescriptor(Bc6hEField.M, 4), new Bc6HModeDescriptor(Bc6hEField.RW, 0), new Bc6HModeDescriptor(Bc6hEField.RW, 1), new Bc6HModeDescriptor(Bc6hEField.RW, 2), new Bc6HModeDescriptor(Bc6hEField.RW, 3), new Bc6HModeDescriptor(Bc6hEField.RW, 4), + new Bc6HModeDescriptor(Bc6hEField.RW, 5), new Bc6HModeDescriptor(Bc6hEField.RW, 6), new Bc6HModeDescriptor(Bc6hEField.RW, 7), new Bc6HModeDescriptor(Bc6hEField.RW, 8), new Bc6HModeDescriptor(Bc6hEField.RW, 9), new Bc6HModeDescriptor(Bc6hEField.GW, 0), new Bc6HModeDescriptor(Bc6hEField.GW, 1), new Bc6HModeDescriptor(Bc6hEField.GW, 2), new Bc6HModeDescriptor(Bc6hEField.GW, 3), new Bc6HModeDescriptor(Bc6hEField.GW, 4), + new Bc6HModeDescriptor(Bc6hEField.GW, 5), new Bc6HModeDescriptor(Bc6hEField.GW, 6), new Bc6HModeDescriptor(Bc6hEField.GW, 7), new Bc6HModeDescriptor(Bc6hEField.GW, 8), new Bc6HModeDescriptor(Bc6hEField.GW, 9), new Bc6HModeDescriptor(Bc6hEField.BW, 0), new Bc6HModeDescriptor(Bc6hEField.BW, 1), new Bc6HModeDescriptor(Bc6hEField.BW, 2), new Bc6HModeDescriptor(Bc6hEField.BW, 3), new Bc6HModeDescriptor(Bc6hEField.BW, 4), + new Bc6HModeDescriptor(Bc6hEField.BW, 5), new Bc6HModeDescriptor(Bc6hEField.BW, 6), new Bc6HModeDescriptor(Bc6hEField.BW, 7), new Bc6HModeDescriptor(Bc6hEField.BW, 8), new Bc6HModeDescriptor(Bc6hEField.BW, 9), new Bc6HModeDescriptor(Bc6hEField.RX, 0), new Bc6HModeDescriptor(Bc6hEField.RX, 1), new Bc6HModeDescriptor(Bc6hEField.RX, 2), new Bc6HModeDescriptor(Bc6hEField.RX, 3), new Bc6HModeDescriptor(Bc6hEField.RW, 15), + new Bc6HModeDescriptor(Bc6hEField.RW, 14), new Bc6HModeDescriptor(Bc6hEField.RW, 13), new Bc6HModeDescriptor(Bc6hEField.RW, 12), new Bc6HModeDescriptor(Bc6hEField.RW, 11), new Bc6HModeDescriptor(Bc6hEField.RW, 10), new Bc6HModeDescriptor(Bc6hEField.GX, 0), new Bc6HModeDescriptor(Bc6hEField.GX, 1), new Bc6HModeDescriptor(Bc6hEField.GX, 2), new Bc6HModeDescriptor(Bc6hEField.GX, 3), new Bc6HModeDescriptor(Bc6hEField.GW, 15), + new Bc6HModeDescriptor(Bc6hEField.GW, 14), new Bc6HModeDescriptor(Bc6hEField.GW, 13), new Bc6HModeDescriptor(Bc6hEField.GW, 12), new Bc6HModeDescriptor(Bc6hEField.GW, 11), new Bc6HModeDescriptor(Bc6hEField.GW, 10), new Bc6HModeDescriptor(Bc6hEField.BX, 0), new Bc6HModeDescriptor(Bc6hEField.BX, 1), new Bc6HModeDescriptor(Bc6hEField.BX, 2), new Bc6HModeDescriptor(Bc6hEField.BX, 3), new Bc6HModeDescriptor(Bc6hEField.BW, 15), + new Bc6HModeDescriptor(Bc6hEField.BW, 14), new Bc6HModeDescriptor(Bc6hEField.BW, 13), new Bc6HModeDescriptor(Bc6hEField.BW, 12), new Bc6HModeDescriptor(Bc6hEField.BW, 11), new Bc6HModeDescriptor(Bc6hEField.BW, 10), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), + new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), + new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0) + ], + ]; + + private static readonly Bc6hModeInfo[] ModeInfo = + [ + new Bc6hModeInfo(0x00, 1, true, 3, [[new LdrColorA(10, 10, 10, 0), new LdrColorA(5, 5, 5, 0)], [new LdrColorA(5, 5, 5, 0), new LdrColorA(5, 5, 5, 0)]]), // Mode 1 + new Bc6hModeInfo(0x01, 1, true, 3, [[new LdrColorA(7, 7, 7, 0), new LdrColorA(6, 6, 6, 0)], [new LdrColorA(6, 6, 6, 0), new LdrColorA(6, 6, 6, 0)]]), // Mode 2 + new Bc6hModeInfo(0x02, 1, true, 3, [[new LdrColorA(11, 11, 11, 0), new LdrColorA(5, 4, 4, 0)], [new LdrColorA(5, 4, 4, 0), new LdrColorA(5, 4, 4, 0)]]), // Mode 3 + new Bc6hModeInfo(0x06, 1, true, 3, [[new LdrColorA(11, 11, 11, 0), new LdrColorA(4, 5, 4, 0)], [new LdrColorA(4, 5, 4, 0), new LdrColorA(4, 5, 4, 0)]]), // Mode 4 + new Bc6hModeInfo(0x0a, 1, true, 3, [[new LdrColorA(11, 11, 11, 0), new LdrColorA(4, 4, 5, 0)], [new LdrColorA(4, 4, 5, 0), new LdrColorA(4, 4, 5, 0)]]), // Mode 5 + new Bc6hModeInfo(0x0e, 1, true, 3, [[new LdrColorA(9, 9, 9, 0), new LdrColorA(5, 5, 5, 0)], [new LdrColorA(5, 5, 5, 0), new LdrColorA(5, 5, 5, 0)]]), // Mode 6 + new Bc6hModeInfo(0x12, 1, true, 3, [[new LdrColorA(8, 8, 8, 0), new LdrColorA(6, 5, 5, 0)], [new LdrColorA(6, 5, 5, 0), new LdrColorA(6, 5, 5, 0)]]), // Mode 7 + new Bc6hModeInfo(0x16, 1, true, 3, [[new LdrColorA(8, 8, 8, 0), new LdrColorA(5, 6, 5, 0)], [new LdrColorA(5, 6, 5, 0), new LdrColorA(5, 6, 5, 0)]]), // Mode 8 + new Bc6hModeInfo(0x1a, 1, true, 3, [[new LdrColorA(8, 8, 8, 0), new LdrColorA(5, 5, 6, 0)], [new LdrColorA(5, 5, 6, 0), new LdrColorA(5, 5, 6, 0)]]), // Mode 9 + new Bc6hModeInfo(0x1e, 1, false, 3, [[new LdrColorA(6, 6, 6, 0), new LdrColorA(6, 6, 6, 0)], [new LdrColorA(6, 6, 6, 0), new LdrColorA(6, 6, 6, 0)]]), // Mode 10 + new Bc6hModeInfo(0x03, 0, false, 4, [[new LdrColorA(10, 10, 10, 0), new LdrColorA(10, 10, 10, 0)], [new LdrColorA(0, 0, 0, 0), new LdrColorA(0, 0, 0, 0)]]), // Mode 11 + new Bc6hModeInfo(0x07, 0, true, 4, [[new LdrColorA(11, 11, 11, 0), new LdrColorA(9, 9, 9, 0)], [new LdrColorA(0, 0, 0, 0), new LdrColorA(0, 0, 0, 0)]]), // Mode 12 + new Bc6hModeInfo(0x0b, 0, true, 4, [[new LdrColorA(12, 12, 12, 0), new LdrColorA(8, 8, 8, 0)], [new LdrColorA(0, 0, 0, 0), new LdrColorA(0, 0, 0, 0)]]), // Mode 13 + new Bc6hModeInfo(0x0f, 0, true, 4, [[new LdrColorA(16, 16, 16, 0), new LdrColorA(4, 4, 4, 0)], [new LdrColorA(0, 0, 0, 0), new LdrColorA(0, 0, 0, 0)]]), // Mode 14 + ]; + + private static readonly int[] ModeToInfo = + [ + 0, // Mode 1 - 0x00 + 1, // Mode 2 - 0x01 + 2, // Mode 3 - 0x02 + 10, // Mode 11 - 0x03 + -1, // Invalid - 0x04 + -1, // Invalid - 0x05 + 3, // Mode 4 - 0x06 + 11, // Mode 12 - 0x07 + -1, // Invalid - 0x08 + -1, // Invalid - 0x09 + 4, // Mode 5 - 0x0a + 12, // Mode 13 - 0x0b + -1, // Invalid - 0x0c + -1, // Invalid - 0x0d + 5, // Mode 6 - 0x0e + 13, // Mode 14 - 0x0f + -1, // Invalid - 0x10 + -1, // Invalid - 0x11 + 6, // Mode 7 - 0x12 + -1, // Reserved - 0x13 + -1, // Invalid - 0x14 + -1, // Invalid - 0x15 + 7, // Mode 8 - 0x16 + -1, // Reserved - 0x17 + -1, // Invalid - 0x18 + -1, // Invalid - 0x19 + 8, // Mode 9 - 0x1a + -1, // Reserved - 0x1b + -1, // Invalid - 0x1c + -1, // Invalid - 0x1d + 9, // Mode 10 - 0x1e + -1, // Resreved - 0x1f + ]; + + /// + public readonly int BitsPerPixel => 32; + + /// + public readonly byte PixelDepthBytes => 4; + + /// + public readonly byte DivSize => 4; + + /// + public readonly byte CompressedBytesPerBlock => 16; + + /// + public readonly bool Compressed => true; + + /// + public Image GetImage(byte[] blockData, int width, int height) { - // Code based on commit 138efff1b9c53fd9a5dd34b8c865e8f5ae798030 2019/10/24 in DirectXTex C++ library - private static readonly Bc6HModeDescriptor[][] ModeDescriptors = new[] + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); + } + + /// + public byte[] Decompress(byte[] blockData, int width, int height) + { + Bc6h self = this; + byte[] currentBlock = new byte[this.CompressedBytesPerBlock]; + + return Helper.InMemoryDecode(blockData, width, height, (stream, data, streamIndex, dataIndex, stride) => { - new[] - { - // Mode 1 (0x00) - 10 5 5 5 - new Bc6HModeDescriptor(Bc6hEField.M, 0), new Bc6HModeDescriptor(Bc6hEField.M, 1), new Bc6HModeDescriptor(Bc6hEField.GY, 4), new Bc6HModeDescriptor(Bc6hEField.BY, 4), new Bc6HModeDescriptor(Bc6hEField.BZ, 4), new Bc6HModeDescriptor(Bc6hEField.RW, 0), new Bc6HModeDescriptor(Bc6hEField.RW, 1), new Bc6HModeDescriptor(Bc6hEField.RW, 2), new Bc6HModeDescriptor(Bc6hEField.RW, 3), new Bc6HModeDescriptor(Bc6hEField.RW, 4), - new Bc6HModeDescriptor(Bc6hEField.RW, 5), new Bc6HModeDescriptor(Bc6hEField.RW, 6), new Bc6HModeDescriptor(Bc6hEField.RW, 7), new Bc6HModeDescriptor(Bc6hEField.RW, 8), new Bc6HModeDescriptor(Bc6hEField.RW, 9), new Bc6HModeDescriptor(Bc6hEField.GW, 0), new Bc6HModeDescriptor(Bc6hEField.GW, 1), new Bc6HModeDescriptor(Bc6hEField.GW, 2), new Bc6HModeDescriptor(Bc6hEField.GW, 3), new Bc6HModeDescriptor(Bc6hEField.GW, 4), - new Bc6HModeDescriptor(Bc6hEField.GW, 5), new Bc6HModeDescriptor(Bc6hEField.GW, 6), new Bc6HModeDescriptor(Bc6hEField.GW, 7), new Bc6HModeDescriptor(Bc6hEField.GW, 8), new Bc6HModeDescriptor(Bc6hEField.GW, 9), new Bc6HModeDescriptor(Bc6hEField.BW, 0), new Bc6HModeDescriptor(Bc6hEField.BW, 1), new Bc6HModeDescriptor(Bc6hEField.BW, 2), new Bc6HModeDescriptor(Bc6hEField.BW, 3), new Bc6HModeDescriptor(Bc6hEField.BW, 4), - new Bc6HModeDescriptor(Bc6hEField.BW, 5), new Bc6HModeDescriptor(Bc6hEField.BW, 6), new Bc6HModeDescriptor(Bc6hEField.BW, 7), new Bc6HModeDescriptor(Bc6hEField.BW, 8), new Bc6HModeDescriptor(Bc6hEField.BW, 9), new Bc6HModeDescriptor(Bc6hEField.RX, 0), new Bc6HModeDescriptor(Bc6hEField.RX, 1), new Bc6HModeDescriptor(Bc6hEField.RX, 2), new Bc6HModeDescriptor(Bc6hEField.RX, 3), new Bc6HModeDescriptor(Bc6hEField.RX, 4), - new Bc6HModeDescriptor(Bc6hEField.GZ, 4), new Bc6HModeDescriptor(Bc6hEField.GY, 0), new Bc6HModeDescriptor(Bc6hEField.GY, 1), new Bc6HModeDescriptor(Bc6hEField.GY, 2), new Bc6HModeDescriptor(Bc6hEField.GY, 3), new Bc6HModeDescriptor(Bc6hEField.GX, 0), new Bc6HModeDescriptor(Bc6hEField.GX, 1), new Bc6HModeDescriptor(Bc6hEField.GX, 2), new Bc6HModeDescriptor(Bc6hEField.GX, 3), new Bc6HModeDescriptor(Bc6hEField.GX, 4), - new Bc6HModeDescriptor(Bc6hEField.BZ, 0), new Bc6HModeDescriptor(Bc6hEField.GZ, 0), new Bc6HModeDescriptor(Bc6hEField.GZ, 1), new Bc6HModeDescriptor(Bc6hEField.GZ, 2), new Bc6HModeDescriptor(Bc6hEField.GZ, 3), new Bc6HModeDescriptor(Bc6hEField.BX, 0), new Bc6HModeDescriptor(Bc6hEField.BX, 1), new Bc6HModeDescriptor(Bc6hEField.BX, 2), new Bc6HModeDescriptor(Bc6hEField.BX, 3), new Bc6HModeDescriptor(Bc6hEField.BX, 4), - new Bc6HModeDescriptor(Bc6hEField.BZ, 1), new Bc6HModeDescriptor(Bc6hEField.BY, 0), new Bc6HModeDescriptor(Bc6hEField.BY, 1), new Bc6HModeDescriptor(Bc6hEField.BY, 2), new Bc6HModeDescriptor(Bc6hEField.BY, 3), new Bc6HModeDescriptor(Bc6hEField.RY, 0), new Bc6HModeDescriptor(Bc6hEField.RY, 1), new Bc6HModeDescriptor(Bc6hEField.RY, 2), new Bc6HModeDescriptor(Bc6hEField.RY, 3), new Bc6HModeDescriptor(Bc6hEField.RY, 4), - new Bc6HModeDescriptor(Bc6hEField.BZ, 2), new Bc6HModeDescriptor(Bc6hEField.RZ, 0), new Bc6HModeDescriptor(Bc6hEField.RZ, 1), new Bc6HModeDescriptor(Bc6hEField.RZ, 2), new Bc6HModeDescriptor(Bc6hEField.RZ, 3), new Bc6HModeDescriptor(Bc6hEField.RZ, 4), new Bc6HModeDescriptor(Bc6hEField.BZ, 3), new Bc6HModeDescriptor(Bc6hEField.D, 0), new Bc6HModeDescriptor(Bc6hEField.D, 1), new Bc6HModeDescriptor(Bc6hEField.D, 2), - new Bc6HModeDescriptor(Bc6hEField.D, 3), new Bc6HModeDescriptor(Bc6hEField.D, 4) - }, - - new[] - { - // Mode 2 (0x01) - 7 6 6 6 - new Bc6HModeDescriptor(Bc6hEField.M, 0), new Bc6HModeDescriptor(Bc6hEField.M, 1), new Bc6HModeDescriptor(Bc6hEField.GY, 5), new Bc6HModeDescriptor(Bc6hEField.GZ, 4), new Bc6HModeDescriptor(Bc6hEField.GZ, 5), new Bc6HModeDescriptor(Bc6hEField.RW, 0), new Bc6HModeDescriptor(Bc6hEField.RW, 1), new Bc6HModeDescriptor(Bc6hEField.RW, 2), new Bc6HModeDescriptor(Bc6hEField.RW, 3), new Bc6HModeDescriptor(Bc6hEField.RW, 4), - new Bc6HModeDescriptor(Bc6hEField.RW, 5), new Bc6HModeDescriptor(Bc6hEField.RW, 6), new Bc6HModeDescriptor(Bc6hEField.BZ, 0), new Bc6HModeDescriptor(Bc6hEField.BZ, 1), new Bc6HModeDescriptor(Bc6hEField.BY, 4), new Bc6HModeDescriptor(Bc6hEField.GW, 0), new Bc6HModeDescriptor(Bc6hEField.GW, 1), new Bc6HModeDescriptor(Bc6hEField.GW, 2), new Bc6HModeDescriptor(Bc6hEField.GW, 3), new Bc6HModeDescriptor(Bc6hEField.GW, 4), - new Bc6HModeDescriptor(Bc6hEField.GW, 5), new Bc6HModeDescriptor(Bc6hEField.GW, 6), new Bc6HModeDescriptor(Bc6hEField.BY, 5), new Bc6HModeDescriptor(Bc6hEField.BZ, 2), new Bc6HModeDescriptor(Bc6hEField.GY, 4), new Bc6HModeDescriptor(Bc6hEField.BW, 0), new Bc6HModeDescriptor(Bc6hEField.BW, 1), new Bc6HModeDescriptor(Bc6hEField.BW, 2), new Bc6HModeDescriptor(Bc6hEField.BW, 3), new Bc6HModeDescriptor(Bc6hEField.BW, 4), - new Bc6HModeDescriptor(Bc6hEField.BW, 5), new Bc6HModeDescriptor(Bc6hEField.BW, 6), new Bc6HModeDescriptor(Bc6hEField.BZ, 3), new Bc6HModeDescriptor(Bc6hEField.BZ, 5), new Bc6HModeDescriptor(Bc6hEField.BZ, 4), new Bc6HModeDescriptor(Bc6hEField.RX, 0), new Bc6HModeDescriptor(Bc6hEField.RX, 1), new Bc6HModeDescriptor(Bc6hEField.RX, 2), new Bc6HModeDescriptor(Bc6hEField.RX, 3), new Bc6HModeDescriptor(Bc6hEField.RX, 4), - new Bc6HModeDescriptor(Bc6hEField.RX, 5), new Bc6HModeDescriptor(Bc6hEField.GY, 0), new Bc6HModeDescriptor(Bc6hEField.GY, 1), new Bc6HModeDescriptor(Bc6hEField.GY, 2), new Bc6HModeDescriptor(Bc6hEField.GY, 3), new Bc6HModeDescriptor(Bc6hEField.GX, 0), new Bc6HModeDescriptor(Bc6hEField.GX, 1), new Bc6HModeDescriptor(Bc6hEField.GX, 2), new Bc6HModeDescriptor(Bc6hEField.GX, 3), new Bc6HModeDescriptor(Bc6hEField.GX, 4), - new Bc6HModeDescriptor(Bc6hEField.GX, 5), new Bc6HModeDescriptor(Bc6hEField.GZ, 0), new Bc6HModeDescriptor(Bc6hEField.GZ, 1), new Bc6HModeDescriptor(Bc6hEField.GZ, 2), new Bc6HModeDescriptor(Bc6hEField.GZ, 3), new Bc6HModeDescriptor(Bc6hEField.BX, 0), new Bc6HModeDescriptor(Bc6hEField.BX, 1), new Bc6HModeDescriptor(Bc6hEField.BX, 2), new Bc6HModeDescriptor(Bc6hEField.BX, 3), new Bc6HModeDescriptor(Bc6hEField.BX, 4), - new Bc6HModeDescriptor(Bc6hEField.BX, 5), new Bc6HModeDescriptor(Bc6hEField.BY, 0), new Bc6HModeDescriptor(Bc6hEField.BY, 1), new Bc6HModeDescriptor(Bc6hEField.BY, 2), new Bc6HModeDescriptor(Bc6hEField.BY, 3), new Bc6HModeDescriptor(Bc6hEField.RY, 0), new Bc6HModeDescriptor(Bc6hEField.RY, 1), new Bc6HModeDescriptor(Bc6hEField.RY, 2), new Bc6HModeDescriptor(Bc6hEField.RY, 3), new Bc6HModeDescriptor(Bc6hEField.RY, 4), - new Bc6HModeDescriptor(Bc6hEField.RY, 5), new Bc6HModeDescriptor(Bc6hEField.RZ, 0), new Bc6HModeDescriptor(Bc6hEField.RZ, 1), new Bc6HModeDescriptor(Bc6hEField.RZ, 2), new Bc6HModeDescriptor(Bc6hEField.RZ, 3), new Bc6HModeDescriptor(Bc6hEField.RZ, 4), new Bc6HModeDescriptor(Bc6hEField.RZ, 5), new Bc6HModeDescriptor(Bc6hEField.D, 0), new Bc6HModeDescriptor(Bc6hEField.D, 1), new Bc6HModeDescriptor(Bc6hEField.D, 2), - new Bc6HModeDescriptor(Bc6hEField.D, 3), new Bc6HModeDescriptor(Bc6hEField.D, 4) - }, - - new[] - { - // Mode 3 (0x02) - 11 5 4 4 - new Bc6HModeDescriptor(Bc6hEField.M, 0), new Bc6HModeDescriptor(Bc6hEField.M, 1), new Bc6HModeDescriptor(Bc6hEField.M, 2), new Bc6HModeDescriptor(Bc6hEField.M, 3), new Bc6HModeDescriptor(Bc6hEField.M, 4), new Bc6HModeDescriptor(Bc6hEField.RW, 0), new Bc6HModeDescriptor(Bc6hEField.RW, 1), new Bc6HModeDescriptor(Bc6hEField.RW, 2), new Bc6HModeDescriptor(Bc6hEField.RW, 3), new Bc6HModeDescriptor(Bc6hEField.RW, 4), - new Bc6HModeDescriptor(Bc6hEField.RW, 5), new Bc6HModeDescriptor(Bc6hEField.RW, 6), new Bc6HModeDescriptor(Bc6hEField.RW, 7), new Bc6HModeDescriptor(Bc6hEField.RW, 8), new Bc6HModeDescriptor(Bc6hEField.RW, 9), new Bc6HModeDescriptor(Bc6hEField.GW, 0), new Bc6HModeDescriptor(Bc6hEField.GW, 1), new Bc6HModeDescriptor(Bc6hEField.GW, 2), new Bc6HModeDescriptor(Bc6hEField.GW, 3), new Bc6HModeDescriptor(Bc6hEField.GW, 4), - new Bc6HModeDescriptor(Bc6hEField.GW, 5), new Bc6HModeDescriptor(Bc6hEField.GW, 6), new Bc6HModeDescriptor(Bc6hEField.GW, 7), new Bc6HModeDescriptor(Bc6hEField.GW, 8), new Bc6HModeDescriptor(Bc6hEField.GW, 9), new Bc6HModeDescriptor(Bc6hEField.BW, 0), new Bc6HModeDescriptor(Bc6hEField.BW, 1), new Bc6HModeDescriptor(Bc6hEField.BW, 2), new Bc6HModeDescriptor(Bc6hEField.BW, 3), new Bc6HModeDescriptor(Bc6hEField.BW, 4), - new Bc6HModeDescriptor(Bc6hEField.BW, 5), new Bc6HModeDescriptor(Bc6hEField.BW, 6), new Bc6HModeDescriptor(Bc6hEField.BW, 7), new Bc6HModeDescriptor(Bc6hEField.BW, 8), new Bc6HModeDescriptor(Bc6hEField.BW, 9), new Bc6HModeDescriptor(Bc6hEField.RX, 0), new Bc6HModeDescriptor(Bc6hEField.RX, 1), new Bc6HModeDescriptor(Bc6hEField.RX, 2), new Bc6HModeDescriptor(Bc6hEField.RX, 3), new Bc6HModeDescriptor(Bc6hEField.RX, 4), - new Bc6HModeDescriptor(Bc6hEField.RW, 10), new Bc6HModeDescriptor(Bc6hEField.GY, 0), new Bc6HModeDescriptor(Bc6hEField.GY, 1), new Bc6HModeDescriptor(Bc6hEField.GY, 2), new Bc6HModeDescriptor(Bc6hEField.GY, 3), new Bc6HModeDescriptor(Bc6hEField.GX, 0), new Bc6HModeDescriptor(Bc6hEField.GX, 1), new Bc6HModeDescriptor(Bc6hEField.GX, 2), new Bc6HModeDescriptor(Bc6hEField.GX, 3), new Bc6HModeDescriptor(Bc6hEField.GW, 10), - new Bc6HModeDescriptor(Bc6hEField.BZ, 0), new Bc6HModeDescriptor(Bc6hEField.GZ, 0), new Bc6HModeDescriptor(Bc6hEField.GZ, 1), new Bc6HModeDescriptor(Bc6hEField.GZ, 2), new Bc6HModeDescriptor(Bc6hEField.GZ, 3), new Bc6HModeDescriptor(Bc6hEField.BX, 0), new Bc6HModeDescriptor(Bc6hEField.BX, 1), new Bc6HModeDescriptor(Bc6hEField.BX, 2), new Bc6HModeDescriptor(Bc6hEField.BX, 3), new Bc6HModeDescriptor(Bc6hEField.BW, 10), - new Bc6HModeDescriptor(Bc6hEField.BZ, 1), new Bc6HModeDescriptor(Bc6hEField.BY, 0), new Bc6HModeDescriptor(Bc6hEField.BY, 1), new Bc6HModeDescriptor(Bc6hEField.BY, 2), new Bc6HModeDescriptor(Bc6hEField.BY, 3), new Bc6HModeDescriptor(Bc6hEField.RY, 0), new Bc6HModeDescriptor(Bc6hEField.RY, 1), new Bc6HModeDescriptor(Bc6hEField.RY, 2), new Bc6HModeDescriptor(Bc6hEField.RY, 3), new Bc6HModeDescriptor(Bc6hEField.RY, 4), - new Bc6HModeDescriptor(Bc6hEField.BZ, 2), new Bc6HModeDescriptor(Bc6hEField.RZ, 0), new Bc6HModeDescriptor(Bc6hEField.RZ, 1), new Bc6HModeDescriptor(Bc6hEField.RZ, 2), new Bc6HModeDescriptor(Bc6hEField.RZ, 3), new Bc6HModeDescriptor(Bc6hEField.RZ, 4), new Bc6HModeDescriptor(Bc6hEField.BZ, 3), new Bc6HModeDescriptor(Bc6hEField.D, 0), new Bc6HModeDescriptor(Bc6hEField.D, 1), new Bc6HModeDescriptor(Bc6hEField.D, 2), - new Bc6HModeDescriptor(Bc6hEField.D, 3), new Bc6HModeDescriptor(Bc6hEField.D, 4) - }, - - new[] - { - // Mode 4 (0x06) - 11 4 5 4 - new Bc6HModeDescriptor(Bc6hEField.M, 0), new Bc6HModeDescriptor(Bc6hEField.M, 1), new Bc6HModeDescriptor(Bc6hEField.M, 2), new Bc6HModeDescriptor(Bc6hEField.M, 3), new Bc6HModeDescriptor(Bc6hEField.M, 4), new Bc6HModeDescriptor(Bc6hEField.RW, 0), new Bc6HModeDescriptor(Bc6hEField.RW, 1), new Bc6HModeDescriptor(Bc6hEField.RW, 2), new Bc6HModeDescriptor(Bc6hEField.RW, 3), new Bc6HModeDescriptor(Bc6hEField.RW, 4), - new Bc6HModeDescriptor(Bc6hEField.RW, 5), new Bc6HModeDescriptor(Bc6hEField.RW, 6), new Bc6HModeDescriptor(Bc6hEField.RW, 7), new Bc6HModeDescriptor(Bc6hEField.RW, 8), new Bc6HModeDescriptor(Bc6hEField.RW, 9), new Bc6HModeDescriptor(Bc6hEField.GW, 0), new Bc6HModeDescriptor(Bc6hEField.GW, 1), new Bc6HModeDescriptor(Bc6hEField.GW, 2), new Bc6HModeDescriptor(Bc6hEField.GW, 3), new Bc6HModeDescriptor(Bc6hEField.GW, 4), - new Bc6HModeDescriptor(Bc6hEField.GW, 5), new Bc6HModeDescriptor(Bc6hEField.GW, 6), new Bc6HModeDescriptor(Bc6hEField.GW, 7), new Bc6HModeDescriptor(Bc6hEField.GW, 8), new Bc6HModeDescriptor(Bc6hEField.GW, 9), new Bc6HModeDescriptor(Bc6hEField.BW, 0), new Bc6HModeDescriptor(Bc6hEField.BW, 1), new Bc6HModeDescriptor(Bc6hEField.BW, 2), new Bc6HModeDescriptor(Bc6hEField.BW, 3), new Bc6HModeDescriptor(Bc6hEField.BW, 4), - new Bc6HModeDescriptor(Bc6hEField.BW, 5), new Bc6HModeDescriptor(Bc6hEField.BW, 6), new Bc6HModeDescriptor(Bc6hEField.BW, 7), new Bc6HModeDescriptor(Bc6hEField.BW, 8), new Bc6HModeDescriptor(Bc6hEField.BW, 9), new Bc6HModeDescriptor(Bc6hEField.RX, 0), new Bc6HModeDescriptor(Bc6hEField.RX, 1), new Bc6HModeDescriptor(Bc6hEField.RX, 2), new Bc6HModeDescriptor(Bc6hEField.RX, 3), new Bc6HModeDescriptor(Bc6hEField.RW, 10), - new Bc6HModeDescriptor(Bc6hEField.GZ, 4), new Bc6HModeDescriptor(Bc6hEField.GY, 0), new Bc6HModeDescriptor(Bc6hEField.GY, 1), new Bc6HModeDescriptor(Bc6hEField.GY, 2), new Bc6HModeDescriptor(Bc6hEField.GY, 3), new Bc6HModeDescriptor(Bc6hEField.GX, 0), new Bc6HModeDescriptor(Bc6hEField.GX, 1), new Bc6HModeDescriptor(Bc6hEField.GX, 2), new Bc6HModeDescriptor(Bc6hEField.GX, 3), new Bc6HModeDescriptor(Bc6hEField.GX, 4), - new Bc6HModeDescriptor(Bc6hEField.GW, 10), new Bc6HModeDescriptor(Bc6hEField.GZ, 0), new Bc6HModeDescriptor(Bc6hEField.GZ, 1), new Bc6HModeDescriptor(Bc6hEField.GZ, 2), new Bc6HModeDescriptor(Bc6hEField.GZ, 3), new Bc6HModeDescriptor(Bc6hEField.BX, 0), new Bc6HModeDescriptor(Bc6hEField.BX, 1), new Bc6HModeDescriptor(Bc6hEField.BX, 2), new Bc6HModeDescriptor(Bc6hEField.BX, 3), new Bc6HModeDescriptor(Bc6hEField.BW, 10), - new Bc6HModeDescriptor(Bc6hEField.BZ, 1), new Bc6HModeDescriptor(Bc6hEField.BY, 0), new Bc6HModeDescriptor(Bc6hEField.BY, 1), new Bc6HModeDescriptor(Bc6hEField.BY, 2), new Bc6HModeDescriptor(Bc6hEField.BY, 3), new Bc6HModeDescriptor(Bc6hEField.RY, 0), new Bc6HModeDescriptor(Bc6hEField.RY, 1), new Bc6HModeDescriptor(Bc6hEField.RY, 2), new Bc6HModeDescriptor(Bc6hEField.RY, 3), new Bc6HModeDescriptor(Bc6hEField.BZ, 0), - new Bc6HModeDescriptor(Bc6hEField.BZ, 2), new Bc6HModeDescriptor(Bc6hEField.RZ, 0), new Bc6HModeDescriptor(Bc6hEField.RZ, 1), new Bc6HModeDescriptor(Bc6hEField.RZ, 2), new Bc6HModeDescriptor(Bc6hEField.RZ, 3), new Bc6HModeDescriptor(Bc6hEField.GY, 4), new Bc6HModeDescriptor(Bc6hEField.BZ, 3), new Bc6HModeDescriptor(Bc6hEField.D, 0), new Bc6HModeDescriptor(Bc6hEField.D, 1), new Bc6HModeDescriptor(Bc6hEField.D, 2), - new Bc6HModeDescriptor(Bc6hEField.D, 3), new Bc6HModeDescriptor(Bc6hEField.D, 4) - }, - - new[] - { - // Mode 5 (0x0a) - 11 4 4 5 - new Bc6HModeDescriptor(Bc6hEField.M, 0), new Bc6HModeDescriptor(Bc6hEField.M, 1), new Bc6HModeDescriptor(Bc6hEField.M, 2), new Bc6HModeDescriptor(Bc6hEField.M, 3), new Bc6HModeDescriptor(Bc6hEField.M, 4), new Bc6HModeDescriptor(Bc6hEField.RW, 0), new Bc6HModeDescriptor(Bc6hEField.RW, 1), new Bc6HModeDescriptor(Bc6hEField.RW, 2), new Bc6HModeDescriptor(Bc6hEField.RW, 3), new Bc6HModeDescriptor(Bc6hEField.RW, 4), - new Bc6HModeDescriptor(Bc6hEField.RW, 5), new Bc6HModeDescriptor(Bc6hEField.RW, 6), new Bc6HModeDescriptor(Bc6hEField.RW, 7), new Bc6HModeDescriptor(Bc6hEField.RW, 8), new Bc6HModeDescriptor(Bc6hEField.RW, 9), new Bc6HModeDescriptor(Bc6hEField.GW, 0), new Bc6HModeDescriptor(Bc6hEField.GW, 1), new Bc6HModeDescriptor(Bc6hEField.GW, 2), new Bc6HModeDescriptor(Bc6hEField.GW, 3), new Bc6HModeDescriptor(Bc6hEField.GW, 4), - new Bc6HModeDescriptor(Bc6hEField.GW, 5), new Bc6HModeDescriptor(Bc6hEField.GW, 6), new Bc6HModeDescriptor(Bc6hEField.GW, 7), new Bc6HModeDescriptor(Bc6hEField.GW, 8), new Bc6HModeDescriptor(Bc6hEField.GW, 9), new Bc6HModeDescriptor(Bc6hEField.BW, 0), new Bc6HModeDescriptor(Bc6hEField.BW, 1), new Bc6HModeDescriptor(Bc6hEField.BW, 2), new Bc6HModeDescriptor(Bc6hEField.BW, 3), new Bc6HModeDescriptor(Bc6hEField.BW, 4), - new Bc6HModeDescriptor(Bc6hEField.BW, 5), new Bc6HModeDescriptor(Bc6hEField.BW, 6), new Bc6HModeDescriptor(Bc6hEField.BW, 7), new Bc6HModeDescriptor(Bc6hEField.BW, 8), new Bc6HModeDescriptor(Bc6hEField.BW, 9), new Bc6HModeDescriptor(Bc6hEField.RX, 0), new Bc6HModeDescriptor(Bc6hEField.RX, 1), new Bc6HModeDescriptor(Bc6hEField.RX, 2), new Bc6HModeDescriptor(Bc6hEField.RX, 3), new Bc6HModeDescriptor(Bc6hEField.RW, 10), - new Bc6HModeDescriptor(Bc6hEField.BY, 4), new Bc6HModeDescriptor(Bc6hEField.GY, 0), new Bc6HModeDescriptor(Bc6hEField.GY, 1), new Bc6HModeDescriptor(Bc6hEField.GY, 2), new Bc6HModeDescriptor(Bc6hEField.GY, 3), new Bc6HModeDescriptor(Bc6hEField.GX, 0), new Bc6HModeDescriptor(Bc6hEField.GX, 1), new Bc6HModeDescriptor(Bc6hEField.GX, 2), new Bc6HModeDescriptor(Bc6hEField.GX, 3), new Bc6HModeDescriptor(Bc6hEField.GW, 10), - new Bc6HModeDescriptor(Bc6hEField.BZ, 0), new Bc6HModeDescriptor(Bc6hEField.GZ, 0), new Bc6HModeDescriptor(Bc6hEField.GZ, 1), new Bc6HModeDescriptor(Bc6hEField.GZ, 2), new Bc6HModeDescriptor(Bc6hEField.GZ, 3), new Bc6HModeDescriptor(Bc6hEField.BX, 0), new Bc6HModeDescriptor(Bc6hEField.BX, 1), new Bc6HModeDescriptor(Bc6hEField.BX, 2), new Bc6HModeDescriptor(Bc6hEField.BX, 3), new Bc6HModeDescriptor(Bc6hEField.BX, 4), - new Bc6HModeDescriptor(Bc6hEField.BW, 10), new Bc6HModeDescriptor(Bc6hEField.BY, 0), new Bc6HModeDescriptor(Bc6hEField.BY, 1), new Bc6HModeDescriptor(Bc6hEField.BY, 2), new Bc6HModeDescriptor(Bc6hEField.BY, 3), new Bc6HModeDescriptor(Bc6hEField.RY, 0), new Bc6HModeDescriptor(Bc6hEField.RY, 1), new Bc6HModeDescriptor(Bc6hEField.RY, 2), new Bc6HModeDescriptor(Bc6hEField.RY, 3), new Bc6HModeDescriptor(Bc6hEField.BZ, 1), - new Bc6HModeDescriptor(Bc6hEField.BZ, 2), new Bc6HModeDescriptor(Bc6hEField.RZ, 0), new Bc6HModeDescriptor(Bc6hEField.RZ, 1), new Bc6HModeDescriptor(Bc6hEField.RZ, 2), new Bc6HModeDescriptor(Bc6hEField.RZ, 3), new Bc6HModeDescriptor(Bc6hEField.BZ, 4), new Bc6HModeDescriptor(Bc6hEField.BZ, 3), new Bc6HModeDescriptor(Bc6hEField.D, 0), new Bc6HModeDescriptor(Bc6hEField.D, 1), new Bc6HModeDescriptor(Bc6hEField.D, 2), - new Bc6HModeDescriptor(Bc6hEField.D, 3), new Bc6HModeDescriptor(Bc6hEField.D, 4) - }, - - new[] - { - // Mode 6 (0x0e) - 9 5 5 5 - new Bc6HModeDescriptor(Bc6hEField.M, 0), new Bc6HModeDescriptor(Bc6hEField.M, 1), new Bc6HModeDescriptor(Bc6hEField.M, 2), new Bc6HModeDescriptor(Bc6hEField.M, 3), new Bc6HModeDescriptor(Bc6hEField.M, 4), new Bc6HModeDescriptor(Bc6hEField.RW, 0), new Bc6HModeDescriptor(Bc6hEField.RW, 1), new Bc6HModeDescriptor(Bc6hEField.RW, 2), new Bc6HModeDescriptor(Bc6hEField.RW, 3), new Bc6HModeDescriptor(Bc6hEField.RW, 4), - new Bc6HModeDescriptor(Bc6hEField.RW, 5), new Bc6HModeDescriptor(Bc6hEField.RW, 6), new Bc6HModeDescriptor(Bc6hEField.RW, 7), new Bc6HModeDescriptor(Bc6hEField.RW, 8), new Bc6HModeDescriptor(Bc6hEField.BY, 4), new Bc6HModeDescriptor(Bc6hEField.GW, 0), new Bc6HModeDescriptor(Bc6hEField.GW, 1), new Bc6HModeDescriptor(Bc6hEField.GW, 2), new Bc6HModeDescriptor(Bc6hEField.GW, 3), new Bc6HModeDescriptor(Bc6hEField.GW, 4), - new Bc6HModeDescriptor(Bc6hEField.GW, 5), new Bc6HModeDescriptor(Bc6hEField.GW, 6), new Bc6HModeDescriptor(Bc6hEField.GW, 7), new Bc6HModeDescriptor(Bc6hEField.GW, 8), new Bc6HModeDescriptor(Bc6hEField.GY, 4), new Bc6HModeDescriptor(Bc6hEField.BW, 0), new Bc6HModeDescriptor(Bc6hEField.BW, 1), new Bc6HModeDescriptor(Bc6hEField.BW, 2), new Bc6HModeDescriptor(Bc6hEField.BW, 3), new Bc6HModeDescriptor(Bc6hEField.BW, 4), - new Bc6HModeDescriptor(Bc6hEField.BW, 5), new Bc6HModeDescriptor(Bc6hEField.BW, 6), new Bc6HModeDescriptor(Bc6hEField.BW, 7), new Bc6HModeDescriptor(Bc6hEField.BW, 8), new Bc6HModeDescriptor(Bc6hEField.BZ, 4), new Bc6HModeDescriptor(Bc6hEField.RX, 0), new Bc6HModeDescriptor(Bc6hEField.RX, 1), new Bc6HModeDescriptor(Bc6hEField.RX, 2), new Bc6HModeDescriptor(Bc6hEField.RX, 3), new Bc6HModeDescriptor(Bc6hEField.RX, 4), - new Bc6HModeDescriptor(Bc6hEField.GZ, 4), new Bc6HModeDescriptor(Bc6hEField.GY, 0), new Bc6HModeDescriptor(Bc6hEField.GY, 1), new Bc6HModeDescriptor(Bc6hEField.GY, 2), new Bc6HModeDescriptor(Bc6hEField.GY, 3), new Bc6HModeDescriptor(Bc6hEField.GX, 0), new Bc6HModeDescriptor(Bc6hEField.GX, 1), new Bc6HModeDescriptor(Bc6hEField.GX, 2), new Bc6HModeDescriptor(Bc6hEField.GX, 3), new Bc6HModeDescriptor(Bc6hEField.GX, 4), - new Bc6HModeDescriptor(Bc6hEField.BZ, 0), new Bc6HModeDescriptor(Bc6hEField.GZ, 0), new Bc6HModeDescriptor(Bc6hEField.GZ, 1), new Bc6HModeDescriptor(Bc6hEField.GZ, 2), new Bc6HModeDescriptor(Bc6hEField.GZ, 3), new Bc6HModeDescriptor(Bc6hEField.BX, 0), new Bc6HModeDescriptor(Bc6hEField.BX, 1), new Bc6HModeDescriptor(Bc6hEField.BX, 2), new Bc6HModeDescriptor(Bc6hEField.BX, 3), new Bc6HModeDescriptor(Bc6hEField.BX, 4), - new Bc6HModeDescriptor(Bc6hEField.BZ, 1), new Bc6HModeDescriptor(Bc6hEField.BY, 0), new Bc6HModeDescriptor(Bc6hEField.BY, 1), new Bc6HModeDescriptor(Bc6hEField.BY, 2), new Bc6HModeDescriptor(Bc6hEField.BY, 3), new Bc6HModeDescriptor(Bc6hEField.RY, 0), new Bc6HModeDescriptor(Bc6hEField.RY, 1), new Bc6HModeDescriptor(Bc6hEField.RY, 2), new Bc6HModeDescriptor(Bc6hEField.RY, 3), new Bc6HModeDescriptor(Bc6hEField.RY, 4), - new Bc6HModeDescriptor(Bc6hEField.BZ, 2), new Bc6HModeDescriptor(Bc6hEField.RZ, 0), new Bc6HModeDescriptor(Bc6hEField.RZ, 1), new Bc6HModeDescriptor(Bc6hEField.RZ, 2), new Bc6HModeDescriptor(Bc6hEField.RZ, 3), new Bc6HModeDescriptor(Bc6hEField.RZ, 4), new Bc6HModeDescriptor(Bc6hEField.BZ, 3), new Bc6HModeDescriptor(Bc6hEField.D, 0), new Bc6HModeDescriptor(Bc6hEField.D, 1), new Bc6HModeDescriptor(Bc6hEField.D, 2), - new Bc6HModeDescriptor(Bc6hEField.D, 3), new Bc6HModeDescriptor(Bc6hEField.D, 4) - }, - - new[] - { - // Mode 7 (0x12) - 8 6 5 5 - new Bc6HModeDescriptor(Bc6hEField.M, 0), new Bc6HModeDescriptor(Bc6hEField.M, 1), new Bc6HModeDescriptor(Bc6hEField.M, 2), new Bc6HModeDescriptor(Bc6hEField.M, 3), new Bc6HModeDescriptor(Bc6hEField.M, 4), new Bc6HModeDescriptor(Bc6hEField.RW, 0), new Bc6HModeDescriptor(Bc6hEField.RW, 1), new Bc6HModeDescriptor(Bc6hEField.RW, 2), new Bc6HModeDescriptor(Bc6hEField.RW, 3), new Bc6HModeDescriptor(Bc6hEField.RW, 4), - new Bc6HModeDescriptor(Bc6hEField.RW, 5), new Bc6HModeDescriptor(Bc6hEField.RW, 6), new Bc6HModeDescriptor(Bc6hEField.RW, 7), new Bc6HModeDescriptor(Bc6hEField.GZ, 4), new Bc6HModeDescriptor(Bc6hEField.BY, 4), new Bc6HModeDescriptor(Bc6hEField.GW, 0), new Bc6HModeDescriptor(Bc6hEField.GW, 1), new Bc6HModeDescriptor(Bc6hEField.GW, 2), new Bc6HModeDescriptor(Bc6hEField.GW, 3), new Bc6HModeDescriptor(Bc6hEField.GW, 4), - new Bc6HModeDescriptor(Bc6hEField.GW, 5), new Bc6HModeDescriptor(Bc6hEField.GW, 6), new Bc6HModeDescriptor(Bc6hEField.GW, 7), new Bc6HModeDescriptor(Bc6hEField.BZ, 2), new Bc6HModeDescriptor(Bc6hEField.GY, 4), new Bc6HModeDescriptor(Bc6hEField.BW, 0), new Bc6HModeDescriptor(Bc6hEField.BW, 1), new Bc6HModeDescriptor(Bc6hEField.BW, 2), new Bc6HModeDescriptor(Bc6hEField.BW, 3), new Bc6HModeDescriptor(Bc6hEField.BW, 4), - new Bc6HModeDescriptor(Bc6hEField.BW, 5), new Bc6HModeDescriptor(Bc6hEField.BW, 6), new Bc6HModeDescriptor(Bc6hEField.BW, 7), new Bc6HModeDescriptor(Bc6hEField.BZ, 3), new Bc6HModeDescriptor(Bc6hEField.BZ, 4), new Bc6HModeDescriptor(Bc6hEField.RX, 0), new Bc6HModeDescriptor(Bc6hEField.RX, 1), new Bc6HModeDescriptor(Bc6hEField.RX, 2), new Bc6HModeDescriptor(Bc6hEField.RX, 3), new Bc6HModeDescriptor(Bc6hEField.RX, 4), - new Bc6HModeDescriptor(Bc6hEField.RX, 5), new Bc6HModeDescriptor(Bc6hEField.GY, 0), new Bc6HModeDescriptor(Bc6hEField.GY, 1), new Bc6HModeDescriptor(Bc6hEField.GY, 2), new Bc6HModeDescriptor(Bc6hEField.GY, 3), new Bc6HModeDescriptor(Bc6hEField.GX, 0), new Bc6HModeDescriptor(Bc6hEField.GX, 1), new Bc6HModeDescriptor(Bc6hEField.GX, 2), new Bc6HModeDescriptor(Bc6hEField.GX, 3), new Bc6HModeDescriptor(Bc6hEField.GX, 4), - new Bc6HModeDescriptor(Bc6hEField.BZ, 0), new Bc6HModeDescriptor(Bc6hEField.GZ, 0), new Bc6HModeDescriptor(Bc6hEField.GZ, 1), new Bc6HModeDescriptor(Bc6hEField.GZ, 2), new Bc6HModeDescriptor(Bc6hEField.GZ, 3), new Bc6HModeDescriptor(Bc6hEField.BX, 0), new Bc6HModeDescriptor(Bc6hEField.BX, 1), new Bc6HModeDescriptor(Bc6hEField.BX, 2), new Bc6HModeDescriptor(Bc6hEField.BX, 3), new Bc6HModeDescriptor(Bc6hEField.BX, 4), - new Bc6HModeDescriptor(Bc6hEField.BZ, 1), new Bc6HModeDescriptor(Bc6hEField.BY, 0), new Bc6HModeDescriptor(Bc6hEField.BY, 1), new Bc6HModeDescriptor(Bc6hEField.BY, 2), new Bc6HModeDescriptor(Bc6hEField.BY, 3), new Bc6HModeDescriptor(Bc6hEField.RY, 0), new Bc6HModeDescriptor(Bc6hEField.RY, 1), new Bc6HModeDescriptor(Bc6hEField.RY, 2), new Bc6HModeDescriptor(Bc6hEField.RY, 3), new Bc6HModeDescriptor(Bc6hEField.RY, 4), - new Bc6HModeDescriptor(Bc6hEField.RY, 5), new Bc6HModeDescriptor(Bc6hEField.RZ, 0), new Bc6HModeDescriptor(Bc6hEField.RZ, 1), new Bc6HModeDescriptor(Bc6hEField.RZ, 2), new Bc6HModeDescriptor(Bc6hEField.RZ, 3), new Bc6HModeDescriptor(Bc6hEField.RZ, 4), new Bc6HModeDescriptor(Bc6hEField.RZ, 5), new Bc6HModeDescriptor(Bc6hEField.D, 0), new Bc6HModeDescriptor(Bc6hEField.D, 1), new Bc6HModeDescriptor(Bc6hEField.D, 2), - new Bc6HModeDescriptor(Bc6hEField.D, 3), new Bc6HModeDescriptor(Bc6hEField.D, 4) - }, - - new[] + // I would prefer to use Span, but not sure if I should reference System.Memory in this project + // copy data instead + Buffer.BlockCopy(blockData, streamIndex, currentBlock, 0, currentBlock.Length); + streamIndex += currentBlock.Length; + + uint uStartBit = 0; + byte uMode = GetBits(currentBlock, ref uStartBit, 2u); + if (uMode is not 0x00 and not 0x01) { - // Mode 8 (0x16) - 8 5 6 5 - new Bc6HModeDescriptor(Bc6hEField.M, 0), new Bc6HModeDescriptor(Bc6hEField.M, 1), new Bc6HModeDescriptor(Bc6hEField.M, 2), new Bc6HModeDescriptor(Bc6hEField.M, 3), new Bc6HModeDescriptor(Bc6hEField.M, 4), new Bc6HModeDescriptor(Bc6hEField.RW, 0), new Bc6HModeDescriptor(Bc6hEField.RW, 1), new Bc6HModeDescriptor(Bc6hEField.RW, 2), new Bc6HModeDescriptor(Bc6hEField.RW, 3), new Bc6HModeDescriptor(Bc6hEField.RW, 4), - new Bc6HModeDescriptor(Bc6hEField.RW, 5), new Bc6HModeDescriptor(Bc6hEField.RW, 6), new Bc6HModeDescriptor(Bc6hEField.RW, 7), new Bc6HModeDescriptor(Bc6hEField.BZ, 0), new Bc6HModeDescriptor(Bc6hEField.BY, 4), new Bc6HModeDescriptor(Bc6hEField.GW, 0), new Bc6HModeDescriptor(Bc6hEField.GW, 1), new Bc6HModeDescriptor(Bc6hEField.GW, 2), new Bc6HModeDescriptor(Bc6hEField.GW, 3), new Bc6HModeDescriptor(Bc6hEField.GW, 4), - new Bc6HModeDescriptor(Bc6hEField.GW, 5), new Bc6HModeDescriptor(Bc6hEField.GW, 6), new Bc6HModeDescriptor(Bc6hEField.GW, 7), new Bc6HModeDescriptor(Bc6hEField.GY, 5), new Bc6HModeDescriptor(Bc6hEField.GY, 4), new Bc6HModeDescriptor(Bc6hEField.BW, 0), new Bc6HModeDescriptor(Bc6hEField.BW, 1), new Bc6HModeDescriptor(Bc6hEField.BW, 2), new Bc6HModeDescriptor(Bc6hEField.BW, 3), new Bc6HModeDescriptor(Bc6hEField.BW, 4), - new Bc6HModeDescriptor(Bc6hEField.BW, 5), new Bc6HModeDescriptor(Bc6hEField.BW, 6), new Bc6HModeDescriptor(Bc6hEField.BW, 7), new Bc6HModeDescriptor(Bc6hEField.GZ, 5), new Bc6HModeDescriptor(Bc6hEField.BZ, 4), new Bc6HModeDescriptor(Bc6hEField.RX, 0), new Bc6HModeDescriptor(Bc6hEField.RX, 1), new Bc6HModeDescriptor(Bc6hEField.RX, 2), new Bc6HModeDescriptor(Bc6hEField.RX, 3), new Bc6HModeDescriptor(Bc6hEField.RX, 4), - new Bc6HModeDescriptor(Bc6hEField.GZ, 4), new Bc6HModeDescriptor(Bc6hEField.GY, 0), new Bc6HModeDescriptor(Bc6hEField.GY, 1), new Bc6HModeDescriptor(Bc6hEField.GY, 2), new Bc6HModeDescriptor(Bc6hEField.GY, 3), new Bc6HModeDescriptor(Bc6hEField.GX, 0), new Bc6HModeDescriptor(Bc6hEField.GX, 1), new Bc6HModeDescriptor(Bc6hEField.GX, 2), new Bc6HModeDescriptor(Bc6hEField.GX, 3), new Bc6HModeDescriptor(Bc6hEField.GX, 4), - new Bc6HModeDescriptor(Bc6hEField.GX, 5), new Bc6HModeDescriptor(Bc6hEField.GZ, 0), new Bc6HModeDescriptor(Bc6hEField.GZ, 1), new Bc6HModeDescriptor(Bc6hEField.GZ, 2), new Bc6HModeDescriptor(Bc6hEField.GZ, 3), new Bc6HModeDescriptor(Bc6hEField.BX, 0), new Bc6HModeDescriptor(Bc6hEField.BX, 1), new Bc6HModeDescriptor(Bc6hEField.BX, 2), new Bc6HModeDescriptor(Bc6hEField.BX, 3), new Bc6HModeDescriptor(Bc6hEField.BX, 4), - new Bc6HModeDescriptor(Bc6hEField.BZ, 1), new Bc6HModeDescriptor(Bc6hEField.BY, 0), new Bc6HModeDescriptor(Bc6hEField.BY, 1), new Bc6HModeDescriptor(Bc6hEField.BY, 2), new Bc6HModeDescriptor(Bc6hEField.BY, 3), new Bc6HModeDescriptor(Bc6hEField.RY, 0), new Bc6HModeDescriptor(Bc6hEField.RY, 1), new Bc6HModeDescriptor(Bc6hEField.RY, 2), new Bc6HModeDescriptor(Bc6hEField.RY, 3), new Bc6HModeDescriptor(Bc6hEField.RY, 4), - new Bc6HModeDescriptor(Bc6hEField.BZ, 2), new Bc6HModeDescriptor(Bc6hEField.RZ, 0), new Bc6HModeDescriptor(Bc6hEField.RZ, 1), new Bc6HModeDescriptor(Bc6hEField.RZ, 2), new Bc6HModeDescriptor(Bc6hEField.RZ, 3), new Bc6HModeDescriptor(Bc6hEField.RZ, 4), new Bc6HModeDescriptor(Bc6hEField.BZ, 3), new Bc6HModeDescriptor(Bc6hEField.D, 0), new Bc6HModeDescriptor(Bc6hEField.D, 1), new Bc6HModeDescriptor(Bc6hEField.D, 2), - new Bc6HModeDescriptor(Bc6hEField.D, 3), new Bc6HModeDescriptor(Bc6hEField.D, 4) - }, - - new[] - { - // Mode 9 (0x1a) - 8 5 5 6 - new Bc6HModeDescriptor(Bc6hEField.M, 0), new Bc6HModeDescriptor(Bc6hEField.M, 1), new Bc6HModeDescriptor(Bc6hEField.M, 2), new Bc6HModeDescriptor(Bc6hEField.M, 3), new Bc6HModeDescriptor(Bc6hEField.M, 4), new Bc6HModeDescriptor(Bc6hEField.RW, 0), new Bc6HModeDescriptor(Bc6hEField.RW, 1), new Bc6HModeDescriptor(Bc6hEField.RW, 2), new Bc6HModeDescriptor(Bc6hEField.RW, 3), new Bc6HModeDescriptor(Bc6hEField.RW, 4), - new Bc6HModeDescriptor(Bc6hEField.RW, 5), new Bc6HModeDescriptor(Bc6hEField.RW, 6), new Bc6HModeDescriptor(Bc6hEField.RW, 7), new Bc6HModeDescriptor(Bc6hEField.BZ, 1), new Bc6HModeDescriptor(Bc6hEField.BY, 4), new Bc6HModeDescriptor(Bc6hEField.GW, 0), new Bc6HModeDescriptor(Bc6hEField.GW, 1), new Bc6HModeDescriptor(Bc6hEField.GW, 2), new Bc6HModeDescriptor(Bc6hEField.GW, 3), new Bc6HModeDescriptor(Bc6hEField.GW, 4), - new Bc6HModeDescriptor(Bc6hEField.GW, 5), new Bc6HModeDescriptor(Bc6hEField.GW, 6), new Bc6HModeDescriptor(Bc6hEField.GW, 7), new Bc6HModeDescriptor(Bc6hEField.BY, 5), new Bc6HModeDescriptor(Bc6hEField.GY, 4), new Bc6HModeDescriptor(Bc6hEField.BW, 0), new Bc6HModeDescriptor(Bc6hEField.BW, 1), new Bc6HModeDescriptor(Bc6hEField.BW, 2), new Bc6HModeDescriptor(Bc6hEField.BW, 3), new Bc6HModeDescriptor(Bc6hEField.BW, 4), - new Bc6HModeDescriptor(Bc6hEField.BW, 5), new Bc6HModeDescriptor(Bc6hEField.BW, 6), new Bc6HModeDescriptor(Bc6hEField.BW, 7), new Bc6HModeDescriptor(Bc6hEField.BZ, 5), new Bc6HModeDescriptor(Bc6hEField.BZ, 4), new Bc6HModeDescriptor(Bc6hEField.RX, 0), new Bc6HModeDescriptor(Bc6hEField.RX, 1), new Bc6HModeDescriptor(Bc6hEField.RX, 2), new Bc6HModeDescriptor(Bc6hEField.RX, 3), new Bc6HModeDescriptor(Bc6hEField.RX, 4), - new Bc6HModeDescriptor(Bc6hEField.GZ, 4), new Bc6HModeDescriptor(Bc6hEField.GY, 0), new Bc6HModeDescriptor(Bc6hEField.GY, 1), new Bc6HModeDescriptor(Bc6hEField.GY, 2), new Bc6HModeDescriptor(Bc6hEField.GY, 3), new Bc6HModeDescriptor(Bc6hEField.GX, 0), new Bc6HModeDescriptor(Bc6hEField.GX, 1), new Bc6HModeDescriptor(Bc6hEField.GX, 2), new Bc6HModeDescriptor(Bc6hEField.GX, 3), new Bc6HModeDescriptor(Bc6hEField.GX, 4), - new Bc6HModeDescriptor(Bc6hEField.BZ, 0), new Bc6HModeDescriptor(Bc6hEField.GZ, 0), new Bc6HModeDescriptor(Bc6hEField.GZ, 1), new Bc6HModeDescriptor(Bc6hEField.GZ, 2), new Bc6HModeDescriptor(Bc6hEField.GZ, 3), new Bc6HModeDescriptor(Bc6hEField.BX, 0), new Bc6HModeDescriptor(Bc6hEField.BX, 1), new Bc6HModeDescriptor(Bc6hEField.BX, 2), new Bc6HModeDescriptor(Bc6hEField.BX, 3), new Bc6HModeDescriptor(Bc6hEField.BX, 4), - new Bc6HModeDescriptor(Bc6hEField.BX, 5), new Bc6HModeDescriptor(Bc6hEField.BY, 0), new Bc6HModeDescriptor(Bc6hEField.BY, 1), new Bc6HModeDescriptor(Bc6hEField.BY, 2), new Bc6HModeDescriptor(Bc6hEField.BY, 3), new Bc6HModeDescriptor(Bc6hEField.RY, 0), new Bc6HModeDescriptor(Bc6hEField.RY, 1), new Bc6HModeDescriptor(Bc6hEField.RY, 2), new Bc6HModeDescriptor(Bc6hEField.RY, 3), new Bc6HModeDescriptor(Bc6hEField.RY, 4), - new Bc6HModeDescriptor(Bc6hEField.BZ, 2), new Bc6HModeDescriptor(Bc6hEField.RZ, 0), new Bc6HModeDescriptor(Bc6hEField.RZ, 1), new Bc6HModeDescriptor(Bc6hEField.RZ, 2), new Bc6HModeDescriptor(Bc6hEField.RZ, 3), new Bc6HModeDescriptor(Bc6hEField.RZ, 4), new Bc6HModeDescriptor(Bc6hEField.BZ, 3), new Bc6HModeDescriptor(Bc6hEField.D, 0), new Bc6HModeDescriptor(Bc6hEField.D, 1), new Bc6HModeDescriptor(Bc6hEField.D, 2), - new Bc6HModeDescriptor(Bc6hEField.D, 3), new Bc6HModeDescriptor(Bc6hEField.D, 4) - }, - - new[] - { - // Mode 10 (0x1e) - 6 6 6 6 - new Bc6HModeDescriptor(Bc6hEField.M, 0), new Bc6HModeDescriptor(Bc6hEField.M, 1), new Bc6HModeDescriptor(Bc6hEField.M, 2), new Bc6HModeDescriptor(Bc6hEField.M, 3), new Bc6HModeDescriptor(Bc6hEField.M, 4), new Bc6HModeDescriptor(Bc6hEField.RW, 0), new Bc6HModeDescriptor(Bc6hEField.RW, 1), new Bc6HModeDescriptor(Bc6hEField.RW, 2), new Bc6HModeDescriptor(Bc6hEField.RW, 3), new Bc6HModeDescriptor(Bc6hEField.RW, 4), - new Bc6HModeDescriptor(Bc6hEField.RW, 5), new Bc6HModeDescriptor(Bc6hEField.GZ, 4), new Bc6HModeDescriptor(Bc6hEField.BZ, 0), new Bc6HModeDescriptor(Bc6hEField.BZ, 1), new Bc6HModeDescriptor(Bc6hEField.BY, 4), new Bc6HModeDescriptor(Bc6hEField.GW, 0), new Bc6HModeDescriptor(Bc6hEField.GW, 1), new Bc6HModeDescriptor(Bc6hEField.GW, 2), new Bc6HModeDescriptor(Bc6hEField.GW, 3), new Bc6HModeDescriptor(Bc6hEField.GW, 4), - new Bc6HModeDescriptor(Bc6hEField.GW, 5), new Bc6HModeDescriptor(Bc6hEField.GY, 5), new Bc6HModeDescriptor(Bc6hEField.BY, 5), new Bc6HModeDescriptor(Bc6hEField.BZ, 2), new Bc6HModeDescriptor(Bc6hEField.GY, 4), new Bc6HModeDescriptor(Bc6hEField.BW, 0), new Bc6HModeDescriptor(Bc6hEField.BW, 1), new Bc6HModeDescriptor(Bc6hEField.BW, 2), new Bc6HModeDescriptor(Bc6hEField.BW, 3), new Bc6HModeDescriptor(Bc6hEField.BW, 4), - new Bc6HModeDescriptor(Bc6hEField.BW, 5), new Bc6HModeDescriptor(Bc6hEField.GZ, 5), new Bc6HModeDescriptor(Bc6hEField.BZ, 3), new Bc6HModeDescriptor(Bc6hEField.BZ, 5), new Bc6HModeDescriptor(Bc6hEField.BZ, 4), new Bc6HModeDescriptor(Bc6hEField.RX, 0), new Bc6HModeDescriptor(Bc6hEField.RX, 1), new Bc6HModeDescriptor(Bc6hEField.RX, 2), new Bc6HModeDescriptor(Bc6hEField.RX, 3), new Bc6HModeDescriptor(Bc6hEField.RX, 4), - new Bc6HModeDescriptor(Bc6hEField.RX, 5), new Bc6HModeDescriptor(Bc6hEField.GY, 0), new Bc6HModeDescriptor(Bc6hEField.GY, 1), new Bc6HModeDescriptor(Bc6hEField.GY, 2), new Bc6HModeDescriptor(Bc6hEField.GY, 3), new Bc6HModeDescriptor(Bc6hEField.GX, 0), new Bc6HModeDescriptor(Bc6hEField.GX, 1), new Bc6HModeDescriptor(Bc6hEField.GX, 2), new Bc6HModeDescriptor(Bc6hEField.GX, 3), new Bc6HModeDescriptor(Bc6hEField.GX, 4), - new Bc6HModeDescriptor(Bc6hEField.GX, 5), new Bc6HModeDescriptor(Bc6hEField.GZ, 0), new Bc6HModeDescriptor(Bc6hEField.GZ, 1), new Bc6HModeDescriptor(Bc6hEField.GZ, 2), new Bc6HModeDescriptor(Bc6hEField.GZ, 3), new Bc6HModeDescriptor(Bc6hEField.BX, 0), new Bc6HModeDescriptor(Bc6hEField.BX, 1), new Bc6HModeDescriptor(Bc6hEField.BX, 2), new Bc6HModeDescriptor(Bc6hEField.BX, 3), new Bc6HModeDescriptor(Bc6hEField.BX, 4), - new Bc6HModeDescriptor(Bc6hEField.BX, 5), new Bc6HModeDescriptor(Bc6hEField.BY, 0), new Bc6HModeDescriptor(Bc6hEField.BY, 1), new Bc6HModeDescriptor(Bc6hEField.BY, 2), new Bc6HModeDescriptor(Bc6hEField.BY, 3), new Bc6HModeDescriptor(Bc6hEField.RY, 0), new Bc6HModeDescriptor(Bc6hEField.RY, 1), new Bc6HModeDescriptor(Bc6hEField.RY, 2), new Bc6HModeDescriptor(Bc6hEField.RY, 3), new Bc6HModeDescriptor(Bc6hEField.RY, 4), - new Bc6HModeDescriptor(Bc6hEField.RY, 5), new Bc6HModeDescriptor(Bc6hEField.RZ, 0), new Bc6HModeDescriptor(Bc6hEField.RZ, 1), new Bc6HModeDescriptor(Bc6hEField.RZ, 2), new Bc6HModeDescriptor(Bc6hEField.RZ, 3), new Bc6HModeDescriptor(Bc6hEField.RZ, 4), new Bc6HModeDescriptor(Bc6hEField.RZ, 5), new Bc6HModeDescriptor(Bc6hEField.D, 0), new Bc6HModeDescriptor(Bc6hEField.D, 1), new Bc6HModeDescriptor(Bc6hEField.D, 2), - new Bc6HModeDescriptor(Bc6hEField.D, 3), new Bc6HModeDescriptor(Bc6hEField.D, 4) - }, - - new[] - { - // Mode 11 (0x03) - 10 10 - new Bc6HModeDescriptor(Bc6hEField.M, 0), new Bc6HModeDescriptor(Bc6hEField.M, 1), new Bc6HModeDescriptor(Bc6hEField.M, 2), new Bc6HModeDescriptor(Bc6hEField.M, 3), new Bc6HModeDescriptor(Bc6hEField.M, 4), new Bc6HModeDescriptor(Bc6hEField.RW, 0), new Bc6HModeDescriptor(Bc6hEField.RW, 1), new Bc6HModeDescriptor(Bc6hEField.RW, 2), new Bc6HModeDescriptor(Bc6hEField.RW, 3), new Bc6HModeDescriptor(Bc6hEField.RW, 4), - new Bc6HModeDescriptor(Bc6hEField.RW, 5), new Bc6HModeDescriptor(Bc6hEField.RW, 6), new Bc6HModeDescriptor(Bc6hEField.RW, 7), new Bc6HModeDescriptor(Bc6hEField.RW, 8), new Bc6HModeDescriptor(Bc6hEField.RW, 9), new Bc6HModeDescriptor(Bc6hEField.GW, 0), new Bc6HModeDescriptor(Bc6hEField.GW, 1), new Bc6HModeDescriptor(Bc6hEField.GW, 2), new Bc6HModeDescriptor(Bc6hEField.GW, 3), new Bc6HModeDescriptor(Bc6hEField.GW, 4), - new Bc6HModeDescriptor(Bc6hEField.GW, 5), new Bc6HModeDescriptor(Bc6hEField.GW, 6), new Bc6HModeDescriptor(Bc6hEField.GW, 7), new Bc6HModeDescriptor(Bc6hEField.GW, 8), new Bc6HModeDescriptor(Bc6hEField.GW, 9), new Bc6HModeDescriptor(Bc6hEField.BW, 0), new Bc6HModeDescriptor(Bc6hEField.BW, 1), new Bc6HModeDescriptor(Bc6hEField.BW, 2), new Bc6HModeDescriptor(Bc6hEField.BW, 3), new Bc6HModeDescriptor(Bc6hEField.BW, 4), - new Bc6HModeDescriptor(Bc6hEField.BW, 5), new Bc6HModeDescriptor(Bc6hEField.BW, 6), new Bc6HModeDescriptor(Bc6hEField.BW, 7), new Bc6HModeDescriptor(Bc6hEField.BW, 8), new Bc6HModeDescriptor(Bc6hEField.BW, 9), new Bc6HModeDescriptor(Bc6hEField.RX, 0), new Bc6HModeDescriptor(Bc6hEField.RX, 1), new Bc6HModeDescriptor(Bc6hEField.RX, 2), new Bc6HModeDescriptor(Bc6hEField.RX, 3), new Bc6HModeDescriptor(Bc6hEField.RX, 4), - new Bc6HModeDescriptor(Bc6hEField.RX, 5), new Bc6HModeDescriptor(Bc6hEField.RX, 6), new Bc6HModeDescriptor(Bc6hEField.RX, 7), new Bc6HModeDescriptor(Bc6hEField.RX, 8), new Bc6HModeDescriptor(Bc6hEField.RX, 9), new Bc6HModeDescriptor(Bc6hEField.GX, 0), new Bc6HModeDescriptor(Bc6hEField.GX, 1), new Bc6HModeDescriptor(Bc6hEField.GX, 2), new Bc6HModeDescriptor(Bc6hEField.GX, 3), new Bc6HModeDescriptor(Bc6hEField.GX, 4), - new Bc6HModeDescriptor(Bc6hEField.GX, 5), new Bc6HModeDescriptor(Bc6hEField.GX, 6), new Bc6HModeDescriptor(Bc6hEField.GX, 7), new Bc6HModeDescriptor(Bc6hEField.GX, 8), new Bc6HModeDescriptor(Bc6hEField.GX, 9), new Bc6HModeDescriptor(Bc6hEField.BX, 0), new Bc6HModeDescriptor(Bc6hEField.BX, 1), new Bc6HModeDescriptor(Bc6hEField.BX, 2), new Bc6HModeDescriptor(Bc6hEField.BX, 3), new Bc6HModeDescriptor(Bc6hEField.BX, 4), - new Bc6HModeDescriptor(Bc6hEField.BX, 5), new Bc6HModeDescriptor(Bc6hEField.BX, 6), new Bc6HModeDescriptor(Bc6hEField.BX, 7), new Bc6HModeDescriptor(Bc6hEField.BX, 8), new Bc6HModeDescriptor(Bc6hEField.BX, 9), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), - new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), - new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0) - }, - - new[] - { - // Mode 12 (0x07) - 11 9 - new Bc6HModeDescriptor(Bc6hEField.M, 0), new Bc6HModeDescriptor(Bc6hEField.M, 1), new Bc6HModeDescriptor(Bc6hEField.M, 2), new Bc6HModeDescriptor(Bc6hEField.M, 3), new Bc6HModeDescriptor(Bc6hEField.M, 4), new Bc6HModeDescriptor(Bc6hEField.RW, 0), new Bc6HModeDescriptor(Bc6hEField.RW, 1), new Bc6HModeDescriptor(Bc6hEField.RW, 2), new Bc6HModeDescriptor(Bc6hEField.RW, 3), new Bc6HModeDescriptor(Bc6hEField.RW, 4), - new Bc6HModeDescriptor(Bc6hEField.RW, 5), new Bc6HModeDescriptor(Bc6hEField.RW, 6), new Bc6HModeDescriptor(Bc6hEField.RW, 7), new Bc6HModeDescriptor(Bc6hEField.RW, 8), new Bc6HModeDescriptor(Bc6hEField.RW, 9), new Bc6HModeDescriptor(Bc6hEField.GW, 0), new Bc6HModeDescriptor(Bc6hEField.GW, 1), new Bc6HModeDescriptor(Bc6hEField.GW, 2), new Bc6HModeDescriptor(Bc6hEField.GW, 3), new Bc6HModeDescriptor(Bc6hEField.GW, 4), - new Bc6HModeDescriptor(Bc6hEField.GW, 5), new Bc6HModeDescriptor(Bc6hEField.GW, 6), new Bc6HModeDescriptor(Bc6hEField.GW, 7), new Bc6HModeDescriptor(Bc6hEField.GW, 8), new Bc6HModeDescriptor(Bc6hEField.GW, 9), new Bc6HModeDescriptor(Bc6hEField.BW, 0), new Bc6HModeDescriptor(Bc6hEField.BW, 1), new Bc6HModeDescriptor(Bc6hEField.BW, 2), new Bc6HModeDescriptor(Bc6hEField.BW, 3), new Bc6HModeDescriptor(Bc6hEField.BW, 4), - new Bc6HModeDescriptor(Bc6hEField.BW, 5), new Bc6HModeDescriptor(Bc6hEField.BW, 6), new Bc6HModeDescriptor(Bc6hEField.BW, 7), new Bc6HModeDescriptor(Bc6hEField.BW, 8), new Bc6HModeDescriptor(Bc6hEField.BW, 9), new Bc6HModeDescriptor(Bc6hEField.RX, 0), new Bc6HModeDescriptor(Bc6hEField.RX, 1), new Bc6HModeDescriptor(Bc6hEField.RX, 2), new Bc6HModeDescriptor(Bc6hEField.RX, 3), new Bc6HModeDescriptor(Bc6hEField.RX, 4), - new Bc6HModeDescriptor(Bc6hEField.RX, 5), new Bc6HModeDescriptor(Bc6hEField.RX, 6), new Bc6HModeDescriptor(Bc6hEField.RX, 7), new Bc6HModeDescriptor(Bc6hEField.RX, 8), new Bc6HModeDescriptor(Bc6hEField.RW, 10), new Bc6HModeDescriptor(Bc6hEField.GX, 0), new Bc6HModeDescriptor(Bc6hEField.GX, 1), new Bc6HModeDescriptor(Bc6hEField.GX, 2), new Bc6HModeDescriptor(Bc6hEField.GX, 3), new Bc6HModeDescriptor(Bc6hEField.GX, 4), - new Bc6HModeDescriptor(Bc6hEField.GX, 5), new Bc6HModeDescriptor(Bc6hEField.GX, 6), new Bc6HModeDescriptor(Bc6hEField.GX, 7), new Bc6HModeDescriptor(Bc6hEField.GX, 8), new Bc6HModeDescriptor(Bc6hEField.GW, 10), new Bc6HModeDescriptor(Bc6hEField.BX, 0), new Bc6HModeDescriptor(Bc6hEField.BX, 1), new Bc6HModeDescriptor(Bc6hEField.BX, 2), new Bc6HModeDescriptor(Bc6hEField.BX, 3), new Bc6HModeDescriptor(Bc6hEField.BX, 4), - new Bc6HModeDescriptor(Bc6hEField.BX, 5), new Bc6HModeDescriptor(Bc6hEField.BX, 6), new Bc6HModeDescriptor(Bc6hEField.BX, 7), new Bc6HModeDescriptor(Bc6hEField.BX, 8), new Bc6HModeDescriptor(Bc6hEField.BW, 10), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), - new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), - new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0) - }, - - new[] - { - // Mode 13 (0x0b) - 12 8 - new Bc6HModeDescriptor(Bc6hEField.M, 0), new Bc6HModeDescriptor(Bc6hEField.M, 1), new Bc6HModeDescriptor(Bc6hEField.M, 2), new Bc6HModeDescriptor(Bc6hEField.M, 3), new Bc6HModeDescriptor(Bc6hEField.M, 4), new Bc6HModeDescriptor(Bc6hEField.RW, 0), new Bc6HModeDescriptor(Bc6hEField.RW, 1), new Bc6HModeDescriptor(Bc6hEField.RW, 2), new Bc6HModeDescriptor(Bc6hEField.RW, 3), new Bc6HModeDescriptor(Bc6hEField.RW, 4), - new Bc6HModeDescriptor(Bc6hEField.RW, 5), new Bc6HModeDescriptor(Bc6hEField.RW, 6), new Bc6HModeDescriptor(Bc6hEField.RW, 7), new Bc6HModeDescriptor(Bc6hEField.RW, 8), new Bc6HModeDescriptor(Bc6hEField.RW, 9), new Bc6HModeDescriptor(Bc6hEField.GW, 0), new Bc6HModeDescriptor(Bc6hEField.GW, 1), new Bc6HModeDescriptor(Bc6hEField.GW, 2), new Bc6HModeDescriptor(Bc6hEField.GW, 3), new Bc6HModeDescriptor(Bc6hEField.GW, 4), - new Bc6HModeDescriptor(Bc6hEField.GW, 5), new Bc6HModeDescriptor(Bc6hEField.GW, 6), new Bc6HModeDescriptor(Bc6hEField.GW, 7), new Bc6HModeDescriptor(Bc6hEField.GW, 8), new Bc6HModeDescriptor(Bc6hEField.GW, 9), new Bc6HModeDescriptor(Bc6hEField.BW, 0), new Bc6HModeDescriptor(Bc6hEField.BW, 1), new Bc6HModeDescriptor(Bc6hEField.BW, 2), new Bc6HModeDescriptor(Bc6hEField.BW, 3), new Bc6HModeDescriptor(Bc6hEField.BW, 4), - new Bc6HModeDescriptor(Bc6hEField.BW, 5), new Bc6HModeDescriptor(Bc6hEField.BW, 6), new Bc6HModeDescriptor(Bc6hEField.BW, 7), new Bc6HModeDescriptor(Bc6hEField.BW, 8), new Bc6HModeDescriptor(Bc6hEField.BW, 9), new Bc6HModeDescriptor(Bc6hEField.RX, 0), new Bc6HModeDescriptor(Bc6hEField.RX, 1), new Bc6HModeDescriptor(Bc6hEField.RX, 2), new Bc6HModeDescriptor(Bc6hEField.RX, 3), new Bc6HModeDescriptor(Bc6hEField.RX, 4), - new Bc6HModeDescriptor(Bc6hEField.RX, 5), new Bc6HModeDescriptor(Bc6hEField.RX, 6), new Bc6HModeDescriptor(Bc6hEField.RX, 7), new Bc6HModeDescriptor(Bc6hEField.RW, 11), new Bc6HModeDescriptor(Bc6hEField.RW, 10), new Bc6HModeDescriptor(Bc6hEField.GX, 0), new Bc6HModeDescriptor(Bc6hEField.GX, 1), new Bc6HModeDescriptor(Bc6hEField.GX, 2), new Bc6HModeDescriptor(Bc6hEField.GX, 3), new Bc6HModeDescriptor(Bc6hEField.GX, 4), - new Bc6HModeDescriptor(Bc6hEField.GX, 5), new Bc6HModeDescriptor(Bc6hEField.GX, 6), new Bc6HModeDescriptor(Bc6hEField.GX, 7), new Bc6HModeDescriptor(Bc6hEField.GW, 11), new Bc6HModeDescriptor(Bc6hEField.GW, 10), new Bc6HModeDescriptor(Bc6hEField.BX, 0), new Bc6HModeDescriptor(Bc6hEField.BX, 1), new Bc6HModeDescriptor(Bc6hEField.BX, 2), new Bc6HModeDescriptor(Bc6hEField.BX, 3), new Bc6HModeDescriptor(Bc6hEField.BX, 4), - new Bc6HModeDescriptor(Bc6hEField.BX, 5), new Bc6HModeDescriptor(Bc6hEField.BX, 6), new Bc6HModeDescriptor(Bc6hEField.BX, 7), new Bc6HModeDescriptor(Bc6hEField.BW, 11), new Bc6HModeDescriptor(Bc6hEField.BW, 10), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), - new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), - new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0) - }, - - new[] - { - // Mode 14 (0x0f) - 16 4 - new Bc6HModeDescriptor(Bc6hEField.M, 0), new Bc6HModeDescriptor(Bc6hEField.M, 1), new Bc6HModeDescriptor(Bc6hEField.M, 2), new Bc6HModeDescriptor(Bc6hEField.M, 3), new Bc6HModeDescriptor(Bc6hEField.M, 4), new Bc6HModeDescriptor(Bc6hEField.RW, 0), new Bc6HModeDescriptor(Bc6hEField.RW, 1), new Bc6HModeDescriptor(Bc6hEField.RW, 2), new Bc6HModeDescriptor(Bc6hEField.RW, 3), new Bc6HModeDescriptor(Bc6hEField.RW, 4), - new Bc6HModeDescriptor(Bc6hEField.RW, 5), new Bc6HModeDescriptor(Bc6hEField.RW, 6), new Bc6HModeDescriptor(Bc6hEField.RW, 7), new Bc6HModeDescriptor(Bc6hEField.RW, 8), new Bc6HModeDescriptor(Bc6hEField.RW, 9), new Bc6HModeDescriptor(Bc6hEField.GW, 0), new Bc6HModeDescriptor(Bc6hEField.GW, 1), new Bc6HModeDescriptor(Bc6hEField.GW, 2), new Bc6HModeDescriptor(Bc6hEField.GW, 3), new Bc6HModeDescriptor(Bc6hEField.GW, 4), - new Bc6HModeDescriptor(Bc6hEField.GW, 5), new Bc6HModeDescriptor(Bc6hEField.GW, 6), new Bc6HModeDescriptor(Bc6hEField.GW, 7), new Bc6HModeDescriptor(Bc6hEField.GW, 8), new Bc6HModeDescriptor(Bc6hEField.GW, 9), new Bc6HModeDescriptor(Bc6hEField.BW, 0), new Bc6HModeDescriptor(Bc6hEField.BW, 1), new Bc6HModeDescriptor(Bc6hEField.BW, 2), new Bc6HModeDescriptor(Bc6hEField.BW, 3), new Bc6HModeDescriptor(Bc6hEField.BW, 4), - new Bc6HModeDescriptor(Bc6hEField.BW, 5), new Bc6HModeDescriptor(Bc6hEField.BW, 6), new Bc6HModeDescriptor(Bc6hEField.BW, 7), new Bc6HModeDescriptor(Bc6hEField.BW, 8), new Bc6HModeDescriptor(Bc6hEField.BW, 9), new Bc6HModeDescriptor(Bc6hEField.RX, 0), new Bc6HModeDescriptor(Bc6hEField.RX, 1), new Bc6HModeDescriptor(Bc6hEField.RX, 2), new Bc6HModeDescriptor(Bc6hEField.RX, 3), new Bc6HModeDescriptor(Bc6hEField.RW, 15), - new Bc6HModeDescriptor(Bc6hEField.RW, 14), new Bc6HModeDescriptor(Bc6hEField.RW, 13), new Bc6HModeDescriptor(Bc6hEField.RW, 12), new Bc6HModeDescriptor(Bc6hEField.RW, 11), new Bc6HModeDescriptor(Bc6hEField.RW, 10), new Bc6HModeDescriptor(Bc6hEField.GX, 0), new Bc6HModeDescriptor(Bc6hEField.GX, 1), new Bc6HModeDescriptor(Bc6hEField.GX, 2), new Bc6HModeDescriptor(Bc6hEField.GX, 3), new Bc6HModeDescriptor(Bc6hEField.GW, 15), - new Bc6HModeDescriptor(Bc6hEField.GW, 14), new Bc6HModeDescriptor(Bc6hEField.GW, 13), new Bc6HModeDescriptor(Bc6hEField.GW, 12), new Bc6HModeDescriptor(Bc6hEField.GW, 11), new Bc6HModeDescriptor(Bc6hEField.GW, 10), new Bc6HModeDescriptor(Bc6hEField.BX, 0), new Bc6HModeDescriptor(Bc6hEField.BX, 1), new Bc6HModeDescriptor(Bc6hEField.BX, 2), new Bc6HModeDescriptor(Bc6hEField.BX, 3), new Bc6HModeDescriptor(Bc6hEField.BW, 15), - new Bc6HModeDescriptor(Bc6hEField.BW, 14), new Bc6HModeDescriptor(Bc6hEField.BW, 13), new Bc6HModeDescriptor(Bc6hEField.BW, 12), new Bc6HModeDescriptor(Bc6hEField.BW, 11), new Bc6HModeDescriptor(Bc6hEField.BW, 10), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), - new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0), - new Bc6HModeDescriptor(Bc6hEField.NA, 0), new Bc6HModeDescriptor(Bc6hEField.NA, 0) - }, - }; - - private static readonly Bc6hModeInfo[] ModeInfo = - { - new Bc6hModeInfo(0x00, 1, true, 3, new[] { new[] { new LdrColorA(10, 10, 10, 0), new LdrColorA(5, 5, 5, 0) }, new[] { new LdrColorA(5, 5, 5, 0), new LdrColorA(5, 5, 5, 0) } }), // Mode 1 - new Bc6hModeInfo(0x01, 1, true, 3, new[] { new[] { new LdrColorA(7, 7, 7, 0), new LdrColorA(6, 6, 6, 0) }, new[] { new LdrColorA(6, 6, 6, 0), new LdrColorA(6, 6, 6, 0) } }), // Mode 2 - new Bc6hModeInfo(0x02, 1, true, 3, new[] { new[] { new LdrColorA(11, 11, 11, 0), new LdrColorA(5, 4, 4, 0) }, new[] { new LdrColorA(5, 4, 4, 0), new LdrColorA(5, 4, 4, 0) } }), // Mode 3 - new Bc6hModeInfo(0x06, 1, true, 3, new[] { new[] { new LdrColorA(11, 11, 11, 0), new LdrColorA(4, 5, 4, 0) }, new[] { new LdrColorA(4, 5, 4, 0), new LdrColorA(4, 5, 4, 0) } }), // Mode 4 - new Bc6hModeInfo(0x0a, 1, true, 3, new[] { new[] { new LdrColorA(11, 11, 11, 0), new LdrColorA(4, 4, 5, 0) }, new[] { new LdrColorA(4, 4, 5, 0), new LdrColorA(4, 4, 5, 0) } }), // Mode 5 - new Bc6hModeInfo(0x0e, 1, true, 3, new[] { new[] { new LdrColorA(9, 9, 9, 0), new LdrColorA(5, 5, 5, 0) }, new[] { new LdrColorA(5, 5, 5, 0), new LdrColorA(5, 5, 5, 0) } }), // Mode 6 - new Bc6hModeInfo(0x12, 1, true, 3, new[] { new[] { new LdrColorA(8, 8, 8, 0), new LdrColorA(6, 5, 5, 0) }, new[] { new LdrColorA(6, 5, 5, 0), new LdrColorA(6, 5, 5, 0) } }), // Mode 7 - new Bc6hModeInfo(0x16, 1, true, 3, new[] { new[] { new LdrColorA(8, 8, 8, 0), new LdrColorA(5, 6, 5, 0) }, new[] { new LdrColorA(5, 6, 5, 0), new LdrColorA(5, 6, 5, 0) } }), // Mode 8 - new Bc6hModeInfo(0x1a, 1, true, 3, new[] { new[] { new LdrColorA(8, 8, 8, 0), new LdrColorA(5, 5, 6, 0) }, new[] { new LdrColorA(5, 5, 6, 0), new LdrColorA(5, 5, 6, 0) } }), // Mode 9 - new Bc6hModeInfo(0x1e, 1, false, 3, new[] { new[] { new LdrColorA(6, 6, 6, 0), new LdrColorA(6, 6, 6, 0) }, new[] { new LdrColorA(6, 6, 6, 0), new LdrColorA(6, 6, 6, 0) } }), // Mode 10 - new Bc6hModeInfo(0x03, 0, false, 4, new[] { new[] { new LdrColorA(10, 10, 10, 0), new LdrColorA(10, 10, 10, 0) }, new[] { new LdrColorA(0, 0, 0, 0), new LdrColorA(0, 0, 0, 0) } }), // Mode 11 - new Bc6hModeInfo(0x07, 0, true, 4, new[] { new[] { new LdrColorA(11, 11, 11, 0), new LdrColorA(9, 9, 9, 0) }, new[] { new LdrColorA(0, 0, 0, 0), new LdrColorA(0, 0, 0, 0) } }), // Mode 12 - new Bc6hModeInfo(0x0b, 0, true, 4, new[] { new[] { new LdrColorA(12, 12, 12, 0), new LdrColorA(8, 8, 8, 0) }, new[] { new LdrColorA(0, 0, 0, 0), new LdrColorA(0, 0, 0, 0) } }), // Mode 13 - new Bc6hModeInfo(0x0f, 0, true, 4, new[] { new[] { new LdrColorA(16, 16, 16, 0), new LdrColorA(4, 4, 4, 0) }, new[] { new LdrColorA(0, 0, 0, 0), new LdrColorA(0, 0, 0, 0) } }), // Mode 14 - }; - - private static readonly int[] ModeToInfo = - { - 0, // Mode 1 - 0x00 - 1, // Mode 2 - 0x01 - 2, // Mode 3 - 0x02 - 10, // Mode 11 - 0x03 - -1, // Invalid - 0x04 - -1, // Invalid - 0x05 - 3, // Mode 4 - 0x06 - 11, // Mode 12 - 0x07 - -1, // Invalid - 0x08 - -1, // Invalid - 0x09 - 4, // Mode 5 - 0x0a - 12, // Mode 13 - 0x0b - -1, // Invalid - 0x0c - -1, // Invalid - 0x0d - 5, // Mode 6 - 0x0e - 13, // Mode 14 - 0x0f - -1, // Invalid - 0x10 - -1, // Invalid - 0x11 - 6, // Mode 7 - 0x12 - -1, // Reserved - 0x13 - -1, // Invalid - 0x14 - -1, // Invalid - 0x15 - 7, // Mode 8 - 0x16 - -1, // Reserved - 0x17 - -1, // Invalid - 0x18 - -1, // Invalid - 0x19 - 8, // Mode 9 - 0x1a - -1, // Reserved - 0x1b - -1, // Invalid - 0x1c - -1, // Invalid - 0x1d - 9, // Mode 10 - 0x1e - -1, // Resreved - 0x1f - }; - - /// - public int BitsPerPixel => 32; - - /// - public byte PixelDepthBytes => 4; - - /// - public byte DivSize => 4; - - /// - public byte CompressedBytesPerBlock => 16; - - /// - public bool Compressed => true; - - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + uMode = (byte)((GetBits(currentBlock, ref uStartBit, 3) << 2) | uMode); + } - /// - public byte[] Decompress(byte[] blockData, int width, int height) - { - Bc6h self = this; - byte[] currentBlock = new byte[this.CompressedBytesPerBlock]; + Debug.Assert(uMode < 32, "uMode should be less then 32"); - return Helper.InMemoryDecode(blockData, width, height, (stream, data, streamIndex, dataIndex, stride) => + if (ModeToInfo[uMode] >= 0) { - // I would prefer to use Span, but not sure if I should reference System.Memory in this project - // copy data instead - Buffer.BlockCopy(blockData, streamIndex, currentBlock, 0, currentBlock.Length); - streamIndex += currentBlock.Length; - - uint uStartBit = 0; - byte uMode = GetBits(currentBlock, ref uStartBit, 2u); - if (uMode != 0x00 && uMode != 0x01) - { - uMode = (byte)((GetBits(currentBlock, ref uStartBit, 3) << 2) | uMode); - } + Debug.Assert(ModeToInfo[uMode] < ModeInfo.Length, "ModeToInfo[uMode] should be less then ModeInfo.Length"); + Bc6HModeDescriptor[] desc = ModeDescriptors[ModeToInfo[uMode]]; - Debug.Assert(uMode < 32, "uMode should be less then 32"); + Debug.Assert(ModeToInfo[uMode] < ModeDescriptors.Length, "ModeToInfo[uMode] should be less then ModeDescriptors.Length"); + ref Bc6hModeInfo info = ref ModeInfo[ModeToInfo[uMode]]; - if (ModeToInfo[uMode] >= 0) + IntEndPntPair[] aEndPts = new IntEndPntPair[Constants.BC6H_MAX_REGIONS]; + for (int i = 0; i < aEndPts.Length; ++i) { - Debug.Assert(ModeToInfo[uMode] < ModeInfo.Length, "ModeToInfo[uMode] should be less then ModeInfo.Length"); - Bc6HModeDescriptor[] desc = ModeDescriptors[ModeToInfo[uMode]]; - - Debug.Assert(ModeToInfo[uMode] < ModeDescriptors.Length, "ModeToInfo[uMode] should be less then ModeDescriptors.Length"); - ref Bc6hModeInfo info = ref ModeInfo[ModeToInfo[uMode]]; - - var aEndPts = new IntEndPntPair[Constants.BC6H_MAX_REGIONS]; - for (int i = 0; i < aEndPts.Length; ++i) - { - aEndPts[i] = new IntEndPntPair(new IntColor(), new IntColor()); - } + aEndPts[i] = new IntEndPntPair(new IntColor(), new IntColor()); + } - uint uShape = 0; + uint uShape = 0; - // Read header - uint uHeaderBits = info.Partitions > 0 ? 82u : 65u; - while (uStartBit < uHeaderBits) + // Read header + uint uHeaderBits = info.Partitions > 0 ? 82u : 65u; + while (uStartBit < uHeaderBits) + { + uint uCurBit = uStartBit; + if (GetBit(currentBlock, ref uStartBit) != 0) { - uint uCurBit = uStartBit; - if (GetBit(currentBlock, ref uStartBit) != 0) + switch (desc[uCurBit].MBc6HEField) { - switch (desc[uCurBit].MBc6HEField) + case Bc6hEField.D: + uShape |= 1u << desc[uCurBit].Bit; + break; + case Bc6hEField.RW: + aEndPts[0].A.R |= 1 << desc[uCurBit].Bit; + break; + case Bc6hEField.RX: + aEndPts[0].B.R |= 1 << desc[uCurBit].Bit; + break; + case Bc6hEField.RY: + aEndPts[1].A.R |= 1 << desc[uCurBit].Bit; + break; + case Bc6hEField.RZ: + aEndPts[1].B.R |= 1 << desc[uCurBit].Bit; + break; + case Bc6hEField.GW: + aEndPts[0].A.G |= 1 << desc[uCurBit].Bit; + break; + case Bc6hEField.GX: + aEndPts[0].B.G |= 1 << desc[uCurBit].Bit; + break; + case Bc6hEField.GY: + aEndPts[1].A.G |= 1 << desc[uCurBit].Bit; + break; + case Bc6hEField.GZ: + aEndPts[1].B.G |= 1 << desc[uCurBit].Bit; + break; + case Bc6hEField.BW: + aEndPts[0].A.B |= 1 << desc[uCurBit].Bit; + break; + case Bc6hEField.BX: + aEndPts[0].B.B |= 1 << desc[uCurBit].Bit; + break; + case Bc6hEField.BY: + aEndPts[1].A.B |= 1 << desc[uCurBit].Bit; + break; + case Bc6hEField.BZ: + aEndPts[1].B.B |= 1 << desc[uCurBit].Bit; + break; + default: { - case Bc6hEField.D: - uShape |= 1u << desc[uCurBit].Bit; - break; - case Bc6hEField.RW: - aEndPts[0].A.R |= 1 << desc[uCurBit].Bit; - break; - case Bc6hEField.RX: - aEndPts[0].B.R |= 1 << desc[uCurBit].Bit; - break; - case Bc6hEField.RY: - aEndPts[1].A.R |= 1 << desc[uCurBit].Bit; - break; - case Bc6hEField.RZ: - aEndPts[1].B.R |= 1 << desc[uCurBit].Bit; - break; - case Bc6hEField.GW: - aEndPts[0].A.G |= 1 << desc[uCurBit].Bit; - break; - case Bc6hEField.GX: - aEndPts[0].B.G |= 1 << desc[uCurBit].Bit; - break; - case Bc6hEField.GY: - aEndPts[1].A.G |= 1 << desc[uCurBit].Bit; - break; - case Bc6hEField.GZ: - aEndPts[1].B.G |= 1 << desc[uCurBit].Bit; - break; - case Bc6hEField.BW: - aEndPts[0].A.B |= 1 << desc[uCurBit].Bit; - break; - case Bc6hEField.BX: - aEndPts[0].B.B |= 1 << desc[uCurBit].Bit; - break; - case Bc6hEField.BY: - aEndPts[1].A.B |= 1 << desc[uCurBit].Bit; - break; - case Bc6hEField.BZ: - aEndPts[1].B.B |= 1 << desc[uCurBit].Bit; - break; - default: - { - Debug.WriteLine("BC6H: Invalid header bits encountered during decoding"); - Helpers.FillWithErrorColors(data, ref dataIndex, Constants.NumPixelsPerBlock, self.DivSize, stride); - return dataIndex; - } + Debug.WriteLine("BC6H: Invalid header bits encountered during decoding"); + Helpers.FillWithErrorColors(data, ref dataIndex, Constants.NumPixelsPerBlock, self.DivSize, stride); + return dataIndex; } } } + } - Debug.Assert(uShape < 64, "uShape should be less then 64"); + Debug.Assert(uShape < 64, "uShape should be less then 64"); - if (info.Transformed) + if (info.Transformed) + { + Debug.Assert(info.Partitions < Constants.BC6H_MAX_REGIONS, $"info.Partitions should be less then {Constants.BC6H_MAX_REGIONS}"); + for (int p = 0; p <= info.Partitions; ++p) { - Debug.Assert(info.Partitions < Constants.BC6H_MAX_REGIONS, $"info.Partitions should be less then {Constants.BC6H_MAX_REGIONS}"); - for (int p = 0; p <= info.Partitions; ++p) + if (p != 0) { - if (p != 0) - { - aEndPts[p].A.SignExtend(info.RgbaPrec[p][0]); - } - - aEndPts[p].B.SignExtend(info.RgbaPrec[p][1]); + aEndPts[p].A.SignExtend(info.RgbaPrec[p][0]); } - } - // Inverse transform the end points - if (info.Transformed) - { - Helpers.TransformInverseUnsigned(aEndPts, info.RgbaPrec[0][0]); + aEndPts[p].B.SignExtend(info.RgbaPrec[p][1]); } + } - // Read indices - for (int i = 0; i < Constants.NumPixelsPerBlock; ++i) + // Inverse transform the end points + if (info.Transformed) + { + Helpers.TransformInverseUnsigned(aEndPts, info.RgbaPrec[0][0]); + } + + // Read indices + for (int i = 0; i < Constants.NumPixelsPerBlock; ++i) + { + uint uNumBits = Helpers.IsFixUpOffset(info.Partitions, (byte)uShape, i) ? info.IndexPrec - 1u : info.IndexPrec; + if (uStartBit + uNumBits > 128) { - uint uNumBits = Helpers.IsFixUpOffset(info.Partitions, (byte)uShape, i) ? info.IndexPrec - 1u : info.IndexPrec; - if (uStartBit + uNumBits > 128) - { - Debug.WriteLine("BC6H: Invalid block encountered during decoding"); - Helpers.FillWithErrorColors(data, ref dataIndex, Constants.NumPixelsPerBlock, self.DivSize, stride); - return dataIndex; - } + Debug.WriteLine("BC6H: Invalid block encountered during decoding"); + Helpers.FillWithErrorColors(data, ref dataIndex, Constants.NumPixelsPerBlock, self.DivSize, stride); + return dataIndex; + } - uint uIndex = GetBits(currentBlock, ref uStartBit, uNumBits); + uint uIndex = GetBits(currentBlock, ref uStartBit, uNumBits); - if (uIndex >= ((info.Partitions > 0) ? 8 : 16)) - { - Debug.WriteLine("BC6H: Invalid index encountered during decoding"); - Helpers.FillWithErrorColors(data, ref dataIndex, Constants.NumPixelsPerBlock, self.DivSize, stride); - return dataIndex; - } + if (uIndex >= ((info.Partitions > 0) ? 8 : 16)) + { + Debug.WriteLine("BC6H: Invalid index encountered during decoding"); + Helpers.FillWithErrorColors(data, ref dataIndex, Constants.NumPixelsPerBlock, self.DivSize, stride); + return dataIndex; + } - uint uRegion = Constants.PartitionTable[info.Partitions][uShape][i]; - Debug.Assert(uRegion < Constants.BC6H_MAX_REGIONS, $"uRegion should be smaller than {Constants.BC6H_MAX_REGIONS}"); - - // Unquantize endpoints and interpolate - int r1 = Unquantize(aEndPts[uRegion].A.R, info.RgbaPrec[0][0].R); - int g1 = Unquantize(aEndPts[uRegion].A.G, info.RgbaPrec[0][0].G); - int b1 = Unquantize(aEndPts[uRegion].A.B, info.RgbaPrec[0][0].B); - int r2 = Unquantize(aEndPts[uRegion].B.R, info.RgbaPrec[0][0].R); - int g2 = Unquantize(aEndPts[uRegion].B.G, info.RgbaPrec[0][0].G); - int b2 = Unquantize(aEndPts[uRegion].B.B, info.RgbaPrec[0][0].B); - int[] aWeights = info.Partitions > 0 ? Constants.Weights3 : Constants.Weights4; - var fc = new IntColor - { - R = FinishUnquantize(((r1 * (Constants.BC67_WEIGHT_MAX - aWeights[uIndex])) + (r2 * aWeights[uIndex]) + Constants.BC67_WEIGHT_ROUND) >> Constants.BC67_WEIGHT_SHIFT), - G = FinishUnquantize(((g1 * (Constants.BC67_WEIGHT_MAX - aWeights[uIndex])) + (g2 * aWeights[uIndex]) + Constants.BC67_WEIGHT_ROUND) >> Constants.BC67_WEIGHT_SHIFT), - B = FinishUnquantize(((b1 * (Constants.BC67_WEIGHT_MAX - aWeights[uIndex])) + (b2 * aWeights[uIndex]) + Constants.BC67_WEIGHT_ROUND) >> Constants.BC67_WEIGHT_SHIFT) - }; - - ushort[] rgb = new ushort[3]; - fc.ToF16Unsigned(rgb); - - // Clamp 0..1, and convert to byte (we're losing high dynamic range) - data[dataIndex++] = (byte)((Math.Max(0.0f, Math.Min(1.0f, FloatHelper.UnpackFloat16ToFloat(rgb[0]))) * 255.0f) + 0.5f); // red - data[dataIndex++] = (byte)((Math.Max(0.0f, Math.Min(1.0f, FloatHelper.UnpackFloat16ToFloat(rgb[1]))) * 255.0f) + 0.5f); // green - data[dataIndex++] = (byte)((Math.Max(0.0f, Math.Min(1.0f, FloatHelper.UnpackFloat16ToFloat(rgb[2]))) * 255.0f) + 0.5f); // blue - data[dataIndex++] = 255; - - // Is mult 4? - if (((i + 1) & 0x3) == 0) - { - dataIndex += self.PixelDepthBytes * (stride - self.DivSize); - } + uint uRegion = Constants.PartitionTable[info.Partitions][uShape][i]; + Debug.Assert(uRegion < Constants.BC6H_MAX_REGIONS, $"uRegion should be smaller than {Constants.BC6H_MAX_REGIONS}"); + + // Unquantize endpoints and interpolate + int r1 = Unquantize(aEndPts[uRegion].A.R, info.RgbaPrec[0][0].R); + int g1 = Unquantize(aEndPts[uRegion].A.G, info.RgbaPrec[0][0].G); + int b1 = Unquantize(aEndPts[uRegion].A.B, info.RgbaPrec[0][0].B); + int r2 = Unquantize(aEndPts[uRegion].B.R, info.RgbaPrec[0][0].R); + int g2 = Unquantize(aEndPts[uRegion].B.G, info.RgbaPrec[0][0].G); + int b2 = Unquantize(aEndPts[uRegion].B.B, info.RgbaPrec[0][0].B); + int[] aWeights = info.Partitions > 0 ? Constants.Weights3 : Constants.Weights4; + IntColor fc = new IntColor + { + R = FinishUnquantize(((r1 * (Constants.BC67_WEIGHT_MAX - aWeights[uIndex])) + (r2 * aWeights[uIndex]) + Constants.BC67_WEIGHT_ROUND) >> Constants.BC67_WEIGHT_SHIFT), + G = FinishUnquantize(((g1 * (Constants.BC67_WEIGHT_MAX - aWeights[uIndex])) + (g2 * aWeights[uIndex]) + Constants.BC67_WEIGHT_ROUND) >> Constants.BC67_WEIGHT_SHIFT), + B = FinishUnquantize(((b1 * (Constants.BC67_WEIGHT_MAX - aWeights[uIndex])) + (b2 * aWeights[uIndex]) + Constants.BC67_WEIGHT_ROUND) >> Constants.BC67_WEIGHT_SHIFT) + }; + + ushort[] rgb = new ushort[3]; + fc.ToF16Unsigned(rgb); + + // Clamp 0..1, and convert to byte (we're losing high dynamic range) + data[dataIndex++] = (byte)((Math.Max(0.0f, Math.Min(1.0f, FloatHelper.UnpackFloat16ToFloat(rgb[0]))) * 255.0f) + 0.5f); // red + data[dataIndex++] = (byte)((Math.Max(0.0f, Math.Min(1.0f, FloatHelper.UnpackFloat16ToFloat(rgb[1]))) * 255.0f) + 0.5f); // green + data[dataIndex++] = (byte)((Math.Max(0.0f, Math.Min(1.0f, FloatHelper.UnpackFloat16ToFloat(rgb[2]))) * 255.0f) + 0.5f); // blue + data[dataIndex++] = 255; + + // Is mult 4? + if (((i + 1) & 0x3) == 0) + { + dataIndex += self.PixelDepthBytes * (stride - self.DivSize); } } - else + } + else + { + string warnStr = "BC6H: Invalid mode encountered during decoding"; + switch (uMode) { - string warnStr = "BC6H: Invalid mode encountered during decoding"; - switch (uMode) - { - case 0x13: - warnStr = "BC6H: Reserved mode 10011 encountered during decoding"; - break; - case 0x17: - warnStr = "BC6H: Reserved mode 10111 encountered during decoding"; - break; - case 0x1B: - warnStr = "BC6H: Reserved mode 11011 encountered during decoding"; - break; - case 0x1F: - warnStr = "BC6H: Reserved mode 11111 encountered during decoding"; - break; - } + case 0x13: + warnStr = "BC6H: Reserved mode 10011 encountered during decoding"; + break; + case 0x17: + warnStr = "BC6H: Reserved mode 10111 encountered during decoding"; + break; + case 0x1B: + warnStr = "BC6H: Reserved mode 11011 encountered during decoding"; + break; + case 0x1F: + warnStr = "BC6H: Reserved mode 11111 encountered during decoding"; + break; + } - Debug.WriteLine(warnStr); + Debug.WriteLine(warnStr); - // Per the BC6H format spec, we must return opaque black - for (int i = 0; i < Constants.NumPixelsPerBlock; ++i) - { - data[dataIndex++] = 0; - data[dataIndex++] = 0; - data[dataIndex++] = 0; - data[dataIndex++] = 0; + // Per the BC6H format spec, we must return opaque black + for (int i = 0; i < Constants.NumPixelsPerBlock; ++i) + { + data[dataIndex++] = 0; + data[dataIndex++] = 0; + data[dataIndex++] = 0; + data[dataIndex++] = 0; - // Is mult 4? - if (((i + 1) & 0x3) == 0) - { - dataIndex += self.PixelDepthBytes * (stride - self.DivSize); - } + // Is mult 4? + if (((i + 1) & 0x3) == 0) + { + dataIndex += self.PixelDepthBytes * (stride - self.DivSize); } } + } - return streamIndex; - }); - } + return streamIndex; + }); + } + + /// + /// Gets a bit for a given position. + /// + /// The current block. + /// The start bit. + /// A bit at a given position. + public static byte GetBit(byte[] currentBlock, ref uint uStartBit) + { + Debug.Assert(uStartBit < 128, "uStartBit should be less then 128"); + uint uIndex = uStartBit >> 3; + byte ret = (byte)((currentBlock[uIndex] >> (int)(uStartBit - (uIndex << 3))) & 0x01); + uStartBit++; + return ret; + } - /// - /// Gets a bit for a given position. - /// - /// The current block. - /// The start bit. - /// A bit at a given position. - public static byte GetBit(byte[] currentBlock, ref uint uStartBit) + /// + /// Gets n bits at a given start position. + /// + /// The current block. + /// The start bit. + /// The number of bits. + /// Bits at a given position. + public static byte GetBits(byte[] currentBlock, ref uint uStartBit, uint uNumBits) + { + if (uNumBits == 0) { - Debug.Assert(uStartBit < 128, "uStartBit should be less then 128"); - uint uIndex = uStartBit >> 3; - byte ret = (byte)((currentBlock[uIndex] >> (int)(uStartBit - (uIndex << 3))) & 0x01); - uStartBit++; - return ret; + return 0; } - /// - /// Gets n bits at a given start position. - /// - /// The current block. - /// The start bit. - /// The number of bits. - /// Bits at a given position. - public static byte GetBits(byte[] currentBlock, ref uint uStartBit, uint uNumBits) + Debug.Assert(uStartBit + uNumBits <= 128 && uNumBits <= 8, "uStartBit + uNumBits <= 128 && uNumBits <= 8"); + byte ret; + uint uIndex = uStartBit >> 3; + uint uBase = uStartBit - (uIndex << 3); + if (uBase + uNumBits > 8) { - if (uNumBits == 0) - { - return 0; - } - - Debug.Assert(uStartBit + uNumBits <= 128 && uNumBits <= 8, "uStartBit + uNumBits <= 128 && uNumBits <= 8"); - byte ret; - uint uIndex = uStartBit >> 3; - uint uBase = uStartBit - (uIndex << 3); - if (uBase + uNumBits > 8) - { - uint uFirstIndexBits = 8 - uBase; - uint uNextIndexBits = uNumBits - uFirstIndexBits; - ret = (byte)((uint)(currentBlock[uIndex] >> (int)uBase) | ((currentBlock[uIndex + 1] & ((1u << (int)uNextIndexBits) - 1)) << (int)uFirstIndexBits)); - } - else - { - ret = (byte)((currentBlock[uIndex] >> (int)uBase) & ((1 << (int)uNumBits) - 1)); - } - - Debug.Assert(ret < (1 << (int)uNumBits), $"GetBits() return value should be less then {1 << (int)uNumBits}"); - uStartBit += uNumBits; - return ret; + uint uFirstIndexBits = 8 - uBase; + uint uNextIndexBits = uNumBits - uFirstIndexBits; + ret = (byte)((uint)(currentBlock[uIndex] >> (int)uBase) | ((currentBlock[uIndex + 1] & ((1u << (int)uNextIndexBits) - 1)) << (int)uFirstIndexBits)); } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int Unquantize(int comp, byte uBitsPerComp) + else { - int unq; + ret = (byte)((currentBlock[uIndex] >> (int)uBase) & ((1 << (int)uNumBits) - 1)); + } - if (uBitsPerComp >= 15) - { - unq = comp; - } - else if (comp == 0) - { - unq = 0; - } - else if (comp == ((1 << uBitsPerComp) - 1)) - { - unq = 0xFFFF; - } - else - { - unq = ((comp << 16) + 0x8000) >> uBitsPerComp; - } + Debug.Assert(ret < (1 << (int)uNumBits), $"GetBits() return value should be less then {1 << (int)uNumBits}"); + uStartBit += uNumBits; + return ret; + } - return unq; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int Unquantize(int comp, byte uBitsPerComp) + { + int unq; + + if (uBitsPerComp >= 15) + { + unq = comp; + } + else if (comp == 0) + { + unq = 0; + } + else if (comp == ((1 << uBitsPerComp) - 1)) + { + unq = 0xFFFF; + } + else + { + unq = ((comp << 16) + 0x8000) >> uBitsPerComp; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int FinishUnquantize(int comp) => (comp * 31) >> 6; // scale the magnitude by 31/64 + return unq; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int FinishUnquantize(int comp) => (comp * 31) >> 6; // scale the magnitude by 31/64 } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Bc6hEField.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Bc6hEField.cs index fd26730b..842971b8 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Bc6hEField.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Bc6hEField.cs @@ -1,24 +1,23 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; + +internal enum Bc6hEField : byte { - internal enum Bc6hEField : byte - { - NA, // N/A - M, // Mode - D, // Shape - RW, - RX, - RY, - RZ, - GW, - GX, - GY, - GZ, - BW, - BX, - BY, - BZ, - } + NA, // N/A + M, // Mode + D, // Shape + RW, + RX, + RY, + RZ, + GW, + GX, + GY, + GZ, + BW, + BX, + BY, + BZ, } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Bc6hModeDescriptor.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Bc6hModeDescriptor.cs index 3bc2f8b0..72c8d392 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Bc6hModeDescriptor.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Bc6hModeDescriptor.cs @@ -1,17 +1,16 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; + +internal struct Bc6HModeDescriptor { - internal struct Bc6HModeDescriptor - { - public readonly Bc6hEField MBc6HEField; - public readonly byte Bit; + public readonly Bc6hEField MBc6HEField; + public readonly byte Bit; - public Bc6HModeDescriptor(Bc6hEField bc6Hef, byte uB) - { - this.MBc6HEField = bc6Hef; - this.Bit = uB; - } + public Bc6HModeDescriptor(Bc6hEField bc6Hef, byte uB) + { + this.MBc6HEField = bc6Hef; + this.Bit = uB; } } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Bc6hModeInfo.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Bc6hModeInfo.cs index 4a460cee..849d644a 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Bc6hModeInfo.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Bc6hModeInfo.cs @@ -3,23 +3,22 @@ using SixLabors.ImageSharp.Textures.TextureFormats.Decoding.PixelFormats; -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; + +internal struct Bc6hModeInfo { - internal struct Bc6hModeInfo - { - public byte Mode; - public byte Partitions; - public bool Transformed; - public byte IndexPrec; - public readonly LdrColorA[][] RgbaPrec; // [Constants.BC6H_MAX_REGIONS][2]; + public byte Mode; + public byte Partitions; + public bool Transformed; + public byte IndexPrec; + public readonly LdrColorA[][] RgbaPrec; // [Constants.BC6H_MAX_REGIONS][2]; - public Bc6hModeInfo(byte m, byte p, bool t, byte i, LdrColorA[][] prec) - { - this.Mode = m; - this.Partitions = p; - this.Transformed = t; - this.IndexPrec = i; - this.RgbaPrec = prec; - } + public Bc6hModeInfo(byte m, byte p, bool t, byte i, LdrColorA[][] prec) + { + this.Mode = m; + this.Partitions = p; + this.Transformed = t; + this.IndexPrec = i; + this.RgbaPrec = prec; } } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Bc6hs.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Bc6hs.cs index 0de87e61..55721ed8 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Bc6hs.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Bc6hs.cs @@ -1,600 +1,584 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Diagnostics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Textures.Common.Helpers; using SixLabors.ImageSharp.Textures.TextureFormats.Decoding.PixelFormats; -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; + +/// +/// Texture compressed with BC6HS, three color channels (16 bits:16 bits:16 bits) in "half" floating point. +/// +internal struct Bc6hs : IBlock { - /// - /// Texture compressed with BC6HS, three color channels (16 bits:16 bits:16 bits) in "half" floating point. - /// - internal struct Bc6hs : IBlock + // Code based on commit 138efff1b9c53fd9a5dd34b8c865e8f5ae798030 2019/10/24 in DirectXTex C++ library + private static readonly Bc6hsModeDescriptor[][] MsADesc = + [ + [ + // Mode 1 (0x00) - 10 5 5 5 + new Bc6hsModeDescriptor(Bc6hEField.M, 0), new Bc6hsModeDescriptor(Bc6hEField.M, 1), new Bc6hsModeDescriptor(Bc6hEField.GY, 4), new Bc6hsModeDescriptor(Bc6hEField.BY, 4), new Bc6hsModeDescriptor(Bc6hEField.BZ, 4), new Bc6hsModeDescriptor(Bc6hEField.RW, 0), new Bc6hsModeDescriptor(Bc6hEField.RW, 1), new Bc6hsModeDescriptor(Bc6hEField.RW, 2), new Bc6hsModeDescriptor(Bc6hEField.RW, 3), new Bc6hsModeDescriptor(Bc6hEField.RW, 4), + new Bc6hsModeDescriptor(Bc6hEField.RW, 5), new Bc6hsModeDescriptor(Bc6hEField.RW, 6), new Bc6hsModeDescriptor(Bc6hEField.RW, 7), new Bc6hsModeDescriptor(Bc6hEField.RW, 8), new Bc6hsModeDescriptor(Bc6hEField.RW, 9), new Bc6hsModeDescriptor(Bc6hEField.GW, 0), new Bc6hsModeDescriptor(Bc6hEField.GW, 1), new Bc6hsModeDescriptor(Bc6hEField.GW, 2), new Bc6hsModeDescriptor(Bc6hEField.GW, 3), new Bc6hsModeDescriptor(Bc6hEField.GW, 4), + new Bc6hsModeDescriptor(Bc6hEField.GW, 5), new Bc6hsModeDescriptor(Bc6hEField.GW, 6), new Bc6hsModeDescriptor(Bc6hEField.GW, 7), new Bc6hsModeDescriptor(Bc6hEField.GW, 8), new Bc6hsModeDescriptor(Bc6hEField.GW, 9), new Bc6hsModeDescriptor(Bc6hEField.BW, 0), new Bc6hsModeDescriptor(Bc6hEField.BW, 1), new Bc6hsModeDescriptor(Bc6hEField.BW, 2), new Bc6hsModeDescriptor(Bc6hEField.BW, 3), new Bc6hsModeDescriptor(Bc6hEField.BW, 4), + new Bc6hsModeDescriptor(Bc6hEField.BW, 5), new Bc6hsModeDescriptor(Bc6hEField.BW, 6), new Bc6hsModeDescriptor(Bc6hEField.BW, 7), new Bc6hsModeDescriptor(Bc6hEField.BW, 8), new Bc6hsModeDescriptor(Bc6hEField.BW, 9), new Bc6hsModeDescriptor(Bc6hEField.RX, 0), new Bc6hsModeDescriptor(Bc6hEField.RX, 1), new Bc6hsModeDescriptor(Bc6hEField.RX, 2), new Bc6hsModeDescriptor(Bc6hEField.RX, 3), new Bc6hsModeDescriptor(Bc6hEField.RX, 4), + new Bc6hsModeDescriptor(Bc6hEField.GZ, 4), new Bc6hsModeDescriptor(Bc6hEField.GY, 0), new Bc6hsModeDescriptor(Bc6hEField.GY, 1), new Bc6hsModeDescriptor(Bc6hEField.GY, 2), new Bc6hsModeDescriptor(Bc6hEField.GY, 3), new Bc6hsModeDescriptor(Bc6hEField.GX, 0), new Bc6hsModeDescriptor(Bc6hEField.GX, 1), new Bc6hsModeDescriptor(Bc6hEField.GX, 2), new Bc6hsModeDescriptor(Bc6hEField.GX, 3), new Bc6hsModeDescriptor(Bc6hEField.GX, 4), + new Bc6hsModeDescriptor(Bc6hEField.BZ, 0), new Bc6hsModeDescriptor(Bc6hEField.GZ, 0), new Bc6hsModeDescriptor(Bc6hEField.GZ, 1), new Bc6hsModeDescriptor(Bc6hEField.GZ, 2), new Bc6hsModeDescriptor(Bc6hEField.GZ, 3), new Bc6hsModeDescriptor(Bc6hEField.BX, 0), new Bc6hsModeDescriptor(Bc6hEField.BX, 1), new Bc6hsModeDescriptor(Bc6hEField.BX, 2), new Bc6hsModeDescriptor(Bc6hEField.BX, 3), new Bc6hsModeDescriptor(Bc6hEField.BX, 4), + new Bc6hsModeDescriptor(Bc6hEField.BZ, 1), new Bc6hsModeDescriptor(Bc6hEField.BY, 0), new Bc6hsModeDescriptor(Bc6hEField.BY, 1), new Bc6hsModeDescriptor(Bc6hEField.BY, 2), new Bc6hsModeDescriptor(Bc6hEField.BY, 3), new Bc6hsModeDescriptor(Bc6hEField.RY, 0), new Bc6hsModeDescriptor(Bc6hEField.RY, 1), new Bc6hsModeDescriptor(Bc6hEField.RY, 2), new Bc6hsModeDescriptor(Bc6hEField.RY, 3), new Bc6hsModeDescriptor(Bc6hEField.RY, 4), + new Bc6hsModeDescriptor(Bc6hEField.BZ, 2), new Bc6hsModeDescriptor(Bc6hEField.RZ, 0), new Bc6hsModeDescriptor(Bc6hEField.RZ, 1), new Bc6hsModeDescriptor(Bc6hEField.RZ, 2), new Bc6hsModeDescriptor(Bc6hEField.RZ, 3), new Bc6hsModeDescriptor(Bc6hEField.RZ, 4), new Bc6hsModeDescriptor(Bc6hEField.BZ, 3), new Bc6hsModeDescriptor(Bc6hEField.D, 0), new Bc6hsModeDescriptor(Bc6hEField.D, 1), new Bc6hsModeDescriptor(Bc6hEField.D, 2), + new Bc6hsModeDescriptor(Bc6hEField.D, 3), new Bc6hsModeDescriptor(Bc6hEField.D, 4) + ], + + [ + // Mode 2 (0x01) - 7 6 6 6 + new Bc6hsModeDescriptor(Bc6hEField.M, 0), new Bc6hsModeDescriptor(Bc6hEField.M, 1), new Bc6hsModeDescriptor(Bc6hEField.GY, 5), new Bc6hsModeDescriptor(Bc6hEField.GZ, 4), new Bc6hsModeDescriptor(Bc6hEField.GZ, 5), new Bc6hsModeDescriptor(Bc6hEField.RW, 0), new Bc6hsModeDescriptor(Bc6hEField.RW, 1), new Bc6hsModeDescriptor(Bc6hEField.RW, 2), new Bc6hsModeDescriptor(Bc6hEField.RW, 3), new Bc6hsModeDescriptor(Bc6hEField.RW, 4), + new Bc6hsModeDescriptor(Bc6hEField.RW, 5), new Bc6hsModeDescriptor(Bc6hEField.RW, 6), new Bc6hsModeDescriptor(Bc6hEField.BZ, 0), new Bc6hsModeDescriptor(Bc6hEField.BZ, 1), new Bc6hsModeDescriptor(Bc6hEField.BY, 4), new Bc6hsModeDescriptor(Bc6hEField.GW, 0), new Bc6hsModeDescriptor(Bc6hEField.GW, 1), new Bc6hsModeDescriptor(Bc6hEField.GW, 2), new Bc6hsModeDescriptor(Bc6hEField.GW, 3), new Bc6hsModeDescriptor(Bc6hEField.GW, 4), + new Bc6hsModeDescriptor(Bc6hEField.GW, 5), new Bc6hsModeDescriptor(Bc6hEField.GW, 6), new Bc6hsModeDescriptor(Bc6hEField.BY, 5), new Bc6hsModeDescriptor(Bc6hEField.BZ, 2), new Bc6hsModeDescriptor(Bc6hEField.GY, 4), new Bc6hsModeDescriptor(Bc6hEField.BW, 0), new Bc6hsModeDescriptor(Bc6hEField.BW, 1), new Bc6hsModeDescriptor(Bc6hEField.BW, 2), new Bc6hsModeDescriptor(Bc6hEField.BW, 3), new Bc6hsModeDescriptor(Bc6hEField.BW, 4), + new Bc6hsModeDescriptor(Bc6hEField.BW, 5), new Bc6hsModeDescriptor(Bc6hEField.BW, 6), new Bc6hsModeDescriptor(Bc6hEField.BZ, 3), new Bc6hsModeDescriptor(Bc6hEField.BZ, 5), new Bc6hsModeDescriptor(Bc6hEField.BZ, 4), new Bc6hsModeDescriptor(Bc6hEField.RX, 0), new Bc6hsModeDescriptor(Bc6hEField.RX, 1), new Bc6hsModeDescriptor(Bc6hEField.RX, 2), new Bc6hsModeDescriptor(Bc6hEField.RX, 3), new Bc6hsModeDescriptor(Bc6hEField.RX, 4), + new Bc6hsModeDescriptor(Bc6hEField.RX, 5), new Bc6hsModeDescriptor(Bc6hEField.GY, 0), new Bc6hsModeDescriptor(Bc6hEField.GY, 1), new Bc6hsModeDescriptor(Bc6hEField.GY, 2), new Bc6hsModeDescriptor(Bc6hEField.GY, 3), new Bc6hsModeDescriptor(Bc6hEField.GX, 0), new Bc6hsModeDescriptor(Bc6hEField.GX, 1), new Bc6hsModeDescriptor(Bc6hEField.GX, 2), new Bc6hsModeDescriptor(Bc6hEField.GX, 3), new Bc6hsModeDescriptor(Bc6hEField.GX, 4), + new Bc6hsModeDescriptor(Bc6hEField.GX, 5), new Bc6hsModeDescriptor(Bc6hEField.GZ, 0), new Bc6hsModeDescriptor(Bc6hEField.GZ, 1), new Bc6hsModeDescriptor(Bc6hEField.GZ, 2), new Bc6hsModeDescriptor(Bc6hEField.GZ, 3), new Bc6hsModeDescriptor(Bc6hEField.BX, 0), new Bc6hsModeDescriptor(Bc6hEField.BX, 1), new Bc6hsModeDescriptor(Bc6hEField.BX, 2), new Bc6hsModeDescriptor(Bc6hEField.BX, 3), new Bc6hsModeDescriptor(Bc6hEField.BX, 4), + new Bc6hsModeDescriptor(Bc6hEField.BX, 5), new Bc6hsModeDescriptor(Bc6hEField.BY, 0), new Bc6hsModeDescriptor(Bc6hEField.BY, 1), new Bc6hsModeDescriptor(Bc6hEField.BY, 2), new Bc6hsModeDescriptor(Bc6hEField.BY, 3), new Bc6hsModeDescriptor(Bc6hEField.RY, 0), new Bc6hsModeDescriptor(Bc6hEField.RY, 1), new Bc6hsModeDescriptor(Bc6hEField.RY, 2), new Bc6hsModeDescriptor(Bc6hEField.RY, 3), new Bc6hsModeDescriptor(Bc6hEField.RY, 4), + new Bc6hsModeDescriptor(Bc6hEField.RY, 5), new Bc6hsModeDescriptor(Bc6hEField.RZ, 0), new Bc6hsModeDescriptor(Bc6hEField.RZ, 1), new Bc6hsModeDescriptor(Bc6hEField.RZ, 2), new Bc6hsModeDescriptor(Bc6hEField.RZ, 3), new Bc6hsModeDescriptor(Bc6hEField.RZ, 4), new Bc6hsModeDescriptor(Bc6hEField.RZ, 5), new Bc6hsModeDescriptor(Bc6hEField.D, 0), new Bc6hsModeDescriptor(Bc6hEField.D, 1), new Bc6hsModeDescriptor(Bc6hEField.D, 2), + new Bc6hsModeDescriptor(Bc6hEField.D, 3), new Bc6hsModeDescriptor(Bc6hEField.D, 4) + ], + + [ + // Mode 3 (0x02) - 11 5 4 4 + new Bc6hsModeDescriptor(Bc6hEField.M, 0), new Bc6hsModeDescriptor(Bc6hEField.M, 1), new Bc6hsModeDescriptor(Bc6hEField.M, 2), new Bc6hsModeDescriptor(Bc6hEField.M, 3), new Bc6hsModeDescriptor(Bc6hEField.M, 4), new Bc6hsModeDescriptor(Bc6hEField.RW, 0), new Bc6hsModeDescriptor(Bc6hEField.RW, 1), new Bc6hsModeDescriptor(Bc6hEField.RW, 2), new Bc6hsModeDescriptor(Bc6hEField.RW, 3), new Bc6hsModeDescriptor(Bc6hEField.RW, 4), + new Bc6hsModeDescriptor(Bc6hEField.RW, 5), new Bc6hsModeDescriptor(Bc6hEField.RW, 6), new Bc6hsModeDescriptor(Bc6hEField.RW, 7), new Bc6hsModeDescriptor(Bc6hEField.RW, 8), new Bc6hsModeDescriptor(Bc6hEField.RW, 9), new Bc6hsModeDescriptor(Bc6hEField.GW, 0), new Bc6hsModeDescriptor(Bc6hEField.GW, 1), new Bc6hsModeDescriptor(Bc6hEField.GW, 2), new Bc6hsModeDescriptor(Bc6hEField.GW, 3), new Bc6hsModeDescriptor(Bc6hEField.GW, 4), + new Bc6hsModeDescriptor(Bc6hEField.GW, 5), new Bc6hsModeDescriptor(Bc6hEField.GW, 6), new Bc6hsModeDescriptor(Bc6hEField.GW, 7), new Bc6hsModeDescriptor(Bc6hEField.GW, 8), new Bc6hsModeDescriptor(Bc6hEField.GW, 9), new Bc6hsModeDescriptor(Bc6hEField.BW, 0), new Bc6hsModeDescriptor(Bc6hEField.BW, 1), new Bc6hsModeDescriptor(Bc6hEField.BW, 2), new Bc6hsModeDescriptor(Bc6hEField.BW, 3), new Bc6hsModeDescriptor(Bc6hEField.BW, 4), + new Bc6hsModeDescriptor(Bc6hEField.BW, 5), new Bc6hsModeDescriptor(Bc6hEField.BW, 6), new Bc6hsModeDescriptor(Bc6hEField.BW, 7), new Bc6hsModeDescriptor(Bc6hEField.BW, 8), new Bc6hsModeDescriptor(Bc6hEField.BW, 9), new Bc6hsModeDescriptor(Bc6hEField.RX, 0), new Bc6hsModeDescriptor(Bc6hEField.RX, 1), new Bc6hsModeDescriptor(Bc6hEField.RX, 2), new Bc6hsModeDescriptor(Bc6hEField.RX, 3), new Bc6hsModeDescriptor(Bc6hEField.RX, 4), + new Bc6hsModeDescriptor(Bc6hEField.RW, 10), new Bc6hsModeDescriptor(Bc6hEField.GY, 0), new Bc6hsModeDescriptor(Bc6hEField.GY, 1), new Bc6hsModeDescriptor(Bc6hEField.GY, 2), new Bc6hsModeDescriptor(Bc6hEField.GY, 3), new Bc6hsModeDescriptor(Bc6hEField.GX, 0), new Bc6hsModeDescriptor(Bc6hEField.GX, 1), new Bc6hsModeDescriptor(Bc6hEField.GX, 2), new Bc6hsModeDescriptor(Bc6hEField.GX, 3), new Bc6hsModeDescriptor(Bc6hEField.GW, 10), + new Bc6hsModeDescriptor(Bc6hEField.BZ, 0), new Bc6hsModeDescriptor(Bc6hEField.GZ, 0), new Bc6hsModeDescriptor(Bc6hEField.GZ, 1), new Bc6hsModeDescriptor(Bc6hEField.GZ, 2), new Bc6hsModeDescriptor(Bc6hEField.GZ, 3), new Bc6hsModeDescriptor(Bc6hEField.BX, 0), new Bc6hsModeDescriptor(Bc6hEField.BX, 1), new Bc6hsModeDescriptor(Bc6hEField.BX, 2), new Bc6hsModeDescriptor(Bc6hEField.BX, 3), new Bc6hsModeDescriptor(Bc6hEField.BW, 10), + new Bc6hsModeDescriptor(Bc6hEField.BZ, 1), new Bc6hsModeDescriptor(Bc6hEField.BY, 0), new Bc6hsModeDescriptor(Bc6hEField.BY, 1), new Bc6hsModeDescriptor(Bc6hEField.BY, 2), new Bc6hsModeDescriptor(Bc6hEField.BY, 3), new Bc6hsModeDescriptor(Bc6hEField.RY, 0), new Bc6hsModeDescriptor(Bc6hEField.RY, 1), new Bc6hsModeDescriptor(Bc6hEField.RY, 2), new Bc6hsModeDescriptor(Bc6hEField.RY, 3), new Bc6hsModeDescriptor(Bc6hEField.RY, 4), + new Bc6hsModeDescriptor(Bc6hEField.BZ, 2), new Bc6hsModeDescriptor(Bc6hEField.RZ, 0), new Bc6hsModeDescriptor(Bc6hEField.RZ, 1), new Bc6hsModeDescriptor(Bc6hEField.RZ, 2), new Bc6hsModeDescriptor(Bc6hEField.RZ, 3), new Bc6hsModeDescriptor(Bc6hEField.RZ, 4), new Bc6hsModeDescriptor(Bc6hEField.BZ, 3), new Bc6hsModeDescriptor(Bc6hEField.D, 0), new Bc6hsModeDescriptor(Bc6hEField.D, 1), new Bc6hsModeDescriptor(Bc6hEField.D, 2), + new Bc6hsModeDescriptor(Bc6hEField.D, 3), new Bc6hsModeDescriptor(Bc6hEField.D, 4) + ], + + [ + // Mode 4 (0x06) - 11 4 5 4 + new Bc6hsModeDescriptor(Bc6hEField.M, 0), new Bc6hsModeDescriptor(Bc6hEField.M, 1), new Bc6hsModeDescriptor(Bc6hEField.M, 2), new Bc6hsModeDescriptor(Bc6hEField.M, 3), new Bc6hsModeDescriptor(Bc6hEField.M, 4), new Bc6hsModeDescriptor(Bc6hEField.RW, 0), new Bc6hsModeDescriptor(Bc6hEField.RW, 1), new Bc6hsModeDescriptor(Bc6hEField.RW, 2), new Bc6hsModeDescriptor(Bc6hEField.RW, 3), new Bc6hsModeDescriptor(Bc6hEField.RW, 4), + new Bc6hsModeDescriptor(Bc6hEField.RW, 5), new Bc6hsModeDescriptor(Bc6hEField.RW, 6), new Bc6hsModeDescriptor(Bc6hEField.RW, 7), new Bc6hsModeDescriptor(Bc6hEField.RW, 8), new Bc6hsModeDescriptor(Bc6hEField.RW, 9), new Bc6hsModeDescriptor(Bc6hEField.GW, 0), new Bc6hsModeDescriptor(Bc6hEField.GW, 1), new Bc6hsModeDescriptor(Bc6hEField.GW, 2), new Bc6hsModeDescriptor(Bc6hEField.GW, 3), new Bc6hsModeDescriptor(Bc6hEField.GW, 4), + new Bc6hsModeDescriptor(Bc6hEField.GW, 5), new Bc6hsModeDescriptor(Bc6hEField.GW, 6), new Bc6hsModeDescriptor(Bc6hEField.GW, 7), new Bc6hsModeDescriptor(Bc6hEField.GW, 8), new Bc6hsModeDescriptor(Bc6hEField.GW, 9), new Bc6hsModeDescriptor(Bc6hEField.BW, 0), new Bc6hsModeDescriptor(Bc6hEField.BW, 1), new Bc6hsModeDescriptor(Bc6hEField.BW, 2), new Bc6hsModeDescriptor(Bc6hEField.BW, 3), new Bc6hsModeDescriptor(Bc6hEField.BW, 4), + new Bc6hsModeDescriptor(Bc6hEField.BW, 5), new Bc6hsModeDescriptor(Bc6hEField.BW, 6), new Bc6hsModeDescriptor(Bc6hEField.BW, 7), new Bc6hsModeDescriptor(Bc6hEField.BW, 8), new Bc6hsModeDescriptor(Bc6hEField.BW, 9), new Bc6hsModeDescriptor(Bc6hEField.RX, 0), new Bc6hsModeDescriptor(Bc6hEField.RX, 1), new Bc6hsModeDescriptor(Bc6hEField.RX, 2), new Bc6hsModeDescriptor(Bc6hEField.RX, 3), new Bc6hsModeDescriptor(Bc6hEField.RW, 10), + new Bc6hsModeDescriptor(Bc6hEField.GZ, 4), new Bc6hsModeDescriptor(Bc6hEField.GY, 0), new Bc6hsModeDescriptor(Bc6hEField.GY, 1), new Bc6hsModeDescriptor(Bc6hEField.GY, 2), new Bc6hsModeDescriptor(Bc6hEField.GY, 3), new Bc6hsModeDescriptor(Bc6hEField.GX, 0), new Bc6hsModeDescriptor(Bc6hEField.GX, 1), new Bc6hsModeDescriptor(Bc6hEField.GX, 2), new Bc6hsModeDescriptor(Bc6hEField.GX, 3), new Bc6hsModeDescriptor(Bc6hEField.GX, 4), + new Bc6hsModeDescriptor(Bc6hEField.GW, 10), new Bc6hsModeDescriptor(Bc6hEField.GZ, 0), new Bc6hsModeDescriptor(Bc6hEField.GZ, 1), new Bc6hsModeDescriptor(Bc6hEField.GZ, 2), new Bc6hsModeDescriptor(Bc6hEField.GZ, 3), new Bc6hsModeDescriptor(Bc6hEField.BX, 0), new Bc6hsModeDescriptor(Bc6hEField.BX, 1), new Bc6hsModeDescriptor(Bc6hEField.BX, 2), new Bc6hsModeDescriptor(Bc6hEField.BX, 3), new Bc6hsModeDescriptor(Bc6hEField.BW, 10), + new Bc6hsModeDescriptor(Bc6hEField.BZ, 1), new Bc6hsModeDescriptor(Bc6hEField.BY, 0), new Bc6hsModeDescriptor(Bc6hEField.BY, 1), new Bc6hsModeDescriptor(Bc6hEField.BY, 2), new Bc6hsModeDescriptor(Bc6hEField.BY, 3), new Bc6hsModeDescriptor(Bc6hEField.RY, 0), new Bc6hsModeDescriptor(Bc6hEField.RY, 1), new Bc6hsModeDescriptor(Bc6hEField.RY, 2), new Bc6hsModeDescriptor(Bc6hEField.RY, 3), new Bc6hsModeDescriptor(Bc6hEField.BZ, 0), + new Bc6hsModeDescriptor(Bc6hEField.BZ, 2), new Bc6hsModeDescriptor(Bc6hEField.RZ, 0), new Bc6hsModeDescriptor(Bc6hEField.RZ, 1), new Bc6hsModeDescriptor(Bc6hEField.RZ, 2), new Bc6hsModeDescriptor(Bc6hEField.RZ, 3), new Bc6hsModeDescriptor(Bc6hEField.GY, 4), new Bc6hsModeDescriptor(Bc6hEField.BZ, 3), new Bc6hsModeDescriptor(Bc6hEField.D, 0), new Bc6hsModeDescriptor(Bc6hEField.D, 1), new Bc6hsModeDescriptor(Bc6hEField.D, 2), + new Bc6hsModeDescriptor(Bc6hEField.D, 3), new Bc6hsModeDescriptor(Bc6hEField.D, 4) + ], + + [ + // Mode 5 (0x0a) - 11 4 4 5 + new Bc6hsModeDescriptor(Bc6hEField.M, 0), new Bc6hsModeDescriptor(Bc6hEField.M, 1), new Bc6hsModeDescriptor(Bc6hEField.M, 2), new Bc6hsModeDescriptor(Bc6hEField.M, 3), new Bc6hsModeDescriptor(Bc6hEField.M, 4), new Bc6hsModeDescriptor(Bc6hEField.RW, 0), new Bc6hsModeDescriptor(Bc6hEField.RW, 1), new Bc6hsModeDescriptor(Bc6hEField.RW, 2), new Bc6hsModeDescriptor(Bc6hEField.RW, 3), new Bc6hsModeDescriptor(Bc6hEField.RW, 4), + new Bc6hsModeDescriptor(Bc6hEField.RW, 5), new Bc6hsModeDescriptor(Bc6hEField.RW, 6), new Bc6hsModeDescriptor(Bc6hEField.RW, 7), new Bc6hsModeDescriptor(Bc6hEField.RW, 8), new Bc6hsModeDescriptor(Bc6hEField.RW, 9), new Bc6hsModeDescriptor(Bc6hEField.GW, 0), new Bc6hsModeDescriptor(Bc6hEField.GW, 1), new Bc6hsModeDescriptor(Bc6hEField.GW, 2), new Bc6hsModeDescriptor(Bc6hEField.GW, 3), new Bc6hsModeDescriptor(Bc6hEField.GW, 4), + new Bc6hsModeDescriptor(Bc6hEField.GW, 5), new Bc6hsModeDescriptor(Bc6hEField.GW, 6), new Bc6hsModeDescriptor(Bc6hEField.GW, 7), new Bc6hsModeDescriptor(Bc6hEField.GW, 8), new Bc6hsModeDescriptor(Bc6hEField.GW, 9), new Bc6hsModeDescriptor(Bc6hEField.BW, 0), new Bc6hsModeDescriptor(Bc6hEField.BW, 1), new Bc6hsModeDescriptor(Bc6hEField.BW, 2), new Bc6hsModeDescriptor(Bc6hEField.BW, 3), new Bc6hsModeDescriptor(Bc6hEField.BW, 4), + new Bc6hsModeDescriptor(Bc6hEField.BW, 5), new Bc6hsModeDescriptor(Bc6hEField.BW, 6), new Bc6hsModeDescriptor(Bc6hEField.BW, 7), new Bc6hsModeDescriptor(Bc6hEField.BW, 8), new Bc6hsModeDescriptor(Bc6hEField.BW, 9), new Bc6hsModeDescriptor(Bc6hEField.RX, 0), new Bc6hsModeDescriptor(Bc6hEField.RX, 1), new Bc6hsModeDescriptor(Bc6hEField.RX, 2), new Bc6hsModeDescriptor(Bc6hEField.RX, 3), new Bc6hsModeDescriptor(Bc6hEField.RW, 10), + new Bc6hsModeDescriptor(Bc6hEField.BY, 4), new Bc6hsModeDescriptor(Bc6hEField.GY, 0), new Bc6hsModeDescriptor(Bc6hEField.GY, 1), new Bc6hsModeDescriptor(Bc6hEField.GY, 2), new Bc6hsModeDescriptor(Bc6hEField.GY, 3), new Bc6hsModeDescriptor(Bc6hEField.GX, 0), new Bc6hsModeDescriptor(Bc6hEField.GX, 1), new Bc6hsModeDescriptor(Bc6hEField.GX, 2), new Bc6hsModeDescriptor(Bc6hEField.GX, 3), new Bc6hsModeDescriptor(Bc6hEField.GW, 10), + new Bc6hsModeDescriptor(Bc6hEField.BZ, 0), new Bc6hsModeDescriptor(Bc6hEField.GZ, 0), new Bc6hsModeDescriptor(Bc6hEField.GZ, 1), new Bc6hsModeDescriptor(Bc6hEField.GZ, 2), new Bc6hsModeDescriptor(Bc6hEField.GZ, 3), new Bc6hsModeDescriptor(Bc6hEField.BX, 0), new Bc6hsModeDescriptor(Bc6hEField.BX, 1), new Bc6hsModeDescriptor(Bc6hEField.BX, 2), new Bc6hsModeDescriptor(Bc6hEField.BX, 3), new Bc6hsModeDescriptor(Bc6hEField.BX, 4), + new Bc6hsModeDescriptor(Bc6hEField.BW, 10), new Bc6hsModeDescriptor(Bc6hEField.BY, 0), new Bc6hsModeDescriptor(Bc6hEField.BY, 1), new Bc6hsModeDescriptor(Bc6hEField.BY, 2), new Bc6hsModeDescriptor(Bc6hEField.BY, 3), new Bc6hsModeDescriptor(Bc6hEField.RY, 0), new Bc6hsModeDescriptor(Bc6hEField.RY, 1), new Bc6hsModeDescriptor(Bc6hEField.RY, 2), new Bc6hsModeDescriptor(Bc6hEField.RY, 3), new Bc6hsModeDescriptor(Bc6hEField.BZ, 1), + new Bc6hsModeDescriptor(Bc6hEField.BZ, 2), new Bc6hsModeDescriptor(Bc6hEField.RZ, 0), new Bc6hsModeDescriptor(Bc6hEField.RZ, 1), new Bc6hsModeDescriptor(Bc6hEField.RZ, 2), new Bc6hsModeDescriptor(Bc6hEField.RZ, 3), new Bc6hsModeDescriptor(Bc6hEField.BZ, 4), new Bc6hsModeDescriptor(Bc6hEField.BZ, 3), new Bc6hsModeDescriptor(Bc6hEField.D, 0), new Bc6hsModeDescriptor(Bc6hEField.D, 1), new Bc6hsModeDescriptor(Bc6hEField.D, 2), + new Bc6hsModeDescriptor(Bc6hEField.D, 3), new Bc6hsModeDescriptor(Bc6hEField.D, 4) + ], + + [ + // Mode 6 (0x0e) - 9 5 5 5 + new Bc6hsModeDescriptor(Bc6hEField.M, 0), new Bc6hsModeDescriptor(Bc6hEField.M, 1), new Bc6hsModeDescriptor(Bc6hEField.M, 2), new Bc6hsModeDescriptor(Bc6hEField.M, 3), new Bc6hsModeDescriptor(Bc6hEField.M, 4), new Bc6hsModeDescriptor(Bc6hEField.RW, 0), new Bc6hsModeDescriptor(Bc6hEField.RW, 1), new Bc6hsModeDescriptor(Bc6hEField.RW, 2), new Bc6hsModeDescriptor(Bc6hEField.RW, 3), new Bc6hsModeDescriptor(Bc6hEField.RW, 4), + new Bc6hsModeDescriptor(Bc6hEField.RW, 5), new Bc6hsModeDescriptor(Bc6hEField.RW, 6), new Bc6hsModeDescriptor(Bc6hEField.RW, 7), new Bc6hsModeDescriptor(Bc6hEField.RW, 8), new Bc6hsModeDescriptor(Bc6hEField.BY, 4), new Bc6hsModeDescriptor(Bc6hEField.GW, 0), new Bc6hsModeDescriptor(Bc6hEField.GW, 1), new Bc6hsModeDescriptor(Bc6hEField.GW, 2), new Bc6hsModeDescriptor(Bc6hEField.GW, 3), new Bc6hsModeDescriptor(Bc6hEField.GW, 4), + new Bc6hsModeDescriptor(Bc6hEField.GW, 5), new Bc6hsModeDescriptor(Bc6hEField.GW, 6), new Bc6hsModeDescriptor(Bc6hEField.GW, 7), new Bc6hsModeDescriptor(Bc6hEField.GW, 8), new Bc6hsModeDescriptor(Bc6hEField.GY, 4), new Bc6hsModeDescriptor(Bc6hEField.BW, 0), new Bc6hsModeDescriptor(Bc6hEField.BW, 1), new Bc6hsModeDescriptor(Bc6hEField.BW, 2), new Bc6hsModeDescriptor(Bc6hEField.BW, 3), new Bc6hsModeDescriptor(Bc6hEField.BW, 4), + new Bc6hsModeDescriptor(Bc6hEField.BW, 5), new Bc6hsModeDescriptor(Bc6hEField.BW, 6), new Bc6hsModeDescriptor(Bc6hEField.BW, 7), new Bc6hsModeDescriptor(Bc6hEField.BW, 8), new Bc6hsModeDescriptor(Bc6hEField.BZ, 4), new Bc6hsModeDescriptor(Bc6hEField.RX, 0), new Bc6hsModeDescriptor(Bc6hEField.RX, 1), new Bc6hsModeDescriptor(Bc6hEField.RX, 2), new Bc6hsModeDescriptor(Bc6hEField.RX, 3), new Bc6hsModeDescriptor(Bc6hEField.RX, 4), + new Bc6hsModeDescriptor(Bc6hEField.GZ, 4), new Bc6hsModeDescriptor(Bc6hEField.GY, 0), new Bc6hsModeDescriptor(Bc6hEField.GY, 1), new Bc6hsModeDescriptor(Bc6hEField.GY, 2), new Bc6hsModeDescriptor(Bc6hEField.GY, 3), new Bc6hsModeDescriptor(Bc6hEField.GX, 0), new Bc6hsModeDescriptor(Bc6hEField.GX, 1), new Bc6hsModeDescriptor(Bc6hEField.GX, 2), new Bc6hsModeDescriptor(Bc6hEField.GX, 3), new Bc6hsModeDescriptor(Bc6hEField.GX, 4), + new Bc6hsModeDescriptor(Bc6hEField.BZ, 0), new Bc6hsModeDescriptor(Bc6hEField.GZ, 0), new Bc6hsModeDescriptor(Bc6hEField.GZ, 1), new Bc6hsModeDescriptor(Bc6hEField.GZ, 2), new Bc6hsModeDescriptor(Bc6hEField.GZ, 3), new Bc6hsModeDescriptor(Bc6hEField.BX, 0), new Bc6hsModeDescriptor(Bc6hEField.BX, 1), new Bc6hsModeDescriptor(Bc6hEField.BX, 2), new Bc6hsModeDescriptor(Bc6hEField.BX, 3), new Bc6hsModeDescriptor(Bc6hEField.BX, 4), + new Bc6hsModeDescriptor(Bc6hEField.BZ, 1), new Bc6hsModeDescriptor(Bc6hEField.BY, 0), new Bc6hsModeDescriptor(Bc6hEField.BY, 1), new Bc6hsModeDescriptor(Bc6hEField.BY, 2), new Bc6hsModeDescriptor(Bc6hEField.BY, 3), new Bc6hsModeDescriptor(Bc6hEField.RY, 0), new Bc6hsModeDescriptor(Bc6hEField.RY, 1), new Bc6hsModeDescriptor(Bc6hEField.RY, 2), new Bc6hsModeDescriptor(Bc6hEField.RY, 3), new Bc6hsModeDescriptor(Bc6hEField.RY, 4), + new Bc6hsModeDescriptor(Bc6hEField.BZ, 2), new Bc6hsModeDescriptor(Bc6hEField.RZ, 0), new Bc6hsModeDescriptor(Bc6hEField.RZ, 1), new Bc6hsModeDescriptor(Bc6hEField.RZ, 2), new Bc6hsModeDescriptor(Bc6hEField.RZ, 3), new Bc6hsModeDescriptor(Bc6hEField.RZ, 4), new Bc6hsModeDescriptor(Bc6hEField.BZ, 3), new Bc6hsModeDescriptor(Bc6hEField.D, 0), new Bc6hsModeDescriptor(Bc6hEField.D, 1), new Bc6hsModeDescriptor(Bc6hEField.D, 2), + new Bc6hsModeDescriptor(Bc6hEField.D, 3), new Bc6hsModeDescriptor(Bc6hEField.D, 4) + ], + + [ + // Mode 7 (0x12) - 8 6 5 5 + new Bc6hsModeDescriptor(Bc6hEField.M, 0), new Bc6hsModeDescriptor(Bc6hEField.M, 1), new Bc6hsModeDescriptor(Bc6hEField.M, 2), new Bc6hsModeDescriptor(Bc6hEField.M, 3), new Bc6hsModeDescriptor(Bc6hEField.M, 4), new Bc6hsModeDescriptor(Bc6hEField.RW, 0), new Bc6hsModeDescriptor(Bc6hEField.RW, 1), new Bc6hsModeDescriptor(Bc6hEField.RW, 2), new Bc6hsModeDescriptor(Bc6hEField.RW, 3), new Bc6hsModeDescriptor(Bc6hEField.RW, 4), + new Bc6hsModeDescriptor(Bc6hEField.RW, 5), new Bc6hsModeDescriptor(Bc6hEField.RW, 6), new Bc6hsModeDescriptor(Bc6hEField.RW, 7), new Bc6hsModeDescriptor(Bc6hEField.GZ, 4), new Bc6hsModeDescriptor(Bc6hEField.BY, 4), new Bc6hsModeDescriptor(Bc6hEField.GW, 0), new Bc6hsModeDescriptor(Bc6hEField.GW, 1), new Bc6hsModeDescriptor(Bc6hEField.GW, 2), new Bc6hsModeDescriptor(Bc6hEField.GW, 3), new Bc6hsModeDescriptor(Bc6hEField.GW, 4), + new Bc6hsModeDescriptor(Bc6hEField.GW, 5), new Bc6hsModeDescriptor(Bc6hEField.GW, 6), new Bc6hsModeDescriptor(Bc6hEField.GW, 7), new Bc6hsModeDescriptor(Bc6hEField.BZ, 2), new Bc6hsModeDescriptor(Bc6hEField.GY, 4), new Bc6hsModeDescriptor(Bc6hEField.BW, 0), new Bc6hsModeDescriptor(Bc6hEField.BW, 1), new Bc6hsModeDescriptor(Bc6hEField.BW, 2), new Bc6hsModeDescriptor(Bc6hEField.BW, 3), new Bc6hsModeDescriptor(Bc6hEField.BW, 4), + new Bc6hsModeDescriptor(Bc6hEField.BW, 5), new Bc6hsModeDescriptor(Bc6hEField.BW, 6), new Bc6hsModeDescriptor(Bc6hEField.BW, 7), new Bc6hsModeDescriptor(Bc6hEField.BZ, 3), new Bc6hsModeDescriptor(Bc6hEField.BZ, 4), new Bc6hsModeDescriptor(Bc6hEField.RX, 0), new Bc6hsModeDescriptor(Bc6hEField.RX, 1), new Bc6hsModeDescriptor(Bc6hEField.RX, 2), new Bc6hsModeDescriptor(Bc6hEField.RX, 3), new Bc6hsModeDescriptor(Bc6hEField.RX, 4), + new Bc6hsModeDescriptor(Bc6hEField.RX, 5), new Bc6hsModeDescriptor(Bc6hEField.GY, 0), new Bc6hsModeDescriptor(Bc6hEField.GY, 1), new Bc6hsModeDescriptor(Bc6hEField.GY, 2), new Bc6hsModeDescriptor(Bc6hEField.GY, 3), new Bc6hsModeDescriptor(Bc6hEField.GX, 0), new Bc6hsModeDescriptor(Bc6hEField.GX, 1), new Bc6hsModeDescriptor(Bc6hEField.GX, 2), new Bc6hsModeDescriptor(Bc6hEField.GX, 3), new Bc6hsModeDescriptor(Bc6hEField.GX, 4), + new Bc6hsModeDescriptor(Bc6hEField.BZ, 0), new Bc6hsModeDescriptor(Bc6hEField.GZ, 0), new Bc6hsModeDescriptor(Bc6hEField.GZ, 1), new Bc6hsModeDescriptor(Bc6hEField.GZ, 2), new Bc6hsModeDescriptor(Bc6hEField.GZ, 3), new Bc6hsModeDescriptor(Bc6hEField.BX, 0), new Bc6hsModeDescriptor(Bc6hEField.BX, 1), new Bc6hsModeDescriptor(Bc6hEField.BX, 2), new Bc6hsModeDescriptor(Bc6hEField.BX, 3), new Bc6hsModeDescriptor(Bc6hEField.BX, 4), + new Bc6hsModeDescriptor(Bc6hEField.BZ, 1), new Bc6hsModeDescriptor(Bc6hEField.BY, 0), new Bc6hsModeDescriptor(Bc6hEField.BY, 1), new Bc6hsModeDescriptor(Bc6hEField.BY, 2), new Bc6hsModeDescriptor(Bc6hEField.BY, 3), new Bc6hsModeDescriptor(Bc6hEField.RY, 0), new Bc6hsModeDescriptor(Bc6hEField.RY, 1), new Bc6hsModeDescriptor(Bc6hEField.RY, 2), new Bc6hsModeDescriptor(Bc6hEField.RY, 3), new Bc6hsModeDescriptor(Bc6hEField.RY, 4), + new Bc6hsModeDescriptor(Bc6hEField.RY, 5), new Bc6hsModeDescriptor(Bc6hEField.RZ, 0), new Bc6hsModeDescriptor(Bc6hEField.RZ, 1), new Bc6hsModeDescriptor(Bc6hEField.RZ, 2), new Bc6hsModeDescriptor(Bc6hEField.RZ, 3), new Bc6hsModeDescriptor(Bc6hEField.RZ, 4), new Bc6hsModeDescriptor(Bc6hEField.RZ, 5), new Bc6hsModeDescriptor(Bc6hEField.D, 0), new Bc6hsModeDescriptor(Bc6hEField.D, 1), new Bc6hsModeDescriptor(Bc6hEField.D, 2), + new Bc6hsModeDescriptor(Bc6hEField.D, 3), new Bc6hsModeDescriptor(Bc6hEField.D, 4) + ], + + [ + // Mode 8 (0x16) - 8 5 6 5 + new Bc6hsModeDescriptor(Bc6hEField.M, 0), new Bc6hsModeDescriptor(Bc6hEField.M, 1), new Bc6hsModeDescriptor(Bc6hEField.M, 2), new Bc6hsModeDescriptor(Bc6hEField.M, 3), new Bc6hsModeDescriptor(Bc6hEField.M, 4), new Bc6hsModeDescriptor(Bc6hEField.RW, 0), new Bc6hsModeDescriptor(Bc6hEField.RW, 1), new Bc6hsModeDescriptor(Bc6hEField.RW, 2), new Bc6hsModeDescriptor(Bc6hEField.RW, 3), new Bc6hsModeDescriptor(Bc6hEField.RW, 4), + new Bc6hsModeDescriptor(Bc6hEField.RW, 5), new Bc6hsModeDescriptor(Bc6hEField.RW, 6), new Bc6hsModeDescriptor(Bc6hEField.RW, 7), new Bc6hsModeDescriptor(Bc6hEField.BZ, 0), new Bc6hsModeDescriptor(Bc6hEField.BY, 4), new Bc6hsModeDescriptor(Bc6hEField.GW, 0), new Bc6hsModeDescriptor(Bc6hEField.GW, 1), new Bc6hsModeDescriptor(Bc6hEField.GW, 2), new Bc6hsModeDescriptor(Bc6hEField.GW, 3), new Bc6hsModeDescriptor(Bc6hEField.GW, 4), + new Bc6hsModeDescriptor(Bc6hEField.GW, 5), new Bc6hsModeDescriptor(Bc6hEField.GW, 6), new Bc6hsModeDescriptor(Bc6hEField.GW, 7), new Bc6hsModeDescriptor(Bc6hEField.GY, 5), new Bc6hsModeDescriptor(Bc6hEField.GY, 4), new Bc6hsModeDescriptor(Bc6hEField.BW, 0), new Bc6hsModeDescriptor(Bc6hEField.BW, 1), new Bc6hsModeDescriptor(Bc6hEField.BW, 2), new Bc6hsModeDescriptor(Bc6hEField.BW, 3), new Bc6hsModeDescriptor(Bc6hEField.BW, 4), + new Bc6hsModeDescriptor(Bc6hEField.BW, 5), new Bc6hsModeDescriptor(Bc6hEField.BW, 6), new Bc6hsModeDescriptor(Bc6hEField.BW, 7), new Bc6hsModeDescriptor(Bc6hEField.GZ, 5), new Bc6hsModeDescriptor(Bc6hEField.BZ, 4), new Bc6hsModeDescriptor(Bc6hEField.RX, 0), new Bc6hsModeDescriptor(Bc6hEField.RX, 1), new Bc6hsModeDescriptor(Bc6hEField.RX, 2), new Bc6hsModeDescriptor(Bc6hEField.RX, 3), new Bc6hsModeDescriptor(Bc6hEField.RX, 4), + new Bc6hsModeDescriptor(Bc6hEField.GZ, 4), new Bc6hsModeDescriptor(Bc6hEField.GY, 0), new Bc6hsModeDescriptor(Bc6hEField.GY, 1), new Bc6hsModeDescriptor(Bc6hEField.GY, 2), new Bc6hsModeDescriptor(Bc6hEField.GY, 3), new Bc6hsModeDescriptor(Bc6hEField.GX, 0), new Bc6hsModeDescriptor(Bc6hEField.GX, 1), new Bc6hsModeDescriptor(Bc6hEField.GX, 2), new Bc6hsModeDescriptor(Bc6hEField.GX, 3), new Bc6hsModeDescriptor(Bc6hEField.GX, 4), + new Bc6hsModeDescriptor(Bc6hEField.GX, 5), new Bc6hsModeDescriptor(Bc6hEField.GZ, 0), new Bc6hsModeDescriptor(Bc6hEField.GZ, 1), new Bc6hsModeDescriptor(Bc6hEField.GZ, 2), new Bc6hsModeDescriptor(Bc6hEField.GZ, 3), new Bc6hsModeDescriptor(Bc6hEField.BX, 0), new Bc6hsModeDescriptor(Bc6hEField.BX, 1), new Bc6hsModeDescriptor(Bc6hEField.BX, 2), new Bc6hsModeDescriptor(Bc6hEField.BX, 3), new Bc6hsModeDescriptor(Bc6hEField.BX, 4), + new Bc6hsModeDescriptor(Bc6hEField.BZ, 1), new Bc6hsModeDescriptor(Bc6hEField.BY, 0), new Bc6hsModeDescriptor(Bc6hEField.BY, 1), new Bc6hsModeDescriptor(Bc6hEField.BY, 2), new Bc6hsModeDescriptor(Bc6hEField.BY, 3), new Bc6hsModeDescriptor(Bc6hEField.RY, 0), new Bc6hsModeDescriptor(Bc6hEField.RY, 1), new Bc6hsModeDescriptor(Bc6hEField.RY, 2), new Bc6hsModeDescriptor(Bc6hEField.RY, 3), new Bc6hsModeDescriptor(Bc6hEField.RY, 4), + new Bc6hsModeDescriptor(Bc6hEField.BZ, 2), new Bc6hsModeDescriptor(Bc6hEField.RZ, 0), new Bc6hsModeDescriptor(Bc6hEField.RZ, 1), new Bc6hsModeDescriptor(Bc6hEField.RZ, 2), new Bc6hsModeDescriptor(Bc6hEField.RZ, 3), new Bc6hsModeDescriptor(Bc6hEField.RZ, 4), new Bc6hsModeDescriptor(Bc6hEField.BZ, 3), new Bc6hsModeDescriptor(Bc6hEField.D, 0), new Bc6hsModeDescriptor(Bc6hEField.D, 1), new Bc6hsModeDescriptor(Bc6hEField.D, 2), + new Bc6hsModeDescriptor(Bc6hEField.D, 3), new Bc6hsModeDescriptor(Bc6hEField.D, 4) + ], + + [ + // Mode 9 (0x1a) - 8 5 5 6 + new Bc6hsModeDescriptor(Bc6hEField.M, 0), new Bc6hsModeDescriptor(Bc6hEField.M, 1), new Bc6hsModeDescriptor(Bc6hEField.M, 2), new Bc6hsModeDescriptor(Bc6hEField.M, 3), new Bc6hsModeDescriptor(Bc6hEField.M, 4), new Bc6hsModeDescriptor(Bc6hEField.RW, 0), new Bc6hsModeDescriptor(Bc6hEField.RW, 1), new Bc6hsModeDescriptor(Bc6hEField.RW, 2), new Bc6hsModeDescriptor(Bc6hEField.RW, 3), new Bc6hsModeDescriptor(Bc6hEField.RW, 4), + new Bc6hsModeDescriptor(Bc6hEField.RW, 5), new Bc6hsModeDescriptor(Bc6hEField.RW, 6), new Bc6hsModeDescriptor(Bc6hEField.RW, 7), new Bc6hsModeDescriptor(Bc6hEField.BZ, 1), new Bc6hsModeDescriptor(Bc6hEField.BY, 4), new Bc6hsModeDescriptor(Bc6hEField.GW, 0), new Bc6hsModeDescriptor(Bc6hEField.GW, 1), new Bc6hsModeDescriptor(Bc6hEField.GW, 2), new Bc6hsModeDescriptor(Bc6hEField.GW, 3), new Bc6hsModeDescriptor(Bc6hEField.GW, 4), + new Bc6hsModeDescriptor(Bc6hEField.GW, 5), new Bc6hsModeDescriptor(Bc6hEField.GW, 6), new Bc6hsModeDescriptor(Bc6hEField.GW, 7), new Bc6hsModeDescriptor(Bc6hEField.BY, 5), new Bc6hsModeDescriptor(Bc6hEField.GY, 4), new Bc6hsModeDescriptor(Bc6hEField.BW, 0), new Bc6hsModeDescriptor(Bc6hEField.BW, 1), new Bc6hsModeDescriptor(Bc6hEField.BW, 2), new Bc6hsModeDescriptor(Bc6hEField.BW, 3), new Bc6hsModeDescriptor(Bc6hEField.BW, 4), + new Bc6hsModeDescriptor(Bc6hEField.BW, 5), new Bc6hsModeDescriptor(Bc6hEField.BW, 6), new Bc6hsModeDescriptor(Bc6hEField.BW, 7), new Bc6hsModeDescriptor(Bc6hEField.BZ, 5), new Bc6hsModeDescriptor(Bc6hEField.BZ, 4), new Bc6hsModeDescriptor(Bc6hEField.RX, 0), new Bc6hsModeDescriptor(Bc6hEField.RX, 1), new Bc6hsModeDescriptor(Bc6hEField.RX, 2), new Bc6hsModeDescriptor(Bc6hEField.RX, 3), new Bc6hsModeDescriptor(Bc6hEField.RX, 4), + new Bc6hsModeDescriptor(Bc6hEField.GZ, 4), new Bc6hsModeDescriptor(Bc6hEField.GY, 0), new Bc6hsModeDescriptor(Bc6hEField.GY, 1), new Bc6hsModeDescriptor(Bc6hEField.GY, 2), new Bc6hsModeDescriptor(Bc6hEField.GY, 3), new Bc6hsModeDescriptor(Bc6hEField.GX, 0), new Bc6hsModeDescriptor(Bc6hEField.GX, 1), new Bc6hsModeDescriptor(Bc6hEField.GX, 2), new Bc6hsModeDescriptor(Bc6hEField.GX, 3), new Bc6hsModeDescriptor(Bc6hEField.GX, 4), + new Bc6hsModeDescriptor(Bc6hEField.BZ, 0), new Bc6hsModeDescriptor(Bc6hEField.GZ, 0), new Bc6hsModeDescriptor(Bc6hEField.GZ, 1), new Bc6hsModeDescriptor(Bc6hEField.GZ, 2), new Bc6hsModeDescriptor(Bc6hEField.GZ, 3), new Bc6hsModeDescriptor(Bc6hEField.BX, 0), new Bc6hsModeDescriptor(Bc6hEField.BX, 1), new Bc6hsModeDescriptor(Bc6hEField.BX, 2), new Bc6hsModeDescriptor(Bc6hEField.BX, 3), new Bc6hsModeDescriptor(Bc6hEField.BX, 4), + new Bc6hsModeDescriptor(Bc6hEField.BX, 5), new Bc6hsModeDescriptor(Bc6hEField.BY, 0), new Bc6hsModeDescriptor(Bc6hEField.BY, 1), new Bc6hsModeDescriptor(Bc6hEField.BY, 2), new Bc6hsModeDescriptor(Bc6hEField.BY, 3), new Bc6hsModeDescriptor(Bc6hEField.RY, 0), new Bc6hsModeDescriptor(Bc6hEField.RY, 1), new Bc6hsModeDescriptor(Bc6hEField.RY, 2), new Bc6hsModeDescriptor(Bc6hEField.RY, 3), new Bc6hsModeDescriptor(Bc6hEField.RY, 4), + new Bc6hsModeDescriptor(Bc6hEField.BZ, 2), new Bc6hsModeDescriptor(Bc6hEField.RZ, 0), new Bc6hsModeDescriptor(Bc6hEField.RZ, 1), new Bc6hsModeDescriptor(Bc6hEField.RZ, 2), new Bc6hsModeDescriptor(Bc6hEField.RZ, 3), new Bc6hsModeDescriptor(Bc6hEField.RZ, 4), new Bc6hsModeDescriptor(Bc6hEField.BZ, 3), new Bc6hsModeDescriptor(Bc6hEField.D, 0), new Bc6hsModeDescriptor(Bc6hEField.D, 1), new Bc6hsModeDescriptor(Bc6hEField.D, 2), + new Bc6hsModeDescriptor(Bc6hEField.D, 3), new Bc6hsModeDescriptor(Bc6hEField.D, 4) + ], + + [ + // Mode 10 (0x1e) - 6 6 6 6 + new Bc6hsModeDescriptor(Bc6hEField.M, 0), new Bc6hsModeDescriptor(Bc6hEField.M, 1), new Bc6hsModeDescriptor(Bc6hEField.M, 2), new Bc6hsModeDescriptor(Bc6hEField.M, 3), new Bc6hsModeDescriptor(Bc6hEField.M, 4), new Bc6hsModeDescriptor(Bc6hEField.RW, 0), new Bc6hsModeDescriptor(Bc6hEField.RW, 1), new Bc6hsModeDescriptor(Bc6hEField.RW, 2), new Bc6hsModeDescriptor(Bc6hEField.RW, 3), new Bc6hsModeDescriptor(Bc6hEField.RW, 4), + new Bc6hsModeDescriptor(Bc6hEField.RW, 5), new Bc6hsModeDescriptor(Bc6hEField.GZ, 4), new Bc6hsModeDescriptor(Bc6hEField.BZ, 0), new Bc6hsModeDescriptor(Bc6hEField.BZ, 1), new Bc6hsModeDescriptor(Bc6hEField.BY, 4), new Bc6hsModeDescriptor(Bc6hEField.GW, 0), new Bc6hsModeDescriptor(Bc6hEField.GW, 1), new Bc6hsModeDescriptor(Bc6hEField.GW, 2), new Bc6hsModeDescriptor(Bc6hEField.GW, 3), new Bc6hsModeDescriptor(Bc6hEField.GW, 4), + new Bc6hsModeDescriptor(Bc6hEField.GW, 5), new Bc6hsModeDescriptor(Bc6hEField.GY, 5), new Bc6hsModeDescriptor(Bc6hEField.BY, 5), new Bc6hsModeDescriptor(Bc6hEField.BZ, 2), new Bc6hsModeDescriptor(Bc6hEField.GY, 4), new Bc6hsModeDescriptor(Bc6hEField.BW, 0), new Bc6hsModeDescriptor(Bc6hEField.BW, 1), new Bc6hsModeDescriptor(Bc6hEField.BW, 2), new Bc6hsModeDescriptor(Bc6hEField.BW, 3), new Bc6hsModeDescriptor(Bc6hEField.BW, 4), + new Bc6hsModeDescriptor(Bc6hEField.BW, 5), new Bc6hsModeDescriptor(Bc6hEField.GZ, 5), new Bc6hsModeDescriptor(Bc6hEField.BZ, 3), new Bc6hsModeDescriptor(Bc6hEField.BZ, 5), new Bc6hsModeDescriptor(Bc6hEField.BZ, 4), new Bc6hsModeDescriptor(Bc6hEField.RX, 0), new Bc6hsModeDescriptor(Bc6hEField.RX, 1), new Bc6hsModeDescriptor(Bc6hEField.RX, 2), new Bc6hsModeDescriptor(Bc6hEField.RX, 3), new Bc6hsModeDescriptor(Bc6hEField.RX, 4), + new Bc6hsModeDescriptor(Bc6hEField.RX, 5), new Bc6hsModeDescriptor(Bc6hEField.GY, 0), new Bc6hsModeDescriptor(Bc6hEField.GY, 1), new Bc6hsModeDescriptor(Bc6hEField.GY, 2), new Bc6hsModeDescriptor(Bc6hEField.GY, 3), new Bc6hsModeDescriptor(Bc6hEField.GX, 0), new Bc6hsModeDescriptor(Bc6hEField.GX, 1), new Bc6hsModeDescriptor(Bc6hEField.GX, 2), new Bc6hsModeDescriptor(Bc6hEField.GX, 3), new Bc6hsModeDescriptor(Bc6hEField.GX, 4), + new Bc6hsModeDescriptor(Bc6hEField.GX, 5), new Bc6hsModeDescriptor(Bc6hEField.GZ, 0), new Bc6hsModeDescriptor(Bc6hEField.GZ, 1), new Bc6hsModeDescriptor(Bc6hEField.GZ, 2), new Bc6hsModeDescriptor(Bc6hEField.GZ, 3), new Bc6hsModeDescriptor(Bc6hEField.BX, 0), new Bc6hsModeDescriptor(Bc6hEField.BX, 1), new Bc6hsModeDescriptor(Bc6hEField.BX, 2), new Bc6hsModeDescriptor(Bc6hEField.BX, 3), new Bc6hsModeDescriptor(Bc6hEField.BX, 4), + new Bc6hsModeDescriptor(Bc6hEField.BX, 5), new Bc6hsModeDescriptor(Bc6hEField.BY, 0), new Bc6hsModeDescriptor(Bc6hEField.BY, 1), new Bc6hsModeDescriptor(Bc6hEField.BY, 2), new Bc6hsModeDescriptor(Bc6hEField.BY, 3), new Bc6hsModeDescriptor(Bc6hEField.RY, 0), new Bc6hsModeDescriptor(Bc6hEField.RY, 1), new Bc6hsModeDescriptor(Bc6hEField.RY, 2), new Bc6hsModeDescriptor(Bc6hEField.RY, 3), new Bc6hsModeDescriptor(Bc6hEField.RY, 4), + new Bc6hsModeDescriptor(Bc6hEField.RY, 5), new Bc6hsModeDescriptor(Bc6hEField.RZ, 0), new Bc6hsModeDescriptor(Bc6hEField.RZ, 1), new Bc6hsModeDescriptor(Bc6hEField.RZ, 2), new Bc6hsModeDescriptor(Bc6hEField.RZ, 3), new Bc6hsModeDescriptor(Bc6hEField.RZ, 4), new Bc6hsModeDescriptor(Bc6hEField.RZ, 5), new Bc6hsModeDescriptor(Bc6hEField.D, 0), new Bc6hsModeDescriptor(Bc6hEField.D, 1), new Bc6hsModeDescriptor(Bc6hEField.D, 2), + new Bc6hsModeDescriptor(Bc6hEField.D, 3), new Bc6hsModeDescriptor(Bc6hEField.D, 4) + ], + + [ + // Mode 11 (0x03) - 10 10 + new Bc6hsModeDescriptor(Bc6hEField.M, 0), new Bc6hsModeDescriptor(Bc6hEField.M, 1), new Bc6hsModeDescriptor(Bc6hEField.M, 2), new Bc6hsModeDescriptor(Bc6hEField.M, 3), new Bc6hsModeDescriptor(Bc6hEField.M, 4), new Bc6hsModeDescriptor(Bc6hEField.RW, 0), new Bc6hsModeDescriptor(Bc6hEField.RW, 1), new Bc6hsModeDescriptor(Bc6hEField.RW, 2), new Bc6hsModeDescriptor(Bc6hEField.RW, 3), new Bc6hsModeDescriptor(Bc6hEField.RW, 4), + new Bc6hsModeDescriptor(Bc6hEField.RW, 5), new Bc6hsModeDescriptor(Bc6hEField.RW, 6), new Bc6hsModeDescriptor(Bc6hEField.RW, 7), new Bc6hsModeDescriptor(Bc6hEField.RW, 8), new Bc6hsModeDescriptor(Bc6hEField.RW, 9), new Bc6hsModeDescriptor(Bc6hEField.GW, 0), new Bc6hsModeDescriptor(Bc6hEField.GW, 1), new Bc6hsModeDescriptor(Bc6hEField.GW, 2), new Bc6hsModeDescriptor(Bc6hEField.GW, 3), new Bc6hsModeDescriptor(Bc6hEField.GW, 4), + new Bc6hsModeDescriptor(Bc6hEField.GW, 5), new Bc6hsModeDescriptor(Bc6hEField.GW, 6), new Bc6hsModeDescriptor(Bc6hEField.GW, 7), new Bc6hsModeDescriptor(Bc6hEField.GW, 8), new Bc6hsModeDescriptor(Bc6hEField.GW, 9), new Bc6hsModeDescriptor(Bc6hEField.BW, 0), new Bc6hsModeDescriptor(Bc6hEField.BW, 1), new Bc6hsModeDescriptor(Bc6hEField.BW, 2), new Bc6hsModeDescriptor(Bc6hEField.BW, 3), new Bc6hsModeDescriptor(Bc6hEField.BW, 4), + new Bc6hsModeDescriptor(Bc6hEField.BW, 5), new Bc6hsModeDescriptor(Bc6hEField.BW, 6), new Bc6hsModeDescriptor(Bc6hEField.BW, 7), new Bc6hsModeDescriptor(Bc6hEField.BW, 8), new Bc6hsModeDescriptor(Bc6hEField.BW, 9), new Bc6hsModeDescriptor(Bc6hEField.RX, 0), new Bc6hsModeDescriptor(Bc6hEField.RX, 1), new Bc6hsModeDescriptor(Bc6hEField.RX, 2), new Bc6hsModeDescriptor(Bc6hEField.RX, 3), new Bc6hsModeDescriptor(Bc6hEField.RX, 4), + new Bc6hsModeDescriptor(Bc6hEField.RX, 5), new Bc6hsModeDescriptor(Bc6hEField.RX, 6), new Bc6hsModeDescriptor(Bc6hEField.RX, 7), new Bc6hsModeDescriptor(Bc6hEField.RX, 8), new Bc6hsModeDescriptor(Bc6hEField.RX, 9), new Bc6hsModeDescriptor(Bc6hEField.GX, 0), new Bc6hsModeDescriptor(Bc6hEField.GX, 1), new Bc6hsModeDescriptor(Bc6hEField.GX, 2), new Bc6hsModeDescriptor(Bc6hEField.GX, 3), new Bc6hsModeDescriptor(Bc6hEField.GX, 4), + new Bc6hsModeDescriptor(Bc6hEField.GX, 5), new Bc6hsModeDescriptor(Bc6hEField.GX, 6), new Bc6hsModeDescriptor(Bc6hEField.GX, 7), new Bc6hsModeDescriptor(Bc6hEField.GX, 8), new Bc6hsModeDescriptor(Bc6hEField.GX, 9), new Bc6hsModeDescriptor(Bc6hEField.BX, 0), new Bc6hsModeDescriptor(Bc6hEField.BX, 1), new Bc6hsModeDescriptor(Bc6hEField.BX, 2), new Bc6hsModeDescriptor(Bc6hEField.BX, 3), new Bc6hsModeDescriptor(Bc6hEField.BX, 4), + new Bc6hsModeDescriptor(Bc6hEField.BX, 5), new Bc6hsModeDescriptor(Bc6hEField.BX, 6), new Bc6hsModeDescriptor(Bc6hEField.BX, 7), new Bc6hsModeDescriptor(Bc6hEField.BX, 8), new Bc6hsModeDescriptor(Bc6hEField.BX, 9), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), + new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), + new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0) + ], + + [ + // Mode 12 (0x07) - 11 9 + new Bc6hsModeDescriptor(Bc6hEField.M, 0), new Bc6hsModeDescriptor(Bc6hEField.M, 1), new Bc6hsModeDescriptor(Bc6hEField.M, 2), new Bc6hsModeDescriptor(Bc6hEField.M, 3), new Bc6hsModeDescriptor(Bc6hEField.M, 4), new Bc6hsModeDescriptor(Bc6hEField.RW, 0), new Bc6hsModeDescriptor(Bc6hEField.RW, 1), new Bc6hsModeDescriptor(Bc6hEField.RW, 2), new Bc6hsModeDescriptor(Bc6hEField.RW, 3), new Bc6hsModeDescriptor(Bc6hEField.RW, 4), + new Bc6hsModeDescriptor(Bc6hEField.RW, 5), new Bc6hsModeDescriptor(Bc6hEField.RW, 6), new Bc6hsModeDescriptor(Bc6hEField.RW, 7), new Bc6hsModeDescriptor(Bc6hEField.RW, 8), new Bc6hsModeDescriptor(Bc6hEField.RW, 9), new Bc6hsModeDescriptor(Bc6hEField.GW, 0), new Bc6hsModeDescriptor(Bc6hEField.GW, 1), new Bc6hsModeDescriptor(Bc6hEField.GW, 2), new Bc6hsModeDescriptor(Bc6hEField.GW, 3), new Bc6hsModeDescriptor(Bc6hEField.GW, 4), + new Bc6hsModeDescriptor(Bc6hEField.GW, 5), new Bc6hsModeDescriptor(Bc6hEField.GW, 6), new Bc6hsModeDescriptor(Bc6hEField.GW, 7), new Bc6hsModeDescriptor(Bc6hEField.GW, 8), new Bc6hsModeDescriptor(Bc6hEField.GW, 9), new Bc6hsModeDescriptor(Bc6hEField.BW, 0), new Bc6hsModeDescriptor(Bc6hEField.BW, 1), new Bc6hsModeDescriptor(Bc6hEField.BW, 2), new Bc6hsModeDescriptor(Bc6hEField.BW, 3), new Bc6hsModeDescriptor(Bc6hEField.BW, 4), + new Bc6hsModeDescriptor(Bc6hEField.BW, 5), new Bc6hsModeDescriptor(Bc6hEField.BW, 6), new Bc6hsModeDescriptor(Bc6hEField.BW, 7), new Bc6hsModeDescriptor(Bc6hEField.BW, 8), new Bc6hsModeDescriptor(Bc6hEField.BW, 9), new Bc6hsModeDescriptor(Bc6hEField.RX, 0), new Bc6hsModeDescriptor(Bc6hEField.RX, 1), new Bc6hsModeDescriptor(Bc6hEField.RX, 2), new Bc6hsModeDescriptor(Bc6hEField.RX, 3), new Bc6hsModeDescriptor(Bc6hEField.RX, 4), + new Bc6hsModeDescriptor(Bc6hEField.RX, 5), new Bc6hsModeDescriptor(Bc6hEField.RX, 6), new Bc6hsModeDescriptor(Bc6hEField.RX, 7), new Bc6hsModeDescriptor(Bc6hEField.RX, 8), new Bc6hsModeDescriptor(Bc6hEField.RW, 10), new Bc6hsModeDescriptor(Bc6hEField.GX, 0), new Bc6hsModeDescriptor(Bc6hEField.GX, 1), new Bc6hsModeDescriptor(Bc6hEField.GX, 2), new Bc6hsModeDescriptor(Bc6hEField.GX, 3), new Bc6hsModeDescriptor(Bc6hEField.GX, 4), + new Bc6hsModeDescriptor(Bc6hEField.GX, 5), new Bc6hsModeDescriptor(Bc6hEField.GX, 6), new Bc6hsModeDescriptor(Bc6hEField.GX, 7), new Bc6hsModeDescriptor(Bc6hEField.GX, 8), new Bc6hsModeDescriptor(Bc6hEField.GW, 10), new Bc6hsModeDescriptor(Bc6hEField.BX, 0), new Bc6hsModeDescriptor(Bc6hEField.BX, 1), new Bc6hsModeDescriptor(Bc6hEField.BX, 2), new Bc6hsModeDescriptor(Bc6hEField.BX, 3), new Bc6hsModeDescriptor(Bc6hEField.BX, 4), + new Bc6hsModeDescriptor(Bc6hEField.BX, 5), new Bc6hsModeDescriptor(Bc6hEField.BX, 6), new Bc6hsModeDescriptor(Bc6hEField.BX, 7), new Bc6hsModeDescriptor(Bc6hEField.BX, 8), new Bc6hsModeDescriptor(Bc6hEField.BW, 10), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), + new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), + new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0) + ], + + [ + // Mode 13 (0x0b) - 12 8 + new Bc6hsModeDescriptor(Bc6hEField.M, 0), new Bc6hsModeDescriptor(Bc6hEField.M, 1), new Bc6hsModeDescriptor(Bc6hEField.M, 2), new Bc6hsModeDescriptor(Bc6hEField.M, 3), new Bc6hsModeDescriptor(Bc6hEField.M, 4), new Bc6hsModeDescriptor(Bc6hEField.RW, 0), new Bc6hsModeDescriptor(Bc6hEField.RW, 1), new Bc6hsModeDescriptor(Bc6hEField.RW, 2), new Bc6hsModeDescriptor(Bc6hEField.RW, 3), new Bc6hsModeDescriptor(Bc6hEField.RW, 4), + new Bc6hsModeDescriptor(Bc6hEField.RW, 5), new Bc6hsModeDescriptor(Bc6hEField.RW, 6), new Bc6hsModeDescriptor(Bc6hEField.RW, 7), new Bc6hsModeDescriptor(Bc6hEField.RW, 8), new Bc6hsModeDescriptor(Bc6hEField.RW, 9), new Bc6hsModeDescriptor(Bc6hEField.GW, 0), new Bc6hsModeDescriptor(Bc6hEField.GW, 1), new Bc6hsModeDescriptor(Bc6hEField.GW, 2), new Bc6hsModeDescriptor(Bc6hEField.GW, 3), new Bc6hsModeDescriptor(Bc6hEField.GW, 4), + new Bc6hsModeDescriptor(Bc6hEField.GW, 5), new Bc6hsModeDescriptor(Bc6hEField.GW, 6), new Bc6hsModeDescriptor(Bc6hEField.GW, 7), new Bc6hsModeDescriptor(Bc6hEField.GW, 8), new Bc6hsModeDescriptor(Bc6hEField.GW, 9), new Bc6hsModeDescriptor(Bc6hEField.BW, 0), new Bc6hsModeDescriptor(Bc6hEField.BW, 1), new Bc6hsModeDescriptor(Bc6hEField.BW, 2), new Bc6hsModeDescriptor(Bc6hEField.BW, 3), new Bc6hsModeDescriptor(Bc6hEField.BW, 4), + new Bc6hsModeDescriptor(Bc6hEField.BW, 5), new Bc6hsModeDescriptor(Bc6hEField.BW, 6), new Bc6hsModeDescriptor(Bc6hEField.BW, 7), new Bc6hsModeDescriptor(Bc6hEField.BW, 8), new Bc6hsModeDescriptor(Bc6hEField.BW, 9), new Bc6hsModeDescriptor(Bc6hEField.RX, 0), new Bc6hsModeDescriptor(Bc6hEField.RX, 1), new Bc6hsModeDescriptor(Bc6hEField.RX, 2), new Bc6hsModeDescriptor(Bc6hEField.RX, 3), new Bc6hsModeDescriptor(Bc6hEField.RX, 4), + new Bc6hsModeDescriptor(Bc6hEField.RX, 5), new Bc6hsModeDescriptor(Bc6hEField.RX, 6), new Bc6hsModeDescriptor(Bc6hEField.RX, 7), new Bc6hsModeDescriptor(Bc6hEField.RW, 11), new Bc6hsModeDescriptor(Bc6hEField.RW, 10), new Bc6hsModeDescriptor(Bc6hEField.GX, 0), new Bc6hsModeDescriptor(Bc6hEField.GX, 1), new Bc6hsModeDescriptor(Bc6hEField.GX, 2), new Bc6hsModeDescriptor(Bc6hEField.GX, 3), new Bc6hsModeDescriptor(Bc6hEField.GX, 4), + new Bc6hsModeDescriptor(Bc6hEField.GX, 5), new Bc6hsModeDescriptor(Bc6hEField.GX, 6), new Bc6hsModeDescriptor(Bc6hEField.GX, 7), new Bc6hsModeDescriptor(Bc6hEField.GW, 11), new Bc6hsModeDescriptor(Bc6hEField.GW, 10), new Bc6hsModeDescriptor(Bc6hEField.BX, 0), new Bc6hsModeDescriptor(Bc6hEField.BX, 1), new Bc6hsModeDescriptor(Bc6hEField.BX, 2), new Bc6hsModeDescriptor(Bc6hEField.BX, 3), new Bc6hsModeDescriptor(Bc6hEField.BX, 4), + new Bc6hsModeDescriptor(Bc6hEField.BX, 5), new Bc6hsModeDescriptor(Bc6hEField.BX, 6), new Bc6hsModeDescriptor(Bc6hEField.BX, 7), new Bc6hsModeDescriptor(Bc6hEField.BW, 11), new Bc6hsModeDescriptor(Bc6hEField.BW, 10), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), + new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), + new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0) + ], + + [ + // Mode 14 (0x0f) - 16 4 + new Bc6hsModeDescriptor(Bc6hEField.M, 0), new Bc6hsModeDescriptor(Bc6hEField.M, 1), new Bc6hsModeDescriptor(Bc6hEField.M, 2), new Bc6hsModeDescriptor(Bc6hEField.M, 3), new Bc6hsModeDescriptor(Bc6hEField.M, 4), new Bc6hsModeDescriptor(Bc6hEField.RW, 0), new Bc6hsModeDescriptor(Bc6hEField.RW, 1), new Bc6hsModeDescriptor(Bc6hEField.RW, 2), new Bc6hsModeDescriptor(Bc6hEField.RW, 3), new Bc6hsModeDescriptor(Bc6hEField.RW, 4), + new Bc6hsModeDescriptor(Bc6hEField.RW, 5), new Bc6hsModeDescriptor(Bc6hEField.RW, 6), new Bc6hsModeDescriptor(Bc6hEField.RW, 7), new Bc6hsModeDescriptor(Bc6hEField.RW, 8), new Bc6hsModeDescriptor(Bc6hEField.RW, 9), new Bc6hsModeDescriptor(Bc6hEField.GW, 0), new Bc6hsModeDescriptor(Bc6hEField.GW, 1), new Bc6hsModeDescriptor(Bc6hEField.GW, 2), new Bc6hsModeDescriptor(Bc6hEField.GW, 3), new Bc6hsModeDescriptor(Bc6hEField.GW, 4), + new Bc6hsModeDescriptor(Bc6hEField.GW, 5), new Bc6hsModeDescriptor(Bc6hEField.GW, 6), new Bc6hsModeDescriptor(Bc6hEField.GW, 7), new Bc6hsModeDescriptor(Bc6hEField.GW, 8), new Bc6hsModeDescriptor(Bc6hEField.GW, 9), new Bc6hsModeDescriptor(Bc6hEField.BW, 0), new Bc6hsModeDescriptor(Bc6hEField.BW, 1), new Bc6hsModeDescriptor(Bc6hEField.BW, 2), new Bc6hsModeDescriptor(Bc6hEField.BW, 3), new Bc6hsModeDescriptor(Bc6hEField.BW, 4), + new Bc6hsModeDescriptor(Bc6hEField.BW, 5), new Bc6hsModeDescriptor(Bc6hEField.BW, 6), new Bc6hsModeDescriptor(Bc6hEField.BW, 7), new Bc6hsModeDescriptor(Bc6hEField.BW, 8), new Bc6hsModeDescriptor(Bc6hEField.BW, 9), new Bc6hsModeDescriptor(Bc6hEField.RX, 0), new Bc6hsModeDescriptor(Bc6hEField.RX, 1), new Bc6hsModeDescriptor(Bc6hEField.RX, 2), new Bc6hsModeDescriptor(Bc6hEField.RX, 3), new Bc6hsModeDescriptor(Bc6hEField.RW, 15), + new Bc6hsModeDescriptor(Bc6hEField.RW, 14), new Bc6hsModeDescriptor(Bc6hEField.RW, 13), new Bc6hsModeDescriptor(Bc6hEField.RW, 12), new Bc6hsModeDescriptor(Bc6hEField.RW, 11), new Bc6hsModeDescriptor(Bc6hEField.RW, 10), new Bc6hsModeDescriptor(Bc6hEField.GX, 0), new Bc6hsModeDescriptor(Bc6hEField.GX, 1), new Bc6hsModeDescriptor(Bc6hEField.GX, 2), new Bc6hsModeDescriptor(Bc6hEField.GX, 3), new Bc6hsModeDescriptor(Bc6hEField.GW, 15), + new Bc6hsModeDescriptor(Bc6hEField.GW, 14), new Bc6hsModeDescriptor(Bc6hEField.GW, 13), new Bc6hsModeDescriptor(Bc6hEField.GW, 12), new Bc6hsModeDescriptor(Bc6hEField.GW, 11), new Bc6hsModeDescriptor(Bc6hEField.GW, 10), new Bc6hsModeDescriptor(Bc6hEField.BX, 0), new Bc6hsModeDescriptor(Bc6hEField.BX, 1), new Bc6hsModeDescriptor(Bc6hEField.BX, 2), new Bc6hsModeDescriptor(Bc6hEField.BX, 3), new Bc6hsModeDescriptor(Bc6hEField.BW, 15), + new Bc6hsModeDescriptor(Bc6hEField.BW, 14), new Bc6hsModeDescriptor(Bc6hEField.BW, 13), new Bc6hsModeDescriptor(Bc6hEField.BW, 12), new Bc6hsModeDescriptor(Bc6hEField.BW, 11), new Bc6hsModeDescriptor(Bc6hEField.BW, 10), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), + new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), + new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0) + ] + ]; + + private static readonly Bc6hsModeInfo[] MsAInfo = + [ + new Bc6hsModeInfo(0x00, 1, true, 3, [[new LdrColorA(10, 10, 10, 0), new LdrColorA(5, 5, 5, 0)], [new LdrColorA(5, 5, 5, 0), new LdrColorA(5, 5, 5, 0)]]), // Mode 1 + new Bc6hsModeInfo(0x01, 1, true, 3, [[new LdrColorA(7, 7, 7, 0), new LdrColorA(6, 6, 6, 0)], [new LdrColorA(6, 6, 6, 0), new LdrColorA(6, 6, 6, 0)]]), // Mode 2 + new Bc6hsModeInfo(0x02, 1, true, 3, [[new LdrColorA(11, 11, 11, 0), new LdrColorA(5, 4, 4, 0)], [new LdrColorA(5, 4, 4, 0), new LdrColorA(5, 4, 4, 0)]]), // Mode 3 + new Bc6hsModeInfo(0x06, 1, true, 3, [[new LdrColorA(11, 11, 11, 0), new LdrColorA(4, 5, 4, 0)], [new LdrColorA(4, 5, 4, 0), new LdrColorA(4, 5, 4, 0)]]), // Mode 4 + new Bc6hsModeInfo(0x0a, 1, true, 3, [[new LdrColorA(11, 11, 11, 0), new LdrColorA(4, 4, 5, 0)], [new LdrColorA(4, 4, 5, 0), new LdrColorA(4, 4, 5, 0)]]), // Mode 5 + new Bc6hsModeInfo(0x0e, 1, true, 3, [[new LdrColorA(9, 9, 9, 0), new LdrColorA(5, 5, 5, 0)], [new LdrColorA(5, 5, 5, 0), new LdrColorA(5, 5, 5, 0)]]), // Mode 6 + new Bc6hsModeInfo(0x12, 1, true, 3, [[new LdrColorA(8, 8, 8, 0), new LdrColorA(6, 5, 5, 0)], [new LdrColorA(6, 5, 5, 0), new LdrColorA(6, 5, 5, 0)]]), // Mode 7 + new Bc6hsModeInfo(0x16, 1, true, 3, [[new LdrColorA(8, 8, 8, 0), new LdrColorA(5, 6, 5, 0)], [new LdrColorA(5, 6, 5, 0), new LdrColorA(5, 6, 5, 0)]]), // Mode 8 + new Bc6hsModeInfo(0x1a, 1, true, 3, [[new LdrColorA(8, 8, 8, 0), new LdrColorA(5, 5, 6, 0)], [new LdrColorA(5, 5, 6, 0), new LdrColorA(5, 5, 6, 0)]]), // Mode 9 + new Bc6hsModeInfo(0x1e, 1, false, 3, [[new LdrColorA(6, 6, 6, 0), new LdrColorA(6, 6, 6, 0)], [new LdrColorA(6, 6, 6, 0), new LdrColorA(6, 6, 6, 0)]]), // Mode 10 + new Bc6hsModeInfo(0x03, 0, false, 4, [[new LdrColorA(10, 10, 10, 0), new LdrColorA(10, 10, 10, 0)], [new LdrColorA(0, 0, 0, 0), new LdrColorA(0, 0, 0, 0)]]), // Mode 11 + new Bc6hsModeInfo(0x07, 0, true, 4, [[new LdrColorA(11, 11, 11, 0), new LdrColorA(9, 9, 9, 0)], [new LdrColorA(0, 0, 0, 0), new LdrColorA(0, 0, 0, 0)]]), // Mode 12 + new Bc6hsModeInfo(0x0b, 0, true, 4, [[new LdrColorA(12, 12, 12, 0), new LdrColorA(8, 8, 8, 0)], [new LdrColorA(0, 0, 0, 0), new LdrColorA(0, 0, 0, 0)]]), // Mode 13 + new Bc6hsModeInfo(0x0f, 0, true, 4, [[new LdrColorA(16, 16, 16, 0), new LdrColorA(4, 4, 4, 0)], [new LdrColorA(0, 0, 0, 0), new LdrColorA(0, 0, 0, 0)]]), // Mode 14 + ]; + + private static readonly int[] MsAModeToInfo = + [ + 0, // Mode 1 - 0x00 + 1, // Mode 2 - 0x01 + 2, // Mode 3 - 0x02 + 10, // Mode 11 - 0x03 + -1, // Invalid - 0x04 + -1, // Invalid - 0x05 + 3, // Mode 4 - 0x06 + 11, // Mode 12 - 0x07 + -1, // Invalid - 0x08 + -1, // Invalid - 0x09 + 4, // Mode 5 - 0x0a + 12, // Mode 13 - 0x0b + -1, // Invalid - 0x0c + -1, // Invalid - 0x0d + 5, // Mode 6 - 0x0e + 13, // Mode 14 - 0x0f + -1, // Invalid - 0x10 + -1, // Invalid - 0x11 + 6, // Mode 7 - 0x12 + -1, // Reserved - 0x13 + -1, // Invalid - 0x14 + -1, // Invalid - 0x15 + 7, // Mode 8 - 0x16 + -1, // Reserved - 0x17 + -1, // Invalid - 0x18 + -1, // Invalid - 0x19 + 8, // Mode 9 - 0x1a + -1, // Reserved - 0x1b + -1, // Invalid - 0x1c + -1, // Invalid - 0x1d + 9, // Mode 10 - 0x1e + -1, // Resreved - 0x1f + ]; + + /// + public readonly int BitsPerPixel => 32; + + /// + public readonly byte PixelDepthBytes => 4; + + /// + public readonly byte DivSize => 4; + + /// + public readonly byte CompressedBytesPerBlock => 16; + + /// + public readonly bool Compressed => true; + + /// + public Image GetImage(byte[] blockData, int width, int height) { - // Code based on commit 138efff1b9c53fd9a5dd34b8c865e8f5ae798030 2019/10/24 in DirectXTex C++ library - private static readonly Bc6hsModeDescriptor[][] MsADesc = new[] + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); + } + + /// + public byte[] Decompress(byte[] blockData, int width, int height) + { + Bc6hs self = this; + byte[] currentBlock = new byte[this.CompressedBytesPerBlock]; + + return Helper.InMemoryDecode(blockData, width, height, (stream, data, streamIndex, dataIndex, stride) => { - new[] - { - // Mode 1 (0x00) - 10 5 5 5 - new Bc6hsModeDescriptor(Bc6hEField.M, 0), new Bc6hsModeDescriptor(Bc6hEField.M, 1), new Bc6hsModeDescriptor(Bc6hEField.GY, 4), new Bc6hsModeDescriptor(Bc6hEField.BY, 4), new Bc6hsModeDescriptor(Bc6hEField.BZ, 4), new Bc6hsModeDescriptor(Bc6hEField.RW, 0), new Bc6hsModeDescriptor(Bc6hEField.RW, 1), new Bc6hsModeDescriptor(Bc6hEField.RW, 2), new Bc6hsModeDescriptor(Bc6hEField.RW, 3), new Bc6hsModeDescriptor(Bc6hEField.RW, 4), - new Bc6hsModeDescriptor(Bc6hEField.RW, 5), new Bc6hsModeDescriptor(Bc6hEField.RW, 6), new Bc6hsModeDescriptor(Bc6hEField.RW, 7), new Bc6hsModeDescriptor(Bc6hEField.RW, 8), new Bc6hsModeDescriptor(Bc6hEField.RW, 9), new Bc6hsModeDescriptor(Bc6hEField.GW, 0), new Bc6hsModeDescriptor(Bc6hEField.GW, 1), new Bc6hsModeDescriptor(Bc6hEField.GW, 2), new Bc6hsModeDescriptor(Bc6hEField.GW, 3), new Bc6hsModeDescriptor(Bc6hEField.GW, 4), - new Bc6hsModeDescriptor(Bc6hEField.GW, 5), new Bc6hsModeDescriptor(Bc6hEField.GW, 6), new Bc6hsModeDescriptor(Bc6hEField.GW, 7), new Bc6hsModeDescriptor(Bc6hEField.GW, 8), new Bc6hsModeDescriptor(Bc6hEField.GW, 9), new Bc6hsModeDescriptor(Bc6hEField.BW, 0), new Bc6hsModeDescriptor(Bc6hEField.BW, 1), new Bc6hsModeDescriptor(Bc6hEField.BW, 2), new Bc6hsModeDescriptor(Bc6hEField.BW, 3), new Bc6hsModeDescriptor(Bc6hEField.BW, 4), - new Bc6hsModeDescriptor(Bc6hEField.BW, 5), new Bc6hsModeDescriptor(Bc6hEField.BW, 6), new Bc6hsModeDescriptor(Bc6hEField.BW, 7), new Bc6hsModeDescriptor(Bc6hEField.BW, 8), new Bc6hsModeDescriptor(Bc6hEField.BW, 9), new Bc6hsModeDescriptor(Bc6hEField.RX, 0), new Bc6hsModeDescriptor(Bc6hEField.RX, 1), new Bc6hsModeDescriptor(Bc6hEField.RX, 2), new Bc6hsModeDescriptor(Bc6hEField.RX, 3), new Bc6hsModeDescriptor(Bc6hEField.RX, 4), - new Bc6hsModeDescriptor(Bc6hEField.GZ, 4), new Bc6hsModeDescriptor(Bc6hEField.GY, 0), new Bc6hsModeDescriptor(Bc6hEField.GY, 1), new Bc6hsModeDescriptor(Bc6hEField.GY, 2), new Bc6hsModeDescriptor(Bc6hEField.GY, 3), new Bc6hsModeDescriptor(Bc6hEField.GX, 0), new Bc6hsModeDescriptor(Bc6hEField.GX, 1), new Bc6hsModeDescriptor(Bc6hEField.GX, 2), new Bc6hsModeDescriptor(Bc6hEField.GX, 3), new Bc6hsModeDescriptor(Bc6hEField.GX, 4), - new Bc6hsModeDescriptor(Bc6hEField.BZ, 0), new Bc6hsModeDescriptor(Bc6hEField.GZ, 0), new Bc6hsModeDescriptor(Bc6hEField.GZ, 1), new Bc6hsModeDescriptor(Bc6hEField.GZ, 2), new Bc6hsModeDescriptor(Bc6hEField.GZ, 3), new Bc6hsModeDescriptor(Bc6hEField.BX, 0), new Bc6hsModeDescriptor(Bc6hEField.BX, 1), new Bc6hsModeDescriptor(Bc6hEField.BX, 2), new Bc6hsModeDescriptor(Bc6hEField.BX, 3), new Bc6hsModeDescriptor(Bc6hEField.BX, 4), - new Bc6hsModeDescriptor(Bc6hEField.BZ, 1), new Bc6hsModeDescriptor(Bc6hEField.BY, 0), new Bc6hsModeDescriptor(Bc6hEField.BY, 1), new Bc6hsModeDescriptor(Bc6hEField.BY, 2), new Bc6hsModeDescriptor(Bc6hEField.BY, 3), new Bc6hsModeDescriptor(Bc6hEField.RY, 0), new Bc6hsModeDescriptor(Bc6hEField.RY, 1), new Bc6hsModeDescriptor(Bc6hEField.RY, 2), new Bc6hsModeDescriptor(Bc6hEField.RY, 3), new Bc6hsModeDescriptor(Bc6hEField.RY, 4), - new Bc6hsModeDescriptor(Bc6hEField.BZ, 2), new Bc6hsModeDescriptor(Bc6hEField.RZ, 0), new Bc6hsModeDescriptor(Bc6hEField.RZ, 1), new Bc6hsModeDescriptor(Bc6hEField.RZ, 2), new Bc6hsModeDescriptor(Bc6hEField.RZ, 3), new Bc6hsModeDescriptor(Bc6hEField.RZ, 4), new Bc6hsModeDescriptor(Bc6hEField.BZ, 3), new Bc6hsModeDescriptor(Bc6hEField.D, 0), new Bc6hsModeDescriptor(Bc6hEField.D, 1), new Bc6hsModeDescriptor(Bc6hEField.D, 2), - new Bc6hsModeDescriptor(Bc6hEField.D, 3), new Bc6hsModeDescriptor(Bc6hEField.D, 4) - }, - - new[] - { - // Mode 2 (0x01) - 7 6 6 6 - new Bc6hsModeDescriptor(Bc6hEField.M, 0), new Bc6hsModeDescriptor(Bc6hEField.M, 1), new Bc6hsModeDescriptor(Bc6hEField.GY, 5), new Bc6hsModeDescriptor(Bc6hEField.GZ, 4), new Bc6hsModeDescriptor(Bc6hEField.GZ, 5), new Bc6hsModeDescriptor(Bc6hEField.RW, 0), new Bc6hsModeDescriptor(Bc6hEField.RW, 1), new Bc6hsModeDescriptor(Bc6hEField.RW, 2), new Bc6hsModeDescriptor(Bc6hEField.RW, 3), new Bc6hsModeDescriptor(Bc6hEField.RW, 4), - new Bc6hsModeDescriptor(Bc6hEField.RW, 5), new Bc6hsModeDescriptor(Bc6hEField.RW, 6), new Bc6hsModeDescriptor(Bc6hEField.BZ, 0), new Bc6hsModeDescriptor(Bc6hEField.BZ, 1), new Bc6hsModeDescriptor(Bc6hEField.BY, 4), new Bc6hsModeDescriptor(Bc6hEField.GW, 0), new Bc6hsModeDescriptor(Bc6hEField.GW, 1), new Bc6hsModeDescriptor(Bc6hEField.GW, 2), new Bc6hsModeDescriptor(Bc6hEField.GW, 3), new Bc6hsModeDescriptor(Bc6hEField.GW, 4), - new Bc6hsModeDescriptor(Bc6hEField.GW, 5), new Bc6hsModeDescriptor(Bc6hEField.GW, 6), new Bc6hsModeDescriptor(Bc6hEField.BY, 5), new Bc6hsModeDescriptor(Bc6hEField.BZ, 2), new Bc6hsModeDescriptor(Bc6hEField.GY, 4), new Bc6hsModeDescriptor(Bc6hEField.BW, 0), new Bc6hsModeDescriptor(Bc6hEField.BW, 1), new Bc6hsModeDescriptor(Bc6hEField.BW, 2), new Bc6hsModeDescriptor(Bc6hEField.BW, 3), new Bc6hsModeDescriptor(Bc6hEField.BW, 4), - new Bc6hsModeDescriptor(Bc6hEField.BW, 5), new Bc6hsModeDescriptor(Bc6hEField.BW, 6), new Bc6hsModeDescriptor(Bc6hEField.BZ, 3), new Bc6hsModeDescriptor(Bc6hEField.BZ, 5), new Bc6hsModeDescriptor(Bc6hEField.BZ, 4), new Bc6hsModeDescriptor(Bc6hEField.RX, 0), new Bc6hsModeDescriptor(Bc6hEField.RX, 1), new Bc6hsModeDescriptor(Bc6hEField.RX, 2), new Bc6hsModeDescriptor(Bc6hEField.RX, 3), new Bc6hsModeDescriptor(Bc6hEField.RX, 4), - new Bc6hsModeDescriptor(Bc6hEField.RX, 5), new Bc6hsModeDescriptor(Bc6hEField.GY, 0), new Bc6hsModeDescriptor(Bc6hEField.GY, 1), new Bc6hsModeDescriptor(Bc6hEField.GY, 2), new Bc6hsModeDescriptor(Bc6hEField.GY, 3), new Bc6hsModeDescriptor(Bc6hEField.GX, 0), new Bc6hsModeDescriptor(Bc6hEField.GX, 1), new Bc6hsModeDescriptor(Bc6hEField.GX, 2), new Bc6hsModeDescriptor(Bc6hEField.GX, 3), new Bc6hsModeDescriptor(Bc6hEField.GX, 4), - new Bc6hsModeDescriptor(Bc6hEField.GX, 5), new Bc6hsModeDescriptor(Bc6hEField.GZ, 0), new Bc6hsModeDescriptor(Bc6hEField.GZ, 1), new Bc6hsModeDescriptor(Bc6hEField.GZ, 2), new Bc6hsModeDescriptor(Bc6hEField.GZ, 3), new Bc6hsModeDescriptor(Bc6hEField.BX, 0), new Bc6hsModeDescriptor(Bc6hEField.BX, 1), new Bc6hsModeDescriptor(Bc6hEField.BX, 2), new Bc6hsModeDescriptor(Bc6hEField.BX, 3), new Bc6hsModeDescriptor(Bc6hEField.BX, 4), - new Bc6hsModeDescriptor(Bc6hEField.BX, 5), new Bc6hsModeDescriptor(Bc6hEField.BY, 0), new Bc6hsModeDescriptor(Bc6hEField.BY, 1), new Bc6hsModeDescriptor(Bc6hEField.BY, 2), new Bc6hsModeDescriptor(Bc6hEField.BY, 3), new Bc6hsModeDescriptor(Bc6hEField.RY, 0), new Bc6hsModeDescriptor(Bc6hEField.RY, 1), new Bc6hsModeDescriptor(Bc6hEField.RY, 2), new Bc6hsModeDescriptor(Bc6hEField.RY, 3), new Bc6hsModeDescriptor(Bc6hEField.RY, 4), - new Bc6hsModeDescriptor(Bc6hEField.RY, 5), new Bc6hsModeDescriptor(Bc6hEField.RZ, 0), new Bc6hsModeDescriptor(Bc6hEField.RZ, 1), new Bc6hsModeDescriptor(Bc6hEField.RZ, 2), new Bc6hsModeDescriptor(Bc6hEField.RZ, 3), new Bc6hsModeDescriptor(Bc6hEField.RZ, 4), new Bc6hsModeDescriptor(Bc6hEField.RZ, 5), new Bc6hsModeDescriptor(Bc6hEField.D, 0), new Bc6hsModeDescriptor(Bc6hEField.D, 1), new Bc6hsModeDescriptor(Bc6hEField.D, 2), - new Bc6hsModeDescriptor(Bc6hEField.D, 3), new Bc6hsModeDescriptor(Bc6hEField.D, 4) - }, - - new[] - { - // Mode 3 (0x02) - 11 5 4 4 - new Bc6hsModeDescriptor(Bc6hEField.M, 0), new Bc6hsModeDescriptor(Bc6hEField.M, 1), new Bc6hsModeDescriptor(Bc6hEField.M, 2), new Bc6hsModeDescriptor(Bc6hEField.M, 3), new Bc6hsModeDescriptor(Bc6hEField.M, 4), new Bc6hsModeDescriptor(Bc6hEField.RW, 0), new Bc6hsModeDescriptor(Bc6hEField.RW, 1), new Bc6hsModeDescriptor(Bc6hEField.RW, 2), new Bc6hsModeDescriptor(Bc6hEField.RW, 3), new Bc6hsModeDescriptor(Bc6hEField.RW, 4), - new Bc6hsModeDescriptor(Bc6hEField.RW, 5), new Bc6hsModeDescriptor(Bc6hEField.RW, 6), new Bc6hsModeDescriptor(Bc6hEField.RW, 7), new Bc6hsModeDescriptor(Bc6hEField.RW, 8), new Bc6hsModeDescriptor(Bc6hEField.RW, 9), new Bc6hsModeDescriptor(Bc6hEField.GW, 0), new Bc6hsModeDescriptor(Bc6hEField.GW, 1), new Bc6hsModeDescriptor(Bc6hEField.GW, 2), new Bc6hsModeDescriptor(Bc6hEField.GW, 3), new Bc6hsModeDescriptor(Bc6hEField.GW, 4), - new Bc6hsModeDescriptor(Bc6hEField.GW, 5), new Bc6hsModeDescriptor(Bc6hEField.GW, 6), new Bc6hsModeDescriptor(Bc6hEField.GW, 7), new Bc6hsModeDescriptor(Bc6hEField.GW, 8), new Bc6hsModeDescriptor(Bc6hEField.GW, 9), new Bc6hsModeDescriptor(Bc6hEField.BW, 0), new Bc6hsModeDescriptor(Bc6hEField.BW, 1), new Bc6hsModeDescriptor(Bc6hEField.BW, 2), new Bc6hsModeDescriptor(Bc6hEField.BW, 3), new Bc6hsModeDescriptor(Bc6hEField.BW, 4), - new Bc6hsModeDescriptor(Bc6hEField.BW, 5), new Bc6hsModeDescriptor(Bc6hEField.BW, 6), new Bc6hsModeDescriptor(Bc6hEField.BW, 7), new Bc6hsModeDescriptor(Bc6hEField.BW, 8), new Bc6hsModeDescriptor(Bc6hEField.BW, 9), new Bc6hsModeDescriptor(Bc6hEField.RX, 0), new Bc6hsModeDescriptor(Bc6hEField.RX, 1), new Bc6hsModeDescriptor(Bc6hEField.RX, 2), new Bc6hsModeDescriptor(Bc6hEField.RX, 3), new Bc6hsModeDescriptor(Bc6hEField.RX, 4), - new Bc6hsModeDescriptor(Bc6hEField.RW, 10), new Bc6hsModeDescriptor(Bc6hEField.GY, 0), new Bc6hsModeDescriptor(Bc6hEField.GY, 1), new Bc6hsModeDescriptor(Bc6hEField.GY, 2), new Bc6hsModeDescriptor(Bc6hEField.GY, 3), new Bc6hsModeDescriptor(Bc6hEField.GX, 0), new Bc6hsModeDescriptor(Bc6hEField.GX, 1), new Bc6hsModeDescriptor(Bc6hEField.GX, 2), new Bc6hsModeDescriptor(Bc6hEField.GX, 3), new Bc6hsModeDescriptor(Bc6hEField.GW, 10), - new Bc6hsModeDescriptor(Bc6hEField.BZ, 0), new Bc6hsModeDescriptor(Bc6hEField.GZ, 0), new Bc6hsModeDescriptor(Bc6hEField.GZ, 1), new Bc6hsModeDescriptor(Bc6hEField.GZ, 2), new Bc6hsModeDescriptor(Bc6hEField.GZ, 3), new Bc6hsModeDescriptor(Bc6hEField.BX, 0), new Bc6hsModeDescriptor(Bc6hEField.BX, 1), new Bc6hsModeDescriptor(Bc6hEField.BX, 2), new Bc6hsModeDescriptor(Bc6hEField.BX, 3), new Bc6hsModeDescriptor(Bc6hEField.BW, 10), - new Bc6hsModeDescriptor(Bc6hEField.BZ, 1), new Bc6hsModeDescriptor(Bc6hEField.BY, 0), new Bc6hsModeDescriptor(Bc6hEField.BY, 1), new Bc6hsModeDescriptor(Bc6hEField.BY, 2), new Bc6hsModeDescriptor(Bc6hEField.BY, 3), new Bc6hsModeDescriptor(Bc6hEField.RY, 0), new Bc6hsModeDescriptor(Bc6hEField.RY, 1), new Bc6hsModeDescriptor(Bc6hEField.RY, 2), new Bc6hsModeDescriptor(Bc6hEField.RY, 3), new Bc6hsModeDescriptor(Bc6hEField.RY, 4), - new Bc6hsModeDescriptor(Bc6hEField.BZ, 2), new Bc6hsModeDescriptor(Bc6hEField.RZ, 0), new Bc6hsModeDescriptor(Bc6hEField.RZ, 1), new Bc6hsModeDescriptor(Bc6hEField.RZ, 2), new Bc6hsModeDescriptor(Bc6hEField.RZ, 3), new Bc6hsModeDescriptor(Bc6hEField.RZ, 4), new Bc6hsModeDescriptor(Bc6hEField.BZ, 3), new Bc6hsModeDescriptor(Bc6hEField.D, 0), new Bc6hsModeDescriptor(Bc6hEField.D, 1), new Bc6hsModeDescriptor(Bc6hEField.D, 2), - new Bc6hsModeDescriptor(Bc6hEField.D, 3), new Bc6hsModeDescriptor(Bc6hEField.D, 4) - }, - - new[] - { - // Mode 4 (0x06) - 11 4 5 4 - new Bc6hsModeDescriptor(Bc6hEField.M, 0), new Bc6hsModeDescriptor(Bc6hEField.M, 1), new Bc6hsModeDescriptor(Bc6hEField.M, 2), new Bc6hsModeDescriptor(Bc6hEField.M, 3), new Bc6hsModeDescriptor(Bc6hEField.M, 4), new Bc6hsModeDescriptor(Bc6hEField.RW, 0), new Bc6hsModeDescriptor(Bc6hEField.RW, 1), new Bc6hsModeDescriptor(Bc6hEField.RW, 2), new Bc6hsModeDescriptor(Bc6hEField.RW, 3), new Bc6hsModeDescriptor(Bc6hEField.RW, 4), - new Bc6hsModeDescriptor(Bc6hEField.RW, 5), new Bc6hsModeDescriptor(Bc6hEField.RW, 6), new Bc6hsModeDescriptor(Bc6hEField.RW, 7), new Bc6hsModeDescriptor(Bc6hEField.RW, 8), new Bc6hsModeDescriptor(Bc6hEField.RW, 9), new Bc6hsModeDescriptor(Bc6hEField.GW, 0), new Bc6hsModeDescriptor(Bc6hEField.GW, 1), new Bc6hsModeDescriptor(Bc6hEField.GW, 2), new Bc6hsModeDescriptor(Bc6hEField.GW, 3), new Bc6hsModeDescriptor(Bc6hEField.GW, 4), - new Bc6hsModeDescriptor(Bc6hEField.GW, 5), new Bc6hsModeDescriptor(Bc6hEField.GW, 6), new Bc6hsModeDescriptor(Bc6hEField.GW, 7), new Bc6hsModeDescriptor(Bc6hEField.GW, 8), new Bc6hsModeDescriptor(Bc6hEField.GW, 9), new Bc6hsModeDescriptor(Bc6hEField.BW, 0), new Bc6hsModeDescriptor(Bc6hEField.BW, 1), new Bc6hsModeDescriptor(Bc6hEField.BW, 2), new Bc6hsModeDescriptor(Bc6hEField.BW, 3), new Bc6hsModeDescriptor(Bc6hEField.BW, 4), - new Bc6hsModeDescriptor(Bc6hEField.BW, 5), new Bc6hsModeDescriptor(Bc6hEField.BW, 6), new Bc6hsModeDescriptor(Bc6hEField.BW, 7), new Bc6hsModeDescriptor(Bc6hEField.BW, 8), new Bc6hsModeDescriptor(Bc6hEField.BW, 9), new Bc6hsModeDescriptor(Bc6hEField.RX, 0), new Bc6hsModeDescriptor(Bc6hEField.RX, 1), new Bc6hsModeDescriptor(Bc6hEField.RX, 2), new Bc6hsModeDescriptor(Bc6hEField.RX, 3), new Bc6hsModeDescriptor(Bc6hEField.RW, 10), - new Bc6hsModeDescriptor(Bc6hEField.GZ, 4), new Bc6hsModeDescriptor(Bc6hEField.GY, 0), new Bc6hsModeDescriptor(Bc6hEField.GY, 1), new Bc6hsModeDescriptor(Bc6hEField.GY, 2), new Bc6hsModeDescriptor(Bc6hEField.GY, 3), new Bc6hsModeDescriptor(Bc6hEField.GX, 0), new Bc6hsModeDescriptor(Bc6hEField.GX, 1), new Bc6hsModeDescriptor(Bc6hEField.GX, 2), new Bc6hsModeDescriptor(Bc6hEField.GX, 3), new Bc6hsModeDescriptor(Bc6hEField.GX, 4), - new Bc6hsModeDescriptor(Bc6hEField.GW, 10), new Bc6hsModeDescriptor(Bc6hEField.GZ, 0), new Bc6hsModeDescriptor(Bc6hEField.GZ, 1), new Bc6hsModeDescriptor(Bc6hEField.GZ, 2), new Bc6hsModeDescriptor(Bc6hEField.GZ, 3), new Bc6hsModeDescriptor(Bc6hEField.BX, 0), new Bc6hsModeDescriptor(Bc6hEField.BX, 1), new Bc6hsModeDescriptor(Bc6hEField.BX, 2), new Bc6hsModeDescriptor(Bc6hEField.BX, 3), new Bc6hsModeDescriptor(Bc6hEField.BW, 10), - new Bc6hsModeDescriptor(Bc6hEField.BZ, 1), new Bc6hsModeDescriptor(Bc6hEField.BY, 0), new Bc6hsModeDescriptor(Bc6hEField.BY, 1), new Bc6hsModeDescriptor(Bc6hEField.BY, 2), new Bc6hsModeDescriptor(Bc6hEField.BY, 3), new Bc6hsModeDescriptor(Bc6hEField.RY, 0), new Bc6hsModeDescriptor(Bc6hEField.RY, 1), new Bc6hsModeDescriptor(Bc6hEField.RY, 2), new Bc6hsModeDescriptor(Bc6hEField.RY, 3), new Bc6hsModeDescriptor(Bc6hEField.BZ, 0), - new Bc6hsModeDescriptor(Bc6hEField.BZ, 2), new Bc6hsModeDescriptor(Bc6hEField.RZ, 0), new Bc6hsModeDescriptor(Bc6hEField.RZ, 1), new Bc6hsModeDescriptor(Bc6hEField.RZ, 2), new Bc6hsModeDescriptor(Bc6hEField.RZ, 3), new Bc6hsModeDescriptor(Bc6hEField.GY, 4), new Bc6hsModeDescriptor(Bc6hEField.BZ, 3), new Bc6hsModeDescriptor(Bc6hEField.D, 0), new Bc6hsModeDescriptor(Bc6hEField.D, 1), new Bc6hsModeDescriptor(Bc6hEField.D, 2), - new Bc6hsModeDescriptor(Bc6hEField.D, 3), new Bc6hsModeDescriptor(Bc6hEField.D, 4) - }, - - new[] - { - // Mode 5 (0x0a) - 11 4 4 5 - new Bc6hsModeDescriptor(Bc6hEField.M, 0), new Bc6hsModeDescriptor(Bc6hEField.M, 1), new Bc6hsModeDescriptor(Bc6hEField.M, 2), new Bc6hsModeDescriptor(Bc6hEField.M, 3), new Bc6hsModeDescriptor(Bc6hEField.M, 4), new Bc6hsModeDescriptor(Bc6hEField.RW, 0), new Bc6hsModeDescriptor(Bc6hEField.RW, 1), new Bc6hsModeDescriptor(Bc6hEField.RW, 2), new Bc6hsModeDescriptor(Bc6hEField.RW, 3), new Bc6hsModeDescriptor(Bc6hEField.RW, 4), - new Bc6hsModeDescriptor(Bc6hEField.RW, 5), new Bc6hsModeDescriptor(Bc6hEField.RW, 6), new Bc6hsModeDescriptor(Bc6hEField.RW, 7), new Bc6hsModeDescriptor(Bc6hEField.RW, 8), new Bc6hsModeDescriptor(Bc6hEField.RW, 9), new Bc6hsModeDescriptor(Bc6hEField.GW, 0), new Bc6hsModeDescriptor(Bc6hEField.GW, 1), new Bc6hsModeDescriptor(Bc6hEField.GW, 2), new Bc6hsModeDescriptor(Bc6hEField.GW, 3), new Bc6hsModeDescriptor(Bc6hEField.GW, 4), - new Bc6hsModeDescriptor(Bc6hEField.GW, 5), new Bc6hsModeDescriptor(Bc6hEField.GW, 6), new Bc6hsModeDescriptor(Bc6hEField.GW, 7), new Bc6hsModeDescriptor(Bc6hEField.GW, 8), new Bc6hsModeDescriptor(Bc6hEField.GW, 9), new Bc6hsModeDescriptor(Bc6hEField.BW, 0), new Bc6hsModeDescriptor(Bc6hEField.BW, 1), new Bc6hsModeDescriptor(Bc6hEField.BW, 2), new Bc6hsModeDescriptor(Bc6hEField.BW, 3), new Bc6hsModeDescriptor(Bc6hEField.BW, 4), - new Bc6hsModeDescriptor(Bc6hEField.BW, 5), new Bc6hsModeDescriptor(Bc6hEField.BW, 6), new Bc6hsModeDescriptor(Bc6hEField.BW, 7), new Bc6hsModeDescriptor(Bc6hEField.BW, 8), new Bc6hsModeDescriptor(Bc6hEField.BW, 9), new Bc6hsModeDescriptor(Bc6hEField.RX, 0), new Bc6hsModeDescriptor(Bc6hEField.RX, 1), new Bc6hsModeDescriptor(Bc6hEField.RX, 2), new Bc6hsModeDescriptor(Bc6hEField.RX, 3), new Bc6hsModeDescriptor(Bc6hEField.RW, 10), - new Bc6hsModeDescriptor(Bc6hEField.BY, 4), new Bc6hsModeDescriptor(Bc6hEField.GY, 0), new Bc6hsModeDescriptor(Bc6hEField.GY, 1), new Bc6hsModeDescriptor(Bc6hEField.GY, 2), new Bc6hsModeDescriptor(Bc6hEField.GY, 3), new Bc6hsModeDescriptor(Bc6hEField.GX, 0), new Bc6hsModeDescriptor(Bc6hEField.GX, 1), new Bc6hsModeDescriptor(Bc6hEField.GX, 2), new Bc6hsModeDescriptor(Bc6hEField.GX, 3), new Bc6hsModeDescriptor(Bc6hEField.GW, 10), - new Bc6hsModeDescriptor(Bc6hEField.BZ, 0), new Bc6hsModeDescriptor(Bc6hEField.GZ, 0), new Bc6hsModeDescriptor(Bc6hEField.GZ, 1), new Bc6hsModeDescriptor(Bc6hEField.GZ, 2), new Bc6hsModeDescriptor(Bc6hEField.GZ, 3), new Bc6hsModeDescriptor(Bc6hEField.BX, 0), new Bc6hsModeDescriptor(Bc6hEField.BX, 1), new Bc6hsModeDescriptor(Bc6hEField.BX, 2), new Bc6hsModeDescriptor(Bc6hEField.BX, 3), new Bc6hsModeDescriptor(Bc6hEField.BX, 4), - new Bc6hsModeDescriptor(Bc6hEField.BW, 10), new Bc6hsModeDescriptor(Bc6hEField.BY, 0), new Bc6hsModeDescriptor(Bc6hEField.BY, 1), new Bc6hsModeDescriptor(Bc6hEField.BY, 2), new Bc6hsModeDescriptor(Bc6hEField.BY, 3), new Bc6hsModeDescriptor(Bc6hEField.RY, 0), new Bc6hsModeDescriptor(Bc6hEField.RY, 1), new Bc6hsModeDescriptor(Bc6hEField.RY, 2), new Bc6hsModeDescriptor(Bc6hEField.RY, 3), new Bc6hsModeDescriptor(Bc6hEField.BZ, 1), - new Bc6hsModeDescriptor(Bc6hEField.BZ, 2), new Bc6hsModeDescriptor(Bc6hEField.RZ, 0), new Bc6hsModeDescriptor(Bc6hEField.RZ, 1), new Bc6hsModeDescriptor(Bc6hEField.RZ, 2), new Bc6hsModeDescriptor(Bc6hEField.RZ, 3), new Bc6hsModeDescriptor(Bc6hEField.BZ, 4), new Bc6hsModeDescriptor(Bc6hEField.BZ, 3), new Bc6hsModeDescriptor(Bc6hEField.D, 0), new Bc6hsModeDescriptor(Bc6hEField.D, 1), new Bc6hsModeDescriptor(Bc6hEField.D, 2), - new Bc6hsModeDescriptor(Bc6hEField.D, 3), new Bc6hsModeDescriptor(Bc6hEField.D, 4) - }, - - new[] - { - // Mode 6 (0x0e) - 9 5 5 5 - new Bc6hsModeDescriptor(Bc6hEField.M, 0), new Bc6hsModeDescriptor(Bc6hEField.M, 1), new Bc6hsModeDescriptor(Bc6hEField.M, 2), new Bc6hsModeDescriptor(Bc6hEField.M, 3), new Bc6hsModeDescriptor(Bc6hEField.M, 4), new Bc6hsModeDescriptor(Bc6hEField.RW, 0), new Bc6hsModeDescriptor(Bc6hEField.RW, 1), new Bc6hsModeDescriptor(Bc6hEField.RW, 2), new Bc6hsModeDescriptor(Bc6hEField.RW, 3), new Bc6hsModeDescriptor(Bc6hEField.RW, 4), - new Bc6hsModeDescriptor(Bc6hEField.RW, 5), new Bc6hsModeDescriptor(Bc6hEField.RW, 6), new Bc6hsModeDescriptor(Bc6hEField.RW, 7), new Bc6hsModeDescriptor(Bc6hEField.RW, 8), new Bc6hsModeDescriptor(Bc6hEField.BY, 4), new Bc6hsModeDescriptor(Bc6hEField.GW, 0), new Bc6hsModeDescriptor(Bc6hEField.GW, 1), new Bc6hsModeDescriptor(Bc6hEField.GW, 2), new Bc6hsModeDescriptor(Bc6hEField.GW, 3), new Bc6hsModeDescriptor(Bc6hEField.GW, 4), - new Bc6hsModeDescriptor(Bc6hEField.GW, 5), new Bc6hsModeDescriptor(Bc6hEField.GW, 6), new Bc6hsModeDescriptor(Bc6hEField.GW, 7), new Bc6hsModeDescriptor(Bc6hEField.GW, 8), new Bc6hsModeDescriptor(Bc6hEField.GY, 4), new Bc6hsModeDescriptor(Bc6hEField.BW, 0), new Bc6hsModeDescriptor(Bc6hEField.BW, 1), new Bc6hsModeDescriptor(Bc6hEField.BW, 2), new Bc6hsModeDescriptor(Bc6hEField.BW, 3), new Bc6hsModeDescriptor(Bc6hEField.BW, 4), - new Bc6hsModeDescriptor(Bc6hEField.BW, 5), new Bc6hsModeDescriptor(Bc6hEField.BW, 6), new Bc6hsModeDescriptor(Bc6hEField.BW, 7), new Bc6hsModeDescriptor(Bc6hEField.BW, 8), new Bc6hsModeDescriptor(Bc6hEField.BZ, 4), new Bc6hsModeDescriptor(Bc6hEField.RX, 0), new Bc6hsModeDescriptor(Bc6hEField.RX, 1), new Bc6hsModeDescriptor(Bc6hEField.RX, 2), new Bc6hsModeDescriptor(Bc6hEField.RX, 3), new Bc6hsModeDescriptor(Bc6hEField.RX, 4), - new Bc6hsModeDescriptor(Bc6hEField.GZ, 4), new Bc6hsModeDescriptor(Bc6hEField.GY, 0), new Bc6hsModeDescriptor(Bc6hEField.GY, 1), new Bc6hsModeDescriptor(Bc6hEField.GY, 2), new Bc6hsModeDescriptor(Bc6hEField.GY, 3), new Bc6hsModeDescriptor(Bc6hEField.GX, 0), new Bc6hsModeDescriptor(Bc6hEField.GX, 1), new Bc6hsModeDescriptor(Bc6hEField.GX, 2), new Bc6hsModeDescriptor(Bc6hEField.GX, 3), new Bc6hsModeDescriptor(Bc6hEField.GX, 4), - new Bc6hsModeDescriptor(Bc6hEField.BZ, 0), new Bc6hsModeDescriptor(Bc6hEField.GZ, 0), new Bc6hsModeDescriptor(Bc6hEField.GZ, 1), new Bc6hsModeDescriptor(Bc6hEField.GZ, 2), new Bc6hsModeDescriptor(Bc6hEField.GZ, 3), new Bc6hsModeDescriptor(Bc6hEField.BX, 0), new Bc6hsModeDescriptor(Bc6hEField.BX, 1), new Bc6hsModeDescriptor(Bc6hEField.BX, 2), new Bc6hsModeDescriptor(Bc6hEField.BX, 3), new Bc6hsModeDescriptor(Bc6hEField.BX, 4), - new Bc6hsModeDescriptor(Bc6hEField.BZ, 1), new Bc6hsModeDescriptor(Bc6hEField.BY, 0), new Bc6hsModeDescriptor(Bc6hEField.BY, 1), new Bc6hsModeDescriptor(Bc6hEField.BY, 2), new Bc6hsModeDescriptor(Bc6hEField.BY, 3), new Bc6hsModeDescriptor(Bc6hEField.RY, 0), new Bc6hsModeDescriptor(Bc6hEField.RY, 1), new Bc6hsModeDescriptor(Bc6hEField.RY, 2), new Bc6hsModeDescriptor(Bc6hEField.RY, 3), new Bc6hsModeDescriptor(Bc6hEField.RY, 4), - new Bc6hsModeDescriptor(Bc6hEField.BZ, 2), new Bc6hsModeDescriptor(Bc6hEField.RZ, 0), new Bc6hsModeDescriptor(Bc6hEField.RZ, 1), new Bc6hsModeDescriptor(Bc6hEField.RZ, 2), new Bc6hsModeDescriptor(Bc6hEField.RZ, 3), new Bc6hsModeDescriptor(Bc6hEField.RZ, 4), new Bc6hsModeDescriptor(Bc6hEField.BZ, 3), new Bc6hsModeDescriptor(Bc6hEField.D, 0), new Bc6hsModeDescriptor(Bc6hEField.D, 1), new Bc6hsModeDescriptor(Bc6hEField.D, 2), - new Bc6hsModeDescriptor(Bc6hEField.D, 3), new Bc6hsModeDescriptor(Bc6hEField.D, 4) - }, - - new[] - { - // Mode 7 (0x12) - 8 6 5 5 - new Bc6hsModeDescriptor(Bc6hEField.M, 0), new Bc6hsModeDescriptor(Bc6hEField.M, 1), new Bc6hsModeDescriptor(Bc6hEField.M, 2), new Bc6hsModeDescriptor(Bc6hEField.M, 3), new Bc6hsModeDescriptor(Bc6hEField.M, 4), new Bc6hsModeDescriptor(Bc6hEField.RW, 0), new Bc6hsModeDescriptor(Bc6hEField.RW, 1), new Bc6hsModeDescriptor(Bc6hEField.RW, 2), new Bc6hsModeDescriptor(Bc6hEField.RW, 3), new Bc6hsModeDescriptor(Bc6hEField.RW, 4), - new Bc6hsModeDescriptor(Bc6hEField.RW, 5), new Bc6hsModeDescriptor(Bc6hEField.RW, 6), new Bc6hsModeDescriptor(Bc6hEField.RW, 7), new Bc6hsModeDescriptor(Bc6hEField.GZ, 4), new Bc6hsModeDescriptor(Bc6hEField.BY, 4), new Bc6hsModeDescriptor(Bc6hEField.GW, 0), new Bc6hsModeDescriptor(Bc6hEField.GW, 1), new Bc6hsModeDescriptor(Bc6hEField.GW, 2), new Bc6hsModeDescriptor(Bc6hEField.GW, 3), new Bc6hsModeDescriptor(Bc6hEField.GW, 4), - new Bc6hsModeDescriptor(Bc6hEField.GW, 5), new Bc6hsModeDescriptor(Bc6hEField.GW, 6), new Bc6hsModeDescriptor(Bc6hEField.GW, 7), new Bc6hsModeDescriptor(Bc6hEField.BZ, 2), new Bc6hsModeDescriptor(Bc6hEField.GY, 4), new Bc6hsModeDescriptor(Bc6hEField.BW, 0), new Bc6hsModeDescriptor(Bc6hEField.BW, 1), new Bc6hsModeDescriptor(Bc6hEField.BW, 2), new Bc6hsModeDescriptor(Bc6hEField.BW, 3), new Bc6hsModeDescriptor(Bc6hEField.BW, 4), - new Bc6hsModeDescriptor(Bc6hEField.BW, 5), new Bc6hsModeDescriptor(Bc6hEField.BW, 6), new Bc6hsModeDescriptor(Bc6hEField.BW, 7), new Bc6hsModeDescriptor(Bc6hEField.BZ, 3), new Bc6hsModeDescriptor(Bc6hEField.BZ, 4), new Bc6hsModeDescriptor(Bc6hEField.RX, 0), new Bc6hsModeDescriptor(Bc6hEField.RX, 1), new Bc6hsModeDescriptor(Bc6hEField.RX, 2), new Bc6hsModeDescriptor(Bc6hEField.RX, 3), new Bc6hsModeDescriptor(Bc6hEField.RX, 4), - new Bc6hsModeDescriptor(Bc6hEField.RX, 5), new Bc6hsModeDescriptor(Bc6hEField.GY, 0), new Bc6hsModeDescriptor(Bc6hEField.GY, 1), new Bc6hsModeDescriptor(Bc6hEField.GY, 2), new Bc6hsModeDescriptor(Bc6hEField.GY, 3), new Bc6hsModeDescriptor(Bc6hEField.GX, 0), new Bc6hsModeDescriptor(Bc6hEField.GX, 1), new Bc6hsModeDescriptor(Bc6hEField.GX, 2), new Bc6hsModeDescriptor(Bc6hEField.GX, 3), new Bc6hsModeDescriptor(Bc6hEField.GX, 4), - new Bc6hsModeDescriptor(Bc6hEField.BZ, 0), new Bc6hsModeDescriptor(Bc6hEField.GZ, 0), new Bc6hsModeDescriptor(Bc6hEField.GZ, 1), new Bc6hsModeDescriptor(Bc6hEField.GZ, 2), new Bc6hsModeDescriptor(Bc6hEField.GZ, 3), new Bc6hsModeDescriptor(Bc6hEField.BX, 0), new Bc6hsModeDescriptor(Bc6hEField.BX, 1), new Bc6hsModeDescriptor(Bc6hEField.BX, 2), new Bc6hsModeDescriptor(Bc6hEField.BX, 3), new Bc6hsModeDescriptor(Bc6hEField.BX, 4), - new Bc6hsModeDescriptor(Bc6hEField.BZ, 1), new Bc6hsModeDescriptor(Bc6hEField.BY, 0), new Bc6hsModeDescriptor(Bc6hEField.BY, 1), new Bc6hsModeDescriptor(Bc6hEField.BY, 2), new Bc6hsModeDescriptor(Bc6hEField.BY, 3), new Bc6hsModeDescriptor(Bc6hEField.RY, 0), new Bc6hsModeDescriptor(Bc6hEField.RY, 1), new Bc6hsModeDescriptor(Bc6hEField.RY, 2), new Bc6hsModeDescriptor(Bc6hEField.RY, 3), new Bc6hsModeDescriptor(Bc6hEField.RY, 4), - new Bc6hsModeDescriptor(Bc6hEField.RY, 5), new Bc6hsModeDescriptor(Bc6hEField.RZ, 0), new Bc6hsModeDescriptor(Bc6hEField.RZ, 1), new Bc6hsModeDescriptor(Bc6hEField.RZ, 2), new Bc6hsModeDescriptor(Bc6hEField.RZ, 3), new Bc6hsModeDescriptor(Bc6hEField.RZ, 4), new Bc6hsModeDescriptor(Bc6hEField.RZ, 5), new Bc6hsModeDescriptor(Bc6hEField.D, 0), new Bc6hsModeDescriptor(Bc6hEField.D, 1), new Bc6hsModeDescriptor(Bc6hEField.D, 2), - new Bc6hsModeDescriptor(Bc6hEField.D, 3), new Bc6hsModeDescriptor(Bc6hEField.D, 4) - }, - - new[] + // I would prefer to use Span, but not sure if I should reference System.Memory in this project + // copy data instead + Buffer.BlockCopy(blockData, streamIndex, currentBlock, 0, currentBlock.Length); + streamIndex += currentBlock.Length; + + uint uStartBit = 0; + byte uMode = GetBits(currentBlock, ref uStartBit, 2u); + if (uMode is not 0x00 and not 0x01) { - // Mode 8 (0x16) - 8 5 6 5 - new Bc6hsModeDescriptor(Bc6hEField.M, 0), new Bc6hsModeDescriptor(Bc6hEField.M, 1), new Bc6hsModeDescriptor(Bc6hEField.M, 2), new Bc6hsModeDescriptor(Bc6hEField.M, 3), new Bc6hsModeDescriptor(Bc6hEField.M, 4), new Bc6hsModeDescriptor(Bc6hEField.RW, 0), new Bc6hsModeDescriptor(Bc6hEField.RW, 1), new Bc6hsModeDescriptor(Bc6hEField.RW, 2), new Bc6hsModeDescriptor(Bc6hEField.RW, 3), new Bc6hsModeDescriptor(Bc6hEField.RW, 4), - new Bc6hsModeDescriptor(Bc6hEField.RW, 5), new Bc6hsModeDescriptor(Bc6hEField.RW, 6), new Bc6hsModeDescriptor(Bc6hEField.RW, 7), new Bc6hsModeDescriptor(Bc6hEField.BZ, 0), new Bc6hsModeDescriptor(Bc6hEField.BY, 4), new Bc6hsModeDescriptor(Bc6hEField.GW, 0), new Bc6hsModeDescriptor(Bc6hEField.GW, 1), new Bc6hsModeDescriptor(Bc6hEField.GW, 2), new Bc6hsModeDescriptor(Bc6hEField.GW, 3), new Bc6hsModeDescriptor(Bc6hEField.GW, 4), - new Bc6hsModeDescriptor(Bc6hEField.GW, 5), new Bc6hsModeDescriptor(Bc6hEField.GW, 6), new Bc6hsModeDescriptor(Bc6hEField.GW, 7), new Bc6hsModeDescriptor(Bc6hEField.GY, 5), new Bc6hsModeDescriptor(Bc6hEField.GY, 4), new Bc6hsModeDescriptor(Bc6hEField.BW, 0), new Bc6hsModeDescriptor(Bc6hEField.BW, 1), new Bc6hsModeDescriptor(Bc6hEField.BW, 2), new Bc6hsModeDescriptor(Bc6hEField.BW, 3), new Bc6hsModeDescriptor(Bc6hEField.BW, 4), - new Bc6hsModeDescriptor(Bc6hEField.BW, 5), new Bc6hsModeDescriptor(Bc6hEField.BW, 6), new Bc6hsModeDescriptor(Bc6hEField.BW, 7), new Bc6hsModeDescriptor(Bc6hEField.GZ, 5), new Bc6hsModeDescriptor(Bc6hEField.BZ, 4), new Bc6hsModeDescriptor(Bc6hEField.RX, 0), new Bc6hsModeDescriptor(Bc6hEField.RX, 1), new Bc6hsModeDescriptor(Bc6hEField.RX, 2), new Bc6hsModeDescriptor(Bc6hEField.RX, 3), new Bc6hsModeDescriptor(Bc6hEField.RX, 4), - new Bc6hsModeDescriptor(Bc6hEField.GZ, 4), new Bc6hsModeDescriptor(Bc6hEField.GY, 0), new Bc6hsModeDescriptor(Bc6hEField.GY, 1), new Bc6hsModeDescriptor(Bc6hEField.GY, 2), new Bc6hsModeDescriptor(Bc6hEField.GY, 3), new Bc6hsModeDescriptor(Bc6hEField.GX, 0), new Bc6hsModeDescriptor(Bc6hEField.GX, 1), new Bc6hsModeDescriptor(Bc6hEField.GX, 2), new Bc6hsModeDescriptor(Bc6hEField.GX, 3), new Bc6hsModeDescriptor(Bc6hEField.GX, 4), - new Bc6hsModeDescriptor(Bc6hEField.GX, 5), new Bc6hsModeDescriptor(Bc6hEField.GZ, 0), new Bc6hsModeDescriptor(Bc6hEField.GZ, 1), new Bc6hsModeDescriptor(Bc6hEField.GZ, 2), new Bc6hsModeDescriptor(Bc6hEField.GZ, 3), new Bc6hsModeDescriptor(Bc6hEField.BX, 0), new Bc6hsModeDescriptor(Bc6hEField.BX, 1), new Bc6hsModeDescriptor(Bc6hEField.BX, 2), new Bc6hsModeDescriptor(Bc6hEField.BX, 3), new Bc6hsModeDescriptor(Bc6hEField.BX, 4), - new Bc6hsModeDescriptor(Bc6hEField.BZ, 1), new Bc6hsModeDescriptor(Bc6hEField.BY, 0), new Bc6hsModeDescriptor(Bc6hEField.BY, 1), new Bc6hsModeDescriptor(Bc6hEField.BY, 2), new Bc6hsModeDescriptor(Bc6hEField.BY, 3), new Bc6hsModeDescriptor(Bc6hEField.RY, 0), new Bc6hsModeDescriptor(Bc6hEField.RY, 1), new Bc6hsModeDescriptor(Bc6hEField.RY, 2), new Bc6hsModeDescriptor(Bc6hEField.RY, 3), new Bc6hsModeDescriptor(Bc6hEField.RY, 4), - new Bc6hsModeDescriptor(Bc6hEField.BZ, 2), new Bc6hsModeDescriptor(Bc6hEField.RZ, 0), new Bc6hsModeDescriptor(Bc6hEField.RZ, 1), new Bc6hsModeDescriptor(Bc6hEField.RZ, 2), new Bc6hsModeDescriptor(Bc6hEField.RZ, 3), new Bc6hsModeDescriptor(Bc6hEField.RZ, 4), new Bc6hsModeDescriptor(Bc6hEField.BZ, 3), new Bc6hsModeDescriptor(Bc6hEField.D, 0), new Bc6hsModeDescriptor(Bc6hEField.D, 1), new Bc6hsModeDescriptor(Bc6hEField.D, 2), - new Bc6hsModeDescriptor(Bc6hEField.D, 3), new Bc6hsModeDescriptor(Bc6hEField.D, 4) - }, - - new[] - { - // Mode 9 (0x1a) - 8 5 5 6 - new Bc6hsModeDescriptor(Bc6hEField.M, 0), new Bc6hsModeDescriptor(Bc6hEField.M, 1), new Bc6hsModeDescriptor(Bc6hEField.M, 2), new Bc6hsModeDescriptor(Bc6hEField.M, 3), new Bc6hsModeDescriptor(Bc6hEField.M, 4), new Bc6hsModeDescriptor(Bc6hEField.RW, 0), new Bc6hsModeDescriptor(Bc6hEField.RW, 1), new Bc6hsModeDescriptor(Bc6hEField.RW, 2), new Bc6hsModeDescriptor(Bc6hEField.RW, 3), new Bc6hsModeDescriptor(Bc6hEField.RW, 4), - new Bc6hsModeDescriptor(Bc6hEField.RW, 5), new Bc6hsModeDescriptor(Bc6hEField.RW, 6), new Bc6hsModeDescriptor(Bc6hEField.RW, 7), new Bc6hsModeDescriptor(Bc6hEField.BZ, 1), new Bc6hsModeDescriptor(Bc6hEField.BY, 4), new Bc6hsModeDescriptor(Bc6hEField.GW, 0), new Bc6hsModeDescriptor(Bc6hEField.GW, 1), new Bc6hsModeDescriptor(Bc6hEField.GW, 2), new Bc6hsModeDescriptor(Bc6hEField.GW, 3), new Bc6hsModeDescriptor(Bc6hEField.GW, 4), - new Bc6hsModeDescriptor(Bc6hEField.GW, 5), new Bc6hsModeDescriptor(Bc6hEField.GW, 6), new Bc6hsModeDescriptor(Bc6hEField.GW, 7), new Bc6hsModeDescriptor(Bc6hEField.BY, 5), new Bc6hsModeDescriptor(Bc6hEField.GY, 4), new Bc6hsModeDescriptor(Bc6hEField.BW, 0), new Bc6hsModeDescriptor(Bc6hEField.BW, 1), new Bc6hsModeDescriptor(Bc6hEField.BW, 2), new Bc6hsModeDescriptor(Bc6hEField.BW, 3), new Bc6hsModeDescriptor(Bc6hEField.BW, 4), - new Bc6hsModeDescriptor(Bc6hEField.BW, 5), new Bc6hsModeDescriptor(Bc6hEField.BW, 6), new Bc6hsModeDescriptor(Bc6hEField.BW, 7), new Bc6hsModeDescriptor(Bc6hEField.BZ, 5), new Bc6hsModeDescriptor(Bc6hEField.BZ, 4), new Bc6hsModeDescriptor(Bc6hEField.RX, 0), new Bc6hsModeDescriptor(Bc6hEField.RX, 1), new Bc6hsModeDescriptor(Bc6hEField.RX, 2), new Bc6hsModeDescriptor(Bc6hEField.RX, 3), new Bc6hsModeDescriptor(Bc6hEField.RX, 4), - new Bc6hsModeDescriptor(Bc6hEField.GZ, 4), new Bc6hsModeDescriptor(Bc6hEField.GY, 0), new Bc6hsModeDescriptor(Bc6hEField.GY, 1), new Bc6hsModeDescriptor(Bc6hEField.GY, 2), new Bc6hsModeDescriptor(Bc6hEField.GY, 3), new Bc6hsModeDescriptor(Bc6hEField.GX, 0), new Bc6hsModeDescriptor(Bc6hEField.GX, 1), new Bc6hsModeDescriptor(Bc6hEField.GX, 2), new Bc6hsModeDescriptor(Bc6hEField.GX, 3), new Bc6hsModeDescriptor(Bc6hEField.GX, 4), - new Bc6hsModeDescriptor(Bc6hEField.BZ, 0), new Bc6hsModeDescriptor(Bc6hEField.GZ, 0), new Bc6hsModeDescriptor(Bc6hEField.GZ, 1), new Bc6hsModeDescriptor(Bc6hEField.GZ, 2), new Bc6hsModeDescriptor(Bc6hEField.GZ, 3), new Bc6hsModeDescriptor(Bc6hEField.BX, 0), new Bc6hsModeDescriptor(Bc6hEField.BX, 1), new Bc6hsModeDescriptor(Bc6hEField.BX, 2), new Bc6hsModeDescriptor(Bc6hEField.BX, 3), new Bc6hsModeDescriptor(Bc6hEField.BX, 4), - new Bc6hsModeDescriptor(Bc6hEField.BX, 5), new Bc6hsModeDescriptor(Bc6hEField.BY, 0), new Bc6hsModeDescriptor(Bc6hEField.BY, 1), new Bc6hsModeDescriptor(Bc6hEField.BY, 2), new Bc6hsModeDescriptor(Bc6hEField.BY, 3), new Bc6hsModeDescriptor(Bc6hEField.RY, 0), new Bc6hsModeDescriptor(Bc6hEField.RY, 1), new Bc6hsModeDescriptor(Bc6hEField.RY, 2), new Bc6hsModeDescriptor(Bc6hEField.RY, 3), new Bc6hsModeDescriptor(Bc6hEField.RY, 4), - new Bc6hsModeDescriptor(Bc6hEField.BZ, 2), new Bc6hsModeDescriptor(Bc6hEField.RZ, 0), new Bc6hsModeDescriptor(Bc6hEField.RZ, 1), new Bc6hsModeDescriptor(Bc6hEField.RZ, 2), new Bc6hsModeDescriptor(Bc6hEField.RZ, 3), new Bc6hsModeDescriptor(Bc6hEField.RZ, 4), new Bc6hsModeDescriptor(Bc6hEField.BZ, 3), new Bc6hsModeDescriptor(Bc6hEField.D, 0), new Bc6hsModeDescriptor(Bc6hEField.D, 1), new Bc6hsModeDescriptor(Bc6hEField.D, 2), - new Bc6hsModeDescriptor(Bc6hEField.D, 3), new Bc6hsModeDescriptor(Bc6hEField.D, 4) - }, - - new[] - { - // Mode 10 (0x1e) - 6 6 6 6 - new Bc6hsModeDescriptor(Bc6hEField.M, 0), new Bc6hsModeDescriptor(Bc6hEField.M, 1), new Bc6hsModeDescriptor(Bc6hEField.M, 2), new Bc6hsModeDescriptor(Bc6hEField.M, 3), new Bc6hsModeDescriptor(Bc6hEField.M, 4), new Bc6hsModeDescriptor(Bc6hEField.RW, 0), new Bc6hsModeDescriptor(Bc6hEField.RW, 1), new Bc6hsModeDescriptor(Bc6hEField.RW, 2), new Bc6hsModeDescriptor(Bc6hEField.RW, 3), new Bc6hsModeDescriptor(Bc6hEField.RW, 4), - new Bc6hsModeDescriptor(Bc6hEField.RW, 5), new Bc6hsModeDescriptor(Bc6hEField.GZ, 4), new Bc6hsModeDescriptor(Bc6hEField.BZ, 0), new Bc6hsModeDescriptor(Bc6hEField.BZ, 1), new Bc6hsModeDescriptor(Bc6hEField.BY, 4), new Bc6hsModeDescriptor(Bc6hEField.GW, 0), new Bc6hsModeDescriptor(Bc6hEField.GW, 1), new Bc6hsModeDescriptor(Bc6hEField.GW, 2), new Bc6hsModeDescriptor(Bc6hEField.GW, 3), new Bc6hsModeDescriptor(Bc6hEField.GW, 4), - new Bc6hsModeDescriptor(Bc6hEField.GW, 5), new Bc6hsModeDescriptor(Bc6hEField.GY, 5), new Bc6hsModeDescriptor(Bc6hEField.BY, 5), new Bc6hsModeDescriptor(Bc6hEField.BZ, 2), new Bc6hsModeDescriptor(Bc6hEField.GY, 4), new Bc6hsModeDescriptor(Bc6hEField.BW, 0), new Bc6hsModeDescriptor(Bc6hEField.BW, 1), new Bc6hsModeDescriptor(Bc6hEField.BW, 2), new Bc6hsModeDescriptor(Bc6hEField.BW, 3), new Bc6hsModeDescriptor(Bc6hEField.BW, 4), - new Bc6hsModeDescriptor(Bc6hEField.BW, 5), new Bc6hsModeDescriptor(Bc6hEField.GZ, 5), new Bc6hsModeDescriptor(Bc6hEField.BZ, 3), new Bc6hsModeDescriptor(Bc6hEField.BZ, 5), new Bc6hsModeDescriptor(Bc6hEField.BZ, 4), new Bc6hsModeDescriptor(Bc6hEField.RX, 0), new Bc6hsModeDescriptor(Bc6hEField.RX, 1), new Bc6hsModeDescriptor(Bc6hEField.RX, 2), new Bc6hsModeDescriptor(Bc6hEField.RX, 3), new Bc6hsModeDescriptor(Bc6hEField.RX, 4), - new Bc6hsModeDescriptor(Bc6hEField.RX, 5), new Bc6hsModeDescriptor(Bc6hEField.GY, 0), new Bc6hsModeDescriptor(Bc6hEField.GY, 1), new Bc6hsModeDescriptor(Bc6hEField.GY, 2), new Bc6hsModeDescriptor(Bc6hEField.GY, 3), new Bc6hsModeDescriptor(Bc6hEField.GX, 0), new Bc6hsModeDescriptor(Bc6hEField.GX, 1), new Bc6hsModeDescriptor(Bc6hEField.GX, 2), new Bc6hsModeDescriptor(Bc6hEField.GX, 3), new Bc6hsModeDescriptor(Bc6hEField.GX, 4), - new Bc6hsModeDescriptor(Bc6hEField.GX, 5), new Bc6hsModeDescriptor(Bc6hEField.GZ, 0), new Bc6hsModeDescriptor(Bc6hEField.GZ, 1), new Bc6hsModeDescriptor(Bc6hEField.GZ, 2), new Bc6hsModeDescriptor(Bc6hEField.GZ, 3), new Bc6hsModeDescriptor(Bc6hEField.BX, 0), new Bc6hsModeDescriptor(Bc6hEField.BX, 1), new Bc6hsModeDescriptor(Bc6hEField.BX, 2), new Bc6hsModeDescriptor(Bc6hEField.BX, 3), new Bc6hsModeDescriptor(Bc6hEField.BX, 4), - new Bc6hsModeDescriptor(Bc6hEField.BX, 5), new Bc6hsModeDescriptor(Bc6hEField.BY, 0), new Bc6hsModeDescriptor(Bc6hEField.BY, 1), new Bc6hsModeDescriptor(Bc6hEField.BY, 2), new Bc6hsModeDescriptor(Bc6hEField.BY, 3), new Bc6hsModeDescriptor(Bc6hEField.RY, 0), new Bc6hsModeDescriptor(Bc6hEField.RY, 1), new Bc6hsModeDescriptor(Bc6hEField.RY, 2), new Bc6hsModeDescriptor(Bc6hEField.RY, 3), new Bc6hsModeDescriptor(Bc6hEField.RY, 4), - new Bc6hsModeDescriptor(Bc6hEField.RY, 5), new Bc6hsModeDescriptor(Bc6hEField.RZ, 0), new Bc6hsModeDescriptor(Bc6hEField.RZ, 1), new Bc6hsModeDescriptor(Bc6hEField.RZ, 2), new Bc6hsModeDescriptor(Bc6hEField.RZ, 3), new Bc6hsModeDescriptor(Bc6hEField.RZ, 4), new Bc6hsModeDescriptor(Bc6hEField.RZ, 5), new Bc6hsModeDescriptor(Bc6hEField.D, 0), new Bc6hsModeDescriptor(Bc6hEField.D, 1), new Bc6hsModeDescriptor(Bc6hEField.D, 2), - new Bc6hsModeDescriptor(Bc6hEField.D, 3), new Bc6hsModeDescriptor(Bc6hEField.D, 4) - }, - - new[] - { - // Mode 11 (0x03) - 10 10 - new Bc6hsModeDescriptor(Bc6hEField.M, 0), new Bc6hsModeDescriptor(Bc6hEField.M, 1), new Bc6hsModeDescriptor(Bc6hEField.M, 2), new Bc6hsModeDescriptor(Bc6hEField.M, 3), new Bc6hsModeDescriptor(Bc6hEField.M, 4), new Bc6hsModeDescriptor(Bc6hEField.RW, 0), new Bc6hsModeDescriptor(Bc6hEField.RW, 1), new Bc6hsModeDescriptor(Bc6hEField.RW, 2), new Bc6hsModeDescriptor(Bc6hEField.RW, 3), new Bc6hsModeDescriptor(Bc6hEField.RW, 4), - new Bc6hsModeDescriptor(Bc6hEField.RW, 5), new Bc6hsModeDescriptor(Bc6hEField.RW, 6), new Bc6hsModeDescriptor(Bc6hEField.RW, 7), new Bc6hsModeDescriptor(Bc6hEField.RW, 8), new Bc6hsModeDescriptor(Bc6hEField.RW, 9), new Bc6hsModeDescriptor(Bc6hEField.GW, 0), new Bc6hsModeDescriptor(Bc6hEField.GW, 1), new Bc6hsModeDescriptor(Bc6hEField.GW, 2), new Bc6hsModeDescriptor(Bc6hEField.GW, 3), new Bc6hsModeDescriptor(Bc6hEField.GW, 4), - new Bc6hsModeDescriptor(Bc6hEField.GW, 5), new Bc6hsModeDescriptor(Bc6hEField.GW, 6), new Bc6hsModeDescriptor(Bc6hEField.GW, 7), new Bc6hsModeDescriptor(Bc6hEField.GW, 8), new Bc6hsModeDescriptor(Bc6hEField.GW, 9), new Bc6hsModeDescriptor(Bc6hEField.BW, 0), new Bc6hsModeDescriptor(Bc6hEField.BW, 1), new Bc6hsModeDescriptor(Bc6hEField.BW, 2), new Bc6hsModeDescriptor(Bc6hEField.BW, 3), new Bc6hsModeDescriptor(Bc6hEField.BW, 4), - new Bc6hsModeDescriptor(Bc6hEField.BW, 5), new Bc6hsModeDescriptor(Bc6hEField.BW, 6), new Bc6hsModeDescriptor(Bc6hEField.BW, 7), new Bc6hsModeDescriptor(Bc6hEField.BW, 8), new Bc6hsModeDescriptor(Bc6hEField.BW, 9), new Bc6hsModeDescriptor(Bc6hEField.RX, 0), new Bc6hsModeDescriptor(Bc6hEField.RX, 1), new Bc6hsModeDescriptor(Bc6hEField.RX, 2), new Bc6hsModeDescriptor(Bc6hEField.RX, 3), new Bc6hsModeDescriptor(Bc6hEField.RX, 4), - new Bc6hsModeDescriptor(Bc6hEField.RX, 5), new Bc6hsModeDescriptor(Bc6hEField.RX, 6), new Bc6hsModeDescriptor(Bc6hEField.RX, 7), new Bc6hsModeDescriptor(Bc6hEField.RX, 8), new Bc6hsModeDescriptor(Bc6hEField.RX, 9), new Bc6hsModeDescriptor(Bc6hEField.GX, 0), new Bc6hsModeDescriptor(Bc6hEField.GX, 1), new Bc6hsModeDescriptor(Bc6hEField.GX, 2), new Bc6hsModeDescriptor(Bc6hEField.GX, 3), new Bc6hsModeDescriptor(Bc6hEField.GX, 4), - new Bc6hsModeDescriptor(Bc6hEField.GX, 5), new Bc6hsModeDescriptor(Bc6hEField.GX, 6), new Bc6hsModeDescriptor(Bc6hEField.GX, 7), new Bc6hsModeDescriptor(Bc6hEField.GX, 8), new Bc6hsModeDescriptor(Bc6hEField.GX, 9), new Bc6hsModeDescriptor(Bc6hEField.BX, 0), new Bc6hsModeDescriptor(Bc6hEField.BX, 1), new Bc6hsModeDescriptor(Bc6hEField.BX, 2), new Bc6hsModeDescriptor(Bc6hEField.BX, 3), new Bc6hsModeDescriptor(Bc6hEField.BX, 4), - new Bc6hsModeDescriptor(Bc6hEField.BX, 5), new Bc6hsModeDescriptor(Bc6hEField.BX, 6), new Bc6hsModeDescriptor(Bc6hEField.BX, 7), new Bc6hsModeDescriptor(Bc6hEField.BX, 8), new Bc6hsModeDescriptor(Bc6hEField.BX, 9), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), - new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), - new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0) - }, - - new[] - { - // Mode 12 (0x07) - 11 9 - new Bc6hsModeDescriptor(Bc6hEField.M, 0), new Bc6hsModeDescriptor(Bc6hEField.M, 1), new Bc6hsModeDescriptor(Bc6hEField.M, 2), new Bc6hsModeDescriptor(Bc6hEField.M, 3), new Bc6hsModeDescriptor(Bc6hEField.M, 4), new Bc6hsModeDescriptor(Bc6hEField.RW, 0), new Bc6hsModeDescriptor(Bc6hEField.RW, 1), new Bc6hsModeDescriptor(Bc6hEField.RW, 2), new Bc6hsModeDescriptor(Bc6hEField.RW, 3), new Bc6hsModeDescriptor(Bc6hEField.RW, 4), - new Bc6hsModeDescriptor(Bc6hEField.RW, 5), new Bc6hsModeDescriptor(Bc6hEField.RW, 6), new Bc6hsModeDescriptor(Bc6hEField.RW, 7), new Bc6hsModeDescriptor(Bc6hEField.RW, 8), new Bc6hsModeDescriptor(Bc6hEField.RW, 9), new Bc6hsModeDescriptor(Bc6hEField.GW, 0), new Bc6hsModeDescriptor(Bc6hEField.GW, 1), new Bc6hsModeDescriptor(Bc6hEField.GW, 2), new Bc6hsModeDescriptor(Bc6hEField.GW, 3), new Bc6hsModeDescriptor(Bc6hEField.GW, 4), - new Bc6hsModeDescriptor(Bc6hEField.GW, 5), new Bc6hsModeDescriptor(Bc6hEField.GW, 6), new Bc6hsModeDescriptor(Bc6hEField.GW, 7), new Bc6hsModeDescriptor(Bc6hEField.GW, 8), new Bc6hsModeDescriptor(Bc6hEField.GW, 9), new Bc6hsModeDescriptor(Bc6hEField.BW, 0), new Bc6hsModeDescriptor(Bc6hEField.BW, 1), new Bc6hsModeDescriptor(Bc6hEField.BW, 2), new Bc6hsModeDescriptor(Bc6hEField.BW, 3), new Bc6hsModeDescriptor(Bc6hEField.BW, 4), - new Bc6hsModeDescriptor(Bc6hEField.BW, 5), new Bc6hsModeDescriptor(Bc6hEField.BW, 6), new Bc6hsModeDescriptor(Bc6hEField.BW, 7), new Bc6hsModeDescriptor(Bc6hEField.BW, 8), new Bc6hsModeDescriptor(Bc6hEField.BW, 9), new Bc6hsModeDescriptor(Bc6hEField.RX, 0), new Bc6hsModeDescriptor(Bc6hEField.RX, 1), new Bc6hsModeDescriptor(Bc6hEField.RX, 2), new Bc6hsModeDescriptor(Bc6hEField.RX, 3), new Bc6hsModeDescriptor(Bc6hEField.RX, 4), - new Bc6hsModeDescriptor(Bc6hEField.RX, 5), new Bc6hsModeDescriptor(Bc6hEField.RX, 6), new Bc6hsModeDescriptor(Bc6hEField.RX, 7), new Bc6hsModeDescriptor(Bc6hEField.RX, 8), new Bc6hsModeDescriptor(Bc6hEField.RW, 10), new Bc6hsModeDescriptor(Bc6hEField.GX, 0), new Bc6hsModeDescriptor(Bc6hEField.GX, 1), new Bc6hsModeDescriptor(Bc6hEField.GX, 2), new Bc6hsModeDescriptor(Bc6hEField.GX, 3), new Bc6hsModeDescriptor(Bc6hEField.GX, 4), - new Bc6hsModeDescriptor(Bc6hEField.GX, 5), new Bc6hsModeDescriptor(Bc6hEField.GX, 6), new Bc6hsModeDescriptor(Bc6hEField.GX, 7), new Bc6hsModeDescriptor(Bc6hEField.GX, 8), new Bc6hsModeDescriptor(Bc6hEField.GW, 10), new Bc6hsModeDescriptor(Bc6hEField.BX, 0), new Bc6hsModeDescriptor(Bc6hEField.BX, 1), new Bc6hsModeDescriptor(Bc6hEField.BX, 2), new Bc6hsModeDescriptor(Bc6hEField.BX, 3), new Bc6hsModeDescriptor(Bc6hEField.BX, 4), - new Bc6hsModeDescriptor(Bc6hEField.BX, 5), new Bc6hsModeDescriptor(Bc6hEField.BX, 6), new Bc6hsModeDescriptor(Bc6hEField.BX, 7), new Bc6hsModeDescriptor(Bc6hEField.BX, 8), new Bc6hsModeDescriptor(Bc6hEField.BW, 10), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), - new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), - new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0) - }, - - new[] - { - // Mode 13 (0x0b) - 12 8 - new Bc6hsModeDescriptor(Bc6hEField.M, 0), new Bc6hsModeDescriptor(Bc6hEField.M, 1), new Bc6hsModeDescriptor(Bc6hEField.M, 2), new Bc6hsModeDescriptor(Bc6hEField.M, 3), new Bc6hsModeDescriptor(Bc6hEField.M, 4), new Bc6hsModeDescriptor(Bc6hEField.RW, 0), new Bc6hsModeDescriptor(Bc6hEField.RW, 1), new Bc6hsModeDescriptor(Bc6hEField.RW, 2), new Bc6hsModeDescriptor(Bc6hEField.RW, 3), new Bc6hsModeDescriptor(Bc6hEField.RW, 4), - new Bc6hsModeDescriptor(Bc6hEField.RW, 5), new Bc6hsModeDescriptor(Bc6hEField.RW, 6), new Bc6hsModeDescriptor(Bc6hEField.RW, 7), new Bc6hsModeDescriptor(Bc6hEField.RW, 8), new Bc6hsModeDescriptor(Bc6hEField.RW, 9), new Bc6hsModeDescriptor(Bc6hEField.GW, 0), new Bc6hsModeDescriptor(Bc6hEField.GW, 1), new Bc6hsModeDescriptor(Bc6hEField.GW, 2), new Bc6hsModeDescriptor(Bc6hEField.GW, 3), new Bc6hsModeDescriptor(Bc6hEField.GW, 4), - new Bc6hsModeDescriptor(Bc6hEField.GW, 5), new Bc6hsModeDescriptor(Bc6hEField.GW, 6), new Bc6hsModeDescriptor(Bc6hEField.GW, 7), new Bc6hsModeDescriptor(Bc6hEField.GW, 8), new Bc6hsModeDescriptor(Bc6hEField.GW, 9), new Bc6hsModeDescriptor(Bc6hEField.BW, 0), new Bc6hsModeDescriptor(Bc6hEField.BW, 1), new Bc6hsModeDescriptor(Bc6hEField.BW, 2), new Bc6hsModeDescriptor(Bc6hEField.BW, 3), new Bc6hsModeDescriptor(Bc6hEField.BW, 4), - new Bc6hsModeDescriptor(Bc6hEField.BW, 5), new Bc6hsModeDescriptor(Bc6hEField.BW, 6), new Bc6hsModeDescriptor(Bc6hEField.BW, 7), new Bc6hsModeDescriptor(Bc6hEField.BW, 8), new Bc6hsModeDescriptor(Bc6hEField.BW, 9), new Bc6hsModeDescriptor(Bc6hEField.RX, 0), new Bc6hsModeDescriptor(Bc6hEField.RX, 1), new Bc6hsModeDescriptor(Bc6hEField.RX, 2), new Bc6hsModeDescriptor(Bc6hEField.RX, 3), new Bc6hsModeDescriptor(Bc6hEField.RX, 4), - new Bc6hsModeDescriptor(Bc6hEField.RX, 5), new Bc6hsModeDescriptor(Bc6hEField.RX, 6), new Bc6hsModeDescriptor(Bc6hEField.RX, 7), new Bc6hsModeDescriptor(Bc6hEField.RW, 11), new Bc6hsModeDescriptor(Bc6hEField.RW, 10), new Bc6hsModeDescriptor(Bc6hEField.GX, 0), new Bc6hsModeDescriptor(Bc6hEField.GX, 1), new Bc6hsModeDescriptor(Bc6hEField.GX, 2), new Bc6hsModeDescriptor(Bc6hEField.GX, 3), new Bc6hsModeDescriptor(Bc6hEField.GX, 4), - new Bc6hsModeDescriptor(Bc6hEField.GX, 5), new Bc6hsModeDescriptor(Bc6hEField.GX, 6), new Bc6hsModeDescriptor(Bc6hEField.GX, 7), new Bc6hsModeDescriptor(Bc6hEField.GW, 11), new Bc6hsModeDescriptor(Bc6hEField.GW, 10), new Bc6hsModeDescriptor(Bc6hEField.BX, 0), new Bc6hsModeDescriptor(Bc6hEField.BX, 1), new Bc6hsModeDescriptor(Bc6hEField.BX, 2), new Bc6hsModeDescriptor(Bc6hEField.BX, 3), new Bc6hsModeDescriptor(Bc6hEField.BX, 4), - new Bc6hsModeDescriptor(Bc6hEField.BX, 5), new Bc6hsModeDescriptor(Bc6hEField.BX, 6), new Bc6hsModeDescriptor(Bc6hEField.BX, 7), new Bc6hsModeDescriptor(Bc6hEField.BW, 11), new Bc6hsModeDescriptor(Bc6hEField.BW, 10), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), - new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), - new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0) - }, - - new[] - { - // Mode 14 (0x0f) - 16 4 - new Bc6hsModeDescriptor(Bc6hEField.M, 0), new Bc6hsModeDescriptor(Bc6hEField.M, 1), new Bc6hsModeDescriptor(Bc6hEField.M, 2), new Bc6hsModeDescriptor(Bc6hEField.M, 3), new Bc6hsModeDescriptor(Bc6hEField.M, 4), new Bc6hsModeDescriptor(Bc6hEField.RW, 0), new Bc6hsModeDescriptor(Bc6hEField.RW, 1), new Bc6hsModeDescriptor(Bc6hEField.RW, 2), new Bc6hsModeDescriptor(Bc6hEField.RW, 3), new Bc6hsModeDescriptor(Bc6hEField.RW, 4), - new Bc6hsModeDescriptor(Bc6hEField.RW, 5), new Bc6hsModeDescriptor(Bc6hEField.RW, 6), new Bc6hsModeDescriptor(Bc6hEField.RW, 7), new Bc6hsModeDescriptor(Bc6hEField.RW, 8), new Bc6hsModeDescriptor(Bc6hEField.RW, 9), new Bc6hsModeDescriptor(Bc6hEField.GW, 0), new Bc6hsModeDescriptor(Bc6hEField.GW, 1), new Bc6hsModeDescriptor(Bc6hEField.GW, 2), new Bc6hsModeDescriptor(Bc6hEField.GW, 3), new Bc6hsModeDescriptor(Bc6hEField.GW, 4), - new Bc6hsModeDescriptor(Bc6hEField.GW, 5), new Bc6hsModeDescriptor(Bc6hEField.GW, 6), new Bc6hsModeDescriptor(Bc6hEField.GW, 7), new Bc6hsModeDescriptor(Bc6hEField.GW, 8), new Bc6hsModeDescriptor(Bc6hEField.GW, 9), new Bc6hsModeDescriptor(Bc6hEField.BW, 0), new Bc6hsModeDescriptor(Bc6hEField.BW, 1), new Bc6hsModeDescriptor(Bc6hEField.BW, 2), new Bc6hsModeDescriptor(Bc6hEField.BW, 3), new Bc6hsModeDescriptor(Bc6hEField.BW, 4), - new Bc6hsModeDescriptor(Bc6hEField.BW, 5), new Bc6hsModeDescriptor(Bc6hEField.BW, 6), new Bc6hsModeDescriptor(Bc6hEField.BW, 7), new Bc6hsModeDescriptor(Bc6hEField.BW, 8), new Bc6hsModeDescriptor(Bc6hEField.BW, 9), new Bc6hsModeDescriptor(Bc6hEField.RX, 0), new Bc6hsModeDescriptor(Bc6hEField.RX, 1), new Bc6hsModeDescriptor(Bc6hEField.RX, 2), new Bc6hsModeDescriptor(Bc6hEField.RX, 3), new Bc6hsModeDescriptor(Bc6hEField.RW, 15), - new Bc6hsModeDescriptor(Bc6hEField.RW, 14), new Bc6hsModeDescriptor(Bc6hEField.RW, 13), new Bc6hsModeDescriptor(Bc6hEField.RW, 12), new Bc6hsModeDescriptor(Bc6hEField.RW, 11), new Bc6hsModeDescriptor(Bc6hEField.RW, 10), new Bc6hsModeDescriptor(Bc6hEField.GX, 0), new Bc6hsModeDescriptor(Bc6hEField.GX, 1), new Bc6hsModeDescriptor(Bc6hEField.GX, 2), new Bc6hsModeDescriptor(Bc6hEField.GX, 3), new Bc6hsModeDescriptor(Bc6hEField.GW, 15), - new Bc6hsModeDescriptor(Bc6hEField.GW, 14), new Bc6hsModeDescriptor(Bc6hEField.GW, 13), new Bc6hsModeDescriptor(Bc6hEField.GW, 12), new Bc6hsModeDescriptor(Bc6hEField.GW, 11), new Bc6hsModeDescriptor(Bc6hEField.GW, 10), new Bc6hsModeDescriptor(Bc6hEField.BX, 0), new Bc6hsModeDescriptor(Bc6hEField.BX, 1), new Bc6hsModeDescriptor(Bc6hEField.BX, 2), new Bc6hsModeDescriptor(Bc6hEField.BX, 3), new Bc6hsModeDescriptor(Bc6hEField.BW, 15), - new Bc6hsModeDescriptor(Bc6hEField.BW, 14), new Bc6hsModeDescriptor(Bc6hEField.BW, 13), new Bc6hsModeDescriptor(Bc6hEField.BW, 12), new Bc6hsModeDescriptor(Bc6hEField.BW, 11), new Bc6hsModeDescriptor(Bc6hEField.BW, 10), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), - new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0), - new Bc6hsModeDescriptor(Bc6hEField.NA, 0), new Bc6hsModeDescriptor(Bc6hEField.NA, 0) + uMode = (byte)((GetBits(currentBlock, ref uStartBit, 3) << 2) | uMode); } - }; - - private static readonly Bc6hsModeInfo[] MsAInfo = - { - new Bc6hsModeInfo(0x00, 1, true, 3, new[] { new[] { new LdrColorA(10, 10, 10, 0), new LdrColorA(5, 5, 5, 0) }, new[] { new LdrColorA(5, 5, 5, 0), new LdrColorA(5, 5, 5, 0) } }), // Mode 1 - new Bc6hsModeInfo(0x01, 1, true, 3, new[] { new[] { new LdrColorA(7, 7, 7, 0), new LdrColorA(6, 6, 6, 0) }, new[] { new LdrColorA(6, 6, 6, 0), new LdrColorA(6, 6, 6, 0) } }), // Mode 2 - new Bc6hsModeInfo(0x02, 1, true, 3, new[] { new[] { new LdrColorA(11, 11, 11, 0), new LdrColorA(5, 4, 4, 0) }, new[] { new LdrColorA(5, 4, 4, 0), new LdrColorA(5, 4, 4, 0) } }), // Mode 3 - new Bc6hsModeInfo(0x06, 1, true, 3, new[] { new[] { new LdrColorA(11, 11, 11, 0), new LdrColorA(4, 5, 4, 0) }, new[] { new LdrColorA(4, 5, 4, 0), new LdrColorA(4, 5, 4, 0) } }), // Mode 4 - new Bc6hsModeInfo(0x0a, 1, true, 3, new[] { new[] { new LdrColorA(11, 11, 11, 0), new LdrColorA(4, 4, 5, 0) }, new[] { new LdrColorA(4, 4, 5, 0), new LdrColorA(4, 4, 5, 0) } }), // Mode 5 - new Bc6hsModeInfo(0x0e, 1, true, 3, new[] { new[] { new LdrColorA(9, 9, 9, 0), new LdrColorA(5, 5, 5, 0) }, new[] { new LdrColorA(5, 5, 5, 0), new LdrColorA(5, 5, 5, 0) } }), // Mode 6 - new Bc6hsModeInfo(0x12, 1, true, 3, new[] { new[] { new LdrColorA(8, 8, 8, 0), new LdrColorA(6, 5, 5, 0) }, new[] { new LdrColorA(6, 5, 5, 0), new LdrColorA(6, 5, 5, 0) } }), // Mode 7 - new Bc6hsModeInfo(0x16, 1, true, 3, new[] { new[] { new LdrColorA(8, 8, 8, 0), new LdrColorA(5, 6, 5, 0) }, new[] { new LdrColorA(5, 6, 5, 0), new LdrColorA(5, 6, 5, 0) } }), // Mode 8 - new Bc6hsModeInfo(0x1a, 1, true, 3, new[] { new[] { new LdrColorA(8, 8, 8, 0), new LdrColorA(5, 5, 6, 0) }, new[] { new LdrColorA(5, 5, 6, 0), new LdrColorA(5, 5, 6, 0) } }), // Mode 9 - new Bc6hsModeInfo(0x1e, 1, false, 3, new[] { new[] { new LdrColorA(6, 6, 6, 0), new LdrColorA(6, 6, 6, 0) }, new[] { new LdrColorA(6, 6, 6, 0), new LdrColorA(6, 6, 6, 0) } }), // Mode 10 - new Bc6hsModeInfo(0x03, 0, false, 4, new[] { new[] { new LdrColorA(10, 10, 10, 0), new LdrColorA(10, 10, 10, 0) }, new[] { new LdrColorA(0, 0, 0, 0), new LdrColorA(0, 0, 0, 0) } }), // Mode 11 - new Bc6hsModeInfo(0x07, 0, true, 4, new[] { new[] { new LdrColorA(11, 11, 11, 0), new LdrColorA(9, 9, 9, 0) }, new[] { new LdrColorA(0, 0, 0, 0), new LdrColorA(0, 0, 0, 0) } }), // Mode 12 - new Bc6hsModeInfo(0x0b, 0, true, 4, new[] { new[] { new LdrColorA(12, 12, 12, 0), new LdrColorA(8, 8, 8, 0) }, new[] { new LdrColorA(0, 0, 0, 0), new LdrColorA(0, 0, 0, 0) } }), // Mode 13 - new Bc6hsModeInfo(0x0f, 0, true, 4, new[] { new[] { new LdrColorA(16, 16, 16, 0), new LdrColorA(4, 4, 4, 0) }, new[] { new LdrColorA(0, 0, 0, 0), new LdrColorA(0, 0, 0, 0) } }), // Mode 14 - }; - - private static readonly int[] MsAModeToInfo = - { - 0, // Mode 1 - 0x00 - 1, // Mode 2 - 0x01 - 2, // Mode 3 - 0x02 - 10, // Mode 11 - 0x03 - -1, // Invalid - 0x04 - -1, // Invalid - 0x05 - 3, // Mode 4 - 0x06 - 11, // Mode 12 - 0x07 - -1, // Invalid - 0x08 - -1, // Invalid - 0x09 - 4, // Mode 5 - 0x0a - 12, // Mode 13 - 0x0b - -1, // Invalid - 0x0c - -1, // Invalid - 0x0d - 5, // Mode 6 - 0x0e - 13, // Mode 14 - 0x0f - -1, // Invalid - 0x10 - -1, // Invalid - 0x11 - 6, // Mode 7 - 0x12 - -1, // Reserved - 0x13 - -1, // Invalid - 0x14 - -1, // Invalid - 0x15 - 7, // Mode 8 - 0x16 - -1, // Reserved - 0x17 - -1, // Invalid - 0x18 - -1, // Invalid - 0x19 - 8, // Mode 9 - 0x1a - -1, // Reserved - 0x1b - -1, // Invalid - 0x1c - -1, // Invalid - 0x1d - 9, // Mode 10 - 0x1e - -1, // Resreved - 0x1f - }; - - /// - public int BitsPerPixel => 32; - - /// - public byte PixelDepthBytes => 4; - - /// - public byte DivSize => 4; - - /// - public byte CompressedBytesPerBlock => 16; - - /// - public bool Compressed => true; - - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } - /// - public byte[] Decompress(byte[] blockData, int width, int height) - { - Bc6hs self = this; - byte[] currentBlock = new byte[this.CompressedBytesPerBlock]; + Debug.Assert(uMode < 32, "uMode should be less then 32"); - return Helper.InMemoryDecode(blockData, width, height, (stream, data, streamIndex, dataIndex, stride) => + if (MsAModeToInfo[uMode] >= 0) { - // I would prefer to use Span, but not sure if I should reference System.Memory in this project - // copy data instead - Buffer.BlockCopy(blockData, streamIndex, currentBlock, 0, currentBlock.Length); - streamIndex += currentBlock.Length; - - uint uStartBit = 0; - byte uMode = GetBits(currentBlock, ref uStartBit, 2u); - if (uMode != 0x00 && uMode != 0x01) - { - uMode = (byte)((GetBits(currentBlock, ref uStartBit, 3) << 2) | uMode); - } + Debug.Assert(MsAModeToInfo[uMode] < MsAInfo.Length, "MsAModeToInfo[uMode] should be smaller then MsAInfo.Length"); + Bc6hsModeDescriptor[] desc = MsADesc[MsAModeToInfo[uMode]]; - Debug.Assert(uMode < 32, "uMode should be less then 32"); + Debug.Assert(MsAModeToInfo[uMode] < MsADesc.Length, "MsAModeToInfo[uMode] should be smaller then MsADesc.Length"); + ref Bc6hsModeInfo info = ref MsAInfo[MsAModeToInfo[uMode]]; - if (MsAModeToInfo[uMode] >= 0) + IntEndPntPair[] aEndPts = new IntEndPntPair[Constants.BC6H_MAX_REGIONS]; + for (int i = 0; i < aEndPts.Length; ++i) { - Debug.Assert(MsAModeToInfo[uMode] < MsAInfo.Length, "MsAModeToInfo[uMode] should be smaller then MsAInfo.Length"); - Bc6hsModeDescriptor[] desc = MsADesc[MsAModeToInfo[uMode]]; - - Debug.Assert(MsAModeToInfo[uMode] < MsADesc.Length, "MsAModeToInfo[uMode] should be smaller then MsADesc.Length"); - ref Bc6hsModeInfo info = ref MsAInfo[MsAModeToInfo[uMode]]; - - var aEndPts = new IntEndPntPair[Constants.BC6H_MAX_REGIONS]; - for (int i = 0; i < aEndPts.Length; ++i) - { - aEndPts[i] = new IntEndPntPair(new IntColor(), new IntColor()); - } + aEndPts[i] = new IntEndPntPair(new IntColor(), new IntColor()); + } - uint uShape = 0; + uint uShape = 0; - // Read header - uint uHeaderBits = info.UPartitions > 0 ? 82u : 65u; - while (uStartBit < uHeaderBits) + // Read header + uint uHeaderBits = info.UPartitions > 0 ? 82u : 65u; + while (uStartBit < uHeaderBits) + { + uint uCurBit = uStartBit; + if (GetBit(currentBlock, ref uStartBit) != 0) { - uint uCurBit = uStartBit; - if (GetBit(currentBlock, ref uStartBit) != 0) + switch (desc[uCurBit].MBc6HEField) { - switch (desc[uCurBit].MBc6HEField) + case Bc6hEField.D: + uShape |= 1u << desc[uCurBit].Bit; + break; + case Bc6hEField.RW: + aEndPts[0].A.R |= 1 << desc[uCurBit].Bit; + break; + case Bc6hEField.RX: + aEndPts[0].B.R |= 1 << desc[uCurBit].Bit; + break; + case Bc6hEField.RY: + aEndPts[1].A.R |= 1 << desc[uCurBit].Bit; + break; + case Bc6hEField.RZ: + aEndPts[1].B.R |= 1 << desc[uCurBit].Bit; + break; + case Bc6hEField.GW: + aEndPts[0].A.G |= 1 << desc[uCurBit].Bit; + break; + case Bc6hEField.GX: + aEndPts[0].B.G |= 1 << desc[uCurBit].Bit; + break; + case Bc6hEField.GY: + aEndPts[1].A.G |= 1 << desc[uCurBit].Bit; + break; + case Bc6hEField.GZ: + aEndPts[1].B.G |= 1 << desc[uCurBit].Bit; + break; + case Bc6hEField.BW: + aEndPts[0].A.B |= 1 << desc[uCurBit].Bit; + break; + case Bc6hEField.BX: + aEndPts[0].B.B |= 1 << desc[uCurBit].Bit; + break; + case Bc6hEField.BY: + aEndPts[1].A.B |= 1 << desc[uCurBit].Bit; + break; + case Bc6hEField.BZ: + aEndPts[1].B.B |= 1 << desc[uCurBit].Bit; + break; + default: { - case Bc6hEField.D: - uShape |= 1u << desc[uCurBit].Bit; - break; - case Bc6hEField.RW: - aEndPts[0].A.R |= 1 << desc[uCurBit].Bit; - break; - case Bc6hEField.RX: - aEndPts[0].B.R |= 1 << desc[uCurBit].Bit; - break; - case Bc6hEField.RY: - aEndPts[1].A.R |= 1 << desc[uCurBit].Bit; - break; - case Bc6hEField.RZ: - aEndPts[1].B.R |= 1 << desc[uCurBit].Bit; - break; - case Bc6hEField.GW: - aEndPts[0].A.G |= 1 << desc[uCurBit].Bit; - break; - case Bc6hEField.GX: - aEndPts[0].B.G |= 1 << desc[uCurBit].Bit; - break; - case Bc6hEField.GY: - aEndPts[1].A.G |= 1 << desc[uCurBit].Bit; - break; - case Bc6hEField.GZ: - aEndPts[1].B.G |= 1 << desc[uCurBit].Bit; - break; - case Bc6hEField.BW: - aEndPts[0].A.B |= 1 << desc[uCurBit].Bit; - break; - case Bc6hEField.BX: - aEndPts[0].B.B |= 1 << desc[uCurBit].Bit; - break; - case Bc6hEField.BY: - aEndPts[1].A.B |= 1 << desc[uCurBit].Bit; - break; - case Bc6hEField.BZ: - aEndPts[1].B.B |= 1 << desc[uCurBit].Bit; - break; - default: - { - Debug.WriteLine("BC6H: Invalid header bits encountered during decoding"); - Helpers.FillWithErrorColors(data, ref dataIndex, Constants.NumPixelsPerBlock, self.DivSize, stride); - return dataIndex; - } + Debug.WriteLine("BC6H: Invalid header bits encountered during decoding"); + Helpers.FillWithErrorColors(data, ref dataIndex, Constants.NumPixelsPerBlock, self.DivSize, stride); + return dataIndex; } } } + } - Debug.Assert(uShape < 64, "uShape shoul be less then 64"); + Debug.Assert(uShape < 64, "uShape shoul be less then 64"); - aEndPts[0].A.SignExtend(info.RgbaPrec[0][0]); + aEndPts[0].A.SignExtend(info.RgbaPrec[0][0]); - if (info.BTransformed) + if (info.BTransformed) + { + Debug.Assert(info.UPartitions < Constants.BC6H_MAX_REGIONS, $"info.UPartitions should be less then {Constants.BC6H_MAX_REGIONS}"); + for (int p = 0; p <= info.UPartitions; ++p) { - Debug.Assert(info.UPartitions < Constants.BC6H_MAX_REGIONS, $"info.UPartitions should be less then {Constants.BC6H_MAX_REGIONS}"); - for (int p = 0; p <= info.UPartitions; ++p) + if (p != 0) { - if (p != 0) - { - aEndPts[p].A.SignExtend(info.RgbaPrec[p][0]); - } - - aEndPts[p].B.SignExtend(info.RgbaPrec[p][1]); + aEndPts[p].A.SignExtend(info.RgbaPrec[p][0]); } - } - // Inverse transform the end points. - if (info.BTransformed) - { - Helpers.TransformInverseSigned(aEndPts, info.RgbaPrec[0][0]); + aEndPts[p].B.SignExtend(info.RgbaPrec[p][1]); } + } - // Read indices. - for (int i = 0; i < Constants.NumPixelsPerBlock; ++i) + // Inverse transform the end points. + if (info.BTransformed) + { + Helpers.TransformInverseSigned(aEndPts, info.RgbaPrec[0][0]); + } + + // Read indices. + for (int i = 0; i < Constants.NumPixelsPerBlock; ++i) + { + uint uNumBits = Helpers.IsFixUpOffset(info.UPartitions, (byte)uShape, i) ? info.UIndexPrec - 1u : info.UIndexPrec; + if (uStartBit + uNumBits > 128) { - uint uNumBits = Helpers.IsFixUpOffset(info.UPartitions, (byte)uShape, i) ? info.UIndexPrec - 1u : info.UIndexPrec; - if (uStartBit + uNumBits > 128) - { - Debug.WriteLine("BC6H: Invalid block encountered during decoding"); - Helpers.FillWithErrorColors(data, ref dataIndex, Constants.NumPixelsPerBlock, self.DivSize, stride); - return dataIndex; - } + Debug.WriteLine("BC6H: Invalid block encountered during decoding"); + Helpers.FillWithErrorColors(data, ref dataIndex, Constants.NumPixelsPerBlock, self.DivSize, stride); + return dataIndex; + } - uint uIndex = GetBits(currentBlock, ref uStartBit, uNumBits); + uint uIndex = GetBits(currentBlock, ref uStartBit, uNumBits); - if (uIndex >= ((info.UPartitions > 0) ? 8 : 16)) - { - Debug.WriteLine("BC6H: Invalid index encountered during decoding"); - Helpers.FillWithErrorColors(data, ref dataIndex, Constants.NumPixelsPerBlock, self.DivSize, stride); - return dataIndex; - } + if (uIndex >= ((info.UPartitions > 0) ? 8 : 16)) + { + Debug.WriteLine("BC6H: Invalid index encountered during decoding"); + Helpers.FillWithErrorColors(data, ref dataIndex, Constants.NumPixelsPerBlock, self.DivSize, stride); + return dataIndex; + } - uint uRegion = Constants.PartitionTable[info.UPartitions][uShape][i]; - Debug.Assert(uRegion < Constants.BC6H_MAX_REGIONS, $"uRegion should be less then {Constants.BC6H_MAX_REGIONS}"); - - // Unquantize endpoints and interpolate - int r1 = Unquantize(aEndPts[uRegion].A.R, info.RgbaPrec[0][0].R); - int g1 = Unquantize(aEndPts[uRegion].A.G, info.RgbaPrec[0][0].G); - int b1 = Unquantize(aEndPts[uRegion].A.B, info.RgbaPrec[0][0].B); - int r2 = Unquantize(aEndPts[uRegion].B.R, info.RgbaPrec[0][0].R); - int g2 = Unquantize(aEndPts[uRegion].B.G, info.RgbaPrec[0][0].G); - int b2 = Unquantize(aEndPts[uRegion].B.B, info.RgbaPrec[0][0].B); - int[] aWeights = info.UPartitions > 0 ? Constants.Weights3 : Constants.Weights4; - var fc = new IntColor - { - R = FinishUnquantize(((r1 * (Constants.BC67_WEIGHT_MAX - aWeights[uIndex])) + (r2 * aWeights[uIndex]) + Constants.BC67_WEIGHT_ROUND) >> Constants.BC67_WEIGHT_SHIFT), - G = FinishUnquantize(((g1 * (Constants.BC67_WEIGHT_MAX - aWeights[uIndex])) + (g2 * aWeights[uIndex]) + Constants.BC67_WEIGHT_ROUND) >> Constants.BC67_WEIGHT_SHIFT), - B = FinishUnquantize(((b1 * (Constants.BC67_WEIGHT_MAX - aWeights[uIndex])) + (b2 * aWeights[uIndex]) + Constants.BC67_WEIGHT_ROUND) >> Constants.BC67_WEIGHT_SHIFT) - }; - - ushort[] rgb = new ushort[3]; - fc.ToF16Signed(rgb); - - // Clamp 0..1, and convert to byte (we're losing high dynamic range) - data[dataIndex++] = (byte)((Math.Max(0.0f, Math.Min(1.0f, FloatHelper.UnpackFloat16ToFloat(rgb[0]))) * 255.0f) + 0.5f); // red - data[dataIndex++] = (byte)((Math.Max(0.0f, Math.Min(1.0f, FloatHelper.UnpackFloat16ToFloat(rgb[1]))) * 255.0f) + 0.5f); // green - data[dataIndex++] = (byte)((Math.Max(0.0f, Math.Min(1.0f, FloatHelper.UnpackFloat16ToFloat(rgb[2]))) * 255.0f) + 0.5f); // blue - data[dataIndex++] = 255; - - // Is mult 4? - if (((i + 1) & 0x3) == 0) - { - dataIndex += self.PixelDepthBytes * (stride - self.DivSize); - } + uint uRegion = Constants.PartitionTable[info.UPartitions][uShape][i]; + Debug.Assert(uRegion < Constants.BC6H_MAX_REGIONS, $"uRegion should be less then {Constants.BC6H_MAX_REGIONS}"); + + // Unquantize endpoints and interpolate + int r1 = Unquantize(aEndPts[uRegion].A.R, info.RgbaPrec[0][0].R); + int g1 = Unquantize(aEndPts[uRegion].A.G, info.RgbaPrec[0][0].G); + int b1 = Unquantize(aEndPts[uRegion].A.B, info.RgbaPrec[0][0].B); + int r2 = Unquantize(aEndPts[uRegion].B.R, info.RgbaPrec[0][0].R); + int g2 = Unquantize(aEndPts[uRegion].B.G, info.RgbaPrec[0][0].G); + int b2 = Unquantize(aEndPts[uRegion].B.B, info.RgbaPrec[0][0].B); + int[] aWeights = info.UPartitions > 0 ? Constants.Weights3 : Constants.Weights4; + IntColor fc = new IntColor + { + R = FinishUnquantize(((r1 * (Constants.BC67_WEIGHT_MAX - aWeights[uIndex])) + (r2 * aWeights[uIndex]) + Constants.BC67_WEIGHT_ROUND) >> Constants.BC67_WEIGHT_SHIFT), + G = FinishUnquantize(((g1 * (Constants.BC67_WEIGHT_MAX - aWeights[uIndex])) + (g2 * aWeights[uIndex]) + Constants.BC67_WEIGHT_ROUND) >> Constants.BC67_WEIGHT_SHIFT), + B = FinishUnquantize(((b1 * (Constants.BC67_WEIGHT_MAX - aWeights[uIndex])) + (b2 * aWeights[uIndex]) + Constants.BC67_WEIGHT_ROUND) >> Constants.BC67_WEIGHT_SHIFT) + }; + + ushort[] rgb = new ushort[3]; + fc.ToF16Signed(rgb); + + // Clamp 0..1, and convert to byte (we're losing high dynamic range) + data[dataIndex++] = (byte)((Math.Max(0.0f, Math.Min(1.0f, FloatHelper.UnpackFloat16ToFloat(rgb[0]))) * 255.0f) + 0.5f); // red + data[dataIndex++] = (byte)((Math.Max(0.0f, Math.Min(1.0f, FloatHelper.UnpackFloat16ToFloat(rgb[1]))) * 255.0f) + 0.5f); // green + data[dataIndex++] = (byte)((Math.Max(0.0f, Math.Min(1.0f, FloatHelper.UnpackFloat16ToFloat(rgb[2]))) * 255.0f) + 0.5f); // blue + data[dataIndex++] = 255; + + // Is mult 4? + if (((i + 1) & 0x3) == 0) + { + dataIndex += self.PixelDepthBytes * (stride - self.DivSize); } } - else + } + else + { + string warnStr = "BC6H: Invalid mode encountered during decoding"; + switch (uMode) { - string warnStr = "BC6H: Invalid mode encountered during decoding"; - switch (uMode) - { - case 0x13: - warnStr = "BC6H: Reserved mode 10011 encountered during decoding"; - break; - case 0x17: - warnStr = "BC6H: Reserved mode 10111 encountered during decoding"; - break; - case 0x1B: - warnStr = "BC6H: Reserved mode 11011 encountered during decoding"; - break; - case 0x1F: - warnStr = "BC6H: Reserved mode 11111 encountered during decoding"; - break; - } + case 0x13: + warnStr = "BC6H: Reserved mode 10011 encountered during decoding"; + break; + case 0x17: + warnStr = "BC6H: Reserved mode 10111 encountered during decoding"; + break; + case 0x1B: + warnStr = "BC6H: Reserved mode 11011 encountered during decoding"; + break; + case 0x1F: + warnStr = "BC6H: Reserved mode 11111 encountered during decoding"; + break; + } - Debug.WriteLine(warnStr); + Debug.WriteLine(warnStr); - // Per the BC6H format spec, we must return opaque black - for (int i = 0; i < Constants.NumPixelsPerBlock; ++i) - { - data[dataIndex++] = 0; - data[dataIndex++] = 0; - data[dataIndex++] = 0; - data[dataIndex++] = 0; + // Per the BC6H format spec, we must return opaque black + for (int i = 0; i < Constants.NumPixelsPerBlock; ++i) + { + data[dataIndex++] = 0; + data[dataIndex++] = 0; + data[dataIndex++] = 0; + data[dataIndex++] = 0; - // Is mult 4? - if (((i + 1) & 0x3) == 0) - { - dataIndex += self.PixelDepthBytes * (stride - self.DivSize); - } + // Is mult 4? + if (((i + 1) & 0x3) == 0) + { + dataIndex += self.PixelDepthBytes * (stride - self.DivSize); } } + } - return streamIndex; - }); - } + return streamIndex; + }); + } + + /// + /// Gets a bit for a given position. + /// + /// The current block. + /// The start bit. + /// A bit at a given position. + public static byte GetBit(byte[] currentBlock, ref uint uStartBit) + { + Guard.MustBeLessThan(uStartBit, 128, nameof(uStartBit)); + + uint uIndex = uStartBit >> 3; + byte ret = (byte)((currentBlock[uIndex] >> (int)(uStartBit - (uIndex << 3))) & 0x01); + uStartBit++; + return ret; + } - /// - /// Gets a bit for a given position. - /// - /// The current block. - /// The start bit. - /// A bit at a given position. - public static byte GetBit(byte[] currentBlock, ref uint uStartBit) + /// + /// Gets n bits at a given start position. + /// + /// The current block. + /// The start bit. + /// The number of bits. + /// Bits at a given position. + public static byte GetBits(byte[] currentBlock, ref uint uStartBit, uint uNumBits) + { + if (uNumBits == 0) { - Guard.MustBeLessThan(uStartBit, 128, nameof(uStartBit)); + return 0; + } - uint uIndex = uStartBit >> 3; - byte ret = (byte)((currentBlock[uIndex] >> (int)(uStartBit - (uIndex << 3))) & 0x01); - uStartBit++; - return ret; + Debug.Assert(uStartBit + uNumBits <= 128 && uNumBits <= 8, "uStartBit + uNumBits <= 128 && uNumBits <= 8"); + byte ret; + uint uIndex = uStartBit >> 3; + uint uBase = uStartBit - (uIndex << 3); + if (uBase + uNumBits > 8) + { + uint uFirstIndexBits = 8 - uBase; + uint uNextIndexBits = uNumBits - uFirstIndexBits; + ret = (byte)((uint)(currentBlock[uIndex] >> (int)uBase) | ((currentBlock[uIndex + 1] & ((1u << (int)uNextIndexBits) - 1)) << (int)uFirstIndexBits)); + } + else + { + ret = (byte)((currentBlock[uIndex] >> (int)uBase) & ((1 << (int)uNumBits) - 1)); } - /// - /// Gets n bits at a given start position. - /// - /// The current block. - /// The start bit. - /// The number of bits. - /// Bits at a given position. - public static byte GetBits(byte[] currentBlock, ref uint uStartBit, uint uNumBits) + Debug.Assert(ret < (1 << (int)uNumBits), $"GetBits return value should be less then {1 << (int)uNumBits}"); + uStartBit += uNumBits; + return ret; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int Unquantize(int comp, byte uBitsPerComp) + { + int s = 0; + int unq; + if (uBitsPerComp >= 16) { - if (uNumBits == 0) + unq = comp; + } + else + { + if (comp < 0) { - return 0; + s = 1; + comp = -comp; } - Debug.Assert(uStartBit + uNumBits <= 128 && uNumBits <= 8, "uStartBit + uNumBits <= 128 && uNumBits <= 8"); - byte ret; - uint uIndex = uStartBit >> 3; - uint uBase = uStartBit - (uIndex << 3); - if (uBase + uNumBits > 8) - { - uint uFirstIndexBits = 8 - uBase; - uint uNextIndexBits = uNumBits - uFirstIndexBits; - ret = (byte)((uint)(currentBlock[uIndex] >> (int)uBase) | ((currentBlock[uIndex + 1] & ((1u << (int)uNextIndexBits) - 1)) << (int)uFirstIndexBits)); - } - else + if (comp == 0) { - ret = (byte)((currentBlock[uIndex] >> (int)uBase) & ((1 << (int)uNumBits) - 1)); + unq = 0; } - - Debug.Assert(ret < (1 << (int)uNumBits), $"GetBits return value should be less then {1 << (int)uNumBits}"); - uStartBit += uNumBits; - return ret; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int Unquantize(int comp, byte uBitsPerComp) - { - int s = 0; - int unq; - if (uBitsPerComp >= 16) + else if (comp >= ((1 << (uBitsPerComp - 1)) - 1)) { - unq = comp; + unq = 0x7FFF; } else { - if (comp < 0) - { - s = 1; - comp = -comp; - } - - if (comp == 0) - { - unq = 0; - } - else if (comp >= ((1 << (uBitsPerComp - 1)) - 1)) - { - unq = 0x7FFF; - } - else - { - unq = ((comp << 15) + 0x4000) >> (uBitsPerComp - 1); - } - - if (s != 0) - { - unq = -unq; - } + unq = ((comp << 15) + 0x4000) >> (uBitsPerComp - 1); } - return unq; + if (s != 0) + { + unq = -unq; + } } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int FinishUnquantize(int comp) => (comp < 0) ? -(((-comp) * 31) >> 5) : (comp * 31) >> 5; // scale the magnitude by 31/32 + return unq; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int FinishUnquantize(int comp) => (comp < 0) ? -(((-comp) * 31) >> 5) : (comp * 31) >> 5; // scale the magnitude by 31/32 } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Bc6hsModeDescriptor.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Bc6hsModeDescriptor.cs index 97e2fd81..2a82b437 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Bc6hsModeDescriptor.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Bc6hsModeDescriptor.cs @@ -1,17 +1,16 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; + +internal struct Bc6hsModeDescriptor { - internal struct Bc6hsModeDescriptor - { - public readonly Bc6hEField MBc6HEField; - public readonly byte Bit; + public readonly Bc6hEField MBc6HEField; + public readonly byte Bit; - public Bc6hsModeDescriptor(Bc6hEField bc6Hef, byte b) - { - this.MBc6HEField = bc6Hef; - this.Bit = b; - } + public Bc6hsModeDescriptor(Bc6hEField bc6Hef, byte b) + { + this.MBc6HEField = bc6Hef; + this.Bit = b; } } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Bc6hsModeInfo.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Bc6hsModeInfo.cs index 0c9047ec..4c98bf0f 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Bc6hsModeInfo.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Bc6hsModeInfo.cs @@ -3,23 +3,22 @@ using SixLabors.ImageSharp.Textures.TextureFormats.Decoding.PixelFormats; -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; + +internal struct Bc6hsModeInfo { - internal struct Bc6hsModeInfo - { - public byte UMode; - public readonly byte UPartitions; - public readonly bool BTransformed; - public readonly byte UIndexPrec; - public readonly LdrColorA[][] RgbaPrec; // [Constants.BC6H_MAX_REGIONS][2]; + public byte UMode; + public readonly byte UPartitions; + public readonly bool BTransformed; + public readonly byte UIndexPrec; + public readonly LdrColorA[][] RgbaPrec; // [Constants.BC6H_MAX_REGIONS][2]; - public Bc6hsModeInfo(byte uM, byte uP, bool bT, byte uI, LdrColorA[][] prec) - { - this.UMode = uM; - this.UPartitions = uP; - this.BTransformed = bT; - this.UIndexPrec = uI; - this.RgbaPrec = prec; - } + public Bc6hsModeInfo(byte uM, byte uP, bool bT, byte uI, LdrColorA[][] prec) + { + this.UMode = uM; + this.UPartitions = uP; + this.BTransformed = bT; + this.UIndexPrec = uI; + this.RgbaPrec = prec; } } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Bc7.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Bc7.cs index a10234ff..69b7c195 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Bc7.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Bc7.cs @@ -1,210 +1,225 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Diagnostics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Textures.TextureFormats.Decoding.PixelFormats; -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; + +/// +/// Texture compressed with BC7 with three color channels (4 to 7 bits per channel) with 0 to 8 bits of alpha. +/// +internal struct Bc7 : IBlock { - /// - /// Texture compressed with BC7 with three color channels (4 to 7 bits per channel) with 0 to 8 bits of alpha. - /// - internal struct Bc7 : IBlock - { - // Code based on commit 138efff1b9c53fd9a5dd34b8c865e8f5ae798030 2019/10/24 in DirectXTex C++ library - private static readonly Bc7ModeInfo[] ModeInfos = - { - // Mode 0: Color only, 3 Subsets, RGBP 4441 (unique P-bit), 3-bit indecies, 16 partitions - new Bc7ModeInfo(2, 4, 6, 0, 0, 3, 0, new LdrColorA(4, 4, 4, 0), new LdrColorA(5, 5, 5, 0)), + // Code based on commit 138efff1b9c53fd9a5dd34b8c865e8f5ae798030 2019/10/24 in DirectXTex C++ library + private static readonly Bc7ModeInfo[] ModeInfos = + [ + // Mode 0: Color only, 3 Subsets, RGBP 4441 (unique P-bit), 3-bit indecies, 16 partitions + new Bc7ModeInfo(2, 4, 6, 0, 0, 3, 0, new LdrColorA(4, 4, 4, 0), new LdrColorA(5, 5, 5, 0)), - // Mode 1: Color only, 2 Subsets, RGBP 6661 (shared P-bit), 3-bit indecies, 64 partitions - new Bc7ModeInfo(1, 6, 2, 0, 0, 3, 0, new LdrColorA(6, 6, 6, 0), new LdrColorA(7, 7, 7, 0)), + // Mode 1: Color only, 2 Subsets, RGBP 6661 (shared P-bit), 3-bit indecies, 64 partitions + new Bc7ModeInfo(1, 6, 2, 0, 0, 3, 0, new LdrColorA(6, 6, 6, 0), new LdrColorA(7, 7, 7, 0)), - // Mode 2: Color only, 3 Subsets, RGB 555, 2-bit indecies, 64 partitions - new Bc7ModeInfo(2, 6, 0, 0, 0, 2, 0, new LdrColorA(5, 5, 5, 0), new LdrColorA(5, 5, 5, 0)), + // Mode 2: Color only, 3 Subsets, RGB 555, 2-bit indecies, 64 partitions + new Bc7ModeInfo(2, 6, 0, 0, 0, 2, 0, new LdrColorA(5, 5, 5, 0), new LdrColorA(5, 5, 5, 0)), - // Mode 3: Color only, 2 Subsets, RGBP 7771 (unique P-bit), 2-bits indecies, 64 partitions - new Bc7ModeInfo(1, 6, 4, 0, 0, 2, 0, new LdrColorA(7, 7, 7, 0), new LdrColorA(8, 8, 8, 0)), + // Mode 3: Color only, 2 Subsets, RGBP 7771 (unique P-bit), 2-bits indecies, 64 partitions + new Bc7ModeInfo(1, 6, 4, 0, 0, 2, 0, new LdrColorA(7, 7, 7, 0), new LdrColorA(8, 8, 8, 0)), - // Mode 4: Color w/ Separate Alpha, 1 Subset, RGB 555, A6, 16x2/16x3-bit indices, 2-bit rotation, 1-bit index selector - new Bc7ModeInfo(0, 0, 0, 2, 1, 2, 3, new LdrColorA(5, 5, 5, 6), new LdrColorA(5, 5, 5, 6)), + // Mode 4: Color w/ Separate Alpha, 1 Subset, RGB 555, A6, 16x2/16x3-bit indices, 2-bit rotation, 1-bit index selector + new Bc7ModeInfo(0, 0, 0, 2, 1, 2, 3, new LdrColorA(5, 5, 5, 6), new LdrColorA(5, 5, 5, 6)), - // Mode 5: Color w/ Separate Alpha, 1 Subset, RGB 777, A8, 16x2/16x2-bit indices, 2-bit rotation - new Bc7ModeInfo(0, 0, 0, 2, 0, 2, 2, new LdrColorA(7, 7, 7, 8), new LdrColorA(7, 7, 7, 8)), + // Mode 5: Color w/ Separate Alpha, 1 Subset, RGB 777, A8, 16x2/16x2-bit indices, 2-bit rotation + new Bc7ModeInfo(0, 0, 0, 2, 0, 2, 2, new LdrColorA(7, 7, 7, 8), new LdrColorA(7, 7, 7, 8)), - // Mode 6: Color+Alpha, 1 Subset, RGBAP 77771 (unique P-bit), 16x4-bit indecies - new Bc7ModeInfo(0, 0, 2, 0, 0, 4, 0, new LdrColorA(7, 7, 7, 7), new LdrColorA(8, 8, 8, 8)), + // Mode 6: Color+Alpha, 1 Subset, RGBAP 77771 (unique P-bit), 16x4-bit indecies + new Bc7ModeInfo(0, 0, 2, 0, 0, 4, 0, new LdrColorA(7, 7, 7, 7), new LdrColorA(8, 8, 8, 8)), - // Mode 7: Color+Alpha, 2 Subsets, RGBAP 55551 (unique P-bit), 2-bit indices, 64 partitions - new Bc7ModeInfo(1, 6, 4, 0, 0, 2, 0, new LdrColorA(5, 5, 5, 5), new LdrColorA(6, 6, 6, 6)) - }; + // Mode 7: Color+Alpha, 2 Subsets, RGBAP 55551 (unique P-bit), 2-bit indices, 64 partitions + new Bc7ModeInfo(1, 6, 4, 0, 0, 2, 0, new LdrColorA(5, 5, 5, 5), new LdrColorA(6, 6, 6, 6)) + ]; - /// - public int BitsPerPixel => 32; + /// + public readonly int BitsPerPixel => 32; - /// - public byte PixelDepthBytes => 4; + /// + public readonly byte PixelDepthBytes => 4; - /// - public byte DivSize => 4; + /// + public readonly byte DivSize => 4; - /// - public byte CompressedBytesPerBlock => 16; + /// + public readonly byte CompressedBytesPerBlock => 16; - /// - public bool Compressed => true; + /// + public readonly bool Compressed => true; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); + } + + /// + public byte[] Decompress(byte[] blockData, int width, int height) + { + byte[] currentBlock = new byte[this.CompressedBytesPerBlock]; + Bc7 self = this; - /// - public byte[] Decompress(byte[] blockData, int width, int height) + return Helper.InMemoryDecode(blockData, width, height, (stream, data, streamIndex, dataIndex, stride) => { - byte[] currentBlock = new byte[this.CompressedBytesPerBlock]; - Bc7 self = this; + // I would prefer to use Span, but not sure if I should reference System.Memory in this project copy data instead. + Buffer.BlockCopy(blockData, streamIndex, currentBlock, 0, currentBlock.Length); + streamIndex += currentBlock.Length; - return Helper.InMemoryDecode(blockData, width, height, (stream, data, streamIndex, dataIndex, stride) => + uint uFirst = 0; + while (uFirst < 128 && GetBit(currentBlock, ref uFirst) == 0) { - // I would prefer to use Span, but not sure if I should reference System.Memory in this project copy data instead. - Buffer.BlockCopy(blockData, streamIndex, currentBlock, 0, currentBlock.Length); - streamIndex += currentBlock.Length; + } - uint uFirst = 0; - while (uFirst < 128 && GetBit(currentBlock, ref uFirst) == 0) + byte uMode = (byte)(uFirst - 1); + + if (uMode < 8) + { + byte uPartitions = ModeInfos[uMode].UPartitions; + Debug.Assert(uPartitions < Constants.BC7_MAX_REGIONS, $"uPartitions should be smaller then {Constants.BC7_MAX_REGIONS}"); + + byte uNumEndPts = (byte)((uPartitions + 1u) << 1); + byte uIndexPrec = ModeInfos[uMode].UIndexPrec; + byte uIndexPrec2 = ModeInfos[uMode].UIndexPrec2; + int i; + uint uStartBit = uMode + 1u; + int[] p = new int[6]; + byte uShape = GetBits(currentBlock, ref uStartBit, ModeInfos[uMode].UPartitionBits); + Debug.Assert(uShape < Constants.BC7_MAX_SHAPES, $"uShape should be smaller then {Constants.BC7_MAX_SHAPES}"); + + byte uRotation = GetBits(currentBlock, ref uStartBit, ModeInfos[uMode].URotationBits); + Debug.Assert(uRotation < 4, "uRotation should be less then 4"); + + byte uIndexMode = GetBits(currentBlock, ref uStartBit, ModeInfos[uMode].UIndexModeBits); + Debug.Assert(uIndexMode < 2, "uIndexMode should be less then 2"); + + LdrColorA[] c = new LdrColorA[Constants.BC7_MAX_REGIONS << 1]; + for (i = 0; i < c.Length; ++i) { + c[i] = new LdrColorA(); } - byte uMode = (byte)(uFirst - 1); + LdrColorA rgbaPrec = ModeInfos[uMode].RgbaPrec; + LdrColorA rgbaPrecWithP = ModeInfos[uMode].RgbaPrecWithP; - if (uMode < 8) + Debug.Assert(uNumEndPts <= (Constants.BC7_MAX_REGIONS << 1), $"uNumEndPts should be smaller or equal to {Constants.BC7_MAX_REGIONS << 1}"); + + // Red channel + for (i = 0; i < uNumEndPts; i++) { - byte uPartitions = ModeInfos[uMode].UPartitions; - Debug.Assert(uPartitions < Constants.BC7_MAX_REGIONS, $"uPartitions should be smaller then {Constants.BC7_MAX_REGIONS}"); - - byte uNumEndPts = (byte)((uPartitions + 1u) << 1); - byte uIndexPrec = ModeInfos[uMode].UIndexPrec; - byte uIndexPrec2 = ModeInfos[uMode].UIndexPrec2; - int i; - uint uStartBit = uMode + 1u; - int[] p = new int[6]; - byte uShape = GetBits(currentBlock, ref uStartBit, ModeInfos[uMode].UPartitionBits); - Debug.Assert(uShape < Constants.BC7_MAX_SHAPES, $"uShape should be smaller then {Constants.BC7_MAX_SHAPES}"); - - byte uRotation = GetBits(currentBlock, ref uStartBit, ModeInfos[uMode].URotationBits); - Debug.Assert(uRotation < 4, "uRotation should be less then 4"); - - byte uIndexMode = GetBits(currentBlock, ref uStartBit, ModeInfos[uMode].UIndexModeBits); - Debug.Assert(uIndexMode < 2, "uIndexMode should be less then 2"); - - var c = new LdrColorA[Constants.BC7_MAX_REGIONS << 1]; - for (i = 0; i < c.Length; ++i) + if (uStartBit + rgbaPrec.R > 128) { - c[i] = new LdrColorA(); + Debug.WriteLine("BC7: Invalid block encountered during decoding"); + Helpers.FillWithErrorColors(data, ref dataIndex, Constants.NumPixelsPerBlock, self.DivSize, stride); + return dataIndex; } - LdrColorA rgbaPrec = ModeInfos[uMode].RgbaPrec; - LdrColorA rgbaPrecWithP = ModeInfos[uMode].RgbaPrecWithP; - - Debug.Assert(uNumEndPts <= (Constants.BC7_MAX_REGIONS << 1), $"uNumEndPts should be smaller or equal to {Constants.BC7_MAX_REGIONS << 1}"); + c[i].R = GetBits(currentBlock, ref uStartBit, rgbaPrec.R); + } - // Red channel - for (i = 0; i < uNumEndPts; i++) + // Green channel + for (i = 0; i < uNumEndPts; i++) + { + if (uStartBit + rgbaPrec.G > 128) { - if (uStartBit + rgbaPrec.R > 128) - { - Debug.WriteLine("BC7: Invalid block encountered during decoding"); - Helpers.FillWithErrorColors(data, ref dataIndex, Constants.NumPixelsPerBlock, self.DivSize, stride); - return dataIndex; - } - - c[i].R = GetBits(currentBlock, ref uStartBit, rgbaPrec.R); + Debug.WriteLine("BC7: Invalid block encountered during decoding"); + Helpers.FillWithErrorColors(data, ref dataIndex, Constants.NumPixelsPerBlock, self.DivSize, stride); + return dataIndex; } - // Green channel - for (i = 0; i < uNumEndPts; i++) - { - if (uStartBit + rgbaPrec.G > 128) - { - Debug.WriteLine("BC7: Invalid block encountered during decoding"); - Helpers.FillWithErrorColors(data, ref dataIndex, Constants.NumPixelsPerBlock, self.DivSize, stride); - return dataIndex; - } - - c[i].G = GetBits(currentBlock, ref uStartBit, rgbaPrec.G); - } + c[i].G = GetBits(currentBlock, ref uStartBit, rgbaPrec.G); + } - // Blue channel - for (i = 0; i < uNumEndPts; i++) + // Blue channel + for (i = 0; i < uNumEndPts; i++) + { + if (uStartBit + rgbaPrec.B > 128) { - if (uStartBit + rgbaPrec.B > 128) - { - Debug.WriteLine("BC7: Invalid block encountered during decoding"); - Helpers.FillWithErrorColors(data, ref dataIndex, Constants.NumPixelsPerBlock, self.DivSize, stride); - return dataIndex; - } - - c[i].B = GetBits(currentBlock, ref uStartBit, rgbaPrec.B); + Debug.WriteLine("BC7: Invalid block encountered during decoding"); + Helpers.FillWithErrorColors(data, ref dataIndex, Constants.NumPixelsPerBlock, self.DivSize, stride); + return dataIndex; } - // Alpha channel - for (i = 0; i < uNumEndPts; i++) - { - if (uStartBit + rgbaPrec.A > 128) - { - Debug.WriteLine("BC7: Invalid block encountered during decoding"); - Helpers.FillWithErrorColors(data, ref dataIndex, Constants.NumPixelsPerBlock, self.DivSize, stride); - return dataIndex; - } + c[i].B = GetBits(currentBlock, ref uStartBit, rgbaPrec.B); + } - c[i].A = (byte)(rgbaPrec.A != 0 ? GetBits(currentBlock, ref uStartBit, rgbaPrec.A) : 255u); + // Alpha channel + for (i = 0; i < uNumEndPts; i++) + { + if (uStartBit + rgbaPrec.A > 128) + { + Debug.WriteLine("BC7: Invalid block encountered during decoding"); + Helpers.FillWithErrorColors(data, ref dataIndex, Constants.NumPixelsPerBlock, self.DivSize, stride); + return dataIndex; } - // P-bits - Debug.Assert(ModeInfos[uMode].UPBits <= 6, "ModeInfos[uMode].UPBits should be less then 7"); - for (i = 0; i < ModeInfos[uMode].UPBits; i++) - { - if (uStartBit > 127) - { - Debug.WriteLine("BC7: Invalid block encountered during decoding"); - Helpers.FillWithErrorColors(data, ref dataIndex, Constants.NumPixelsPerBlock, self.DivSize, stride); - return dataIndex; - } + c[i].A = (byte)(rgbaPrec.A != 0 ? GetBits(currentBlock, ref uStartBit, rgbaPrec.A) : 255u); + } - p[i] = GetBit(currentBlock, ref uStartBit); + // P-bits + Debug.Assert(ModeInfos[uMode].UPBits <= 6, "ModeInfos[uMode].UPBits should be less then 7"); + for (i = 0; i < ModeInfos[uMode].UPBits; i++) + { + if (uStartBit > 127) + { + Debug.WriteLine("BC7: Invalid block encountered during decoding"); + Helpers.FillWithErrorColors(data, ref dataIndex, Constants.NumPixelsPerBlock, self.DivSize, stride); + return dataIndex; } - if (ModeInfos[uMode].UPBits != 0) + p[i] = GetBit(currentBlock, ref uStartBit); + } + + if (ModeInfos[uMode].UPBits != 0) + { + for (i = 0; i < uNumEndPts; i++) { - for (i = 0; i < uNumEndPts; i++) + int pi = i * ModeInfos[uMode].UPBits / uNumEndPts; + for (byte ch = 0; ch < Constants.BC7_NUM_CHANNELS; ch++) { - int pi = i * ModeInfos[uMode].UPBits / uNumEndPts; - for (byte ch = 0; ch < Constants.BC7_NUM_CHANNELS; ch++) + if (rgbaPrec[ch] != rgbaPrecWithP[ch]) { - if (rgbaPrec[ch] != rgbaPrecWithP[ch]) - { - c[i][ch] = (byte)((c[i][ch] << 1) | p[pi]); - } + c[i][ch] = (byte)((c[i][ch] << 1) | p[pi]); } } } + } - for (i = 0; i < uNumEndPts; i++) + for (i = 0; i < uNumEndPts; i++) + { + c[i] = Unquantize(c[i], rgbaPrecWithP); + } + + byte[] w1 = new byte[Constants.NumPixelsPerBlock], w2 = new byte[Constants.NumPixelsPerBlock]; + + // read color indices + for (i = 0; i < Constants.NumPixelsPerBlock; i++) + { + uint uNumBits = Helpers.IsFixUpOffset(ModeInfos[uMode].UPartitions, uShape, i) ? uIndexPrec - 1u : uIndexPrec; + if (uStartBit + uNumBits > 128) { - c[i] = Unquantize(c[i], rgbaPrecWithP); + Debug.WriteLine("BC7: Invalid block encountered during decoding"); + Helpers.FillWithErrorColors(data, ref dataIndex, Constants.NumPixelsPerBlock, self.DivSize, stride); + return dataIndex; } - byte[] w1 = new byte[Constants.NumPixelsPerBlock], w2 = new byte[Constants.NumPixelsPerBlock]; + w1[i] = GetBits(currentBlock, ref uStartBit, uNumBits); + } - // read color indices + // read alpha indices + if (uIndexPrec2 != 0) + { for (i = 0; i < Constants.NumPixelsPerBlock; i++) { - uint uNumBits = Helpers.IsFixUpOffset(ModeInfos[uMode].UPartitions, uShape, i) ? uIndexPrec - 1u : uIndexPrec; + uint uNumBits = i != 0 ? uIndexPrec2 : uIndexPrec2 - 1u; if (uStartBit + uNumBits > 128) { Debug.WriteLine("BC7: Invalid block encountered during decoding"); @@ -212,167 +227,150 @@ public byte[] Decompress(byte[] blockData, int width, int height) return dataIndex; } - w1[i] = GetBits(currentBlock, ref uStartBit, uNumBits); + w2[i] = GetBits(currentBlock, ref uStartBit, uNumBits); } + } - // read alpha indices - if (uIndexPrec2 != 0) + for (i = 0; i < Constants.NumPixelsPerBlock; ++i) + { + byte uRegion = Constants.PartitionTable[uPartitions][uShape][i]; + LdrColorA outPixel = new LdrColorA(); + if (uIndexPrec2 == 0) { - for (i = 0; i < Constants.NumPixelsPerBlock; i++) - { - uint uNumBits = i != 0 ? uIndexPrec2 : uIndexPrec2 - 1u; - if (uStartBit + uNumBits > 128) - { - Debug.WriteLine("BC7: Invalid block encountered during decoding"); - Helpers.FillWithErrorColors(data, ref dataIndex, Constants.NumPixelsPerBlock, self.DivSize, stride); - return dataIndex; - } - - w2[i] = GetBits(currentBlock, ref uStartBit, uNumBits); - } + LdrColorA.Interpolate(c[uRegion << 1], c[(uRegion << 1) + 1], w1[i], w1[i], uIndexPrec, uIndexPrec, outPixel); } - - for (i = 0; i < Constants.NumPixelsPerBlock; ++i) + else { - byte uRegion = Constants.PartitionTable[uPartitions][uShape][i]; - var outPixel = new LdrColorA(); - if (uIndexPrec2 == 0) + if (uIndexMode == 0) { - LdrColorA.Interpolate(c[uRegion << 1], c[(uRegion << 1) + 1], w1[i], w1[i], uIndexPrec, uIndexPrec, outPixel); + LdrColorA.Interpolate(c[uRegion << 1], c[(uRegion << 1) + 1], w1[i], w2[i], uIndexPrec, uIndexPrec2, outPixel); } else { - if (uIndexMode == 0) - { - LdrColorA.Interpolate(c[uRegion << 1], c[(uRegion << 1) + 1], w1[i], w2[i], uIndexPrec, uIndexPrec2, outPixel); - } - else - { - LdrColorA.Interpolate(c[uRegion << 1], c[(uRegion << 1) + 1], w2[i], w1[i], uIndexPrec2, uIndexPrec, outPixel); - } + LdrColorA.Interpolate(c[uRegion << 1], c[(uRegion << 1) + 1], w2[i], w1[i], uIndexPrec2, uIndexPrec, outPixel); } + } - switch (uRotation) - { - case 1: - outPixel.SwapRedWithAlpha(); - break; - case 2: - outPixel.SwapGreenWithAlpha(); - break; - case 3: - outPixel.SwapBlueWithAlpha(); - break; - } + switch (uRotation) + { + case 1: + outPixel.SwapRedWithAlpha(); + break; + case 2: + outPixel.SwapGreenWithAlpha(); + break; + case 3: + outPixel.SwapBlueWithAlpha(); + break; + } - // Note: whether it's sRGB is not taken into consideration - // we're returning data that could be either/or depending - // on the input BC7 format - data[dataIndex++] = outPixel.R; - data[dataIndex++] = outPixel.G; - data[dataIndex++] = outPixel.B; - data[dataIndex++] = outPixel.A; + // Note: whether it's sRGB is not taken into consideration + // we're returning data that could be either/or depending + // on the input BC7 format + data[dataIndex++] = outPixel.R; + data[dataIndex++] = outPixel.G; + data[dataIndex++] = outPixel.B; + data[dataIndex++] = outPixel.A; - // Is mult 4? - if (((i + 1) & 0x3) == 0) - { - dataIndex += self.PixelDepthBytes * (stride - self.DivSize); - } + // Is mult 4? + if (((i + 1) & 0x3) == 0) + { + dataIndex += self.PixelDepthBytes * (stride - self.DivSize); } } - else + } + else + { + Debug.WriteLine("BC7: Reserved mode 8 encountered during decoding"); + + // Per the BC7 format spec, we must return transparent black + for (int i = 0; i < Constants.NumPixelsPerBlock; ++i) { - Debug.WriteLine("BC7: Reserved mode 8 encountered during decoding"); + data[dataIndex++] = 0; + data[dataIndex++] = 0; + data[dataIndex++] = 0; + data[dataIndex++] = 0; - // Per the BC7 format spec, we must return transparent black - for (int i = 0; i < Constants.NumPixelsPerBlock; ++i) + // Is mult 4? + if (((i + 1) & 0x3) == 0) { - data[dataIndex++] = 0; - data[dataIndex++] = 0; - data[dataIndex++] = 0; - data[dataIndex++] = 0; - - // Is mult 4? - if (((i + 1) & 0x3) == 0) - { - dataIndex += self.PixelDepthBytes * (stride - self.DivSize); - } + dataIndex += self.PixelDepthBytes * (stride - self.DivSize); } } + } - return streamIndex; - }); - } + return streamIndex; + }); + } - /// - /// Gets a bit for a given position. - /// - /// The current block. - /// The start bit. - /// A bit at a given position. - public static byte GetBit(byte[] currentBlock, ref uint uStartBit) + /// + /// Gets a bit for a given position. + /// + /// The current block. + /// The start bit. + /// A bit at a given position. + public static byte GetBit(byte[] currentBlock, ref uint uStartBit) + { + Guard.MustBeLessThan(uStartBit, 128, nameof(uStartBit)); + uint uIndex = uStartBit >> 3; + byte ret = (byte)((currentBlock[uIndex] >> (int)(uStartBit - (uIndex << 3))) & 0x01); + uStartBit++; + return ret; + } + + /// + /// Gets n bits at a given start position. + /// + /// The current block. + /// The start bit. + /// The number of bits. + /// Bits at a given position. + public static byte GetBits(byte[] currentBlock, ref uint uStartBit, uint uNumBits) + { + if (uNumBits == 0) { - Guard.MustBeLessThan(uStartBit, 128, nameof(uStartBit)); - uint uIndex = uStartBit >> 3; - byte ret = (byte)((currentBlock[uIndex] >> (int)(uStartBit - (uIndex << 3))) & 0x01); - uStartBit++; - return ret; + return 0; } - /// - /// Gets n bits at a given start position. - /// - /// The current block. - /// The start bit. - /// The number of bits. - /// Bits at a given position. - public static byte GetBits(byte[] currentBlock, ref uint uStartBit, uint uNumBits) + Debug.Assert(uStartBit + uNumBits <= 128 && uNumBits <= 8, "uStartBit + uNumBits <= 128 && uNumBits <= 8"); + byte ret; + uint uIndex = uStartBit >> 3; + uint uBase = uStartBit - (uIndex << 3); + if (uBase + uNumBits > 8) { - if (uNumBits == 0) - { - return 0; - } - - Debug.Assert(uStartBit + uNumBits <= 128 && uNumBits <= 8, "uStartBit + uNumBits <= 128 && uNumBits <= 8"); - byte ret; - uint uIndex = uStartBit >> 3; - uint uBase = uStartBit - (uIndex << 3); - if (uBase + uNumBits > 8) - { - uint uFirstIndexBits = 8 - uBase; - uint uNextIndexBits = uNumBits - uFirstIndexBits; - ret = (byte)((uint)(currentBlock[uIndex] >> (int)uBase) | ((currentBlock[uIndex + 1] & ((1u << (int)uNextIndexBits) - 1)) << (int)uFirstIndexBits)); - } - else - { - ret = (byte)((currentBlock[uIndex] >> (int)uBase) & ((1 << (int)uNumBits) - 1)); - } - - Debug.Assert(ret < (1 << (int)uNumBits), $"GetBits() return value should be smaller then {1 << (int)uNumBits}"); - uStartBit += uNumBits; - return ret; + uint uFirstIndexBits = 8 - uBase; + uint uNextIndexBits = uNumBits - uFirstIndexBits; + ret = (byte)((uint)(currentBlock[uIndex] >> (int)uBase) | ((currentBlock[uIndex + 1] & ((1u << (int)uNextIndexBits) - 1)) << (int)uFirstIndexBits)); } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static byte Unquantize(byte comp, uint uPrec) + else { - Guard.MustBeBetweenOrEqualTo(uPrec, 1, 8, nameof(uPrec)); - comp = (byte)(comp << (int)(8u - uPrec)); - return (byte)(comp | (comp >> (int)uPrec)); + ret = (byte)((currentBlock[uIndex] >> (int)uBase) & ((1 << (int)uNumBits) - 1)); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static LdrColorA Unquantize(LdrColorA c, LdrColorA rgbaPrec) + Debug.Assert(ret < (1 << (int)uNumBits), $"GetBits() return value should be smaller then {1 << (int)uNumBits}"); + uStartBit += uNumBits; + return ret; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static byte Unquantize(byte comp, uint uPrec) + { + Guard.MustBeBetweenOrEqualTo(uPrec, 1, 8, nameof(uPrec)); + comp = (byte)(comp << (int)(8u - uPrec)); + return (byte)(comp | (comp >> (int)uPrec)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static LdrColorA Unquantize(LdrColorA c, LdrColorA rgbaPrec) + { + LdrColorA q = new LdrColorA { - var q = new LdrColorA - { - R = Unquantize(c.R, rgbaPrec.R), - G = Unquantize(c.G, rgbaPrec.G), - B = Unquantize(c.B, rgbaPrec.B), - A = rgbaPrec.A > 0 ? Unquantize(c.A, rgbaPrec.A) : (byte)255u - }; + R = Unquantize(c.R, rgbaPrec.R), + G = Unquantize(c.G, rgbaPrec.G), + B = Unquantize(c.B, rgbaPrec.B), + A = rgbaPrec.A > 0 ? Unquantize(c.A, rgbaPrec.A) : (byte)255u + }; - return q; - } + return q; } } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Bc7ModeInfo.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Bc7ModeInfo.cs index e3debea2..b5bde5ac 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Bc7ModeInfo.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Bc7ModeInfo.cs @@ -3,31 +3,30 @@ using SixLabors.ImageSharp.Textures.TextureFormats.Decoding.PixelFormats; -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; + +internal struct Bc7ModeInfo { - internal struct Bc7ModeInfo - { - public readonly byte UPartitions; - public readonly byte UPartitionBits; - public readonly byte UPBits; - public readonly byte URotationBits; - public readonly byte UIndexModeBits; - public readonly byte UIndexPrec; - public readonly byte UIndexPrec2; - public readonly LdrColorA RgbaPrec; - public readonly LdrColorA RgbaPrecWithP; + public readonly byte UPartitions; + public readonly byte UPartitionBits; + public readonly byte UPBits; + public readonly byte URotationBits; + public readonly byte UIndexModeBits; + public readonly byte UIndexPrec; + public readonly byte UIndexPrec2; + public readonly LdrColorA RgbaPrec; + public readonly LdrColorA RgbaPrecWithP; - public Bc7ModeInfo(byte uParts, byte uPartBits, byte upBits, byte uRotBits, byte uIndModeBits, byte uIndPrec, byte uIndPrec2, LdrColorA rgbaPrec, LdrColorA rgbaPrecWithP) - { - this.UPartitions = uParts; - this.UPartitionBits = uPartBits; - this.UPBits = upBits; - this.URotationBits = uRotBits; - this.UIndexModeBits = uIndModeBits; - this.UIndexPrec = uIndPrec; - this.UIndexPrec2 = uIndPrec2; - this.RgbaPrec = rgbaPrec; - this.RgbaPrecWithP = rgbaPrecWithP; - } + public Bc7ModeInfo(byte uParts, byte uPartBits, byte upBits, byte uRotBits, byte uIndModeBits, byte uIndPrec, byte uIndPrec2, LdrColorA rgbaPrec, LdrColorA rgbaPrecWithP) + { + this.UPartitions = uParts; + this.UPartitionBits = uPartBits; + this.UPBits = upBits; + this.URotationBits = uRotBits; + this.UIndexModeBits = uIndModeBits; + this.UIndexPrec = uIndPrec; + this.UIndexPrec2 = uIndPrec2; + this.RgbaPrec = rgbaPrec; + this.RgbaPrecWithP = rgbaPrecWithP; } } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Bgr24.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Bgr24.cs index d349a910..7de43926 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Bgr24.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Bgr24.cs @@ -1,36 +1,35 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding -{ - /// - /// Texture for pixel with the BGR24 format. - /// - internal struct Bgr24 : IBlock - { - /// - public int BitsPerPixel => 24; +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; - /// - public byte PixelDepthBytes => 3; +/// +/// Texture for pixel with the BGR24 format. +/// +internal struct Bgr24 : IBlock +{ + /// + public readonly int BitsPerPixel => 24; - /// - public byte DivSize => 1; + /// + public readonly byte PixelDepthBytes => 3; - /// - public byte CompressedBytesPerBlock => 3; + /// + public readonly byte DivSize => 1; - /// - public bool Compressed => false; + /// + public readonly byte CompressedBytesPerBlock => 3; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public readonly bool Compressed => false; - /// - public byte[] Decompress(byte[] blockData, int width, int height) => blockData; + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); } + + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) => blockData; } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Bgr32.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Bgr32.cs index b3374cec..1deca0f2 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Bgr32.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Bgr32.cs @@ -1,36 +1,35 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding -{ - /// - /// Texture for the pixel format Bgr32. - /// - internal struct Bgr32 : IBlock - { - /// - public int BitsPerPixel => 32; +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; - /// - public byte PixelDepthBytes => 4; +/// +/// Texture for the pixel format Bgr32. +/// +internal struct Bgr32 : IBlock +{ + /// + public readonly int BitsPerPixel => 32; - /// - public byte DivSize => 1; + /// + public readonly byte PixelDepthBytes => 4; - /// - public byte CompressedBytesPerBlock => 4; + /// + public readonly byte DivSize => 1; - /// - public bool Compressed => false; + /// + public readonly byte CompressedBytesPerBlock => 4; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public readonly bool Compressed => false; - /// - public byte[] Decompress(byte[] blockData, int width, int height) => blockData; + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); } + + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) => blockData; } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Bgr555.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Bgr555.cs index 1d0ddbeb..4bcc8b18 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Bgr555.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Bgr555.cs @@ -1,36 +1,35 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding -{ - /// - /// Texture for pixels with 5 bits per channel (without a alpha channel). - /// - internal struct Bgr555 : IBlock - { - /// - public int BitsPerPixel => 16; +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; - /// - public byte PixelDepthBytes => 2; +/// +/// Texture for pixels with 5 bits per channel (without a alpha channel). +/// +internal struct Bgr555 : IBlock +{ + /// + public readonly int BitsPerPixel => 16; - /// - public byte DivSize => 1; + /// + public readonly byte PixelDepthBytes => 2; - /// - public byte CompressedBytesPerBlock => 2; + /// + public readonly byte DivSize => 1; - /// - public bool Compressed => false; + /// + public readonly byte CompressedBytesPerBlock => 2; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public readonly bool Compressed => false; - /// - public byte[] Decompress(byte[] blockData, int width, int height) => blockData; + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); } + + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) => blockData; } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Bgr565.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Bgr565.cs index e8d1ee2c..0954f017 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Bgr565.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Bgr565.cs @@ -1,36 +1,35 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding -{ - /// - /// Texture for pixels with 5 bits for the red 6 bits for blue and 5 bits for green. - /// - internal struct Bgr565 : IBlock - { - /// - public int BitsPerPixel => 16; +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; - /// - public byte PixelDepthBytes => 2; +/// +/// Texture for pixels with 5 bits for the red 6 bits for blue and 5 bits for green. +/// +internal struct Bgr565 : IBlock +{ + /// + public readonly int BitsPerPixel => 16; - /// - public byte DivSize => 1; + /// + public readonly byte PixelDepthBytes => 2; - /// - public byte CompressedBytesPerBlock => 2; + /// + public readonly byte DivSize => 1; - /// - public bool Compressed => false; + /// + public readonly byte CompressedBytesPerBlock => 2; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public readonly bool Compressed => false; - /// - public byte[] Decompress(byte[] blockData, int width, int height) => blockData; + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); } + + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) => blockData; } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Bgra16.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Bgra16.cs index 0ff94436..4bb23fe7 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Bgra16.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Bgra16.cs @@ -1,36 +1,35 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding -{ - /// - /// Texture for pixels with 4 bits for each channel including the alpha channel. - /// - internal struct Bgra16 : IBlock - { - /// - public int BitsPerPixel => 16; +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; - /// - public byte PixelDepthBytes => 2; +/// +/// Texture for pixels with 4 bits for each channel including the alpha channel. +/// +internal struct Bgra16 : IBlock +{ + /// + public readonly int BitsPerPixel => 16; - /// - public byte DivSize => 1; + /// + public readonly byte PixelDepthBytes => 2; - /// - public byte CompressedBytesPerBlock => 2; + /// + public readonly byte DivSize => 1; - /// - public bool Compressed => false; + /// + public readonly byte CompressedBytesPerBlock => 2; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public readonly bool Compressed => false; - /// - public byte[] Decompress(byte[] blockData, int width, int height) => blockData; + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); } + + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) => blockData; } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Bgra32.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Bgra32.cs index a2f5e85f..e08ed97e 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Bgra32.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Bgra32.cs @@ -1,36 +1,35 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding -{ - /// - /// Texture for the pixel format Bgra32. - /// - internal struct Bgra32 : IBlock - { - /// - public int BitsPerPixel => 32; +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; - /// - public byte PixelDepthBytes => 4; +/// +/// Texture for the pixel format Bgra32. +/// +internal struct Bgra32 : IBlock +{ + /// + public readonly int BitsPerPixel => 32; - /// - public byte DivSize => 1; + /// + public readonly byte PixelDepthBytes => 4; - /// - public byte CompressedBytesPerBlock => 4; + /// + public readonly byte DivSize => 1; - /// - public bool Compressed => false; + /// + public readonly byte CompressedBytesPerBlock => 4; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public readonly bool Compressed => false; - /// - public byte[] Decompress(byte[] blockData, int width, int height) => blockData; + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); } + + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) => blockData; } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Bgra4444.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Bgra4444.cs index 97b67963..ab357fc6 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Bgra4444.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Bgra4444.cs @@ -1,36 +1,35 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding -{ - /// - /// Texture for pixels with 4 bits per channel (including alpha channel). - /// - internal struct Bgra4444 : IBlock - { - /// - public int BitsPerPixel => 16; +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; - /// - public byte PixelDepthBytes => 2; +/// +/// Texture for pixels with 4 bits per channel (including alpha channel). +/// +internal struct Bgra4444 : IBlock +{ + /// + public readonly int BitsPerPixel => 16; - /// - public byte DivSize => 1; + /// + public readonly byte PixelDepthBytes => 2; - /// - public byte CompressedBytesPerBlock => 2; + /// + public readonly byte DivSize => 1; - /// - public bool Compressed => false; + /// + public readonly byte CompressedBytesPerBlock => 2; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public readonly bool Compressed => false; - /// - public byte[] Decompress(byte[] blockData, int width, int height) => blockData; + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); } + + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) => blockData; } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Bgra5551.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Bgra5551.cs index a52919ec..17a31554 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Bgra5551.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Bgra5551.cs @@ -1,36 +1,35 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding -{ - /// - /// Texture format for pixel data with 5 bit for each color channel and 1 bit for the alpha channel. - /// - internal struct Bgra5551 : IBlock - { - /// - public int BitsPerPixel => 16; +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; - /// - public byte PixelDepthBytes => 2; +/// +/// Texture format for pixel data with 5 bit for each color channel and 1 bit for the alpha channel. +/// +internal struct Bgra5551 : IBlock +{ + /// + public readonly int BitsPerPixel => 16; - /// - public byte DivSize => 1; + /// + public readonly byte PixelDepthBytes => 2; - /// - public byte CompressedBytesPerBlock => 2; + /// + public readonly byte DivSize => 1; - /// - public bool Compressed => false; + /// + public readonly byte CompressedBytesPerBlock => 2; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public readonly bool Compressed => false; - /// - public byte[] Decompress(byte[] blockData, int width, int height) => blockData; + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); } + + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) => blockData; } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Dxt1.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Dxt1.cs index 635826c6..9476dd60 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Dxt1.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Dxt1.cs @@ -3,98 +3,97 @@ using SixLabors.ImageSharp.Textures.Common.Helpers; -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; + +/// +/// Texture compressed with DXT1. +/// +internal struct Dxt1 : IBlock { - /// - /// Texture compressed with DXT1. - /// - internal struct Dxt1 : IBlock - { - /// - public int BitsPerPixel => 24; + /// + public readonly int BitsPerPixel => 24; - /// - public byte PixelDepthBytes => 3; + /// + public readonly byte PixelDepthBytes => 3; - /// - public byte DivSize => 4; + /// + public readonly byte DivSize => 4; - /// - public byte CompressedBytesPerBlock => 8; + /// + public readonly byte CompressedBytesPerBlock => 8; - /// - public bool Compressed => true; + /// + public readonly bool Compressed => true; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); + } - /// - public byte[] Decompress(byte[] blockData, int width, int height) - { - Dxt1 self = this; - var colors = new ImageSharp.PixelFormats.Rgb24[4]; + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) + { + Dxt1 self = this; + ImageSharp.PixelFormats.Rgb24[] colors = new ImageSharp.PixelFormats.Rgb24[4]; - return Helper.InMemoryDecode(blockData, width, height, (stream, data, streamIndex, dataIndex, stride) => - { - ushort color0 = blockData[streamIndex++]; - color0 |= (ushort)(blockData[streamIndex++] << 8); + return Helper.InMemoryDecode(blockData, width, height, (stream, data, streamIndex, dataIndex, stride) => + { + ushort color0 = blockData[streamIndex++]; + color0 |= (ushort)(blockData[streamIndex++] << 8); - ushort color1 = blockData[streamIndex++]; - color1 |= (ushort)(blockData[streamIndex++] << 8); + ushort color1 = blockData[streamIndex++]; + color1 |= (ushort)(blockData[streamIndex++] << 8); - // Extract R5G6B5 - PixelUtils.ExtractR5G6B5(color0, ref colors[0]); - PixelUtils.ExtractR5G6B5(color1, ref colors[1]); + // Extract R5G6B5 + PixelUtils.ExtractR5G6B5(color0, ref colors[0]); + PixelUtils.ExtractR5G6B5(color1, ref colors[1]); - // Used the two extracted colors to create two new colors that are - // slightly different. - if (color0 > color1) - { - colors[2].R = (byte)(((2 * colors[0].R) + colors[1].R) / 3); - colors[2].G = (byte)(((2 * colors[0].G) + colors[1].G) / 3); - colors[2].B = (byte)(((2 * colors[0].B) + colors[1].B) / 3); + // Used the two extracted colors to create two new colors that are + // slightly different. + if (color0 > color1) + { + colors[2].R = (byte)(((2 * colors[0].R) + colors[1].R) / 3); + colors[2].G = (byte)(((2 * colors[0].G) + colors[1].G) / 3); + colors[2].B = (byte)(((2 * colors[0].B) + colors[1].B) / 3); + + colors[3].R = (byte)((colors[0].R + (2 * colors[1].R)) / 3); + colors[3].G = (byte)((colors[0].G + (2 * colors[1].G)) / 3); + colors[3].B = (byte)((colors[0].B + (2 * colors[1].B)) / 3); + } + else + { + colors[2].R = (byte)((colors[0].R + colors[1].R) / 2); + colors[2].G = (byte)((colors[0].G + colors[1].G) / 2); + colors[2].B = (byte)((colors[0].B + colors[1].B) / 2); - colors[3].R = (byte)((colors[0].R + (2 * colors[1].R)) / 3); - colors[3].G = (byte)((colors[0].G + (2 * colors[1].G)) / 3); - colors[3].B = (byte)((colors[0].B + (2 * colors[1].B)) / 3); - } - else - { - colors[2].R = (byte)((colors[0].R + colors[1].R) / 2); - colors[2].G = (byte)((colors[0].G + colors[1].G) / 2); - colors[2].B = (byte)((colors[0].B + colors[1].B) / 2); + colors[3].R = 0; + colors[3].G = 0; + colors[3].B = 0; + } - colors[3].R = 0; - colors[3].G = 0; - colors[3].B = 0; - } + for (int i = 0; i < 4; i++) + { + // Every 2 bit is a code [0-3] and represent what color the current pixel is. - for (int i = 0; i < 4; i++) + // Read in a byte and thus 4 colors. + byte rowVal = blockData[streamIndex++]; + for (int j = 0; j < 8; j += 2) { - // Every 2 bit is a code [0-3] and represent what color the current pixel is. - - // Read in a byte and thus 4 colors. - byte rowVal = blockData[streamIndex++]; - for (int j = 0; j < 8; j += 2) - { - // Extract code by shifting the row byte so that we can - // AND it with 3 and get a value [0-3] - ImageSharp.PixelFormats.Rgb24 col = colors[(rowVal >> j) & 0x03]; - data[dataIndex++] = col.R; - data[dataIndex++] = col.G; - data[dataIndex++] = col.B; - } - - // Jump down a row and start at the beginning of the row. - dataIndex += self.PixelDepthBytes * (stride - self.DivSize); + // Extract code by shifting the row byte so that we can + // AND it with 3 and get a value [0-3] + ImageSharp.PixelFormats.Rgb24 col = colors[(rowVal >> j) & 0x03]; + data[dataIndex++] = col.R; + data[dataIndex++] = col.G; + data[dataIndex++] = col.B; } - return streamIndex; - }); - } + // Jump down a row and start at the beginning of the row. + dataIndex += self.PixelDepthBytes * (stride - self.DivSize); + } + + return streamIndex; + }); } } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Dxt3.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Dxt3.cs index c00291df..773d5824 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Dxt3.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Dxt3.cs @@ -3,101 +3,100 @@ using SixLabors.ImageSharp.Textures.Common.Helpers; -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; + +/// +/// Texture compressed with DXT3. +/// +internal struct Dxt3 : IBlock { - /// - /// Texture compressed with DXT3. - /// - internal struct Dxt3 : IBlock - { - /// - public int BitsPerPixel => 32; + /// + public readonly int BitsPerPixel => 32; - /// - public byte PixelDepthBytes => 4; + /// + public readonly byte PixelDepthBytes => 4; - /// - public byte DivSize => 4; + /// + public readonly byte DivSize => 4; - /// - public byte CompressedBytesPerBlock => 16; + /// + public readonly byte CompressedBytesPerBlock => 16; - /// - public bool Compressed => true; + /// + public readonly bool Compressed => true; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); + } - /// - public byte[] Decompress(byte[] blockData, int width, int height) + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) + { + Dxt3 self = this; + ImageSharp.PixelFormats.Rgb24[] colors = new ImageSharp.PixelFormats.Rgb24[4]; + + return Helper.InMemoryDecode(blockData, width, height, (stream, data, streamIndex, dataIndex, stride) => { - Dxt3 self = this; - var colors = new ImageSharp.PixelFormats.Rgb24[4]; + /* + * Strategy for decompression: + * -We're going to decode both alpha and color at the same time + * to save on space and time as we don't have to allocate an array + * to store values for later use. + */ - return Helper.InMemoryDecode(blockData, width, height, (stream, data, streamIndex, dataIndex, stride) => - { - /* - * Strategy for decompression: - * -We're going to decode both alpha and color at the same time - * to save on space and time as we don't have to allocate an array - * to store values for later use. - */ + // Remember where the alpha data is stored so we can decode simultaneously. + int alphaPtr = streamIndex; - // Remember where the alpha data is stored so we can decode simultaneously. - int alphaPtr = streamIndex; + // Jump ahead to the color data. + streamIndex += 8; - // Jump ahead to the color data. - streamIndex += 8; + // Colors are stored in a pair of 16 bits. + ushort color0 = blockData[streamIndex++]; + color0 |= (ushort)(blockData[streamIndex++] << 8); - // Colors are stored in a pair of 16 bits. - ushort color0 = blockData[streamIndex++]; - color0 |= (ushort)(blockData[streamIndex++] << 8); + ushort color1 = blockData[streamIndex++]; + color1 |= (ushort)(blockData[streamIndex++] << 8); - ushort color1 = blockData[streamIndex++]; - color1 |= (ushort)(blockData[streamIndex++] << 8); + // Extract R5G6B5. + PixelUtils.ExtractR5G6B5(color0, ref colors[0]); + PixelUtils.ExtractR5G6B5(color1, ref colors[1]); - // Extract R5G6B5. - PixelUtils.ExtractR5G6B5(color0, ref colors[0]); - PixelUtils.ExtractR5G6B5(color1, ref colors[1]); + // Used the two extracted colors to create two new colors + // that are slightly different. + colors[2].R = (byte)(((2 * colors[0].R) + colors[1].R) / 3); + colors[2].G = (byte)(((2 * colors[0].G) + colors[1].G) / 3); + colors[2].B = (byte)(((2 * colors[0].B) + colors[1].B) / 3); - // Used the two extracted colors to create two new colors - // that are slightly different. - colors[2].R = (byte)(((2 * colors[0].R) + colors[1].R) / 3); - colors[2].G = (byte)(((2 * colors[0].G) + colors[1].G) / 3); - colors[2].B = (byte)(((2 * colors[0].B) + colors[1].B) / 3); + colors[3].R = (byte)((colors[0].R + (2 * colors[1].R)) / 3); + colors[3].G = (byte)((colors[0].G + (2 * colors[1].G)) / 3); + colors[3].B = (byte)((colors[0].B + (2 * colors[1].B)) / 3); - colors[3].R = (byte)((colors[0].R + (2 * colors[1].R)) / 3); - colors[3].G = (byte)((colors[0].G + (2 * colors[1].G)) / 3); - colors[3].B = (byte)((colors[0].B + (2 * colors[1].B)) / 3); + for (int i = 0; i < 4; i++) + { + byte rowVal = blockData[streamIndex++]; - for (int i = 0; i < 4; i++) + // Each row of rgb values have 4 alpha values that are encoded in 4 bits. + ushort rowAlpha = blockData[alphaPtr++]; + rowAlpha |= (ushort)(blockData[alphaPtr++] << 8); + + for (int j = 0; j < 8; j += 2) { - byte rowVal = blockData[streamIndex++]; - - // Each row of rgb values have 4 alpha values that are encoded in 4 bits. - ushort rowAlpha = blockData[alphaPtr++]; - rowAlpha |= (ushort)(blockData[alphaPtr++] << 8); - - for (int j = 0; j < 8; j += 2) - { - byte currentAlpha = (byte)((rowAlpha >> (j * 2)) & 0x0f); - currentAlpha |= (byte)(currentAlpha << 4); - ImageSharp.PixelFormats.Rgb24 col = colors[(rowVal >> j) & 0x03]; - data[dataIndex++] = col.R; - data[dataIndex++] = col.G; - data[dataIndex++] = col.B; - data[dataIndex++] = currentAlpha; - } - - dataIndex += self.PixelDepthBytes * (stride - self.DivSize); + byte currentAlpha = (byte)((rowAlpha >> (j * 2)) & 0x0f); + currentAlpha |= (byte)(currentAlpha << 4); + ImageSharp.PixelFormats.Rgb24 col = colors[(rowVal >> j) & 0x03]; + data[dataIndex++] = col.R; + data[dataIndex++] = col.G; + data[dataIndex++] = col.B; + data[dataIndex++] = currentAlpha; } - return streamIndex; - }); - } + dataIndex += self.PixelDepthBytes * (stride - self.DivSize); + } + + return streamIndex; + }); } } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Dxt5.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Dxt5.cs index f16df743..9d3734a9 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Dxt5.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Dxt5.cs @@ -3,91 +3,90 @@ using SixLabors.ImageSharp.Textures.Common.Helpers; -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; + +/// +/// Texture compressed with DXT5. +/// +internal struct Dxt5 : IBlock { - /// - /// Texture compressed with DXT5. - /// - internal struct Dxt5 : IBlock - { - /// - public int BitsPerPixel => 32; + /// + public readonly int BitsPerPixel => 32; - /// - public byte PixelDepthBytes => 4; + /// + public readonly byte PixelDepthBytes => 4; - /// - public byte DivSize => 4; + /// + public readonly byte DivSize => 4; - /// - public byte CompressedBytesPerBlock => 16; + /// + public readonly byte CompressedBytesPerBlock => 16; - /// - public bool Compressed => true; + /// + public readonly bool Compressed => true; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); + } - /// - public byte[] Decompress(byte[] blockData, int width, int height) - { - Dxt5 self = this; - byte[] alpha = new byte[8]; - var colors = new ImageSharp.PixelFormats.Rgb24[4]; + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) + { + Dxt5 self = this; + byte[] alpha = new byte[8]; + ImageSharp.PixelFormats.Rgb24[] colors = new ImageSharp.PixelFormats.Rgb24[4]; - return Helper.InMemoryDecode(blockData, width, height, (stream, data, streamIndex, dataIndex, stride) => - { - streamIndex = Bc5.ExtractGradient(alpha, blockData, streamIndex); + return Helper.InMemoryDecode(blockData, width, height, (stream, data, streamIndex, dataIndex, stride) => + { + streamIndex = Bc5.ExtractGradient(alpha, blockData, streamIndex); - ulong alphaCodes = blockData[streamIndex++]; - alphaCodes |= (ulong)blockData[streamIndex++] << 8; - alphaCodes |= (ulong)blockData[streamIndex++] << 16; - alphaCodes |= (ulong)blockData[streamIndex++] << 24; - alphaCodes |= (ulong)blockData[streamIndex++] << 32; - alphaCodes |= (ulong)blockData[streamIndex++] << 40; + ulong alphaCodes = blockData[streamIndex++]; + alphaCodes |= (ulong)blockData[streamIndex++] << 8; + alphaCodes |= (ulong)blockData[streamIndex++] << 16; + alphaCodes |= (ulong)blockData[streamIndex++] << 24; + alphaCodes |= (ulong)blockData[streamIndex++] << 32; + alphaCodes |= (ulong)blockData[streamIndex++] << 40; - // Colors are stored in a pair of 16 bits. - ushort color0 = blockData[streamIndex++]; - color0 |= (ushort)(blockData[streamIndex++] << 8); + // Colors are stored in a pair of 16 bits. + ushort color0 = blockData[streamIndex++]; + color0 |= (ushort)(blockData[streamIndex++] << 8); - ushort color1 = blockData[streamIndex++]; - color1 |= (ushort)(blockData[streamIndex++] << 8); + ushort color1 = blockData[streamIndex++]; + color1 |= (ushort)(blockData[streamIndex++] << 8); - // Extract R5G6B5. - PixelUtils.ExtractR5G6B5(color0, ref colors[0]); - PixelUtils.ExtractR5G6B5(color1, ref colors[1]); + // Extract R5G6B5. + PixelUtils.ExtractR5G6B5(color0, ref colors[0]); + PixelUtils.ExtractR5G6B5(color1, ref colors[1]); - colors[2].R = (byte)(((2 * colors[0].R) + colors[1].R) / 3); - colors[2].G = (byte)(((2 * colors[0].G) + colors[1].G) / 3); - colors[2].B = (byte)(((2 * colors[0].B) + colors[1].B) / 3); + colors[2].R = (byte)(((2 * colors[0].R) + colors[1].R) / 3); + colors[2].G = (byte)(((2 * colors[0].G) + colors[1].G) / 3); + colors[2].B = (byte)(((2 * colors[0].B) + colors[1].B) / 3); - colors[3].R = (byte)((colors[0].R + (2 * colors[1].R)) / 3); - colors[3].G = (byte)((colors[0].G + (2 * colors[1].G)) / 3); - colors[3].B = (byte)((colors[0].B + (2 * colors[1].B)) / 3); + colors[3].R = (byte)((colors[0].R + (2 * colors[1].R)) / 3); + colors[3].G = (byte)((colors[0].G + (2 * colors[1].G)) / 3); + colors[3].B = (byte)((colors[0].B + (2 * colors[1].B)) / 3); - for (int alphaShift = 0; alphaShift < 48; alphaShift += 12) + for (int alphaShift = 0; alphaShift < 48; alphaShift += 12) + { + byte rowVal = blockData[streamIndex++]; + for (int j = 0; j < 4; j++) { - byte rowVal = blockData[streamIndex++]; - for (int j = 0; j < 4; j++) - { - // 3 bits determine alpha index to use. - byte alphaIndex = (byte)((alphaCodes >> (alphaShift + (3 * j))) & 0x07); - ImageSharp.PixelFormats.Rgb24 col = colors[(rowVal >> (j * 2)) & 0x03]; - data[dataIndex++] = col.R; - data[dataIndex++] = col.G; - data[dataIndex++] = col.B; - data[dataIndex++] = alpha[alphaIndex]; - } - - dataIndex += self.PixelDepthBytes * (stride - self.DivSize); + // 3 bits determine alpha index to use. + byte alphaIndex = (byte)((alphaCodes >> (alphaShift + (3 * j))) & 0x07); + ImageSharp.PixelFormats.Rgb24 col = colors[(rowVal >> (j * 2)) & 0x03]; + data[dataIndex++] = col.R; + data[dataIndex++] = col.G; + data[dataIndex++] = col.B; + data[dataIndex++] = alpha[alphaIndex]; } - return streamIndex; - }); - } + dataIndex += self.PixelDepthBytes * (stride - self.DivSize); + } + + return streamIndex; + }); } } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Etc1.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Etc1.cs index 7ce1acb3..4d1b9c62 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Etc1.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Etc1.cs @@ -1,72 +1,69 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding +/// +/// Texture compressed with ETC1. +/// +internal struct Etc1 : IBlock { - /// - /// Texture compressed with ETC1. - /// - internal struct Etc1 : IBlock - { - /// - public int BitsPerPixel => 24; + /// + public readonly int BitsPerPixel => 24; - /// - public byte PixelDepthBytes => 3; + /// + public readonly byte PixelDepthBytes => 3; - /// - public byte DivSize => 1; + /// + public readonly byte DivSize => 1; - /// - public byte CompressedBytesPerBlock => 8; + /// + public readonly byte CompressedBytesPerBlock => 8; - /// - public bool Compressed => true; + /// + public readonly bool Compressed => true; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); + } - /// - public byte[] Decompress(byte[] blockData, int width, int height) - { - int extraX = 4 - (width % 4); - int extraY = 4 - (height % 4); - byte[] decompressedData = new byte[(width + extraX) * (height + extraY) * 3]; - byte[] decodedPixels = new byte[16 * 3]; - Span decodedPixelSpan = decodedPixels.AsSpan(); - int blockDataIdx = 0; + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) + { + int extraX = 4 - (width % 4); + int extraY = 4 - (height % 4); + byte[] decompressedData = new byte[(width + extraX) * (height + extraY) * 3]; + byte[] decodedPixels = new byte[16 * 3]; + Span decodedPixelSpan = decodedPixels.AsSpan(); + int blockDataIdx = 0; - for (int y = 0; y < height; y += 4) + for (int y = 0; y < height; y += 4) + { + for (int x = 0; x < width; x += 4) { - for (int x = 0; x < width; x += 4) - { - EtcDecoder.DecodeEtc1Block(blockData.AsSpan(blockDataIdx, 8), decodedPixelSpan); + EtcDecoder.DecodeEtc1Block(blockData.AsSpan(blockDataIdx, 8), decodedPixelSpan); - int decodedPixelSpanIdx = 0; - for (int b = 0; b < 4; b++) + int decodedPixelSpanIdx = 0; + for (int b = 0; b < 4; b++) + { + for (int a = 0; a < 4; a++) { - for (int a = 0; a < 4; a++) - { - int imageX = x + b; - int imageY = y + a; - int offset = (imageY * width * 3) + (imageX * 3); - decompressedData[offset] = decodedPixelSpan[decodedPixelSpanIdx++]; - decompressedData[offset + 1] = decodedPixelSpan[decodedPixelSpanIdx++]; - decompressedData[offset + 2] = decodedPixelSpan[decodedPixelSpanIdx++]; - } + int imageX = x + b; + int imageY = y + a; + int offset = (imageY * width * 3) + (imageX * 3); + decompressedData[offset] = decodedPixelSpan[decodedPixelSpanIdx++]; + decompressedData[offset + 1] = decodedPixelSpan[decodedPixelSpanIdx++]; + decompressedData[offset + 2] = decodedPixelSpan[decodedPixelSpanIdx++]; } - - blockDataIdx += 8; } - } - return decompressedData; + blockDataIdx += 8; + } } + + return decompressedData; } } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Etc2.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Etc2.cs index 0b59fa38..c05ca006 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Etc2.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Etc2.cs @@ -1,72 +1,69 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding +/// +/// Texture compressed with ETC2. +/// +internal struct Etc2 : IBlock { - /// - /// Texture compressed with ETC2. - /// - internal struct Etc2 : IBlock - { - /// - public int BitsPerPixel => 24; + /// + public readonly int BitsPerPixel => 24; - /// - public byte PixelDepthBytes => 3; + /// + public readonly byte PixelDepthBytes => 3; - /// - public byte DivSize => 1; + /// + public readonly byte DivSize => 1; - /// - public byte CompressedBytesPerBlock => 8; + /// + public readonly byte CompressedBytesPerBlock => 8; - /// - public bool Compressed => true; + /// + public readonly bool Compressed => true; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); + } - /// - public byte[] Decompress(byte[] blockData, int width, int height) - { - int extraX = 4 - (width % 4); - int extraY = 4 - (height % 4); - byte[] decompressedData = new byte[(width + extraX) * (height + extraY) * 3]; - byte[] decodedPixels = new byte[16 * 3]; - Span decodedPixelSpan = decodedPixels.AsSpan(); - var blockDataIdx = 0; + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) + { + int extraX = 4 - (width % 4); + int extraY = 4 - (height % 4); + byte[] decompressedData = new byte[(width + extraX) * (height + extraY) * 3]; + byte[] decodedPixels = new byte[16 * 3]; + Span decodedPixelSpan = decodedPixels.AsSpan(); + int blockDataIdx = 0; - for (int y = 0; y < height; y += 4) + for (int y = 0; y < height; y += 4) + { + for (int x = 0; x < width; x += 4) { - for (int x = 0; x < width; x += 4) - { - EtcDecoder.DecodeEtc2Block(blockData.AsSpan(blockDataIdx, 8), decodedPixelSpan); + EtcDecoder.DecodeEtc2Block(blockData.AsSpan(blockDataIdx, 8), decodedPixelSpan); - var decodedPixelSpanIdx = 0; - for (int b = 0; b < 4; b++) + int decodedPixelSpanIdx = 0; + for (int b = 0; b < 4; b++) + { + for (int a = 0; a < 4; a++) { - for (int a = 0; a < 4; a++) - { - var imageX = x + b; - var imageY = y + a; - var offset = (imageY * width * 3) + (imageX * 3); - decompressedData[offset] = decodedPixelSpan[decodedPixelSpanIdx++]; - decompressedData[offset + 1] = decodedPixelSpan[decodedPixelSpanIdx++]; - decompressedData[offset + 2] = decodedPixelSpan[decodedPixelSpanIdx++]; - } + int imageX = x + b; + int imageY = y + a; + int offset = (imageY * width * 3) + (imageX * 3); + decompressedData[offset] = decodedPixelSpan[decodedPixelSpanIdx++]; + decompressedData[offset + 1] = decodedPixelSpan[decodedPixelSpanIdx++]; + decompressedData[offset + 2] = decodedPixelSpan[decodedPixelSpanIdx++]; } - - blockDataIdx += 8; } - } - return decompressedData; + blockDataIdx += 8; + } } + + return decompressedData; } } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/EtcDecoder.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/EtcDecoder.cs index 32b1a65a..4804522c 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/EtcDecoder.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/EtcDecoder.cs @@ -1,386 +1,384 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Buffers.Binary; using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; + +/// +/// Decoder for ETC (Ericsson Texture Compression) compressed textures. +/// Based on https://github.com/hglm/detex.git +/// +/// +/// See ktx specification: https://www.khronos.org/registry/DataFormat/specs/1.3/dataformat.1.3.html#ETC1 +/// +internal static class EtcDecoder { - /// - /// Decoder for ETC (Ericsson Texture Compression) compressed textures. - /// Based on https://github.com/hglm/detex.git - /// - /// - /// See ktx specification: https://www.khronos.org/registry/DataFormat/specs/1.3/dataformat.1.3.html#ETC1 - /// - internal static class EtcDecoder + private static readonly int[,] ModifierTable = { - private static readonly int[,] ModifierTable = - { - { 2, 8, -2, -8 }, - { 5, 17, -5, -17 }, - { 9, 29, -9, -29 }, - { 13, 42, -13, -42 }, - { 18, 60, -18, -60 }, - { 24, 80, -24, -80 }, - { 33, 106, -33, -106 }, - { 47, 183, -47, -183 } - }; - - private static readonly int[] Complement3BitShiftedTable = - { - 0, 8, 16, 24, -32, -24, -16, -8 - }; - - private static readonly int[] Etc2DistanceTable = { 3, 6, 11, 16, 23, 32, 41, 64 }; - - public static void DecodeEtc1Block(Span payload, Span decodedPixelSpan) - { - byte red = payload[0]; - byte green = payload[1]; - byte blue = payload[2]; - byte codeWordsWithFlags = payload[3]; - - bool diffFlag = (codeWordsWithFlags & 2) != 0; - bool flipFlag = (codeWordsWithFlags & 1) != 0; - byte codeword1 = (byte)((codeWordsWithFlags & 224) >> 5); - byte codeword2 = (byte)((codeWordsWithFlags & 28) >> 2); - - int c0r, c0g, c0b; - int c1r, c1g, c1b; + { 2, 8, -2, -8 }, + { 5, 17, -5, -17 }, + { 9, 29, -9, -29 }, + { 13, 42, -13, -42 }, + { 18, 60, -18, -60 }, + { 24, 80, -24, -80 }, + { 33, 106, -33, -106 }, + { 47, 183, -47, -183 } + }; + + private static readonly int[] Complement3BitShiftedTable = + [ + 0, 8, 16, 24, -32, -24, -16, -8 + ]; + + private static readonly int[] Etc2DistanceTable = [3, 6, 11, 16, 23, 32, 41, 64]; + + public static void DecodeEtc1Block(Span payload, Span decodedPixelSpan) + { + byte red = payload[0]; + byte green = payload[1]; + byte blue = payload[2]; + byte codeWordsWithFlags = payload[3]; - if (diffFlag) - { - int c0r5 = red & 0xF8; - c0r = FiveToEightBit(c0r5); - int c0g5 = green & 0xF8; - c0g = FiveToEightBit(c0g5); - int c0b5 = blue & 0xF8; - c0b = FiveToEightBit(c0b5); - - int rd3 = Complement3BitShifted(red & 0x7); - int gd3 = Complement3BitShifted(green & 0x7); - int bd3 = Complement3BitShifted(blue & 0x7); - c1r = FiveToEightBit(c0r5 + rd3); - c1g = FiveToEightBit(c0g5 + gd3); - c1b = FiveToEightBit(c0b5 + bd3); - } - else - { - int c0r4 = red & 0xF0; - c0r = c0r4 | (c0r4 >> 4); - int c0g4 = green & 0xF0; - c0g = c0g4 | (c0g4 >> 4); - int c0b4 = blue & 0xF0; - c0b = c0b4 | (c0b4 >> 4); - - int c1r4 = red & 0x0F; - c1r = c1r4 | (c1r4 << 4); - int c1g4 = green & 0x0F; - c1g = c1g4 | (c1g4 << 4); - int c1b4 = blue & 0x0F; - c1b = c1b4 | (c1b4 << 4); - } + bool diffFlag = (codeWordsWithFlags & 2) != 0; + bool flipFlag = (codeWordsWithFlags & 1) != 0; + byte codeword1 = (byte)((codeWordsWithFlags & 224) >> 5); + byte codeword2 = (byte)((codeWordsWithFlags & 28) >> 2); - uint pixelIndexWord = BinaryPrimitives.ReadUInt32BigEndian(payload.Slice(4, 4)); + int c0r, c0g, c0b; + int c1r, c1g, c1b; - // Check if the sub-blocks are horizontal or vertical. - if (!flipFlag) - { - // Flip bit indicates horizontal sub-blocks. - // 0000 - // 0000 - // 1111 - // 1111 - // Iterate over the pixels in each sub-block and set their final values in the image data. - DecompressEtc1BlockHorizontal(pixelIndexWord, codeword1, codeword2, (byte)c0r, (byte)c0g, (byte)c0b, (byte)c1r, (byte)c1g, (byte)c1b, decodedPixelSpan); - } - else - { - // Flip bit indicates vertical sub-blocks. - // 0011 - // 0011 - // 0011 - // 0011 - DecompressEtc1BlockVertical(pixelIndexWord, codeword1, codeword2, (byte)c0r, (byte)c0g, (byte)c0b, (byte)c1r, (byte)c1g, (byte)c1b, decodedPixelSpan); - } + if (diffFlag) + { + int c0r5 = red & 0xF8; + c0r = FiveToEightBit(c0r5); + int c0g5 = green & 0xF8; + c0g = FiveToEightBit(c0g5); + int c0b5 = blue & 0xF8; + c0b = FiveToEightBit(c0b5); + + int rd3 = Complement3BitShifted(red & 0x7); + int gd3 = Complement3BitShifted(green & 0x7); + int bd3 = Complement3BitShifted(blue & 0x7); + c1r = FiveToEightBit(c0r5 + rd3); + c1g = FiveToEightBit(c0g5 + gd3); + c1b = FiveToEightBit(c0b5 + bd3); } - - private static void DecompressEtc1BlockHorizontal( - uint pixelIndexWord, - uint tableCodeword1, - uint tableCodeword2, - byte redBaseColorSubBlock1, - byte greenBaseColorSubBlock1, - byte blueBaseColorSubBlock1, - byte redBaseColorSubBlock2, - byte greenBaseColorSubBlock2, - byte blueBaseColorSubBlock2, - Span pixelBuffer) + else { - ProcessPixelEtc1(0, pixelIndexWord, tableCodeword1, redBaseColorSubBlock1, greenBaseColorSubBlock1, blueBaseColorSubBlock1, pixelBuffer.Slice(0, 3)); - ProcessPixelEtc1(1, pixelIndexWord, tableCodeword1, redBaseColorSubBlock1, greenBaseColorSubBlock1, blueBaseColorSubBlock1, pixelBuffer.Slice(3, 3)); - ProcessPixelEtc1(2, pixelIndexWord, tableCodeword1, redBaseColorSubBlock1, greenBaseColorSubBlock1, blueBaseColorSubBlock1, pixelBuffer.Slice(6, 3)); - ProcessPixelEtc1(3, pixelIndexWord, tableCodeword1, redBaseColorSubBlock1, greenBaseColorSubBlock1, blueBaseColorSubBlock1, pixelBuffer.Slice(9, 3)); - ProcessPixelEtc1(4, pixelIndexWord, tableCodeword1, redBaseColorSubBlock1, greenBaseColorSubBlock1, blueBaseColorSubBlock1, pixelBuffer.Slice(12, 3)); - ProcessPixelEtc1(5, pixelIndexWord, tableCodeword1, redBaseColorSubBlock1, greenBaseColorSubBlock1, blueBaseColorSubBlock1, pixelBuffer.Slice(15, 3)); - ProcessPixelEtc1(6, pixelIndexWord, tableCodeword1, redBaseColorSubBlock1, greenBaseColorSubBlock1, blueBaseColorSubBlock1, pixelBuffer.Slice(18, 3)); - ProcessPixelEtc1(7, pixelIndexWord, tableCodeword1, redBaseColorSubBlock1, greenBaseColorSubBlock1, blueBaseColorSubBlock1, pixelBuffer.Slice(21, 3)); - - ProcessPixelEtc1(8, pixelIndexWord, tableCodeword2, redBaseColorSubBlock2, greenBaseColorSubBlock2, blueBaseColorSubBlock2, pixelBuffer.Slice(24, 3)); - ProcessPixelEtc1(9, pixelIndexWord, tableCodeword2, redBaseColorSubBlock2, greenBaseColorSubBlock2, blueBaseColorSubBlock2, pixelBuffer.Slice(27, 3)); - ProcessPixelEtc1(10, pixelIndexWord, tableCodeword2, redBaseColorSubBlock2, greenBaseColorSubBlock2, blueBaseColorSubBlock2, pixelBuffer.Slice(30, 3)); - ProcessPixelEtc1(11, pixelIndexWord, tableCodeword2, redBaseColorSubBlock2, greenBaseColorSubBlock2, blueBaseColorSubBlock2, pixelBuffer.Slice(33, 3)); - ProcessPixelEtc1(12, pixelIndexWord, tableCodeword2, redBaseColorSubBlock2, greenBaseColorSubBlock2, blueBaseColorSubBlock2, pixelBuffer.Slice(36, 3)); - ProcessPixelEtc1(13, pixelIndexWord, tableCodeword2, redBaseColorSubBlock2, greenBaseColorSubBlock2, blueBaseColorSubBlock2, pixelBuffer.Slice(39, 3)); - ProcessPixelEtc1(14, pixelIndexWord, tableCodeword2, redBaseColorSubBlock2, greenBaseColorSubBlock2, blueBaseColorSubBlock2, pixelBuffer.Slice(42, 3)); - ProcessPixelEtc1(15, pixelIndexWord, tableCodeword2, redBaseColorSubBlock2, greenBaseColorSubBlock2, blueBaseColorSubBlock2, pixelBuffer.Slice(45, 3)); + int c0r4 = red & 0xF0; + c0r = c0r4 | (c0r4 >> 4); + int c0g4 = green & 0xF0; + c0g = c0g4 | (c0g4 >> 4); + int c0b4 = blue & 0xF0; + c0b = c0b4 | (c0b4 >> 4); + + int c1r4 = red & 0x0F; + c1r = c1r4 | (c1r4 << 4); + int c1g4 = green & 0x0F; + c1g = c1g4 | (c1g4 << 4); + int c1b4 = blue & 0x0F; + c1b = c1b4 | (c1b4 << 4); } - private static void DecompressEtc1BlockVertical( - uint pixelIndexWord, - uint tableCodeword1, - uint tableCodeword2, - byte redBaseColorSubBlock1, - byte greenBaseColorSubBlock1, - byte blueBaseColorSubBlock1, - byte redBaseColorSubBlock2, - byte greenBaseColorSubBlock2, - byte blueBaseColorSubBlock2, - Span pixelBuffer) - { - ProcessPixelEtc1(0, pixelIndexWord, tableCodeword1, redBaseColorSubBlock1, greenBaseColorSubBlock1, blueBaseColorSubBlock1, pixelBuffer.Slice(0, 3)); - ProcessPixelEtc1(1, pixelIndexWord, tableCodeword1, redBaseColorSubBlock1, greenBaseColorSubBlock1, blueBaseColorSubBlock1, pixelBuffer.Slice(3, 3)); + uint pixelIndexWord = BinaryPrimitives.ReadUInt32BigEndian(payload.Slice(4, 4)); - ProcessPixelEtc1(2, pixelIndexWord, tableCodeword2, redBaseColorSubBlock2, greenBaseColorSubBlock2, blueBaseColorSubBlock2, pixelBuffer.Slice(6, 3)); - ProcessPixelEtc1(3, pixelIndexWord, tableCodeword2, redBaseColorSubBlock2, greenBaseColorSubBlock2, blueBaseColorSubBlock2, pixelBuffer.Slice(9, 3)); + // Check if the sub-blocks are horizontal or vertical. + if (!flipFlag) + { + // Flip bit indicates horizontal sub-blocks. + // 0000 + // 0000 + // 1111 + // 1111 + // Iterate over the pixels in each sub-block and set their final values in the image data. + DecompressEtc1BlockHorizontal(pixelIndexWord, codeword1, codeword2, (byte)c0r, (byte)c0g, (byte)c0b, (byte)c1r, (byte)c1g, (byte)c1b, decodedPixelSpan); + } + else + { + // Flip bit indicates vertical sub-blocks. + // 0011 + // 0011 + // 0011 + // 0011 + DecompressEtc1BlockVertical(pixelIndexWord, codeword1, codeword2, (byte)c0r, (byte)c0g, (byte)c0b, (byte)c1r, (byte)c1g, (byte)c1b, decodedPixelSpan); + } + } - ProcessPixelEtc1(4, pixelIndexWord, tableCodeword1, redBaseColorSubBlock1, greenBaseColorSubBlock1, blueBaseColorSubBlock1, pixelBuffer.Slice(12, 3)); - ProcessPixelEtc1(5, pixelIndexWord, tableCodeword1, redBaseColorSubBlock1, greenBaseColorSubBlock1, blueBaseColorSubBlock1, pixelBuffer.Slice(15, 3)); + private static void DecompressEtc1BlockHorizontal( + uint pixelIndexWord, + uint tableCodeword1, + uint tableCodeword2, + byte redBaseColorSubBlock1, + byte greenBaseColorSubBlock1, + byte blueBaseColorSubBlock1, + byte redBaseColorSubBlock2, + byte greenBaseColorSubBlock2, + byte blueBaseColorSubBlock2, + Span pixelBuffer) + { + ProcessPixelEtc1(0, pixelIndexWord, tableCodeword1, redBaseColorSubBlock1, greenBaseColorSubBlock1, blueBaseColorSubBlock1, pixelBuffer[..3]); + ProcessPixelEtc1(1, pixelIndexWord, tableCodeword1, redBaseColorSubBlock1, greenBaseColorSubBlock1, blueBaseColorSubBlock1, pixelBuffer.Slice(3, 3)); + ProcessPixelEtc1(2, pixelIndexWord, tableCodeword1, redBaseColorSubBlock1, greenBaseColorSubBlock1, blueBaseColorSubBlock1, pixelBuffer.Slice(6, 3)); + ProcessPixelEtc1(3, pixelIndexWord, tableCodeword1, redBaseColorSubBlock1, greenBaseColorSubBlock1, blueBaseColorSubBlock1, pixelBuffer.Slice(9, 3)); + ProcessPixelEtc1(4, pixelIndexWord, tableCodeword1, redBaseColorSubBlock1, greenBaseColorSubBlock1, blueBaseColorSubBlock1, pixelBuffer.Slice(12, 3)); + ProcessPixelEtc1(5, pixelIndexWord, tableCodeword1, redBaseColorSubBlock1, greenBaseColorSubBlock1, blueBaseColorSubBlock1, pixelBuffer.Slice(15, 3)); + ProcessPixelEtc1(6, pixelIndexWord, tableCodeword1, redBaseColorSubBlock1, greenBaseColorSubBlock1, blueBaseColorSubBlock1, pixelBuffer.Slice(18, 3)); + ProcessPixelEtc1(7, pixelIndexWord, tableCodeword1, redBaseColorSubBlock1, greenBaseColorSubBlock1, blueBaseColorSubBlock1, pixelBuffer.Slice(21, 3)); + + ProcessPixelEtc1(8, pixelIndexWord, tableCodeword2, redBaseColorSubBlock2, greenBaseColorSubBlock2, blueBaseColorSubBlock2, pixelBuffer.Slice(24, 3)); + ProcessPixelEtc1(9, pixelIndexWord, tableCodeword2, redBaseColorSubBlock2, greenBaseColorSubBlock2, blueBaseColorSubBlock2, pixelBuffer.Slice(27, 3)); + ProcessPixelEtc1(10, pixelIndexWord, tableCodeword2, redBaseColorSubBlock2, greenBaseColorSubBlock2, blueBaseColorSubBlock2, pixelBuffer.Slice(30, 3)); + ProcessPixelEtc1(11, pixelIndexWord, tableCodeword2, redBaseColorSubBlock2, greenBaseColorSubBlock2, blueBaseColorSubBlock2, pixelBuffer.Slice(33, 3)); + ProcessPixelEtc1(12, pixelIndexWord, tableCodeword2, redBaseColorSubBlock2, greenBaseColorSubBlock2, blueBaseColorSubBlock2, pixelBuffer.Slice(36, 3)); + ProcessPixelEtc1(13, pixelIndexWord, tableCodeword2, redBaseColorSubBlock2, greenBaseColorSubBlock2, blueBaseColorSubBlock2, pixelBuffer.Slice(39, 3)); + ProcessPixelEtc1(14, pixelIndexWord, tableCodeword2, redBaseColorSubBlock2, greenBaseColorSubBlock2, blueBaseColorSubBlock2, pixelBuffer.Slice(42, 3)); + ProcessPixelEtc1(15, pixelIndexWord, tableCodeword2, redBaseColorSubBlock2, greenBaseColorSubBlock2, blueBaseColorSubBlock2, pixelBuffer.Slice(45, 3)); + } - ProcessPixelEtc1(6, pixelIndexWord, tableCodeword2, redBaseColorSubBlock2, greenBaseColorSubBlock2, blueBaseColorSubBlock2, pixelBuffer.Slice(18, 3)); - ProcessPixelEtc1(7, pixelIndexWord, tableCodeword2, redBaseColorSubBlock2, greenBaseColorSubBlock2, blueBaseColorSubBlock2, pixelBuffer.Slice(21, 3)); + private static void DecompressEtc1BlockVertical( + uint pixelIndexWord, + uint tableCodeword1, + uint tableCodeword2, + byte redBaseColorSubBlock1, + byte greenBaseColorSubBlock1, + byte blueBaseColorSubBlock1, + byte redBaseColorSubBlock2, + byte greenBaseColorSubBlock2, + byte blueBaseColorSubBlock2, + Span pixelBuffer) + { + ProcessPixelEtc1(0, pixelIndexWord, tableCodeword1, redBaseColorSubBlock1, greenBaseColorSubBlock1, blueBaseColorSubBlock1, pixelBuffer[..3]); + ProcessPixelEtc1(1, pixelIndexWord, tableCodeword1, redBaseColorSubBlock1, greenBaseColorSubBlock1, blueBaseColorSubBlock1, pixelBuffer.Slice(3, 3)); - ProcessPixelEtc1(8, pixelIndexWord, tableCodeword1, redBaseColorSubBlock1, greenBaseColorSubBlock1, blueBaseColorSubBlock1, pixelBuffer.Slice(24, 3)); - ProcessPixelEtc1(9, pixelIndexWord, tableCodeword1, redBaseColorSubBlock1, greenBaseColorSubBlock1, blueBaseColorSubBlock1, pixelBuffer.Slice(27, 3)); + ProcessPixelEtc1(2, pixelIndexWord, tableCodeword2, redBaseColorSubBlock2, greenBaseColorSubBlock2, blueBaseColorSubBlock2, pixelBuffer.Slice(6, 3)); + ProcessPixelEtc1(3, pixelIndexWord, tableCodeword2, redBaseColorSubBlock2, greenBaseColorSubBlock2, blueBaseColorSubBlock2, pixelBuffer.Slice(9, 3)); - ProcessPixelEtc1(10, pixelIndexWord, tableCodeword2, redBaseColorSubBlock2, greenBaseColorSubBlock2, blueBaseColorSubBlock2, pixelBuffer.Slice(30, 3)); - ProcessPixelEtc1(11, pixelIndexWord, tableCodeword2, redBaseColorSubBlock2, greenBaseColorSubBlock2, blueBaseColorSubBlock2, pixelBuffer.Slice(33, 3)); + ProcessPixelEtc1(4, pixelIndexWord, tableCodeword1, redBaseColorSubBlock1, greenBaseColorSubBlock1, blueBaseColorSubBlock1, pixelBuffer.Slice(12, 3)); + ProcessPixelEtc1(5, pixelIndexWord, tableCodeword1, redBaseColorSubBlock1, greenBaseColorSubBlock1, blueBaseColorSubBlock1, pixelBuffer.Slice(15, 3)); - ProcessPixelEtc1(12, pixelIndexWord, tableCodeword1, redBaseColorSubBlock1, greenBaseColorSubBlock1, blueBaseColorSubBlock1, pixelBuffer.Slice(36, 3)); - ProcessPixelEtc1(13, pixelIndexWord, tableCodeword1, redBaseColorSubBlock1, greenBaseColorSubBlock1, blueBaseColorSubBlock1, pixelBuffer.Slice(39, 3)); + ProcessPixelEtc1(6, pixelIndexWord, tableCodeword2, redBaseColorSubBlock2, greenBaseColorSubBlock2, blueBaseColorSubBlock2, pixelBuffer.Slice(18, 3)); + ProcessPixelEtc1(7, pixelIndexWord, tableCodeword2, redBaseColorSubBlock2, greenBaseColorSubBlock2, blueBaseColorSubBlock2, pixelBuffer.Slice(21, 3)); - ProcessPixelEtc1(14, pixelIndexWord, tableCodeword2, redBaseColorSubBlock2, greenBaseColorSubBlock2, blueBaseColorSubBlock2, pixelBuffer.Slice(42, 3)); - ProcessPixelEtc1(15, pixelIndexWord, tableCodeword2, redBaseColorSubBlock2, greenBaseColorSubBlock2, blueBaseColorSubBlock2, pixelBuffer.Slice(45, 3)); - } + ProcessPixelEtc1(8, pixelIndexWord, tableCodeword1, redBaseColorSubBlock1, greenBaseColorSubBlock1, blueBaseColorSubBlock1, pixelBuffer.Slice(24, 3)); + ProcessPixelEtc1(9, pixelIndexWord, tableCodeword1, redBaseColorSubBlock1, greenBaseColorSubBlock1, blueBaseColorSubBlock1, pixelBuffer.Slice(27, 3)); - public static void DecodeEtc2Block(Span payload, Span decodedPixelSpan) - { - // Figure out the mode. - if ((payload[3] & 2) == 0) - { - // Individual mode. - DecodeEtc1Block(payload, decodedPixelSpan); - return; - } + ProcessPixelEtc1(10, pixelIndexWord, tableCodeword2, redBaseColorSubBlock2, greenBaseColorSubBlock2, blueBaseColorSubBlock2, pixelBuffer.Slice(30, 3)); + ProcessPixelEtc1(11, pixelIndexWord, tableCodeword2, redBaseColorSubBlock2, greenBaseColorSubBlock2, blueBaseColorSubBlock2, pixelBuffer.Slice(33, 3)); - int r = payload[0] & 0xF8; - r += Complement3BitShifted(payload[0] & 7); - int g = payload[1] & 0xF8; - g += Complement3BitShifted(payload[1] & 7); - int b = payload[2] & 0xF8; - b += Complement3BitShifted(payload[2] & 7); + ProcessPixelEtc1(12, pixelIndexWord, tableCodeword1, redBaseColorSubBlock1, greenBaseColorSubBlock1, blueBaseColorSubBlock1, pixelBuffer.Slice(36, 3)); + ProcessPixelEtc1(13, pixelIndexWord, tableCodeword1, redBaseColorSubBlock1, greenBaseColorSubBlock1, blueBaseColorSubBlock1, pixelBuffer.Slice(39, 3)); - decodedPixelSpan.Clear(); - if ((r & 0xFF07) != 0) - { - ProcessBlockEtc2TMode(payload, decodedPixelSpan); - return; - } + ProcessPixelEtc1(14, pixelIndexWord, tableCodeword2, redBaseColorSubBlock2, greenBaseColorSubBlock2, blueBaseColorSubBlock2, pixelBuffer.Slice(42, 3)); + ProcessPixelEtc1(15, pixelIndexWord, tableCodeword2, redBaseColorSubBlock2, greenBaseColorSubBlock2, blueBaseColorSubBlock2, pixelBuffer.Slice(45, 3)); + } - if ((g & 0xFF07) != 0) - { - ProcessBlockEtc2HMode(payload, decodedPixelSpan); - return; - } + public static void DecodeEtc2Block(Span payload, Span decodedPixelSpan) + { + // Figure out the mode. + if ((payload[3] & 2) == 0) + { + // Individual mode. + DecodeEtc1Block(payload, decodedPixelSpan); + return; + } - if ((b & 0xFF07) != 0) - { - // Planar mode. - ProcessBlockEtc2PlanarMode(payload, decodedPixelSpan); - return; - } + int r = payload[0] & 0xF8; + r += Complement3BitShifted(payload[0] & 7); + int g = payload[1] & 0xF8; + g += Complement3BitShifted(payload[1] & 7); + int b = payload[2] & 0xF8; + b += Complement3BitShifted(payload[2] & 7); - // Differential mode. - DecodeEtc1Block(payload, decodedPixelSpan); + decodedPixelSpan.Clear(); + if ((r & 0xFF07) != 0) + { + ProcessBlockEtc2TMode(payload, decodedPixelSpan); + return; } - private static void ProcessBlockEtc2PlanarMode(Span payload, Span decodedPixelSpan) + if ((g & 0xFF07) != 0) { - // Each color O, H and V is in 6-7-6 format. - int ro = (payload[0] & 0x7E) >> 1; - int go = ((payload[0] & 0x1) << 6) | ((payload[1] & 0x7E) >> 1); - int bo = ((payload[1] & 0x1) << 5) | (payload[2] & 0x18) | ((payload[2] & 0x03) << 1) | ((payload[3] & 0x80) >> 7); - int rh = ((payload[3] & 0x7C) >> 1) | (payload[3] & 0x1); - int gh = (payload[4] & 0xFE) >> 1; - int bh = ((payload[4] & 0x1) << 5) | ((payload[5] & 0xF8) >> 3); - int rv = ((payload[5] & 0x7) << 3) | ((payload[6] & 0xE0) >> 5); - int gv = ((payload[6] & 0x1F) << 2) | ((payload[7] & 0xC0) >> 6); - int bv = payload[7] & 0x3F; - - // Replicate bits. - ro = (ro << 2) | ((ro & 0x30) >> 4); - go = (go << 1) | ((go & 0x40) >> 6); - bo = (bo << 2) | ((bo & 0x30) >> 4); - rh = (rh << 2) | ((rh & 0x30) >> 4); - gh = (gh << 1) | ((gh & 0x40) >> 6); - bh = (bh << 2) | ((bh & 0x30) >> 4); - rv = (rv << 2) | ((rv & 0x30) >> 4); - gv = (gv << 1) | ((gv & 0x40) >> 6); - bv = (bv << 2) | ((bv & 0x30) >> 4); - - for (int y = 0; y < 4; y++) - { - for (int x = 0; x < 4; x++) - { - byte r = (byte)Helper.Clamp(((x * (rh - ro)) + (y * (rv - ro)) + (4 * ro) + 2) >> 2, 0, 255); - byte g = (byte)Helper.Clamp(((x * (gh - go)) + (y * (gv - go)) + (4 * go) + 2) >> 2, 0, 255); - byte b = (byte)Helper.Clamp(((x * (bh - bo)) + (y * (bv - bo)) + (4 * bo) + 2) >> 2, 0, 255); - int pixelIdx = ((y * 4) + x) * 3; - decodedPixelSpan[pixelIdx] = r; - decodedPixelSpan[pixelIdx + 1] = g; - decodedPixelSpan[pixelIdx + 2] = b; - } - } + ProcessBlockEtc2HMode(payload, decodedPixelSpan); + return; } - private static void ProcessBlockEtc2TMode(Span payload, Span decodedPixelSpan) + if ((b & 0xFF07) != 0) { - int[] paintColorR = new int[4]; - int[] paintColorG = new int[4]; - int[] paintColorB = new int[4]; - - int c0r = ((payload[0] & 0x18) >> 1) | (payload[0] & 0x3); - c0r |= c0r << 4; - int c0g = payload[1] & 0xF0; - c0g |= c0g >> 4; - int c0b = payload[1] & 0x0F; - c0b |= c0b << 4; - int c1r = payload[2] & 0xF0; - c1r |= c1r >> 4; - int c1g = payload[2] & 0x0F; - c1g |= c1g << 4; - int c1b = payload[3] & 0xF0; - c1b |= c1b >> 4; - - int distance = Etc2DistanceTable[((payload[3] & 0x0C) >> 1) | (payload[3] & 0x1)]; - paintColorR[0] = c0r; - paintColorG[0] = c0g; - paintColorB[0] = c0b; - paintColorR[2] = c1r; - paintColorG[2] = c1g; - paintColorB[2] = c1b; - paintColorR[1] = Helper.Clamp(c1r + distance, 0, 255); - paintColorG[1] = Helper.Clamp(c1g + distance, 0, 255); - paintColorB[1] = Helper.Clamp(c1b + distance, 0, 255); - paintColorR[3] = Helper.Clamp(c1r - distance, 0, 255); - paintColorG[3] = Helper.Clamp(c1g - distance, 0, 255); - paintColorB[3] = Helper.Clamp(c1b - distance, 0, 255); - - uint pixel_index_word = (uint)((payload[4] << 24) | (payload[5] << 16) | (payload[6] << 8) | payload[7]); - int decodedPixelIdx = 0; - for (int i = 0; i < 16; i++) - { - uint pixel_index = (uint)(((pixel_index_word & (1 << i)) >> i) | ((pixel_index_word & (0x10000 << i)) >> (16 + i - 1))); - int r = paintColorR[pixel_index]; - int g = paintColorG[pixel_index]; - int b = paintColorB[pixel_index]; - decodedPixelSpan[decodedPixelIdx++] = (byte)r; - decodedPixelSpan[decodedPixelIdx++] = (byte)g; - decodedPixelSpan[decodedPixelIdx++] = (byte)b; - } + // Planar mode. + ProcessBlockEtc2PlanarMode(payload, decodedPixelSpan); + return; } - private static void ProcessBlockEtc2HMode(Span payload, Span decodedPixelSpan) + // Differential mode. + DecodeEtc1Block(payload, decodedPixelSpan); + } + + private static void ProcessBlockEtc2PlanarMode(Span payload, Span decodedPixelSpan) + { + // Each color O, H and V is in 6-7-6 format. + int ro = (payload[0] & 0x7E) >> 1; + int go = ((payload[0] & 0x1) << 6) | ((payload[1] & 0x7E) >> 1); + int bo = ((payload[1] & 0x1) << 5) | (payload[2] & 0x18) | ((payload[2] & 0x03) << 1) | ((payload[3] & 0x80) >> 7); + int rh = ((payload[3] & 0x7C) >> 1) | (payload[3] & 0x1); + int gh = (payload[4] & 0xFE) >> 1; + int bh = ((payload[4] & 0x1) << 5) | ((payload[5] & 0xF8) >> 3); + int rv = ((payload[5] & 0x7) << 3) | ((payload[6] & 0xE0) >> 5); + int gv = ((payload[6] & 0x1F) << 2) | ((payload[7] & 0xC0) >> 6); + int bv = payload[7] & 0x3F; + + // Replicate bits. + ro = (ro << 2) | ((ro & 0x30) >> 4); + go = (go << 1) | ((go & 0x40) >> 6); + bo = (bo << 2) | ((bo & 0x30) >> 4); + rh = (rh << 2) | ((rh & 0x30) >> 4); + gh = (gh << 1) | ((gh & 0x40) >> 6); + bh = (bh << 2) | ((bh & 0x30) >> 4); + rv = (rv << 2) | ((rv & 0x30) >> 4); + gv = (gv << 1) | ((gv & 0x40) >> 6); + bv = (bv << 2) | ((bv & 0x30) >> 4); + + for (int y = 0; y < 4; y++) { - int[] paintColorR = new int[4]; - int[] paintColorG = new int[4]; - int[] paintColorB = new int[4]; - - int c0r = (payload[0] & 0x78) >> 3; - c0r |= c0r << 4; - int c0g = ((payload[0] & 0x07) << 1) | ((payload[1] & 0x10) >> 4); - c0g |= c0g << 4; - int c0b = (payload[1] & 0x08) | ((payload[1] & 0x03) << 1) | ((payload[2] & 0x80) >> 7); - c0b |= c0b << 4; - int c1r = (payload[2] & 0x78) >> 3; - c1r |= c1r << 4; - int c1g = ((payload[2] & 0x07) << 1) | ((payload[3] & 0x80) >> 7); - c1g |= c1g << 4; - int c1b = (payload[3] & 0x78) >> 3; - c1b |= c1b << 4; - - int baseColor0Value = (c0r << 16) + (c0g << 8) + c0b; - int baseColor1Value = (c1r << 16) + (c1g << 8) + c1b; - int bit = baseColor0Value >= baseColor1Value ? 1 : 0; - - int distance = Etc2DistanceTable[(payload[3] & 0x04) | ((payload[3] & 0x01) << 1) | bit]; - paintColorR[0] = Helper.Clamp(c0r + distance, 0, 255); - paintColorG[0] = Helper.Clamp(c0g + distance, 0, 255); - paintColorB[0] = Helper.Clamp(c0b + distance, 0, 255); - paintColorR[1] = Helper.Clamp(c0r - distance, 0, 255); - paintColorG[1] = Helper.Clamp(c0g - distance, 0, 255); - paintColorB[1] = Helper.Clamp(c0b - distance, 0, 255); - paintColorR[2] = Helper.Clamp(c1r + distance, 0, 255); - paintColorG[2] = Helper.Clamp(c1g + distance, 0, 255); - paintColorB[2] = Helper.Clamp(c1b + distance, 0, 255); - paintColorR[3] = Helper.Clamp(c1r - distance, 0, 255); - paintColorG[3] = Helper.Clamp(c1g - distance, 0, 255); - paintColorB[3] = Helper.Clamp(c1b - distance, 0, 255); - - uint pixel_index_word = (uint)((payload[4] << 24) | (payload[5] << 16) | (payload[6] << 8) | payload[7]); - int decodedPixelIdx = 0; - for (int i = 0; i < 16; i++) + for (int x = 0; x < 4; x++) { - uint pixel_index = (uint)(((pixel_index_word & (1 << i)) >> i) | ((pixel_index_word & (0x10000 << i)) >> (16 + i - 1))); - int r = paintColorR[pixel_index]; - int g = paintColorG[pixel_index]; - int b = paintColorB[pixel_index]; - decodedPixelSpan[decodedPixelIdx++] = (byte)r; - decodedPixelSpan[decodedPixelIdx++] = (byte)g; - decodedPixelSpan[decodedPixelIdx++] = (byte)b; + byte r = (byte)Helper.Clamp(((x * (rh - ro)) + (y * (rv - ro)) + (4 * ro) + 2) >> 2, 0, 255); + byte g = (byte)Helper.Clamp(((x * (gh - go)) + (y * (gv - go)) + (4 * go) + 2) >> 2, 0, 255); + byte b = (byte)Helper.Clamp(((x * (bh - bo)) + (y * (bv - bo)) + (4 * bo) + 2) >> 2, 0, 255); + int pixelIdx = ((y * 4) + x) * 3; + decodedPixelSpan[pixelIdx] = r; + decodedPixelSpan[pixelIdx + 1] = g; + decodedPixelSpan[pixelIdx + 2] = b; } } + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int FiveToEightBit(int color) + private static void ProcessBlockEtc2TMode(Span payload, Span decodedPixelSpan) + { + int[] paintColorR = new int[4]; + int[] paintColorG = new int[4]; + int[] paintColorB = new int[4]; + + int c0r = ((payload[0] & 0x18) >> 1) | (payload[0] & 0x3); + c0r |= c0r << 4; + int c0g = payload[1] & 0xF0; + c0g |= c0g >> 4; + int c0b = payload[1] & 0x0F; + c0b |= c0b << 4; + int c1r = payload[2] & 0xF0; + c1r |= c1r >> 4; + int c1g = payload[2] & 0x0F; + c1g |= c1g << 4; + int c1b = payload[3] & 0xF0; + c1b |= c1b >> 4; + + int distance = Etc2DistanceTable[((payload[3] & 0x0C) >> 1) | (payload[3] & 0x1)]; + paintColorR[0] = c0r; + paintColorG[0] = c0g; + paintColorB[0] = c0b; + paintColorR[2] = c1r; + paintColorG[2] = c1g; + paintColorB[2] = c1b; + paintColorR[1] = Helper.Clamp(c1r + distance, 0, 255); + paintColorG[1] = Helper.Clamp(c1g + distance, 0, 255); + paintColorB[1] = Helper.Clamp(c1b + distance, 0, 255); + paintColorR[3] = Helper.Clamp(c1r - distance, 0, 255); + paintColorG[3] = Helper.Clamp(c1g - distance, 0, 255); + paintColorB[3] = Helper.Clamp(c1b - distance, 0, 255); + + uint pixel_index_word = (uint)((payload[4] << 24) | (payload[5] << 16) | (payload[6] << 8) | payload[7]); + int decodedPixelIdx = 0; + for (int i = 0; i < 16; i++) { - int c0r = color | ((color & 0xE0) >> 5); - return c0r; + uint pixel_index = (uint)(((pixel_index_word & (1 << i)) >> i) | ((pixel_index_word & (0x10000 << i)) >> (16 + i - 1))); + int r = paintColorR[pixel_index]; + int g = paintColorG[pixel_index]; + int b = paintColorB[pixel_index]; + decodedPixelSpan[decodedPixelIdx++] = (byte)r; + decodedPixelSpan[decodedPixelIdx++] = (byte)g; + decodedPixelSpan[decodedPixelIdx++] = (byte)b; } + } - /// - /// This function calculates the 3-bit complement value in the range -4 to 3 of a three bit representation. - /// The result is arithmetically shifted 3 places to the left before returning. - /// - /// The value. - /// 3-bit complement value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int Complement3BitShifted(int x) => Complement3BitShiftedTable[x]; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void ProcessPixelEtc1(int i, uint pixelIndexWord, uint tableCodeword, byte redBaseColorSubBlock, byte greenBaseColorSubBlock, byte blueBaseColorSubBlock, Span pixelBuffer) + private static void ProcessBlockEtc2HMode(Span payload, Span decodedPixelSpan) + { + int[] paintColorR = new int[4]; + int[] paintColorG = new int[4]; + int[] paintColorB = new int[4]; + + int c0r = (payload[0] & 0x78) >> 3; + c0r |= c0r << 4; + int c0g = ((payload[0] & 0x07) << 1) | ((payload[1] & 0x10) >> 4); + c0g |= c0g << 4; + int c0b = (payload[1] & 0x08) | ((payload[1] & 0x03) << 1) | ((payload[2] & 0x80) >> 7); + c0b |= c0b << 4; + int c1r = (payload[2] & 0x78) >> 3; + c1r |= c1r << 4; + int c1g = ((payload[2] & 0x07) << 1) | ((payload[3] & 0x80) >> 7); + c1g |= c1g << 4; + int c1b = (payload[3] & 0x78) >> 3; + c1b |= c1b << 4; + + int baseColor0Value = (c0r << 16) + (c0g << 8) + c0b; + int baseColor1Value = (c1r << 16) + (c1g << 8) + c1b; + int bit = baseColor0Value >= baseColor1Value ? 1 : 0; + + int distance = Etc2DistanceTable[(payload[3] & 0x04) | ((payload[3] & 0x01) << 1) | bit]; + paintColorR[0] = Helper.Clamp(c0r + distance, 0, 255); + paintColorG[0] = Helper.Clamp(c0g + distance, 0, 255); + paintColorB[0] = Helper.Clamp(c0b + distance, 0, 255); + paintColorR[1] = Helper.Clamp(c0r - distance, 0, 255); + paintColorG[1] = Helper.Clamp(c0g - distance, 0, 255); + paintColorB[1] = Helper.Clamp(c0b - distance, 0, 255); + paintColorR[2] = Helper.Clamp(c1r + distance, 0, 255); + paintColorG[2] = Helper.Clamp(c1g + distance, 0, 255); + paintColorB[2] = Helper.Clamp(c1b + distance, 0, 255); + paintColorR[3] = Helper.Clamp(c1r - distance, 0, 255); + paintColorG[3] = Helper.Clamp(c1g - distance, 0, 255); + paintColorB[3] = Helper.Clamp(c1b - distance, 0, 255); + + uint pixel_index_word = (uint)((payload[4] << 24) | (payload[5] << 16) | (payload[6] << 8) | payload[7]); + int decodedPixelIdx = 0; + for (int i = 0; i < 16; i++) { - long pixelIndex = ((pixelIndexWord & (1 << i)) >> i) | ((pixelIndexWord & (0x10000 << i)) >> (16 + i - 1)); - int modifier = ModifierTable[tableCodeword, pixelIndex]; - byte red = (byte)Helper.Clamp(0, redBaseColorSubBlock + modifier, 255); - byte green = (byte)Helper.Clamp(0, greenBaseColorSubBlock + modifier, 255); - byte blue = (byte)Helper.Clamp(0, blueBaseColorSubBlock + modifier, 255); - - pixelBuffer[0] = red; - pixelBuffer[1] = green; - pixelBuffer[2] = blue; + uint pixel_index = (uint)(((pixel_index_word & (1 << i)) >> i) | ((pixel_index_word & (0x10000 << i)) >> (16 + i - 1))); + int r = paintColorR[pixel_index]; + int g = paintColorG[pixel_index]; + int b = paintColorB[pixel_index]; + decodedPixelSpan[decodedPixelIdx++] = (byte)r; + decodedPixelSpan[decodedPixelIdx++] = (byte)g; + decodedPixelSpan[decodedPixelIdx++] = (byte)b; } } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int FiveToEightBit(int color) + { + int c0r = color | ((color & 0xE0) >> 5); + return c0r; + } + + /// + /// This function calculates the 3-bit complement value in the range -4 to 3 of a three bit representation. + /// The result is arithmetically shifted 3 places to the left before returning. + /// + /// The value. + /// 3-bit complement value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int Complement3BitShifted(int x) => Complement3BitShiftedTable[x]; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void ProcessPixelEtc1(int i, uint pixelIndexWord, uint tableCodeword, byte redBaseColorSubBlock, byte greenBaseColorSubBlock, byte blueBaseColorSubBlock, Span pixelBuffer) + { + long pixelIndex = ((pixelIndexWord & (1 << i)) >> i) | ((pixelIndexWord & (0x10000 << i)) >> (16 + i - 1)); + int modifier = ModifierTable[tableCodeword, pixelIndex]; + byte red = (byte)Helper.Clamp(0, redBaseColorSubBlock + modifier, 255); + byte green = (byte)Helper.Clamp(0, greenBaseColorSubBlock + modifier, 255); + byte blue = (byte)Helper.Clamp(0, blueBaseColorSubBlock + modifier, 255); + + pixelBuffer[0] = red; + pixelBuffer[1] = green; + pixelBuffer[2] = blue; + } } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Fp32.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Fp32.cs index 2e5d5538..001e61c6 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Fp32.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Fp32.cs @@ -1,36 +1,35 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding -{ - /// - /// Texture for pixel data for single channel 32 bit float values. - /// - internal struct Fp32 : IBlock - { - /// - public int BitsPerPixel => 32; +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; - /// - public byte PixelDepthBytes => 4; +/// +/// Texture for pixel data for single channel 32 bit float values. +/// +internal struct Fp32 : IBlock +{ + /// + public readonly int BitsPerPixel => 32; - /// - public byte DivSize => 1; + /// + public readonly byte PixelDepthBytes => 4; - /// - public byte CompressedBytesPerBlock => 4; + /// + public readonly byte DivSize => 1; - /// - public bool Compressed => false; + /// + public readonly byte CompressedBytesPerBlock => 4; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public readonly bool Compressed => false; - /// - public byte[] Decompress(byte[] blockData, int width, int height) => blockData; + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); } + + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) => blockData; } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Grgb32.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Grgb32.cs index bf02c556..5da9cc85 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Grgb32.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Grgb32.cs @@ -1,66 +1,64 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Runtime.InteropServices; -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; + +/// +/// Texture for the pixel format G8R8G8B8. +/// +internal struct Grgb32 : IBlock { - /// - /// Texture for the pixel format G8R8G8B8. - /// - internal struct Grgb32 : IBlock - { - /// - public int BitsPerPixel => 32; + /// + public readonly int BitsPerPixel => 32; - /// - public byte PixelDepthBytes => 3; + /// + public readonly byte PixelDepthBytes => 3; - /// - public byte DivSize => 1; + /// + public readonly byte DivSize => 1; - /// - public byte CompressedBytesPerBlock => 2; + /// + public readonly byte CompressedBytesPerBlock => 2; - /// - public bool Compressed => false; + /// + public readonly bool Compressed => false; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); + } - /// - public byte[] Decompress(byte[] blockData, int width, int height) + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) + { + int totalPixels = width * height; + byte[] decompressed = new byte[totalPixels * 3]; + Span rgb24Span = MemoryMarshal.Cast(decompressed); + + ImageSharp.PixelFormats.Rgb24 pixel = default(ImageSharp.PixelFormats.Rgb24); + int pixelIdx = 0; + for (int i = 0; i < blockData.Length; i += 4) { - int totalPixels = width * height; - byte[] decompressed = new byte[totalPixels * 3]; - Span rgb24Span = MemoryMarshal.Cast(decompressed); + byte g0 = blockData[i]; + byte r = blockData[i + 1]; + byte g1 = blockData[i + 2]; + byte b = blockData[i + 3]; - var pixel = default(ImageSharp.PixelFormats.Rgb24); - int pixelIdx = 0; - for (int i = 0; i < blockData.Length; i += 4) + pixel.FromRgb24(new ImageSharp.PixelFormats.Rgb24(r, g0, b)); + rgb24Span[pixelIdx++] = pixel; + if (pixelIdx >= totalPixels) { - byte g0 = blockData[i]; - byte r = blockData[i + 1]; - byte g1 = blockData[i + 2]; - byte b = blockData[i + 3]; - - pixel.FromRgb24(new ImageSharp.PixelFormats.Rgb24(r, g0, b)); - rgb24Span[pixelIdx++] = pixel; - if (pixelIdx >= totalPixels) - { - break; - } - - pixel.FromRgb24(new ImageSharp.PixelFormats.Rgb24(r, g1, b)); - rgb24Span[pixelIdx++] = pixel; + break; } - return decompressed; + pixel.FromRgb24(new ImageSharp.PixelFormats.Rgb24(r, g1, b)); + rgb24Span[pixelIdx++] = pixel; } + + return decompressed; } } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Helper.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Helper.cs index e755cc84..b01c8ae9 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Helper.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Helper.cs @@ -1,100 +1,98 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; + +/// +/// Helper methods for decoding textures. +/// +public static class Helper { /// - /// Helper methods for decoding textures. + /// Delegate to decode a texture. /// - public static class Helper - { - /// - /// Delegate to decode a texture. - /// - /// The stream containing the encoded texture. - /// The data array to decompress the texture data to. - /// Index of the stream. - /// Index of the data. - /// The stride. - /// Stream position after decompression. - public delegate int DecodeDelegate(byte[] stream, byte[] data, int streamIndex, int dataIndex, int stride); - - /// - /// Calculates the number of blocks. - /// - /// The number of pixels. - /// The number of blocks. - public static int CalcBlocks(int pixels) => Math.Max(1, (pixels + 3) / 4); + /// The stream containing the encoded texture. + /// The data array to decompress the texture data to. + /// Index of the stream. + /// Index of the data. + /// The stride. + /// Stream position after decompression. + public delegate int DecodeDelegate(byte[] stream, byte[] data, int streamIndex, int dataIndex, int stride); - /// - /// Decodes a block texture. - /// - /// The type of the block. - /// The memory buffer with the encoded texture data. - /// The width of the texture. - /// The height of the texture. - /// The decode delegate to use. - /// The decoded bytes of the texture. - public static byte[] InMemoryDecode(byte[] memBuffer, int width, int height, DecodeDelegate decode) - where TBlock : struct, IBlock - { - TBlock blockFormat = default; - int heightBlocks = CalcBlocks(height); - int widthBlocks = CalcBlocks(width); - int stridePixels = widthBlocks * blockFormat.DivSize; - int deflatedStrideBytes = stridePixels * blockFormat.PixelDepthBytes; - int dataLen = heightBlocks * blockFormat.DivSize * deflatedStrideBytes; - byte[] data = new byte[dataLen]; - - int dataIndex = 0; - int bIndex = 0; + /// + /// Calculates the number of blocks. + /// + /// The number of pixels. + /// The number of blocks. + public static int CalcBlocks(int pixels) => Math.Max(1, (pixels + 3) / 4); - int stride = stridePixels * blockFormat.PixelDepthBytes; - int blocksPerStride = widthBlocks; - int indexPixelsLeft = heightBlocks * blockFormat.DivSize * stride; + /// + /// Decodes a block texture. + /// + /// The type of the block. + /// The memory buffer with the encoded texture data. + /// The width of the texture. + /// The height of the texture. + /// The decode delegate to use. + /// The decoded bytes of the texture. + public static byte[] InMemoryDecode(byte[] memBuffer, int width, int height, DecodeDelegate decode) + where TBlock : struct, IBlock + { + TBlock blockFormat = default; + int heightBlocks = CalcBlocks(height); + int widthBlocks = CalcBlocks(width); + int stridePixels = widthBlocks * blockFormat.DivSize; + int deflatedStrideBytes = stridePixels * blockFormat.PixelDepthBytes; + int dataLen = heightBlocks * blockFormat.DivSize * deflatedStrideBytes; + byte[] data = new byte[dataLen]; - while (indexPixelsLeft > 0) - { - int origDataIndex = dataIndex; + int dataIndex = 0; + int bIndex = 0; - for (int i = 0; i < blocksPerStride; i++) - { - bIndex = decode.Invoke(memBuffer, data, bIndex, dataIndex, stridePixels); - dataIndex += blockFormat.DivSize * blockFormat.PixelDepthBytes; - } + int stride = stridePixels * blockFormat.PixelDepthBytes; + int blocksPerStride = widthBlocks; + int indexPixelsLeft = heightBlocks * blockFormat.DivSize * stride; - int filled = stride * blockFormat.DivSize; - indexPixelsLeft -= filled; + while (indexPixelsLeft > 0) + { + int origDataIndex = dataIndex; - // Jump down to the block that is exactly (divSize - 1) - // below the current row we are on - dataIndex = origDataIndex + filled; + for (int i = 0; i < blocksPerStride; i++) + { + bIndex = decode.Invoke(memBuffer, data, bIndex, dataIndex, stridePixels); + dataIndex += blockFormat.DivSize * blockFormat.PixelDepthBytes; } - return data; - } + int filled = stride * blockFormat.DivSize; + indexPixelsLeft -= filled; - /// - /// Clamps the specified value between a min and a max value. - /// - /// The value to clamp. - /// The minimum value. - /// The maximum value. - /// The clamped value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint Clamp(uint val, uint min, uint max) => Math.Min(Math.Max(val, min), max); + // Jump down to the block that is exactly (divSize - 1) + // below the current row we are on + dataIndex = origDataIndex + filled; + } - /// - /// Clamps the specified value between a min and a max value. - /// - /// The value to clamp. - /// The minimum value. - /// The maximum value. - /// The clamped value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Clamp(int val, int min, int max) => Math.Min(Math.Max(val, min), max); + return data; } + + /// + /// Clamps the specified value between a min and a max value. + /// + /// The value to clamp. + /// The minimum value. + /// The maximum value. + /// The clamped value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint Clamp(uint val, uint min, uint max) => Math.Min(Math.Max(val, min), max); + + /// + /// Clamps the specified value between a min and a max value. + /// + /// The value to clamp. + /// The minimum value. + /// The maximum value. + /// The clamped value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Clamp(int val, int min, int max) => Math.Min(Math.Max(val, min), max); } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/IBlock.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/IBlock.cs index 99edf380..cc63c174 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/IBlock.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/IBlock.cs @@ -1,54 +1,53 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; + +/// +/// Interface for a block texture. +/// +public interface IBlock { /// - /// Interface for a block texture. + /// Gets the bits per pixel. + /// + int BitsPerPixel { get; } + + /// + /// Gets the pixel depth in bytes. + /// + byte PixelDepthBytes { get; } + + /// + /// Gets the div size. + /// + byte DivSize { get; } + + /// + /// Gets the number of compressed bytes per block. + /// + byte CompressedBytesPerBlock { get; } + + /// + /// Gets a value indicating whether this block is compressed. + /// + bool Compressed { get; } + + /// + /// Gets the image from the (maybe compressed) block data. + /// + /// The block data bytes. + /// The width of the texture. + /// The height of the texture. + /// The Image. + Image GetImage(byte[] blockData, int width, int height); + + /// + /// Gets the decompressed data. /// - public interface IBlock - { - /// - /// Gets the bits per pixel. - /// - int BitsPerPixel { get; } - - /// - /// Gets the pixel depth in bytes. - /// - byte PixelDepthBytes { get; } - - /// - /// Gets the div size. - /// - byte DivSize { get; } - - /// - /// Gets the number of compressed bytes per block. - /// - byte CompressedBytesPerBlock { get; } - - /// - /// Gets a value indicating whether this block is compressed. - /// - bool Compressed { get; } - - /// - /// Gets the image from the (maybe compressed) block data. - /// - /// The block data bytes. - /// The width of the texture. - /// The height of the texture. - /// The Image. - Image GetImage(byte[] blockData, int width, int height); - - /// - /// Gets the decompressed data. - /// - /// The block data bytes. - /// The width of the texture. - /// The height of the texture. - /// The decompressed byte data of the texture. - byte[] Decompress(byte[] blockData, int width, int height); - } + /// The block data bytes. + /// The width of the texture. + /// The height of the texture. + /// The decompressed byte data of the texture. + byte[] Decompress(byte[] blockData, int width, int height); } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/IBlock{TSelf}.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/IBlock{TSelf}.cs index d656dc1f..f3932bac 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/IBlock{TSelf}.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/IBlock{TSelf}.cs @@ -1,14 +1,13 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; + +/// +/// Interface for a block texture. +/// +/// The type of the texture. +public interface IBlock : IBlock + where TSelf : struct, IBlock { - /// - /// Interface for a block texture. - /// - /// The type of the texture. - public interface IBlock : IBlock - where TSelf : struct, IBlock - { - } } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/L16.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/L16.cs index 8951fa6a..0a3dfde0 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/L16.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/L16.cs @@ -1,36 +1,35 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding -{ - /// - /// Texture for pixels with 16 bit luminance values. - /// - internal struct L16 : IBlock - { - /// - public int BitsPerPixel => 16; +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; - /// - public byte PixelDepthBytes => 2; +/// +/// Texture for pixels with 16 bit luminance values. +/// +internal struct L16 : IBlock +{ + /// + public readonly int BitsPerPixel => 16; - /// - public byte DivSize => 1; + /// + public readonly byte PixelDepthBytes => 2; - /// - public byte CompressedBytesPerBlock => 2; + /// + public readonly byte DivSize => 1; - /// - public bool Compressed => false; + /// + public readonly byte CompressedBytesPerBlock => 2; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public readonly bool Compressed => false; - /// - public byte[] Decompress(byte[] blockData, int width, int height) => blockData; + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); } + + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) => blockData; } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/L32.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/L32.cs index 32813f6e..b7733196 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/L32.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/L32.cs @@ -1,36 +1,35 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding -{ - /// - /// Texture for 32 bit luminance pixel data. - /// - internal struct L32 : IBlock - { - /// - public int BitsPerPixel => 32; +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; - /// - public byte PixelDepthBytes => 4; +/// +/// Texture for 32 bit luminance pixel data. +/// +internal struct L32 : IBlock +{ + /// + public readonly int BitsPerPixel => 32; - /// - public byte DivSize => 1; + /// + public readonly byte PixelDepthBytes => 4; - /// - public byte CompressedBytesPerBlock => 4; + /// + public readonly byte DivSize => 1; - /// - public bool Compressed => false; + /// + public readonly byte CompressedBytesPerBlock => 4; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public readonly bool Compressed => false; - /// - public byte[] Decompress(byte[] blockData, int width, int height) => blockData; + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); } + + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) => blockData; } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/L8.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/L8.cs index e43d7d8c..212db6af 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/L8.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/L8.cs @@ -1,36 +1,35 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding -{ - /// - /// Texture for 8 bit luminance pixels. - /// - internal struct L8 : IBlock - { - /// - public int BitsPerPixel => 8; +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; - /// - public byte PixelDepthBytes => 1; +/// +/// Texture for 8 bit luminance pixels. +/// +internal struct L8 : IBlock +{ + /// + public readonly int BitsPerPixel => 8; - /// - public byte DivSize => 1; + /// + public readonly byte PixelDepthBytes => 1; - /// - public byte CompressedBytesPerBlock => 1; + /// + public readonly byte DivSize => 1; - /// - public bool Compressed => false; + /// + public readonly byte CompressedBytesPerBlock => 1; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public readonly bool Compressed => false; - /// - public byte[] Decompress(byte[] blockData, int width, int height) => blockData; + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); } + + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) => blockData; } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/La16.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/La16.cs index 98aa5ff9..c295bfb1 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/La16.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/La16.cs @@ -1,36 +1,35 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding -{ - /// - /// Texture for 8 bit luminance and 8 bit alpha data. - /// - internal struct La16 : IBlock - { - /// - public int BitsPerPixel => 16; +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; - /// - public byte PixelDepthBytes => 2; +/// +/// Texture for 8 bit luminance and 8 bit alpha data. +/// +internal struct La16 : IBlock +{ + /// + public readonly int BitsPerPixel => 16; - /// - public byte DivSize => 1; + /// + public readonly byte PixelDepthBytes => 2; - /// - public byte CompressedBytesPerBlock => 2; + /// + public readonly byte DivSize => 1; - /// - public bool Compressed => false; + /// + public readonly byte CompressedBytesPerBlock => 2; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public readonly bool Compressed => false; - /// - public byte[] Decompress(byte[] blockData, int width, int height) => blockData; + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); } + + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) => blockData; } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/PixelFormats/Constants.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/PixelFormats/Constants.cs index 60fc7929..1655799d 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/PixelFormats/Constants.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/PixelFormats/Constants.cs @@ -2,325 +2,318 @@ // Licensed under the Six Labors Split License. // ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding.PixelFormats +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding.PixelFormats; + +internal static class Constants { - internal static class Constants - { - public const int NumPixelsPerBlock = 16; + public const int NumPixelsPerBlock = 16; #pragma warning disable SA1310 // Field names should not contain underscore - public const int BC6H_MAX_REGIONS = 2; - public const int BC6H_MAX_INDICES = 16; - public const int BC7_MAX_REGIONS = 3; - public const int BC7_MAX_INDICES = 16; + public const int BC6H_MAX_REGIONS = 2; + public const int BC6H_MAX_INDICES = 16; + public const int BC7_MAX_REGIONS = 3; + public const int BC7_MAX_INDICES = 16; - public const ushort F16S_MASK = 0x8000; // f16 sign mask - public const ushort F16EM_MASK = 0x7fff; // f16 exp & mantissa mask - public const ushort F16MAX = 0x7bff; // MAXFLT bit pattern for XMHALF + public const ushort F16S_MASK = 0x8000; // f16 sign mask + public const ushort F16EM_MASK = 0x7fff; // f16 exp & mantissa mask + public const ushort F16MAX = 0x7bff; // MAXFLT bit pattern for XMHALF - public const int BC6H_NUM_CHANNELS = 3; - public const int BC6H_MAX_SHAPES = 32; + public const int BC6H_NUM_CHANNELS = 3; + public const int BC6H_MAX_SHAPES = 32; - public const int BC7_NUM_CHANNELS = 4; - public const int BC7_MAX_SHAPES = 64; + public const int BC7_NUM_CHANNELS = 4; + public const int BC7_MAX_SHAPES = 64; - public const int BC67_WEIGHT_MAX = 64; - public const int BC67_WEIGHT_SHIFT = 6; - public const int BC67_WEIGHT_ROUND = 32; + public const int BC67_WEIGHT_MAX = 64; + public const int BC67_WEIGHT_SHIFT = 6; + public const int BC67_WEIGHT_ROUND = 32; #pragma warning restore SA1310 // Field names should not contain underscore - /* - public const float fEpsilon = 0.25f / 64.0f * (0.25f / 64.0f); - public static readonly float[] pC3 = { 2.0f / 2.0f, 1.0f / 2.0f, 0.0f / 2.0f }; - public static readonly float[] pD3 = { 0.0f / 2.0f, 1.0f / 2.0f, 2.0f / 2.0f }; - public static readonly float[] pC4 = { 3.0f / 3.0f, 2.0f / 3.0f, 1.0f / 3.0f, 0.0f / 3.0f }; - public static readonly float[] pD4 = { 0.0f / 3.0f, 1.0f / 3.0f, 2.0f / 3.0f, 3.0f / 3.0f }; - */ + /* + public const float fEpsilon = 0.25f / 64.0f * (0.25f / 64.0f); + public static readonly float[] pC3 = { 2.0f / 2.0f, 1.0f / 2.0f, 0.0f / 2.0f }; + public static readonly float[] pD3 = { 0.0f / 2.0f, 1.0f / 2.0f, 2.0f / 2.0f }; + public static readonly float[] pC4 = { 3.0f / 3.0f, 2.0f / 3.0f, 1.0f / 3.0f, 0.0f / 3.0f }; + public static readonly float[] pD4 = { 0.0f / 3.0f, 1.0f / 3.0f, 2.0f / 3.0f, 3.0f / 3.0f }; + */ - // Partition, Shape, Pixel (index into 4x4 block) - public static readonly byte[][][] PartitionTable = - { - new[] - { - // 1 Region case has no subsets (all 0) - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } - }, + // Partition, Shape, Pixel (index into 4x4 block) + public static readonly byte[][][] PartitionTable = + [ + [ + // 1 Region case has no subsets (all 0) + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + ], - new[] - { - // BC6H/BC7 Partition Set for 2 Subsets - new byte[] { 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1 }, // Shape 0 - new byte[] { 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1 }, // Shape 1 - new byte[] { 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1 }, // Shape 2 - new byte[] { 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1 }, // Shape 3 - new byte[] { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1 }, // Shape 4 - new byte[] { 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1 }, // Shape 5 - new byte[] { 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1 }, // Shape 6 - new byte[] { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1 }, // Shape 7 - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1 }, // Shape 8 - new byte[] { 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, // Shape 9 - new byte[] { 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1 }, // Shape 10 - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1 }, // Shape 11 - new byte[] { 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, // Shape 12 - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1 }, // Shape 13 - new byte[] { 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, // Shape 14 - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1 }, // Shape 15 - new byte[] { 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1 }, // Shape 16 - new byte[] { 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, // Shape 17 - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0 }, // Shape 18 - new byte[] { 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0 }, // Shape 19 - new byte[] { 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, // Shape 20 - new byte[] { 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0 }, // Shape 21 - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0 }, // Shape 22 - new byte[] { 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1 }, // Shape 23 - new byte[] { 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0 }, // Shape 24 - new byte[] { 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0 }, // Shape 25 - new byte[] { 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0 }, // Shape 26 - new byte[] { 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0 }, // Shape 27 - new byte[] { 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0 }, // Shape 28 - new byte[] { 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 }, // Shape 29 - new byte[] { 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0 }, // Shape 30 - new byte[] { 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0 }, // Shape 31 + [ + // BC6H/BC7 Partition Set for 2 Subsets + [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], // Shape 0 + [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], // Shape 1 + [0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1], // Shape 2 + [0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1], // Shape 3 + [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1], // Shape 4 + [0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1], // Shape 5 + [0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1], // Shape 6 + [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1], // Shape 7 + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1], // Shape 8 + [0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], // Shape 9 + [0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1], // Shape 10 + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1], // Shape 11 + [0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], // Shape 12 + [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1], // Shape 13 + [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], // Shape 14 + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1], // Shape 15 + [0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1], // Shape 16 + [0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0], // Shape 17 + [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0], // Shape 18 + [0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0], // Shape 19 + [0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0], // Shape 20 + [0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0], // Shape 21 + [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0], // Shape 22 + [0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1], // Shape 23 + [0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0], // Shape 24 + [0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0], // Shape 25 + [0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0], // Shape 26 + [0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0], // Shape 27 + [0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0], // Shape 28 + [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0], // Shape 29 + [0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0], // Shape 30 + [0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0], // Shape 31 - // BC7 Partition Set for 2 Subsets (second-half) - new byte[] { 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1 }, // Shape 32 - new byte[] { 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1 }, // Shape 33 - new byte[] { 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0 }, // Shape 34 - new byte[] { 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0 }, // Shape 35 - new byte[] { 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0 }, // Shape 36 - new byte[] { 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0 }, // Shape 37 - new byte[] { 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1 }, // Shape 38 - new byte[] { 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1 }, // Shape 39 - new byte[] { 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0 }, // Shape 40 - new byte[] { 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0 }, // Shape 41 - new byte[] { 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0 }, // Shape 42 - new byte[] { 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0 }, // Shape 43 - new byte[] { 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0 }, // Shape 44 - new byte[] { 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1 }, // Shape 45 - new byte[] { 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1 }, // Shape 46 - new byte[] { 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0 }, // Shape 47 - new byte[] { 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, // Shape 48 - new byte[] { 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, // Shape 49 - new byte[] { 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0 }, // Shape 50 - new byte[] { 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0 }, // Shape 51 - new byte[] { 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1 }, // Shape 52 - new byte[] { 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1 }, // Shape 53 - new byte[] { 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0 }, // Shape 54 - new byte[] { 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0 }, // Shape 55 - new byte[] { 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1 }, // Shape 56 - new byte[] { 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1 }, // Shape 57 - new byte[] { 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1 }, // Shape 58 - new byte[] { 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1 }, // Shape 59 - new byte[] { 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1 }, // Shape 60 - new byte[] { 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 }, // Shape 61 - new byte[] { 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0 }, // Shape 62 - new byte[] { 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1 } // Shape 63 - }, + // BC7 Partition Set for 2 Subsets (second-half) + [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], // Shape 32 + [0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1], // Shape 33 + [0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0], // Shape 34 + [0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0], // Shape 35 + [0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0], // Shape 36 + [0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0], // Shape 37 + [0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1], // Shape 38 + [0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1], // Shape 39 + [0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0], // Shape 40 + [0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0], // Shape 41 + [0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0], // Shape 42 + [0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0], // Shape 43 + [0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0], // Shape 44 + [0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1], // Shape 45 + [0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1], // Shape 46 + [0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0], // Shape 47 + [0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0], // Shape 48 + [0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0], // Shape 49 + [0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0], // Shape 50 + [0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0], // Shape 51 + [0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1], // Shape 52 + [0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1], // Shape 53 + [0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0], // Shape 54 + [0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0], // Shape 55 + [0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1], // Shape 56 + [0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1], // Shape 57 + [0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1], // Shape 58 + [0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1], // Shape 59 + [0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], // Shape 60 + [0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0], // Shape 61 + [0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0], // Shape 62 + [0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1] // Shape 63 + ], - new[] - { - // BC7 Partition Set for 3 Subsets - new byte[] { 0, 0, 1, 1, 0, 0, 1, 1, 0, 2, 2, 1, 2, 2, 2, 2 }, // Shape 0 - new byte[] { 0, 0, 0, 1, 0, 0, 1, 1, 2, 2, 1, 1, 2, 2, 2, 1 }, // Shape 1 - new byte[] { 0, 0, 0, 0, 2, 0, 0, 1, 2, 2, 1, 1, 2, 2, 1, 1 }, // Shape 2 - new byte[] { 0, 2, 2, 2, 0, 0, 2, 2, 0, 0, 1, 1, 0, 1, 1, 1 }, // Shape 3 - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 1, 1, 2, 2 }, // Shape 4 - new byte[] { 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 2, 2, 0, 0, 2, 2 }, // Shape 5 - new byte[] { 0, 0, 2, 2, 0, 0, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1 }, // Shape 6 - new byte[] { 0, 0, 1, 1, 0, 0, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1 }, // Shape 7 - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2 }, // Shape 8 - new byte[] { 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2 }, // Shape 9 - new byte[] { 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2 }, // Shape 10 - new byte[] { 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2 }, // Shape 11 - new byte[] { 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2 }, // Shape 12 - new byte[] { 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 2 }, // Shape 13 - new byte[] { 0, 0, 1, 1, 0, 1, 1, 2, 1, 1, 2, 2, 1, 2, 2, 2 }, // Shape 14 - new byte[] { 0, 0, 1, 1, 2, 0, 0, 1, 2, 2, 0, 0, 2, 2, 2, 0 }, // Shape 15 - new byte[] { 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 2, 1, 1, 2, 2 }, // Shape 16 - new byte[] { 0, 1, 1, 1, 0, 0, 1, 1, 2, 0, 0, 1, 2, 2, 0, 0 }, // Shape 17 - new byte[] { 0, 0, 0, 0, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2 }, // Shape 18 - new byte[] { 0, 0, 2, 2, 0, 0, 2, 2, 0, 0, 2, 2, 1, 1, 1, 1 }, // Shape 19 - new byte[] { 0, 1, 1, 1, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2 }, // Shape 20 - new byte[] { 0, 0, 0, 1, 0, 0, 0, 1, 2, 2, 2, 1, 2, 2, 2, 1 }, // Shape 21 - new byte[] { 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 2, 2, 0, 1, 2, 2 }, // Shape 22 - new byte[] { 0, 0, 0, 0, 1, 1, 0, 0, 2, 2, 1, 0, 2, 2, 1, 0 }, // Shape 23 - new byte[] { 0, 1, 2, 2, 0, 1, 2, 2, 0, 0, 1, 1, 0, 0, 0, 0 }, // Shape 24 - new byte[] { 0, 0, 1, 2, 0, 0, 1, 2, 1, 1, 2, 2, 2, 2, 2, 2 }, // Shape 25 - new byte[] { 0, 1, 1, 0, 1, 2, 2, 1, 1, 2, 2, 1, 0, 1, 1, 0 }, // Shape 26 - new byte[] { 0, 0, 0, 0, 0, 1, 1, 0, 1, 2, 2, 1, 1, 2, 2, 1 }, // Shape 27 - new byte[] { 0, 0, 2, 2, 1, 1, 0, 2, 1, 1, 0, 2, 0, 0, 2, 2 }, // Shape 28 - new byte[] { 0, 1, 1, 0, 0, 1, 1, 0, 2, 0, 0, 2, 2, 2, 2, 2 }, // Shape 29 - new byte[] { 0, 0, 1, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 0, 1, 1 }, // Shape 30 - new byte[] { 0, 0, 0, 0, 2, 0, 0, 0, 2, 2, 1, 1, 2, 2, 2, 1 }, // Shape 31 - new byte[] { 0, 0, 0, 0, 0, 0, 0, 2, 1, 1, 2, 2, 1, 2, 2, 2 }, // Shape 32 - new byte[] { 0, 2, 2, 2, 0, 0, 2, 2, 0, 0, 1, 2, 0, 0, 1, 1 }, // Shape 33 - new byte[] { 0, 0, 1, 1, 0, 0, 1, 2, 0, 0, 2, 2, 0, 2, 2, 2 }, // Shape 34 - new byte[] { 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0 }, // Shape 35 - new byte[] { 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 0, 0, 0, 0 }, // Shape 36 - new byte[] { 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0 }, // Shape 37 - new byte[] { 0, 1, 2, 0, 2, 0, 1, 2, 1, 2, 0, 1, 0, 1, 2, 0 }, // Shape 38 - new byte[] { 0, 0, 1, 1, 2, 2, 0, 0, 1, 1, 2, 2, 0, 0, 1, 1 }, // Shape 39 - new byte[] { 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 0, 0, 0, 0, 1, 1 }, // Shape 40 - new byte[] { 0, 1, 0, 1, 0, 1, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2 }, // Shape 41 - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 2, 1, 2, 1, 2, 1 }, // Shape 42 - new byte[] { 0, 0, 2, 2, 1, 1, 2, 2, 0, 0, 2, 2, 1, 1, 2, 2 }, // Shape 43 - new byte[] { 0, 0, 2, 2, 0, 0, 1, 1, 0, 0, 2, 2, 0, 0, 1, 1 }, // Shape 44 - new byte[] { 0, 2, 2, 0, 1, 2, 2, 1, 0, 2, 2, 0, 1, 2, 2, 1 }, // Shape 45 - new byte[] { 0, 1, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 1 }, // Shape 46 - new byte[] { 0, 0, 0, 0, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1 }, // Shape 47 - new byte[] { 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 2, 2, 2, 2 }, // Shape 48 - new byte[] { 0, 2, 2, 2, 0, 1, 1, 1, 0, 2, 2, 2, 0, 1, 1, 1 }, // Shape 49 - new byte[] { 0, 0, 0, 2, 1, 1, 1, 2, 0, 0, 0, 2, 1, 1, 1, 2 }, // Shape 50 - new byte[] { 0, 0, 0, 0, 2, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2 }, // Shape 51 - new byte[] { 0, 2, 2, 2, 0, 1, 1, 1, 0, 1, 1, 1, 0, 2, 2, 2 }, // Shape 52 - new byte[] { 0, 0, 0, 2, 1, 1, 1, 2, 1, 1, 1, 2, 0, 0, 0, 2 }, // Shape 53 - new byte[] { 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 2, 2, 2, 2 }, // Shape 54 - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 1, 2, 2, 1, 1, 2 }, // Shape 55 - new byte[] { 0, 1, 1, 0, 0, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2 }, // Shape 56 - new byte[] { 0, 0, 2, 2, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 2, 2 }, // Shape 57 - new byte[] { 0, 0, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, 0, 0, 2, 2 }, // Shape 58 - new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 1, 2 }, // Shape 59 - new byte[] { 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 1 }, // Shape 60 - new byte[] { 0, 2, 2, 2, 1, 2, 2, 2, 0, 2, 2, 2, 1, 2, 2, 2 }, // Shape 61 - new byte[] { 0, 1, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }, // Shape 62 - new byte[] { 0, 1, 1, 1, 2, 0, 1, 1, 2, 2, 0, 1, 2, 2, 2, 0 } // Shape 63 - } - }; + [ + // BC7 Partition Set for 3 Subsets + [0, 0, 1, 1, 0, 0, 1, 1, 0, 2, 2, 1, 2, 2, 2, 2], // Shape 0 + [0, 0, 0, 1, 0, 0, 1, 1, 2, 2, 1, 1, 2, 2, 2, 1], // Shape 1 + [0, 0, 0, 0, 2, 0, 0, 1, 2, 2, 1, 1, 2, 2, 1, 1], // Shape 2 + [0, 2, 2, 2, 0, 0, 2, 2, 0, 0, 1, 1, 0, 1, 1, 1], // Shape 3 + [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 1, 1, 2, 2], // Shape 4 + [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 2, 2, 0, 0, 2, 2], // Shape 5 + [0, 0, 2, 2, 0, 0, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1], // Shape 6 + [0, 0, 1, 1, 0, 0, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1], // Shape 7 + [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2], // Shape 8 + [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2], // Shape 9 + [0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2], // Shape 10 + [0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2], // Shape 11 + [0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2], // Shape 12 + [0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 2], // Shape 13 + [0, 0, 1, 1, 0, 1, 1, 2, 1, 1, 2, 2, 1, 2, 2, 2], // Shape 14 + [0, 0, 1, 1, 2, 0, 0, 1, 2, 2, 0, 0, 2, 2, 2, 0], // Shape 15 + [0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 2, 1, 1, 2, 2], // Shape 16 + [0, 1, 1, 1, 0, 0, 1, 1, 2, 0, 0, 1, 2, 2, 0, 0], // Shape 17 + [0, 0, 0, 0, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2], // Shape 18 + [0, 0, 2, 2, 0, 0, 2, 2, 0, 0, 2, 2, 1, 1, 1, 1], // Shape 19 + [0, 1, 1, 1, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2], // Shape 20 + [0, 0, 0, 1, 0, 0, 0, 1, 2, 2, 2, 1, 2, 2, 2, 1], // Shape 21 + [0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 2, 2, 0, 1, 2, 2], // Shape 22 + [0, 0, 0, 0, 1, 1, 0, 0, 2, 2, 1, 0, 2, 2, 1, 0], // Shape 23 + [0, 1, 2, 2, 0, 1, 2, 2, 0, 0, 1, 1, 0, 0, 0, 0], // Shape 24 + [0, 0, 1, 2, 0, 0, 1, 2, 1, 1, 2, 2, 2, 2, 2, 2], // Shape 25 + [0, 1, 1, 0, 1, 2, 2, 1, 1, 2, 2, 1, 0, 1, 1, 0], // Shape 26 + [0, 0, 0, 0, 0, 1, 1, 0, 1, 2, 2, 1, 1, 2, 2, 1], // Shape 27 + [0, 0, 2, 2, 1, 1, 0, 2, 1, 1, 0, 2, 0, 0, 2, 2], // Shape 28 + [0, 1, 1, 0, 0, 1, 1, 0, 2, 0, 0, 2, 2, 2, 2, 2], // Shape 29 + [0, 0, 1, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 0, 1, 1], // Shape 30 + [0, 0, 0, 0, 2, 0, 0, 0, 2, 2, 1, 1, 2, 2, 2, 1], // Shape 31 + [0, 0, 0, 0, 0, 0, 0, 2, 1, 1, 2, 2, 1, 2, 2, 2], // Shape 32 + [0, 2, 2, 2, 0, 0, 2, 2, 0, 0, 1, 2, 0, 0, 1, 1], // Shape 33 + [0, 0, 1, 1, 0, 0, 1, 2, 0, 0, 2, 2, 0, 2, 2, 2], // Shape 34 + [0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0], // Shape 35 + [0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 0, 0, 0, 0], // Shape 36 + [0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0], // Shape 37 + [0, 1, 2, 0, 2, 0, 1, 2, 1, 2, 0, 1, 0, 1, 2, 0], // Shape 38 + [0, 0, 1, 1, 2, 2, 0, 0, 1, 1, 2, 2, 0, 0, 1, 1], // Shape 39 + [0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 0, 0, 0, 0, 1, 1], // Shape 40 + [0, 1, 0, 1, 0, 1, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2], // Shape 41 + [0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 2, 1, 2, 1, 2, 1], // Shape 42 + [0, 0, 2, 2, 1, 1, 2, 2, 0, 0, 2, 2, 1, 1, 2, 2], // Shape 43 + [0, 0, 2, 2, 0, 0, 1, 1, 0, 0, 2, 2, 0, 0, 1, 1], // Shape 44 + [0, 2, 2, 0, 1, 2, 2, 1, 0, 2, 2, 0, 1, 2, 2, 1], // Shape 45 + [0, 1, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 1], // Shape 46 + [0, 0, 0, 0, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1], // Shape 47 + [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 2, 2, 2, 2], // Shape 48 + [0, 2, 2, 2, 0, 1, 1, 1, 0, 2, 2, 2, 0, 1, 1, 1], // Shape 49 + [0, 0, 0, 2, 1, 1, 1, 2, 0, 0, 0, 2, 1, 1, 1, 2], // Shape 50 + [0, 0, 0, 0, 2, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2], // Shape 51 + [0, 2, 2, 2, 0, 1, 1, 1, 0, 1, 1, 1, 0, 2, 2, 2], // Shape 52 + [0, 0, 0, 2, 1, 1, 1, 2, 1, 1, 1, 2, 0, 0, 0, 2], // Shape 53 + [0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 2, 2, 2, 2], // Shape 54 + [0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 1, 2, 2, 1, 1, 2], // Shape 55 + [0, 1, 1, 0, 0, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2], // Shape 56 + [0, 0, 2, 2, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 2, 2], // Shape 57 + [0, 0, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, 0, 0, 2, 2], // Shape 58 + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 1, 2], // Shape 59 + [0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 1], // Shape 60 + [0, 2, 2, 2, 1, 2, 2, 2, 0, 2, 2, 2, 1, 2, 2, 2], // Shape 61 + [0, 1, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], // Shape 62 + [0, 1, 1, 1, 2, 0, 1, 1, 2, 2, 0, 1, 2, 2, 2, 0] // Shape 63 + ] + ]; - // Partition, Shape, Fixup - public static readonly byte[][][] FixUp = - { - new[] - { - // No fix-ups for 1st subset for BC6H or BC7 - new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, - new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, - new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, - new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, - new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, - new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, - new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, - new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, - new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, - new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, - new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, - new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, - new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, - new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, - new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, - new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 }, new byte[] { 0, 0, 0 } - }, + // Partition, Shape, Fixup + public static readonly byte[][][] FixUp = + [ + [ + // No fix-ups for 1st subset for BC6H or BC7 + [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], + [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], + [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], + [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], + [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], + [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], + [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], + [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], + [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], + [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], + [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], + [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], + [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], + [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], + [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], + [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0] + ], - new[] - { - // BC6H/BC7 Partition Set Fixups for 2 Subsets - new byte[] { 0, 15, 0 }, new byte[] { 0, 15, 0 }, new byte[] { 0, 15, 0 }, new byte[] { 0, 15, 0 }, - new byte[] { 0, 15, 0 }, new byte[] { 0, 15, 0 }, new byte[] { 0, 15, 0 }, new byte[] { 0, 15, 0 }, - new byte[] { 0, 15, 0 }, new byte[] { 0, 15, 0 }, new byte[] { 0, 15, 0 }, new byte[] { 0, 15, 0 }, - new byte[] { 0, 15, 0 }, new byte[] { 0, 15, 0 }, new byte[] { 0, 15, 0 }, new byte[] { 0, 15, 0 }, - new byte[] { 0, 15, 0 }, new byte[] { 0, 2, 0 }, new byte[] { 0, 8, 0 }, new byte[] { 0, 2, 0 }, - new byte[] { 0, 2, 0 }, new byte[] { 0, 8, 0 }, new byte[] { 0, 8, 0 }, new byte[] { 0, 15, 0 }, - new byte[] { 0, 2, 0 }, new byte[] { 0, 8, 0 }, new byte[] { 0, 2, 0 }, new byte[] { 0, 2, 0 }, - new byte[] { 0, 8, 0 }, new byte[] { 0, 8, 0 }, new byte[] { 0, 2, 0 }, new byte[] { 0, 2, 0 }, + [ + // BC6H/BC7 Partition Set Fixups for 2 Subsets + [0, 15, 0], [0, 15, 0], [0, 15, 0], [0, 15, 0], + [0, 15, 0], [0, 15, 0], [0, 15, 0], [0, 15, 0], + [0, 15, 0], [0, 15, 0], [0, 15, 0], [0, 15, 0], + [0, 15, 0], [0, 15, 0], [0, 15, 0], [0, 15, 0], + [0, 15, 0], [0, 2, 0], [0, 8, 0], [0, 2, 0], + [0, 2, 0], [0, 8, 0], [0, 8, 0], [0, 15, 0], + [0, 2, 0], [0, 8, 0], [0, 2, 0], [0, 2, 0], + [0, 8, 0], [0, 8, 0], [0, 2, 0], [0, 2, 0], - // BC7 Partition Set Fixups for 2 Subsets (second-half) - new byte[] { 0, 15, 0 }, new byte[] { 0, 15, 0 }, new byte[] { 0, 6, 0 }, new byte[] { 0, 8, 0 }, - new byte[] { 0, 2, 0 }, new byte[] { 0, 8, 0 }, new byte[] { 0, 15, 0 }, new byte[] { 0, 15, 0 }, - new byte[] { 0, 2, 0 }, new byte[] { 0, 8, 0 }, new byte[] { 0, 2, 0 }, new byte[] { 0, 2, 0 }, - new byte[] { 0, 2, 0 }, new byte[] { 0, 15, 0 }, new byte[] { 0, 15, 0 }, new byte[] { 0, 6, 0 }, - new byte[] { 0, 6, 0 }, new byte[] { 0, 2, 0 }, new byte[] { 0, 6, 0 }, new byte[] { 0, 8, 0 }, - new byte[] { 0, 15, 0 }, new byte[] { 0, 15, 0 }, new byte[] { 0, 2, 0 }, new byte[] { 0, 2, 0 }, - new byte[] { 0, 15, 0 }, new byte[] { 0, 15, 0 }, new byte[] { 0, 15, 0 }, new byte[] { 0, 15, 0 }, - new byte[] { 0, 15, 0 }, new byte[] { 0, 2, 0 }, new byte[] { 0, 2, 0 }, new byte[] { 0, 15, 0 } - }, + // BC7 Partition Set Fixups for 2 Subsets (second-half) + [0, 15, 0], [0, 15, 0], [0, 6, 0], [0, 8, 0], + [0, 2, 0], [0, 8, 0], [0, 15, 0], [0, 15, 0], + [0, 2, 0], [0, 8, 0], [0, 2, 0], [0, 2, 0], + [0, 2, 0], [0, 15, 0], [0, 15, 0], [0, 6, 0], + [0, 6, 0], [0, 2, 0], [0, 6, 0], [0, 8, 0], + [0, 15, 0], [0, 15, 0], [0, 2, 0], [0, 2, 0], + [0, 15, 0], [0, 15, 0], [0, 15, 0], [0, 15, 0], + [0, 15, 0], [0, 2, 0], [0, 2, 0], [0, 15, 0] + ], - new[] - { - // BC7 Partition Set Fixups for 3 Subsets - new byte[] { 0, 3, 15 }, new byte[] { 0, 3, 8 }, new byte[] { 0, 15, 8 }, new byte[] { 0, 15, 3 }, - new byte[] { 0, 8, 15 }, new byte[] { 0, 3, 15 }, new byte[] { 0, 15, 3 }, new byte[] { 0, 15, 8 }, - new byte[] { 0, 8, 15 }, new byte[] { 0, 8, 15 }, new byte[] { 0, 6, 15 }, new byte[] { 0, 6, 15 }, - new byte[] { 0, 6, 15 }, new byte[] { 0, 5, 15 }, new byte[] { 0, 3, 15 }, new byte[] { 0, 3, 8 }, - new byte[] { 0, 3, 15 }, new byte[] { 0, 3, 8 }, new byte[] { 0, 8, 15 }, new byte[] { 0, 15, 3 }, - new byte[] { 0, 3, 15 }, new byte[] { 0, 3, 8 }, new byte[] { 0, 6, 15 }, new byte[] { 0, 10, 8 }, - new byte[] { 0, 5, 3 }, new byte[] { 0, 8, 15 }, new byte[] { 0, 8, 6 }, new byte[] { 0, 6, 10 }, - new byte[] { 0, 8, 15 }, new byte[] { 0, 5, 15 }, new byte[] { 0, 15, 10 }, new byte[] { 0, 15, 8 }, - new byte[] { 0, 8, 15 }, new byte[] { 0, 15, 3 }, new byte[] { 0, 3, 15 }, new byte[] { 0, 5, 10 }, - new byte[] { 0, 6, 10 }, new byte[] { 0, 10, 8 }, new byte[] { 0, 8, 9 }, new byte[] { 0, 15, 10 }, - new byte[] { 0, 15, 6 }, new byte[] { 0, 3, 15 }, new byte[] { 0, 15, 8 }, new byte[] { 0, 5, 15 }, - new byte[] { 0, 15, 3 }, new byte[] { 0, 15, 6 }, new byte[] { 0, 15, 6 }, new byte[] { 0, 15, 8 }, - new byte[] { 0, 3, 15 }, new byte[] { 0, 15, 3 }, new byte[] { 0, 5, 15 }, new byte[] { 0, 5, 15 }, - new byte[] { 0, 5, 15 }, new byte[] { 0, 8, 15 }, new byte[] { 0, 5, 15 }, new byte[] { 0, 10, 15 }, - new byte[] { 0, 5, 15 }, new byte[] { 0, 10, 15 }, new byte[] { 0, 8, 15 }, new byte[] { 0, 13, 15 }, - new byte[] { 0, 15, 3 }, new byte[] { 0, 12, 15 }, new byte[] { 0, 3, 15 }, new byte[] { 0, 3, 8 } - } - }; + [ + // BC7 Partition Set Fixups for 3 Subsets + [0, 3, 15], [0, 3, 8], [0, 15, 8], [0, 15, 3], + [0, 8, 15], [0, 3, 15], [0, 15, 3], [0, 15, 8], + [0, 8, 15], [0, 8, 15], [0, 6, 15], [0, 6, 15], + [0, 6, 15], [0, 5, 15], [0, 3, 15], [0, 3, 8], + [0, 3, 15], [0, 3, 8], [0, 8, 15], [0, 15, 3], + [0, 3, 15], [0, 3, 8], [0, 6, 15], [0, 10, 8], + [0, 5, 3], [0, 8, 15], [0, 8, 6], [0, 6, 10], + [0, 8, 15], [0, 5, 15], [0, 15, 10], [0, 15, 8], + [0, 8, 15], [0, 15, 3], [0, 3, 15], [0, 5, 10], + [0, 6, 10], [0, 10, 8], [0, 8, 9], [0, 15, 10], + [0, 15, 6], [0, 3, 15], [0, 15, 8], [0, 5, 15], + [0, 15, 3], [0, 15, 6], [0, 15, 6], [0, 15, 8], + [0, 3, 15], [0, 15, 3], [0, 5, 15], [0, 5, 15], + [0, 5, 15], [0, 8, 15], [0, 5, 15], [0, 10, 15], + [0, 5, 15], [0, 10, 15], [0, 8, 15], [0, 13, 15], + [0, 15, 3], [0, 12, 15], [0, 3, 15], [0, 3, 8] + ] + ]; - public static readonly int[] Weights2 = { 0, 21, 43, 64 }; - public static readonly int[] Weights3 = { 0, 9, 18, 27, 37, 46, 55, 64 }; - public static readonly int[] Weights4 = { 0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64 }; - } + public static readonly int[] Weights2 = [0, 21, 43, 64]; + public static readonly int[] Weights3 = [0, 9, 18, 27, 37, 46, 55, 64]; + public static readonly int[] Weights4 = [0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64]; } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/PixelFormats/Helpers.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/PixelFormats/Helpers.cs index 0b3ce4f6..c765896d 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/PixelFormats/Helpers.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/PixelFormats/Helpers.cs @@ -1,75 +1,72 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding.PixelFormats; -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding.PixelFormats +internal static class Helpers { - internal static class Helpers + public static bool IsFixUpOffset(byte uPartitions, byte uShape, int uOffset) { - public static bool IsFixUpOffset(byte uPartitions, byte uShape, int uOffset) - { - Guard.MustBeLessThan(uPartitions, (byte)3, nameof(uPartitions)); - Guard.MustBeLessThan(uShape, (byte)64, nameof(uShape)); - Guard.MustBeBetweenOrEqualTo(uOffset, 0, 15, nameof(uOffset)); + Guard.MustBeLessThan(uPartitions, (byte)3, nameof(uPartitions)); + Guard.MustBeLessThan(uShape, (byte)64, nameof(uShape)); + Guard.MustBeBetweenOrEqualTo(uOffset, 0, 15, nameof(uOffset)); - for (byte p = 0; p <= uPartitions; p++) + for (byte p = 0; p <= uPartitions; p++) + { + if (uOffset == Constants.FixUp[uPartitions][uShape][p]) { - if (uOffset == Constants.FixUp[uPartitions][uShape][p]) - { - return true; - } + return true; } - - return false; } - public static void TransformInverseSigned(IntEndPntPair[] aEndPts, LdrColorA prec) - { - var wrapMask = new IntColor((1 << prec.R) - 1, (1 << prec.G) - 1, (1 << prec.B) - 1); - aEndPts[0].B += aEndPts[0].A; - aEndPts[0].B &= wrapMask; - aEndPts[1].A += aEndPts[0].A; - aEndPts[1].A &= wrapMask; - aEndPts[1].B += aEndPts[0].A; - aEndPts[1].B &= wrapMask; - aEndPts[0].B.SignExtend(prec); - aEndPts[1].A.SignExtend(prec); - aEndPts[1].B.SignExtend(prec); - } + return false; + } - public static void TransformInverseUnsigned(IntEndPntPair[] aEndPts, LdrColorA prec) - { - var wrapMask = new IntColor((1 << prec.R) - 1, (1 << prec.G) - 1, (1 << prec.B) - 1); - aEndPts[0].B += aEndPts[0].A; - aEndPts[0].B &= wrapMask; - aEndPts[1].A += aEndPts[0].A; - aEndPts[1].A &= wrapMask; - aEndPts[1].B += aEndPts[0].A; - aEndPts[1].B &= wrapMask; - } + public static void TransformInverseSigned(IntEndPntPair[] aEndPts, LdrColorA prec) + { + IntColor wrapMask = new IntColor((1 << prec.R) - 1, (1 << prec.G) - 1, (1 << prec.B) - 1); + aEndPts[0].B += aEndPts[0].A; + aEndPts[0].B &= wrapMask; + aEndPts[1].A += aEndPts[0].A; + aEndPts[1].A &= wrapMask; + aEndPts[1].B += aEndPts[0].A; + aEndPts[1].B &= wrapMask; + aEndPts[0].B.SignExtend(prec); + aEndPts[1].A.SignExtend(prec); + aEndPts[1].B.SignExtend(prec); + } - public static int DivRem(int a, int b, out int result) - { - int div = a / b; - result = a - (div * b); - return div; - } + public static void TransformInverseUnsigned(IntEndPntPair[] aEndPts, LdrColorA prec) + { + IntColor wrapMask = new IntColor((1 << prec.R) - 1, (1 << prec.G) - 1, (1 << prec.B) - 1); + aEndPts[0].B += aEndPts[0].A; + aEndPts[0].B &= wrapMask; + aEndPts[1].A += aEndPts[0].A; + aEndPts[1].A &= wrapMask; + aEndPts[1].B += aEndPts[0].A; + aEndPts[1].B &= wrapMask; + } - // Fill colors where each pixel is 4 bytes (rgba) - public static void FillWithErrorColors(Span pOut, ref int index, int numPixels, byte divSize, int stride) + public static int DivRem(int a, int b, out int result) + { + int div = a / b; + result = a - (div * b); + return div; + } + + // Fill colors where each pixel is 4 bytes (rgba) + public static void FillWithErrorColors(Span pOut, ref int index, int numPixels, byte divSize, int stride) + { + for (int i = 0; i < numPixels; ++i) { - for (int i = 0; i < numPixels; ++i) + pOut[index++] = 0; + pOut[index++] = 0; + pOut[index++] = 0; + pOut[index++] = 255; + DivRem(i + 1, divSize, out int rem); + if (rem == 0) { - pOut[index++] = 0; - pOut[index++] = 0; - pOut[index++] = 0; - pOut[index++] = 255; - DivRem(i + 1, divSize, out int rem); - if (rem == 0) - { - index += 4 * (stride - divSize); - } + index += 4 * (stride - divSize); } } } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/PixelFormats/IntColor.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/PixelFormats/IntColor.cs index 7bceb861..870957f9 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/PixelFormats/IntColor.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/PixelFormats/IntColor.cs @@ -1,94 +1,90 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding.PixelFormats +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding.PixelFormats; + +internal class IntColor { - internal class IntColor + public IntColor() { - public IntColor() - { - } + } - public IntColor(int nr, int ng, int nb) - { - this.R = nr; - this.G = ng; - this.B = nb; - this.Pad = 0; - } + public IntColor(int nr, int ng, int nb) + { + this.R = nr; + this.G = ng; + this.B = nb; + this.Pad = 0; + } - public int R { get; set; } + public int R { get; set; } - public int G { get; set; } + public int G { get; set; } - public int B { get; set; } + public int B { get; set; } - public int Pad { get; } + public int Pad { get; } - public static IntColor operator +(IntColor a, IntColor c) - { - a.R += c.R; - a.G += c.G; - a.B += c.B; - return a; - } + public static IntColor operator +(IntColor a, IntColor c) + { + a.R += c.R; + a.G += c.G; + a.B += c.B; + return a; + } - public static IntColor operator &(IntColor a, IntColor c) - { - a.R &= c.R; - a.G &= c.G; - a.B &= c.B; - return a; - } + public static IntColor operator &(IntColor a, IntColor c) + { + a.R &= c.R; + a.G &= c.G; + a.B &= c.B; + return a; + } - public IntColor SignExtend(LdrColorA prec) - { - this.R = SIGN_EXTEND(this.R, prec.R); - this.G = SIGN_EXTEND(this.G, prec.G); - this.B = SIGN_EXTEND(this.B, prec.B); - return this; - } + public IntColor SignExtend(LdrColorA prec) + { + this.R = SIGN_EXTEND(this.R, prec.R); + this.G = SIGN_EXTEND(this.G, prec.G); + this.B = SIGN_EXTEND(this.B, prec.B); + return this; + } - private static int SIGN_EXTEND(int x, int nb) - { - return ((x & 1 << (nb - 1)) != 0 ? ~0 ^ (1 << nb) - 1 : 0) | x; - } + private static int SIGN_EXTEND(int x, int nb) => ((x & (1 << (nb - 1))) != 0 ? ~0 ^ ((1 << nb) - 1) : 0) | x; - public void ToF16Signed(ushort[] aF16) - { - aF16[0] = Int2F16Signed(this.R); - aF16[1] = Int2F16Signed(this.G); - aF16[2] = Int2F16Signed(this.B); - } + public void ToF16Signed(ushort[] aF16) + { + aF16[0] = Int2F16Signed(this.R); + aF16[1] = Int2F16Signed(this.G); + aF16[2] = Int2F16Signed(this.B); + } - public void ToF16Unsigned(ushort[] aF16) - { - aF16[0] = Int2F16Unsigned(this.R); - aF16[1] = Int2F16Unsigned(this.G); - aF16[2] = Int2F16Unsigned(this.B); - } + public void ToF16Unsigned(ushort[] aF16) + { + aF16[0] = Int2F16Unsigned(this.R); + aF16[1] = Int2F16Unsigned(this.G); + aF16[2] = Int2F16Unsigned(this.B); + } - private static ushort Int2F16Unsigned(int input) - { - Guard.MustBeBetweenOrEqualTo(input, 0, Constants.F16MAX, nameof(input)); + private static ushort Int2F16Unsigned(int input) + { + Guard.MustBeBetweenOrEqualTo(input, 0, Constants.F16MAX, nameof(input)); - ushort res = (ushort)input; + ushort res = (ushort)input; - return res; - } + return res; + } - private static ushort Int2F16Signed(int input) + private static ushort Int2F16Signed(int input) + { + int s = 0; + if (input < 0) { - int s = 0; - if (input < 0) - { - s = Constants.F16S_MASK; - input = -input; - } + s = Constants.F16S_MASK; + input = -input; + } - ushort res = (ushort)(s | input); + ushort res = (ushort)(s | input); - return res; - } + return res; } } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/PixelFormats/IntEndPntPair.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/PixelFormats/IntEndPntPair.cs index 50dd10d2..2dd3ed97 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/PixelFormats/IntEndPntPair.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/PixelFormats/IntEndPntPair.cs @@ -1,17 +1,16 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding.PixelFormats +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding.PixelFormats; + +internal struct IntEndPntPair { - internal struct IntEndPntPair - { - public IntColor A; - public IntColor B; + public IntColor A; + public IntColor B; - public IntEndPntPair(IntColor a, IntColor b) - { - this.A = a; - this.B = b; - } + public IntEndPntPair(IntColor a, IntColor b) + { + this.A = a; + this.B = b; } } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/PixelFormats/LdrColorA.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/PixelFormats/LdrColorA.cs index f7e1ce3f..84823486 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/PixelFormats/LdrColorA.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/PixelFormats/LdrColorA.cs @@ -1,187 +1,179 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Diagnostics; -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding.PixelFormats +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding.PixelFormats; + +internal class LdrColorA { - internal class LdrColorA + /// + /// Initializes a new instance of the class. + /// + public LdrColorA() { - /// - /// Initializes a new instance of the class. - /// - public LdrColorA() - { - } + } - /// - /// Initializes a new instance of the class. - /// - /// The red color value. - /// The green color value. - /// The blue color value. - /// The alpha value. - public LdrColorA(byte r, byte g, byte b, byte a) - { - this.R = r; - this.G = g; - this.B = b; - this.A = a; - } + /// + /// Initializes a new instance of the class. + /// + /// The red color value. + /// The green color value. + /// The blue color value. + /// The alpha value. + public LdrColorA(byte r, byte g, byte b, byte a) + { + this.R = r; + this.G = g; + this.B = b; + this.A = a; + } - /// - /// Gets or sets the red component. - /// - public byte R { get; set; } + /// + /// Gets or sets the red component. + /// + public byte R { get; set; } - /// - /// Gets or sets the green component. - /// - public byte G { get; set; } + /// + /// Gets or sets the green component. + /// + public byte G { get; set; } - /// - /// Gets or sets the blue component. - /// - public byte B { get; set; } + /// + /// Gets or sets the blue component. + /// + public byte B { get; set; } - /// - /// Gets or sets the alpha value. - /// - public byte A { get; set; } + /// + /// Gets or sets the alpha value. + /// + public byte A { get; set; } - public byte this[int uElement] + public byte this[int uElement] + { + get => uElement switch { - get => uElement switch - { - 0 => this.R, - 1 => this.G, - 2 => this.B, - 3 => this.A, - _ => throw new ArgumentOutOfRangeException(nameof(uElement)), - }; - - set + 0 => this.R, + 1 => this.G, + 2 => this.B, + 3 => this.A, + _ => throw new ArgumentOutOfRangeException(nameof(uElement)), + }; + + set + { + switch (uElement) { - switch (uElement) - { - case 0: - this.R = value; - break; - case 1: - this.G = value; - break; - case 2: - this.B = value; - break; - case 3: - this.A = value; - break; - default: - throw new ArgumentOutOfRangeException(nameof(uElement)); - } + case 0: + this.R = value; + break; + case 1: + this.G = value; + break; + case 2: + this.B = value; + break; + case 3: + this.A = value; + break; + default: + throw new ArgumentOutOfRangeException(nameof(uElement)); } } + } - public void SwapRedWithAlpha() - { - byte tmp = this.A; - this.A = this.R; - this.R = tmp; - } + public void SwapRedWithAlpha() + { + (this.R, this.A) = (this.A, this.R); + } - public void SwapBlueWithAlpha() - { - byte tmp = this.A; - this.A = this.B; - this.B = tmp; - } + public void SwapBlueWithAlpha() + { + (this.B, this.A) = (this.A, this.B); + } - public void SwapGreenWithAlpha() - { - byte tmp = this.A; - this.A = this.G; - this.G = tmp; - } + public void SwapGreenWithAlpha() + { + (this.G, this.A) = (this.A, this.G); + } - public static void InterpolateRgb(LdrColorA c0, LdrColorA c1, int wc, int wcprec, LdrColorA outt) - { - DebugGuard.MustBeBetweenOrEqualTo(wcprec, 2, 4, nameof(wcprec)); + public static void InterpolateRgb(LdrColorA c0, LdrColorA c1, int wc, int wcprec, LdrColorA outt) + { + DebugGuard.MustBeBetweenOrEqualTo(wcprec, 2, 4, nameof(wcprec)); - int[] aWeights; - switch (wcprec) + int[] aWeights; + switch (wcprec) + { + case 2: { - case 2: - { - aWeights = Constants.Weights2; - Debug.Assert(wc < 4, "wc is expected to be smaller then 4"); - break; - } - - case 3: - { - aWeights = Constants.Weights3; - Debug.Assert(wc < 8, "wc is expected to be smaller then 8"); - break; - } + aWeights = Constants.Weights2; + Debug.Assert(wc < 4, "wc is expected to be smaller then 4"); + break; + } - case 4: - { - aWeights = Constants.Weights4; - Debug.Assert(wc < 16, "wc is expected to be smaller then 16"); - break; - } + case 3: + { + aWeights = Constants.Weights3; + Debug.Assert(wc < 8, "wc is expected to be smaller then 8"); + break; + } - default: - outt.R = outt.G = outt.B = 0; - return; + case 4: + { + aWeights = Constants.Weights4; + Debug.Assert(wc < 16, "wc is expected to be smaller then 16"); + break; } - outt.R = (byte)(((c0.R * (uint)(Constants.BC67_WEIGHT_MAX - aWeights[wc])) + (c1.R * (uint)aWeights[wc]) + Constants.BC67_WEIGHT_ROUND) >> Constants.BC67_WEIGHT_SHIFT); - outt.G = (byte)(((c0.G * (uint)(Constants.BC67_WEIGHT_MAX - aWeights[wc])) + (c1.G * (uint)aWeights[wc]) + Constants.BC67_WEIGHT_ROUND) >> Constants.BC67_WEIGHT_SHIFT); - outt.B = (byte)(((c0.B * (uint)(Constants.BC67_WEIGHT_MAX - aWeights[wc])) + (c1.B * (uint)aWeights[wc]) + Constants.BC67_WEIGHT_ROUND) >> Constants.BC67_WEIGHT_SHIFT); + default: + outt.R = outt.G = outt.B = 0; + return; } - public static void InterpolateA(LdrColorA c0, LdrColorA c1, int wa, int waprec, LdrColorA outt) - { - DebugGuard.MustBeBetweenOrEqualTo(waprec, 2, 4, nameof(waprec)); + outt.R = (byte)(((c0.R * (uint)(Constants.BC67_WEIGHT_MAX - aWeights[wc])) + (c1.R * (uint)aWeights[wc]) + Constants.BC67_WEIGHT_ROUND) >> Constants.BC67_WEIGHT_SHIFT); + outt.G = (byte)(((c0.G * (uint)(Constants.BC67_WEIGHT_MAX - aWeights[wc])) + (c1.G * (uint)aWeights[wc]) + Constants.BC67_WEIGHT_ROUND) >> Constants.BC67_WEIGHT_SHIFT); + outt.B = (byte)(((c0.B * (uint)(Constants.BC67_WEIGHT_MAX - aWeights[wc])) + (c1.B * (uint)aWeights[wc]) + Constants.BC67_WEIGHT_ROUND) >> Constants.BC67_WEIGHT_SHIFT); + } - int[] aWeights; - switch (waprec) - { - case 2: - { - aWeights = Constants.Weights2; - Debug.Assert(wa < 4, "wc is expected to be smaller then 4"); - break; - } + public static void InterpolateA(LdrColorA c0, LdrColorA c1, int wa, int waprec, LdrColorA outt) + { + DebugGuard.MustBeBetweenOrEqualTo(waprec, 2, 4, nameof(waprec)); - case 3: - { - aWeights = Constants.Weights3; - Debug.Assert(wa < 8, "wc is expected to be smaller then 8"); - break; - } + int[] aWeights; + switch (waprec) + { + case 2: + { + aWeights = Constants.Weights2; + Debug.Assert(wa < 4, "wc is expected to be smaller then 4"); + break; + } - case 4: - { - aWeights = Constants.Weights4; - Debug.Assert(wa < 16, "wc is expected to be smaller then 16"); - break; - } + case 3: + { + aWeights = Constants.Weights3; + Debug.Assert(wa < 8, "wc is expected to be smaller then 8"); + break; + } - default: - outt.A = 0; - return; + case 4: + { + aWeights = Constants.Weights4; + Debug.Assert(wa < 16, "wc is expected to be smaller then 16"); + break; } - outt.A = (byte)(((c0.A * (uint)(Constants.BC67_WEIGHT_MAX - aWeights[wa])) + (c1.A * (uint)aWeights[wa]) + Constants.BC67_WEIGHT_ROUND) >> Constants.BC67_WEIGHT_SHIFT); + default: + outt.A = 0; + return; } - public static void Interpolate(LdrColorA c0, LdrColorA c1, int wc, int wa, int wcprec, int waprec, LdrColorA outt) - { - InterpolateRgb(c0, c1, wc, wcprec, outt); - InterpolateA(c0, c1, wa, waprec, outt); - } + outt.A = (byte)(((c0.A * (uint)(Constants.BC67_WEIGHT_MAX - aWeights[wa])) + (c1.A * (uint)aWeights[wa]) + Constants.BC67_WEIGHT_ROUND) >> Constants.BC67_WEIGHT_SHIFT); + } + + public static void Interpolate(LdrColorA c0, LdrColorA c1, int wc, int wa, int wcprec, int waprec, LdrColorA outt) + { + InterpolateRgb(c0, c1, wc, wcprec, outt); + InterpolateA(c0, c1, wa, waprec, outt); } } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/R16Float.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/R16Float.cs index 6701581e..8a8be7bd 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/R16Float.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/R16Float.cs @@ -1,36 +1,35 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding -{ - /// - /// Texture for data with a single channel red channel as 16 bit float. - /// - internal struct R16Float : IBlock - { - /// - public int BitsPerPixel => 16; +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; - /// - public byte PixelDepthBytes => 2; +/// +/// Texture for data with a single channel red channel as 16 bit float. +/// +internal struct R16Float : IBlock +{ + /// + public readonly int BitsPerPixel => 16; - /// - public byte DivSize => 1; + /// + public readonly byte PixelDepthBytes => 2; - /// - public byte CompressedBytesPerBlock => 2; + /// + public readonly byte DivSize => 1; - /// - public bool Compressed => false; + /// + public readonly byte CompressedBytesPerBlock => 2; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public readonly bool Compressed => false; - /// - public byte[] Decompress(byte[] blockData, int width, int height) => blockData; + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); } + + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) => blockData; } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Rg16.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Rg16.cs index a9747f05..d1ae979c 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Rg16.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Rg16.cs @@ -1,36 +1,35 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding -{ - /// - /// Texture format for pixels which have only the red and green channel and use 8 bit for each. - /// - internal struct Rg16 : IBlock - { - /// - public int BitsPerPixel => 16; +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; - /// - public byte PixelDepthBytes => 2; +/// +/// Texture format for pixels which have only the red and green channel and use 8 bit for each. +/// +internal struct Rg16 : IBlock +{ + /// + public readonly int BitsPerPixel => 16; - /// - public byte DivSize => 1; + /// + public readonly byte PixelDepthBytes => 2; - /// - public byte CompressedBytesPerBlock => 2; + /// + public readonly byte DivSize => 1; - /// - public bool Compressed => false; + /// + public readonly byte CompressedBytesPerBlock => 2; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public readonly bool Compressed => false; - /// - public byte[] Decompress(byte[] blockData, int width, int height) => blockData; + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); } + + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) => blockData; } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Rg32.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Rg32.cs index f3df0a7a..f734f40c 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Rg32.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Rg32.cs @@ -1,36 +1,35 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding -{ - /// - /// Texture format for pixels which have only the red and green channel and use 16 bit for each. - /// - internal struct Rg32 : IBlock - { - /// - public int BitsPerPixel => 32; +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; - /// - public byte PixelDepthBytes => 4; +/// +/// Texture format for pixels which have only the red and green channel and use 16 bit for each. +/// +internal struct Rg32 : IBlock +{ + /// + public readonly int BitsPerPixel => 32; - /// - public byte DivSize => 1; + /// + public readonly byte PixelDepthBytes => 4; - /// - public byte CompressedBytesPerBlock => 4; + /// + public readonly byte DivSize => 1; - /// - public bool Compressed => false; + /// + public readonly byte CompressedBytesPerBlock => 4; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public readonly bool Compressed => false; - /// - public byte[] Decompress(byte[] blockData, int width, int height) => blockData; + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); } + + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) => blockData; } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Rg32Float.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Rg32Float.cs index b8253623..b2044efc 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Rg32Float.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Rg32Float.cs @@ -1,36 +1,35 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding -{ - /// - /// Texture format for pixels which have only the red and green channel and use 16 bit for each as float. - /// - internal struct Rg32Float : IBlock - { - /// - public int BitsPerPixel => 32; +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; - /// - public byte PixelDepthBytes => 4; +/// +/// Texture format for pixels which have only the red and green channel and use 16 bit for each as float. +/// +internal struct Rg32Float : IBlock +{ + /// + public readonly int BitsPerPixel => 32; - /// - public byte DivSize => 1; + /// + public readonly byte PixelDepthBytes => 4; - /// - public byte CompressedBytesPerBlock => 4; + /// + public readonly byte DivSize => 1; - /// - public bool Compressed => false; + /// + public readonly byte CompressedBytesPerBlock => 4; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public readonly bool Compressed => false; - /// - public byte[] Decompress(byte[] blockData, int width, int height) => blockData; + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); } + + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) => blockData; } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Rg64.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Rg64.cs index ef5cacb5..58d83354 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Rg64.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Rg64.cs @@ -1,36 +1,35 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding -{ - /// - /// Texture format for pixels which have only the red and green channel and use 32 bit for each. - /// - internal struct Rg64 : IBlock - { - /// - public int BitsPerPixel => 64; +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; - /// - public byte PixelDepthBytes => 8; +/// +/// Texture format for pixels which have only the red and green channel and use 32 bit for each. +/// +internal struct Rg64 : IBlock +{ + /// + public readonly int BitsPerPixel => 64; - /// - public byte DivSize => 1; + /// + public readonly byte PixelDepthBytes => 8; - /// - public byte CompressedBytesPerBlock => 8; + /// + public readonly byte DivSize => 1; - /// - public bool Compressed => false; + /// + public readonly byte CompressedBytesPerBlock => 8; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public readonly bool Compressed => false; - /// - public byte[] Decompress(byte[] blockData, int width, int height) => blockData; + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); } + + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) => blockData; } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Rg64Float.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Rg64Float.cs index 216a1b98..8a89ba4d 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Rg64Float.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Rg64Float.cs @@ -1,36 +1,35 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding -{ - /// - /// Texture format for pixels which have only the red and green channel and use 32 bit for each as float. - /// - internal struct Rg64Float : IBlock - { - /// - public int BitsPerPixel => 64; +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; - /// - public byte PixelDepthBytes => 8; +/// +/// Texture format for pixels which have only the red and green channel and use 32 bit for each as float. +/// +internal struct Rg64Float : IBlock +{ + /// + public readonly int BitsPerPixel => 64; - /// - public byte DivSize => 1; + /// + public readonly byte PixelDepthBytes => 8; - /// - public byte CompressedBytesPerBlock => 8; + /// + public readonly byte DivSize => 1; - /// - public bool Compressed => false; + /// + public readonly byte CompressedBytesPerBlock => 8; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public readonly bool Compressed => false; - /// - public byte[] Decompress(byte[] blockData, int width, int height) => blockData; + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); } + + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) => blockData; } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Rgb111110Float.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Rgb111110Float.cs index 2eed5248..4e873ec3 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Rgb111110Float.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Rgb111110Float.cs @@ -1,36 +1,35 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding -{ - /// - /// Texture for pixel data with 11 bits for red and green and 10 bits for the blue channel as float. - /// - internal struct Rgb111110Float : IBlock - { - /// - public int BitsPerPixel => 32; +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; - /// - public byte PixelDepthBytes => 4; +/// +/// Texture for pixel data with 11 bits for red and green and 10 bits for the blue channel as float. +/// +internal struct Rgb111110Float : IBlock +{ + /// + public readonly int BitsPerPixel => 32; - /// - public byte DivSize => 1; + /// + public readonly byte PixelDepthBytes => 4; - /// - public byte CompressedBytesPerBlock => 4; + /// + public readonly byte DivSize => 1; - /// - public bool Compressed => false; + /// + public readonly byte CompressedBytesPerBlock => 4; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public readonly bool Compressed => false; - /// - public byte[] Decompress(byte[] blockData, int width, int height) => blockData; + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); } + + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) => blockData; } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Rgb24.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Rgb24.cs index 905cfd88..58dfb50f 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Rgb24.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Rgb24.cs @@ -1,36 +1,35 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding -{ - /// - /// Texture format with pixels which use 8 bits for each channel without an alpha channel (pixel format is Rgb24). - /// - internal struct Rgb24 : IBlock - { - /// - public int BitsPerPixel => 24; +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; - /// - public byte PixelDepthBytes => 3; +/// +/// Texture format with pixels which use 8 bits for each channel without an alpha channel (pixel format is Rgb24). +/// +internal struct Rgb24 : IBlock +{ + /// + public readonly int BitsPerPixel => 24; - /// - public byte DivSize => 1; + /// + public readonly byte PixelDepthBytes => 3; - /// - public byte CompressedBytesPerBlock => 3; + /// + public readonly byte DivSize => 1; - /// - public bool Compressed => false; + /// + public readonly byte CompressedBytesPerBlock => 3; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public readonly bool Compressed => false; - /// - public byte[] Decompress(byte[] blockData, int width, int height) => blockData; + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); } + + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) => blockData; } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Rgb32.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Rgb32.cs index 76f4a805..2651a919 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Rgb32.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Rgb32.cs @@ -1,36 +1,35 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding -{ - /// - /// Texture format with pixels which have one byte for R, G, B channels and an unused alpha channel. - /// - internal struct Rgb32 : IBlock - { - /// - public int BitsPerPixel => 32; +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; - /// - public byte PixelDepthBytes => 4; +/// +/// Texture format with pixels which have one byte for R, G, B channels and an unused alpha channel. +/// +internal struct Rgb32 : IBlock +{ + /// + public readonly int BitsPerPixel => 32; - /// - public byte DivSize => 1; + /// + public readonly byte PixelDepthBytes => 4; - /// - public byte CompressedBytesPerBlock => 4; + /// + public readonly byte DivSize => 1; - /// - public bool Compressed => false; + /// + public readonly byte CompressedBytesPerBlock => 4; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public readonly bool Compressed => false; - /// - public byte[] Decompress(byte[] blockData, int width, int height) => blockData; + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); } + + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) => blockData; } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Rgb48.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Rgb48.cs index 70de61eb..88c340bd 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Rgb48.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Rgb48.cs @@ -1,36 +1,35 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding -{ - /// - /// Texture format with pixels which use 16 bits for each channel without an alpha channel (pixel format is Rgb48). - /// - internal struct Rgb48 : IBlock - { - /// - public int BitsPerPixel => 24; +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; - /// - public byte PixelDepthBytes => 3; +/// +/// Texture format with pixels which use 16 bits for each channel without an alpha channel (pixel format is Rgb48). +/// +internal struct Rgb48 : IBlock +{ + /// + public readonly int BitsPerPixel => 24; - /// - public byte DivSize => 1; + /// + public readonly byte PixelDepthBytes => 3; - /// - public byte CompressedBytesPerBlock => 3; + /// + public readonly byte DivSize => 1; - /// - public bool Compressed => false; + /// + public readonly byte CompressedBytesPerBlock => 3; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public readonly bool Compressed => false; - /// - public byte[] Decompress(byte[] blockData, int width, int height) => blockData; + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); } + + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) => blockData; } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Rgb565.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Rgb565.cs index 7d245e86..51165fcc 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Rgb565.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Rgb565.cs @@ -1,36 +1,35 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding -{ - /// - /// Texture format for pixel data with 5 bit for red and blue and 6 bits for the green color channel. - /// - internal struct Rgb565 : IBlock - { - /// - public int BitsPerPixel => 16; +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; - /// - public byte PixelDepthBytes => 2; +/// +/// Texture format for pixel data with 5 bit for red and blue and 6 bits for the green color channel. +/// +internal struct Rgb565 : IBlock +{ + /// + public readonly int BitsPerPixel => 16; - /// - public byte DivSize => 1; + /// + public readonly byte PixelDepthBytes => 2; - /// - public byte CompressedBytesPerBlock => 2; + /// + public readonly byte DivSize => 1; - /// - public bool Compressed => false; + /// + public readonly byte CompressedBytesPerBlock => 2; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public readonly bool Compressed => false; - /// - public byte[] Decompress(byte[] blockData, int width, int height) => blockData; + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); } + + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) => blockData; } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Rgb96.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Rgb96.cs index 5e3557b5..2c86cafe 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Rgb96.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Rgb96.cs @@ -1,36 +1,35 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding -{ - /// - /// Texture which has 32 bits per color channel without a alpha channel (pixel format R32G32B32). - /// - internal struct Rgb96 : IBlock - { - /// - public int BitsPerPixel => 96; +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; - /// - public byte PixelDepthBytes => 12; +/// +/// Texture which has 32 bits per color channel without a alpha channel (pixel format R32G32B32). +/// +internal struct Rgb96 : IBlock +{ + /// + public readonly int BitsPerPixel => 96; - /// - public byte DivSize => 1; + /// + public readonly byte PixelDepthBytes => 12; - /// - public byte CompressedBytesPerBlock => 12; + /// + public readonly byte DivSize => 1; - /// - public bool Compressed => false; + /// + public readonly byte CompressedBytesPerBlock => 12; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public readonly bool Compressed => false; - /// - public byte[] Decompress(byte[] blockData, int width, int height) => blockData; + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); } + + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) => blockData; } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Rgb96Float.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Rgb96Float.cs index 0260a2f3..7c170ef6 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Rgb96Float.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Rgb96Float.cs @@ -1,36 +1,35 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding -{ - /// - /// Texture format for pixels which use 32 bit float values for the RGB channels. - /// - internal struct Rgb96Float : IBlock - { - /// - public int BitsPerPixel => 96; +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; - /// - public byte PixelDepthBytes => 12; +/// +/// Texture format for pixels which use 32 bit float values for the RGB channels. +/// +internal struct Rgb96Float : IBlock +{ + /// + public readonly int BitsPerPixel => 96; - /// - public byte DivSize => 1; + /// + public readonly byte PixelDepthBytes => 12; - /// - public byte CompressedBytesPerBlock => 12; + /// + public readonly byte DivSize => 1; - /// - public bool Compressed => false; + /// + public readonly byte CompressedBytesPerBlock => 12; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public readonly bool Compressed => false; - /// - public byte[] Decompress(byte[] blockData, int width, int height) => blockData; + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); } + + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) => blockData; } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Rgba1010102.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Rgba1010102.cs index c1fab15c..fc67373c 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Rgba1010102.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Rgba1010102.cs @@ -1,36 +1,35 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding -{ - /// - /// Texture format for textures with 10 the RGB channel and 2 Bits for the alpha channel. - /// - internal struct Rgba1010102 : IBlock - { - /// - public int BitsPerPixel => 32; +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; - /// - public byte PixelDepthBytes => 4; +/// +/// Texture format for textures with 10 the RGB channel and 2 Bits for the alpha channel. +/// +internal struct Rgba1010102 : IBlock +{ + /// + public readonly int BitsPerPixel => 32; - /// - public byte DivSize => 1; + /// + public readonly byte PixelDepthBytes => 4; - /// - public byte CompressedBytesPerBlock => 4; + /// + public readonly byte DivSize => 1; - /// - public bool Compressed => false; + /// + public readonly byte CompressedBytesPerBlock => 4; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public readonly bool Compressed => false; - /// - public byte[] Decompress(byte[] blockData, int width, int height) => blockData; + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); } + + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) => blockData; } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Rgba128.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Rgba128.cs index 06bfe861..b9ddfd94 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Rgba128.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Rgba128.cs @@ -1,36 +1,35 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding -{ - /// - /// Texture which has 32 bits per color channel including the alpha channel. - /// - internal struct Rgba128 : IBlock - { - /// - public int BitsPerPixel => 128; +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; - /// - public byte PixelDepthBytes => 16; +/// +/// Texture which has 32 bits per color channel including the alpha channel. +/// +internal struct Rgba128 : IBlock +{ + /// + public readonly int BitsPerPixel => 128; - /// - public byte DivSize => 1; + /// + public readonly byte PixelDepthBytes => 16; - /// - public byte CompressedBytesPerBlock => 16; + /// + public readonly byte DivSize => 1; - /// - public bool Compressed => false; + /// + public readonly byte CompressedBytesPerBlock => 16; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public readonly bool Compressed => false; - /// - public byte[] Decompress(byte[] blockData, int width, int height) => blockData; + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); } + + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) => blockData; } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Rgba128Float.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Rgba128Float.cs index 2e2329d1..121eceef 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Rgba128Float.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Rgba128Float.cs @@ -1,36 +1,35 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding -{ - /// - /// Texture format for pixels which use 32 bit float values for the RGBA channels. - /// - internal struct Rgba128Float : IBlock - { - /// - public int BitsPerPixel => 128; +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; - /// - public byte PixelDepthBytes => 16; +/// +/// Texture format for pixels which use 32 bit float values for the RGBA channels. +/// +internal struct Rgba128Float : IBlock +{ + /// + public readonly int BitsPerPixel => 128; - /// - public byte DivSize => 1; + /// + public readonly byte PixelDepthBytes => 16; - /// - public byte CompressedBytesPerBlock => 16; + /// + public readonly byte DivSize => 1; - /// - public bool Compressed => false; + /// + public readonly byte CompressedBytesPerBlock => 16; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public readonly bool Compressed => false; - /// - public byte[] Decompress(byte[] blockData, int width, int height) => blockData; + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); } + + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) => blockData; } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Rgba32.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Rgba32.cs index af7859ec..e4dcd993 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Rgba32.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Rgba32.cs @@ -1,36 +1,35 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding -{ - /// - /// Texture which has 8 bits per color channel (uses pixel format Rgba32). - /// - internal struct Rgba32 : IBlock - { - /// - public int BitsPerPixel => 32; +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; - /// - public byte PixelDepthBytes => 4; +/// +/// Texture which has 8 bits per color channel (uses pixel format Rgba32). +/// +internal struct Rgba32 : IBlock +{ + /// + public readonly int BitsPerPixel => 32; - /// - public byte DivSize => 1; + /// + public readonly byte PixelDepthBytes => 4; - /// - public byte CompressedBytesPerBlock => 4; + /// + public readonly byte DivSize => 1; - /// - public bool Compressed => false; + /// + public readonly byte CompressedBytesPerBlock => 4; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public readonly bool Compressed => false; - /// - public byte[] Decompress(byte[] blockData, int width, int height) => blockData; + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); } + + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) => blockData; } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Rgba4444.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Rgba4444.cs index 25a1c4d1..5d16362b 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Rgba4444.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Rgba4444.cs @@ -1,36 +1,35 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding -{ - /// - /// Texture format for pixel data with 4 bit for each color channel (including alpha). - /// - internal struct Rgba4444 : IBlock - { - /// - public int BitsPerPixel => 16; +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; - /// - public byte PixelDepthBytes => 2; +/// +/// Texture format for pixel data with 4 bit for each color channel (including alpha). +/// +internal struct Rgba4444 : IBlock +{ + /// + public readonly int BitsPerPixel => 16; - /// - public byte DivSize => 1; + /// + public readonly byte PixelDepthBytes => 2; - /// - public byte CompressedBytesPerBlock => 2; + /// + public readonly byte DivSize => 1; - /// - public bool Compressed => false; + /// + public readonly byte CompressedBytesPerBlock => 2; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public readonly bool Compressed => false; - /// - public byte[] Decompress(byte[] blockData, int width, int height) => blockData; + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); } + + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) => blockData; } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Rgba5551.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Rgba5551.cs index 8e560232..6a5d7795 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Rgba5551.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Rgba5551.cs @@ -1,36 +1,35 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding -{ - /// - /// Texture format for pixel data with 5 bit for each color channel and 1 bit for the alpha channel. - /// - internal struct Rgba5551 : IBlock - { - /// - public int BitsPerPixel => 16; +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; - /// - public byte PixelDepthBytes => 2; +/// +/// Texture format for pixel data with 5 bit for each color channel and 1 bit for the alpha channel. +/// +internal struct Rgba5551 : IBlock +{ + /// + public readonly int BitsPerPixel => 16; - /// - public byte DivSize => 1; + /// + public readonly byte PixelDepthBytes => 2; - /// - public byte CompressedBytesPerBlock => 2; + /// + public readonly byte DivSize => 1; - /// - public bool Compressed => false; + /// + public readonly byte CompressedBytesPerBlock => 2; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public readonly bool Compressed => false; - /// - public byte[] Decompress(byte[] blockData, int width, int height) => blockData; + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); } + + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) => blockData; } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Rgba64.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Rgba64.cs index 10228960..41a87fd2 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Rgba64.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Rgba64.cs @@ -1,36 +1,35 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding -{ - /// - /// Texture format with 16 bits per color channel. - /// - internal struct Rgba64 : IBlock - { - /// - public int BitsPerPixel => 64; +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; - /// - public byte PixelDepthBytes => 8; +/// +/// Texture format with 16 bits per color channel. +/// +internal struct Rgba64 : IBlock +{ + /// + public readonly int BitsPerPixel => 64; - /// - public byte DivSize => 1; + /// + public readonly byte PixelDepthBytes => 8; - /// - public byte CompressedBytesPerBlock => 8; + /// + public readonly byte DivSize => 1; - /// - public bool Compressed => false; + /// + public readonly byte CompressedBytesPerBlock => 8; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public readonly bool Compressed => false; - /// - public byte[] Decompress(byte[] blockData, int width, int height) => blockData; + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); } + + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) => blockData; } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Rgba64Float.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Rgba64Float.cs index ec517a81..a862854b 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Rgba64Float.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Rgba64Float.cs @@ -1,38 +1,35 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Textures.PixelFormats; +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding +/// +/// Texture format for pixels with 16 bit floating point values for each channel (including the alpha channel). +/// +internal struct Rgba64Float : IBlock { - /// - /// Texture format for pixels with 16 bit floating point values for each channel (including the alpha channel). - /// - internal struct Rgba64Float : IBlock - { - /// - public int BitsPerPixel => 64; - - /// - public byte PixelDepthBytes => 8; + /// + public readonly int BitsPerPixel => 64; - /// - public byte DivSize => 1; + /// + public readonly byte PixelDepthBytes => 8; - /// - public byte CompressedBytesPerBlock => 8; + /// + public readonly byte DivSize => 1; - /// - public bool Compressed => false; + /// + public readonly byte CompressedBytesPerBlock => 8; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public readonly bool Compressed => false; - /// - public byte[] Decompress(byte[] blockData, int width, int height) => blockData; + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); } + + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) => blockData; } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/RgbaAstc10x10.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/RgbaAstc10x10.cs new file mode 100644 index 00000000..7f13e992 --- /dev/null +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/RgbaAstc10x10.cs @@ -0,0 +1,38 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; + +/// +/// Texture compressed with RgbaAstc10x10. +/// +internal readonly struct RgbaAstc10X10 : IBlock +{ + public static Size BlockSize => new(10, 10); + + /// + public int BitsPerPixel => 128 / (BlockSize.Width * BlockSize.Height); + + /// + public byte PixelDepthBytes => 4; + + /// + public byte DivSize => 10; + + /// + public byte CompressedBytesPerBlock => 16; + + /// + public bool Compressed => true; + + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); + } + + /// + public byte[] Decompress(byte[] blockData, int width, int height) => + AstcDecoder.DecompressImage(blockData, width, height, BlockSize.Width, BlockSize.Height, this.CompressedBytesPerBlock); +} diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/RgbaAstc10x5.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/RgbaAstc10x5.cs new file mode 100644 index 00000000..d75b58a9 --- /dev/null +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/RgbaAstc10x5.cs @@ -0,0 +1,38 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; + +/// +/// Texture compressed with RgbaAstc10x5. +/// +internal readonly struct RgbaAstc10X5 : IBlock +{ + public static Size BlockSize => new(10, 5); + + /// + public int BitsPerPixel => 128 / (BlockSize.Width * BlockSize.Height); + + /// + public byte PixelDepthBytes => 4; + + /// + public byte DivSize => 10; + + /// + public byte CompressedBytesPerBlock => 16; + + /// + public bool Compressed => true; + + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); + } + + /// + public byte[] Decompress(byte[] blockData, int width, int height) => + AstcDecoder.DecompressImage(blockData, width, height, BlockSize.Width, BlockSize.Height, this.CompressedBytesPerBlock); +} diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/RgbaAstc10x6.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/RgbaAstc10x6.cs new file mode 100644 index 00000000..1f041af9 --- /dev/null +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/RgbaAstc10x6.cs @@ -0,0 +1,38 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; + +/// +/// Texture compressed with RgbaAstc10x6. +/// +internal readonly struct RgbaAstc10X6 : IBlock +{ + public static Size BlockSize => new(10, 6); + + /// + public int BitsPerPixel => 128 / (BlockSize.Width * BlockSize.Height); + + /// + public byte PixelDepthBytes => 4; + + /// + public byte DivSize => 10; + + /// + public byte CompressedBytesPerBlock => 16; + + /// + public bool Compressed => true; + + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); + } + + /// + public byte[] Decompress(byte[] blockData, int width, int height) => + AstcDecoder.DecompressImage(blockData, width, height, BlockSize.Width, BlockSize.Height, this.CompressedBytesPerBlock); +} diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/RgbaAstc10x8.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/RgbaAstc10x8.cs new file mode 100644 index 00000000..d2883ded --- /dev/null +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/RgbaAstc10x8.cs @@ -0,0 +1,38 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; + +/// +/// Texture compressed with RgbaAstc10x8. +/// +internal readonly struct RgbaAstc10X8 : IBlock +{ + public static Size BlockSize => new(10, 8); + + /// + public int BitsPerPixel => 128 / (BlockSize.Width * BlockSize.Height); + + /// + public byte PixelDepthBytes => 4; + + /// + public byte DivSize => 10; + + /// + public byte CompressedBytesPerBlock => 16; + + /// + public bool Compressed => true; + + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); + } + + /// + public byte[] Decompress(byte[] blockData, int width, int height) => + AstcDecoder.DecompressImage(blockData, width, height, BlockSize.Width, BlockSize.Height, this.CompressedBytesPerBlock); +} diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/RgbaAstc12x10.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/RgbaAstc12x10.cs new file mode 100644 index 00000000..38a4e1aa --- /dev/null +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/RgbaAstc12x10.cs @@ -0,0 +1,38 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; + +/// +/// Texture compressed with RgbaAstc12x10. +/// +internal readonly struct RgbaAstc12X10 : IBlock +{ + public static Size BlockSize => new(12, 10); + + /// + public int BitsPerPixel => 128 / (BlockSize.Width * BlockSize.Height); + + /// + public byte PixelDepthBytes => 4; + + /// + public byte DivSize => 12; + + /// + public byte CompressedBytesPerBlock => 16; + + /// + public bool Compressed => true; + + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); + } + + /// + public byte[] Decompress(byte[] blockData, int width, int height) => + AstcDecoder.DecompressImage(blockData, width, height, BlockSize.Width, BlockSize.Height, this.CompressedBytesPerBlock); +} diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/RgbaAstc12x12.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/RgbaAstc12x12.cs new file mode 100644 index 00000000..0c0d9347 --- /dev/null +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/RgbaAstc12x12.cs @@ -0,0 +1,38 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; + +/// +/// Texture compressed with RgbaAstc12x12. +/// +internal readonly struct RgbaAstc12X12 : IBlock +{ + public static Size BlockSize => new(12, 12); + + /// + public int BitsPerPixel => 128 / (BlockSize.Width * BlockSize.Height); + + /// + public byte PixelDepthBytes => 4; + + /// + public byte DivSize => 12; + + /// + public byte CompressedBytesPerBlock => 16; + + /// + public bool Compressed => true; + + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); + } + + /// + public byte[] Decompress(byte[] blockData, int width, int height) => + AstcDecoder.DecompressImage(blockData, width, height, BlockSize.Width, BlockSize.Height, this.CompressedBytesPerBlock); +} diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/RgbaAstc4x4.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/RgbaAstc4x4.cs new file mode 100644 index 00000000..c1068b05 --- /dev/null +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/RgbaAstc4x4.cs @@ -0,0 +1,42 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; + +/// +/// Texture compressed with RgbaAstc4x4. +/// +internal readonly struct RgbaAstc4X4 : IBlock +{ + // See https://developer.nvidia.com/astc-texture-compression-for-game-assets + // https://chromium.googlesource.com/external/github.com/ARM-software/astc-encoder/+/HEAD/Docs/FormatOverview.md + public static Size BlockSize => new(4, 4); + + /// + // The 2D block footprints in ASTC range from 4x4 texels up to 12x12 texels, which all compress into 128-bit output blocks. + // By dividing 128 bits by the number of texels in the footprint, we derive the format bit rates which range from 8 bpt(128/(4*4)) down to 0.89 bpt(128/(12*12)). + public int BitsPerPixel => 128 / (BlockSize.Width * BlockSize.Height); + + /// + public byte PixelDepthBytes => 4; + + /// + public byte DivSize => 4; + + /// + public byte CompressedBytesPerBlock => 16; + + /// + public bool Compressed => true; + + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); + } + + /// + public byte[] Decompress(byte[] blockData, int width, int height) => + AstcDecoder.DecompressImage(blockData, width, height, BlockSize.Width, BlockSize.Height, this.CompressedBytesPerBlock); +} diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/RgbaAstc5x4.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/RgbaAstc5x4.cs new file mode 100644 index 00000000..f08b8494 --- /dev/null +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/RgbaAstc5x4.cs @@ -0,0 +1,38 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; + +/// +/// Texture compressed with RgbaAstc5x4. +/// +internal readonly struct RgbaAstc5X4 : IBlock +{ + public static Size BlockSize => new(5, 4); + + /// + public int BitsPerPixel => 128 / (BlockSize.Width * BlockSize.Height); + + /// + public byte PixelDepthBytes => 4; + + /// + public byte DivSize => 5; + + /// + public byte CompressedBytesPerBlock => 16; + + /// + public bool Compressed => true; + + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); + } + + /// + public byte[] Decompress(byte[] blockData, int width, int height) => + AstcDecoder.DecompressImage(blockData, width, height, BlockSize.Width, BlockSize.Height, this.CompressedBytesPerBlock); +} diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/RgbaAstc5x5.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/RgbaAstc5x5.cs new file mode 100644 index 00000000..dd7efeb7 --- /dev/null +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/RgbaAstc5x5.cs @@ -0,0 +1,38 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; + +/// +/// Texture compressed with RgbaAstc5x5. +/// +internal readonly struct RgbaAstc5X5 : IBlock +{ + public static Size BlockSize => new(5, 5); + + /// + public int BitsPerPixel => 128 / (BlockSize.Width * BlockSize.Height); + + /// + public byte PixelDepthBytes => 4; + + /// + public byte DivSize => 5; + + /// + public byte CompressedBytesPerBlock => 16; + + /// + public bool Compressed => true; + + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); + } + + /// + public byte[] Decompress(byte[] blockData, int width, int height) => + AstcDecoder.DecompressImage(blockData, width, height, BlockSize.Width, BlockSize.Height, this.CompressedBytesPerBlock); +} diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/RgbaAstc6x5.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/RgbaAstc6x5.cs new file mode 100644 index 00000000..f28368a6 --- /dev/null +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/RgbaAstc6x5.cs @@ -0,0 +1,38 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; + +/// +/// Texture compressed with RgbaAstc6x5. +/// +internal readonly struct RgbaAstc6X5 : IBlock +{ + public static Size BlockSize => new(6, 5); + + /// + public int BitsPerPixel => 128 / (BlockSize.Width * BlockSize.Height); + + /// + public byte PixelDepthBytes => 4; + + /// + public byte DivSize => 6; + + /// + public byte CompressedBytesPerBlock => 16; + + /// + public bool Compressed => true; + + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); + } + + /// + public byte[] Decompress(byte[] blockData, int width, int height) => + AstcDecoder.DecompressImage(blockData, width, height, BlockSize.Width, BlockSize.Height, this.CompressedBytesPerBlock); +} diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/RgbaAstc6x6.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/RgbaAstc6x6.cs new file mode 100644 index 00000000..ce71e216 --- /dev/null +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/RgbaAstc6x6.cs @@ -0,0 +1,38 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; + +/// +/// Texture compressed with RgbaAstc6x6. +/// +internal readonly struct RgbaAstc6X6 : IBlock +{ + public static Size BlockSize => new(6, 6); + + /// + public int BitsPerPixel => 128 / (BlockSize.Width * BlockSize.Height); + + /// + public byte PixelDepthBytes => 4; + + /// + public byte DivSize => 6; + + /// + public byte CompressedBytesPerBlock => 16; + + /// + public bool Compressed => true; + + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); + } + + /// + public byte[] Decompress(byte[] blockData, int width, int height) => + AstcDecoder.DecompressImage(blockData, width, height, BlockSize.Width, BlockSize.Height, this.CompressedBytesPerBlock); +} diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/RgbaAstc8x5.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/RgbaAstc8x5.cs new file mode 100644 index 00000000..5f9b80b7 --- /dev/null +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/RgbaAstc8x5.cs @@ -0,0 +1,38 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; + +/// +/// Texture compressed with RgbaAstc8x5. +/// +internal readonly struct RgbaAstc8X5 : IBlock +{ + public static Size BlockSize => new(8, 5); + + /// + public int BitsPerPixel => 128 / (BlockSize.Width * BlockSize.Height); + + /// + public byte PixelDepthBytes => 4; + + /// + public byte DivSize => 8; + + /// + public byte CompressedBytesPerBlock => 16; + + /// + public bool Compressed => true; + + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); + } + + /// + public byte[] Decompress(byte[] blockData, int width, int height) => + AstcDecoder.DecompressImage(blockData, width, height, BlockSize.Width, BlockSize.Height, this.CompressedBytesPerBlock); +} diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/RgbaAstc8x6.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/RgbaAstc8x6.cs new file mode 100644 index 00000000..984859ea --- /dev/null +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/RgbaAstc8x6.cs @@ -0,0 +1,38 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; + +/// +/// Texture compressed with RgbaAstc8x6. +/// +internal readonly struct RgbaAstc8X6 : IBlock +{ + public static Size BlockSize => new(8, 6); + + /// + public int BitsPerPixel => 128 / (BlockSize.Width * BlockSize.Height); + + /// + public byte PixelDepthBytes => 4; + + /// + public byte DivSize => 8; + + /// + public byte CompressedBytesPerBlock => 16; + + /// + public bool Compressed => true; + + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); + } + + /// + public byte[] Decompress(byte[] blockData, int width, int height) => + AstcDecoder.DecompressImage(blockData, width, height, BlockSize.Width, BlockSize.Height, this.CompressedBytesPerBlock); +} diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/RgbaAstc8x8.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/RgbaAstc8x8.cs new file mode 100644 index 00000000..da93a202 --- /dev/null +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/RgbaAstc8x8.cs @@ -0,0 +1,38 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; + +/// +/// Texture compressed with RgbaAstc8x8. +/// +internal readonly struct RgbaAstc8X8 : IBlock +{ + public static Size BlockSize => new(8, 8); + + /// + public int BitsPerPixel => 128 / (BlockSize.Width * BlockSize.Height); + + /// + public byte PixelDepthBytes => 4; + + /// + public byte DivSize => 8; + + /// + public byte CompressedBytesPerBlock => 16; + + /// + public bool Compressed => true; + + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); + } + + /// + public byte[] Decompress(byte[] blockData, int width, int height) => + AstcDecoder.DecompressImage(blockData, width, height, BlockSize.Width, BlockSize.Height, this.CompressedBytesPerBlock); +} diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Rgbg32.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Rgbg32.cs index 71612c4e..412ad310 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Rgbg32.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Rgbg32.cs @@ -1,66 +1,64 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Runtime.InteropServices; -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; + +/// +/// Texture for pixel data with the R8G8B8G8 format. +/// +internal struct Rgbg32 : IBlock { - /// - /// Texture for pixel data with the R8G8B8G8 format. - /// - internal struct Rgbg32 : IBlock - { - /// - public int BitsPerPixel => 32; + /// + public readonly int BitsPerPixel => 32; - /// - public byte PixelDepthBytes => 3; + /// + public readonly byte PixelDepthBytes => 3; - /// - public byte DivSize => 1; + /// + public readonly byte DivSize => 1; - /// - public byte CompressedBytesPerBlock => 2; + /// + public readonly byte CompressedBytesPerBlock => 2; - /// - public bool Compressed => false; + /// + public readonly bool Compressed => false; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); + } - /// - public byte[] Decompress(byte[] blockData, int width, int height) + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) + { + int totalPixels = width * height; + byte[] decompressed = new byte[totalPixels * 3]; + Span rgb24Span = MemoryMarshal.Cast(decompressed); + + ImageSharp.PixelFormats.Rgb24 pixel = default(ImageSharp.PixelFormats.Rgb24); + int pixelIdx = 0; + for (int i = 0; i < blockData.Length; i += 4) { - int totalPixels = width * height; - byte[] decompressed = new byte[totalPixels * 3]; - Span rgb24Span = MemoryMarshal.Cast(decompressed); + byte r = blockData[i]; + byte g0 = blockData[i + 1]; + byte b = blockData[i + 2]; + byte g1 = blockData[i + 3]; - var pixel = default(ImageSharp.PixelFormats.Rgb24); - int pixelIdx = 0; - for (int i = 0; i < blockData.Length; i += 4) + pixel.FromRgb24(new ImageSharp.PixelFormats.Rgb24(r, g0, b)); + rgb24Span[pixelIdx++] = pixel; + if (pixelIdx >= totalPixels) { - byte r = blockData[i]; - byte g0 = blockData[i + 1]; - byte b = blockData[i + 2]; - byte g1 = blockData[i + 3]; - - pixel.FromRgb24(new ImageSharp.PixelFormats.Rgb24(r, g0, b)); - rgb24Span[pixelIdx++] = pixel; - if (pixelIdx >= totalPixels) - { - break; - } - - pixel.FromRgb24(new ImageSharp.PixelFormats.Rgb24(r, g1, b)); - rgb24Span[pixelIdx++] = pixel; + break; } - return decompressed; + pixel.FromRgb24(new ImageSharp.PixelFormats.Rgb24(r, g1, b)); + rgb24Span[pixelIdx++] = pixel; } + + return decompressed; } } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Y210.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Y210.cs index e924fc33..07835a4c 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Y210.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Y210.cs @@ -1,76 +1,74 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Numerics; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Textures.PixelFormats; -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; + +/// +/// Texture for 10-bit per channel packed YUV 4:2:2 video resource format. +/// +internal struct Y210 : IBlock { - /// - /// Texture for 10-bit per channel packed YUV 4:2:2 video resource format. - /// - internal struct Y210 : IBlock - { - /// - public int BitsPerPixel => 64; + /// + public readonly int BitsPerPixel => 64; - /// - public byte PixelDepthBytes => 3; + /// + public readonly byte PixelDepthBytes => 3; - /// - public byte DivSize => 1; + /// + public readonly byte DivSize => 1; - /// - public byte CompressedBytesPerBlock => 4; + /// + public readonly byte CompressedBytesPerBlock => 4; - /// - public bool Compressed => false; + /// + public readonly bool Compressed => false; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } - - /// - public byte[] Decompress(byte[] blockData, int width, int height) - { - int totalPixels = width * height; - byte[] decompressed = new byte[totalPixels * 3]; - Span rgb24Span = MemoryMarshal.Cast(decompressed); + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); + } - // Same as Y216 with least significant 6 bits set to zero. - var pixel = default(ImageSharp.PixelFormats.Rgb24); - int pixelIdx = 0; - for (int i = 0; i < blockData.Length; i += 8) - { - uint y0 = BitConverter.ToUInt16(blockData, i); - uint u = BitConverter.ToUInt16(blockData, i + 2); - uint y1 = BitConverter.ToUInt16(blockData, i + 4); - uint v = BitConverter.ToUInt16(blockData, i + 6); + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) + { + int totalPixels = width * height; + byte[] decompressed = new byte[totalPixels * 3]; + Span rgb24Span = MemoryMarshal.Cast(decompressed); - y0 = (y0 >> 6) - 64; - u = (u >> 6) - 512; - y1 = (y1 >> 6) - 64; - v = (v >> 6) - 512; + // Same as Y216 with least significant 6 bits set to zero. + ImageSharp.PixelFormats.Rgb24 pixel = default(ImageSharp.PixelFormats.Rgb24); + int pixelIdx = 0; + for (int i = 0; i < blockData.Length; i += 8) + { + uint y0 = BitConverter.ToUInt16(blockData, i); + uint u = BitConverter.ToUInt16(blockData, i + 2); + uint y1 = BitConverter.ToUInt16(blockData, i + 4); + uint v = BitConverter.ToUInt16(blockData, i + 6); - Vector4 rgbVec = ColorSpaceConversion.YuvToRgba10Bit(y0, u, v); - pixel.FromVector4(rgbVec); - rgb24Span[pixelIdx++] = pixel; - if (pixelIdx >= totalPixels) - { - break; - } + y0 = (y0 >> 6) - 64; + u = (u >> 6) - 512; + y1 = (y1 >> 6) - 64; + v = (v >> 6) - 512; - rgbVec = ColorSpaceConversion.YuvToRgba10Bit(y1, u, v); - pixel.FromVector4(rgbVec); - rgb24Span[pixelIdx++] = pixel; + Vector4 rgbVec = ColorSpaceConversion.YuvToRgba10Bit(y0, u, v); + pixel.FromVector4(rgbVec); + rgb24Span[pixelIdx++] = pixel; + if (pixelIdx >= totalPixels) + { + break; } - return decompressed; + rgbVec = ColorSpaceConversion.YuvToRgba10Bit(y1, u, v); + pixel.FromVector4(rgbVec); + rgb24Span[pixelIdx++] = pixel; } + + return decompressed; } } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Y216.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Y216.cs index 88e09459..8e3ac72c 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Y216.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Y216.cs @@ -1,75 +1,73 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Numerics; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Textures.PixelFormats; -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; + +/// +/// Texture for 16-bit per channel packed YUV 4:2:2 video resource format. +/// +internal struct Y216 : IBlock { - /// - /// Texture for 16-bit per channel packed YUV 4:2:2 video resource format. - /// - internal struct Y216 : IBlock - { - /// - public int BitsPerPixel => 64; + /// + public readonly int BitsPerPixel => 64; - /// - public byte PixelDepthBytes => 3; + /// + public readonly byte PixelDepthBytes => 3; - /// - public byte DivSize => 1; + /// + public readonly byte DivSize => 1; - /// - public byte CompressedBytesPerBlock => 4; + /// + public readonly byte CompressedBytesPerBlock => 4; - /// - public bool Compressed => false; + /// + public readonly bool Compressed => false; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } - - /// - public byte[] Decompress(byte[] blockData, int width, int height) - { - int totalPixels = width * height; - byte[] decompressed = new byte[totalPixels * 3]; - Span rgb24Span = MemoryMarshal.Cast(decompressed); + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); + } - var pixel = default(ImageSharp.PixelFormats.Rgb24); - int pixelIdx = 0; - for (int i = 0; i < blockData.Length; i += 8) - { - uint y0 = BitConverter.ToUInt16(blockData, i); - uint u = BitConverter.ToUInt16(blockData, i + 2); - uint y1 = BitConverter.ToUInt16(blockData, i + 4); - uint v = BitConverter.ToUInt16(blockData, i + 6); + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) + { + int totalPixels = width * height; + byte[] decompressed = new byte[totalPixels * 3]; + Span rgb24Span = MemoryMarshal.Cast(decompressed); - y0 -= 4096; - u -= 32768; - y1 -= 4096; - v -= 32768; + ImageSharp.PixelFormats.Rgb24 pixel = default(ImageSharp.PixelFormats.Rgb24); + int pixelIdx = 0; + for (int i = 0; i < blockData.Length; i += 8) + { + uint y0 = BitConverter.ToUInt16(blockData, i); + uint u = BitConverter.ToUInt16(blockData, i + 2); + uint y1 = BitConverter.ToUInt16(blockData, i + 4); + uint v = BitConverter.ToUInt16(blockData, i + 6); - Vector4 rgbVec = ColorSpaceConversion.YuvToRgba16Bit(y0, u, v); - pixel.FromVector4(rgbVec); - rgb24Span[pixelIdx++] = pixel; - if (pixelIdx >= totalPixels) - { - break; - } + y0 -= 4096; + u -= 32768; + y1 -= 4096; + v -= 32768; - rgbVec = ColorSpaceConversion.YuvToRgba16Bit(y1, u, v); - pixel.FromVector4(rgbVec); - rgb24Span[pixelIdx++] = pixel; + Vector4 rgbVec = ColorSpaceConversion.YuvToRgba16Bit(y0, u, v); + pixel.FromVector4(rgbVec); + rgb24Span[pixelIdx++] = pixel; + if (pixelIdx >= totalPixels) + { + break; } - return decompressed; + rgbVec = ColorSpaceConversion.YuvToRgba16Bit(y1, u, v); + pixel.FromVector4(rgbVec); + rgb24Span[pixelIdx++] = pixel; } + + return decompressed; } } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Y410.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Y410.cs index 6679bb72..4c31d399 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Y410.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Y410.cs @@ -1,36 +1,35 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding -{ - /// - /// Texture for 10-bit per channel packed YUV 4:4:4 video resource format. - /// - internal struct Y410 : IBlock - { - /// - public int BitsPerPixel => 32; +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; - /// - public byte PixelDepthBytes => 4; +/// +/// Texture for 10-bit per channel packed YUV 4:4:4 video resource format. +/// +internal struct Y410 : IBlock +{ + /// + public readonly int BitsPerPixel => 32; - /// - public byte DivSize => 1; + /// + public readonly byte PixelDepthBytes => 4; - /// - public byte CompressedBytesPerBlock => 4; + /// + public readonly byte DivSize => 1; - /// - public bool Compressed => false; + /// + public readonly byte CompressedBytesPerBlock => 4; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public readonly bool Compressed => false; - /// - public byte[] Decompress(byte[] blockData, int width, int height) => blockData; + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); } + + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) => blockData; } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Y416.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Y416.cs index aee555b6..ff684ad0 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Y416.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Y416.cs @@ -1,36 +1,35 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding -{ - /// - /// Texture for 16-bit per channel packed YUV 4:4:4 video resource format. - /// - internal struct Y416 : IBlock - { - /// - public int BitsPerPixel => 64; +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; - /// - public byte PixelDepthBytes => 8; +/// +/// Texture for 16-bit per channel packed YUV 4:4:4 video resource format. +/// +internal struct Y416 : IBlock +{ + /// + public readonly int BitsPerPixel => 64; - /// - public byte DivSize => 1; + /// + public readonly byte PixelDepthBytes => 8; - /// - public byte CompressedBytesPerBlock => 8; + /// + public readonly byte DivSize => 1; - /// - public bool Compressed => false; + /// + public readonly byte CompressedBytesPerBlock => 8; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } + /// + public readonly bool Compressed => false; - /// - public byte[] Decompress(byte[] blockData, int width, int height) => blockData; + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); } + + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) => blockData; } diff --git a/src/ImageSharp.Textures/TextureFormats/Decoding/Yuy2.cs b/src/ImageSharp.Textures/TextureFormats/Decoding/Yuy2.cs index 275e52e1..0412eaa3 100644 --- a/src/ImageSharp.Textures/TextureFormats/Decoding/Yuy2.cs +++ b/src/ImageSharp.Textures/TextureFormats/Decoding/Yuy2.cs @@ -1,75 +1,73 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Numerics; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Textures.PixelFormats; -namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding +namespace SixLabors.ImageSharp.Textures.TextureFormats.Decoding; + +/// +/// A texture based on the YUV 4:2:2 video resource format. The pixel format will be decoded into Rgb24. +/// +internal struct Yuy2 : IBlock { - /// - /// A texture based on the YUV 4:2:2 video resource format. The pixel format will be decoded into Rgb24. - /// - internal struct Yuy2 : IBlock - { - /// - public int BitsPerPixel => 32; + /// + public readonly int BitsPerPixel => 32; - /// - public byte PixelDepthBytes => 3; + /// + public readonly byte PixelDepthBytes => 3; - /// - public byte DivSize => 1; + /// + public readonly byte DivSize => 1; - /// - public byte CompressedBytesPerBlock => 2; + /// + public readonly byte CompressedBytesPerBlock => 2; - /// - public bool Compressed => false; + /// + public readonly bool Compressed => false; - /// - public Image GetImage(byte[] blockData, int width, int height) - { - byte[] decompressedData = this.Decompress(blockData, width, height); - return Image.LoadPixelData(decompressedData, width, height); - } - - /// - public byte[] Decompress(byte[] blockData, int width, int height) - { - int totalPixels = width * height; - byte[] decompressed = new byte[totalPixels * 3]; - Span rgb24Span = MemoryMarshal.Cast(decompressed); + /// + public Image GetImage(byte[] blockData, int width, int height) + { + byte[] decompressedData = this.Decompress(blockData, width, height); + return Image.LoadPixelData(decompressedData, width, height); + } - var pixel = default(ImageSharp.PixelFormats.Rgb24); - int pixelIdx = 0; - for (int i = 0; i < blockData.Length; i += 4) - { - int y0 = blockData[i]; - int u = blockData[i + 1]; - int y1 = blockData[i + 2]; - int v = blockData[i + 3]; + /// + public readonly byte[] Decompress(byte[] blockData, int width, int height) + { + int totalPixels = width * height; + byte[] decompressed = new byte[totalPixels * 3]; + Span rgb24Span = MemoryMarshal.Cast(decompressed); - y0 -= 16; - u -= 128; - y1 -= 16; - v -= 128; + ImageSharp.PixelFormats.Rgb24 pixel = default(ImageSharp.PixelFormats.Rgb24); + int pixelIdx = 0; + for (int i = 0; i < blockData.Length; i += 4) + { + int y0 = blockData[i]; + int u = blockData[i + 1]; + int y1 = blockData[i + 2]; + int v = blockData[i + 3]; - Vector4 rgbVec = ColorSpaceConversion.YuvToRgba8Bit(y0, u, v); - pixel.FromVector4(rgbVec); - rgb24Span[pixelIdx++] = pixel; - if (pixelIdx >= totalPixels) - { - break; - } + y0 -= 16; + u -= 128; + y1 -= 16; + v -= 128; - rgbVec = ColorSpaceConversion.YuvToRgba8Bit(y1, u, v); - pixel.FromVector4(rgbVec); - rgb24Span[pixelIdx++] = pixel; + Vector4 rgbVec = ColorSpaceConversion.YuvToRgba8Bit(y0, u, v); + pixel.FromVector4(rgbVec); + rgb24Span[pixelIdx++] = pixel; + if (pixelIdx >= totalPixels) + { + break; } - return decompressed; + rgbVec = ColorSpaceConversion.YuvToRgba8Bit(y1, u, v); + pixel.FromVector4(rgbVec); + rgb24Span[pixelIdx++] = pixel; } + + return decompressed; } } diff --git a/src/ImageSharp.Textures/TextureFormats/FlatTexture.cs b/src/ImageSharp.Textures/TextureFormats/FlatTexture.cs index b1f0183f..0395512b 100644 --- a/src/ImageSharp.Textures/TextureFormats/FlatTexture.cs +++ b/src/ImageSharp.Textures/TextureFormats/FlatTexture.cs @@ -1,50 +1,46 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; -using System.Collections.Generic; +namespace SixLabors.ImageSharp.Textures.TextureFormats; -namespace SixLabors.ImageSharp.Textures.TextureFormats +/// +/// A flat texture. +/// +/// +public class FlatTexture : Texture { + private bool isDisposed; + /// - /// A flat texture. + /// Initializes a new instance of the class. /// - /// - public class FlatTexture : Texture - { - private bool isDisposed; - - /// - /// Initializes a new instance of the class. - /// - public FlatTexture() => this.MipMaps = new List(); + public FlatTexture() => this.MipMaps = []; - /// - /// Gets the list of mip maps of the texture. - /// - public List MipMaps { get; } + /// + /// Gets the list of mip maps of the texture. + /// + public List MipMaps { get; } - /// - protected override void Dispose(bool disposing) + /// + protected override void Dispose(bool disposing) + { + if (this.isDisposed) { - if (this.isDisposed) - { - return; - } + return; + } - if (disposing) + if (disposing) + { + foreach (MipMap mipMap in this.MipMaps) { - foreach (MipMap mipMap in this.MipMaps) - { - // mipMap.Dispose(); - } + // mipMap.Dispose(); } - - this.isDisposed = true; } - /// - internal override void EnsureNotDisposed() - => ObjectDisposedException.ThrowIf(this.isDisposed, "Trying to execute an operation on a disposed image."); + this.isDisposed = true; } + + /// + internal override void EnsureNotDisposed() + => ObjectDisposedException.ThrowIf(this.isDisposed, "Trying to execute an operation on a disposed image."); } diff --git a/src/ImageSharp.Textures/TextureFormats/VolumeTexture.cs b/src/ImageSharp.Textures/TextureFormats/VolumeTexture.cs index 2a514e93..f90dad19 100644 --- a/src/ImageSharp.Textures/TextureFormats/VolumeTexture.cs +++ b/src/ImageSharp.Textures/TextureFormats/VolumeTexture.cs @@ -1,50 +1,46 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; -using System.Collections.Generic; +namespace SixLabors.ImageSharp.Textures.TextureFormats; -namespace SixLabors.ImageSharp.Textures.TextureFormats +/// +/// Represents a volume texture. +/// +/// +public class VolumeTexture : Texture { + private bool isDisposed; + /// - /// Represents a volume texture. + /// Initializes a new instance of the class. /// - /// - public class VolumeTexture : Texture - { - private bool isDisposed; - - /// - /// Initializes a new instance of the class. - /// - public VolumeTexture() => this.Slices = new List(); + public VolumeTexture() => this.Slices = []; - /// - /// Gets a list of flat textures from which the volume texture is composed of. - /// - public List Slices { get; } + /// + /// Gets a list of flat textures from which the volume texture is composed of. + /// + public List Slices { get; } - /// - protected override void Dispose(bool disposing) + /// + protected override void Dispose(bool disposing) + { + if (this.isDisposed) { - if (this.isDisposed) - { - return; - } + return; + } - if (disposing) + if (disposing) + { + foreach (FlatTexture slice in this.Slices) { - foreach (FlatTexture slice in this.Slices) - { - slice.Dispose(); - } + slice.Dispose(); } - - this.isDisposed = true; } - /// - internal override void EnsureNotDisposed() - => ObjectDisposedException.ThrowIf(this.isDisposed, "Trying to execute an operation on a disposed image."); + this.isDisposed = true; } + + /// + internal override void EnsureNotDisposed() + => ObjectDisposedException.ThrowIf(this.isDisposed, "Trying to execute an operation on a disposed image."); } diff --git a/src/ImageSharp.Textures/TextureInfo.cs b/src/ImageSharp.Textures/TextureInfo.cs index 53f8e83b..cb944b1d 100644 --- a/src/ImageSharp.Textures/TextureInfo.cs +++ b/src/ImageSharp.Textures/TextureInfo.cs @@ -3,33 +3,32 @@ using SixLabors.ImageSharp.Textures.Formats; -namespace SixLabors.ImageSharp.Textures +namespace SixLabors.ImageSharp.Textures; + +/// +/// Contains information about the image including dimensions, pixel type information and additional metadata +/// +internal sealed class TextureInfo : ITextureInfo { /// - /// Contains information about the image including dimensions, pixel type information and additional metadata + /// Initializes a new instance of the class. /// - internal sealed class TextureInfo : ITextureInfo + /// The image pixel type information. + /// The width of the image in pixels. + /// The height of the image in pixels. + public TextureInfo(TextureTypeInfo pixelType, int width, int height) { - /// - /// Initializes a new instance of the class. - /// - /// The image pixel type information. - /// The width of the image in pixels. - /// The height of the image in pixels. - public TextureInfo(TextureTypeInfo pixelType, int width, int height) - { - this.PixelType = pixelType; - this.Width = width; - this.Height = height; - } + this.PixelType = pixelType; + this.Width = width; + this.Height = height; + } - /// - public TextureTypeInfo PixelType { get; } + /// + public TextureTypeInfo PixelType { get; } - /// - public int Width { get; } + /// + public int Width { get; } - /// - public int Height { get; } - } + /// + public int Height { get; } } diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props index 775062ad..527225fe 100644 --- a/tests/Directory.Build.props +++ b/tests/Directory.Build.props @@ -11,7 +11,7 @@ --> - + diff --git a/tests/ImageSharp.Textures.Astc.Reference.Tests/ImageSharp.Textures.Astc.Reference.Tests.csproj b/tests/ImageSharp.Textures.Astc.Reference.Tests/ImageSharp.Textures.Astc.Reference.Tests.csproj new file mode 100644 index 00000000..5873dfab --- /dev/null +++ b/tests/ImageSharp.Textures.Astc.Reference.Tests/ImageSharp.Textures.Astc.Reference.Tests.csproj @@ -0,0 +1,41 @@ + + + + net10.0 + + SixLabors.ImageSharp.Textures.Astc.Reference.Tests + SixLabors.ImageSharp.Textures.Astc.Reference.Tests + true + $(NoWarn);CS8002 + + + + + + + + + + + + + + + + + + + <_TestData Include="..\Images\Input\Astc\**\*.*" /> + + + + + + + + <_AstcEncNativeFiles Include="$(NuGetPackageRoot)astcencodercsharp\5.3.0\runtimes\**\*.*" /> + + + + + diff --git a/tests/ImageSharp.Textures.Astc.Reference.Tests/ReferenceDecoderHdrTests.cs b/tests/ImageSharp.Textures.Astc.Reference.Tests/ReferenceDecoderHdrTests.cs new file mode 100644 index 00000000..bdb36ee9 --- /dev/null +++ b/tests/ImageSharp.Textures.Astc.Reference.Tests/ReferenceDecoderHdrTests.cs @@ -0,0 +1,252 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.ComponentModel; +using AwesomeAssertions; +using SixLabors.ImageSharp.Textures.Astc.Core; +using SixLabors.ImageSharp.Textures.Astc.IO; +using SixLabors.ImageSharp.Textures.Astc.Reference.Tests.Utils; + +namespace SixLabors.ImageSharp.Textures.Astc.Reference.Tests; + +/// +/// HDR comparison tests between SixLabors.ImageSharp.Textures.Astc and the ARM reference ASTC decoder. +/// These validate that SixLabors.ImageSharp.Textures.Astc produces HDR output matching the official ARM implementation. +/// +public class ReferenceDecoderHdrTests +{ + public static TheoryData AllFootprintTypes => + new() + { + FootprintType.Footprint4x4, + FootprintType.Footprint5x4, + FootprintType.Footprint5x5, + FootprintType.Footprint6x5, + FootprintType.Footprint6x6, + FootprintType.Footprint8x5, + FootprintType.Footprint8x6, + FootprintType.Footprint8x8, + FootprintType.Footprint10x5, + FootprintType.Footprint10x6, + FootprintType.Footprint10x8, + FootprintType.Footprint10x10, + FootprintType.Footprint12x10, + FootprintType.Footprint12x12, + }; + + [Theory] + [InlineData("HDR-A-1x1")] + [InlineData("hdr-tile")] + [InlineData("LDR-A-1x1")] + [InlineData("ldr-tile")] + public void DecompressHdr_WithHdrImage_ShouldMatch(string basename) + { + string filePath = Path.Combine("TestData", "HDR", basename + ".astc"); + + byte[] bytes = File.ReadAllBytes(filePath); + AstcFile astcFile = AstcFile.FromMemory(bytes); + (int blockX, int blockY) = ReferenceDecoder.ToBlockDimensions(astcFile.Footprint.Type); + + Half[] expected = ReferenceDecoder.DecompressHdr( + astcFile.Blocks, astcFile.Width, astcFile.Height, blockX, blockY); + Span actual = AstcDecoder.DecompressHdrImage( + astcFile.Blocks, astcFile.Width, astcFile.Height, astcFile.Footprint); + + CompareF16(actual, expected, astcFile.Width, astcFile.Height, basename); + } + + [Theory] + [InlineData("atlas_small_4x4")] + [InlineData("atlas_small_5x5")] + [InlineData("atlas_small_6x6")] + [InlineData("atlas_small_8x8")] + public void DecompressHdr_WithLdrImage_ShouldMatch(string basename) + { + string filePath = Path.Combine("TestData", "Input", basename + ".astc"); + byte[] bytes = File.ReadAllBytes(filePath); + AstcFile astcFile = AstcFile.FromMemory(bytes); + (int blockX, int blockY) = ReferenceDecoder.ToBlockDimensions(astcFile.Footprint.Type); + + Half[] expected = ReferenceDecoder.DecompressHdr( + astcFile.Blocks, astcFile.Width, astcFile.Height, blockX, blockY); + Span actual = AstcDecoder.DecompressHdrImage( + astcFile.Blocks, astcFile.Width, astcFile.Height, astcFile.Footprint); + + CompareF16(actual, expected, astcFile.Width, astcFile.Height, basename); + } + + [Theory] + [MemberData(nameof(AllFootprintTypes))] + public void DecompressHdr_SolidColor_ShouldMatch(FootprintType footprintType) + { + (int blockX, int blockY) = ReferenceDecoder.ToBlockDimensions(footprintType); + int width = blockX; + int height = blockY; + + // Single block: R=G=B=2.0, A=1.0 (above LDR range) + Half[] pixels = new Half[width * height * 4]; + for (int index = 0; index < width * height; index++) + { + pixels[(index * 4) + 0] = (Half)2.0f; + pixels[(index * 4) + 1] = (Half)2.0f; + pixels[(index * 4) + 2] = (Half)2.0f; + pixels[(index * 4) + 3] = (Half)1.0f; + } + + byte[] compressed = ReferenceDecoder.CompressHdr(pixels, width, height, blockX, blockY); + Footprint footprint = Footprint.FromFootprintType(footprintType); + + Half[] expected = ReferenceDecoder.DecompressHdr(compressed, width, height, blockX, blockY); + Span actual = AstcDecoder.DecompressHdrImage(compressed, width, height, footprint); + + CompareF16(actual, expected, width, height, $"BrightSolid_{footprintType}"); + } + + [Theory] + [MemberData(nameof(AllFootprintTypes))] + public void DecompressHdr_Gradient_ShouldMatch(FootprintType footprintType) + { + (int blockX, int blockY) = ReferenceDecoder.ToBlockDimensions(footprintType); + + // 2×2 blocks for HDR gradient + int width = blockX * 2; + int height = blockY * 2; + + // Gradient from 0.0 to 4.0 + Half[] pixels = new Half[width * height * 4]; + for (int row = 0; row < height; row++) + { + for (int col = 0; col < width; col++) + { + int idx = ((row * width) + col) * 4; + float fraction = (float)((row * width) + col) / ((width * height) - 1); + float value = fraction * 4.0f; + pixels[idx + 0] = (Half)value; + pixels[idx + 1] = (Half)value; + pixels[idx + 2] = (Half)value; + pixels[idx + 3] = (Half)1.0f; + } + } + + byte[] compressed = ReferenceDecoder.CompressHdr(pixels, width, height, blockX, blockY); + Footprint footprint = Footprint.FromFootprintType(footprintType); + + Half[] expected = ReferenceDecoder.DecompressHdr(compressed, width, height, blockX, blockY); + Span actual = AstcDecoder.DecompressHdrImage(compressed, width, height, footprint); + + CompareF16(actual, expected, width, height, $"HdrGradient_{footprintType}"); + } + + [Theory] + [MemberData(nameof(AllFootprintTypes))] + [Description("In ASTC, the encoder picks the best endpoint mode per block. A single image can have some blocks" + + " encoded with LDR modes and others with HDR modes, the encoder optimizes each block independently.")] + public void DecompressHdr_MixedLdrHdr_ShouldMatch(FootprintType footprintType) + { + (int blockX, int blockY) = ReferenceDecoder.ToBlockDimensions(footprintType); + + // 2×2 blocks + int width = blockX * 2; + int height = blockY * 2; + int halfWidth = width / 2; + + Half[] pixels = new Half[width * height * 4]; + for (int row = 0; row < height; row++) + { + for (int col = 0; col < width; col++) + { + int idx = ((row * width) + col) * 4; + if (col < halfWidth) + { + // LDR left half: values in 0.0-1.0 + float fraction = (float)row / (height - 1); + pixels[idx + 0] = (Half)(fraction * 0.8f); + pixels[idx + 1] = (Half)(fraction * 0.5f); + pixels[idx + 2] = (Half)(fraction * 0.3f); + } + else + { + // HDR right half: values above 1.0 + float fraction = (float)row / (height - 1); + pixels[idx + 0] = (Half)(1.0f + (fraction * 3.0f)); + pixels[idx + 1] = (Half)(0.5f + (fraction * 2.0f)); + pixels[idx + 2] = (Half)(0.2f + (fraction * 1.5f)); + } + + pixels[idx + 3] = (Half)1.0f; + } + } + + byte[] compressed = ReferenceDecoder.CompressHdr(pixels, width, height, blockX, blockY); + Footprint footprint = Footprint.FromFootprintType(footprintType); + + Half[] expected = ReferenceDecoder.DecompressHdr(compressed, width, height, blockX, blockY); + Span actual = AstcDecoder.DecompressHdrImage(compressed, width, height, footprint); + + CompareF16(actual, expected, width, height, $"MixedLdrHdr_{footprintType}"); + } + + /// + /// Compare float output from SixLabors.ImageSharp.Textures.Astc against FP16 output from the ARM reference decoder. + /// SixLabors.ImageSharp.Textures.Astc outputs float values (bit-cast from FP16 for HDR, normalized for LDR). + /// The ARM reference outputs raw FP16 Half values which are converted to float for comparison. + /// + private static void CompareF16(Span actual, Half[] expected, int width, int height, string label) + { + int channelCount = width * height * RgbaColor.BytesPerPixel; + actual.Length.Should().Be(channelCount, because: $"actual float output size should match for {label}"); + expected.Length.Should().Be(channelCount, because: $"expected F16 output size should match for {label}"); + + int mismatches = 0; + float worstRelDiff = 0; + int worstPixel = -1; + int worstChannel = -1; + + for (int index = 0; index < channelCount; index++) + { + float actualValue = actual[index]; + float expectedValue = (float)expected[index]; + + // Both NaN == match; one NaN == mismatch + if (float.IsNaN(actualValue) && float.IsNaN(expectedValue)) + { + continue; + } + + if (float.IsNaN(actualValue) || float.IsNaN(expectedValue)) + { + mismatches++; + continue; + } + + float absDiff = MathF.Abs(actualValue - expectedValue); + float maxVal = MathF.Max(MathF.Abs(actualValue), MathF.Max(MathF.Abs(expectedValue), 1e-6f)); + float relDiff = absDiff / maxVal; + + // Use a relative tolerance of 0.1% plus absolute tolerance of one FP16 ULP (~0.001 for values near 1.0) + if (absDiff > 0.001f && relDiff > 0.001f) + { + mismatches++; + if (relDiff > worstRelDiff) + { + worstRelDiff = relDiff; + worstPixel = index / 4; + worstChannel = index % 4; + } + } + } + + if (mismatches > 0) + { + string channelName = worstChannel switch { 0 => "R", 1 => "G", 2 => "B", _ => "A" }; + int pixelX = worstPixel % width; + int pixelY = worstPixel / width; + Assert.Fail( + $"[{label}] {mismatches}/{channelCount} F16 channel mismatches. " + + $"Worst: pixel ({pixelX},{pixelY}) channel {channelName}, " + + $"actual={actual[(worstPixel * 4) + worstChannel]:G5} vs " + + $"expected={(float)expected[(worstPixel * 4) + worstChannel]:G5} " + + $"(relDiff={worstRelDiff:P2})."); + } + } +} diff --git a/tests/ImageSharp.Textures.Astc.Reference.Tests/ReferenceDecoderTests.cs b/tests/ImageSharp.Textures.Astc.Reference.Tests/ReferenceDecoderTests.cs new file mode 100644 index 00000000..3f206630 --- /dev/null +++ b/tests/ImageSharp.Textures.Astc.Reference.Tests/ReferenceDecoderTests.cs @@ -0,0 +1,272 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using AwesomeAssertions; +using SixLabors.ImageSharp.Textures.Astc.Core; +using SixLabors.ImageSharp.Textures.Astc.IO; +using SixLabors.ImageSharp.Textures.Astc.Reference.Tests.Utils; + +namespace SixLabors.ImageSharp.Textures.Astc.Reference.Tests; + +/// +/// LDR comparison tests between SixLabors.ImageSharp.Textures.Astc and the ARM reference ASTC decoder. +/// These validate that SixLabors.ImageSharp.Textures.Astc produces output matching the official ARM implementation. +/// +public class ReferenceDecoderTests +{ + // Per-channel tolerance for RGBA8 comparisons. + // ASTC spec conformance allows ±1 for UNORM8 output due to rounding differences. + private const int Ldr8BitTolerance = 1; + + public static TheoryData AllFootprintTypes => + new() + { + FootprintType.Footprint4x4, + FootprintType.Footprint5x4, + FootprintType.Footprint5x5, + FootprintType.Footprint6x5, + FootprintType.Footprint6x6, + FootprintType.Footprint8x5, + FootprintType.Footprint8x6, + FootprintType.Footprint8x8, + FootprintType.Footprint10x5, + FootprintType.Footprint10x6, + FootprintType.Footprint10x8, + FootprintType.Footprint10x10, + FootprintType.Footprint12x10, + FootprintType.Footprint12x12, + }; + + [Theory] + [InlineData("atlas_small_4x4")] + [InlineData("atlas_small_5x5")] + [InlineData("atlas_small_6x6")] + [InlineData("atlas_small_8x8")] + [InlineData("checkerboard")] + [InlineData("checkered_4")] + [InlineData("checkered_5")] + [InlineData("checkered_6")] + [InlineData("checkered_7")] + [InlineData("checkered_8")] + [InlineData("checkered_9")] + [InlineData("checkered_10")] + [InlineData("checkered_11")] + [InlineData("checkered_12")] + [InlineData("footprint_4x4")] + [InlineData("footprint_5x4")] + [InlineData("footprint_5x5")] + [InlineData("footprint_6x5")] + [InlineData("footprint_6x6")] + [InlineData("footprint_8x5")] + [InlineData("footprint_8x6")] + [InlineData("footprint_8x8")] + [InlineData("footprint_10x5")] + [InlineData("footprint_10x6")] + [InlineData("footprint_10x8")] + [InlineData("footprint_10x10")] + [InlineData("footprint_12x10")] + [InlineData("footprint_12x12")] + [InlineData("rgb_4x4")] + [InlineData("rgb_5x4")] + [InlineData("rgb_6x6")] + [InlineData("rgb_8x8")] + [InlineData("rgb_12x12")] + public void DecompressLdr_WithImage_ShouldMatch(string basename) + { + string filePath = Path.Combine("TestData", "Input", basename + ".astc"); + byte[] bytes = File.ReadAllBytes(filePath); + AstcFile astcFile = AstcFile.FromMemory(bytes); + (int blockX, int blockY) = ReferenceDecoder.ToBlockDimensions(astcFile.Footprint.Type); + + byte[] expected = ReferenceDecoder.DecompressLdr( + astcFile.Blocks, astcFile.Width, astcFile.Height, blockX, blockY); + Span actual = AstcDecoder.DecompressImage( + astcFile.Blocks, astcFile.Width, astcFile.Height, astcFile.Footprint); + + CompareRgba8(actual, expected, astcFile.Width, astcFile.Height, basename); + } + + [Theory] + [MemberData(nameof(AllFootprintTypes))] + public void DecompressLdr_SolidColor_ShouldMatch(FootprintType footprintType) + { + (int blockX, int blockY) = ReferenceDecoder.ToBlockDimensions(footprintType); + int width = blockX; + int height = blockY; + + // Single solid color block + byte[] pixels = new byte[width * height * RgbaColor.BytesPerPixel]; + for (int index = 0; index < width * height; index++) + { + pixels[(index * 4) + 0] = 128; // R + pixels[(index * 4) + 1] = 64; // G + pixels[(index * 4) + 2] = 200; // B + pixels[(index * 4) + 3] = 255; // A + } + + byte[] compressed = ReferenceDecoder.CompressLdr(pixels, width, height, blockX, blockY); + Footprint footprint = Footprint.FromFootprintType(footprintType); + + byte[] expected = ReferenceDecoder.DecompressLdr(compressed, width, height, blockX, blockY); + Span actual = AstcDecoder.DecompressImage(compressed, width, height, footprint); + + CompareRgba8(actual, expected, width, height, $"SolidColor_{footprintType}"); + } + + [Theory] + [MemberData(nameof(AllFootprintTypes))] + public void DecompressLdr_Gradient_ShouldMatch(FootprintType footprintType) + { + (int blockX, int blockY) = ReferenceDecoder.ToBlockDimensions(footprintType); + + // 2×2 blocks for gradient + int width = blockX * 2; + int height = blockY * 2; + + byte[] pixels = new byte[width * height * RgbaColor.BytesPerPixel]; + for (int row = 0; row < height; row++) + { + for (int col = 0; col < width; col++) + { + int idx = ((row * width) + col) * 4; + pixels[idx + 0] = (byte)(255 * col / (width - 1)); // R: left-to-right + pixels[idx + 1] = (byte)(255 * row / (height - 1)); // G: top-to-bottom + pixels[idx + 2] = (byte)(255 - (255 * col / (width - 1))); // B: inverse of R + pixels[idx + 3] = 255; + } + } + + byte[] compressed = ReferenceDecoder.CompressLdr(pixels, width, height, blockX, blockY); + Footprint footprint = Footprint.FromFootprintType(footprintType); + + byte[] expected = ReferenceDecoder.DecompressLdr(compressed, width, height, blockX, blockY); + Span actual = AstcDecoder.DecompressImage(compressed, width, height, footprint); + + CompareRgba8(actual, expected, width, height, $"Gradient_{footprintType}"); + } + + [Theory] + [MemberData(nameof(AllFootprintTypes))] + public void DecompressLdr_RandomNoise_ShouldMatch(FootprintType footprintType) + { + (int blockX, int blockY) = ReferenceDecoder.ToBlockDimensions(footprintType); + + // 2×2 blocks + int width = blockX * 2; + int height = blockY * 2; + + Random rng = new(42); // Fixed seed for reproducibility + byte[] pixels = new byte[width * height * RgbaColor.BytesPerPixel]; + rng.NextBytes(pixels); + + // Force alpha to 255 so compression doesn't introduce alpha-related variance + for (int index = 3; index < pixels.Length; index += RgbaColor.BytesPerPixel) + { + pixels[index] = byte.MaxValue; + } + + byte[] compressed = ReferenceDecoder.CompressLdr(pixels, width, height, blockX, blockY); + Footprint footprint = Footprint.FromFootprintType(footprintType); + + byte[] expected = ReferenceDecoder.DecompressLdr(compressed, width, height, blockX, blockY); + Span actual = AstcDecoder.DecompressImage(compressed, width, height, footprint); + + CompareRgba8(actual, expected, width, height, $"RandomNoise_{footprintType}"); + } + + [Theory] + [MemberData(nameof(AllFootprintTypes))] + public void DecompressLdr_NonBlockAlignedDimensions_ShouldMatch(FootprintType footprintType) + { + (int blockX, int blockY) = ReferenceDecoder.ToBlockDimensions(footprintType); + + // Non-block-aligned dimensions: use dimensions that don't evenly divide by block size + int width = blockX + (blockX / 2) + 1; // e.g. for 4x4: 7, for 8x8: 13 + int height = blockY + (blockY / 2) + 1; + + Random rng = new(123); + byte[] pixels = new byte[width * height * RgbaColor.BytesPerPixel]; + rng.NextBytes(pixels); + for (int index = 3; index < pixels.Length; index += RgbaColor.BytesPerPixel) + { + pixels[index] = byte.MaxValue; + } + + byte[] compressed = ReferenceDecoder.CompressLdr(pixels, width, height, blockX, blockY); + Footprint footprint = Footprint.FromFootprintType(footprintType); + + byte[] expected = ReferenceDecoder.DecompressLdr(compressed, width, height, blockX, blockY); + Span actual = AstcDecoder.DecompressImage(compressed, width, height, footprint); + + CompareRgba8(actual, expected, width, height, $"NonAligned_{footprintType}"); + } + + [Fact] + public void DecompressLdr_VoidExtentBlock_ShouldMatch() + { + // Manually construct a void-extent constant-color block (128 bits): + // Bits [0..8] = 0b111111100 (0x1FC, void-extent marker) + // Bit [9] = 0 (LDR mode) + // Bits [10..11] = 0b11 (reserved, must be 11 for valid void-extent) + // Bits [12..63] = all 1s (no extent coordinates = constant color block) + // Bits [64..79] = R (UNORM16) + // Bits [80..95] = G (UNORM16) + // Bits [96..111] = B (UNORM16) + // Bits [112..127]= A (UNORM16) + byte[] block = new byte[16]; + ulong low = 0xFFFFFFFFFFFFFDFC; + ulong high = (0xFFFFUL << 48) | (0xC000UL << 32) | (0x4000UL << 16) | 0x8000; + BitConverter.TryWriteBytes(block.AsSpan(0, 8), low); + BitConverter.TryWriteBytes(block.AsSpan(8, 8), high); + + const int blockX = 4; + const int blockY = 4; + Footprint footprint = Footprint.FromFootprintType(FootprintType.Footprint4x4); + + byte[] expected = ReferenceDecoder.DecompressLdr(block, blockX, blockY, blockX, blockY); + Span actual = AstcDecoder.DecompressImage(block, blockX, blockY, footprint); + + CompareRgba8(actual, expected, blockX, blockY, "VoidExtent"); + } + + /// + /// Compare RGBA8 output from both decoders with per-channel tolerance. + /// + private static void CompareRgba8(Span actual, byte[] expected, int width, int height, string label) + { + int pixelCount = width * height * RgbaColor.BytesPerPixel; + actual.Length.Should().Be(pixelCount, because: $"actual output size should match for {label}"); + expected.Length.Should().Be(pixelCount, because: $"expected output size should match for {label}"); + + int mismatches = 0; + int worstDiff = 0; + int worstPixel = -1; + int worstChannel = -1; + + for (int index = 0; index < pixelCount; index++) + { + int diff = Math.Abs(actual[index] - expected[index]); + if (diff > Ldr8BitTolerance) + { + mismatches++; + if (diff > worstDiff) + { + worstDiff = diff; + worstPixel = index / RgbaColor.BytesPerPixel; + worstChannel = index % RgbaColor.BytesPerPixel; + } + } + } + + if (mismatches > 0) + { + string channelName = worstChannel switch { 0 => "R", 1 => "G", 2 => "B", _ => "A" }; + int pixelX = worstPixel % width; + int pixelY = worstPixel / width; + Assert.Fail( + $"[{label}] {mismatches} channel mismatches exceed tolerance ±{Ldr8BitTolerance}. " + + $"Worst: pixel ({pixelX},{pixelY}) channel {channelName}, " + + $"actual={actual[(worstPixel * 4) + worstChannel]} vs expected={expected[(worstPixel * 4) + worstChannel]} (diff={worstDiff})"); + } + } +} diff --git a/tests/ImageSharp.Textures.Astc.Reference.Tests/Utils/ReferenceDecoder.cs b/tests/ImageSharp.Textures.Astc.Reference.Tests/Utils/ReferenceDecoder.cs new file mode 100644 index 00000000..5240ee8b --- /dev/null +++ b/tests/ImageSharp.Textures.Astc.Reference.Tests/Utils/ReferenceDecoder.cs @@ -0,0 +1,238 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.InteropServices; +using AstcEncoder; +using SixLabors.ImageSharp.Textures.Astc.Core; + +namespace SixLabors.ImageSharp.Textures.Astc.Reference.Tests.Utils; + +/// +/// Wrapper around the ARM reference ASTC encoder/decoder (AstcEncoderCSharp package) +/// for use as a comparison baseline in tests. +/// +internal static class ReferenceDecoder +{ + private static readonly AstcencSwizzle IdentitySwizzle = new() + { + r = AstcencSwz.AstcencSwzR, + g = AstcencSwz.AstcencSwzG, + b = AstcencSwz.AstcencSwzB, + a = AstcencSwz.AstcencSwzA, + }; + + /// + /// Decompress ASTC blocks to RGBA8 (LDR) using the ARM reference decoder. + /// + public static byte[] DecompressLdr(ReadOnlySpan blocks, int w, int h, int blockX, int blockY) + { + AstcencError error = Astcenc.AstcencConfigInit( + AstcencProfile.AstcencPrfLdr, + (uint)blockX, + (uint)blockY, + 1, + Astcenc.AstcencPreFastest, + AstcencFlags.DecompressOnly, + out AstcencConfig config); + ThrowOnError(error, "ConfigInit(LDR)"); + + error = Astcenc.AstcencContextAlloc(ref config, 1, out AstcencContext context); + ThrowOnError(error, "ContextAlloc(LDR)"); + + try + { + int pixelCount = w * h; + byte[] outputBytes = new byte[pixelCount * 4]; // RGBA8 + + AstcencImage image = new() + { + dimX = (uint)w, + dimY = (uint)h, + dimZ = 1, + dataType = AstcencType.AstcencTypeU8, + data = outputBytes, + }; + + // We need a mutable copy of blocks for the Span parameter + byte[] blocksCopy = blocks.ToArray(); + error = Astcenc.AstcencDecompressImage(context, blocksCopy, ref image, IdentitySwizzle, 0); + ThrowOnError(error, "DecompressImage(LDR)"); + + return outputBytes; + } + finally + { + Astcenc.AstcencContextFree(context); + } + } + + /// + /// Decompress ASTC blocks to FP16 RGBA (HDR) using the ARM reference decoder. + /// + public static Half[] DecompressHdr(ReadOnlySpan blocks, int w, int h, int blockX, int blockY) + { + AstcencError error = Astcenc.AstcencConfigInit( + AstcencProfile.AstcencPrfHdr, + (uint)blockX, + (uint)blockY, + 1, + Astcenc.AstcencPreFastest, + AstcencFlags.DecompressOnly, + out AstcencConfig config); + ThrowOnError(error, "ConfigInit(HDR)"); + + error = Astcenc.AstcencContextAlloc(ref config, 1, out AstcencContext context); + ThrowOnError(error, "ContextAlloc(HDR)"); + + try + { + int pixelCount = w * h; + Half[] outputHalves = new Half[pixelCount * 4]; // RGBA FP16 + byte[] outputBytes = MemoryMarshal.AsBytes(outputHalves.AsSpan()).ToArray(); + + AstcencImage image = new() + { + dimX = (uint)w, + dimY = (uint)h, + dimZ = 1, + dataType = AstcencType.AstcencTypeF16, + data = outputBytes, + }; + + byte[] blocksCopy = blocks.ToArray(); + error = Astcenc.AstcencDecompressImage(context, blocksCopy, ref image, IdentitySwizzle, 0); + ThrowOnError(error, "DecompressImage(HDR)"); + + // Copy the decompressed bytes back into the Half array + MemoryMarshal.AsBytes(outputHalves.AsSpan()).Clear(); + outputBytes.AsSpan().CopyTo(MemoryMarshal.AsBytes(outputHalves.AsSpan())); + + return outputHalves; + } + finally + { + Astcenc.AstcencContextFree(context); + } + } + + /// + /// Compress RGBA8 pixel data to ASTC using the ARM reference encoder (LDR). + /// + public static byte[] CompressLdr(byte[] pixels, int w, int h, int blockX, int blockY) + { + AstcencError error = Astcenc.AstcencConfigInit( + AstcencProfile.AstcencPrfLdr, + (uint)blockX, + (uint)blockY, + 1, + Astcenc.AstcencPreMedium, + 0, + out AstcencConfig config); + ThrowOnError(error, "ConfigInit(CompressLDR)"); + + error = Astcenc.AstcencContextAlloc(ref config, 1, out AstcencContext context); + ThrowOnError(error, "ContextAlloc(CompressLDR)"); + + try + { + AstcencImage image = new() + { + dimX = (uint)w, + dimY = (uint)h, + dimZ = 1, + dataType = AstcencType.AstcencTypeU8, + data = pixels, + }; + + int blocksWide = (w + blockX - 1) / blockX; + int blocksHigh = (h + blockY - 1) / blockY; + byte[] compressedData = new byte[blocksWide * blocksHigh * 16]; + + error = Astcenc.AstcencCompressImage(context, ref image, IdentitySwizzle, compressedData, 0); + ThrowOnError(error, "CompressImage(LDR)"); + + return compressedData; + } + finally + { + Astcenc.AstcencContextFree(context); + } + } + + /// + /// Compress FP16 RGBA pixel data to ASTC using the ARM reference encoder (HDR). + /// + public static byte[] CompressHdr(Half[] pixels, int w, int h, int blockX, int blockY) + { + AstcencError error = Astcenc.AstcencConfigInit( + AstcencProfile.AstcencPrfHdr, + (uint)blockX, + (uint)blockY, + 1, + Astcenc.AstcencPreMedium, + 0, + out AstcencConfig config); + ThrowOnError(error, "ConfigInit(CompressHDR)"); + + error = Astcenc.AstcencContextAlloc(ref config, 1, out AstcencContext context); + ThrowOnError(error, "ContextAlloc(CompressHDR)"); + + try + { + byte[] pixelBytes = MemoryMarshal.AsBytes(pixels.AsSpan()).ToArray(); + + AstcencImage image = new() + { + dimX = (uint)w, + dimY = (uint)h, + dimZ = 1, + dataType = AstcencType.AstcencTypeF16, + data = pixelBytes, + }; + + int blocksWide = (w + blockX - 1) / blockX; + int blocksHigh = (h + blockY - 1) / blockY; + byte[] compressedData = new byte[blocksWide * blocksHigh * 16]; + + error = Astcenc.AstcencCompressImage(context, ref image, IdentitySwizzle, compressedData, 0); + ThrowOnError(error, "CompressImage(HDR)"); + + return compressedData; + } + finally + { + Astcenc.AstcencContextFree(context); + } + } + + /// + /// Map a FootprintType to its (blockX, blockY) dimensions. + /// + public static (int BlockX, int BlockY) ToBlockDimensions(FootprintType footprint) => footprint switch + { + FootprintType.Footprint4x4 => (4, 4), + FootprintType.Footprint5x4 => (5, 4), + FootprintType.Footprint5x5 => (5, 5), + FootprintType.Footprint6x5 => (6, 5), + FootprintType.Footprint6x6 => (6, 6), + FootprintType.Footprint8x5 => (8, 5), + FootprintType.Footprint8x6 => (8, 6), + FootprintType.Footprint8x8 => (8, 8), + FootprintType.Footprint10x5 => (10, 5), + FootprintType.Footprint10x6 => (10, 6), + FootprintType.Footprint10x8 => (10, 8), + FootprintType.Footprint10x10 => (10, 10), + FootprintType.Footprint12x10 => (12, 10), + FootprintType.Footprint12x12 => (12, 12), + _ => throw new ArgumentOutOfRangeException(nameof(footprint)), + }; + + private static void ThrowOnError(AstcencError error, string operation) + { + if (error != AstcencError.AstcencSuccess) + { + string message = Astcenc.GetErrorString(error) ?? error.ToString(); + throw new InvalidOperationException($"ARM ASTC encoder {operation} failed: {message}"); + } + } +} diff --git a/tests/ImageSharp.Textures.Benchmarks/AstcDecodingBenchmark.cs b/tests/ImageSharp.Textures.Benchmarks/AstcDecodingBenchmark.cs new file mode 100644 index 00000000..6b939923 --- /dev/null +++ b/tests/ImageSharp.Textures.Benchmarks/AstcDecodingBenchmark.cs @@ -0,0 +1,67 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using BenchmarkDotNet.Attributes; +using SixLabors.ImageSharp.Textures.Astc.Core; +using SixLabors.ImageSharp.Textures.Astc.IO; +using SixLabors.ImageSharp.Textures.Astc.TexelBlock; + +namespace SixLabors.ImageSharp.Textures.Benchmarks; + +[MemoryDiagnoser] +public class AstcDecodingBenchmark +{ + private AstcFile? astcFile; + + [GlobalSetup] + public void Setup() + { + string path = BenchmarkTestDataLocator.FindAstcTestData(Path.Combine("Input", "atlas_small_4x4.astc")); + byte[] astcData = File.ReadAllBytes(path); + this.astcFile = AstcFile.FromMemory(astcData); + } + + [Benchmark] + public bool ParseBlock() + { + ReadOnlySpan blocks = this.astcFile!.Blocks; + Span blockBytes = stackalloc byte[16]; + blocks[..16].CopyTo(blockBytes); + ulong low = BitConverter.ToUInt64(blockBytes); + ulong high = BitConverter.ToUInt64(blockBytes[8..]); + PhysicalBlock phyiscalBlock = PhysicalBlock.Create((UInt128)low | ((UInt128)high << 64)); + + return !phyiscalBlock.IsIllegalEncoding; + } + + [Benchmark] + public bool DecodeEndpoints() + { + ReadOnlySpan blocks = this.astcFile!.Blocks; + Span blockBytes = stackalloc byte[16]; + blocks[..16].CopyTo(blockBytes); + ulong low = BitConverter.ToUInt64(blockBytes); + ulong high = BitConverter.ToUInt64(blockBytes[8..]); + PhysicalBlock physicalBlock = PhysicalBlock.Create((UInt128)low | ((UInt128)high << 64)); + + IntermediateBlock.IntermediateBlockData? blockData = IntermediateBlock.UnpackIntermediateBlock(physicalBlock); + + return blockData is not null; + } + + [Benchmark] + public bool Partitioning() + { + ReadOnlySpan blocks = this.astcFile!.Blocks; + Span blockBytes = stackalloc byte[16]; + blocks[..16].CopyTo(blockBytes); + ulong low = BitConverter.ToUInt64(blockBytes); + ulong high = BitConverter.ToUInt64(blockBytes[8..]); + UInt128 bits = (UInt128)low | ((UInt128)high << 64); + BlockInfo info = BlockInfo.Decode(bits); + LogicalBlock? logicalBlock = LogicalBlock.UnpackLogicalBlock(Footprint.Get4x4(), bits, in info) + ?? throw new InvalidOperationException("Failed to unpack block"); + + return logicalBlock is not null; + } +} diff --git a/tests/ImageSharp.Textures.Benchmarks/AstcImageDecodeBenchmark.cs b/tests/ImageSharp.Textures.Benchmarks/AstcImageDecodeBenchmark.cs new file mode 100644 index 00000000..00b446b0 --- /dev/null +++ b/tests/ImageSharp.Textures.Benchmarks/AstcImageDecodeBenchmark.cs @@ -0,0 +1,38 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using BenchmarkDotNet.Attributes; +using SixLabors.ImageSharp.Textures.Astc.IO; +using SixLabors.ImageSharp.Textures.Astc.TexelBlock; + +namespace SixLabors.ImageSharp.Textures.Benchmarks; + +[MemoryDiagnoser] +public class AstcImageDecodeBenchmark +{ + private AstcFile? astcFile; + + [GlobalSetup] + public void Setup() + { + string path = BenchmarkTestDataLocator.FindAstcTestData(Path.Combine("Input", "atlas_small_4x4.astc")); + byte[] astcData = File.ReadAllBytes(path); + this.astcFile = AstcFile.FromMemory(astcData); + } + + [Benchmark] + public void ImageDecode() + { + ReadOnlySpan blocks = this.astcFile!.Blocks; + int numBlocks = blocks.Length / 16; + Span blockBytes = stackalloc byte[16]; + for (int i = 0; i < numBlocks; ++i) + { + blocks.Slice(i * 16, 16).CopyTo(blockBytes); + ulong low = BitConverter.ToUInt64(blockBytes); + ulong high = BitConverter.ToUInt64(blockBytes[8..]); + PhysicalBlock block = PhysicalBlock.Create((UInt128)low | ((UInt128)high << 64)); + _ = IntermediateBlock.UnpackIntermediateBlock(block); + } + } +} diff --git a/tests/ImageSharp.Textures.Benchmarks/BenchmarkTestDataLocator.cs b/tests/ImageSharp.Textures.Benchmarks/BenchmarkTestDataLocator.cs new file mode 100644 index 00000000..1b34770f --- /dev/null +++ b/tests/ImageSharp.Textures.Benchmarks/BenchmarkTestDataLocator.cs @@ -0,0 +1,36 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Textures.Benchmarks; + +public static class BenchmarkTestDataLocator +{ + private const string SolutionFileName = "ImageSharp.Textures.sln"; + + /// + /// Locates a test data file under tests/Images/Input/Astc by walking up + /// from the benchmark output directory to find the solution root. + /// + /// Relative path from the Astc test data root (e.g. "Input/atlas_small_4x4.astc"). + /// Full path to the test data file. + /// Thrown when the file cannot be found. + public static string FindAstcTestData(string relativePath) + { + string dir = AppContext.BaseDirectory; + for (int i = 0; i < 10; ++i) + { + if (File.Exists(Path.Combine(dir, SolutionFileName))) + { + string candidate = Path.Combine(dir, "tests", "Images", "Input", "Astc", relativePath); + if (File.Exists(candidate)) + { + return Path.GetFullPath(candidate); + } + } + + dir = Path.GetFullPath(Path.Combine(dir, "..")); + } + + throw new FileNotFoundException($"Could not locate test data file: {relativePath}"); + } +} diff --git a/tests/ImageSharp.Textures.Benchmarks/Config.cs b/tests/ImageSharp.Textures.Benchmarks/Config.cs index f57970ad..06502c3f 100644 --- a/tests/ImageSharp.Textures.Benchmarks/Config.cs +++ b/tests/ImageSharp.Textures.Benchmarks/Config.cs @@ -6,23 +6,16 @@ using BenchmarkDotNet.Environments; using BenchmarkDotNet.Jobs; -namespace SixLabors.ImageSharp.Textures.Benchmarks +namespace SixLabors.ImageSharp.Textures.Benchmarks; + +public class Config : ManualConfig { - public class Config : ManualConfig - { - public Config() - { - this.AddDiagnoser(MemoryDiagnoser.Default); - } + public Config() => this.AddDiagnoser(MemoryDiagnoser.Default); - public class ShortRun : Config - { - public ShortRun() - { - this.AddJob( - Job.Default.WithRuntime(ClrRuntime.Net472).WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(3), - Job.Default.WithRuntime(CoreRuntime.Core31).WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(3)); - } - } + public class ShortRun : Config + { + public ShortRun() => this.AddJob( + Job.Default.WithRuntime(ClrRuntime.Net472).WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(3), + Job.Default.WithRuntime(CoreRuntime.Core31).WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(3)); } } diff --git a/tests/ImageSharp.Textures.Benchmarks/ImageSharp.Textures.Benchmarks.csproj b/tests/ImageSharp.Textures.Benchmarks/ImageSharp.Textures.Benchmarks.csproj index 528ca0cf..5afc6e6b 100644 --- a/tests/ImageSharp.Textures.Benchmarks/ImageSharp.Textures.Benchmarks.csproj +++ b/tests/ImageSharp.Textures.Benchmarks/ImageSharp.Textures.Benchmarks.csproj @@ -1,4 +1,4 @@ - + Exe @@ -12,9 +12,13 @@ - + - + + + + + diff --git a/tests/ImageSharp.Textures.Benchmarks/Program.cs b/tests/ImageSharp.Textures.Benchmarks/Program.cs index 287b56e1..d8ace843 100644 --- a/tests/ImageSharp.Textures.Benchmarks/Program.cs +++ b/tests/ImageSharp.Textures.Benchmarks/Program.cs @@ -4,10 +4,9 @@ using System.Reflection; using BenchmarkDotNet.Running; -namespace SixLabors.ImageSharp.Textures.Benchmarks +namespace SixLabors.ImageSharp.Textures.Benchmarks; + +public class Program { - public class Program - { - public static void Main(string[] args) => new BenchmarkSwitcher(typeof(Program).GetTypeInfo().Assembly).Run(args); - } + public static void Main(string[] args) => new BenchmarkSwitcher(typeof(Program).GetTypeInfo().Assembly).Run(args); } diff --git a/tests/ImageSharp.Textures.InteractiveTest/ApplicationManager.cs b/tests/ImageSharp.Textures.InteractiveTest/ApplicationManager.cs index f1e71471..0a8fa42e 100644 --- a/tests/ImageSharp.Textures.InteractiveTest/ApplicationManager.cs +++ b/tests/ImageSharp.Textures.InteractiveTest/ApplicationManager.cs @@ -1,47 +1,44 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; -using System.Collections.Generic; using SixLabors.ImageSharp.PixelFormats; using Veldrid; -namespace SixLabors.ImageSharp.Textures.InteractiveTest +namespace SixLabors.ImageSharp.Textures.InteractiveTest; + +public static class ApplicationManager { - public static class ApplicationManager - { - public static CommandList CommandList { get; set; } + public static CommandList CommandList { get; set; } - public static GraphicsDevice GraphicsDevice { get; set; } + public static GraphicsDevice GraphicsDevice { get; set; } - public static ImGuiRenderer Controller { get; set; } + public static ImGuiRenderer Controller { get; set; } - private static readonly object LockObject = new(); + private static readonly object LockObject = new(); - public static unsafe IntPtr Create(Image image) + public static unsafe IntPtr Create(Image image) + { + lock (LockObject) { - lock (LockObject) + Veldrid.Texture texture = GraphicsDevice.ResourceFactory.CreateTexture(TextureDescription.Texture2D((uint)image.Width, (uint)image.Height, 1, 1, PixelFormat.R8_G8_B8_A8_UNorm, TextureUsage.Sampled)); + bool gotPixelMemory = image.DangerousTryGetSinglePixelMemory(out Memory pixelsMemory); + if (gotPixelMemory) + { + System.Buffers.MemoryHandle pin = pixelsMemory.Pin(); + GraphicsDevice.UpdateTexture(texture, (IntPtr)pin.Pointer, (uint)(4 * image.Width * image.Height), 0, 0, 0, (uint)image.Width, (uint)image.Height, 1, 0, 0); + } + else { - Veldrid.Texture texture = GraphicsDevice.ResourceFactory.CreateTexture(TextureDescription.Texture2D((uint)image.Width, (uint)image.Height, 1, 1, PixelFormat.R8_G8_B8_A8_UNorm, TextureUsage.Sampled)); - bool gotPixelMemory = image.DangerousTryGetSinglePixelMemory(out Memory pixelsMemory); - if (gotPixelMemory) - { - System.Buffers.MemoryHandle pin = pixelsMemory.Pin(); - GraphicsDevice.UpdateTexture(texture, (IntPtr)pin.Pointer, (uint)(4 * image.Width * image.Height), 0, 0, 0, (uint)image.Width, (uint)image.Height, 1, 0, 0); - } - else - { - throw new Exception("DangerousTryGetSinglePixelMemory failed!"); - } - - return Controller.GetOrCreateImGuiBinding(GraphicsDevice.ResourceFactory, texture); + throw new Exception("DangerousTryGetSinglePixelMemory failed!"); } + + return Controller.GetOrCreateImGuiBinding(GraphicsDevice.ResourceFactory, texture); } + } - public static void ClearImageCache() => Controller.ClearCachedImageResources(); + public static void ClearImageCache() => Controller.ClearCachedImageResources(); - private static Dictionary datastore; + private static Dictionary datastore; - public static Dictionary DataStore => datastore ?? (datastore = new Dictionary()); - } + public static Dictionary DataStore => datastore ?? (datastore = []); } diff --git a/tests/ImageSharp.Textures.InteractiveTest/Extensions.cs b/tests/ImageSharp.Textures.InteractiveTest/Extensions.cs index f342e50d..1b2f715d 100644 --- a/tests/ImageSharp.Textures.InteractiveTest/Extensions.cs +++ b/tests/ImageSharp.Textures.InteractiveTest/Extensions.cs @@ -1,21 +1,17 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; -using System.Collections.Generic; +namespace SixLabors.ImageSharp.Textures.InteractiveTest; -namespace SixLabors.ImageSharp.Textures.InteractiveTest +public static class Extensions { - public static class Extensions + public static void AddOrReplace(this Dictionary dictionary, string key, object value) { - public static void AddOrReplace(this Dictionary dictionary, string key, object value) + if (dictionary.ContainsKey(key)) { - if (dictionary.ContainsKey(key)) - { - dictionary.Remove(key); - } - - dictionary.Add(key, value); + dictionary.Remove(key); } + + dictionary.Add(key, value); } } diff --git a/tests/ImageSharp.Textures.InteractiveTest/Program.cs b/tests/ImageSharp.Textures.InteractiveTest/Program.cs index 759c1b70..676db80b 100644 --- a/tests/ImageSharp.Textures.InteractiveTest/Program.cs +++ b/tests/ImageSharp.Textures.InteractiveTest/Program.cs @@ -1,90 +1,88 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using ImGuiNET; using Veldrid; using Veldrid.Sdl2; using Veldrid.StartupUtilities; -namespace SixLabors.ImageSharp.Textures.InteractiveTest +namespace SixLabors.ImageSharp.Textures.InteractiveTest; + +public class Program { - public class Program - { - private static UiManager uiManager; - private static Sdl2Window window; - private static DateTime prevUpdateTime; + private static UiManager uiManager; + private static Sdl2Window window; + private static DateTime prevUpdateTime; - public static void Main() - { - uiManager = new UiManager(); + public static void Main() + { + uiManager = new UiManager(); - window = VeldridStartup.CreateWindow(new WindowCreateInfo(50, 50, 1280, 720, WindowState.Normal, "ImageSharp.Textures.InteractiveTest")); - ApplicationManager.GraphicsDevice = VeldridStartup.CreateGraphicsDevice(window, GraphicsBackend.OpenGL); + window = VeldridStartup.CreateWindow(new WindowCreateInfo(50, 50, 1280, 720, WindowState.Normal, "ImageSharp.Textures.InteractiveTest")); + ApplicationManager.GraphicsDevice = VeldridStartup.CreateGraphicsDevice(window, GraphicsBackend.OpenGL); - window.Resized += Window_Resized; + window.Resized += Window_Resized; - ApplicationManager.CommandList = ApplicationManager.GraphicsDevice.ResourceFactory.CreateCommandList(); - ApplicationManager.Controller = new ImGuiRenderer(ApplicationManager.GraphicsDevice, ApplicationManager.GraphicsDevice.MainSwapchain.Framebuffer.OutputDescription, window.Width, window.Height); + ApplicationManager.CommandList = ApplicationManager.GraphicsDevice.ResourceFactory.CreateCommandList(); + ApplicationManager.Controller = new ImGuiRenderer(ApplicationManager.GraphicsDevice, ApplicationManager.GraphicsDevice.MainSwapchain.Framebuffer.OutputDescription, window.Width, window.Height); - ImGui.StyleColorsDark(); + ImGui.StyleColorsDark(); - // Main application loop - while (window.Exists) + // Main application loop + while (window.Exists) + { + InputSnapshot snapshot = window.PumpEvents(); + if (!window.Exists) { - InputSnapshot snapshot = window.PumpEvents(); - if (!window.Exists) - { - break; - } - - DateTime curUpdateTime = DateTime.Now; - if (prevUpdateTime.Ticks == 0) - { - prevUpdateTime = curUpdateTime; - } - - float dt = (float)(curUpdateTime - prevUpdateTime).TotalSeconds; - if (dt <= 0) - { - dt = float.Epsilon; - } + break; + } + DateTime curUpdateTime = DateTime.Now; + if (prevUpdateTime.Ticks == 0) + { prevUpdateTime = curUpdateTime; + } - ApplicationManager.Controller.Update(dt, snapshot); - - SubmitUi(); - - ApplicationManager.CommandList.Begin(); - ApplicationManager.CommandList.SetFramebuffer(ApplicationManager.GraphicsDevice.MainSwapchain.Framebuffer); - ApplicationManager.CommandList.ClearColorTarget(0, new RgbaFloat(0.5f, 0.5f, 0.5f, 1f)); - try - { - ApplicationManager.Controller.Render(ApplicationManager.GraphicsDevice, ApplicationManager.CommandList); - } - catch (Exception) - { - // do nothing. - } - - ApplicationManager.CommandList.End(); - ApplicationManager.GraphicsDevice.SubmitCommands(ApplicationManager.CommandList); - ApplicationManager.GraphicsDevice.SwapBuffers(ApplicationManager.GraphicsDevice.MainSwapchain); + float dt = (float)(curUpdateTime - prevUpdateTime).TotalSeconds; + if (dt <= 0) + { + dt = float.Epsilon; } - ApplicationManager.GraphicsDevice.WaitForIdle(); - ApplicationManager.Controller.Dispose(); - ApplicationManager.CommandList.Dispose(); - ApplicationManager.GraphicsDevice.Dispose(); - } + prevUpdateTime = curUpdateTime; - private static void Window_Resized() - { - ApplicationManager.GraphicsDevice.MainSwapchain.Resize((uint)window.Width, (uint)window.Height); - ApplicationManager.Controller.WindowResized(window.Width, window.Height); + ApplicationManager.Controller.Update(dt, snapshot); + + SubmitUi(); + + ApplicationManager.CommandList.Begin(); + ApplicationManager.CommandList.SetFramebuffer(ApplicationManager.GraphicsDevice.MainSwapchain.Framebuffer); + ApplicationManager.CommandList.ClearColorTarget(0, new RgbaFloat(0.5f, 0.5f, 0.5f, 1f)); + try + { + ApplicationManager.Controller.Render(ApplicationManager.GraphicsDevice, ApplicationManager.CommandList); + } + catch (Exception) + { + // do nothing. + } + + ApplicationManager.CommandList.End(); + ApplicationManager.GraphicsDevice.SubmitCommands(ApplicationManager.CommandList); + ApplicationManager.GraphicsDevice.SwapBuffers(ApplicationManager.GraphicsDevice.MainSwapchain); } - private static void SubmitUi() => uiManager.Render(window.Width, window.Height); + ApplicationManager.GraphicsDevice.WaitForIdle(); + ApplicationManager.Controller.Dispose(); + ApplicationManager.CommandList.Dispose(); + ApplicationManager.GraphicsDevice.Dispose(); } + + private static void Window_Resized() + { + ApplicationManager.GraphicsDevice.MainSwapchain.Resize((uint)window.Width, (uint)window.Height); + ApplicationManager.Controller.WindowResized(window.Width, window.Height); + } + + private static void SubmitUi() => uiManager.Render(window.Width, window.Height); } diff --git a/tests/ImageSharp.Textures.InteractiveTest/ResourceLoader.cs b/tests/ImageSharp.Textures.InteractiveTest/ResourceLoader.cs index f7ab619a..fa5cd7bb 100644 --- a/tests/ImageSharp.Textures.InteractiveTest/ResourceLoader.cs +++ b/tests/ImageSharp.Textures.InteractiveTest/ResourceLoader.cs @@ -1,70 +1,62 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; -using System.IO; -using System.Linq; using System.Reflection; -namespace SixLabors.ImageSharp.Textures.InteractiveTest +namespace SixLabors.ImageSharp.Textures.InteractiveTest; + +public static class ResourceLoader { - public static class ResourceLoader + public static bool GetEmbeddedResourceExists(string resourceFileName, Assembly assembly = null) { - public static bool GetEmbeddedResourceExists(string resourceFileName, Assembly assembly = null) + if (assembly == null) { - if (assembly == null) - { - assembly = typeof(ResourceLoader).GetTypeInfo().Assembly; - } + assembly = typeof(ResourceLoader).GetTypeInfo().Assembly; + } - string[] resourceNames = assembly.GetManifestResourceNames(); + string[] resourceNames = assembly.GetManifestResourceNames(); - string[] resourcePaths = resourceNames - .Where(x => x.EndsWith(resourceFileName, StringComparison.CurrentCultureIgnoreCase)) - .ToArray(); + string[] resourcePaths = [.. resourceNames.Where(x => x.EndsWith(resourceFileName, StringComparison.CurrentCultureIgnoreCase))]; - return resourcePaths.Any() && resourcePaths.Count() <= 1; - } + return resourcePaths.Any() && resourcePaths.Count() <= 1; + } - public static Stream GetEmbeddedResourceStream(string resourceFileName, Assembly assembly = null) + public static Stream GetEmbeddedResourceStream(string resourceFileName, Assembly assembly = null) + { + if (assembly == null) { - if (assembly == null) - { - assembly = typeof(ResourceLoader).GetTypeInfo().Assembly; - } - - string[] resourceNames = assembly.GetManifestResourceNames(); - - string[] resourcePaths = resourceNames - .Where(x => x.EndsWith(resourceFileName, StringComparison.CurrentCultureIgnoreCase)) - .ToArray(); + assembly = typeof(ResourceLoader).GetTypeInfo().Assembly; + } - if (!resourcePaths.Any()) - { - throw new Exception($"Resource ending with {resourceFileName} not found."); - } + string[] resourceNames = assembly.GetManifestResourceNames(); - if (resourcePaths.Length > 1) - { - throw new Exception($"Multiple resources ending with {resourceFileName} found: {System.Environment.NewLine}{string.Join(System.Environment.NewLine, resourcePaths)}"); - } + string[] resourcePaths = [.. resourceNames.Where(x => x.EndsWith(resourceFileName, StringComparison.CurrentCultureIgnoreCase))]; - return assembly.GetManifestResourceStream(resourcePaths.Single()); - } - - public static string GetEmbeddedResourceString(string resourceFileName, Assembly assembly = null) + if (!resourcePaths.Any()) { - Stream stream = GetEmbeddedResourceStream(resourceFileName, assembly); - using var streamReader = new StreamReader(stream); - return streamReader.ReadToEnd(); + throw new Exception($"Resource ending with {resourceFileName} not found."); } - public static byte[] GetEmbeddedResourceBytes(string resourceFileName, Assembly assembly = null) + if (resourcePaths.Length > 1) { - Stream stream = GetEmbeddedResourceStream(resourceFileName, assembly); - using var streamReader = new MemoryStream(); - stream.CopyTo(streamReader); - return streamReader.ToArray(); + throw new Exception($"Multiple resources ending with {resourceFileName} found: {System.Environment.NewLine}{string.Join(System.Environment.NewLine, resourcePaths)}"); } + + return assembly.GetManifestResourceStream(resourcePaths.Single()); + } + + public static string GetEmbeddedResourceString(string resourceFileName, Assembly assembly = null) + { + Stream stream = GetEmbeddedResourceStream(resourceFileName, assembly); + using StreamReader streamReader = new StreamReader(stream); + return streamReader.ReadToEnd(); + } + + public static byte[] GetEmbeddedResourceBytes(string resourceFileName, Assembly assembly = null) + { + Stream stream = GetEmbeddedResourceStream(resourceFileName, assembly); + using MemoryStream streamReader = new MemoryStream(); + stream.CopyTo(streamReader); + return streamReader.ToArray(); } } diff --git a/tests/ImageSharp.Textures.InteractiveTest/UI/Button.cs b/tests/ImageSharp.Textures.InteractiveTest/UI/Button.cs index c163405e..cd6cb4fd 100644 --- a/tests/ImageSharp.Textures.InteractiveTest/UI/Button.cs +++ b/tests/ImageSharp.Textures.InteractiveTest/UI/Button.cs @@ -1,39 +1,37 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Numerics; using ImGuiNET; -namespace SixLabors.ImageSharp.Textures.InteractiveTest.UI +namespace SixLabors.ImageSharp.Textures.InteractiveTest.UI; + +public class Button { - public class Button - { - public Vector2 Size { get; set; } = new Vector2(100, 30); + public Vector2 Size { get; set; } = new Vector2(100, 30); - public string Title { get; set; } = string.Empty; + public string Title { get; set; } = string.Empty; - public bool Enabled { get; set; } = true; + public bool Enabled { get; set; } = true; - public bool Visible { get; set; } = true; + public bool Visible { get; set; } = true; - public void Render(Action clicked) + public void Render(Action clicked) + { + if (!this.Visible) { - if (!this.Visible) - { - return; - } + return; + } - ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * (this.Enabled ? 1.0f : 0.5f)); - if (ImGui.Button(this.Title, this.Size)) + ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * (this.Enabled ? 1.0f : 0.5f)); + if (ImGui.Button(this.Title, this.Size)) + { + if (this.Enabled) { - if (this.Enabled) - { - clicked?.Invoke(); - } + clicked?.Invoke(); } - - ImGui.PopStyleVar(); } + + ImGui.PopStyleVar(); } } diff --git a/tests/ImageSharp.Textures.InteractiveTest/UI/MenuBar.cs b/tests/ImageSharp.Textures.InteractiveTest/UI/MenuBar.cs index 37b9606b..ce08cf6b 100644 --- a/tests/ImageSharp.Textures.InteractiveTest/UI/MenuBar.cs +++ b/tests/ImageSharp.Textures.InteractiveTest/UI/MenuBar.cs @@ -3,56 +3,55 @@ using ImGuiNET; -namespace SixLabors.ImageSharp.Textures.InteractiveTest.UI +namespace SixLabors.ImageSharp.Textures.InteractiveTest.UI; + +public class MenuBar { - public class MenuBar - { - public bool DarkMode { get; private set; } + public bool DarkMode { get; private set; } - public bool DemoMode { get; } + public bool DemoMode { get; } + + public MenuBar() + { + this.DarkMode = true; + this.DemoMode = false; + } - public MenuBar() + public void Render(out float menuHeight) + { + if (this.DarkMode) + { + ImGui.StyleColorsDark(); + } + else { - this.DarkMode = true; - this.DemoMode = false; + ImGui.StyleColorsLight(); } - public void Render(out float menuHeight) + menuHeight = 0.0f; + if (ImGui.BeginMainMenuBar()) { - if (this.DarkMode) + if (ImGui.BeginMenu("Theme")) { - ImGui.StyleColorsDark(); - } - else - { - ImGui.StyleColorsLight(); - } + if (ImGui.MenuItem("Light", string.Empty, !this.DarkMode, this.DarkMode)) + { + this.DarkMode = false; + } - menuHeight = 0.0f; - if (ImGui.BeginMainMenuBar()) - { - if (ImGui.BeginMenu("Theme")) + if (ImGui.MenuItem("Dark", string.Empty, this.DarkMode, !this.DarkMode)) { - if (ImGui.MenuItem("Light", string.Empty, !this.DarkMode, this.DarkMode)) - { - this.DarkMode = false; - } - - if (ImGui.MenuItem("Dark", string.Empty, this.DarkMode, !this.DarkMode)) - { - this.DarkMode = true; - } - - // if (ImGui.MenuItem("Demo", "", DemoMode)) - // { - // DemoMode = !DemoMode; - // } - ImGui.EndMenu(); + this.DarkMode = true; } - menuHeight = ImGui.GetWindowHeight(); - ImGui.EndMainMenuBar(); + // if (ImGui.MenuItem("Demo", "", DemoMode)) + // { + // DemoMode = !DemoMode; + // } + ImGui.EndMenu(); } + + menuHeight = ImGui.GetWindowHeight(); + ImGui.EndMainMenuBar(); } } } diff --git a/tests/ImageSharp.Textures.InteractiveTest/UI/TitleBar.cs b/tests/ImageSharp.Textures.InteractiveTest/UI/TitleBar.cs index ad93a5f0..24461c4a 100644 --- a/tests/ImageSharp.Textures.InteractiveTest/UI/TitleBar.cs +++ b/tests/ImageSharp.Textures.InteractiveTest/UI/TitleBar.cs @@ -4,28 +4,27 @@ using System.Numerics; using ImGuiNET; -namespace SixLabors.ImageSharp.Textures.InteractiveTest.UI +namespace SixLabors.ImageSharp.Textures.InteractiveTest.UI; + +public class TitleBar { - public class TitleBar + public void Render(int page) { - public void Render(int page) - { - Vector2 position = ImGui.GetCursorScreenPos(); + Vector2 position = ImGui.GetCursorScreenPos(); - if (ImGui.BeginChild("TitleBar", new Vector2(0, 30), true, ImGuiWindowFlags.None)) - { - Vector2 size = ImGui.GetWindowSize(); + if (ImGui.BeginChild("TitleBar", new Vector2(0, 30), true, ImGuiWindowFlags.None)) + { + Vector2 size = ImGui.GetWindowSize(); - Vector4 inactiveColor = ImGui.ColorConvertU32ToFloat4(ImGui.GetColorU32(ImGuiCol.Text)); - Vector4 activeColor = ImGui.ColorConvertU32ToFloat4(ImGui.GetColorU32(ImGuiCol.ButtonActive)); + Vector4 inactiveColor = ImGui.ColorConvertU32ToFloat4(ImGui.GetColorU32(ImGuiCol.Text)); + Vector4 activeColor = ImGui.ColorConvertU32ToFloat4(ImGui.GetColorU32(ImGuiCol.ButtonActive)); - ImGui.TextColored(page == 0 ? activeColor : inactiveColor, "Welcome"); - ImGui.SameLine(0, 16); - ImGui.TextColored(page == 1 ? activeColor : inactiveColor, "Preview"); + ImGui.TextColored(page == 0 ? activeColor : inactiveColor, "Welcome"); + ImGui.SameLine(0, 16); + ImGui.TextColored(page == 1 ? activeColor : inactiveColor, "Preview"); - ImGui.EndChild(); - ImGui.GetWindowDrawList().AddRectFilled(position, position + size, ImGui.GetColorU32(ImGuiCol.TitleBgActive)); - } + ImGui.EndChild(); + ImGui.GetWindowDrawList().AddRectFilled(position, position + size, ImGui.GetColorU32(ImGuiCol.TitleBgActive)); } } } diff --git a/tests/ImageSharp.Textures.InteractiveTest/UI/Widgets.cs b/tests/ImageSharp.Textures.InteractiveTest/UI/Widgets.cs index 453ae269..9f2849e8 100644 --- a/tests/ImageSharp.Textures.InteractiveTest/UI/Widgets.cs +++ b/tests/ImageSharp.Textures.InteractiveTest/UI/Widgets.cs @@ -1,35 +1,33 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Numerics; using ImGuiNET; -namespace SixLabors.ImageSharp.Textures.InteractiveTest.UI +namespace SixLabors.ImageSharp.Textures.InteractiveTest.UI; + +public static class Widgets { - public static class Widgets + public static void RenderSpinner(Vector2 position, float radius, int thickness) { - public static void RenderSpinner(Vector2 position, float radius, int thickness) - { - float time = (float)ImGui.GetTime(); - uint color = ImGui.ColorConvertFloat4ToU32(new Vector4(0.5f, 0.5f, 0.5f, 1)); - ImDrawListPtr drawList = ImGui.GetWindowDrawList(); - - int num_segments = 30; - int start = (int)MathF.Abs(MathF.Sin(time * 1.8f) * (num_segments - 5)); - float aMin = MathF.PI * 2.0f * start / num_segments; - float aMax = MathF.PI * 2.0f * ((float)num_segments - 3) / num_segments; - var centre = new Vector2(position.X + radius, position.Y + radius + ImGui.GetStyle().FramePadding.Y); + float time = (float)ImGui.GetTime(); + uint color = ImGui.ColorConvertFloat4ToU32(new Vector4(0.5f, 0.5f, 0.5f, 1)); + ImDrawListPtr drawList = ImGui.GetWindowDrawList(); - drawList.PathClear(); - for (int i = 0; i < num_segments; i++) - { - float a = aMin + (i / (float)num_segments * (aMax - aMin)); - var location = new Vector2(centre.X + (MathF.Cos(a + (time * 8)) * radius), centre.Y + (MathF.Sin(a + (time * 8)) * radius)); - drawList.PathLineTo(location); - } + int num_segments = 30; + int start = (int)MathF.Abs(MathF.Sin(time * 1.8f) * (num_segments - 5)); + float aMin = MathF.PI * 2.0f * start / num_segments; + float aMax = MathF.PI * 2.0f * ((float)num_segments - 3) / num_segments; + Vector2 centre = new Vector2(position.X + radius, position.Y + radius + ImGui.GetStyle().FramePadding.Y); - drawList.PathStroke(color, false, thickness); + drawList.PathClear(); + for (int i = 0; i < num_segments; i++) + { + float a = aMin + (i / (float)num_segments * (aMax - aMin)); + Vector2 location = new Vector2(centre.X + (MathF.Cos(a + (time * 8)) * radius), centre.Y + (MathF.Sin(a + (time * 8)) * radius)); + drawList.PathLineTo(location); } + + drawList.PathStroke(color, false, thickness); } } diff --git a/tests/ImageSharp.Textures.InteractiveTest/UI/Wizard.cs b/tests/ImageSharp.Textures.InteractiveTest/UI/Wizard.cs index fc48ce38..9073b241 100644 --- a/tests/ImageSharp.Textures.InteractiveTest/UI/Wizard.cs +++ b/tests/ImageSharp.Textures.InteractiveTest/UI/Wizard.cs @@ -1,125 +1,123 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Numerics; using ImGuiNET; -namespace SixLabors.ImageSharp.Textures.InteractiveTest.UI +namespace SixLabors.ImageSharp.Textures.InteractiveTest.UI; + +public class Wizard { - public class Wizard - { - public int Pages { get; set; } + public int Pages { get; set; } - public int CurrentPageIndex { get; set; } + public int CurrentPageIndex { get; set; } - public Action OnCancel { get; set; } + public Action OnCancel { get; set; } - public Action OnValidate { get; set; } + public Action OnValidate { get; set; } - public Func OnPrevious { get; set; } + public Func OnPrevious { get; set; } - public Func OnNext { get; set; } + public Func OnNext { get; set; } - public Button CancelButton { get; } + public Button CancelButton { get; } - public Button ValidateButton { get; } + public Button ValidateButton { get; } - public Button PreviousButton { get; } + public Button PreviousButton { get; } - public Button NextButton { get; } + public Button NextButton { get; } - public Wizard() + public Wizard() + { + this.CancelButton = new Button() { - this.CancelButton = new Button() - { - Title = "Cancel", - Enabled = true, - Visible = false, - Size = new Vector2(100, 30) - }; - - this.ValidateButton = new Button() - { - Title = "Validate", - Enabled = true, - Visible = false, - Size = new Vector2(100, 30) - }; - - this.PreviousButton = new Button() - { - Title = "Previous", - Enabled = true, - Visible = true, - Size = new Vector2(100, 30) - }; - - this.NextButton = new Button() - { - Title = "Next", - Enabled = true, - Visible = true, - Size = new Vector2(100, 30) - }; - - this.Pages = 1; - this.CurrentPageIndex = 0; - } + Title = "Cancel", + Enabled = true, + Visible = false, + Size = new Vector2(100, 30) + }; - public void Render(Action renderPage) + this.ValidateButton = new Button() { - ImGui.BeginChild("Wizard", new Vector2(0, ImGui.GetWindowSize().Y - 88), true, ImGuiWindowFlags.None); - renderPage?.Invoke(); - ImGui.EndChild(); + Title = "Validate", + Enabled = true, + Visible = false, + Size = new Vector2(100, 30) + }; - ImGui.SetCursorPos(new Vector2(8, ImGui.GetWindowSize().Y - 38)); - this.CancelButton.Render(this.CancelAction); + this.PreviousButton = new Button() + { + Title = "Previous", + Enabled = true, + Visible = true, + Size = new Vector2(100, 30) + }; - ImGui.SetCursorPos(new Vector2(ImGui.GetWindowSize().X - 324, ImGui.GetWindowSize().Y - 38)); - this.ValidateButton.Render(this.ValidateAction); + this.NextButton = new Button() + { + Title = "Next", + Enabled = true, + Visible = true, + Size = new Vector2(100, 30) + }; + + this.Pages = 1; + this.CurrentPageIndex = 0; + } - ImGui.SetCursorPos(new Vector2(ImGui.GetWindowSize().X - 216, ImGui.GetWindowSize().Y - 38)); - this.PreviousButton.Render(this.PreviousAction); + public void Render(Action renderPage) + { + ImGui.BeginChild("Wizard", new Vector2(0, ImGui.GetWindowSize().Y - 88), true, ImGuiWindowFlags.None); + renderPage?.Invoke(); + ImGui.EndChild(); - ImGui.SetCursorPos(new Vector2(ImGui.GetWindowSize().X - 108, ImGui.GetWindowSize().Y - 38)); - this.NextButton.Render(this.NextAction); - } + ImGui.SetCursorPos(new Vector2(8, ImGui.GetWindowSize().Y - 38)); + this.CancelButton.Render(this.CancelAction); + + ImGui.SetCursorPos(new Vector2(ImGui.GetWindowSize().X - 324, ImGui.GetWindowSize().Y - 38)); + this.ValidateButton.Render(this.ValidateAction); + + ImGui.SetCursorPos(new Vector2(ImGui.GetWindowSize().X - 216, ImGui.GetWindowSize().Y - 38)); + this.PreviousButton.Render(this.PreviousAction); - private void CancelAction() => this.OnCancel?.Invoke(); + ImGui.SetCursorPos(new Vector2(ImGui.GetWindowSize().X - 108, ImGui.GetWindowSize().Y - 38)); + this.NextButton.Render(this.NextAction); + } + + private void CancelAction() => this.OnCancel?.Invoke(); - private void ValidateAction() => this.OnValidate?.Invoke(); + private void ValidateAction() => this.OnValidate?.Invoke(); - private void PreviousAction() + private void PreviousAction() + { + int pageIndex = this.CurrentPageIndex; + if (pageIndex > 0) { - int pageIndex = this.CurrentPageIndex; - if (pageIndex > 0) - { - pageIndex -= 1; - } - - if (this.OnPrevious?.Invoke(pageIndex) ?? false) - { - this.CurrentPageIndex = pageIndex; - } + pageIndex -= 1; } - private void NextAction() + if (this.OnPrevious?.Invoke(pageIndex) ?? false) { - int pageIndex = this.CurrentPageIndex; - if (pageIndex < (this.Pages - 1)) - { - pageIndex += 1; - } - - if (this.OnNext?.Invoke(pageIndex) ?? false) - { - this.CurrentPageIndex = pageIndex; - } + this.CurrentPageIndex = pageIndex; } + } - public void GoHome() => this.CurrentPageIndex = 0; + private void NextAction() + { + int pageIndex = this.CurrentPageIndex; + if (pageIndex < (this.Pages - 1)) + { + pageIndex += 1; + } - public void GoNext() => this.NextAction(); + if (this.OnNext?.Invoke(pageIndex) ?? false) + { + this.CurrentPageIndex = pageIndex; + } } + + public void GoHome() => this.CurrentPageIndex = 0; + + public void GoNext() => this.NextAction(); } diff --git a/tests/ImageSharp.Textures.InteractiveTest/UI/WizardPage.cs b/tests/ImageSharp.Textures.InteractiveTest/UI/WizardPage.cs index 2563785f..91f3dcb9 100644 --- a/tests/ImageSharp.Textures.InteractiveTest/UI/WizardPage.cs +++ b/tests/ImageSharp.Textures.InteractiveTest/UI/WizardPage.cs @@ -1,38 +1,25 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.InteractiveTest.UI +namespace SixLabors.ImageSharp.Textures.InteractiveTest.UI; + +public abstract class WizardPage { - public abstract class WizardPage - { - public Wizard Wizard { get; } + public Wizard Wizard { get; } - protected WizardPage(Wizard wizard) - { - this.Wizard = wizard; - } + protected WizardPage(Wizard wizard) => this.Wizard = wizard; - public virtual void Cancel() - { - this.Wizard.GoHome(); - } + public virtual void Cancel() => this.Wizard.GoHome(); - public virtual void Validate() - { - } + public virtual void Validate() + { + } - public virtual bool Previous(WizardPage newWizardPage) - { - return true; - } + public virtual bool Previous(WizardPage newWizardPage) => true; - public virtual bool Next(WizardPage newWizardPage) - { - return true; - } + public virtual bool Next(WizardPage newWizardPage) => true; - public abstract void Initialize(); + public abstract void Initialize(); - public abstract void Render(); - } + public abstract void Render(); } diff --git a/tests/ImageSharp.Textures.InteractiveTest/UI/WizardPages/Preview.cs b/tests/ImageSharp.Textures.InteractiveTest/UI/WizardPages/Preview.cs index 81384eee..ec07101b 100644 --- a/tests/ImageSharp.Textures.InteractiveTest/UI/WizardPages/Preview.cs +++ b/tests/ImageSharp.Textures.InteractiveTest/UI/WizardPages/Preview.cs @@ -1,10 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; -using System.Collections.Generic; using System.Diagnostics; -using System.IO; using System.Numerics; using System.Runtime.InteropServices; using ImGuiNET; @@ -13,244 +10,243 @@ using SixLabors.ImageSharp.Textures.Formats.Dds; using SixLabors.ImageSharp.Textures.TextureFormats; -namespace SixLabors.ImageSharp.Textures.InteractiveTest.UI.WizardPages +namespace SixLabors.ImageSharp.Textures.InteractiveTest.UI.WizardPages; + +public class Preview : WizardPage { - public class Preview : WizardPage + public struct ImageInfo { - public struct ImageInfo - { - public IntPtr TexturePtr; - public Vector2 Size; - public string FilePath; - public string TempFilePath; - public string ErrorMessage; - } + public IntPtr TexturePtr; + public Vector2 Size; + public string FilePath; + public string TempFilePath; + public string ErrorMessage; + } - private readonly string rootFolder; - private string currentFolder; - private string currentFile; + private readonly string rootFolder; + private string currentFolder; + private string currentFile; - private ImageInfo expectedImageInfo; - private ImageInfo actualImageInfo; + private ImageInfo expectedImageInfo; + private ImageInfo actualImageInfo; - public Preview(Wizard wizard) - : base(wizard) - { - this.rootFolder = Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, "Dds"); - this.currentFolder = this.rootFolder; - } + public Preview(Wizard wizard) + : base(wizard) + { + this.rootFolder = Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, "Dds"); + this.currentFolder = this.rootFolder; + } - public void OpenCompare(string filePath1, string filePath2) + public void OpenCompare(string filePath1, string filePath2) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - string command = @"C:\Program Files\Beyond Compare 4\bcomp.exe"; - Process.Start(new ProcessStartInfo(command, $"\"{filePath1}\" \"{filePath2}\" /fv=\"Picture Compare\"")); - } + string command = @"C:\Program Files\Beyond Compare 4\bcomp.exe"; + Process.Start(new ProcessStartInfo(command, $"\"{filePath1}\" \"{filePath2}\" /fv=\"Picture Compare\"")); } + } - public string DecompressDds(string filePath) + public string DecompressDds(string filePath) + { + string command = Path.Combine(TestEnvironment.ToolsDirectoryFullPath, "TexConv.exe"); + Process process = new Process { - string command = Path.Combine(TestEnvironment.ToolsDirectoryFullPath, "TexConv.exe"); - var process = new Process - { - StartInfo = - { - FileName = command, - Arguments = $"-ft PNG \"{filePath}\" -f rgba -o {Path.GetTempPath()}", - RedirectStandardOutput = true - } - }; - process.Start(); - process.WaitForExit(); - string sourceFile = Path.Combine(Path.GetTempPath(), $"{Path.GetFileNameWithoutExtension(filePath)}.png"); - if (!File.Exists(sourceFile)) + StartInfo = { - throw new Exception(process.StandardOutput.ReadToEnd()); + FileName = command, + Arguments = $"-ft PNG \"{filePath}\" -f rgba -o {Path.GetTempPath()}", + RedirectStandardOutput = true } - - string saveFilePath = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.png"); - File.Move(sourceFile, saveFilePath); - return saveFilePath; + }; + process.Start(); + process.WaitForExit(); + string sourceFile = Path.Combine(Path.GetTempPath(), $"{Path.GetFileNameWithoutExtension(filePath)}.png"); + if (!File.Exists(sourceFile)) + { + throw new Exception(process.StandardOutput.ReadToEnd()); } - public ImageInfo LoadExpected(string filePath) + string saveFilePath = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.png"); + File.Move(sourceFile, saveFilePath); + return saveFilePath; + } + + public ImageInfo LoadExpected(string filePath) + { + try { - try - { - string ddsSaveFilePath = this.DecompressDds(filePath); - using var clone = Image.Load(ddsSaveFilePath); - return new ImageInfo { TexturePtr = ApplicationManager.Create(clone), Size = new Vector2(clone.Width, clone.Height), FilePath = filePath, TempFilePath = ddsSaveFilePath }; - } - catch (Exception ex) - { - return new ImageInfo { TexturePtr = IntPtr.Zero, Size = Vector2.Zero, FilePath = filePath, TempFilePath = string.Empty, ErrorMessage = ex.ToString() }; - } + string ddsSaveFilePath = this.DecompressDds(filePath); + using Image clone = Image.Load(ddsSaveFilePath); + return new ImageInfo { TexturePtr = ApplicationManager.Create(clone), Size = new Vector2(clone.Width, clone.Height), FilePath = filePath, TempFilePath = ddsSaveFilePath }; } - - public ImageInfo LoadActualImage(string filePath) + catch (Exception ex) { - try - { - string saveFilePath = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.png"); - var decoder = new DdsDecoder(); - using FileStream fileStream = File.OpenRead(filePath); - using var result = (FlatTexture)decoder.DecodeTexture(Configuration.Default, fileStream); - using Image ddsImage = result.MipMaps[0].GetImage(); - using Image clone = ddsImage.CloneAs(); - clone.Save(saveFilePath); - return new ImageInfo { TexturePtr = ApplicationManager.Create(clone), Size = new Vector2(clone.Width, clone.Height), FilePath = filePath, TempFilePath = saveFilePath }; - } - catch (Exception ex) - { - return new ImageInfo { TexturePtr = IntPtr.Zero, Size = Vector2.Zero, FilePath = filePath, TempFilePath = string.Empty, ErrorMessage = ex.ToString() }; - } + return new ImageInfo { TexturePtr = IntPtr.Zero, Size = Vector2.Zero, FilePath = filePath, TempFilePath = string.Empty, ErrorMessage = ex.ToString() }; } + } - public override void Initialize() => ApplicationManager.ClearImageCache(); - - private static void DrawLines(IReadOnlyList points, Vector2 location, float size) + public ImageInfo LoadActualImage(string filePath) + { + try { - uint iconColor = ImGui.ColorConvertFloat4ToU32(new Vector4(1, 1, 1, 1)); - ImDrawListPtr drawList = ImGui.GetWindowDrawList(); - for (int i = 0; i < points.Count; i += 2) - { - Vector2 vector1 = points[i] / 100 * size; - Vector2 vector2 = points[i + 1] / 100 * size; - drawList.AddLine(location + vector1, location + vector2, iconColor); - } + string saveFilePath = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.png"); + DdsDecoder decoder = new DdsDecoder(); + using FileStream fileStream = File.OpenRead(filePath); + using FlatTexture result = (FlatTexture)decoder.DecodeTexture(Configuration.Default, fileStream); + using Image ddsImage = result.MipMaps[0].GetImage(); + using Image clone = ddsImage.CloneAs(); + clone.Save(saveFilePath); + return new ImageInfo { TexturePtr = ApplicationManager.Create(clone), Size = new Vector2(clone.Width, clone.Height), FilePath = filePath, TempFilePath = saveFilePath }; } - - private static void GenerateFolderIcon(Vector2 location, float size) + catch (Exception ex) { - Vector2[] points = new[] - { - new Vector2(0.0f, 0.0f), new Vector2(45.0f, 0.0f), - new Vector2(45.0f, 0.0f), new Vector2(55.0f, 22.5f), - new Vector2(55.0f, 22.5f), new Vector2(100.0f, 22.5f), - new Vector2(100.0f, 22.5f), new Vector2(100.0f, 87.5f), - new Vector2(100.0f, 87.5f), new Vector2(0.0f, 87.5f), - new Vector2(0.0f, 87.5f), new Vector2(0.0f, 0.0f) - }; - DrawLines(points, location, size); + return new ImageInfo { TexturePtr = IntPtr.Zero, Size = Vector2.Zero, FilePath = filePath, TempFilePath = string.Empty, ErrorMessage = ex.ToString() }; } + } - private static void GenerateFileIcon(Vector2 location, float size) + public override void Initialize() => ApplicationManager.ClearImageCache(); + + private static void DrawLines(IReadOnlyList points, Vector2 location, float size) + { + uint iconColor = ImGui.ColorConvertFloat4ToU32(new Vector4(1, 1, 1, 1)); + ImDrawListPtr drawList = ImGui.GetWindowDrawList(); + for (int i = 0; i < points.Count; i += 2) { - Vector2[] points = new[] - { - new Vector2(12.5f, 0.0f), new Vector2(62.5f, 0.0f), - new Vector2(62.5f, 0.0f), new Vector2(87.5f, 50.0f), - new Vector2(87.5f, 50.0f), new Vector2(87.5f, 100.0f), - new Vector2(87.5f, 100.0f), new Vector2(12.5f, 100.0f), - new Vector2(12.5f, 100.0f), new Vector2(12.5f, 0.0f), - new Vector2(62.5f, 0.0f), new Vector2(62.5f, 50.0f), - new Vector2(62.5f, 50.0f), new Vector2(87.5f, 50.0f) - }; - DrawLines(points, location, size); + Vector2 vector1 = points[i] / 100 * size; + Vector2 vector2 = points[i + 1] / 100 * size; + drawList.AddLine(location + vector1, location + vector2, iconColor); } + } - public override void Render() - { - this.Wizard.NextButton.Enabled = true; - this.Wizard.PreviousButton.Visible = false; - this.Wizard.NextButton.Title = "Home"; + private static void GenerateFolderIcon(Vector2 location, float size) + { + Vector2[] points = + [ + new Vector2(0.0f, 0.0f), new Vector2(45.0f, 0.0f), + new Vector2(45.0f, 0.0f), new Vector2(55.0f, 22.5f), + new Vector2(55.0f, 22.5f), new Vector2(100.0f, 22.5f), + new Vector2(100.0f, 22.5f), new Vector2(100.0f, 87.5f), + new Vector2(100.0f, 87.5f), new Vector2(0.0f, 87.5f), + new Vector2(0.0f, 87.5f), new Vector2(0.0f, 0.0f) + ]; + DrawLines(points, location, size); + } - Vector2 size = ImGui.GetWindowSize(); + private static void GenerateFileIcon(Vector2 location, float size) + { + Vector2[] points = + [ + new Vector2(12.5f, 0.0f), new Vector2(62.5f, 0.0f), + new Vector2(62.5f, 0.0f), new Vector2(87.5f, 50.0f), + new Vector2(87.5f, 50.0f), new Vector2(87.5f, 100.0f), + new Vector2(87.5f, 100.0f), new Vector2(12.5f, 100.0f), + new Vector2(12.5f, 100.0f), new Vector2(12.5f, 0.0f), + new Vector2(62.5f, 0.0f), new Vector2(62.5f, 50.0f), + new Vector2(62.5f, 50.0f), new Vector2(87.5f, 50.0f) + ]; + DrawLines(points, location, size); + } - ImGui.PushItemWidth(size.X - 16); - ImGui.PopItemWidth(); - ImGui.Spacing(); + public override void Render() + { + this.Wizard.NextButton.Enabled = true; + this.Wizard.PreviousButton.Visible = false; + this.Wizard.NextButton.Title = "Home"; - if (ImGui.BeginChildFrame(1, new Vector2(200, size.Y - 24), ImGuiWindowFlags.None)) - { - var directories = new List(); - if (!this.currentFolder.Equals(this.rootFolder, StringComparison.CurrentCultureIgnoreCase)) - { - directories.Add(".."); - } + Vector2 size = ImGui.GetWindowSize(); - directories.AddRange(Directory.GetDirectories(this.currentFolder)); - foreach (string directory in directories) - { - Vector2 iconPosition = ImGui.GetWindowPos() + ImGui.GetCursorPos(); - iconPosition.Y -= ImGui.GetScrollY(); - float lineHeight = ImGui.GetTextLineHeight(); - ImGui.SetCursorPosX(lineHeight * 2); - if (ImGui.Selectable(Path.GetFileName(directory), false, ImGuiSelectableFlags.DontClosePopups)) - { - this.currentFile = null; - this.currentFolder = directory.Equals("..") ? Path.GetFullPath(Path.Combine(this.currentFolder, "..")) : directory; - } - - GenerateFolderIcon(iconPosition, lineHeight); - } + ImGui.PushItemWidth(size.X - 16); + ImGui.PopItemWidth(); + ImGui.Spacing(); - string[] files = Directory.GetFiles(this.currentFolder); - foreach (string file in files) + if (ImGui.BeginChildFrame(1, new Vector2(200, size.Y - 24), ImGuiWindowFlags.None)) + { + List directories = new List(); + if (!this.currentFolder.Equals(this.rootFolder, StringComparison.CurrentCultureIgnoreCase)) + { + directories.Add(".."); + } + + directories.AddRange(Directory.GetDirectories(this.currentFolder)); + foreach (string directory in directories) + { + Vector2 iconPosition = ImGui.GetWindowPos() + ImGui.GetCursorPos(); + iconPosition.Y -= ImGui.GetScrollY(); + float lineHeight = ImGui.GetTextLineHeight(); + ImGui.SetCursorPosX(lineHeight * 2); + if (ImGui.Selectable(Path.GetFileName(directory), false, ImGuiSelectableFlags.DontClosePopups)) { - Vector2 iconPosition = ImGui.GetWindowPos() + ImGui.GetCursorPos(); - iconPosition.Y -= ImGui.GetScrollY(); - float lineHeight = ImGui.GetTextLineHeight(); - ImGui.SetCursorPosX(lineHeight * 2); - if (ImGui.Selectable(Path.GetFileName(file), string.Equals(file, this.currentFile, StringComparison.CurrentCultureIgnoreCase), ImGuiSelectableFlags.DontClosePopups)) - { - ApplicationManager.ClearImageCache(); - this.currentFile = file; - this.expectedImageInfo = this.LoadExpected(file); - this.actualImageInfo = this.LoadActualImage(file); - } - - GenerateFileIcon(iconPosition, lineHeight); + this.currentFile = null; + this.currentFolder = directory.Equals("..") ? Path.GetFullPath(Path.Combine(this.currentFolder, "..")) : directory; } - ImGui.EndChildFrame(); + GenerateFolderIcon(iconPosition, lineHeight); } - ImGui.SameLine(); - if (ImGui.BeginChildFrame(2, new Vector2(size.X - 224, size.Y - 24), ImGuiWindowFlags.None)) + string[] files = Directory.GetFiles(this.currentFolder); + foreach (string file in files) { - ImGui.Text("Expected Image"); - if (this.expectedImageInfo.TexturePtr != IntPtr.Zero) - { - ImGui.Image(this.expectedImageInfo.TexturePtr, this.expectedImageInfo.Size); - } - else if (this.expectedImageInfo.ErrorMessage != null) + Vector2 iconPosition = ImGui.GetWindowPos() + ImGui.GetCursorPos(); + iconPosition.Y -= ImGui.GetScrollY(); + float lineHeight = ImGui.GetTextLineHeight(); + ImGui.SetCursorPosX(lineHeight * 2); + if (ImGui.Selectable(Path.GetFileName(file), string.Equals(file, this.currentFile, StringComparison.CurrentCultureIgnoreCase), ImGuiSelectableFlags.DontClosePopups)) { - ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(1, 0, 0, 1)); - ImGui.TextWrapped(this.expectedImageInfo.ErrorMessage); - ImGui.PopStyleColor(); + ApplicationManager.ClearImageCache(); + this.currentFile = file; + this.expectedImageInfo = this.LoadExpected(file); + this.actualImageInfo = this.LoadActualImage(file); } - ImGui.Spacing(); + GenerateFileIcon(iconPosition, lineHeight); + } - ImGui.Text("Actual Image"); - if (this.actualImageInfo.TexturePtr != IntPtr.Zero) - { - ImGui.Image(this.actualImageInfo.TexturePtr, this.actualImageInfo.Size); - } - else if (this.actualImageInfo.ErrorMessage != null) - { - ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(1, 0, 0, 1)); - ImGui.TextWrapped(this.actualImageInfo.ErrorMessage); - ImGui.PopStyleColor(); - } + ImGui.EndChildFrame(); + } - ImGui.Spacing(); + ImGui.SameLine(); + if (ImGui.BeginChildFrame(2, new Vector2(size.X - 224, size.Y - 24), ImGuiWindowFlags.None)) + { + ImGui.Text("Expected Image"); + if (this.expectedImageInfo.TexturePtr != IntPtr.Zero) + { + ImGui.Image(this.expectedImageInfo.TexturePtr, this.expectedImageInfo.Size); + } + else if (this.expectedImageInfo.ErrorMessage != null) + { + ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(1, 0, 0, 1)); + ImGui.TextWrapped(this.expectedImageInfo.ErrorMessage); + ImGui.PopStyleColor(); + } - if (ImGui.Button("Compare")) - { - this.OpenCompare(this.expectedImageInfo.TempFilePath, this.actualImageInfo.TempFilePath); - } + ImGui.Spacing(); - ImGui.EndChildFrame(); + ImGui.Text("Actual Image"); + if (this.actualImageInfo.TexturePtr != IntPtr.Zero) + { + ImGui.Image(this.actualImageInfo.TexturePtr, this.actualImageInfo.Size); + } + else if (this.actualImageInfo.ErrorMessage != null) + { + ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(1, 0, 0, 1)); + ImGui.TextWrapped(this.actualImageInfo.ErrorMessage); + ImGui.PopStyleColor(); } - } - public override bool Next(WizardPage newWizardPage) - { - this.Wizard.GoHome(); - return false; + ImGui.Spacing(); + + if (ImGui.Button("Compare")) + { + this.OpenCompare(this.expectedImageInfo.TempFilePath, this.actualImageInfo.TempFilePath); + } + + ImGui.EndChildFrame(); } } + + public override bool Next(WizardPage newWizardPage) + { + this.Wizard.GoHome(); + return false; + } } diff --git a/tests/ImageSharp.Textures.InteractiveTest/UI/WizardPages/Welcome.cs b/tests/ImageSharp.Textures.InteractiveTest/UI/WizardPages/Welcome.cs index 893a9cd7..8944f45c 100644 --- a/tests/ImageSharp.Textures.InteractiveTest/UI/WizardPages/Welcome.cs +++ b/tests/ImageSharp.Textures.InteractiveTest/UI/WizardPages/Welcome.cs @@ -3,28 +3,27 @@ using ImGuiNET; -namespace SixLabors.ImageSharp.Textures.InteractiveTest.UI.WizardPages +namespace SixLabors.ImageSharp.Textures.InteractiveTest.UI.WizardPages; + +public class Welcome : WizardPage { - public class Welcome : WizardPage + public Welcome(Wizard wizard) + : base(wizard) { - public Welcome(Wizard wizard) - : base(wizard) - { - } + } - public override void Initialize() - { - } + public override void Initialize() + { + } - public override void Render() - { - this.Wizard.PreviousButton.Visible = false; - this.Wizard.CancelButton.Visible = false; + public override void Render() + { + this.Wizard.PreviousButton.Visible = false; + this.Wizard.CancelButton.Visible = false; - ImGui.TextWrapped("Welcome"); - ImGui.Spacing(); - ImGui.Spacing(); - ImGui.TextWrapped("Welcome to the ImageSharp Textures Test tool."); - } + ImGui.TextWrapped("Welcome"); + ImGui.Spacing(); + ImGui.Spacing(); + ImGui.TextWrapped("Welcome to the ImageSharp Textures Test tool."); } } diff --git a/tests/ImageSharp.Textures.InteractiveTest/UIManager.cs b/tests/ImageSharp.Textures.InteractiveTest/UIManager.cs index 9abaf4df..e000dc03 100644 --- a/tests/ImageSharp.Textures.InteractiveTest/UIManager.cs +++ b/tests/ImageSharp.Textures.InteractiveTest/UIManager.cs @@ -6,97 +6,96 @@ using SixLabors.ImageSharp.Textures.InteractiveTest.UI; using SixLabors.ImageSharp.Textures.InteractiveTest.UI.WizardPages; -namespace SixLabors.ImageSharp.Textures.InteractiveTest +namespace SixLabors.ImageSharp.Textures.InteractiveTest; + +public class UiManager { - public class UiManager + private readonly MenuBar menuBar; + private readonly TitleBar titleBar; + private readonly Wizard wizard; + private readonly WizardPage[] wizardPages; + + public UiManager() + { + this.menuBar = new MenuBar(); + this.titleBar = new TitleBar(); + + this.wizard = new Wizard(); + this.wizardPages = + [ + new Welcome(this.wizard), + new Preview(this.wizard) + ]; + this.wizard.Pages = this.wizardPages.Length; + + this.wizard.OnCancel += this.CancelButton_Action; + this.wizard.OnValidate += this.ValidateButton_Action; + this.wizard.OnPrevious += this.PreviousButton_Action; + this.wizard.OnNext += this.NextButton_Action; + } + + public void Render(float width, float height) { - private readonly MenuBar menuBar; - private readonly TitleBar titleBar; - private readonly Wizard wizard; - private readonly WizardPage[] wizardPages; + uint backgroundColor = ImGui.GetColorU32(ImGuiCol.WindowBg); + Vector4 newBackgroundColor = ImGui.ColorConvertU32ToFloat4(backgroundColor); + newBackgroundColor.W = 1.0f; + ImGui.PushStyleColor(ImGuiCol.WindowBg, newBackgroundColor); - public UiManager() + this.menuBar.Render(out float menuHeight); + if (this.menuBar.DemoMode) { - this.menuBar = new MenuBar(); - this.titleBar = new TitleBar(); - - this.wizard = new Wizard(); - this.wizardPages = new WizardPage[] - { - new Welcome(this.wizard), - new Preview(this.wizard) - }; - this.wizard.Pages = this.wizardPages.Length; - - this.wizard.OnCancel += this.CancelButton_Action; - this.wizard.OnValidate += this.ValidateButton_Action; - this.wizard.OnPrevious += this.PreviousButton_Action; - this.wizard.OnNext += this.NextButton_Action; + ImGui.ShowDemoWindow(); + return; } - public void Render(float width, float height) + ImGui.PushStyleVar(ImGuiStyleVar.WindowRounding, 0); + if (ImGui.Begin(string.Empty, ImGuiWindowFlags.NoCollapse | ImGuiWindowFlags.NoMove | ImGuiWindowFlags.NoNav | ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse)) { - uint backgroundColor = ImGui.GetColorU32(ImGuiCol.WindowBg); - Vector4 newBackgroundColor = ImGui.ColorConvertU32ToFloat4(backgroundColor); - newBackgroundColor.W = 1.0f; - ImGui.PushStyleColor(ImGuiCol.WindowBg, newBackgroundColor); - - this.menuBar.Render(out float menuHeight); - if (this.menuBar.DemoMode) - { - ImGui.ShowDemoWindow(); - return; - } - - ImGui.PushStyleVar(ImGuiStyleVar.WindowRounding, 0); - if (ImGui.Begin(string.Empty, ImGuiWindowFlags.NoCollapse | ImGuiWindowFlags.NoMove | ImGuiWindowFlags.NoNav | ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse)) - { - ImGui.SetWindowPos(new Vector2(0.0f, menuHeight)); - ImGui.SetWindowSize(new Vector2(width, height - menuHeight)); - - this.titleBar.Render(this.wizard.CurrentPageIndex); - - this.wizard.CancelButton.Visible = false; - this.wizard.CancelButton.Enabled = true; - this.wizard.CancelButton.Title = "Cancel"; - - this.wizard.ValidateButton.Visible = false; - this.wizard.ValidateButton.Enabled = true; - this.wizard.ValidateButton.Title = "Validate"; - - this.wizard.PreviousButton.Visible = true; - this.wizard.PreviousButton.Enabled = this.wizard.CurrentPageIndex > 0; - this.wizard.PreviousButton.Title = "Previous"; - - this.wizard.NextButton.Visible = true; - this.wizard.NextButton.Enabled = this.wizard.CurrentPageIndex < (this.wizard.Pages - 1); - this.wizard.NextButton.Title = "Next"; - this.wizard.Render(this.RenderPage_Action); - - ImGui.End(); - } - - ImGui.PopStyleVar(); - ImGui.PopStyleColor(); + ImGui.SetWindowPos(new Vector2(0.0f, menuHeight)); + ImGui.SetWindowSize(new Vector2(width, height - menuHeight)); + + this.titleBar.Render(this.wizard.CurrentPageIndex); + + this.wizard.CancelButton.Visible = false; + this.wizard.CancelButton.Enabled = true; + this.wizard.CancelButton.Title = "Cancel"; + + this.wizard.ValidateButton.Visible = false; + this.wizard.ValidateButton.Enabled = true; + this.wizard.ValidateButton.Title = "Validate"; + + this.wizard.PreviousButton.Visible = true; + this.wizard.PreviousButton.Enabled = this.wizard.CurrentPageIndex > 0; + this.wizard.PreviousButton.Title = "Previous"; + + this.wizard.NextButton.Visible = true; + this.wizard.NextButton.Enabled = this.wizard.CurrentPageIndex < (this.wizard.Pages - 1); + this.wizard.NextButton.Title = "Next"; + this.wizard.Render(this.RenderPage_Action); + + ImGui.End(); } - private void RenderPage_Action() => this.wizardPages[this.wizard.CurrentPageIndex].Render(); + ImGui.PopStyleVar(); + ImGui.PopStyleColor(); + } + + private void RenderPage_Action() => this.wizardPages[this.wizard.CurrentPageIndex].Render(); - private void CancelButton_Action() => this.wizardPages[this.wizard.CurrentPageIndex].Cancel(); + private void CancelButton_Action() => this.wizardPages[this.wizard.CurrentPageIndex].Cancel(); - private void ValidateButton_Action() => this.wizardPages[this.wizard.CurrentPageIndex].Validate(); + private void ValidateButton_Action() => this.wizardPages[this.wizard.CurrentPageIndex].Validate(); - private bool PreviousButton_Action(int newPageIndex) => this.wizardPages[this.wizard.CurrentPageIndex].Previous(this.wizardPages[newPageIndex]); + private bool PreviousButton_Action(int newPageIndex) => this.wizardPages[this.wizard.CurrentPageIndex].Previous(this.wizardPages[newPageIndex]); - private bool NextButton_Action(int newPageIndex) + private bool NextButton_Action(int newPageIndex) + { + bool value = this.wizardPages[this.wizard.CurrentPageIndex].Next(this.wizardPages[newPageIndex]); + if (value) { - bool value = this.wizardPages[this.wizard.CurrentPageIndex].Next(this.wizardPages[newPageIndex]); - if (value) - { - this.wizardPages[newPageIndex].Initialize(); - } - - return value; + this.wizardPages[newPageIndex].Initialize(); } + + return value; } } diff --git a/tests/ImageSharp.Textures.Tests/Enums/TestTextureFormat.cs b/tests/ImageSharp.Textures.Tests/Enums/TestTextureFormat.cs index f737c793..def8dc7b 100644 --- a/tests/ImageSharp.Textures.Tests/Enums/TestTextureFormat.cs +++ b/tests/ImageSharp.Textures.Tests/Enums/TestTextureFormat.cs @@ -1,23 +1,22 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.Tests.Enums +namespace SixLabors.ImageSharp.Textures.Tests.Enums; + +public enum TestTextureFormat { - public enum TestTextureFormat - { - /// - /// DirectDraw Surface Textures. - /// - Dds, + /// + /// DirectDraw Surface Textures. + /// + Dds, - /// - /// Khronos Texture, version 1. - /// - Ktx, + /// + /// Khronos Texture, version 1. + /// + Ktx, - /// - /// Khronos Texture, version 2. - /// - Ktx2, - } + /// + /// Khronos Texture, version 2. + /// + Ktx2, } diff --git a/tests/ImageSharp.Textures.Tests/Enums/TestTextureTool.cs b/tests/ImageSharp.Textures.Tests/Enums/TestTextureTool.cs index 169d4daf..b74a8972 100644 --- a/tests/ImageSharp.Textures.Tests/Enums/TestTextureTool.cs +++ b/tests/ImageSharp.Textures.Tests/Enums/TestTextureTool.cs @@ -1,28 +1,27 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.Tests.Enums +namespace SixLabors.ImageSharp.Textures.Tests.Enums; + +public enum TestTextureTool { - public enum TestTextureTool - { - /// - /// TexConv. - /// - TexConv, + /// + /// TexConv. + /// + TexConv, - /// - /// NvDxt. - /// - NvDxt, + /// + /// NvDxt. + /// + NvDxt, - /// - /// ToKtx. - /// - ToKtx, + /// + /// ToKtx. + /// + ToKtx, - /// - /// The PVR tex tool cli. - /// - PvrTexToolCli - } + /// + /// The PVR tex tool cli. + /// + PvrTexToolCli } diff --git a/tests/ImageSharp.Textures.Tests/Enums/TestTextureType.cs b/tests/ImageSharp.Textures.Tests/Enums/TestTextureType.cs index d550f166..40858bac 100644 --- a/tests/ImageSharp.Textures.Tests/Enums/TestTextureType.cs +++ b/tests/ImageSharp.Textures.Tests/Enums/TestTextureType.cs @@ -1,23 +1,22 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.Tests.Enums +namespace SixLabors.ImageSharp.Textures.Tests.Enums; + +public enum TestTextureType { - public enum TestTextureType - { - /// - /// FlatTexture - /// - Flat, + /// + /// FlatTexture + /// + Flat, - /// - /// VolumeTexture - /// - Volume, + /// + /// VolumeTexture + /// + Volume, - /// - /// Cubemap - /// - Cubemap, - } + /// + /// Cubemap + /// + Cubemap, } diff --git a/tests/ImageSharp.Textures.Tests/Formats/Astc/BitOperationsTests.cs b/tests/ImageSharp.Textures.Tests/Formats/Astc/BitOperationsTests.cs new file mode 100644 index 00000000..d719c4bf --- /dev/null +++ b/tests/ImageSharp.Textures.Tests/Formats/Astc/BitOperationsTests.cs @@ -0,0 +1,123 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using AwesomeAssertions; +using SixLabors.ImageSharp.Textures.Astc.Core; + +namespace SixLabors.ImageSharp.Textures.Tests.Formats.Astc; + +public class BitOperationsTests +{ + [Fact] + public void GetBits_UInt128WithLowBits_ShouldExtractCorrectly() + { + UInt128 value = new(0x1234567890ABCDEF, 0xFEDCBA0987654321); + + UInt128 result = BitOperations.GetBits(value, 0, 8); + + result.Low().Should().Be(0x21UL); + } + + [Fact] + public void GetBits_UInt128WithZeroLength_ShouldReturnZero() + { + UInt128 value = new(0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF); + + UInt128 result = BitOperations.GetBits(value, 0, 0); + + result.Should().Be(UInt128.Zero); + } + + [Fact] + public void GetBits_ULongWithLowBits_ShouldExtractCorrectly() + { + ulong value = 0xFEDCBA0987654321; + + ulong result = BitOperations.GetBits(value, 0, 8); + + result.Should().Be(0x21UL); + } + + [Fact] + public void GetBits_ULongWithZeroLength_ShouldReturnZero() + { + ulong value = 0xFFFFFFFFFFFFFFFF; + + ulong result = BitOperations.GetBits(value, 0, 0); + + result.Should().Be(0UL); + } + + [Theory] + [InlineData(0, 0)] + [InlineData(10, 20)] + [InlineData(128, 255)] + [InlineData(255, 128)] + [InlineData(64, 64)] + public void TransferPrecision_WithSameInput_ShouldBeDeterministic(int inputA, int inputB) + { + (int a1, int b1) = BitOperations.TransferPrecision(inputA, inputB); + (int a2, int b2) = BitOperations.TransferPrecision(inputA, inputB); + + a1.Should().Be(a2); + b1.Should().Be(b2); + } + + [Fact] + public void TransferPrecision_WithAllValidByteInputs_ShouldNotThrow() + { + for (int a = byte.MinValue; a <= byte.MaxValue; a++) + { + for (int b = byte.MinValue; b <= byte.MaxValue; b++) + { + Action action = () => BitOperations.TransferPrecision(a, b); + action.Should().NotThrow(); + } + } + } + + [Theory] + [InlineData(0, 0)] + [InlineData(5, 10)] + [InlineData(10, 255)] + [InlineData(31, 128)] + [InlineData(-32, 200)] + [InlineData(-1, 100)] + public void TransferPrecisionInverse_WithSameInput_ShouldBeDeterministic(int inputA, int inputB) + { + (int a1, int b1) = BitOperations.TransferPrecisionInverse(inputA, inputB); + (int a2, int b2) = BitOperations.TransferPrecisionInverse(inputA, inputB); + + a1.Should().Be(a2); + b1.Should().Be(b2); + } + + [Theory] + [InlineData(-33, 128)] // a too small + [InlineData(32, 128)] // a too large + [InlineData(0, -1)] // b too small + [InlineData(0, 256)] // b too large + public void TransferPrecisionInverse_WithInvalidInput_ShouldThrowArgumentOutOfRangeException(int a, int b) + { + Action action = () => BitOperations.TransferPrecisionInverse(a, b); + + action.Should().Throw(); + } + + [Theory] + [InlineData(0, 0)] + [InlineData(10, 20)] + [InlineData(31, 255)] + [InlineData(-32, 128)] + [InlineData(-1, 200)] + public void TransferPrecision_AfterInverse_ShouldReturnOriginalValues(int originalA, int originalB) + { + (int encodedA, int encodedB) = BitOperations.TransferPrecisionInverse(originalA, originalB); + + // Apply regular to decode + (int decodedA, int decodedB) = BitOperations.TransferPrecision(encodedA, encodedB); + + decodedA.Should().Be(originalA); + decodedB.Should().Be(originalB); + } +} diff --git a/tests/ImageSharp.Textures.Tests/Formats/Astc/BitStreamTests.cs b/tests/ImageSharp.Textures.Tests/Formats/Astc/BitStreamTests.cs new file mode 100644 index 00000000..1e7dd250 --- /dev/null +++ b/tests/ImageSharp.Textures.Tests/Formats/Astc/BitStreamTests.cs @@ -0,0 +1,193 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using AwesomeAssertions; +using SixLabors.ImageSharp.Textures.Astc.IO; + +namespace SixLabors.ImageSharp.Textures.Tests.Formats.Astc; + +public class BitStreamTests +{ + [Fact] + public void Constructor_WithBitsAndLength_ShouldInitializeCorrectly() + { + BitStream stream = new(0b1010101010101010UL, 32); + + stream.Bits.Should().Be(32); + } + + [Fact] + public void Constructor_WithoutParameters_ShouldInitializeEmpty() + { + BitStream stream = default; + + stream.Bits.Should().Be(0); + } + + [Fact] + public void TryGetBits_WithSingleBitFromZero_ShouldReturnZero() + { + BitStream stream = new(0UL, 1); + + bool success = stream.TryGetBits(1, out uint bits); + + success.Should().BeTrue(); + bits.Should().Be(0U); + } + + [Fact] + public void TryGetBits_StreamEnd_ShouldReturnFalse() + { + BitStream stream = new(0UL, 1); + stream.TryGetBits(1, out _); + + bool success = stream.TryGetBits(1, out uint _); + + success.Should().BeFalse(); + } + + [Fact] + public void TryGetBits_WithAlternatingBitPattern_ShouldExtractCorrectly() + { + BitStream stream = new(0b1010101010101010UL, 32); + + stream.TryGetBits(1, out uint bits1).Should().BeTrue(); + bits1.Should().Be(0U); + + stream.TryGetBits(3, out uint bits2).Should().BeTrue(); + bits2.Should().Be(0b101U); + + stream.TryGetBits(8, out uint bits3).Should().BeTrue(); + bits3.Should().Be(0b10101010U); + + stream.Bits.Should().Be(20); + + stream.TryGetBits(20, out uint bits4).Should().BeTrue(); + bits4.Should().Be(0b1010U); + stream.Bits.Should().Be(0); + } + + [Fact] + public void TryGetBits_With64BitsOfOnes_ShouldReturnAllOnes() + { + const ulong allBits = 0xFFFFFFFFFFFFFFFFUL; + BitStream stream = new(allBits, 64); + + // Check initial state + stream.Bits.Should().Be(64); + + bool success = stream.TryGetBits(64, out ulong bits); + + success.Should().BeTrue(); + bits.Should().Be(allBits); + stream.Bits.Should().Be(0); + } + + [Fact] + public void TryGetBits_With40BitsFromFullBits_ShouldReturnLower40Bits() + { + const ulong allBits = 0xFFFFFFFFFFFFFFFFUL; + const ulong expected40Bits = 0x000000FFFFFFFFFFUL; + BitStream stream = new(allBits, 64); + + // Check initial state + stream.Bits.Should().Be(64); + + bool success = stream.TryGetBits(40, out ulong bits); + + success.Should().BeTrue(); + bits.Should().Be(expected40Bits); + stream.Bits.Should().Be(24); + } + + [Fact] + public void TryGetBits_WithZeroBits_ShouldReturnZeroAndNotConsume() + { + const ulong allBits = 0xFFFFFFFFFFFFFFFFUL; + const ulong expected40Bits = 0x000000FFFFFFFFFFUL; + BitStream stream = new(allBits, 32); + + stream.TryGetBits(0, out ulong bits1).Should().BeTrue(); + bits1.Should().Be(0UL); + + stream.TryGetBits(32, out ulong bits2).Should().BeTrue(); + bits2.Should().Be(expected40Bits & 0xFFFFFFFFUL); + + stream.TryGetBits(0, out ulong bits3).Should().BeTrue(); + bits3.Should().Be(0UL); + stream.Bits.Should().Be(0); + } + + [Fact] + public void PutBits_WithSmallValues_ShouldAccumulateCorrectly() + { + BitStream stream = default; + + stream.PutBits(0U, 1); + stream.PutBits(0b11U, 2); + + stream.Bits.Should().Be(3); + stream.TryGetBits(3, out uint bits).Should().BeTrue(); + bits.Should().Be(0b110U); + } + + [Fact] + public void PutBits_With64BitsOfOnes_ShouldStoreCorrectly() + { + const ulong allBits = 0xFFFFFFFFFFFFFFFFUL; + BitStream stream = default; + + stream.PutBits(allBits, 64); + + stream.Bits.Should().Be(64); + stream.TryGetBits(64, out ulong bits).Should().BeTrue(); + bits.Should().Be(allBits); + stream.Bits.Should().Be(0); + } + + [Fact] + public void PutBits_With40BitsOfOnes_ShouldMaskTo40Bits() + { + const ulong allBits = 0xFFFFFFFFFFFFFFFFUL; + const ulong expected40Bits = 0x000000FFFFFFFFFFUL; + BitStream stream = default; + + stream.PutBits(allBits, 40); + + stream.TryGetBits(40, out ulong bits).Should().BeTrue(); + bits.Should().Be(expected40Bits); + stream.Bits.Should().Be(0); + } + + [Fact] + public void PutBits_WithZeroBitsInterspersed_ShouldReturnValue() + { + const ulong allBits = 0xFFFFFFFFFFFFFFFFUL; + const ulong expected40Bits = 0x000000FFFFFFFFFFUL; + BitStream stream = default; + + stream.PutBits(0U, 0); + stream.PutBits((uint)(allBits & 0xFFFFFFFFUL), 32); + stream.PutBits(0U, 0); + + stream.TryGetBits(32, out ulong bits).Should().BeTrue(); + bits.Should().Be(expected40Bits & 0xFFFFFFFFUL); + stream.Bits.Should().Be(0); + } + + [Fact] + public void PutBits_ThenGetBits_ShouldReturnValue() + { + BitStream stream = default; + const uint value1 = 0b101; + const uint value2 = 0b11001100; + + stream.PutBits(value1, 3); + stream.PutBits(value2, 8); + + stream.TryGetBits(3, out uint retrieved1).Should().BeTrue(); + retrieved1.Should().Be(value1); + stream.TryGetBits(8, out uint retrieved2).Should().BeTrue(); + retrieved2.Should().Be(value2); + } +} diff --git a/tests/ImageSharp.Textures.Tests/Formats/Astc/CodecTests.cs b/tests/ImageSharp.Textures.Tests/Formats/Astc/CodecTests.cs new file mode 100644 index 00000000..ae4b7730 --- /dev/null +++ b/tests/ImageSharp.Textures.Tests/Formats/Astc/CodecTests.cs @@ -0,0 +1,137 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using AwesomeAssertions; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Textures.Astc; +using SixLabors.ImageSharp.Textures.Astc.Core; +using SixLabors.ImageSharp.Textures.Astc.IO; +using SixLabors.ImageSharp.Textures.Astc.TexelBlock; +using SixLabors.ImageSharp.Textures.Tests.TestUtilities.ImageComparison; + +namespace SixLabors.ImageSharp.Textures.Tests.Formats.Astc; + +#nullable enable +public class CodecTests +{ + [Fact] + public void ASTCDecompressToRGBA_WithZeroWidth_ShouldReturnEmpty() + { + byte[] data = new byte[256]; + const int height = 16; + + Span result = AstcDecoder.DecompressImage(data, 0, height, FootprintType.Footprint4x4); + + result.ToArray().Should().BeEmpty(); + } + + [Fact] + public void ASTCDecompressToRGBA_WithZeroHeight_ShouldReturnEmpty() + { + byte[] data = new byte[256]; + const int width = 16; + + Span result = AstcDecoder.DecompressImage(data, width, 0, FootprintType.Footprint4x4); + + result.ToArray().Should().BeEmpty(); + } + + [Fact] + public void ASTCDecompressToRGBA_WithDataSizeNotMultipleOfBlockSize_ShouldReturnEmpty() + { + byte[] data = new byte[256]; + const int width = 16; + const int height = 16; + byte[] invalidData = data.AsSpan(0, data.Length - 1).ToArray(); + + Span result = AstcDecoder.DecompressImage(invalidData, width, height, FootprintType.Footprint4x4); + + result.ToArray().Should().BeEmpty(); + } + + [Fact] + public void ASTCDecompressToRGBA_WithMismatchedBlockCount_ShouldReturnEmpty() + { + byte[] data = new byte[256]; + const int width = 16; + const int height = 16; + byte[] mismatchedData = data.AsSpan(0, data.Length - PhysicalBlock.SizeInBytes).ToArray(); + + Span result = AstcDecoder.DecompressImage(mismatchedData, width, height, FootprintType.Footprint4x4); + + result.ToArray().Should().BeEmpty(); + } + + [Theory] + [InlineData("atlas_small_4x4", FootprintType.Footprint4x4, 256, 256)] + [InlineData("atlas_small_5x5", FootprintType.Footprint5x5, 256, 256)] + [InlineData("atlas_small_6x6", FootprintType.Footprint6x6, 256, 256)] + [InlineData("atlas_small_8x8", FootprintType.Footprint8x8, 256, 256)] + public void ASTCDecompressToRGBA_WithValidData_ShouldMatchExpected( + string imageName, + FootprintType footprintType, + int width, + int height) + { + byte[] astcData = TestFile.Create(Path.Combine(TestImages.Astc.InputFolder, imageName + ".astc")).Bytes[16..]; + Footprint footprint = Footprint.FromFootprintType(footprintType); + int blockWidth = footprint.Width; + int blockHeight = footprint.Height; + int blocksWide = (width + blockWidth - 1) / blockWidth; + int blocksHigh = (height + blockHeight - 1) / blockHeight; + int expectedBlockCount = blocksWide * blocksHigh; + + // Check ASTC data structure + (astcData.Length % PhysicalBlock.SizeInBytes).Should().Be(0, "astc byte length must be multiple of block size"); + (astcData.Length / PhysicalBlock.SizeInBytes).Should().Be(expectedBlockCount, $"ASTC block count should match expected"); + + // Verify all blocks can be unpacked + for (int i = 0; i < astcData.Length; i += PhysicalBlock.SizeInBytes) + { + byte[] block = astcData.AsSpan(i, PhysicalBlock.SizeInBytes).ToArray(); + UInt128 bits = new(BitConverter.ToUInt64(block, 8), BitConverter.ToUInt64(block, 0)); + BlockInfo info = BlockInfo.Decode(bits); + LogicalBlock? logicalBlock = LogicalBlock.UnpackLogicalBlock(footprint, bits, in info); + + logicalBlock.Should().NotBeNull("all blocks should unpack successfully"); + } + + byte[] decodedPixels = AstcDecoder.DecompressImage(astcData, width, height, footprintType).ToArray(); + using Image actualImage = Image.LoadPixelData(decodedPixels, width, height); + actualImage.Mutate(x => x.Flip(FlipMode.Vertical)); + + string expectedImagePath = TestFile.GetInputFileFullPath(Path.Combine(TestImages.Astc.ExpectedFolder, imageName + ".bmp")); + using Image expectedImage = Image.Load(expectedImagePath); + ImageComparer.TolerantPercentage(0.1f).VerifySimilarity(expectedImage, actualImage); + } + + [Theory] + [InlineData("atlas_small_4x4", FootprintType.Footprint4x4, 256, 256)] + [InlineData("atlas_small_5x5", FootprintType.Footprint5x5, 256, 256)] + [InlineData("atlas_small_6x6", FootprintType.Footprint6x6, 256, 256)] + [InlineData("atlas_small_8x8", FootprintType.Footprint8x8, 256, 256)] + public void DecompressToImage_WithAstcFile_ShouldMatchExpected( + string imageName, + FootprintType footprint, + int width, + int height) + { + string astcPath = TestFile.GetInputFileFullPath(Path.Combine(TestImages.Astc.InputFolder, imageName + ".astc")); + byte[] astcBytes = File.ReadAllBytes(astcPath); + AstcFile file = AstcFile.FromMemory(astcBytes); + + // Check file header + file.Footprint.Type.Should().Be(footprint); + file.Width.Should().Be(width); + file.Height.Should().Be(height); + + byte[] decodedPixels = AstcDecoder.DecompressImage(file).ToArray(); + using Image actualImage = Image.LoadPixelData(decodedPixels, width, height); + actualImage.Mutate(x => x.Flip(FlipMode.Vertical)); + + string expectedImagePath = TestFile.GetInputFileFullPath(Path.Combine(TestImages.Astc.ExpectedFolder, imageName + ".bmp")); + using Image expectedImage = Image.Load(expectedImagePath); + ImageComparer.TolerantPercentage(0.1f).VerifySimilarity(expectedImage, actualImage); + } +} diff --git a/tests/ImageSharp.Textures.Tests/Formats/Astc/EndpointCodecTests.cs b/tests/ImageSharp.Textures.Tests/Formats/Astc/EndpointCodecTests.cs new file mode 100644 index 00000000..0aaea912 --- /dev/null +++ b/tests/ImageSharp.Textures.Tests/Formats/Astc/EndpointCodecTests.cs @@ -0,0 +1,384 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Buffers.Binary; +using AwesomeAssertions; +using SixLabors.ImageSharp.Textures.Astc.ColorEncoding; +using SixLabors.ImageSharp.Textures.Astc.Core; +using SixLabors.ImageSharp.Textures.Astc.TexelBlock; + +namespace SixLabors.ImageSharp.Textures.Tests.Formats.Astc; + +public class EndpointCodecTests +{ + [Theory] + [InlineData(EndpointEncodingMode.DirectLuma)] + [InlineData(EndpointEncodingMode.DirectLumaAlpha)] + [InlineData(EndpointEncodingMode.BaseScaleRgb)] + [InlineData(EndpointEncodingMode.BaseScaleRgba)] + [InlineData(EndpointEncodingMode.DirectRbg)] + [InlineData(EndpointEncodingMode.DirectRgba)] + internal void EncodeColorsForMode_WithVariousRanges_ShouldProduceValidQuantizedValues(EndpointEncodingMode mode) + { + RgbaColor low = new(0, 0, 0, 0); + RgbaColor high = new(255, 255, 255, 255); + + for (int quantRange = 5; quantRange < 256; quantRange++) + { + List values = []; + EndpointEncoder.EncodeColorsForMode(low, high, quantRange, mode, out ColorEndpointMode _, values); + + // Assert value count matches expected + values.Should().HaveCount(mode.GetValuesCount()); + + // Assert all values are within quantization range + values.Should().AllSatisfy(v => v.Should().BeInRange(0, quantRange)); + } + } + + [Theory] + [InlineData(EndpointEncodingMode.DirectLuma)] + [InlineData(EndpointEncodingMode.DirectLumaAlpha)] + [InlineData(EndpointEncodingMode.BaseScaleRgb)] + [InlineData(EndpointEncodingMode.BaseScaleRgba)] + [InlineData(EndpointEncodingMode.DirectRbg)] + [InlineData(EndpointEncodingMode.DirectRgba)] + internal void EncodeDecodeColors_WithBlackAndWhite_ShouldPreserveColors(EndpointEncodingMode mode) + { + RgbaColor white = new(255, 255, 255, 255); + RgbaColor black = new(0, 0, 0, 255); + + for (int quantRange = 5; quantRange < 256; ++quantRange) + { + (RgbaColor low, RgbaColor high) = EncodeAndDecodeColors(white, black, quantRange, mode); + + (low == white).Should().BeTrue(); + (high == black).Should().BeTrue(); + } + } + + [Fact] + public void UsesBlueContract_WithDirectModes_ShouldDetectCorrectly() + { + List values = [132, 127, 116, 112, 183, 180, 31, 22]; + + EndpointEncoder.UsesBlueContract(255, ColorEndpointMode.LdrRgbDirect, values).Should().BeTrue(); + EndpointEncoder.UsesBlueContract(255, ColorEndpointMode.LdrRgbaDirect, values).Should().BeTrue(); + } + + [Fact] + public void UsesBlueContract_WithOffsetModes_ShouldDetectBasedOnBitFlags() + { + List baseValues = [132, 127, 116, 112, 183, 180, 31, 22]; + + List valuesClearedBit6 = [.. baseValues]; + valuesClearedBit6[1] &= 0xBF; + valuesClearedBit6[3] &= 0xBF; + valuesClearedBit6[5] &= 0xBF; + valuesClearedBit6[7] &= 0xBF; + + EndpointEncoder.UsesBlueContract(255, ColorEndpointMode.LdrRgbBaseOffset, valuesClearedBit6).Should().BeFalse(); + EndpointEncoder.UsesBlueContract(255, ColorEndpointMode.LdrRgbaBaseOffset, valuesClearedBit6).Should().BeFalse(); + + List valuesSetBit6 = [.. baseValues]; + valuesSetBit6[1] |= 0x40; + valuesSetBit6[3] |= 0x40; + valuesSetBit6[5] |= 0x40; + valuesSetBit6[7] |= 0x40; + + EndpointEncoder.UsesBlueContract(255, ColorEndpointMode.LdrRgbBaseOffset, valuesSetBit6).Should().BeTrue(); + EndpointEncoder.UsesBlueContract(255, ColorEndpointMode.LdrRgbaBaseOffset, valuesSetBit6).Should().BeTrue(); + } + + [Fact] + public void EncodeColorsForMode_WithRgbDirectAndSpecificPairs_ShouldUseBlueContract() + { + (RgbaColor, RgbaColor)[] pairs = + [ + (new RgbaColor(22, 18, 30, 59), new RgbaColor(162, 148, 155, 59)), + (new RgbaColor(22, 30, 27, 36), new RgbaColor(228, 221, 207, 36)), + (new RgbaColor(54, 60, 55, 255), new RgbaColor(23, 30, 27, 255)) + ]; + + const int endpointRange = 31; + + foreach ((RgbaColor low, RgbaColor high) in pairs) + { + List values = []; + EndpointEncoder.EncodeColorsForMode(low, high, endpointRange, EndpointEncodingMode.DirectRbg, out ColorEndpointMode astcMode, values); + + EndpointEncoder.UsesBlueContract(endpointRange, astcMode, values).Should().BeTrue(); + } + } + + [Fact] + public void EncodeDecodeColors_WithLumaDirect_ShouldProduceLumaValues() + { + EndpointEncodingMode mode = EndpointEncodingMode.DirectLuma; + + (RgbaColor low, RgbaColor high) = EncodeAndDecodeColors( + new RgbaColor(247, 248, 246, 255), + new RgbaColor(2, 3, 1, 255), + 255, + mode); + + (low == new RgbaColor(247, 247, 247, 255)).Should().BeTrue(); + (high == new RgbaColor(2, 2, 2, 255)).Should().BeTrue(); + + (RgbaColor low2, RgbaColor high2) = EncodeAndDecodeColors( + new RgbaColor(80, 80, 50, 255), + new RgbaColor(99, 255, 6, 255), + 255, + mode); + + (low2 == new RgbaColor(70, 70, 70, 255)).Should().BeTrue(); + (high2 == new RgbaColor(120, 120, 120, 255)).Should().BeTrue(); + + (RgbaColor low3, RgbaColor high3) = EncodeAndDecodeColors( + new RgbaColor(247, 248, 246, 255), + new RgbaColor(2, 3, 1, 255), + 15, + mode); + + (low3 == new RgbaColor(255, 255, 255, 255)).Should().BeTrue(); + (high3 == new RgbaColor(0, 0, 0, 255)).Should().BeTrue(); + + (RgbaColor low4, RgbaColor high4) = EncodeAndDecodeColors( + new RgbaColor(64, 127, 192, 255), + new RgbaColor(0, 0, 0, 255), + 63, + mode); + + (low4 == new RgbaColor(130, 130, 130, 255)).Should().BeTrue(); + (high4 == new RgbaColor(0, 0, 0, 255)).Should().BeTrue(); + } + + [Fact] + public void EncodeDecodeColors_WithLumaAlphaDirect_ShouldPreserveLumaAndAlpha() + { + EndpointEncodingMode mode = EndpointEncodingMode.DirectLumaAlpha; + + // Grey with varying alpha + (RgbaColor low, RgbaColor high) = EncodeAndDecodeColors( + new RgbaColor(64, 127, 192, 127), + new RgbaColor(0, 0, 0, 20), + 63, + mode); + + ((low == new RgbaColor(130, 130, 130, 125)) || + low.IsCloseTo(new RgbaColor(130, 130, 130, 125), 1)).Should().BeTrue(); + ((high == new RgbaColor(0, 0, 0, 20)) || + high.IsCloseTo(new RgbaColor(0, 0, 0, 20), 1)).Should().BeTrue(); + + // Different alpha values + (RgbaColor low2, RgbaColor high2) = EncodeAndDecodeColors( + new RgbaColor(247, 248, 246, 250), + new RgbaColor(2, 3, 1, 172), + 255, + mode); + + (low2 == new RgbaColor(247, 247, 247, 250)).Should().BeTrue(); + (high2 == new RgbaColor(2, 2, 2, 172)).Should().BeTrue(); + } + + [Fact] + public void EncodeDecodeColors_WithRgbDirectAndRandomColors_ShouldPreserveColors() + { + EndpointEncodingMode mode = EndpointEncodingMode.DirectRbg; + Random random = new(unchecked((int)0xdeadbeef)); + + for (int i = 0; i < 100; ++i) + { + RgbaColor low = new(random.Next(0, 256), random.Next(0, 256), random.Next(0, 256), 255); + RgbaColor high = new(random.Next(0, 256), random.Next(0, 256), random.Next(0, 256), 255); + (RgbaColor low1, RgbaColor high1) = EncodeAndDecodeColors(low, high, 255, mode); + + (low1 == low).Should().BeTrue(); + (high1 == high).Should().BeTrue(); + } + } + + [Fact] + public void EncodeDecodeColors_WithRgbDirectAndSpecificColors_ShouldMatchExpected() + { + EndpointEncodingMode mode = EndpointEncodingMode.DirectRbg; + + (RgbaColor low, RgbaColor high) = EncodeAndDecodeColors( + new RgbaColor(64, 127, 192, 255), + new RgbaColor(0, 0, 0, 255), + 63, + mode); + + (low == new RgbaColor(65, 125, 190, 255)).Should().BeTrue(); + (high == new RgbaColor(0, 0, 0, 255)).Should().BeTrue(); + + (RgbaColor low2, RgbaColor high2) = EncodeAndDecodeColors( + new RgbaColor(0, 0, 0, 255), + new RgbaColor(64, 127, 192, 255), + 63, + mode); + + (low2 == new RgbaColor(0, 0, 0, 255)).Should().BeTrue(); + (high2 == new RgbaColor(65, 125, 190, 255)).Should().BeTrue(); + } + + [Fact] + public void EncodeDecodeColors_WithRgbBaseScaleAndIdenticalColors_ShouldBeCloseToOriginal() + { + EndpointEncodingMode mode = EndpointEncodingMode.BaseScaleRgb; + Random random = new(unchecked((int)0xdeadbeef)); + + for (int i = 0; i < 100; ++i) + { + RgbaColor color = new(random.Next(0, 256), random.Next(0, 256), random.Next(0, 256), 255); + (RgbaColor low, RgbaColor high) = EncodeAndDecodeColors(color, color, 255, mode); + + low.IsCloseTo(color, 1).Should().BeTrue(); + high.IsCloseTo(color, 1).Should().BeTrue(); + } + } + + [Fact] + public void EncodeDecodeColors_WithRgbBaseScaleAndDifferentColors_ShouldMatchExpected() + { + EndpointEncodingMode mode = EndpointEncodingMode.BaseScaleRgb; + RgbaColor low = new(20, 4, 40, 255); + RgbaColor high = new(80, 16, 160, 255); + + (RgbaColor decodedLow, RgbaColor decodedHigh) = EncodeAndDecodeColors(low, high, 255, mode); + decodedLow.IsCloseTo(low, 0).Should().BeTrue(); + decodedHigh.IsCloseTo(high, 0).Should().BeTrue(); + + (RgbaColor low2, RgbaColor high2) = EncodeAndDecodeColors(low, high, 127, mode); + low2.IsCloseTo(low, 1).Should().BeTrue(); + high2.IsCloseTo(high, 1).Should().BeTrue(); + } + + internal static TheoryData RgbBaseOffsetColorPairs() => new() + { + { new RgbaColor(80, 16, 112, 255), new RgbaColor(87, 18, 132, 255) }, + { new RgbaColor(80, 74, 82, 255), new RgbaColor(90, 92, 110, 255) }, + { new RgbaColor(0, 0, 0, 255), new RgbaColor(2, 2, 2, 255) }, + }; + + [Theory] +#pragma warning disable xUnit1016 // MemberData is internal because RgbaColor is internal + [MemberData(nameof(RgbBaseOffsetColorPairs))] +#pragma warning restore xUnit1016 + internal void DecodeColorsForMode_WithRgbBaseOffset_AndSpecificColorPairs_ShouldDecodeCorrectly( + RgbaColor expectedLow, RgbaColor expectedHigh) + { + int[] values = EncodeRgbBaseOffset(expectedLow, expectedHigh); + (RgbaColor decLow, RgbaColor decHigh) = EndpointCodec.DecodeColorsForMode(values, 255, ColorEndpointMode.LdrRgbBaseOffset); + + (decLow == expectedLow).Should().BeTrue(); + (decHigh == expectedHigh).Should().BeTrue(); + } + + [Fact] + public void DecodeColorsForMode_WithRgbBaseOffset_AndIdenticalColors_ShouldDecodeCorrectly() + { + Random random = new(unchecked((int)0xdeadbeef)); + + for (int i = 0; i < 100; ++i) + { + int r = random.Next(0, 256); + int g = random.Next(0, 256); + int b = random.Next(0, 256); + + // Ensure even channels (reference test skips odd) + if (((r | g | b) & 1) != 0) + { + continue; + } + + RgbaColor color = new(r, g, b, 255); + int[] values = EncodeRgbBaseOffset(color, color); + (RgbaColor decLow, RgbaColor decHigh) = EndpointCodec.DecodeColorsForMode(values, 255, ColorEndpointMode.LdrRgbBaseOffset); + + (decLow == color).Should().BeTrue(); + (decHigh == color).Should().BeTrue(); + } + } + + private static int[] EncodeRgbBaseOffset(RgbaColor low, RgbaColor high) + { + List values = []; + for (int i = 0; i < 3; ++i) + { + bool isLarge = low[i] >= 128; + values.Add((low[i] * 2) & 0xFF); + int diff = (high[i] - low[i]) * 2; + if (isLarge) + { + diff |= 0x80; + } + + values.Add(diff); + } + + return [.. values]; + } + + [Fact] + public void DecodeCheckerboard_ShouldDecodeToGrayscaleEndpoints() + { + string astcFilePath = TestFile.GetInputFileFullPath(Path.Combine(TestImages.Astc.InputFolder, "checkerboard.astc")); + byte[] astcData = File.ReadAllBytes(astcFilePath); + + int blocksDecoded = 0; + + for (int i = 0; i < astcData.Length; i += PhysicalBlock.SizeInBytes) + { + // Read block bytes + UInt128 blockData = BinaryPrimitives.ReadUInt128LittleEndian(astcData.AsSpan(i, PhysicalBlock.SizeInBytes)); + PhysicalBlock physicalBlock = PhysicalBlock.Create(blockData); + + // Unpack to intermediate block + IntermediateBlock.IntermediateBlockData? intermediateBlock = IntermediateBlock.UnpackIntermediateBlock(physicalBlock); + intermediateBlock.Should().NotBeNull("checkerboard blocks should not be void extent"); + IntermediateBlock.IntermediateBlockData ib = intermediateBlock!.Value; + + // Verify endpoints exist + ib.EndpointCount.Should().BeGreaterThan(0, "block should have endpoints"); + + int colorRange = IntermediateBlock.EndpointRangeForBlock(ib); + colorRange.Should().BeGreaterThan(0, "color range should be valid"); + + // Check all endpoint pairs decode successfully to grayscale colors + for (int ep = 0; ep < ib.EndpointCount; ep++) + { + IntermediateBlock.IntermediateEndpointData endpoints = ib.Endpoints[ep]; + ReadOnlySpan colorSpan = ((ReadOnlySpan)endpoints.Colors)[..endpoints.ColorCount]; + (RgbaColor low, RgbaColor high) = EndpointCodec.DecodeColorsForMode( + colorSpan, + colorRange, + endpoints.Mode); + + // Assert - Checkerboard should produce grayscale colors (R == G == B) + low.R.Should().Be(low.G, $"block {i} low endpoint should be grayscale"); + low.G.Should().Be(low.B, $"block {i} low endpoint should be grayscale"); + high.R.Should().Be(high.G, $"block {i} high endpoint should be grayscale"); + high.G.Should().Be(high.B, $"block {i} high endpoint should be grayscale"); + } + + blocksDecoded++; + } + + // Verify we decoded a reasonable number of blocks + blocksDecoded.Should().BeGreaterThan(0, "should have decoded at least one block"); + } + + private static (RgbaColor Low, RgbaColor High) EncodeAndDecodeColors( + RgbaColor low, + RgbaColor high, + int quantRange, + EndpointEncodingMode mode) + { + List values = []; + bool needsSwap = EndpointEncoder.EncodeColorsForMode(low, high, quantRange, mode, out ColorEndpointMode astcMode, values); + (RgbaColor decLow, RgbaColor decHigh) = EndpointCodec.DecodeColorsForMode(values.ToArray(), quantRange, astcMode); + + return needsSwap ? (decHigh, decLow) : (decLow, decHigh); + } +} diff --git a/tests/ImageSharp.Textures.Tests/Formats/Astc/FootprintTests.cs b/tests/ImageSharp.Textures.Tests/Formats/Astc/FootprintTests.cs new file mode 100644 index 00000000..df95f691 --- /dev/null +++ b/tests/ImageSharp.Textures.Tests/Formats/Astc/FootprintTests.cs @@ -0,0 +1,83 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using AwesomeAssertions; +using SixLabors.ImageSharp.Textures.Astc.Core; + +namespace SixLabors.ImageSharp.Textures.Tests.Formats.Astc; + +public class FootprintTests +{ + [Theory] + [InlineData(FootprintType.Footprint4x4, 4, 4)] + [InlineData(FootprintType.Footprint5x4, 5, 4)] + [InlineData(FootprintType.Footprint5x5, 5, 5)] + [InlineData(FootprintType.Footprint6x5, 6, 5)] + [InlineData(FootprintType.Footprint6x6, 6, 6)] + [InlineData(FootprintType.Footprint8x5, 8, 5)] + [InlineData(FootprintType.Footprint8x6, 8, 6)] + [InlineData(FootprintType.Footprint8x8, 8, 8)] + [InlineData(FootprintType.Footprint10x5, 10, 5)] + [InlineData(FootprintType.Footprint10x6, 10, 6)] + [InlineData(FootprintType.Footprint10x8, 10, 8)] + [InlineData(FootprintType.Footprint10x10, 10, 10)] + [InlineData(FootprintType.Footprint12x10, 12, 10)] + [InlineData(FootprintType.Footprint12x12, 12, 12)] + public void FromFootprintType_WithValidType_ShouldReturnCorrectDimensions( + FootprintType type, int expectedWidth, int expectedHeight) + { + Footprint footprint = Footprint.FromFootprintType(type); + + footprint.Type.Should().Be(type); + footprint.Width.Should().Be(expectedWidth); + footprint.Height.Should().Be(expectedHeight); + footprint.PixelCount.Should().Be(expectedWidth * expectedHeight); + } + + [Fact] + public void FromFootprintType_WithAllValidTypes_ShouldReturnUniqueFootprints() + { + FootprintType[] allTypes = + [ + FootprintType.Footprint4x4, FootprintType.Footprint5x4, FootprintType.Footprint5x5, + FootprintType.Footprint6x5, FootprintType.Footprint6x6, FootprintType.Footprint8x5, + FootprintType.Footprint8x6, FootprintType.Footprint8x8, FootprintType.Footprint10x5, + FootprintType.Footprint10x6, FootprintType.Footprint10x8, FootprintType.Footprint10x10, + FootprintType.Footprint12x10, FootprintType.Footprint12x12 + ]; + + List footprints = [.. allTypes.Select(Footprint.FromFootprintType)]; + + footprints.Should().HaveCount(allTypes.Length); + footprints.Should().OnlyHaveUniqueItems(); + } + + [Fact] + public void Footprint_PixelCount_ShouldEqualWidthTimesHeight() + { + Footprint footprint = Footprint.FromFootprintType(FootprintType.Footprint10x8); + + footprint.PixelCount.Should().Be(footprint.Width * footprint.Height); + footprint.PixelCount.Should().Be(80); + } + + [Fact] + public void Footprint_ValueEquality_WithSameType_ShouldBeEqual() + { + Footprint footprint1 = Footprint.FromFootprintType(FootprintType.Footprint6x6); + Footprint footprint2 = Footprint.FromFootprintType(FootprintType.Footprint6x6); + + footprint1.Should().Be(footprint2); + (footprint1 == footprint2).Should().BeTrue(); + } + + [Fact] + public void Footprint_ValueEquality_WithDifferentType_ShouldNotBeEqual() + { + Footprint footprint1 = Footprint.FromFootprintType(FootprintType.Footprint6x6); + Footprint footprint2 = Footprint.FromFootprintType(FootprintType.Footprint8x8); + + footprint1.Should().NotBe(footprint2); + (footprint1 != footprint2).Should().BeTrue(); + } +} diff --git a/tests/ImageSharp.Textures.Tests/Formats/Astc/HDR/HdrComparisonTests.cs b/tests/ImageSharp.Textures.Tests/Formats/Astc/HDR/HdrComparisonTests.cs new file mode 100644 index 00000000..5ad72fc9 --- /dev/null +++ b/tests/ImageSharp.Textures.Tests/Formats/Astc/HDR/HdrComparisonTests.cs @@ -0,0 +1,211 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using AwesomeAssertions; +using SixLabors.ImageSharp.Textures.Astc; +using SixLabors.ImageSharp.Textures.Astc.Core; +using SixLabors.ImageSharp.Textures.Astc.IO; + +namespace SixLabors.ImageSharp.Textures.Tests.Formats.Astc.HDR; + +/// +/// Comparing HDR and LDR ASTC decoding behavior using real reference files. +/// +public class HdrComparisonTests +{ + [Fact] + public void HdrFile_DecodedWithHdrApi_ShouldPreserveExtendedRange() + { + // HDR files should decode to values potentially exceeding 1.0 + string astcPath = TestFile.GetInputFileFullPath(Path.Combine(TestImages.Astc.HdrFolder, "HDR-A-1x1.astc")); + + byte[] astcData = File.ReadAllBytes(astcPath); + AstcFile astcFile = AstcFile.FromMemory(astcData); + + // Decode with HDR API + Span hdrResult = AstcDecoder.DecompressHdrImage( + astcFile.Blocks, astcFile.Width, astcFile.Height, astcFile.Footprint); + + // Verify we get Float16 output + hdrResult.Length.Should().Be(4); // 1 pixel, 4 channels + + // HDR content can have values > 1.0 (this file may or may not, but should allow it) + foreach (float value in hdrResult) + { + float.IsNaN(value).Should().BeFalse(); + float.IsInfinity(value).Should().BeFalse(); + value.Should().BeGreaterThanOrEqualTo(0.0f); + } + } + + [Fact] + public void LdrFile_DecodedWithHdrApi_ShouldUpscaleToHdrRange() + { + // LDR files decoded with HDR API should produce values in 0.0-1.0 range + string astcPath = TestFile.GetInputFileFullPath(Path.Combine(TestImages.Astc.HdrFolder, "LDR-A-1x1.astc")); + + byte[] astcData = File.ReadAllBytes(astcPath); + AstcFile astcFile = AstcFile.FromMemory(astcData); + + // Decode with HDR API + Span hdrResult = AstcDecoder.DecompressHdrImage( + astcFile.Blocks, astcFile.Width, astcFile.Height, astcFile.Footprint); + + hdrResult.Length.Should().Be(4); + + // LDR content should map to 0.0-1.0 range when decoded with HDR API + foreach (float value in hdrResult) + { + value.Should().BeGreaterThanOrEqualTo(0.0f); + value.Should().BeLessThanOrEqualTo(1.0f); + } + } + + [Fact] + public void HdrFile_DecodedWithLdrApi_ShouldClampToByteRange() + { + // HDR files decoded with LDR API should clamp to 0-255 + string astcPath = TestFile.GetInputFileFullPath(Path.Combine(TestImages.Astc.HdrFolder, "HDR-A-1x1.astc")); + + byte[] astcData = File.ReadAllBytes(astcPath); + AstcFile astcFile = AstcFile.FromMemory(astcData); + + // Decode with LDR API + Span ldrResult = AstcDecoder.DecompressImage(astcFile); + + ldrResult.Length.Should().Be(4); + + // All values must be in LDR range + foreach (byte value in ldrResult) + { + value.Should().BeGreaterThanOrEqualTo(0); + value.Should().BeLessThanOrEqualTo(255); + } + } + + [Fact] + public void LdrFile_DecodedWithBothApis_ShouldProduceConsistentValues() + { + // LDR content should produce equivalent results with both APIs + string astcPath = TestFile.GetInputFileFullPath(Path.Combine(TestImages.Astc.HdrFolder, "LDR-A-1x1.astc")); + + byte[] astcData = File.ReadAllBytes(astcPath); + AstcFile astcFile = AstcFile.FromMemory(astcData); + + // Decode with both APIs + Span ldrResult = AstcDecoder.DecompressImage(astcFile); + Span hdrResult = AstcDecoder.DecompressHdrImage( + astcFile.Blocks, astcFile.Width, astcFile.Height, astcFile.Footprint); + + // Compare results - LDR byte should map to HDR float / 255.0 + for (int i = 0; i < 4; i++) + { + byte ldrValue = ldrResult[i]; + float hdrValue = hdrResult[i]; + + float expectedHdr = ldrValue / 255.0f; + + Math.Abs(hdrValue - expectedHdr).Should().BeLessThan(0.01f); + } + } + + [Fact] + public void HdrTile_ShouldDecodeSuccessfully() + { + // Test larger HDR tile decoding + string astcPath = TestFile.GetInputFileFullPath(Path.Combine(TestImages.Astc.HdrFolder, "hdr-tile.astc")); + + byte[] astcData = File.ReadAllBytes(astcPath); + AstcFile astcFile = AstcFile.FromMemory(astcData); + + Span hdrResult = AstcDecoder.DecompressHdrImage( + astcFile.Blocks, astcFile.Width, astcFile.Height, astcFile.Footprint); + + // Should produce Width * Height * 4 values + hdrResult.Length.Should().Be(astcFile.Width * astcFile.Height * 4); + + foreach (float value in hdrResult) + { + float.IsNaN(value).Should().BeFalse(); + float.IsInfinity(value).Should().BeFalse(); + } + } + + [Fact] + public void LdrTile_ShouldDecodeSuccessfully() + { + // Test larger LDR tile decoding + string astcPath = TestFile.GetInputFileFullPath(Path.Combine(TestImages.Astc.HdrFolder, "ldr-tile.astc")); + + byte[] astcData = File.ReadAllBytes(astcPath); + AstcFile astcFile = AstcFile.FromMemory(astcData); + + // Decode with both APIs + Span ldrResult = AstcDecoder.DecompressImage(astcFile); + Span hdrResult = AstcDecoder.DecompressHdrImage( + astcFile.Blocks, astcFile.Width, astcFile.Height, astcFile.Footprint); + + // Both should produce correct output sizes + ldrResult.Length.Should().Be(astcFile.Width * astcFile.Height * 4); + hdrResult.Length.Should().Be(astcFile.Width * astcFile.Height * 4); + } + + [Fact] + public void SameFootprint_HdrVsLdr_ShouldBothDecode() + { + // Verify files with same footprint decode correctly + string hdrPath = TestFile.GetInputFileFullPath(Path.Combine(TestImages.Astc.HdrFolder, "HDR-A-1x1.astc")); + string ldrPath = TestFile.GetInputFileFullPath(Path.Combine(TestImages.Astc.HdrFolder, "LDR-A-1x1.astc")); + + byte[] hdrData = File.ReadAllBytes(hdrPath); + byte[] ldrData = File.ReadAllBytes(ldrPath); + + AstcFile hdrFile = AstcFile.FromMemory(hdrData); + AstcFile ldrFile = AstcFile.FromMemory(ldrData); + + // Both are 1x1 with 6x6 footprint + hdrFile.Width.Should().Be(ldrFile.Width); + hdrFile.Height.Should().Be(ldrFile.Height); + hdrFile.Footprint.Width.Should().Be(ldrFile.Footprint.Width); + hdrFile.Footprint.Height.Should().Be(ldrFile.Footprint.Height); + + // Both should decode successfully with HDR API + Span hdrDecoded = AstcDecoder.DecompressHdrImage( + hdrFile.Blocks, hdrFile.Width, hdrFile.Height, hdrFile.Footprint); + Span ldrDecoded = AstcDecoder.DecompressHdrImage( + ldrFile.Blocks, ldrFile.Width, ldrFile.Height, ldrFile.Footprint); + + hdrDecoded.Length.Should().Be(4); + ldrDecoded.Length.Should().Be(4); + } + + [Fact] + public void HdrColor_FromLdr_ShouldMatchLdrToHdrApiConversion() + { + // Verify that HdrColor.FromRgba() produces same results as decoding LDR with HDR API + string astcPath = TestFile.GetInputFileFullPath(Path.Combine(TestImages.Astc.HdrFolder, "LDR-A-1x1.astc")); + + byte[] astcData = File.ReadAllBytes(astcPath); + AstcFile astcFile = AstcFile.FromMemory(astcData); + + // Decode with LDR API to get byte values + Span ldrBytes = AstcDecoder.DecompressImage(astcFile); + + // Convert LDR bytes to HDR using HdrColor + RgbaColor ldrColor = new(ldrBytes[0], ldrBytes[1], ldrBytes[2], ldrBytes[3]); + RgbaHdrColor hdrFromLdr = RgbaHdrColor.FromRgba(ldrColor); + + // Decode with HDR API + Span hdrDirect = AstcDecoder.DecompressHdrImage( + astcFile.Blocks, astcFile.Width, astcFile.Height, astcFile.Footprint); + + // Compare: UNORM16 normalized values should match HDR API output + for (int i = 0; i < 4; i++) + { + float fromConversion = hdrFromLdr[i] / 65535.0f; + float fromDirect = hdrDirect[i]; + + Math.Abs(fromConversion - fromDirect).Should().BeLessThan(0.0001f); + } + } +} diff --git a/tests/ImageSharp.Textures.Tests/Formats/Astc/HDR/HdrDecoderTests.cs b/tests/ImageSharp.Textures.Tests/Formats/Astc/HDR/HdrDecoderTests.cs new file mode 100644 index 00000000..50cf2594 --- /dev/null +++ b/tests/ImageSharp.Textures.Tests/Formats/Astc/HDR/HdrDecoderTests.cs @@ -0,0 +1,96 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using AwesomeAssertions; +using SixLabors.ImageSharp.Textures.Astc; +using SixLabors.ImageSharp.Textures.Astc.Core; + +namespace SixLabors.ImageSharp.Textures.Tests.Formats.Astc.HDR; + +public class HdrDecoderTests +{ + [Fact] + public void DecompressToFloat16_WithValidBlock_ShouldProduceCorrectOutputSize() + { + // Create a simple 4x4 block (16 bytes) + byte[] astcData = new byte[16]; + + Footprint footprint = Footprint.FromFootprintType(FootprintType.Footprint4x4); + + // Decompress using HDR API + Span hdrResult = AstcDecoder.DecompressHdrImage(astcData, 4, 4, footprint); + + // Verify output size: 4x4 pixels, 4 Half values (RGBA) per pixel + hdrResult.Length.Should().Be(4 * 4 * 4); // 64 Half values total + + foreach (float value in hdrResult) + { + float.IsNaN(value).Should().BeFalse(); + float.IsInfinity(value).Should().BeFalse(); + + // Values should be in reasonable range for normalized colors + value.Should().BeGreaterThanOrEqualTo(0.0f); + value.Should().BeLessThanOrEqualTo(1.1f); // Allow slight overshoot for HDR + } + } + + [Fact] + public void DecompressToFloat16_WithDifferentFootprints_ShouldWork() + { + // Test that HDR API works with various footprint types + FootprintType[] footprints = + [ + FootprintType.Footprint4x4, + FootprintType.Footprint5x5, + FootprintType.Footprint6x6, + FootprintType.Footprint8x8 + ]; + + foreach (FootprintType footprint in footprints) + { + // Create a simple test: 1 block (footprint size) of zeros + Footprint fp = Footprint.FromFootprintType(footprint); + byte[] astcData = new byte[16]; // One ASTC block (all zeros = void extent block) + + Span result = AstcDecoder.DecompressHdrImage(astcData, fp.Width, fp.Height, footprint); + + // Should produce footprint.Width * footprint.Height pixels, each with 4 Half values + result.Length.Should().Be(fp.Width * fp.Height * 4); + } + } + + [Fact] + public void ASTCDecompressToFloat16_WithInvalidData_ShouldReturnEmpty() + { + byte[] emptyData = []; + + Span result = AstcDecoder.DecompressHdrImage(emptyData, 64, 64, FootprintType.Footprint4x4); + + result.Length.Should().Be(0); + } + + [Fact] + public void DecompressToFloat16_WithZeroDimensions_ShouldReturnEmpty() + { + byte[] astcData = new byte[16]; + Footprint footprint = Footprint.FromFootprintType(FootprintType.Footprint4x4); + + Span result = AstcDecoder.DecompressHdrImage(astcData, 0, 0, footprint); + + result.Length.Should().Be(0); + } + + [Fact] + public void HdrColor_LdrRoundTrip_ShouldPreserveValues() + { + RgbaColor ldrColor = new(50, 100, 150, 200); + + RgbaHdrColor hdrColor = RgbaHdrColor.FromRgba(ldrColor); + RgbaColor backToLdr = hdrColor.ToLowDynamicRange(); + + backToLdr.R.Should().Be(ldrColor.R); + backToLdr.G.Should().Be(ldrColor.G); + backToLdr.B.Should().Be(ldrColor.B); + backToLdr.A.Should().Be(ldrColor.A); + } +} diff --git a/tests/ImageSharp.Textures.Tests/Formats/Astc/HDR/HdrImageTests.cs b/tests/ImageSharp.Textures.Tests/Formats/Astc/HDR/HdrImageTests.cs new file mode 100644 index 00000000..f308c25b --- /dev/null +++ b/tests/ImageSharp.Textures.Tests/Formats/Astc/HDR/HdrImageTests.cs @@ -0,0 +1,154 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.ComponentModel; +using AwesomeAssertions; +using SixLabors.ImageSharp.Textures.Astc; +using SixLabors.ImageSharp.Textures.Astc.Core; +using SixLabors.ImageSharp.Textures.Astc.IO; + +namespace SixLabors.ImageSharp.Textures.Tests.Formats.Astc.HDR; + +/// +/// Tests using real HDR ASTC files from the ARM astc-encoder reference repository. +/// These tests validate that our HDR implementation produces valid output for +/// actual HDR-compressed ASTC data. +/// +public class HdrImageTests +{ + [Fact] + [Description("Verify that the ASTC file header is correctly parsed for HDR content, including footprint detection")] + public void DecodeHdrFile_VerifyFootprintDetection() + { + string astcPath = TestFile.GetInputFileFullPath(Path.Combine(TestImages.Astc.HdrFolder, "HDR-A-1x1.astc")); + + byte[] astcData = File.ReadAllBytes(astcPath); + AstcFile astcFile = AstcFile.FromMemory(astcData); + + // The HDR-A-1x1.astc file has a 6x6 footprint based on the header + astcFile.Footprint.Width.Should().Be(6); + astcFile.Footprint.Height.Should().Be(6); + astcFile.Footprint.Type.Should().Be(FootprintType.Footprint6x6); + } + + [Fact] + public void DecodeHdrAstcFile_1x1Pixel_ShouldProduceValidHdrOutput() + { + string astcPath = TestFile.GetInputFileFullPath(Path.Combine(TestImages.Astc.HdrFolder, "HDR-A-1x1.astc")); + + byte[] astcData = File.ReadAllBytes(astcPath); + AstcFile astcFile = AstcFile.FromMemory(astcData); + + Span hdrResult = AstcDecoder.DecompressHdrImage( + astcFile.Blocks, + astcFile.Width, + astcFile.Height, + astcFile.Footprint); + + // Should produce 1 pixel with 4 values (RGBA) + hdrResult.Length.Should().Be(RgbaColor.BytesPerPixel); + + // HDR values can exceed 1.0 + // Just verify they're in a reasonable range (0.0 to 10.0) + foreach (float value in hdrResult) + { + value.Should().BeGreaterThanOrEqualTo(0.0f); + value.Should().BeLessThan(10.0f); + } + } + + [Fact] + public void DecodeHdrAstcFile_Tile_ShouldProduceValidHdrOutput() + { + string astcPath = TestFile.GetInputFileFullPath(Path.Combine(TestImages.Astc.HdrFolder, "hdr-tile.astc")); + + byte[] astcData = File.ReadAllBytes(astcPath); + AstcFile astcFile = AstcFile.FromMemory(astcData); + + Span hdrResult = AstcDecoder.DecompressHdrImage( + astcFile.Blocks, + astcFile.Width, + astcFile.Height, + astcFile.Footprint); + + // Should produce Width * Height pixels, each with 4 values + hdrResult.Length.Should().Be(astcFile.Width * astcFile.Height * RgbaColor.BytesPerPixel); + + // Verify at least some HDR values exceed 1.0 (typical for HDR content) + int valuesGreaterThanOne = 0; + foreach (float v in hdrResult) + { + if (v > 1.0f) + { + valuesGreaterThanOne++; + } + } + + valuesGreaterThanOne.Should().Be(64); + } + + [Fact] + [Description("Verify that HDR ASTC files can be decoded with the LDR API, producing clamped values")] + public void DecodeHdrAstcFile_WithLdrApi_ShouldClampValues() + { + string astcPath = TestFile.GetInputFileFullPath(Path.Combine(TestImages.Astc.HdrFolder, "HDR-A-1x1.astc")); + + if (!File.Exists(astcPath)) + { + return; + } + + byte[] astcData = File.ReadAllBytes(astcPath); + AstcFile astcFile = AstcFile.FromMemory(astcData); + + // Decode using LDR API + Span ldrResult = AstcDecoder.DecompressImage(astcFile); + + // Should produce 1 pixel with 4 bytes (RGBA) + ldrResult.Length.Should().Be(RgbaColor.BytesPerPixel); + + // All values should be in LDR range + foreach (byte value in ldrResult) + { + value.Should().BeGreaterThanOrEqualTo(byte.MinValue); + value.Should().BeLessThanOrEqualTo(byte.MaxValue); + } + } + + [Fact] + [Description("Verify that HDR and LDR APIs produce consistent relative channel values for the same HDR ASTC file")] + public void HdrAndLdrApis_OnSameHdrFile_ShouldProduceConsistentRelativeValues() + { + string astcPath = TestFile.GetInputFileFullPath(Path.Combine(TestImages.Astc.HdrFolder, "HDR-A-1x1.astc")); + + byte[] astcData = File.ReadAllBytes(astcPath); + AstcFile astcFile = AstcFile.FromMemory(astcData); + + // Decode with both APIs + Span hdrResult = AstcDecoder.DecompressHdrImage( + astcFile.Blocks, astcFile.Width, astcFile.Height, astcFile.Footprint); + Span ldrResult = AstcDecoder.DecompressImage(astcFile); + + // Both should produce output for 1 pixel + hdrResult.Length.Should().Be(4); + ldrResult.Length.Should().Be(4); + + // The relative ordering of RGB channels should be consistent between APIs. + // If HDR channel i > channel j, then LDR channel i should be >= channel j + // (accounting for clamping at 255). + for (int i = 0; i < 3; i++) + { + for (int j = i + 1; j < 3; j++) + { + if (hdrResult[i] > hdrResult[j]) + { + ldrResult[i].Should().BeGreaterThanOrEqualTo(ldrResult[j]); + } + else if (hdrResult[i] < hdrResult[j]) + { + ldrResult[i].Should().BeLessThanOrEqualTo(ldrResult[j]); + } + } + } + } +} diff --git a/tests/ImageSharp.Textures.Tests/Formats/Astc/HDR/RgbaHdrColorTests.cs b/tests/ImageSharp.Textures.Tests/Formats/Astc/HDR/RgbaHdrColorTests.cs new file mode 100644 index 00000000..dc1a8c82 --- /dev/null +++ b/tests/ImageSharp.Textures.Tests/Formats/Astc/HDR/RgbaHdrColorTests.cs @@ -0,0 +1,115 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using AwesomeAssertions; +using SixLabors.ImageSharp.Textures.Astc.Core; + +namespace SixLabors.ImageSharp.Textures.Tests.Formats.Astc.HDR; + +public class RgbaHdrColorTests +{ + [Fact] + public void Constructor_WithValidValues_ShouldInitializeCorrectly() + { + RgbaHdrColor color = new(1000, 2000, 3000, 4000); + + color.R.Should().Be(1000); + color.G.Should().Be(2000); + color.B.Should().Be(3000); + color.A.Should().Be(4000); + } + + [Fact] + public void Indexer_WithValidIndices_ShouldReturnCorrectChannels() + { + RgbaHdrColor color = new(1000, 2000, 3000, 4000); + + color[0].Should().Be(1000); + color[1].Should().Be(2000); + color[2].Should().Be(3000); + color[3].Should().Be(4000); + } + + [Fact] + public void Indexer_WithInvalidIndex_ShouldThrowException() + { + RgbaHdrColor color = new(1000, 2000, 3000, 4000); + + Action act = () => _ = color[4]; + + act.Should().Throw(); + } + + [Fact] + public void FromLdr_WithMinMaxValues_ShouldScaleCorrectly() + { + RgbaColor ldrColor = new(0, 127, 255, 200); + + RgbaHdrColor hdrColor = RgbaHdrColor.FromRgba(ldrColor); + + hdrColor.R.Should().Be(0); // 0 * 257 = 0 + hdrColor.G.Should().Be(32639); // 127 * 257 = 32639 + hdrColor.B.Should().Be(65535); // 255 * 257 = 65535 + hdrColor.A.Should().Be(51400); // 200 * 257 = 51400 + } + + [Fact] + public void ToLdr_WithHdrValues_ShouldDownscaleCorrectly() + { + RgbaHdrColor hdrColor = new(0, 32639, 65535, 51400); + + RgbaColor ldrColor = hdrColor.ToLowDynamicRange(); + + ldrColor.R.Should().Be(0); // 0 >> 8 = 0 + ldrColor.G.Should().Be(127); // 32639 >> 8 = 127 + ldrColor.B.Should().Be(255); // 65535 >> 8 = 255 + ldrColor.A.Should().Be(200); // 51400 >> 8 = 200 + } + + [Fact] + public void FromLdr_ToLdr_RoundTrip_ShouldPreserveValues() + { + RgbaColor original = new(50, 100, 150, 200); + + RgbaHdrColor hdrColor = RgbaHdrColor.FromRgba(original); + RgbaColor result = hdrColor.ToLowDynamicRange(); + + result.R.Should().Be(original.R); + result.G.Should().Be(original.G); + result.B.Should().Be(original.B); + result.A.Should().Be(original.A); + } + + [Fact] + public void IsCloseTo_WithSimilarColors_ShouldReturnTrue() + { + RgbaHdrColor color1 = new(1000, 2000, 3000, 4000); + RgbaHdrColor color2 = new(1005, 1995, 3002, 3998); + + bool result = color1.IsCloseTo(color2, 10); + + result.Should().BeTrue(); + } + + [Fact] + public void IsCloseTo_WithDifferentColors_ShouldReturnFalse() + { + RgbaHdrColor color1 = new(1000, 2000, 3000, 4000); + RgbaHdrColor color2 = new(1020, 2000, 3000, 4000); + + bool result = color1.IsCloseTo(color2, 10); + + result.Should().BeFalse(); + } + + [Fact] + public void Empty_ShouldReturnBlackTransparent() + { + RgbaHdrColor empty = RgbaHdrColor.Empty; + + empty.R.Should().Be(0); + empty.G.Should().Be(0); + empty.B.Should().Be(0); + empty.A.Should().Be(0); + } +} diff --git a/tests/ImageSharp.Textures.Tests/Formats/Astc/IntegerSequenceCodecTests.cs b/tests/ImageSharp.Textures.Tests/Formats/Astc/IntegerSequenceCodecTests.cs new file mode 100644 index 00000000..757bec77 --- /dev/null +++ b/tests/ImageSharp.Textures.Tests/Formats/Astc/IntegerSequenceCodecTests.cs @@ -0,0 +1,334 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.ComponentModel; +using AwesomeAssertions; +using SixLabors.ImageSharp.Textures.Astc.BiseEncoding; +using SixLabors.ImageSharp.Textures.Astc.IO; + +namespace SixLabors.ImageSharp.Textures.Tests.Formats.Astc; + +public class IntegerSequenceCodecTests +{ + [Fact] + [Description("1 to 31 are the densest packing of valid encodings and those supported by the codec.")] + public void GetPackingModeBitCount_ForValidRange_ShouldNotReturnUnknownMode() + { + for (int i = 1; i < 32; ++i) + { + (BiseEncodingMode mode, int _) = BoundedIntegerSequenceCodec.GetPackingModeBitCount(i); + mode.Should().NotBe(BiseEncodingMode.Unknown, $"Range {i} should not yield Unknown encoding mode"); + } + } + + [Fact] + public void GetPackingModeBitCount_ForValidRange_ShouldMatchExpectedValues() + { + (BiseEncodingMode Mode, int BitCount)[] expected = + [ + (BiseEncodingMode.BitEncoding, 1), // Range 1 + (BiseEncodingMode.TritEncoding, 0), // Range 2 + (BiseEncodingMode.BitEncoding, 2), // Range 3 + (BiseEncodingMode.QuintEncoding, 0), // Range 4 + (BiseEncodingMode.TritEncoding, 1), // Range 5 + (BiseEncodingMode.BitEncoding, 3), // Range 6 + (BiseEncodingMode.BitEncoding, 3), // Range 7 + (BiseEncodingMode.QuintEncoding, 1), // Range 8 + (BiseEncodingMode.QuintEncoding, 1), // Range 9 + (BiseEncodingMode.TritEncoding, 2), // Range 10 + (BiseEncodingMode.TritEncoding, 2), // Range 11 + (BiseEncodingMode.BitEncoding, 4), // Range 12 + (BiseEncodingMode.BitEncoding, 4), // Range 13 + (BiseEncodingMode.BitEncoding, 4), // Range 14 + (BiseEncodingMode.BitEncoding, 4), // Range 15 + (BiseEncodingMode.QuintEncoding, 2), // Range 16 + (BiseEncodingMode.QuintEncoding, 2), // Range 17 + (BiseEncodingMode.QuintEncoding, 2), // Range 18 + (BiseEncodingMode.QuintEncoding, 2), // Range 19 + (BiseEncodingMode.TritEncoding, 3), // Range 20 + (BiseEncodingMode.TritEncoding, 3), // Range 21 + (BiseEncodingMode.TritEncoding, 3), // Range 22 + (BiseEncodingMode.TritEncoding, 3), // Range 23 + (BiseEncodingMode.BitEncoding, 5), // Range 24 + (BiseEncodingMode.BitEncoding, 5), // Range 25 + (BiseEncodingMode.BitEncoding, 5), // Range 26 + (BiseEncodingMode.BitEncoding, 5), // Range 27 + (BiseEncodingMode.BitEncoding, 5), // Range 28 + (BiseEncodingMode.BitEncoding, 5), // Range 29 + (BiseEncodingMode.BitEncoding, 5), // Range 30 + (BiseEncodingMode.BitEncoding, 5) // Range 31 + ]; + + for (int i = 1; i < 32; ++i) + { + (BiseEncodingMode mode, int bitCount) = BoundedIntegerSequenceCodec.GetPackingModeBitCount(i); + (BiseEncodingMode expectedMode, int expectedBitCount) = expected[i - 1]; + + mode.Should().Be(expectedMode, $"range {i} mode should match"); + bitCount.Should().Be(expectedBitCount, $"range {i} bit count should match"); + } + } + + [Theory] + [InlineData(0)] + [InlineData(256)] + public void GetPackingModeBitCount_WithInvalidRange_ShouldThrowArgumentOutOfRangeException(int range) + { + Action action = () => BoundedIntegerSequenceCodec.GetPackingModeBitCount(range); + + action.Should().Throw(); + } + + [Theory] + [InlineData(1)] + [InlineData(10)] + [InlineData(32)] + [InlineData(63)] + public void GetBitCount_WithBitEncodingMode1Bit_ShouldReturnValueCount(int valueCount) + { + int bitCount = BoundedIntegerSequenceCodec.GetBitCount(BiseEncodingMode.BitEncoding, valueCount, 1); + int bitCountForRange = BoundedIntegerSequenceCodec.GetBitCountForRange(valueCount, 1); + + bitCount.Should().Be(valueCount); + bitCountForRange.Should().Be(valueCount); + } + + [Theory] + [InlineData(0, 0)] + [InlineData(1, 2)] + [InlineData(10, 20)] + [InlineData(32, 64)] + public void GetBitCount_WithBitEncodingMode2Bits_ShouldReturnTwiceValueCount(int valueCount, int expected) + { + int bitCount = BoundedIntegerSequenceCodec.GetBitCount(BiseEncodingMode.BitEncoding, valueCount, 2); + int bitCountForRange = BoundedIntegerSequenceCodec.GetBitCountForRange(valueCount, 3); + + bitCount.Should().Be(expected); + bitCountForRange.Should().Be(expected); + } + + [Fact] + public void GetBitCount_WithTritEncoding15Values_ShouldReturnExpectedBitCount() + { + const int valueCount = 15; + const int bits = 3; + int expectedBitCount = (8 * 3) + (15 * 3); // 69 bits + + int bitCount = BoundedIntegerSequenceCodec.GetBitCount(BiseEncodingMode.TritEncoding, valueCount, bits); + int bitCountForRange = BoundedIntegerSequenceCodec.GetBitCountForRange(valueCount, 23); + + bitCount.Should().Be(expectedBitCount); + bitCountForRange.Should().Be(bitCount); + } + + [Fact] + public void GetBitCount_WithTritEncoding13Values_ShouldReturnExpectedBitCount() + { + const int valueCount = 13; + const int bits = 2; + const int expectedBitCount = 47; + + int bitCount = BoundedIntegerSequenceCodec.GetBitCount(BiseEncodingMode.TritEncoding, valueCount, bits); + int bitCountForRange = BoundedIntegerSequenceCodec.GetBitCountForRange(valueCount, 11); + + bitCount.Should().Be(expectedBitCount); + bitCountForRange.Should().Be(bitCount); + } + + [Fact] + public void GetBitCount_WithQuintEncoding6Values_ShouldReturnExpectedBitCount() + { + const int valueCount = 6; + const int bits = 4; + int expectedBitCount = (7 * 2) + (6 * 4); // 38 bits + + int bitCount = BoundedIntegerSequenceCodec.GetBitCount(BiseEncodingMode.QuintEncoding, valueCount, bits); + int bitCountForRange = BoundedIntegerSequenceCodec.GetBitCountForRange(valueCount, 79); + + bitCount.Should().Be(expectedBitCount); + bitCountForRange.Should().Be(bitCount); + } + + [Fact] + public void GetBitCount_WithQuintEncoding7Values_ShouldReturnExpectedBitCount() + { + const int valueCount = 7; + const int bits = 3; + int expectedBitCount = (7 * 2) + // First two quint blocks + (6 * 3) + // First two blocks of bits + 3 + // Last quint block without high order four bits + 3; // Last block with one set of three bits + + int bitCount = BoundedIntegerSequenceCodec.GetBitCount(BiseEncodingMode.QuintEncoding, valueCount, bits); + + bitCount.Should().Be(expectedBitCount); + } + + [Fact] + public void EncodeDecode_WithQuintValues_ShouldEncodeAndDecodeExpectedValues() + { + const int valueRange = 79; + BoundedIntegerSequenceEncoder encoder = new(valueRange); + int[] values = [3, 79, 37]; + + foreach (int value in values) + { + encoder.AddValue(value); + } + + // Encode + BitStream bitSink = default; + encoder.Encode(ref bitSink); + + // Verify encoded data + bitSink.Bits.Should().Be(19); + bitSink.TryGetBits(19, out ulong encoded).Should().BeTrue(); + encoded.Should().Be(0x4A7D3UL); + + // Decode + BitStream bitSrc = new(encoded, 19); + BoundedIntegerSequenceDecoder decoder = new(valueRange); + int[] decoded = decoder.Decode(3, ref bitSrc); + + decoded.Should().Equal(values); + } + + [Fact] + public void DecodeThenEncode_WithQuintValues_ShouldPreserveEncoding() + { + int[] expectedValues = [16, 18, 17, 4, 7, 14, 10, 0]; + const ulong encoding = 0x2b9c83dc; + const int range = 19; + + // Decode + BitStream bitSrc = new(encoding, 64); + BoundedIntegerSequenceDecoder decoder = new(range); + int[] decoded = decoder.Decode(expectedValues.Length, ref bitSrc); + + // Check decoded values + decoded.Should().HaveCount(expectedValues.Length); + decoded.Should().Equal(expectedValues); + + // Re-encode + BitStream bitSink = default; + BoundedIntegerSequenceEncoder encoder = new(range); + foreach (int value in expectedValues) + { + encoder.AddValue(value); + } + + encoder.Encode(ref bitSink); + + // Re-encoded should match original + bitSink.Bits.Should().Be(35); + bitSink.TryGetBits(35, out ulong reencoded).Should().BeTrue(); + reencoded.Should().Be(encoding); + } + + [Fact] + public void EncodeDecode_WithTritValues_ShouldEncodeAndDecodeExpectedValues() + { + const int valueRange = 11; + BoundedIntegerSequenceEncoder encoder = new(valueRange); + int[] values = [7, 5, 3, 6, 10]; + + foreach (int value in values) + { + encoder.AddValue(value); + } + + // Encode + BitStream bitSink = default; + encoder.Encode(ref bitSink); + + // Verify encoded data + bitSink.Bits.Should().Be(18); + bitSink.TryGetBits(18, out ulong encoded).Should().BeTrue(); + encoded.Should().Be(0x37357UL); + + // Decode + BitStream bitSrc = new(encoded, 19); + BoundedIntegerSequenceDecoder decoder = new(valueRange); + int[] decoded = decoder.Decode(5, ref bitSrc); + + decoded.Should().Equal(values); + } + + [Fact] + public void DecodeThenEncode_WithTritValues_ShouldPreserveEncoding() + { + int[] expectedValues = [6, 0, 0, 2, 0, 0, 0, 0, 8, 0, 0, 0, 0, 8, 8, 0]; + const ulong encoding = 0x0004c0100001006UL; + const int range = 11; + + // Decode + BitStream bitSrc = new(encoding, 64); + BoundedIntegerSequenceDecoder decoder = new(range); + int[] decoded = decoder.Decode(expectedValues.Length, ref bitSrc); + + // Check decoded values + decoded.Should().HaveCount(expectedValues.Length); + decoded.Should().Equal(expectedValues); + + // Re-encode + BitStream bitSink = default; + BoundedIntegerSequenceEncoder encoder = new(range); + foreach (int value in expectedValues) + { + encoder.AddValue(value); + } + + encoder.Encode(ref bitSink); + + // Assert re-encoded matches original + bitSink.Bits.Should().Be(58); + bitSink.TryGetBits(58, out ulong reencoded).Should().BeTrue(); + reencoded.Should().Be(encoding); + } + + [Fact] + public void EncodeDecode_WithRandomValues_ShouldAlwaysRoundTripCorrectly() + { + Random random = new(unchecked(0xbad7357)); + const int testCount = 1600; + + for (int test = 0; test < testCount; test++) + { + int valueCount = 4 + (random.Next(0, 256) % 44); + int range = 1 + (random.Next(0, 256) % 63); + + int bitCount = BoundedIntegerSequenceCodec.GetBitCountForRange(valueCount, range); + if (bitCount >= 64) + { + continue; + } + + // Generate random values + List generated = new(valueCount); + for (int i = 0; i < valueCount; i++) + { + generated.Add(random.Next(range + 1)); + } + + // Encode + BitStream bitSink = default; + BoundedIntegerSequenceEncoder encoder = new(range); + foreach (int value in generated) + { + encoder.AddValue(value); + } + + encoder.Encode(ref bitSink); + + bitSink.TryGetBits((int)bitSink.Bits, out ulong encoded).Should().BeTrue(); + + // Decode + BitStream bitSrc = new(encoded, 64); + BoundedIntegerSequenceDecoder decoder = new(range); + int[] decoded = decoder.Decode(valueCount, ref bitSrc); + + decoded.Should().HaveCount(generated.Count); + decoded.Should().Equal(generated); + } + } +} diff --git a/tests/ImageSharp.Textures.Tests/Formats/Astc/IntegrationTests.cs b/tests/ImageSharp.Textures.Tests/Formats/Astc/IntegrationTests.cs new file mode 100644 index 00000000..b03cc039 --- /dev/null +++ b/tests/ImageSharp.Textures.Tests/Formats/Astc/IntegrationTests.cs @@ -0,0 +1,56 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using AwesomeAssertions; +using SixLabors.ImageSharp.Textures.Astc; +using SixLabors.ImageSharp.Textures.Astc.IO; + +namespace SixLabors.ImageSharp.Textures.Tests.Formats.Astc; + +public class IntegrationTests +{ + [Theory] + [InlineData("atlas_small_4x4")] + [InlineData("atlas_small_5x5")] + [InlineData("atlas_small_6x6")] + [InlineData("atlas_small_8x8")] + [InlineData("checkerboard")] + [InlineData("checkered_4")] + [InlineData("checkered_5")] + [InlineData("checkered_6")] + [InlineData("checkered_7")] + [InlineData("checkered_8")] + [InlineData("checkered_9")] + [InlineData("checkered_10")] + [InlineData("checkered_11")] + [InlineData("checkered_12")] + [InlineData("footprint_4x4")] + [InlineData("footprint_5x4")] + [InlineData("footprint_5x5")] + [InlineData("footprint_6x5")] + [InlineData("footprint_6x6")] + [InlineData("footprint_8x5")] + [InlineData("footprint_8x6")] + [InlineData("footprint_8x8")] + [InlineData("footprint_10x5")] + [InlineData("footprint_10x6")] + [InlineData("footprint_10x8")] + [InlineData("footprint_10x10")] + [InlineData("footprint_12x10")] + [InlineData("footprint_12x12")] + [InlineData("rgb_4x4")] + [InlineData("rgb_5x4")] + [InlineData("rgb_6x6")] + [InlineData("rgb_8x8")] + [InlineData("rgb_12x12")] + public void DecompressToImage_WithTestdataFile_ShouldDecodeSuccessfully(string basename) + { + string filePath = TestFile.GetInputFileFullPath(Path.Combine(TestImages.Astc.InputFolder, basename + ".astc")); + byte[] bytes = File.ReadAllBytes(filePath); + AstcFile astc = AstcFile.FromMemory(bytes); + + Span result = AstcDecoder.DecompressImage(astc); + + result.Length.Should().BeGreaterThan(0, because: $"decoding should succeed for {basename}"); + } +} diff --git a/tests/ImageSharp.Textures.Tests/Formats/Astc/IntermediateBlockTests.cs b/tests/ImageSharp.Textures.Tests/Formats/Astc/IntermediateBlockTests.cs new file mode 100644 index 00000000..7281d7ef --- /dev/null +++ b/tests/ImageSharp.Textures.Tests/Formats/Astc/IntermediateBlockTests.cs @@ -0,0 +1,463 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using AwesomeAssertions; +using SixLabors.ImageSharp.Textures.Astc.ColorEncoding; +using SixLabors.ImageSharp.Textures.Astc.Core; +using SixLabors.ImageSharp.Textures.Astc.TexelBlock; + +namespace SixLabors.ImageSharp.Textures.Tests.Formats.Astc; + +#nullable enable +public class IntermediateBlockTests +{ + private static readonly UInt128 ErrorBlock = UInt128.Zero; + + [Fact] + public void UnpackVoidExtent_WithErrorBlock_ShouldReturnNull() + { + PhysicalBlock errorBlock = PhysicalBlock.Create(ErrorBlock); + + IntermediateBlock.VoidExtentData? result = IntermediateBlock.UnpackVoidExtent(errorBlock); + + result.Should().BeNull(); + } + + [Fact] + public void UnpackIntermediateBlock_WithErrorBlock_ShouldReturnNull() + { + PhysicalBlock errorBlock = PhysicalBlock.Create(ErrorBlock); + + IntermediateBlock.IntermediateBlockData? result = IntermediateBlock.UnpackIntermediateBlock(errorBlock); + + result.Should().BeNull(); + } + + [Fact] + public void EndpointRangeForBlock_WithoutWeights_ShouldReturnNegativeOne() + { + IntermediateBlock.IntermediateBlockData data = new() + { + WeightRange = 15, + WeightGridX = 6, + WeightGridY = 6 + }; + + int result = IntermediateBlock.EndpointRangeForBlock(data); + + result.Should().Be(-1); + } + + [Fact] + public void Pack_WithIncorrectNumberOfWeights_ShouldReturnError() + { + IntermediateBlock.IntermediateBlockData data = new() + { + WeightRange = 15, + WeightGridX = 6, + WeightGridY = 6 + }; + + (string? error, UInt128 _) = IntermediateBlockPacker.Pack(data); + + error.Should().NotBeNull(); + error.Should().Contain("Incorrect number of weights"); + } + + [Fact] + public void EndpointRangeForBlock_WithNotEnoughBits_ShouldReturnNegativeTwo() + { + IntermediateBlock.IntermediateBlockData data = new() + { + WeightRange = 1, + PartitionId = 0, + WeightGridX = 8, + WeightGridY = 8, + EndpointCount = 3 + }; + data.Endpoints[0] = new() { Mode = ColorEndpointMode.LdrRgbDirect }; + data.Endpoints[1] = new() { Mode = ColorEndpointMode.LdrRgbDirect }; + data.Endpoints[2] = new() { Mode = ColorEndpointMode.LdrRgbDirect }; + + int result = IntermediateBlock.EndpointRangeForBlock(data); + + result.Should().Be(-2); + } + + [Fact] + public void Pack_WithNotEnoughBitsForColors_ShouldReturnError() + { + IntermediateBlock.IntermediateBlockData data = new() + { + WeightRange = 1, + PartitionId = 0, + WeightGridX = 8, + WeightGridY = 8, + Weights = new int[64], + EndpointCount = 3 + }; + data.Endpoints[0] = new() { Mode = ColorEndpointMode.LdrRgbDirect }; + data.Endpoints[1] = new() { Mode = ColorEndpointMode.LdrRgbDirect }; + data.Endpoints[2] = new() { Mode = ColorEndpointMode.LdrRgbDirect }; + + (string? error, UInt128 _) = IntermediateBlockPacker.Pack(data); + + error.Should().NotBeNull(); + error.Should().Contain("illegal color range"); + } + + [Fact] + public void EndpointRangeForBlock_WithIncreasingWeightGrid_ShouldDecreaseColorRange() + { + IntermediateBlock.IntermediateBlockData data = new() + { + WeightRange = 2, + DualPlaneChannel = null, + EndpointCount = 2 + }; + data.Endpoints[0] = new() { Mode = ColorEndpointMode.LdrRgbDirect }; + data.Endpoints[1] = new() { Mode = ColorEndpointMode.LdrRgbDirect }; + + List<(int W, int H)> weightParams = []; + for (int y = 2; y < 8; ++y) + { + for (int x = 2; x < 8; ++x) + { + weightParams.Add((x, y)); + } + } + + weightParams.Sort((a, b) => (a.W * a.H).CompareTo(b.W * b.H)); + + int lastColorRange = byte.MaxValue; + foreach ((int w, int h) in weightParams) + { + data.WeightGridX = w; + data.WeightGridY = h; + int colorRange = IntermediateBlock.EndpointRangeForBlock(data); + + colorRange.Should().BeLessThanOrEqualTo(lastColorRange); + lastColorRange = Math.Min(colorRange, lastColorRange); + } + + lastColorRange.Should().BeLessThan(byte.MaxValue); + } + + [Fact] + public void EndpointRange_WithStandardBlock_ShouldBe255() + { + PhysicalBlock block = PhysicalBlock.Create((UInt128)0x0000000001FE000173UL); + + IntermediateBlock.IntermediateBlockData? data = IntermediateBlock.UnpackIntermediateBlock(block); + + block.GetColorValuesRange().Should().Be(255); + data.Should().NotBeNull(); + IntermediateBlock.IntermediateBlockData ib = data!.Value; + ib.EndpointCount.Should().Be(1); + ib.Endpoints[0].Mode.Should().Be(ColorEndpointMode.LdrLumaDirect); + ib.Endpoints[0].Colors[0].Should().Be(byte.MinValue); + ib.Endpoints[0].Colors[1].Should().Be(byte.MaxValue); + ib.Endpoints[0].ColorCount.Should().Be(2); + ib.EndpointRange.Should().Be(byte.MaxValue); + } + + [Fact] + public void UnpackIntermediateBlock_WithStandardBlock_ShouldReturnCorrectData() + { + PhysicalBlock block = PhysicalBlock.Create((UInt128)0x0000000001FE000173UL); + + IntermediateBlock.IntermediateBlockData? result = IntermediateBlock.UnpackIntermediateBlock(block); + + result.Should().NotBeNull(); + IntermediateBlock.IntermediateBlockData data = result!.Value; + + data.WeightGridX.Should().Be(6); + data.WeightGridY.Should().Be(5); + data.WeightRange.Should().Be(7); + data.PartitionId.Should().BeNull(); + data.DualPlaneChannel.Should().BeNull(); + + data.WeightsCount.Should().Be(30); + data.Weights.AsSpan(0, data.WeightsCount).ToArray().Should().AllBeEquivalentTo(0); + + data.EndpointCount.Should().Be(1); + IntermediateBlock.IntermediateEndpointData endpoint = data.Endpoints[0]; + endpoint.Mode.Should().Be(ColorEndpointMode.LdrLumaDirect); + endpoint.ColorCount.Should().Be(2); + endpoint.Colors[0].Should().Be(byte.MinValue); + endpoint.Colors[1].Should().Be(byte.MaxValue); + } + + [Fact] + public void Pack_WithStandardBlockData_ShouldProduceExpectedBits() + { + IntermediateBlock.IntermediateBlockData data = new() + { + WeightGridX = 6, + WeightGridY = 5, + WeightRange = 7, + PartitionId = null, + DualPlaneChannel = null, + Weights = new int[30] + }; + + IntermediateBlock.IntermediateEndpointData endpoint = new() + { + Mode = ColorEndpointMode.LdrLumaDirect, + ColorCount = 2 + }; + endpoint.Colors[0] = byte.MinValue; + endpoint.Colors[1] = byte.MaxValue; + data.Endpoints[0] = endpoint; + data.EndpointCount = 1; + + (string? error, UInt128 packed) = IntermediateBlockPacker.Pack(data); + + error.Should().BeNull(); + packed.Should().Be((UInt128)0x0000000001FE000173UL); + } + + [Fact] + public void Pack_WithLargeGapInBits_ShouldPreserveOriginalEncoding() + { + UInt128 original = new(0xBEDEAD0000000000UL, 0x0000000001FE032EUL); + PhysicalBlock block = PhysicalBlock.Create(original); + IntermediateBlock.IntermediateBlockData? data = IntermediateBlock.UnpackIntermediateBlock(block); + + data.Should().NotBeNull(); + IntermediateBlock.IntermediateBlockData intermediate = data!.Value; + + // Check unpacked values + intermediate.WeightGridX.Should().Be(2); + intermediate.WeightGridY.Should().Be(3); + intermediate.WeightRange.Should().Be(15); + intermediate.PartitionId.Should().BeNull(); + intermediate.DualPlaneChannel.Should().BeNull(); + intermediate.EndpointCount.Should().Be(1); + intermediate.Endpoints[0].Mode.Should().Be(ColorEndpointMode.LdrLumaDirect); + intermediate.Endpoints[0].ColorCount.Should().Be(2); + intermediate.Endpoints[0].Colors[0].Should().Be(255); + intermediate.Endpoints[0].Colors[1].Should().Be(0); + + // Repack + (string? error, UInt128 repacked) = IntermediateBlockPacker.Pack(intermediate); + + error.Should().BeNull(); + repacked.Should().Be(original); + } + + [Fact] + public void UnpackVoidExtent_WithAllOnesPattern_ShouldReturnZeroColors() + { + PhysicalBlock block = PhysicalBlock.Create((UInt128)0xFFFFFFFFFFFFFDFCUL); + + IntermediateBlock.VoidExtentData? result = IntermediateBlock.UnpackVoidExtent(block); + + result.Should().NotBeNull(); + IntermediateBlock.VoidExtentData data = result!.Value; + + data.R.Should().Be(0); + data.G.Should().Be(0); + data.B.Should().Be(0); + data.A.Should().Be(0); + + data.Coords.Should().AllSatisfy(c => c.Should().Be((1 << 13) - 1)); + } + + [Fact] + public void UnpackVoidExtent_WithColorData_ShouldReturnCorrectColors() + { + UInt128 blockBits = new(0xdeadbeefdeadbeefUL, 0xFFF8003FFE000DFCUL); + PhysicalBlock block = PhysicalBlock.Create(blockBits); + + IntermediateBlock.VoidExtentData? result = IntermediateBlock.UnpackVoidExtent(block); + + result.Should().NotBeNull(); + IntermediateBlock.VoidExtentData data = result!.Value; + + data.R.Should().Be(0xbeef); + data.G.Should().Be(0xdead); + data.B.Should().Be(0xbeef); + data.A.Should().Be(0xdead); + + data.Coords[0].Should().Be(0); + data.Coords[1].Should().Be(8191); + data.Coords[2].Should().Be(0); + data.Coords[3].Should().Be(8191); + } + + [Fact] + public void Pack_WithZeroColorVoidExtent_ShouldProduceAllOnesPattern() + { + IntermediateBlock.VoidExtentData data = new() + { + R = 0, + G = 0, + B = 0, + A = 0, + Coords = new ushort[4] + }; + + for (int i = 0; i < 4; ++i) + { + data.Coords[i] = (1 << 13) - 1; + } + + (string? error, UInt128 packed) = IntermediateBlockPacker.Pack(data); + + error.Should().BeNull(); + packed.Should().Be((UInt128)0xFFFFFFFFFFFFFDFCUL); + } + + [Fact] + public void Pack_WithColorVoidExtent_ShouldProduceExpectedBits() + { + IntermediateBlock.VoidExtentData data = new() + { + R = 0xbeef, + G = 0xdead, + B = 0xbeef, + A = 0xdead, + Coords = [0, 8191, 0, 8191] + }; + + (string? error, UInt128 packed) = IntermediateBlockPacker.Pack(data); + + error.Should().BeNull(); + packed.Should().Be(new UInt128(0xdeadbeefdeadbeefUL, 0xFFF8003FFE000DFCUL)); + } + + [Theory] + [InlineData(0xe8e8eaea20000980UL, 0x20000200cb73f045UL)] + [InlineData(0x3300c30700cb01c5UL, 0x0573907b8c0f6879UL)] + public void PackUnpack_WithSameCEM_ShouldRoundTripCorrectly(ulong high, ulong low) + { + UInt128 original = new(high, low); + PhysicalBlock block = PhysicalBlock.Create(original); + + IntermediateBlock.IntermediateBlockData? unpacked = IntermediateBlock.UnpackIntermediateBlock(block); + + unpacked.Should().NotBeNull(); + IntermediateBlock.IntermediateBlockData ib = unpacked!.Value; + + (string? error, UInt128 repacked) = IntermediateBlockPacker.Pack(ib); + + error.Should().BeNull(); + repacked.Should().Be(original); + } + + [Theory] + [InlineData("checkered_4", 4)] + [InlineData("checkered_5", 5)] + [InlineData("checkered_6", 6)] + [InlineData("checkered_7", 7)] + [InlineData("checkered_8", 8)] + [InlineData("checkered_9", 9)] + [InlineData("checkered_10", 10)] + [InlineData("checkered_11", 11)] + [InlineData("checkered_12", 12)] + public void PackUnpack_WithTestDataBlocks_ShouldPreserveBlockProperties(string imageName, int checkeredDim) + { + const int astcDim = 8; + int imgDim = checkeredDim * astcDim; + byte[] astcData = LoadASTCFile(imageName); + int numBlocks = (imgDim / astcDim) * (imgDim / astcDim); + + (astcData.Length % PhysicalBlock.SizeInBytes).Should().Be(0); + + for (int i = 0; i < numBlocks; ++i) + { + ReadOnlySpan slice = new(astcData, i * PhysicalBlock.SizeInBytes, PhysicalBlock.SizeInBytes); + UInt128 blockBits = new( + BitConverter.ToUInt64(slice.Slice(8, 8)), + BitConverter.ToUInt64(slice[..8])); + PhysicalBlock originalBlock = PhysicalBlock.Create(blockBits); + + // Unpack and repack + UInt128 repacked; + if (originalBlock.IsVoidExtent) + { + IntermediateBlock.VoidExtentData? voidData = IntermediateBlock.UnpackVoidExtent(originalBlock); + voidData.Should().NotBeNull(); + + (string? error, UInt128 packed) = IntermediateBlockPacker.Pack(voidData!.Value); + error.Should().BeNull(); + repacked = packed; + } + else + { + IntermediateBlock.IntermediateBlockData? intermediateData = IntermediateBlock.UnpackIntermediateBlock(originalBlock); + intermediateData.Should().NotBeNull(); + IntermediateBlock.IntermediateBlockData ibData = intermediateData!.Value; + + // Verify endpoint range was set + ibData.EndpointRange.Should().Be(originalBlock.GetColorValuesRange()); + + // Clear endpoint range before repacking (to test calculation) + ibData.EndpointRange = null; + (string? error, UInt128 packed) = IntermediateBlockPacker.Pack(ibData); + error.Should().BeNull(); + repacked = packed; + } + + // Verify repacked block + PhysicalBlock repackedBlock = PhysicalBlock.Create(repacked); + VerifyBlockPropertiesMatch(repackedBlock, originalBlock); + } + } + + private static void VerifyBlockPropertiesMatch(PhysicalBlock repacked, PhysicalBlock original) + { + repacked.IsIllegalEncoding.Should().BeFalse(); + + // Verify color bits match + int repackedColorBitCount = repacked.GetColorBitCount().Value; + UInt128 repackedColorMask = UInt128Extensions.OnesMask(repackedColorBitCount); + UInt128 repackedColorBits = (repacked.BlockBits >> repacked.GetColorStartBit().Value) & repackedColorMask; + + int originalColorBitCount = original.GetColorBitCount().Value; + UInt128 originalColorMask = UInt128Extensions.OnesMask(originalColorBitCount); + UInt128 originalColorBits = (original.BlockBits >> original.GetColorStartBit().Value) & originalColorMask; + + repackedColorMask.Should().Be(originalColorMask); + repackedColorBits.Should().Be(originalColorBits); + + // Verify void extent properties + repacked.IsVoidExtent.Should().Be(original.IsVoidExtent); + repacked.GetVoidExtentCoordinates().Should().Equal(original.GetVoidExtentCoordinates()); + + // Verify weight properties + repacked.GetWeightGridDimensions().Should().Be(original.GetWeightGridDimensions()); + repacked.GetWeightRange().Should().Be(original.GetWeightRange()); + repacked.GetWeightBitCount().Should().Be(original.GetWeightBitCount()); + repacked.GetWeightStartBit().Should().Be(original.GetWeightStartBit()); + + // Verify dual plane properties + repacked.IsDualPlane.Should().Be(original.IsDualPlane); + repacked.GetDualPlaneChannel().Should().Be(original.GetDualPlaneChannel()); + + // Verify partition properties + repacked.GetPartitionsCount().Should().Be(original.GetPartitionsCount()); + repacked.GetPartitionId().Should().Be(original.GetPartitionId()); + + // Verify color value properties + repacked.GetColorValuesCount().Should().Be(original.GetColorValuesCount()); + repacked.GetColorValuesRange().Should().Be(original.GetColorValuesRange()); + + // Verify endpoint modes for all partitions + int numParts = repacked.GetPartitionsCount().GetValueOrDefault(0); + for (int j = 0; j < numParts; ++j) + { + repacked.GetEndpointMode(j).Should().Be(original.GetEndpointMode(j)); + } + } + + private static byte[] LoadASTCFile(string basename) + { + string filename = TestFile.GetInputFileFullPath(Path.Combine(TestImages.Astc.InputFolder, basename + ".astc")); + File.Exists(filename).Should().BeTrue($"Testdata missing: {filename}"); + byte[] data = File.ReadAllBytes(filename); + data.Length.Should().BeGreaterThanOrEqualTo(16, "ASTC file too small"); + return [.. data.Skip(16)]; + } +} diff --git a/tests/ImageSharp.Textures.Tests/Formats/Astc/LogicalAstcBlockTests.cs b/tests/ImageSharp.Textures.Tests/Formats/Astc/LogicalAstcBlockTests.cs new file mode 100644 index 00000000..4e72c74c --- /dev/null +++ b/tests/ImageSharp.Textures.Tests/Formats/Astc/LogicalAstcBlockTests.cs @@ -0,0 +1,444 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using AwesomeAssertions; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Textures.Astc.ColorEncoding; +using SixLabors.ImageSharp.Textures.Astc.Core; +using SixLabors.ImageSharp.Textures.Astc.TexelBlock; +using SixLabors.ImageSharp.Textures.Tests.TestUtilities.ImageComparison; + +namespace SixLabors.ImageSharp.Textures.Tests.Formats.Astc; + +#nullable enable +public class LogicalAstcBlockTests +{ + [Theory] + [InlineData(FootprintType.Footprint4x4)] + [InlineData(FootprintType.Footprint5x5)] + [InlineData(FootprintType.Footprint8x8)] + [InlineData(FootprintType.Footprint10x10)] + [InlineData(FootprintType.Footprint12x12)] + public void Constructor_WithValidFootprintType_ShoulReturnExpectedFootprint(FootprintType footprintType) + { + Footprint footprint = Footprint.FromFootprintType(footprintType); + LogicalBlock logicalBlock = new(footprint); + + logicalBlock.GetFootprint().Should().Be(footprint); + logicalBlock.GetFootprint().Type.Should().Be(footprintType); + } + + [Fact] + public void GetFootprint_AfterConstruction_ShouldReturnOriginalFootprint() + { + Footprint footprint = Footprint.Get8x8(); + LogicalBlock logicalBlock = new(footprint); + + Footprint result = logicalBlock.GetFootprint(); + + result.Should().Be(footprint); + } + + [Theory] + [InlineData(0)] + [InlineData(32)] + [InlineData(64)] + public void SetWeightAt_WithValidWeight_ShouldStoreCorrectly(int weight) + { + LogicalBlock logicalBlock = new(Footprint.Get4x4()); + + logicalBlock.SetWeightAt(1, 1, weight); + + logicalBlock.WeightAt(1, 1).Should().Be(weight); + } + + [Theory] + [InlineData(-1)] + [InlineData(65)] + [InlineData(100)] + public void SetWeightAt_WithInvalidWeight_ShouldThrowArgumentOutOfRangeException(int weight) + { + LogicalBlock logicalBlock = new(Footprint.Get4x4()); + + Action action = () => logicalBlock.SetWeightAt(0, 0, weight); + + action.Should().Throw(); + } + + [Fact] + public void WeightAt_WithDefaultWeights_ShouldReturnZero() + { + LogicalBlock logicalBlock = new(Footprint.Get4x4()); + + int weight = logicalBlock.WeightAt(2, 2); + + weight.Should().Be(0); + } + + [Fact] + public void IsDualPlane_ByDefault_ShouldBeFalse() + { + LogicalBlock logicalBlock = new(Footprint.Get4x4()); + + bool result = logicalBlock.IsDualPlane(); + + result.Should().BeFalse(); + } + + [Fact] + public void SetDualPlaneChannel_WithValidChannel_ShouldEnableDualPlane() + { + LogicalBlock logicalBlock = new(Footprint.Get4x4()); + + logicalBlock.SetDualPlaneChannel(0); + + logicalBlock.IsDualPlane().Should().BeTrue(); + } + + [Fact] + public void SetDualPlaneChannel_WithNegativeValue_ShouldDisableDualPlane() + { + LogicalBlock logicalBlock = new(Footprint.Get4x4()); + logicalBlock.SetDualPlaneChannel(0); + + logicalBlock.SetDualPlaneChannel(-1); + + logicalBlock.IsDualPlane().Should().BeFalse(); + } + + [Fact] + public void SetDualPlaneWeightAt_WhenNotDualPlane_ShouldThrowInvalidOperationException() + { + LogicalBlock logicalBlock = new(Footprint.Get4x4()); + + Action action = () => logicalBlock.SetDualPlaneWeightAt(0, 2, 3, 1); + + action.Should().Throw() + .WithMessage("Not a dual plane block"); + } + + [Fact] + public void SetDualPlaneWeightAt_AfterEnablingDualPlane_ShouldPreserveOriginalWeight() + { + LogicalBlock logicalBlock = new(Footprint.Get4x4()); + logicalBlock.SetWeightAt(2, 3, 2); + logicalBlock.SetDualPlaneChannel(0); + + logicalBlock.SetDualPlaneWeightAt(0, 2, 3, 1); + + logicalBlock.WeightAt(2, 3).Should().Be(2); + logicalBlock.DualPlaneWeightAt(0, 2, 3).Should().Be(1); + } + + [Fact] + public void DualPlaneWeightAt_ForNonDualPlaneChannel_ShouldReturnOriginalWeight() + { + LogicalBlock logicalBlock = new(Footprint.Get4x4()); + logicalBlock.SetWeightAt(2, 3, 2); + logicalBlock.SetDualPlaneChannel(0); + logicalBlock.SetDualPlaneWeightAt(0, 2, 3, 1); + + for (int i = 1; i < 4; ++i) + { + logicalBlock.DualPlaneWeightAt(i, 2, 3).Should().Be(2); + } + } + + [Fact] + public void DualPlaneWeightAt_WhenNotDualPlane_ShouldReturnWeightAt() + { + LogicalBlock logicalBlock = new(Footprint.Get4x4()); + logicalBlock.SetWeightAt(2, 3, 42); + + int result = logicalBlock.DualPlaneWeightAt(0, 2, 3); + + result.Should().Be(42); + } + + [Fact] + public void SetDualPlaneWeightAt_ThenDisableDualPlane_ShouldResetToOriginalWeight() + { + LogicalBlock logicalBlock = new(Footprint.Get4x4()); + logicalBlock.SetWeightAt(2, 3, 2); + logicalBlock.SetDualPlaneChannel(0); + logicalBlock.SetDualPlaneWeightAt(0, 2, 3, 1); + + logicalBlock.SetDualPlaneChannel(-1); + + logicalBlock.IsDualPlane().Should().BeFalse(); + logicalBlock.WeightAt(2, 3).Should().Be(2); + for (int i = 0; i < 4; ++i) + { + logicalBlock.DualPlaneWeightAt(i, 2, 3).Should().Be(2); + } + } + + [Fact] + public void SetEndpoints_WithValidColors_ShouldStoreCorrectly() + { + LogicalBlock logicalBlock = new(Footprint.Get4x4()); + RgbaColor color1 = new(byte.MaxValue, byte.MinValue, byte.MinValue, byte.MaxValue); + RgbaColor color2 = new(byte.MinValue, byte.MaxValue, byte.MinValue, byte.MaxValue); + + logicalBlock.SetEndpoints(color1, color2, 0); + + // No direct getter, but we can verify through ColorAt + logicalBlock.SetWeightAt(0, 0, 0); + logicalBlock.SetWeightAt(1, 1, 64); + + RgbaColor colorAtMinWeight = logicalBlock.ColorAt(0, 0); + RgbaColor colorAtMaxWeight = logicalBlock.ColorAt(1, 1); + + colorAtMinWeight.R.Should().Be(color1.R); + colorAtMaxWeight.R.Should().BeCloseTo(color2.R, 1); + } + + [Fact] + public void ColorAt_WithCheckerboardWeights_ShouldInterpolateCorrectly() + { + LogicalBlock logicalBlock = new(Footprint.Get8x8()); + + // Create checkerboard weight pattern + for (int j = 0; j < 8; ++j) + { + for (int i = 0; i < 8; ++i) + { + if (((i ^ j) & 1) == 1) + { + logicalBlock.SetWeightAt(i, j, 0); + } + else + { + logicalBlock.SetWeightAt(i, j, 64); + } + } + } + + RgbaColor endpointA = new(123, 45, 67, 89); + RgbaColor endpointB = new(101, 121, 31, 41); + logicalBlock.SetEndpoints(endpointA, endpointB, 0); + + for (int j = 0; j < 8; ++j) + { + for (int i = 0; i < 8; ++i) + { + RgbaColor color = logicalBlock.ColorAt(i, j); + if (((i ^ j) & 1) == 1) + { + // Weight 0 = first endpoint + color.R.Should().Be(endpointA.R); + color.G.Should().Be(endpointA.G); + color.B.Should().Be(endpointA.B); + color.A.Should().Be(endpointA.A); + } + else + { + // Weight 64 = second endpoint + color.R.Should().Be(endpointB.R); + color.G.Should().Be(endpointB.G); + color.B.Should().Be(endpointB.B); + color.A.Should().Be(endpointB.A); + } + } + } + } + + [Theory] + [InlineData(-1, 0)] + [InlineData(0, -1)] + [InlineData(4, 0)] + [InlineData(0, 4)] + public void ColorAt_WithOutOfBoundsCoordinates_ShouldThrowArgumentOutOfRangeException(int x, int y) + { + LogicalBlock logicalBlock = new(Footprint.Get4x4()); + + Action action = () => logicalBlock.ColorAt(x, y); + + action.Should().Throw(); + } + + [Fact] + public void SetPartition_WithValidPartition_ShouldUpdateCorrectly() + { + Footprint footprint = Footprint.Get8x8(); + LogicalBlock logicalBlock = new(footprint); + + // Create partition with 2 subsets, all pixels assigned to subset 0 + Partition newPartition = new(footprint, 2, 5) + { + Assignment = new int[footprint.PixelCount] + }; + + logicalBlock.SetPartition(newPartition); + + // Should be able to set endpoints for both valid partitions (0 and 1) + RgbaColor redEndpoint = new(byte.MaxValue, byte.MinValue, byte.MinValue, byte.MaxValue); + RgbaColor blackEndpoint = new(byte.MinValue, byte.MinValue, byte.MinValue, byte.MaxValue); + RgbaColor greenEndpoint = new(byte.MinValue, byte.MaxValue, byte.MinValue, byte.MaxValue); + + Action setEndpoint0 = () => logicalBlock.SetEndpoints(redEndpoint, blackEndpoint, 0); + Action setEndpoint1 = () => logicalBlock.SetEndpoints(greenEndpoint, blackEndpoint, 1); + + setEndpoint0.Should().NotThrow(); + setEndpoint1.Should().NotThrow(); + + // Should not be able to set endpoints for non-existent partition 2 + Action setEndpoint2 = () => logicalBlock.SetEndpoints(redEndpoint, blackEndpoint, 2); + setEndpoint2.Should().Throw(); + } + + [Fact] + public void SetPartition_WithDifferentFootprint_ShouldThrowInvalidOperationException() + { + LogicalBlock logicalBlock = new(Footprint.Get4x4()); + Partition wrongPartition = new(Footprint.Get8x8(), 1, 0) + { + Assignment = new int[64] + }; + + Action action = () => logicalBlock.SetPartition(wrongPartition); + + action.Should() + .Throw() + .WithMessage("New partitions may not be for a different footprint"); + } + + [Theory] + [InlineData(-1)] + [InlineData(2)] + public void SetEndpoints_WithInvalidSubset_ShouldThrowArgumentOutOfRangeException(int subset) + { + LogicalBlock logicalBlock = new(Footprint.Get4x4()); + RgbaColor color1 = new(byte.MaxValue, byte.MinValue, byte.MinValue, byte.MaxValue); + RgbaColor color2 = new(byte.MinValue, byte.MaxValue, byte.MinValue, byte.MaxValue); + + Action action = () => logicalBlock.SetEndpoints(color1, color2, subset); + + action.Should().Throw(); + } + + [Fact] + public void UnpackLogicalBlock_WithErrorBlock_ShouldReturnNull() + { + UInt128 bits = UInt128.Zero; + BlockInfo info = BlockInfo.Decode(bits); + + LogicalBlock? result = LogicalBlock.UnpackLogicalBlock(Footprint.Get8x8(), bits, in info); + + result.Should().BeNull(); + } + + [Fact] + public void UnpackLogicalBlock_WithVoidExtentBlock_ShouldReturnLogicalBlock() + { + UInt128 bits = (UInt128)0xFFFFFFFFFFFFFDFCUL; + BlockInfo info = BlockInfo.Decode(bits); + + LogicalBlock? result = LogicalBlock.UnpackLogicalBlock(Footprint.Get8x8(), bits, in info); + + result.Should().NotBeNull(); + result!.GetFootprint().Should().Be(Footprint.Get8x8()); + } + + [Fact] + public void UnpackLogicalBlock_WithStandardBlock_ShouldReturnLogicalBlock() + { + UInt128 bits = (UInt128)0x0000000001FE000173UL; + BlockInfo info = BlockInfo.Decode(bits); + + LogicalBlock? result = LogicalBlock.UnpackLogicalBlock(Footprint.Get6x5(), bits, in info); + + result.Should().NotBeNull(); + result!.GetFootprint().Should().Be(Footprint.Get6x5()); + } + + [Theory] + + // Synthetic test images + [InlineData("footprint_4x4", false, FootprintType.Footprint4x4, 32, 32)] + [InlineData("footprint_5x4", false, FootprintType.Footprint5x4, 32, 32)] + [InlineData("footprint_5x5", false, FootprintType.Footprint5x5, 32, 32)] + [InlineData("footprint_6x5", false, FootprintType.Footprint6x5, 32, 32)] + [InlineData("footprint_6x6", false, FootprintType.Footprint6x6, 32, 32)] + [InlineData("footprint_8x5", false, FootprintType.Footprint8x5, 32, 32)] + [InlineData("footprint_8x6", false, FootprintType.Footprint8x6, 32, 32)] + [InlineData("footprint_8x8", false, FootprintType.Footprint8x8, 32, 32)] + [InlineData("footprint_10x5", false, FootprintType.Footprint10x5, 32, 32)] + [InlineData("footprint_10x6", false, FootprintType.Footprint10x6, 32, 32)] + [InlineData("footprint_10x8", false, FootprintType.Footprint10x8, 32, 32)] + [InlineData("footprint_10x10", false, FootprintType.Footprint10x10, 32, 32)] + [InlineData("footprint_12x10", false, FootprintType.Footprint12x10, 32, 32)] + [InlineData("footprint_12x12", false, FootprintType.Footprint12x12, 32, 32)] + + // RGB without alpha images + [InlineData("rgb_4x4", false, FootprintType.Footprint4x4, 224, 288)] + [InlineData("rgb_5x4", false, FootprintType.Footprint5x4, 224, 288)] + [InlineData("rgb_6x6", false, FootprintType.Footprint6x6, 224, 288)] + [InlineData("rgb_8x8", false, FootprintType.Footprint8x8, 224, 288)] + [InlineData("rgb_12x12", false, FootprintType.Footprint12x12, 224, 288)] + + // RGB with alpha images + [InlineData("atlas_small_4x4", true, FootprintType.Footprint4x4, 256, 256)] + [InlineData("atlas_small_5x5", true, FootprintType.Footprint5x5, 256, 256)] + [InlineData("atlas_small_6x6", true, FootprintType.Footprint6x6, 256, 256)] + [InlineData("atlas_small_8x8", true, FootprintType.Footprint8x8, 256, 256)] + public void UnpackLogicalBlock_FromImage_ShouldDecodeCorrectly( + string imageName, + bool hasAlpha, + FootprintType footprintType, + int width, + int height) + { + _ = hasAlpha; + Footprint footprint = Footprint.FromFootprintType(footprintType); + byte[] astcData = TestFile.Create(Path.Combine(TestImages.Astc.InputFolder, imageName + ".astc")).Bytes[16..]; + + using Image decodedImage = DecodeAstcBlocksToImage(footprint, astcData, width, height); + + string expectedPath = TestFile.GetInputFileFullPath(Path.Combine(TestImages.Astc.ExpectedFolder, imageName + ".bmp")); + using Image expectedImage = Image.Load(expectedPath); + ImageComparer.TolerantPercentage(1.0f).VerifySimilarity(expectedImage, decodedImage); + } + + private static Image DecodeAstcBlocksToImage(Footprint footprint, byte[] astcData, int width, int height) + { + // ASTC uses x/y ordering, so we flip Y to match ImageSharp's row/column origin. + Image image = new(width, height); + int blockWidth = footprint.Width; + int blockHeight = footprint.Height; + int blocksWide = (width + blockWidth - 1) / blockWidth; + + for (int i = 0; i < astcData.Length; i += PhysicalBlock.SizeInBytes) + { + int blockIndex = i / PhysicalBlock.SizeInBytes; + int blockX = blockIndex % blocksWide; + int blockY = blockIndex / blocksWide; + + byte[] blockSpan = astcData.AsSpan(i, PhysicalBlock.SizeInBytes).ToArray(); + UInt128 bits = new( + BitConverter.ToUInt64(blockSpan, 8), + BitConverter.ToUInt64(blockSpan, 0)); + BlockInfo info = BlockInfo.Decode(bits); + LogicalBlock? logicalBlock = LogicalBlock.UnpackLogicalBlock(footprint, bits, in info); + Assert.NotNull(logicalBlock); + + for (int y = 0; y < blockHeight; ++y) + { + for (int x = 0; x < blockWidth; ++x) + { + int px = (blockWidth * blockX) + x; + int py = (blockHeight * blockY) + y; + if (px >= width || py >= height) + { + continue; + } + + RgbaColor decoded = logicalBlock!.ColorAt(x, y); + image[px, height - 1 - py] = new Rgba32(decoded.R, decoded.G, decoded.B, decoded.A); + } + } + } + + return image; + } +} diff --git a/tests/ImageSharp.Textures.Tests/Formats/Astc/PartitionTests.cs b/tests/ImageSharp.Textures.Tests/Formats/Astc/PartitionTests.cs new file mode 100644 index 00000000..eeff44a7 --- /dev/null +++ b/tests/ImageSharp.Textures.Tests/Formats/Astc/PartitionTests.cs @@ -0,0 +1,227 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Globalization; +using AwesomeAssertions; +using SixLabors.ImageSharp.Textures.Astc.ColorEncoding; +using SixLabors.ImageSharp.Textures.Astc.Core; + +namespace SixLabors.ImageSharp.Textures.Tests.Formats.Astc; + +public class PartitionTests +{ + [Fact] + public void PartitionMetric_WithSimplePartitions_ShouldCalculateCorrectDistance() + { + Partition partitionA = new(Footprint.Get6x6(), 2) + { + Assignment = + [ + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1 + ] + }; + + Partition partitionB = new(Footprint.Get6x6(), 2) + { + Assignment = + [ + 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0 + ] + }; + + int distance = Partition.PartitionMetric(partitionA, partitionB); + + distance.Should().Be(2); + } + + [Fact] + public void PartitionMetric_WithDifferentPartCounts_ShouldCalculateCorrectDistance() + { + Partition partitionA = new(Footprint.Get4x4(), 2) + { + Assignment = + [ + 2, 2, 2, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 1 + ] + }; + + Partition partitionB = new(Footprint.Get4x4(), 3) + { + Assignment = + [ + 1, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 + ] + }; + + int distance = Partition.PartitionMetric(partitionA, partitionB); + + distance.Should().Be(3); + } + + [Fact] + public void PartitionMetric_WithDifferentMapping_ShouldCalculateCorrectDistance() + { + Partition partitionA = new(Footprint.Get4x4(), 2) + { + Assignment = + [ + 0, 1, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2, + 2, 2, 2, 2 + ] + }; + + Partition partitionB = new(Footprint.Get4x4(), 3) + { + Assignment = + [ + 1, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 + ] + }; + + int distance = Partition.PartitionMetric(partitionA, partitionB); + + distance.Should().Be(1); + } + + [Fact] + public void GetASTCPartition_WithSpecificParameters_ShouldReturnExpectedAssignment() + { + int[] expected = + [ + 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, + 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, + 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, + 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, + 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, + 0, 0, 0, 0, 1, 1, 1, 2, 2, 2 + ]; + + Partition partition = Partition.GetASTCPartition(Footprint.Get10x6(), 3, 557); + + partition.Assignment.Should().Equal(expected); + } + + [Fact] + public void GetASTCPartition_WithDifferentIds_ShouldProduceUniqueAssignments() + { + Partition partition0 = Partition.GetASTCPartition(Footprint.Get6x6(), 2, 0); + Partition partition1 = Partition.GetASTCPartition(Footprint.Get6x6(), 2, 1); + + partition0.Assignment.Should().NotEqual(partition1.Assignment); + } + + [Fact] + public void FindClosestASTCPartition_ShouldPreservePartitionCount() + { + Partition partition = new(Footprint.Get6x6(), 2) + { + Assignment = + [ + 0, 0, 1, 1, 1, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1 + ] + }; + + Partition closestAstcPartition = Partition.FindClosestASTCPartition(partition); + + closestAstcPartition.PartitionCount.Should().Be(partition.PartitionCount); + } + + [Fact] + public void FindClosestASTCPartition_WithModifiedPartition_ShouldReturnValidASTCPartition() + { + Partition astcPartition = Partition.GetASTCPartition(Footprint.Get12x12(), 3, 0x3CB); + Partition modifiedPartition = new(astcPartition.Footprint, astcPartition.PartitionCount) + { + Assignment = [.. astcPartition.Assignment] + }; + modifiedPartition.Assignment[0]++; + + // Find closest ASTC partition + Partition closestPartition = Partition.FindClosestASTCPartition(modifiedPartition); + + // The closest partition should be a valid ASTC partition with the same footprint and number of parts + closestPartition.Footprint.Should().Be(astcPartition.Footprint); + closestPartition.PartitionCount.Should().Be(astcPartition.PartitionCount); + closestPartition.PartitionId.Should().HaveValue("returned partition should have a valid ID"); + + // Verify we can retrieve the same partition again using its ID + Partition verifyPartition = Partition.GetASTCPartition( + closestPartition.Footprint, + closestPartition.PartitionCount, + closestPartition.PartitionId!.Value); + verifyPartition.Should().Be(closestPartition); + } + + [Theory] + [InlineData(FootprintType.Footprint4x4)] + [InlineData(FootprintType.Footprint5x4)] + [InlineData(FootprintType.Footprint5x5)] + [InlineData(FootprintType.Footprint6x5)] + [InlineData(FootprintType.Footprint6x6)] + [InlineData(FootprintType.Footprint8x5)] + [InlineData(FootprintType.Footprint8x6)] + [InlineData(FootprintType.Footprint8x8)] + [InlineData(FootprintType.Footprint10x5)] + [InlineData(FootprintType.Footprint10x6)] + [InlineData(FootprintType.Footprint10x8)] + [InlineData(FootprintType.Footprint10x10)] + [InlineData(FootprintType.Footprint12x10)] + [InlineData(FootprintType.Footprint12x12)] + public void FindClosestASTCPartition_WithRandomPartitions_ShouldReturnFewerOrEqualSubsets(FootprintType footprintType) + { + Footprint footprint = Footprint.FromFootprintType(footprintType); + Random random = new(unchecked((int)0xdeadbeef)); + + const int numTests = 15; // Tests per footprint type + for (int i = 0; i < numTests; i++) + { + // Create random partition + int numParts = 2 + random.Next(3); // 2, 3, or 4 parts + int[] assignment = new int[footprint.PixelCount]; + for (int j = 0; j < footprint.PixelCount; j++) + { + assignment[j] = random.Next(numParts); + } + + Partition partition = new(footprint, numParts) + { + Assignment = assignment + }; + + Partition astcPartition = Partition.FindClosestASTCPartition(partition); + + // Matched partition should have fewer or equal subsets + astcPartition.PartitionCount + .Should() + .BeLessThanOrEqualTo( + partition.PartitionCount, + $"Footprint {footprintType}, Test #{i}: Selected partition with ID {astcPartition.PartitionId?.ToString(CultureInfo.InvariantCulture) ?? "null"}"); + } + } +} diff --git a/tests/ImageSharp.Textures.Tests/Formats/Astc/PhysicalAstcBlockTests.cs b/tests/ImageSharp.Textures.Tests/Formats/Astc/PhysicalAstcBlockTests.cs new file mode 100644 index 00000000..7fa383d9 --- /dev/null +++ b/tests/ImageSharp.Textures.Tests/Formats/Astc/PhysicalAstcBlockTests.cs @@ -0,0 +1,616 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using AwesomeAssertions; +using SixLabors.ImageSharp.Textures.Astc.ColorEncoding; +using SixLabors.ImageSharp.Textures.Astc.TexelBlock; + +namespace SixLabors.ImageSharp.Textures.Tests.Formats.Astc; + +public class PhysicalAstcBlockTests +{ + private static readonly UInt128 ErrorBlock = UInt128.Zero; + + [Fact] + public void Create_WithUInt64_ShouldRoundTripBlockBits() + { + const ulong expectedLow = 0x0000000001FE000173UL; + + PhysicalBlock block = PhysicalBlock.Create(expectedLow); + + block.BlockBits.Should().Be((UInt128)expectedLow); + } + + [Fact] + public void Create_WithUInt128_ShouldRoundTripBlockBits() + { + UInt128 expected = (UInt128)0x12345678ABCDEF00UL | ((UInt128)0xCAFEBABEDEADBEEFUL << 64); + + PhysicalBlock block = PhysicalBlock.Create(expected); + + block.BlockBits.Should().Be(expected); + } + + [Fact] + public void Create_WithMatchingUInt64AndUInt128_ShouldProduceIdenticalBlocks() + { + const ulong value = 0x0000000001FE000173UL; + + PhysicalBlock block1 = PhysicalBlock.Create(value); + PhysicalBlock block2 = PhysicalBlock.Create((UInt128)value); + + block1.BlockBits.Should().Be(block2.BlockBits); + } + + [Fact] + public void IsVoidExtent_WithKnownVoidExtentPattern_ShouldReturnTrue() + { + PhysicalBlock block = PhysicalBlock.Create((UInt128)0xFFFFFFFFFFFFFDFCUL); + + block.IsVoidExtent.Should().BeTrue(); + } + + [Fact] + public void IsVoidExtent_WithStandardBlock_ShouldReturnFalse() + { + PhysicalBlock block = PhysicalBlock.Create(0x0000000001FE000173UL); + + block.IsVoidExtent.Should().BeFalse(); + } + + [Fact] + public void IsVoidExtent_WithErrorBlock_ShouldReturnFalse() + { + PhysicalBlock block = PhysicalBlock.Create(ErrorBlock); + + block.IsVoidExtent.Should().BeFalse(); + } + + [Fact] + public void GetVoidExtentCoordinates_WithValidVoidExtentBlock_ShouldReturnExpectedCoordinates() + { + PhysicalBlock block = PhysicalBlock.Create(0xFFF8003FFE000DFCUL); + + int[] coords = block.GetVoidExtentCoordinates(); + + coords.Should().NotBeNull(); + coords.Should().HaveCount(4); + coords![0].Should().Be(0); + coords[1].Should().Be(8191); + coords[2].Should().Be(0); + coords[3].Should().Be(8191); + } + + [Fact] + public void GetVoidExtentCoordinates_WithAllOnesPattern_ShouldReturnNull() + { + PhysicalBlock block = PhysicalBlock.Create(0xFFFFFFFFFFFFFDFCUL); + + int[] coords = block.GetVoidExtentCoordinates(); + + block.IsVoidExtent.Should().BeTrue(); + coords.Should().BeNull(); + } + + [Fact] + public void Create_WithInvalidVoidExtentCoordinates_ShouldBeIllegalEncoding() + { + PhysicalBlock block1 = PhysicalBlock.Create(0x0008004002001DFCUL); + PhysicalBlock block2 = PhysicalBlock.Create(0x0007FFC001FFFDFCUL); + + block1.IsIllegalEncoding.Should().BeTrue(); + block2.IsIllegalEncoding.Should().BeTrue(); + } + + [Fact] + public void Create_WithModifiedHighBitsOnVoidExtent_ShouldStillBeValid() + { + PhysicalBlock original = PhysicalBlock.Create(0xFFF8003FFE000DFCUL, 0UL); + PhysicalBlock modified = PhysicalBlock.Create(0xFFF8003FFE000DFCUL, 0xdeadbeefdeadbeef); + + original.IsIllegalEncoding.Should().BeFalse(); + original.IsVoidExtent.Should().BeTrue(); + modified.IsIllegalEncoding.Should().BeFalse(); + modified.IsVoidExtent.Should().BeTrue(); + } + + [Fact] + public void GetWeightRange_WithValidBlock_ShouldReturn7() + { + PhysicalBlock block = PhysicalBlock.Create(0x0000000001FE000173UL); + + int? weightRange = block.GetWeightRange(); + + weightRange.Should().HaveValue(); + weightRange.Should().Be(7); + } + + [Fact] + public void GetWeightRange_WithTooManyBits_ShouldReturnNull() + { + PhysicalBlock block = PhysicalBlock.Create(0x0000000001FE000373UL); + + int? weightRange = block.GetWeightRange(); + + weightRange.Should().BeNull(); + } + + [Fact] + public void GetWeightRange_WithOneBitPerWeight_ShouldReturn1() + { + PhysicalBlock block = PhysicalBlock.Create(0x4000000000800D44UL); + + int? weightRange = block.GetWeightRange(); + + weightRange.Should().HaveValue(); + weightRange.Should().Be(1); + } + + [Fact] + public void GetWeightRange_WithErrorBlock_ShouldReturnNull() + { + PhysicalBlock block = PhysicalBlock.Create(ErrorBlock); + + int? weightRange = block.GetWeightRange(); + + weightRange.Should().BeNull(); + } + + [Fact] + public void GetWeightGridDimensions_WithValidBlock_ShouldReturn6x5() + { + PhysicalBlock block = PhysicalBlock.Create(0x0000000001FE000173UL); + + (int Width, int Height)? dims = block.GetWeightGridDimensions(); + + dims.Should().NotBeNull(); + dims!.Value.Width.Should().Be(6); + dims.Value.Height.Should().Be(5); + } + + [Fact] + public void GetWeightGridDimensions_WithTooManyBitsForGrid_ShouldReturnNull() + { + PhysicalBlock block = PhysicalBlock.Create(0x0000000001FE000373UL); + + (int Width, int Height)? dims = block.GetWeightGridDimensions(); + + dims.Should().BeNull(); + string error = block.IdentifyInvalidEncodingIssues(); + error.Should().Contain("Invalid block encoding"); + } + + [Fact] + public void GetWeightGridDimensions_WithDualPlaneBlock_ShouldReturn3x5() + { + PhysicalBlock block = PhysicalBlock.Create(0x0000000001FE0005FFUL); + + (int Width, int Height)? dims = block.GetWeightGridDimensions(); + + dims.Should().NotBeNull(); + dims!.Value.Width.Should().Be(3); + dims.Value.Height.Should().Be(5); + } + + [Fact] + public void GetWeightGridDimensions_WithNonSharedCEM_ShouldReturn8x8() + { + PhysicalBlock block = PhysicalBlock.Create(0x4000000000800D44UL); + + (int Width, int Height)? dims = block.GetWeightGridDimensions(); + + dims.Should().NotBeNull(); + dims!.Value.Width.Should().Be(8); + dims.Value.Height.Should().Be(8); + } + + [Fact] + public void GetWeightGridDimensions_WithErrorBlock_ShouldReturnNull() + { + PhysicalBlock block = PhysicalBlock.Create(ErrorBlock); + + (int Width, int Height)? dims = block.GetWeightGridDimensions(); + + dims.Should().BeNull(); + } + + [Fact] + public void IsDualPlane_WithSinglePlaneBlock_ShouldReturnFalse() + { + PhysicalBlock block = PhysicalBlock.Create(0x0000000001FE000173UL); + + block.IsDualPlane.Should().BeFalse(); + } + + [Fact] + public void IsDualPlane_WithDualPlaneBlock_ShouldReturnTrue() + { + PhysicalBlock block = PhysicalBlock.Create(0x0000000001FE0005FFUL); + + block.IsDualPlane.Should().BeTrue(); + } + + [Fact] + public void IsDualPlane_WithErrorBlock_ShouldReturnFalse() + { + PhysicalBlock block = PhysicalBlock.Create(ErrorBlock); + + block.IsDualPlane.Should().BeFalse(); + } + + [Fact] + public void IsDualPlane_WithInvalidEncoding_ShouldReturnFalse() + { + PhysicalBlock block = PhysicalBlock.Create(0x0000000001FE000573UL); + + block.IsDualPlane.Should().BeFalse(); + block.GetWeightGridDimensions().Should().BeNull(); + block.IdentifyInvalidEncodingIssues().Should().Contain("Invalid block encoding"); + } + + [Fact] + public void IsDualPlane_WithValidSinglePlaneBlock_ShouldHaveValidEncoding() + { + PhysicalBlock block = PhysicalBlock.Create(0x0000000001FE000108UL); + + block.IsDualPlane.Should().BeFalse(); + block.IsIllegalEncoding.Should().BeFalse(); + } + + [Fact] + public void GetWeightBitCount_WithStandardBlock_ShouldReturn90() + { + PhysicalBlock block = PhysicalBlock.Create(0x0000000001FE000173UL); + + int? bitCount = block.GetWeightBitCount(); + + bitCount.Should().Be(90); + } + + [Fact] + public void GetWeightBitCount_WithDualPlaneBlock_ShouldReturn90() + { + PhysicalBlock block = PhysicalBlock.Create(0x0000000001FE0005FFUL); + + int? bitCount = block.GetWeightBitCount(); + + bitCount.Should().Be(90); + } + + [Fact] + public void GetWeightBitCount_WithErrorBlock_ShouldReturnNull() + { + PhysicalBlock block = PhysicalBlock.Create(ErrorBlock); + + int? bitCount = block.GetWeightBitCount(); + + bitCount.Should().BeNull(); + } + + [Fact] + public void GetWeightBitCount_WithVoidExtent_ShouldReturnNull() + { + PhysicalBlock block = PhysicalBlock.Create(0xFFF8003FFE000DFCUL); + + int? bitCount = block.GetWeightBitCount(); + + bitCount.Should().BeNull(); + } + + [Fact] + public void GetWeightBitCount_WithInvalidBlock_ShouldReturnNull() + { + PhysicalBlock block = PhysicalBlock.Create(0x0000000001FE000573UL); + + int? bitCount = block.GetWeightBitCount(); + + bitCount.Should().BeNull(); + } + + [Fact] + public void GetWeightStartBit_WithNonSharedCEM_ShouldReturn64() + { + PhysicalBlock block = PhysicalBlock.Create(0x4000000000800D44UL); + + int? startBit = block.GetWeightStartBit(); + + startBit.Should().Be(64); + } + + [Fact] + public void GetWeightStartBit_WithErrorBlock_ShouldReturnNull() + { + PhysicalBlock block = PhysicalBlock.Create(ErrorBlock); + + int? startBit = block.GetWeightStartBit(); + + startBit.Should().BeNull(); + } + + [Fact] + public void GetWeightStartBit_WithVoidExtent_ShouldReturnNull() + { + PhysicalBlock block = PhysicalBlock.Create(0xFFF8003FFE000DFCUL); + + int? startBit = block.GetWeightStartBit(); + + startBit.Should().BeNull(); + } + + [Fact] + public void IsIllegalEncoding_WithValidBlocks_ShouldReturnFalse() + { + PhysicalBlock.Create(0x0000000001FE000173UL).IsIllegalEncoding.Should().BeFalse(); + PhysicalBlock.Create(0x0000000001FE0005FFUL).IsIllegalEncoding.Should().BeFalse(); + PhysicalBlock.Create(0x0000000001FE000108UL).IsIllegalEncoding.Should().BeFalse(); + } + + [Fact] + public void IdentifyInvalidEncodingIssues_WithZeroBlock_ShouldReturnReservedBlockModeError() + { + PhysicalBlock block = PhysicalBlock.Create(ErrorBlock); + + string error = block.IdentifyInvalidEncodingIssues(); + + error.Should().NotBeNull(); + error.Should().Contain("Invalid block encoding"); + } + + [Fact] + public void IdentifyInvalidEncodingIssues_WithTooManyWeightBits_ShouldReturnError() + { + PhysicalBlock block = PhysicalBlock.Create(0x0000000001FE000573UL); + + string error = block.IdentifyInvalidEncodingIssues(); + + error.Should().NotBeNull(); + error.Should().Contain("Invalid block encoding"); + } + + [Theory] + [InlineData(0x0000000001FE0005A8UL)] + [InlineData(0x0000000001FE000588UL)] + [InlineData(0x0000000001FE00002UL)] + public void IdentifyInvalidEncodingIssues_WithInvalidBlocks_ShouldReturnError(ulong blockBits) + { + PhysicalBlock block = PhysicalBlock.Create(blockBits); + + string error = block.IdentifyInvalidEncodingIssues(); + + error.Should().NotBeNull(); + } + + [Fact] + public void IdentifyInvalidEncodingIssues_WithDualPlaneFourPartitions_ShouldReturnError() + { + PhysicalBlock block = PhysicalBlock.Create(0x000000000000001D1FUL); + + string error = block.IdentifyInvalidEncodingIssues(); + + block.GetPartitionsCount().Should().BeNull(); + error.Should().NotBeNull(); + error.Should().Contain("Invalid block encoding"); + } + + [Theory] + [InlineData(0x000000000000000973UL)] + [InlineData(0x000000000000001173UL)] + [InlineData(0x000000000000001973UL)] + public void GetPartitionsCount_WithInvalidPartitionConfig_ShouldReturnNull(ulong blockBits) + { + PhysicalBlock block = PhysicalBlock.Create(blockBits); + + int? partitions = block.GetPartitionsCount(); + + partitions.Should().BeNull(); + } + + [Theory] + [InlineData(0x0000000001FE000173UL, 1)] + [InlineData(0x0000000001FE0005FFUL, 1)] + [InlineData(0x0000000001FE000108UL, 1)] + [InlineData(0x4000000000800D44UL, 2)] + public void GetPartitionsCount_WithValidBlock_ShouldReturnExpectedCount(ulong blockBits, int expectedCount) + { + PhysicalBlock block = PhysicalBlock.Create(blockBits); + + int? count = block.GetPartitionsCount(); + + count.Should().Be(expectedCount); + } + + [Theory] + [InlineData(0x4000000000FFED44UL, 0x3FF)] + [InlineData(0x4000000000AAAD44UL, 0x155)] + public void GetPartitionId_WithValidMultiPartitionBlock_ShouldReturnExpectedId(ulong blockBits, int expectedId) + { + PhysicalBlock block = PhysicalBlock.Create(blockBits); + + int? partitionId = block.GetPartitionId(); + + partitionId.Should().Be(expectedId); + } + + [Fact] + public void GetPartitionId_WithErrorBlock_ShouldReturnNull() + { + PhysicalBlock block = PhysicalBlock.Create(ErrorBlock); + + int? partitionId = block.GetPartitionId(); + + partitionId.Should().BeNull(); + } + + [Fact] + public void GetPartitionId_WithVoidExtent_ShouldReturnNull() + { + PhysicalBlock block = PhysicalBlock.Create(0xFFF8003FFE000DFCUL); + + int? partitionId = block.GetPartitionId(); + + partitionId.Should().BeNull(); + } + + [Fact] + public void GetEndpointMode_WithFourPartitionBlock_ShouldReturnSameModeForAll() + { + PhysicalBlock block = PhysicalBlock.Create(0x000000000000001961UL); + + for (int i = 0; i < 4; ++i) + { + ColorEndpointMode? mode = block.GetEndpointMode(i); + mode.Should().Be(ColorEndpointMode.LdrLumaDirect); + } + } + + [Fact] + public void GetEndpointMode_WithNonSharedCEM_ShouldReturnDifferentModes() + { + PhysicalBlock block = PhysicalBlock.Create(0x4000000000800D44UL); + + ColorEndpointMode? mode0 = block.GetEndpointMode(0); + ColorEndpointMode? mode1 = block.GetEndpointMode(1); + + mode0.Should().Be(ColorEndpointMode.LdrLumaDirect); + mode1.Should().Be(ColorEndpointMode.LdrLumaBaseOffset); + } + + [Fact] + public void GetEndpointMode_WithVoidExtent_ShouldReturnNull() + { + PhysicalBlock block = PhysicalBlock.Create(0xFFF8003FFE000DFCUL); + + ColorEndpointMode? mode = block.GetEndpointMode(0); + + mode.Should().BeNull(); + } + + [Theory] + [InlineData(1)] + [InlineData(-1)] + [InlineData(100)] + public void GetEndpointMode_WithInvalidPartitionIndex_ShouldReturnNull(int index) + { + PhysicalBlock block = PhysicalBlock.Create(0x0000000001FE000173UL); + + ColorEndpointMode? mode = block.GetEndpointMode(index); + + mode.Should().BeNull(); + } + + [Fact] + public void GetColorValuesCount_WithStandardBlock_ShouldReturn2() + { + PhysicalBlock block = PhysicalBlock.Create(0x0000000001FE000173UL); + + int? count = block.GetColorValuesCount(); + + count.Should().Be(2); + } + + [Fact] + public void GetColorValuesCount_WithVoidExtent_ShouldReturn4() + { + PhysicalBlock block = PhysicalBlock.Create(0xFFF8003FFE000DFCUL); + + int? count = block.GetColorValuesCount(); + + count.Should().Be(4); + } + + [Fact] + public void GetColorValuesCount_WithErrorBlock_ShouldReturnNull() + { + PhysicalBlock block = PhysicalBlock.Create(ErrorBlock); + + int? count = block.GetColorValuesCount(); + + count.Should().BeNull(); + } + + [Fact] + public void GetColorBitCount_WithStandardBlock_ShouldReturn16() + { + PhysicalBlock block = PhysicalBlock.Create(0x0000000001FE000173UL); + + int? bitCount = block.GetColorBitCount(); + + bitCount.Should().Be(16); + } + + [Fact] + public void GetColorBitCount_WithVoidExtent_ShouldReturn64() + { + PhysicalBlock block = PhysicalBlock.Create(0xFFF8003FFE000DFCUL); + + int? bitCount = block.GetColorBitCount(); + + bitCount.Should().Be(64); + } + + [Fact] + public void GetColorBitCount_WithErrorBlock_ShouldReturnNull() + { + PhysicalBlock block = PhysicalBlock.Create(ErrorBlock); + + int? bitCount = block.GetColorBitCount(); + + bitCount.Should().BeNull(); + } + + [Fact] + public void GetColorValuesRange_WithStandardBlock_ShouldReturn255() + { + PhysicalBlock block = PhysicalBlock.Create(0x0000000001FE000173UL); + + int? range = block.GetColorValuesRange(); + + range.Should().Be(255); + } + + [Fact] + public void GetColorValuesRange_WithVoidExtent_ShouldReturnMaxUInt16() + { + PhysicalBlock block = PhysicalBlock.Create(0xFFF8003FFE000DFCUL); + + int? range = block.GetColorValuesRange(); + + range.Should().Be((1 << 16) - 1); + } + + [Fact] + public void GetColorValuesRange_WithErrorBlock_ShouldReturnNull() + { + PhysicalBlock block = PhysicalBlock.Create(ErrorBlock); + + int? range = block.GetColorValuesRange(); + + range.Should().BeNull(); + } + + [Theory] + [InlineData(0x0000000001FE000173UL, 17)] + [InlineData(0x0000000001FE0005FFUL, 17)] + [InlineData(0x0000000001FE000108UL, 17)] + [InlineData(0x4000000000FFED44UL, 29)] + [InlineData(0x4000000000AAAD44UL, 29)] + [InlineData(0xFFF8003FFE000DFCUL, 64)] + public void GetColorStartBit_WithVariousBlocks_ShouldReturnExpectedValue(ulong blockBits, int expectedStartBit) + { + PhysicalBlock block = PhysicalBlock.Create(blockBits); + + int? startBit = block.GetColorStartBit(); + + startBit.Should().Be(expectedStartBit); + } + + [Fact] + public void GetColorStartBit_WithErrorBlock_ShouldReturnNull() + { + PhysicalBlock block = PhysicalBlock.Create(ErrorBlock); + + int? startBit = block.GetColorStartBit(); + + startBit.Should().BeNull(); + } +} diff --git a/tests/ImageSharp.Textures.Tests/Formats/Astc/QuantizationTests.cs b/tests/ImageSharp.Textures.Tests/Formats/Astc/QuantizationTests.cs new file mode 100644 index 00000000..e261b4a7 --- /dev/null +++ b/tests/ImageSharp.Textures.Tests/Formats/Astc/QuantizationTests.cs @@ -0,0 +1,398 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using AwesomeAssertions; +using SixLabors.ImageSharp.Textures.Astc.BiseEncoding; +using SixLabors.ImageSharp.Textures.Astc.BiseEncoding.Quantize; + +namespace SixLabors.ImageSharp.Textures.Tests.Formats.Astc; + +public class QuantizationTests +{ + [Fact] + public void QuantizeCEValueToRange_WithMaxValue_ShouldNotExceedRange() + { + for (int range = Quantization.EndpointRangeMinValue; range <= byte.MaxValue; range++) + { + Quantization.QuantizeCEValueToRange(byte.MaxValue, range).Should().BeLessThanOrEqualTo(range); + } + } + + [Fact] + public void QuantizeWeightToRange_WithMaxValue_ShouldNotExceedRange() + { + for (int range = 1; range < Quantization.WeightRangeMaxValue; range++) + { + Quantization.QuantizeWeightToRange(64, range).Should().BeLessThanOrEqualTo(range); + } + } + + [Fact] + public void QuantizeCEValueToRange_WithVariousValues_ShouldNotExceedRange() + { + int[] ranges = BoundedIntegerSequenceCodec.MaxRanges; + int[] testValues = [0, 4, 15, 22, 66, 91, 126]; + + foreach (int range in ranges.Where(r => r >= Quantization.EndpointRangeMinValue)) + { + foreach (int value in testValues) + { + Quantization.QuantizeCEValueToRange(value, range).Should().BeLessThanOrEqualTo(range); + } + } + } + + [Fact] + public void QuantizeWeightToRange_WithVariousValues_ShouldNotExceedRange() + { + int[] ranges = BoundedIntegerSequenceCodec.MaxRanges; + int[] testValues = [0, 4, 15, 22]; + + foreach (int range in ranges.Where(r => r <= Quantization.WeightRangeMaxValue)) + { + foreach (int value in testValues) + { + Quantization.QuantizeWeightToRange(value, range).Should().BeLessThanOrEqualTo(range); + } + } + } + + [Fact] + public void QuantizeWeight_ThenUnquantize_ShouldReturnOriginalQuantizedValue() + { + int[] ranges = BoundedIntegerSequenceCodec.MaxRanges; + + foreach (int range in ranges.Where(r => r <= Quantization.WeightRangeMaxValue)) + { + for (int quantizedValue = 0; quantizedValue <= range; ++quantizedValue) + { + int unquantized = Quantization.UnquantizeWeightFromRange(quantizedValue, range); + int requantized = Quantization.QuantizeWeightToRange(unquantized, range); + + requantized.Should().Be(quantizedValue); + } + } + } + + [Fact] + public void QuantizeCEValue_ThenUnquantize_ShouldReturnOriginalQuantizedValue() + { + int[] ranges = BoundedIntegerSequenceCodec.MaxRanges; + + foreach (int range in ranges.Where(r => r >= Quantization.EndpointRangeMinValue)) + { + for (int quantizedValue = 0; quantizedValue <= range; ++quantizedValue) + { + int unquantized = Quantization.UnquantizeCEValueFromRange(quantizedValue, range); + int requantized = Quantization.QuantizeCEValueToRange(unquantized, range); + + requantized.Should().Be(quantizedValue); + } + } + } + + [Theory] + [InlineData(2, 7)] + [InlineData(7, 7)] + [InlineData(39, 63)] + [InlineData(66, 79)] + [InlineData(91, 191)] + [InlineData(126, 255)] + [InlineData(255, 255)] + public void UnquantizeCEValueFromRange_ShouldProduceValidByteValue(int quantizedValue, int range) + { + int result = Quantization.UnquantizeCEValueFromRange(quantizedValue, range); + + result.Should().BeLessThan(256); + } + + [Theory] + [InlineData(0, 1)] + [InlineData(2, 7)] + [InlineData(7, 7)] + [InlineData(29, 31)] + public void UnquantizeWeightFromRange_ShouldNotExceed64(int quantizedValue, int range) + { + int result = Quantization.UnquantizeWeightFromRange(quantizedValue, range); + + result.Should().BeLessThanOrEqualTo(64); + } + + [Fact] + public void Quantize_WithDesiredRange_ShouldMatchExpectedRangeOutput() + { + int[] ranges = BoundedIntegerSequenceCodec.MaxRanges; + int rangeIndex = 0; + + for (int desiredRange = 1; desiredRange <= byte.MaxValue; ++desiredRange) + { + while (rangeIndex + 1 < ranges.Length && ranges[rangeIndex + 1] <= desiredRange) + { + ++rangeIndex; + } + + int expectedRange = ranges[rangeIndex]; + + // Test CE values + if (desiredRange >= Quantization.EndpointRangeMinValue) + { + int[] testValues = [0, 13, 173, 208, 255]; + foreach (int value in testValues) + { + Quantization.QuantizeCEValueToRange(value, desiredRange) + .Should().Be(Quantization.QuantizeCEValueToRange(value, expectedRange)); + } + } + + // Test weight values + if (desiredRange <= Quantization.WeightRangeMaxValue) + { + int[] testValues = [0, 12, 23, 63]; + foreach (int value in testValues) + { + Quantization.QuantizeWeightToRange(value, desiredRange) + .Should().Be(Quantization.QuantizeWeightToRange(value, expectedRange)); + } + } + } + + rangeIndex.Should().Be(ranges.Length - 1); + } + + [Fact] + public void QuantizeCEValueToRange_WithRangeByteMax_ShouldBeIdentity() + { + for (int value = byte.MinValue; value <= byte.MaxValue; value++) + { + Quantization.QuantizeCEValueToRange(value, byte.MaxValue).Should().Be(value); + } + } + + [Fact] + public void QuantizeCEValueToRange_ShouldBeMonotonicIncreasing() + { + for (int numBits = 3; numBits < 8; numBits++) + { + int range = (1 << numBits) - 1; + int lastQuantizedValue = -1; + + for (int value = byte.MinValue; value <= byte.MaxValue; value++) + { + int quantizedValue = Quantization.QuantizeCEValueToRange(value, range); + + quantizedValue.Should().BeGreaterThanOrEqualTo(lastQuantizedValue); + lastQuantizedValue = quantizedValue; + } + + lastQuantizedValue.Should().Be(range); + } + } + + [Fact] + public void QuantizeWeightToRange_ShouldBeMonotonicallyIncreasing() + { + for (int numBits = 3; numBits < 8; ++numBits) + { + int range = (1 << numBits) - 1; + + if (range > Quantization.WeightRangeMaxValue) + { + continue; + } + + int lastQuantizedValue = -1; + + for (int value = 0; value <= 64; ++value) + { + int quantizedValue = Quantization.QuantizeWeightToRange(value, range); + + quantizedValue.Should().BeGreaterThanOrEqualTo(lastQuantizedValue); + lastQuantizedValue = quantizedValue; + } + + lastQuantizedValue.Should().Be(range); + } + } + + [Fact] + public void QuantizeCEValueToRange_WithSmallBitRanges_ShouldQuantizeLowValuesToZero() + { + for (int numBits = 1; numBits <= 8; ++numBits) + { + int range = (1 << numBits) - 1; + + if (range < Quantization.EndpointRangeMinValue) + { + continue; + } + + const int cevBits = 8; + int halfMaxQuantBits = Math.Max(0, cevBits - numBits - 1); + int largestCevToZero = (1 << halfMaxQuantBits) - 1; + + Quantization.QuantizeCEValueToRange(largestCevToZero, range).Should().Be(0); + } + } + + [Fact] + public void QuantizeWeightToRange_WithSmallBitRanges_ShouldQuantizeLowValuesToZero() + { + for (int numBits = 1; numBits <= 8; numBits++) + { + int range = (1 << numBits) - 1; + + if (range > Quantization.WeightRangeMaxValue) + { + continue; + } + + const int weightBits = 6; + int halfMaxQuantBits = Math.Max(0, weightBits - numBits - 1); + int largestWeightToZero = (1 << halfMaxQuantBits) - 1; + + Quantization.QuantizeWeightToRange(largestWeightToZero, range).Should().Be(0); + } + } + + [Fact] + public void UnquantizeWeightFromRange_WithQuintRange_ShouldMatchExpected() + { + List values = [4, 6, 4, 6, 7, 5, 7, 5]; + List quintExpected = [14, 21, 14, 21, 43, 50, 43, 50]; + + List quantized = [.. values.Select(v => Quantization.UnquantizeWeightFromRange(v, 9))]; + + quantized.Should().Equal(quintExpected); + } + + [Fact] + public void UnquantizeWeightFromRange_WithTritRange_ShouldMatchExpected() + { + List values = [4, 6, 4, 6, 7, 5, 7, 5]; + List tritExpected = [5, 23, 5, 23, 41, 59, 41, 59]; + + List quantized = [.. values.Select(v => Quantization.UnquantizeWeightFromRange(v, 11))]; + + quantized.Should().Equal(tritExpected); + } + + [Fact] + public void QuantizeCEValueToRange_WithInvalidMinRange_ShouldThrowArgumentOutOfRangeException() + { + for (int range = 0; range < Quantization.EndpointRangeMinValue; range++) + { + Action action = () => Quantization.QuantizeCEValueToRange(0, range); + action.Should().Throw(); + } + } + + [Fact] + public void UnquantizeCEValueFromRange_WithInvalidMinRange_ShouldThrowArgumentOutOfRangeException() + { + for (int range = 0; range < Quantization.EndpointRangeMinValue; range++) + { + Action action = () => Quantization.UnquantizeCEValueFromRange(0, range); + action.Should().Throw(); + } + } + + [Fact] + public void QuantizeWeightToRange_WithZeroRange_ShouldThrowArgumentOutOfRangeException() + { + Action action = () => Quantization.QuantizeWeightToRange(0, 0); + + action.Should().Throw(); + } + + [Fact] + public void UnquantizeWeightFromRange_WithZeroRange_ShouldThrowArgumentOutOfRangeException() + { + Action action = () => Quantization.UnquantizeWeightFromRange(0, 0); + + action.Should().Throw(); + } + + [Theory] + [InlineData(-1, 10)] + [InlineData(256, 7)] + [InlineData(10000, 17)] + public void QuantizeCEValueToRange_WithInvalidValue_ShouldThrowArgumentOutOfRangeException(int value, int range) + { + Action action = () => Quantization.QuantizeCEValueToRange(value, range); + + action.Should().Throw(); + } + + [Theory] + [InlineData(-1, 10)] + [InlineData(8, 7)] + [InlineData(-1000, 17)] + public void UnquantizeCEValueFromRange_WithInvalidValue_ShouldThrowArgumentOutOfRangeException(int value, int range) + { + Action action = () => Quantization.UnquantizeCEValueFromRange(value, range); + + action.Should().Throw(); + } + + [Theory] + [InlineData(0, -7)] + [InlineData(0, 257)] + public void QuantizeCEValueToRange_WithInvalidRange_ShouldThrowArgumentOutOfRangeException(int value, int range) + { + Action action = () => Quantization.QuantizeCEValueToRange(value, range); + + action.Should().Throw(); + } + + [Theory] + [InlineData(0, -17)] + [InlineData(0, 256)] + public void UnquantizeCEValueFromRange_WithInvalidRange_ShouldThrowArgumentOutOfRangeException(int value, int range) + { + Action action = () => Quantization.UnquantizeCEValueFromRange(value, range); + + action.Should().Throw(); + } + + [Theory] + [InlineData(-1, 10)] + [InlineData(256, 7)] + [InlineData(10000, 17)] + public void QuantizeWeightToRange_WithInvalidValue_ShouldThrowArgumentOutOfRangeException(int value, int range) + { + Action action = () => Quantization.QuantizeWeightToRange(value, range); + + action.Should().Throw(); + } + + [Theory] + [InlineData(-1, 10)] + [InlineData(8, 7)] + [InlineData(-1000, 17)] + public void UnquantizeWeightFromRange_WithInvalidValue_ShouldThrowArgumentOutOfRangeException(int value, int range) + { + Action action = () => Quantization.UnquantizeWeightFromRange(value, range); + + action.Should().Throw(); + } + + [Theory] + [InlineData(0, -7)] + [InlineData(0, 32)] + public void QuantizeWeightToRange_WithInvalidRange_ShouldThrowArgumentOutOfRangeException(int value, int range) + { + Action action = () => Quantization.QuantizeWeightToRange(value, range); + + action.Should().Throw(); + } + + [Theory] + [InlineData(0, -17)] + [InlineData(0, 64)] + public void UnquantizeWeightFromRange_WithInvalidRange_ShouldThrowArgumentOutOfRangeException(int value, int range) + { + Action action = () => Quantization.UnquantizeWeightFromRange(value, range); + + action.Should().Throw(); + } +} diff --git a/tests/ImageSharp.Textures.Tests/Formats/Astc/WeightInfillTests.cs b/tests/ImageSharp.Textures.Tests/Formats/Astc/WeightInfillTests.cs new file mode 100644 index 00000000..a71c8e97 --- /dev/null +++ b/tests/ImageSharp.Textures.Tests/Formats/Astc/WeightInfillTests.cs @@ -0,0 +1,48 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using AwesomeAssertions; +using SixLabors.ImageSharp.Textures.Astc.BiseEncoding; +using SixLabors.ImageSharp.Textures.Astc.Core; + +namespace SixLabors.ImageSharp.Textures.Tests.Formats.Astc; + +public class WeightInfillTests +{ + [Theory] + [InlineData(4, 4, 3, 32)] + [InlineData(4, 4, 7, 48)] + [InlineData(2, 4, 7, 24)] + [InlineData(2, 4, 1, 8)] + [InlineData(4, 5, 2, 32)] + [InlineData(4, 4, 2, 26)] + [InlineData(4, 5, 5, 52)] + [InlineData(4, 4, 5, 42)] + [InlineData(3, 3, 4, 21)] + [InlineData(4, 4, 4, 38)] + [InlineData(3, 7, 4, 49)] + [InlineData(4, 3, 19, 52)] + [InlineData(4, 4, 19, 70)] + public void CountBitsForWeights_WithVariousParameters_ShouldReturnCorrectBitCount( + int width, int height, int range, int expectedBitCount) + { + int bitCount = BoundedIntegerSequenceCodec.GetBitCountForRange(width * height, range); + + bitCount.Should().Be(expectedBitCount); + } + + [Fact] + public void InfillWeights_With3x3Grid_ShouldBilinearlyInterpolateTo5x5() + { + int[] weights = [1, 3, 5, 3, 5, 7, 5, 7, 9]; + int[] expected = [1, 2, 3, 4, 5, 2, 3, 4, 5, 6, 3, 4, 5, 6, 7, 4, 5, 6, 7, 8, 5, 6, 7, 8, 9]; + + Footprint footprint = Footprint.Get5x5(); + DecimationInfo di = DecimationTable.Get(footprint, 3, 3); + int[] result = new int[footprint.PixelCount]; + DecimationTable.InfillWeights(weights, di, result); + + result.Should().HaveCount(expected.Length); + result.Should().Equal(expected); + } +} diff --git a/tests/ImageSharp.Textures.Tests/Formats/Dds/DdsDecoderCubemapTests.cs b/tests/ImageSharp.Textures.Tests/Formats/Dds/DdsDecoderCubemapTests.cs index aed5e795..f81e64a9 100644 --- a/tests/ImageSharp.Textures.Tests/Formats/Dds/DdsDecoderCubemapTests.cs +++ b/tests/ImageSharp.Textures.Tests/Formats/Dds/DdsDecoderCubemapTests.cs @@ -5,30 +5,28 @@ using SixLabors.ImageSharp.Textures.Tests.Enums; using SixLabors.ImageSharp.Textures.Tests.TestUtilities.Attributes; using SixLabors.ImageSharp.Textures.Tests.TestUtilities.TextureProviders; -using Xunit; // ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Textures.Tests.Formats.Dds +namespace SixLabors.ImageSharp.Textures.Tests.Formats.Dds; + +[Trait("Format", "Dds")] +public class DdsDecoderCubemapTests { - [Trait("Format", "Dds")] - public class DdsDecoderCubemapTests - { - private static readonly DdsDecoder DdsDecoder = new DdsDecoder(); + private static readonly DdsDecoder DdsDecoder = new(); - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Cubemap, TestTextureTool.NvDxt, "cubemap has-mips.dds")] - public void DdsDecoder_CanDecode_Cubemap_With_Mips(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Cubemap, TestTextureTool.NvDxt, "cubemap has-mips.dds")] + public void DdsDecoder_CanDecode_Cubemap_With_Mips(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Cubemap, TestTextureTool.NvDxt, "cubemap no-mips.dds")] - public void DdsDecoder_CanDecode_Cubemap_Without_Mips(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Cubemap, TestTextureTool.NvDxt, "cubemap no-mips.dds")] + public void DdsDecoder_CanDecode_Cubemap_Without_Mips(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); } } diff --git a/tests/ImageSharp.Textures.Tests/Formats/Dds/DdsDecoderFlatTests.cs b/tests/ImageSharp.Textures.Tests/Formats/Dds/DdsDecoderFlatTests.cs index 1186142b..1c9d0e88 100644 --- a/tests/ImageSharp.Textures.Tests/Formats/Dds/DdsDecoderFlatTests.cs +++ b/tests/ImageSharp.Textures.Tests/Formats/Dds/DdsDecoderFlatTests.cs @@ -1,355 +1,352 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using SixLabors.ImageSharp.Textures.Formats.Dds; using SixLabors.ImageSharp.Textures.Tests.Enums; using SixLabors.ImageSharp.Textures.Tests.TestUtilities.Attributes; using SixLabors.ImageSharp.Textures.Tests.TestUtilities.TextureProviders; -using Xunit; // ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Textures.Tests.Formats.Dds +namespace SixLabors.ImageSharp.Textures.Tests.Formats.Dds; + +[Trait("Format", "Dds")] +public class DdsDecoderFlatTests { - [Trait("Format", "Dds")] - public class DdsDecoderFlatTests - { - private static readonly DdsDecoder DdsDecoder = new DdsDecoder(); - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat has-mips 3DC.dds")] - public void DdsDecoder_CanDecode_Flat_NvDxt_Has_Mips_3DC(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat no-mips 3DC.dds")] - public void DdsDecoder_CanDecode_Flat_NvDxt_No_Mips_3DC(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat has-mips A8.dds")] - public void DdsDecoder_CanDecode_Flat_NvDxt_Has_Mips_A8(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat no-mips A8.dds")] - public void DdsDecoder_CanDecode_Flat_NvDxt_No_Mips_A8(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat has-mips A8L8.dds")] - public void DdsDecoder_CanDecode_Flat_NvDxt_Has_Mips_A8L8(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat no-mips A8L8.dds")] - public void DdsDecoder_CanDecode_Flat_NvDxt_No_Mips_A8L8(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat has-mips CXV8U8.dds")] - public void DdsDecoder_CanDecode_Flat_NvDxt_Has_Mips_CXV8U8(TestTextureProvider provider) => - Assert.Throws(() => - { - using Texture texture = provider.GetTexture(DdsDecoder); - }); - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat no-mips CXV8U8.dds")] - public void DdsDecoder_CanDecode_Flat_NvDxt_No_Mips_CXV8U8(TestTextureProvider provider) => - Assert.Throws(() => - { - using Texture texture = provider.GetTexture(DdsDecoder); - }); - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat has-mips DXT1A.dds")] - public void DdsDecoder_CanDecode_Flat_NvDxt_Has_Mips_DXT1A(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat no-mips DXT1A.dds")] - public void DdsDecoder_CanDecode_Flat_NvDxt_No_Mips_DXT1A(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat has-mips DXT1C.dds")] - public void DdsDecoder_CanDecode_Flat_NvDxt_Has_Mips_DXT1C(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat no-mips DXT1C.dds")] - public void DdsDecoder_CanDecode_Flat_NvDxt_No_Mips_DXT1C(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat has-mips DXT3.dds")] - public void DdsDecoder_CanDecode_Flat_NvDxt_Has_Mips_DXT3(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat no-mips DXT3.dds")] - public void DdsDecoder_CanDecode_Flat_NvDxt_No_Mips_DXT3(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat has-mips DXT5.dds")] - public void DdsDecoder_CanDecode_Flat_NvDxt_Has_Mips_DXT5(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat no-mips DXT5.dds")] - public void DdsDecoder_CanDecode_Flat_NvDxt_No_Mips_DXT5(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat has-mips DXT5NM.dds")] - public void DdsDecoder_CanDecode_Flat_NvDxt_Has_Mips_DXT5NM(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat no-mips DXT5NM.dds")] - public void DdsDecoder_CanDecode_Flat_NvDxt_No_Mips_DXT5NM(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat has-mips FP16X4.dds")] - public void DdsDecoder_CanDecode_Flat_NvDxt_Has_Mips_FP16X4(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat no-mips FP16X4.dds")] - public void DdsDecoder_CanDecode_Flat_NvDxt_No_Mips_FP16X4(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat has-mips FP32.dds")] - public void DdsDecoder_CanDecode_Flat_NvDxt_Has_Mips_FP32(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat no-mips FP32.dds")] - public void DdsDecoder_CanDecode_Flat_NvDxt_No_Mips_FP32(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat has-mips FP32X4.dds")] - public void DdsDecoder_CanDecode_Flat_NvDxt_Has_Mips_FP32X4(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat no-mips FP32X4.dds")] - public void DdsDecoder_CanDecode_Flat_NvDxt_No_Mips_FP32X4(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat has-mips G16R16.dds")] - public void DdsDecoder_CanDecode_Flat_NvDxt_Has_Mips_G16R16(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat no-mips G16R16.dds")] - public void DdsDecoder_CanDecode_Flat_NvDxt_No_Mips_G16R16(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat has-mips G16R16F.dds")] - public void DdsDecoder_CanDecode_Flat_NvDxt_Has_Mips_G16R16F(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat no-mips G16R16F.dds")] - public void DdsDecoder_CanDecode_Flat_NvDxt_No_Mips_G16R16F(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat has-mips U1555.dds")] - public void DdsDecoder_CanDecode_Flat_NvDxt_Has_Mips_U1555(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat no-mips U1555.dds")] - public void DdsDecoder_CanDecode_Flat_NvDxt_No_Mips_U1555(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat has-mips U4444.dds")] - public void DdsDecoder_CanDecode_Flat_NvDxt_Has_Mips_U4444(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat no-mips U4444.dds")] - public void DdsDecoder_CanDecode_Flat_NvDxt_No_Mips_U4444(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat has-mips U555.dds")] - public void DdsDecoder_CanDecode_Flat_NvDxt_Has_Mips_U555(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat no-mips U555.dds")] - public void DdsDecoder_CanDecode_Flat_NvDxt_No_Mips_U555(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat has-mips U565.dds")] - public void DdsDecoder_CanDecode_Flat_NvDxt_Has_Mips_U565(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat no-mips U565.dds")] - public void DdsDecoder_CanDecode_Flat_NvDxt_No_Mips_U565(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat has-mips U888.dds")] - public void DdsDecoder_CanDecode_Flat_NvDxt_Has_Mips_U888(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat no-mips U888.dds")] - public void DdsDecoder_CanDecode_Flat_NvDxt_No_Mips_U888(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat has-mips U8888.dds")] - public void DdsDecoder_CanDecode_Flat_NvDxt_Has_Mips_U8888(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat no-mips U8888.dds")] - public void DdsDecoder_CanDecode_Flat_NvDxt_No_Mips_U8888(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat has-mips V8U8.dds")] - public void DdsDecoder_CanDecode_Flat_NvDxt_Has_Mips_V8U8(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat no-mips V8U8.dds")] - public void DdsDecoder_CanDecode_Flat_NvDxt_No_Mips_V8U8(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + private static readonly DdsDecoder DdsDecoder = new(); + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat has-mips 3DC.dds")] + public void DdsDecoder_CanDecode_Flat_NvDxt_Has_Mips_3DC(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat no-mips 3DC.dds")] + public void DdsDecoder_CanDecode_Flat_NvDxt_No_Mips_3DC(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat has-mips A8.dds")] + public void DdsDecoder_CanDecode_Flat_NvDxt_Has_Mips_A8(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat no-mips A8.dds")] + public void DdsDecoder_CanDecode_Flat_NvDxt_No_Mips_A8(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat has-mips A8L8.dds")] + public void DdsDecoder_CanDecode_Flat_NvDxt_Has_Mips_A8L8(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat no-mips A8L8.dds")] + public void DdsDecoder_CanDecode_Flat_NvDxt_No_Mips_A8L8(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat has-mips CXV8U8.dds")] + public void DdsDecoder_CanDecode_Flat_NvDxt_Has_Mips_CXV8U8(TestTextureProvider provider) => + Assert.Throws(() => + { + using Texture texture = provider.GetTexture(DdsDecoder); + }); + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat no-mips CXV8U8.dds")] + public void DdsDecoder_CanDecode_Flat_NvDxt_No_Mips_CXV8U8(TestTextureProvider provider) => + Assert.Throws(() => + { + using Texture texture = provider.GetTexture(DdsDecoder); + }); + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat has-mips DXT1A.dds")] + public void DdsDecoder_CanDecode_Flat_NvDxt_Has_Mips_DXT1A(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat no-mips DXT1A.dds")] + public void DdsDecoder_CanDecode_Flat_NvDxt_No_Mips_DXT1A(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat has-mips DXT1C.dds")] + public void DdsDecoder_CanDecode_Flat_NvDxt_Has_Mips_DXT1C(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat no-mips DXT1C.dds")] + public void DdsDecoder_CanDecode_Flat_NvDxt_No_Mips_DXT1C(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat has-mips DXT3.dds")] + public void DdsDecoder_CanDecode_Flat_NvDxt_Has_Mips_DXT3(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat no-mips DXT3.dds")] + public void DdsDecoder_CanDecode_Flat_NvDxt_No_Mips_DXT3(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat has-mips DXT5.dds")] + public void DdsDecoder_CanDecode_Flat_NvDxt_Has_Mips_DXT5(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat no-mips DXT5.dds")] + public void DdsDecoder_CanDecode_Flat_NvDxt_No_Mips_DXT5(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat has-mips DXT5NM.dds")] + public void DdsDecoder_CanDecode_Flat_NvDxt_Has_Mips_DXT5NM(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat no-mips DXT5NM.dds")] + public void DdsDecoder_CanDecode_Flat_NvDxt_No_Mips_DXT5NM(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat has-mips FP16X4.dds")] + public void DdsDecoder_CanDecode_Flat_NvDxt_Has_Mips_FP16X4(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat no-mips FP16X4.dds")] + public void DdsDecoder_CanDecode_Flat_NvDxt_No_Mips_FP16X4(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat has-mips FP32.dds")] + public void DdsDecoder_CanDecode_Flat_NvDxt_Has_Mips_FP32(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat no-mips FP32.dds")] + public void DdsDecoder_CanDecode_Flat_NvDxt_No_Mips_FP32(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat has-mips FP32X4.dds")] + public void DdsDecoder_CanDecode_Flat_NvDxt_Has_Mips_FP32X4(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat no-mips FP32X4.dds")] + public void DdsDecoder_CanDecode_Flat_NvDxt_No_Mips_FP32X4(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat has-mips G16R16.dds")] + public void DdsDecoder_CanDecode_Flat_NvDxt_Has_Mips_G16R16(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat no-mips G16R16.dds")] + public void DdsDecoder_CanDecode_Flat_NvDxt_No_Mips_G16R16(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat has-mips G16R16F.dds")] + public void DdsDecoder_CanDecode_Flat_NvDxt_Has_Mips_G16R16F(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat no-mips G16R16F.dds")] + public void DdsDecoder_CanDecode_Flat_NvDxt_No_Mips_G16R16F(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat has-mips U1555.dds")] + public void DdsDecoder_CanDecode_Flat_NvDxt_Has_Mips_U1555(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat no-mips U1555.dds")] + public void DdsDecoder_CanDecode_Flat_NvDxt_No_Mips_U1555(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat has-mips U4444.dds")] + public void DdsDecoder_CanDecode_Flat_NvDxt_Has_Mips_U4444(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat no-mips U4444.dds")] + public void DdsDecoder_CanDecode_Flat_NvDxt_No_Mips_U4444(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat has-mips U555.dds")] + public void DdsDecoder_CanDecode_Flat_NvDxt_Has_Mips_U555(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat no-mips U555.dds")] + public void DdsDecoder_CanDecode_Flat_NvDxt_No_Mips_U555(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat has-mips U565.dds")] + public void DdsDecoder_CanDecode_Flat_NvDxt_Has_Mips_U565(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat no-mips U565.dds")] + public void DdsDecoder_CanDecode_Flat_NvDxt_No_Mips_U565(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat has-mips U888.dds")] + public void DdsDecoder_CanDecode_Flat_NvDxt_Has_Mips_U888(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat no-mips U888.dds")] + public void DdsDecoder_CanDecode_Flat_NvDxt_No_Mips_U888(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat has-mips U8888.dds")] + public void DdsDecoder_CanDecode_Flat_NvDxt_Has_Mips_U8888(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat no-mips U8888.dds")] + public void DdsDecoder_CanDecode_Flat_NvDxt_No_Mips_U8888(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat has-mips V8U8.dds")] + public void DdsDecoder_CanDecode_Flat_NvDxt_Has_Mips_V8U8(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.NvDxt, "flat no-mips V8U8.dds")] + public void DdsDecoder_CanDecode_Flat_NvDxt_No_Mips_V8U8(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); } } diff --git a/tests/ImageSharp.Textures.Tests/Formats/Dds/DdsDecoderTexConvFlatTests.cs b/tests/ImageSharp.Textures.Tests/Formats/Dds/DdsDecoderTexConvFlatTests.cs index e2ad3379..daba9a91 100644 --- a/tests/ImageSharp.Textures.Tests/Formats/Dds/DdsDecoderTexConvFlatTests.cs +++ b/tests/ImageSharp.Textures.Tests/Formats/Dds/DdsDecoderTexConvFlatTests.cs @@ -1,707 +1,704 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using SixLabors.ImageSharp.Textures.Formats.Dds; using SixLabors.ImageSharp.Textures.Tests.Enums; using SixLabors.ImageSharp.Textures.Tests.TestUtilities.Attributes; using SixLabors.ImageSharp.Textures.Tests.TestUtilities.TextureProviders; -using Xunit; // ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Textures.Tests.Formats.Dds +namespace SixLabors.ImageSharp.Textures.Tests.Formats.Dds; + +[Trait("Format", "Dds")] +public class DdsDecoderTexConvFlatTests { - [Trait("Format", "Dds")] - public class DdsDecoderTexConvFlatTests + private static readonly DdsDecoder DdsDecoder = new(); + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat A8_UNORM.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_A8_UNORM(TestTextureProvider provider) { - private static readonly DdsDecoder DdsDecoder = new DdsDecoder(); + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat A8_UNORM.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_A8_UNORM(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat AYUV.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_AYUV(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat AYUV.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_AYUV(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat B4G4R4A4_UNORM.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_B4G4R4A4_UNORM(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat B4G4R4A4_UNORM.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_B4G4R4A4_UNORM(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat B5G5R5A1_UNORM.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_B5G5R5A1_UNORM(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat B5G5R5A1_UNORM.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_B5G5R5A1_UNORM(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat B5G6R5_UNORM.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_B5G6R5_UNORM(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat B5G6R5_UNORM.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_B5G6R5_UNORM(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat B8G8R8A8_UNORM.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_B8G8R8A8_UNORM(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat B8G8R8A8_UNORM.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_B8G8R8A8_UNORM(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat B8G8R8A8_UNORM_SRGB.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_B8G8R8A8_UNORM_SRGB(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat B8G8R8A8_UNORM_SRGB.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_B8G8R8A8_UNORM_SRGB(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat B8G8R8X8_UNORM.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_B8G8R8X8_UNORM(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat B8G8R8X8_UNORM.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_B8G8R8X8_UNORM(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat B8G8R8X8_UNORM_SRGB.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_B8G8R8X8_UNORM_SRGB(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat B8G8R8X8_UNORM_SRGB.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_B8G8R8X8_UNORM_SRGB(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat BC1_UNORM.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_BC1_UNORM(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat BC1_UNORM.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_BC1_UNORM(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat BC1_UNORM_SRGB.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_BC1_UNORM_SRGB(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat BC1_UNORM_SRGB.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_BC1_UNORM_SRGB(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat BC2_UNORM.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_BC2_UNORM(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat BC2_UNORM.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_BC2_UNORM(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat BC2_UNORM_SRGB.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_BC2_UNORM_SRGB(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat BC2_UNORM_SRGB.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_BC2_UNORM_SRGB(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat BC3_UNORM.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_BC3_UNORM(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat BC3_UNORM.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_BC3_UNORM(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat BC3_UNORM_SRGB.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_BC3_UNORM_SRGB(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat BC3_UNORM_SRGB.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_BC3_UNORM_SRGB(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat BC4_SNORM.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_BC4_SNORM(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat BC4_SNORM.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_BC4_SNORM(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat BC4_UNORM.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_BC4_UNORM(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat BC4_UNORM.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_BC4_UNORM(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat BC5_SNORM.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_BC5_SNORM(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat BC5_SNORM.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_BC5_SNORM(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat BC5_UNORM.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_BC5_UNORM(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat BC5_UNORM.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_BC5_UNORM(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat BC6H_SF16.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_BC6H_SF16(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat BC6H_SF16.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_BC6H_SF16(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat BC6H_UF16.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_BC6H_UF16(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat BC6H_UF16.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_BC6H_UF16(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat BC7_UNORM.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_BC7_UNORM(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat BC7_UNORM.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_BC7_UNORM(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat BC7_UNORM_SRGB.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_BC7_UNORM_SRGB(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat BC7_UNORM_SRGB.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_BC7_UNORM_SRGB(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat BGRA.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_BGRA(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat BGRA.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_BGRA(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat BPTC.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_BPTC(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat BPTC.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_BPTC(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat BPTC_FLOAT.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_BPTC_FLOAT(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat BPTC_FLOAT.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_BPTC_FLOAT(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat DXT1.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_DXT1(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat DXT1.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_DXT1(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat DXT2.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_DXT2(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat DXT2.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_DXT2(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat DXT3.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_DXT3(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat DXT3.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_DXT3(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat DXT4.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_DXT4(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat DXT4.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_DXT4(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat DXT5.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_DXT5(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat DXT5.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_DXT5(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat FP16.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_FP16(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat FP16.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_FP16(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat FP32.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_FP32(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat FP32.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_FP32(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat G8R8_G8B8_UNORM.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_G8R8_G8B8_UNORM(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat G8R8_G8B8_UNORM.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_G8R8_G8B8_UNORM(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R10G10B10_XR_BIAS_A2_UNORM.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R10G10B10_XR_BIAS_A2_UNORM(TestTextureProvider provider) => - Assert.Throws(() => - { - using Texture texture = provider.GetTexture(DdsDecoder); - }); - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R10G10B10A2_UINT.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R10G10B10A2_UINT(TestTextureProvider provider) + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R10G10B10_XR_BIAS_A2_UNORM.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R10G10B10_XR_BIAS_A2_UNORM(TestTextureProvider provider) => + Assert.Throws(() => { using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + }); - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R10G10B10A2_UNORM.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R10G10B10A2_UNORM(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R10G10B10A2_UINT.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R10G10B10A2_UINT(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R11G11B10_FLOAT.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R11G11B10_FLOAT(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R10G10B10A2_UNORM.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R10G10B10A2_UNORM(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R16_FLOAT.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R16_FLOAT(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R11G11B10_FLOAT.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R11G11B10_FLOAT(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R16_SINT.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R16_SINT(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R16_FLOAT.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R16_FLOAT(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R16_SNORM.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R16_SNORM(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R16_SINT.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R16_SINT(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R16_UINT.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R16_UINT(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R16_SNORM.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R16_SNORM(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R16_UNORM.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R16_UNORM(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R16_UINT.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R16_UINT(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R16G16_FLOAT.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R16G16_FLOAT(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R16_UNORM.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R16_UNORM(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R16G16_SINT.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R16G16_SINT(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R16G16_FLOAT.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R16G16_FLOAT(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R16G16_SNORM.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R16G16_SNORM(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R16G16_SINT.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R16G16_SINT(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R16G16_UINT.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R16G16_UINT(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R16G16_SNORM.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R16G16_SNORM(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R16G16_UNORM.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R16G16_UNORM(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R16G16_UINT.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R16G16_UINT(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R16G16B16A16_FLOAT.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R16G16B16A16_FLOAT(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R16G16_UNORM.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R16G16_UNORM(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R16G16B16A16_SINT.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R16G16B16A16_SINT(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R16G16B16A16_FLOAT.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R16G16B16A16_FLOAT(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R16G16B16A16_SNORM.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R16G16B16A16_SNORM(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R16G16B16A16_SINT.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R16G16B16A16_SINT(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R16G16B16A16_UINT.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R16G16B16A16_UINT(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R16G16B16A16_SNORM.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R16G16B16A16_SNORM(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R16G16B16A16_UNORM.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R16G16B16A16_UNORM(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R16G16B16A16_UINT.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R16G16B16A16_UINT(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R32_FLOAT.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R32_FLOAT(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R16G16B16A16_UNORM.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R16G16B16A16_UNORM(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R32_SINT.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R32_SINT(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R32_FLOAT.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R32_FLOAT(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R32_UINT.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R32_UINT(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R32_SINT.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R32_SINT(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R32G32_FLOAT.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R32G32_FLOAT(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R32_UINT.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R32_UINT(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R32G32_SINT.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R32G32_SINT(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R32G32_FLOAT.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R32G32_FLOAT(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R32G32_UINT.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R32G32_UINT(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R32G32_SINT.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R32G32_SINT(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R32G32B32_FLOAT.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R32G32B32_FLOAT(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R32G32_UINT.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R32G32_UINT(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R32G32B32_SINT.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R32G32B32_SINT(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R32G32B32_FLOAT.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R32G32B32_FLOAT(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R32G32B32_UINT.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R32G32B32_UINT(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R32G32B32_SINT.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R32G32B32_SINT(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R32G32B32A32_FLOAT.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R32G32B32A32_FLOAT(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R32G32B32_UINT.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R32G32B32_UINT(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R32G32B32A32_SINT.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R32G32B32A32_SINT(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R32G32B32A32_FLOAT.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R32G32B32A32_FLOAT(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R32G32B32A32_UINT.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R32G32B32A32_UINT(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R32G32B32A32_SINT.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R32G32B32A32_SINT(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R8_SINT.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R8_SINT(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R32G32B32A32_UINT.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R32G32B32A32_UINT(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R8_SNORM.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R8_SNORM(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R8_SINT.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R8_SINT(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R8_UINT.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R8_UINT(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R8_SNORM.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R8_SNORM(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R8_UNORM.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R8_UNORM(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R8_UINT.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R8_UINT(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R8G8_B8G8_UNORM.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R8G8_B8G8_UNORM(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R8_UNORM.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R8_UNORM(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R8G8_SINT.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R8G8_SINT(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R8G8_B8G8_UNORM.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R8G8_B8G8_UNORM(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R8G8_SNORM.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R8G8_SNORM(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R8G8_SINT.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R8G8_SINT(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R8G8_UINT.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R8G8_UINT(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R8G8_SNORM.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R8G8_SNORM(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R8G8_UNORM.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R8G8_UNORM(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R8G8_UINT.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R8G8_UINT(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R8G8B8A8_SINT.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R8G8B8A8_SINT(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R8G8_UNORM.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R8G8_UNORM(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R8G8B8A8_SNORM.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R8G8B8A8_SNORM(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R8G8B8A8_SINT.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R8G8B8A8_SINT(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R8G8B8A8_UINT.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R8G8B8A8_UINT(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R8G8B8A8_SNORM.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R8G8B8A8_SNORM(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R8G8B8A8_UNORM.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R8G8B8A8_UNORM(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R8G8B8A8_UINT.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R8G8B8A8_UINT(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R8G8B8A8_UNORM_SRGB.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R8G8B8A8_UNORM_SRGB(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R9G9B9E5_SHAREDEXP.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_R9G9B9E5_SHAREDEXP(TestTextureProvider provider) => - Assert.Throws(() => - { - using Texture texture = provider.GetTexture(DdsDecoder); - }); - - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat RGBA.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_RGBA(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R8G8B8A8_UNORM.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R8G8B8A8_UNORM(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat Y210.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_Y210(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R8G8B8A8_UNORM_SRGB.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R8G8B8A8_UNORM_SRGB(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat Y216.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_Y216(TestTextureProvider provider) + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat R9G9B9E5_SHAREDEXP.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_R9G9B9E5_SHAREDEXP(TestTextureProvider provider) => + Assert.Throws(() => { using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + }); - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat Y410.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_Y410(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat RGBA.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_RGBA(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat Y416.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_Y416(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat Y210.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_Y210(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat YUY2.DDS")] - public void DdsDecoder_CanDecode_Flat_TexConv_YUY2(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(DdsDecoder); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat Y216.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_Y216(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat Y410.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_Y410(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat Y416.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_Y416(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); + } + + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Flat, TestTextureTool.TexConv, "flat YUY2.DDS")] + public void DdsDecoder_CanDecode_Flat_TexConv_YUY2(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(DdsDecoder); + provider.SaveTextures(texture); } } diff --git a/tests/ImageSharp.Textures.Tests/Formats/Dds/DdsDecoderVolumeTests.cs b/tests/ImageSharp.Textures.Tests/Formats/Dds/DdsDecoderVolumeTests.cs index 0a3f897b..dd426104 100644 --- a/tests/ImageSharp.Textures.Tests/Formats/Dds/DdsDecoderVolumeTests.cs +++ b/tests/ImageSharp.Textures.Tests/Formats/Dds/DdsDecoderVolumeTests.cs @@ -5,27 +5,25 @@ using SixLabors.ImageSharp.Textures.Tests.Enums; using SixLabors.ImageSharp.Textures.Tests.TestUtilities.Attributes; using SixLabors.ImageSharp.Textures.Tests.TestUtilities.TextureProviders; -using Xunit; -namespace SixLabors.ImageSharp.Textures.Tests.Formats.Dds +namespace SixLabors.ImageSharp.Textures.Tests.Formats.Dds; + +[Trait("Format", "Dds")] +public class DdsDecoderVolumeTests { - [Trait("Format", "Dds")] - public class DdsDecoderVolumeTests + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Volume, TestTextureTool.NvDxt, "volume has-mips.dds")] + public void DdsDecoder_CanDecode_Volume_NvDxt_Has_Mips(TestTextureProvider provider) { - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Volume, TestTextureTool.NvDxt, "volume has-mips.dds")] - public void DdsDecoder_CanDecode_Volume_NvDxt_Has_Mips(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(new DdsDecoder()); - provider.SaveTextures(texture); - } + using Texture texture = provider.GetTexture(new DdsDecoder()); + provider.SaveTextures(texture); + } - [Theory] - [WithFile(TestTextureFormat.Dds, TestTextureType.Volume, TestTextureTool.NvDxt, "volume no-mips.dds")] - public void DdsDecoder_CanDecode_Volume_NvDxt_No_Mips(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(new DdsDecoder()); - provider.SaveTextures(texture); - } + [Theory] + [WithFile(TestTextureFormat.Dds, TestTextureType.Volume, TestTextureTool.NvDxt, "volume no-mips.dds")] + public void DdsDecoder_CanDecode_Volume_NvDxt_No_Mips(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(new DdsDecoder()); + provider.SaveTextures(texture); } } diff --git a/tests/ImageSharp.Textures.Tests/Formats/Ktx/KtxAstcDecoderTests.cs b/tests/ImageSharp.Textures.Tests/Formats/Ktx/KtxAstcDecoderTests.cs new file mode 100644 index 00000000..71482d92 --- /dev/null +++ b/tests/ImageSharp.Textures.Tests/Formats/Ktx/KtxAstcDecoderTests.cs @@ -0,0 +1,40 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Textures.Formats.Ktx; +using SixLabors.ImageSharp.Textures.Tests.Enums; +using SixLabors.ImageSharp.Textures.Tests.TestUtilities; +using SixLabors.ImageSharp.Textures.Tests.TestUtilities.Attributes; +using SixLabors.ImageSharp.Textures.Tests.TestUtilities.TextureProviders; +using SixLabors.ImageSharp.Textures.TextureFormats; + +namespace SixLabors.ImageSharp.Textures.Tests.Formats.Ktx; + +[Trait("Format", "Ktx")] +[Trait("Format", "Astc")] +public class KtxAstcDecoderTests +{ + private static readonly KtxDecoder KtxDecoder = new(); + + [Theory] + [WithFile(TestTextureFormat.Ktx, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx.Astc.Rgb32_8x8)] + public void KtxAstcDecoder_CanDecode_Rgba32_Blocksizes(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(KtxDecoder); + provider.SaveTextures(texture); + FlatTexture flatTexture = texture as FlatTexture; + + Assert.NotNull(flatTexture?.MipMaps); + Assert.Single(flatTexture.MipMaps); + + Image firstMipMap = flatTexture.MipMaps[0].GetImage(); + Assert.Equal(256, firstMipMap.Width); + Assert.Equal(256, firstMipMap.Height); + Assert.Equal(32, firstMipMap.PixelType.BitsPerPixel); + + Image firstMipMapImage = firstMipMap as Image; + + firstMipMapImage.CompareToReferenceOutput(provider, appendPixelTypeToFileName: false); + } +} diff --git a/tests/ImageSharp.Textures.Tests/Formats/Ktx/KtxDecoderTests.cs b/tests/ImageSharp.Textures.Tests/Formats/Ktx/KtxDecoderTests.cs index 1fbacc67..3d28c5c7 100644 --- a/tests/ImageSharp.Textures.Tests/Formats/Ktx/KtxDecoderTests.cs +++ b/tests/ImageSharp.Textures.Tests/Formats/Ktx/KtxDecoderTests.cs @@ -8,45 +8,43 @@ using SixLabors.ImageSharp.Textures.Tests.TestUtilities.Attributes; using SixLabors.ImageSharp.Textures.Tests.TestUtilities.TextureProviders; using SixLabors.ImageSharp.Textures.TextureFormats; -using Xunit; -namespace SixLabors.ImageSharp.Textures.Tests.Formats.Ktx +namespace SixLabors.ImageSharp.Textures.Tests.Formats.Ktx; + +[Trait("Format", "Ktx")] +public class KtxDecoderTests { - [Trait("Format", "Ktx")] - public class KtxDecoderTests - { - private static readonly KtxDecoder KtxDecoder = new KtxDecoder(); + private static readonly KtxDecoder KtxDecoder = new(); - [Theory] - [WithFile(TestTextureFormat.Ktx, TestTextureType.Flat, TestTextureTool.PvrTexToolCli, TestImages.Ktx.Rgba)] - public void KtxDecoder_CanDecode_Rgba8888(TestTextureProvider provider) - { - using Texture texture = provider.GetTexture(KtxDecoder); - provider.SaveTextures(texture); - var flatTexture = texture as FlatTexture; + [Theory] + [WithFile(TestTextureFormat.Ktx, TestTextureType.Flat, TestTextureTool.PvrTexToolCli, TestImages.Ktx.Rgba)] + public void KtxDecoder_CanDecode_Rgba8888(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(KtxDecoder); + provider.SaveTextures(texture); + FlatTexture flatTexture = texture as FlatTexture; - Assert.NotNull(flatTexture?.MipMaps); - Assert.Equal(8, flatTexture.MipMaps.Count); - Assert.Equal(200, flatTexture.MipMaps[0].GetImage().Height); - Assert.Equal(200, flatTexture.MipMaps[0].GetImage().Width); - Assert.Equal(100, flatTexture.MipMaps[1].GetImage().Height); - Assert.Equal(100, flatTexture.MipMaps[1].GetImage().Width); - Assert.Equal(50, flatTexture.MipMaps[2].GetImage().Height); - Assert.Equal(50, flatTexture.MipMaps[2].GetImage().Width); - Assert.Equal(25, flatTexture.MipMaps[3].GetImage().Height); - Assert.Equal(25, flatTexture.MipMaps[3].GetImage().Width); - Assert.Equal(12, flatTexture.MipMaps[4].GetImage().Height); - Assert.Equal(12, flatTexture.MipMaps[4].GetImage().Width); - Assert.Equal(6, flatTexture.MipMaps[5].GetImage().Height); - Assert.Equal(6, flatTexture.MipMaps[5].GetImage().Width); - Assert.Equal(3, flatTexture.MipMaps[6].GetImage().Height); - Assert.Equal(3, flatTexture.MipMaps[6].GetImage().Width); - Assert.Equal(1, flatTexture.MipMaps[7].GetImage().Height); - Assert.Equal(1, flatTexture.MipMaps[7].GetImage().Width); - Image firstMipMap = flatTexture.MipMaps[0].GetImage(); - Assert.Equal(32, firstMipMap.PixelType.BitsPerPixel); - var firstMipMapImage = firstMipMap as Image; - firstMipMapImage.CompareToReferenceOutput(provider, appendPixelTypeToFileName: false); - } + Assert.NotNull(flatTexture?.MipMaps); + Assert.Equal(8, flatTexture.MipMaps.Count); + Assert.Equal(200, flatTexture.MipMaps[0].GetImage().Height); + Assert.Equal(200, flatTexture.MipMaps[0].GetImage().Width); + Assert.Equal(100, flatTexture.MipMaps[1].GetImage().Height); + Assert.Equal(100, flatTexture.MipMaps[1].GetImage().Width); + Assert.Equal(50, flatTexture.MipMaps[2].GetImage().Height); + Assert.Equal(50, flatTexture.MipMaps[2].GetImage().Width); + Assert.Equal(25, flatTexture.MipMaps[3].GetImage().Height); + Assert.Equal(25, flatTexture.MipMaps[3].GetImage().Width); + Assert.Equal(12, flatTexture.MipMaps[4].GetImage().Height); + Assert.Equal(12, flatTexture.MipMaps[4].GetImage().Width); + Assert.Equal(6, flatTexture.MipMaps[5].GetImage().Height); + Assert.Equal(6, flatTexture.MipMaps[5].GetImage().Width); + Assert.Equal(3, flatTexture.MipMaps[6].GetImage().Height); + Assert.Equal(3, flatTexture.MipMaps[6].GetImage().Width); + Assert.Equal(1, flatTexture.MipMaps[7].GetImage().Height); + Assert.Equal(1, flatTexture.MipMaps[7].GetImage().Width); + Image firstMipMap = flatTexture.MipMaps[0].GetImage(); + Assert.Equal(32, firstMipMap.PixelType.BitsPerPixel); + Image firstMipMapImage = firstMipMap as Image; + firstMipMapImage.CompareToReferenceOutput(provider, appendPixelTypeToFileName: false); } } diff --git a/tests/ImageSharp.Textures.Tests/Formats/Ktx2/Ktx2AstcDecoderCubemapTests.cs b/tests/ImageSharp.Textures.Tests/Formats/Ktx2/Ktx2AstcDecoderCubemapTests.cs new file mode 100644 index 00000000..63e8d375 --- /dev/null +++ b/tests/ImageSharp.Textures.Tests/Formats/Ktx2/Ktx2AstcDecoderCubemapTests.cs @@ -0,0 +1,65 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Textures.Formats.Ktx2; +using SixLabors.ImageSharp.Textures.Tests.Enums; +using SixLabors.ImageSharp.Textures.Tests.TestUtilities; +using SixLabors.ImageSharp.Textures.Tests.TestUtilities.Attributes; +using SixLabors.ImageSharp.Textures.Tests.TestUtilities.ImageComparison; +using SixLabors.ImageSharp.Textures.Tests.TestUtilities.TextureProviders; +using SixLabors.ImageSharp.Textures.TextureFormats; + +namespace SixLabors.ImageSharp.Textures.Tests.Formats.Ktx2; + +[Trait("Format", "Ktx2")] +[Trait("Format", "Astc")] +public class Ktx2AstcDecoderCubemapTests +{ + private static readonly Ktx2Decoder KtxDecoder = new(); + + [Theory] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Cubemap, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgb32_Srgb_6x6_Cube)] + public void Ktx2AstcDecoder_CanDecode_All_Faces(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(KtxDecoder); + provider.SaveTextures(texture); + CubemapTexture cubemapTexture = texture as CubemapTexture; + + using Image posXImage = cubemapTexture.PositiveX.MipMaps[0].GetImage(); + (posXImage as Image).CompareToReferenceOutput( + ImageComparer.Exact, + provider, + testOutputDetails: "posX"); + + using Image negXImage = cubemapTexture.NegativeX.MipMaps[0].GetImage(); + (negXImage as Image).CompareToReferenceOutput( + ImageComparer.Exact, + provider, + testOutputDetails: "negX"); + + using Image posYImage = cubemapTexture.PositiveY.MipMaps[0].GetImage(); + (posYImage as Image).CompareToReferenceOutput( + ImageComparer.Exact, + provider, + testOutputDetails: "posY"); + + using Image negYImage = cubemapTexture.NegativeY.MipMaps[0].GetImage(); + (negYImage as Image).CompareToReferenceOutput( + ImageComparer.TolerantPercentage(3.0f), + provider, + testOutputDetails: "negY"); + + using Image posZImage = cubemapTexture.PositiveZ.MipMaps[0].GetImage(); + (posZImage as Image).CompareToReferenceOutput( + ImageComparer.Exact, + provider, + testOutputDetails: "posZ"); + + using Image negZImage = cubemapTexture.NegativeZ.MipMaps[0].GetImage(); + (negZImage as Image).CompareToReferenceOutput( + ImageComparer.Exact, + provider, + testOutputDetails: "negZ"); + } +} diff --git a/tests/ImageSharp.Textures.Tests/Formats/Ktx2/Ktx2AstcDecoderFlatTests.cs b/tests/ImageSharp.Textures.Tests/Formats/Ktx2/Ktx2AstcDecoderFlatTests.cs new file mode 100644 index 00000000..b8efc920 --- /dev/null +++ b/tests/ImageSharp.Textures.Tests/Formats/Ktx2/Ktx2AstcDecoderFlatTests.cs @@ -0,0 +1,194 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Text.RegularExpressions; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Textures.Formats.Ktx2; +using SixLabors.ImageSharp.Textures.Tests.Enums; +using SixLabors.ImageSharp.Textures.Tests.TestUtilities; +using SixLabors.ImageSharp.Textures.Tests.TestUtilities.Attributes; +using SixLabors.ImageSharp.Textures.Tests.TestUtilities.ImageComparison; +using SixLabors.ImageSharp.Textures.Tests.TestUtilities.TextureProviders; +using SixLabors.ImageSharp.Textures.TextureFormats; + +namespace SixLabors.ImageSharp.Textures.Tests.Formats.Ktx2; + +[Trait("Format", "Ktx2")] +[Trait("Format", "Astc")] +public partial class Ktx2AstcDecoderFlatTests +{ + private static readonly Ktx2Decoder KtxDecoder = new(); + + [Theory] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgba32_4x4)] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgba32_5x4)] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgba32_5x5)] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgba32_6x5)] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgba32_6x6)] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgba32_8x5)] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgba32_8x6)] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgba32_8x8)] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgba32_10x5)] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgba32_10x6)] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgba32_10x8)] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgba32_10x10)] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgba32_12x10)] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgba32_12x12)] + public void Ktx2AstcDecoder_CanDecode_Rgba32_Blocksizes(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(KtxDecoder); + provider.SaveTextures(texture); + FlatTexture flatTexture = texture as FlatTexture; + + Assert.NotNull(flatTexture?.MipMaps); + Assert.Single(flatTexture.MipMaps); + + using Image firstMipMap = flatTexture.MipMaps[0].GetImage(); + Assert.Equal(256, firstMipMap.Width); + Assert.Equal(256, firstMipMap.Height); + Assert.Equal(32, firstMipMap.PixelType.BitsPerPixel); + + Image firstMipMapImage = firstMipMap as Image; + + // Note that the comparer is given a higher threshold to allow for the lossy compression of ASTC, + // especially at larger block sizes, but the output is still expected to be very similar to the reference image. + // A single reference image is used to save on the amount of test data otherwise required for each block size. + firstMipMapImage.CompareToReferenceOutput(ImageComparer.TolerantPercentage(4.0f), provider, appendPixelTypeToFileName: false); + } + + [Theory] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgb32_Unorm_4x4)] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgb32_Unorm_5x4)] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgb32_Unorm_5x5)] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgb32_Unorm_6x5)] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgb32_Unorm_6x6)] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgb32_Unorm_8x5)] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgb32_Unorm_8x6)] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgb32_Unorm_8x8)] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgb32_Unorm_10x5)] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgb32_Unorm_10x6)] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgb32_Unorm_10x8)] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgb32_Unorm_10x10)] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgb32_Unorm_12x10)] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgb32_Unorm_12x12)] + public void Ktx2AstcDecoder_CanDecode_Rgba32_Unorm(TestTextureProvider provider) + { + string blockSize = GetBlockSizeFromFileName(provider.InputFile); + using Texture texture = provider.GetTexture(KtxDecoder); + provider.SaveTextures(texture); + FlatTexture flatTexture = texture as FlatTexture; + + Assert.NotNull(flatTexture?.MipMaps); + Assert.Single(flatTexture.MipMaps); + + using Image firstMipMap = flatTexture.MipMaps[0].GetImage(); + Assert.Equal(16, firstMipMap.Width); + Assert.Equal(16, firstMipMap.Height); + Assert.Equal(32, firstMipMap.PixelType.BitsPerPixel); + + (firstMipMap as Image).CompareToReferenceOutput(ImageComparer.TolerantPercentage(0.05f), provider, testOutputDetails: $"{blockSize}"); + } + + [Theory] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgb32_sRgb_4x4)] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgb32_sRgb_5x4)] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgb32_sRgb_5x5)] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgb32_sRgb_6x5)] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgb32_sRgb_6x6)] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgb32_sRgb_8x5)] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgb32_sRgb_8x6)] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgb32_sRgb_8x8)] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgb32_sRgb_10x5)] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgb32_sRgb_10x6)] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgb32_sRgb_10x8)] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgb32_sRgb_10x10)] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgb32_sRgb_12x10)] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgb32_sRgb_12x12)] + public void Ktx2AstcDecoder_CanDecode_Rgba32_Srgb(TestTextureProvider provider) + { + string blockSize = GetBlockSizeFromFileName(provider.InputFile); + using Texture texture = provider.GetTexture(KtxDecoder); + provider.SaveTextures(texture); + FlatTexture flatTexture = texture as FlatTexture; + + Assert.NotNull(flatTexture?.MipMaps); + Assert.Single(flatTexture.MipMaps); + + using Image firstMipMap = flatTexture.MipMaps[0].GetImage(); + Assert.Equal(16, firstMipMap.Width); + Assert.Equal(16, firstMipMap.Height); + Assert.Equal(32, firstMipMap.PixelType.BitsPerPixel); + + (firstMipMap as Image).CompareToReferenceOutput(ImageComparer.TolerantPercentage(0.05f), provider, testOutputDetails: $"{blockSize}"); + } + + [Theory] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgb32_Srgb_Large)] + public void Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_Large(TestTextureProvider provider) + { + using Texture texture = provider.GetTexture(KtxDecoder); + provider.SaveTextures(texture); + FlatTexture flatTexture = texture as FlatTexture; + + using Image firstMipMap = flatTexture.MipMaps[0].GetImage(); + Assert.Equal(2048, firstMipMap.Width); + Assert.Equal(2048, firstMipMap.Height); + Assert.Equal(32, firstMipMap.PixelType.BitsPerPixel); + + (firstMipMap as Image).CompareToReferenceOutput(ImageComparer.Exact, provider); + } + + [Theory] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgb32_Srgb_6x6_MipMap)] + public void Ktx2AstcDecoder_CanDecode_MipMaps(TestTextureProvider provider) + { + int mimMapLevel = 0; + + using Texture texture = provider.GetTexture(KtxDecoder); + provider.SaveTextures(texture); + FlatTexture flatTexture = texture as FlatTexture; + + foreach (MipMap mipMap in flatTexture.MipMaps) + { + using Image image = mipMap.GetImage(); + (image as Image).CompareToReferenceOutput(ImageComparer.Exact, provider, testOutputDetails: $"{mimMapLevel++}"); + } + } + + [Theory(Skip = "Supercompression support not yet implemented")] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgb32_Unorm_4x4_Zlib1)] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgb32_Unorm_4x4_Zlib9)] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgb32_Unorm_4x4_Zstd1)] + [WithFile(TestTextureFormat.Ktx2, TestTextureType.Flat, TestTextureTool.ToKtx, TestImages.Ktx2.Astc.Rgb32_Unorm_4x4_Zstd9)] + public void Ktx2AstcDecoder_CanDecode_Rgba32_Supercompressed(TestTextureProvider provider) + { + string fileName = Path.GetFileNameWithoutExtension(provider.InputFile); + string compressionDetails = fileName.Contains("ZLIB", StringComparison.Ordinal) + ? fileName[fileName.IndexOf("ZLIB", StringComparison.Ordinal)..] + : fileName[fileName.IndexOf("ZSTD", StringComparison.Ordinal)..]; + + using Texture texture = provider.GetTexture(KtxDecoder); + provider.SaveTextures(texture); + FlatTexture flatTexture = texture as FlatTexture; + + Assert.NotNull(flatTexture?.MipMaps); + Assert.Single(flatTexture.MipMaps); + + using Image firstMipMap = flatTexture.MipMaps[0].GetImage(); + Assert.Equal(16, firstMipMap.Width); + Assert.Equal(16, firstMipMap.Height); + Assert.Equal(32, firstMipMap.PixelType.BitsPerPixel); + + (firstMipMap as Image).CompareToReferenceOutput(ImageComparer.TolerantPercentage(0.05f), provider, testOutputDetails: $"{compressionDetails}"); + } + + private static string GetBlockSizeFromFileName(string fileName) + { + Match match = GetBlockSizeFromFileName().Match(fileName); + + return match.Success ? match.Value : string.Empty; + } + + [GeneratedRegex(@"(\d+x\d+)")] + private static partial Regex GetBlockSizeFromFileName(); +} diff --git a/tests/ImageSharp.Textures.Tests/Formats/PixelFormat/PixelFormatTests.cs b/tests/ImageSharp.Textures.Tests/Formats/PixelFormat/PixelFormatTests.cs index 68103752..e19c7bcf 100644 --- a/tests/ImageSharp.Textures.Tests/Formats/PixelFormat/PixelFormatTests.cs +++ b/tests/ImageSharp.Textures.Tests/Formats/PixelFormat/PixelFormatTests.cs @@ -2,126 +2,126 @@ // Licensed under the Six Labors Split License. using System.Globalization; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Textures.PixelFormats; -using Xunit; using Rg16 = SixLabors.ImageSharp.Textures.PixelFormats.Rg16; -namespace SixLabors.ImageSharp.Textures.Tests.Formats.PixelFormat +namespace SixLabors.ImageSharp.Textures.Tests.Formats.PixelFormat; + +[Trait("Category", "PixelFormats")] +public class PixelFormatTests { - [Trait("Category", "PixelFormats")] - public class PixelFormatTests + [Fact] + public void Test_Rg16() { - [Fact] - public void Test_Rg16() + string[] hexValues = ["FF", "FF00"]; + for (int i = 0; i < 2; i++) { - string[] hexValues = { "FF", "FF00" }; - for (int i = 0; i < 2; i++) - { - int x = i == 0 ? 1 : 0; - int y = i == 1 ? 1 : 0; + int x = i == 0 ? 1 : 0; + int y = i == 1 ? 1 : 0; - var testPixel = new Rg16(x, y); + Rg16 testPixel = new(x, y); - Assert.Equal(string.Format(CultureInfo.InvariantCulture, "Rg16({0}, {1})", x, y), testPixel.ToString()); + Assert.Equal(string.Format(CultureInfo.InvariantCulture, "Rg16({0}, {1})", x, y), testPixel.ToString()); - var destPixel = new ImageSharp.PixelFormats.Rgba32(0); - testPixel.ToRgba32(ref destPixel); + Rgba32 destPixel = new(0); + testPixel.ToRgba32(ref destPixel); - Assert.Equal(hexValues[i], testPixel.PackedValue.ToString("X", CultureInfo.InvariantCulture)); + Assert.Equal(hexValues[i], testPixel.PackedValue.ToString("X", CultureInfo.InvariantCulture)); - Assert.Equal(i == 0 ? 255 : 0, destPixel.R); - Assert.Equal(i == 1 ? 255 : 0, destPixel.G); - Assert.Equal(0, destPixel.B); - Assert.Equal(255, destPixel.A); + Assert.Equal(i == 0 ? 255 : 0, destPixel.R); + Assert.Equal(i == 1 ? 255 : 0, destPixel.G); + Assert.Equal(0, destPixel.B); + Assert.Equal(255, destPixel.A); - var vector4 = testPixel.ToVector4(); - testPixel.FromVector4(vector4); - Assert.Equal(testPixel.ToVector4(), vector4); - } + Vector4 vector4 = testPixel.ToVector4(); + testPixel.FromVector4(vector4); + Assert.Equal(testPixel.ToVector4(), vector4); } + } - [Fact] - public void Test_Bgr555() + [Fact] + public void Test_Bgr555() + { + string[] hexValues = ["7C00", "3E0", "1F"]; + for (int i = 0; i < 3; i++) { - var hexValues = new[] { "7C00", "3E0", "1F" }; - for (int i = 0; i < 3; i++) - { - int x = i == 0 ? 1 : 0; - int y = i == 1 ? 1 : 0; - int z = i == 2 ? 1 : 0; + int x = i == 0 ? 1 : 0; + int y = i == 1 ? 1 : 0; + int z = i == 2 ? 1 : 0; - var testPixel = new Bgr555(x, y, z); + Bgr555 testPixel = new(x, y, z); - var destPixel = new ImageSharp.PixelFormats.Rgba32(0); - testPixel.ToRgba32(ref destPixel); + Rgba32 destPixel = new(0); + testPixel.ToRgba32(ref destPixel); - Assert.Equal(hexValues[i], testPixel.PackedValue.ToString("X", CultureInfo.InvariantCulture)); + Assert.Equal(hexValues[i], testPixel.PackedValue.ToString("X", CultureInfo.InvariantCulture)); - Assert.Equal(i == 0 ? 255 : 0, destPixel.R); - Assert.Equal(i == 1 ? 255 : 0, destPixel.G); - Assert.Equal(i == 2 ? 255 : 0, destPixel.B); - Assert.Equal(255, destPixel.A); + Assert.Equal(i == 0 ? 255 : 0, destPixel.R); + Assert.Equal(i == 1 ? 255 : 0, destPixel.G); + Assert.Equal(i == 2 ? 255 : 0, destPixel.B); + Assert.Equal(255, destPixel.A); - var vector4 = testPixel.ToVector4(); - testPixel.FromVector4(vector4); - Assert.Equal(testPixel.ToVector4(), vector4); - } + Vector4 vector4 = testPixel.ToVector4(); + testPixel.FromVector4(vector4); + Assert.Equal(testPixel.ToVector4(), vector4); } + } - [Fact] - public void Test_Bgr32() + [Fact] + public void Test_Bgr32() + { + string[] hexValues = ["FF0000", "FF00", "FF"]; + for (int i = 0; i < 3; i++) { - string[] hexValues = { "FF0000", "FF00", "FF" }; - for (int i = 0; i < 3; i++) - { - int x = i == 0 ? 1 : 0; - int y = i == 1 ? 1 : 0; - int z = i == 2 ? 1 : 0; + int x = i == 0 ? 1 : 0; + int y = i == 1 ? 1 : 0; + int z = i == 2 ? 1 : 0; - var testPixel = new Bgr32(x, y, z); + Bgr32 testPixel = new(x, y, z); - var destPixel = new ImageSharp.PixelFormats.Rgba32(0); - testPixel.ToRgba32(ref destPixel); + Rgba32 destPixel = new(0); + testPixel.ToRgba32(ref destPixel); - Assert.Equal(hexValues[i], testPixel.PackedValue.ToString("X", CultureInfo.InvariantCulture)); + Assert.Equal(hexValues[i], testPixel.PackedValue.ToString("X", CultureInfo.InvariantCulture)); - Assert.Equal(i == 0 ? 255 : 0, destPixel.R); - Assert.Equal(i == 1 ? 255 : 0, destPixel.G); - Assert.Equal(i == 2 ? 255 : 0, destPixel.B); - Assert.Equal(255, destPixel.A); + Assert.Equal(i == 0 ? 255 : 0, destPixel.R); + Assert.Equal(i == 1 ? 255 : 0, destPixel.G); + Assert.Equal(i == 2 ? 255 : 0, destPixel.B); + Assert.Equal(255, destPixel.A); - var vector4 = testPixel.ToVector4(); - testPixel.FromVector4(vector4); - Assert.Equal(testPixel.ToVector4(), vector4); - } + Vector4 vector4 = testPixel.ToVector4(); + testPixel.FromVector4(vector4); + Assert.Equal(testPixel.ToVector4(), vector4); } + } - [Fact] - public void Test_Rgb32() + [Fact] + public void Test_Rgb32() + { + string[] hexValues = ["FF", "FF00", "FF0000"]; + for (int i = 0; i < 3; i++) { - string[] hexValues = { "FF", "FF00", "FF0000" }; - for (int i = 0; i < 3; i++) - { - int x = i == 0 ? 1 : 0; - int y = i == 1 ? 1 : 0; - int z = i == 2 ? 1 : 0; + int x = i == 0 ? 1 : 0; + int y = i == 1 ? 1 : 0; + int z = i == 2 ? 1 : 0; - var testPixel = new Rgb32(x, y, z); + Rgb32 testPixel = new(x, y, z); - var destPixel = new ImageSharp.PixelFormats.Rgba32(0); - testPixel.ToRgba32(ref destPixel); + Rgba32 destPixel = new(0); + testPixel.ToRgba32(ref destPixel); - Assert.Equal(hexValues[i], testPixel.PackedValue.ToString("X", CultureInfo.InvariantCulture)); + Assert.Equal(hexValues[i], testPixel.PackedValue.ToString("X", CultureInfo.InvariantCulture)); - Assert.Equal(i == 0 ? 255 : 0, destPixel.R); - Assert.Equal(i == 1 ? 255 : 0, destPixel.G); - Assert.Equal(i == 2 ? 255 : 0, destPixel.B); - Assert.Equal(255, destPixel.A); + Assert.Equal(i == 0 ? 255 : 0, destPixel.R); + Assert.Equal(i == 1 ? 255 : 0, destPixel.G); + Assert.Equal(i == 2 ? 255 : 0, destPixel.B); + Assert.Equal(255, destPixel.A); - var vector4 = testPixel.ToVector4(); - testPixel.FromVector4(vector4); - Assert.Equal(testPixel.ToVector4(), vector4); - } + Vector4 vector4 = testPixel.ToVector4(); + testPixel.FromVector4(vector4); + Assert.Equal(testPixel.ToVector4(), vector4); } } } diff --git a/tests/ImageSharp.Textures.Tests/ImageSharp.Textures.Tests.csproj b/tests/ImageSharp.Textures.Tests/ImageSharp.Textures.Tests.csproj index ac9c7c4c..0818c145 100644 --- a/tests/ImageSharp.Textures.Tests/ImageSharp.Textures.Tests.csproj +++ b/tests/ImageSharp.Textures.Tests/ImageSharp.Textures.Tests.csproj @@ -10,11 +10,16 @@ - - + + + + + + + PreserveNewest diff --git a/tests/ImageSharp.Textures.Tests/TestFile.cs b/tests/ImageSharp.Textures.Tests/TestFile.cs index bc4c0ce5..a2ba51f3 100644 --- a/tests/ImageSharp.Textures.Tests/TestFile.cs +++ b/tests/ImageSharp.Textures.Tests/TestFile.cs @@ -1,138 +1,134 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Collections.Concurrent; -using System.IO; -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Textures.Tests +namespace SixLabors.ImageSharp.Textures.Tests; + +/// +/// A test image file. +/// +public class TestFile { /// - /// A test image file. + /// The test file cache. + /// + private static readonly ConcurrentDictionary Cache = new(); + + /// + /// The "Formats" directory, as lazy value + /// + // ReSharper disable once InconsistentNaming + private static readonly Lazy InputImagesDirectoryValue = new(() => TestEnvironment.InputImagesDirectoryFullPath); + + /// + /// The image (lazy initialized value) + /// + private Image image; + + /// + /// The image bytes + /// + private byte[] bytes; + + /// + /// Initializes a new instance of the class. + /// + /// The file. + private TestFile(string file) => this.FullPath = file; + + /// + /// Gets the image bytes. + /// + public byte[] Bytes => this.bytes ??= File.ReadAllBytes(this.FullPath); + + /// + /// Gets the full path to file. + /// + public string FullPath { get; } + + /// + /// Gets the file name. + /// + public string FileName => Path.GetFileName(this.FullPath); + + /// + /// Gets the file name without extension. /// - public class TestFile + public string FileNameWithoutExtension => Path.GetFileNameWithoutExtension(this.FullPath); + + /// + /// Gets the image with lazy initialization. + /// + private Image Image => this.image ??= ImageSharp.Image.Load(this.Bytes); + + /// + /// Gets the input image directory. + /// + private static string InputImagesDirectory => InputImagesDirectoryValue.Value; + + /// + /// Gets the full qualified path to the input test file. + /// + /// + /// The file path. + /// + /// + /// The . + /// + public static string GetInputFileFullPath(string file) => Path.Combine(InputImagesDirectory, file).Replace('\\', Path.DirectorySeparatorChar); + + /// + /// Creates a new test file or returns one from the cache. + /// + /// The file path. + /// + /// The . + /// + public static TestFile Create(string file) => Cache.GetOrAdd(file, (string fileName) => new TestFile(GetInputFileFullPath(file))); + + /// + /// Gets the file name. + /// + /// The value. + /// + /// The . + /// + public string GetFileName(object value) => $"{this.FileNameWithoutExtension}-{value}{Path.GetExtension(this.FullPath)}"; + + /// + /// Gets the file name without extension. + /// + /// The value. + /// + /// The . + /// + public string GetFileNameWithoutExtension(object value) => this.FileNameWithoutExtension + "-" + value; + + /// + /// Creates a new image. + /// + /// + /// The . + /// + public Image CreateRgba32Image() => this.Image.Clone(); + + /// + /// Creates a new image. + /// + /// + /// The . + /// + public Image CreateRgba32Image(IImageFormat format, IImageDecoder decoder) { - /// - /// The test file cache. - /// - private static readonly ConcurrentDictionary Cache = new(); - - /// - /// The "Formats" directory, as lazy value - /// - // ReSharper disable once InconsistentNaming - private static readonly Lazy InputImagesDirectoryValue = new(() => TestEnvironment.InputImagesDirectoryFullPath); - - /// - /// The image (lazy initialized value) - /// - private Image image; - - /// - /// The image bytes - /// - private byte[] bytes; - - /// - /// Initializes a new instance of the class. - /// - /// The file. - private TestFile(string file) => this.FullPath = file; - - /// - /// Gets the image bytes. - /// - public byte[] Bytes => this.bytes ??= File.ReadAllBytes(this.FullPath); - - /// - /// Gets the full path to file. - /// - public string FullPath { get; } - - /// - /// Gets the file name. - /// - public string FileName => Path.GetFileName(this.FullPath); - - /// - /// Gets the file name without extension. - /// - public string FileNameWithoutExtension => Path.GetFileNameWithoutExtension(this.FullPath); - - /// - /// Gets the image with lazy initialization. - /// - private Image Image => this.image ??= ImageSharp.Image.Load(this.Bytes); - - /// - /// Gets the input image directory. - /// - private static string InputImagesDirectory => InputImagesDirectoryValue.Value; - - /// - /// Gets the full qualified path to the input test file. - /// - /// - /// The file path. - /// - /// - /// The . - /// - public static string GetInputFileFullPath(string file) => Path.Combine(InputImagesDirectory, file).Replace('\\', Path.DirectorySeparatorChar); - - /// - /// Creates a new test file or returns one from the cache. - /// - /// The file path. - /// - /// The . - /// - public static TestFile Create(string file) => Cache.GetOrAdd(file, (string fileName) => new TestFile(GetInputFileFullPath(file))); - - /// - /// Gets the file name. - /// - /// The value. - /// - /// The . - /// - public string GetFileName(object value) => $"{this.FileNameWithoutExtension}-{value}{Path.GetExtension(this.FullPath)}"; - - /// - /// Gets the file name without extension. - /// - /// The value. - /// - /// The . - /// - public string GetFileNameWithoutExtension(object value) => this.FileNameWithoutExtension + "-" + value; - - /// - /// Creates a new image. - /// - /// - /// The . - /// - public Image CreateRgba32Image() => this.Image.Clone(); - - /// - /// Creates a new image. - /// - /// - /// The . - /// - public Image CreateRgba32Image(IImageFormat format, IImageDecoder decoder) + DecoderOptions options = new() { - var options = new DecoderOptions - { - Configuration = this.Image.Configuration - }; - options.Configuration.ImageFormatsManager.SetDecoder(format, decoder); - - return ImageSharp.Image.Load(options, this.Bytes); - } + Configuration = this.Image.Configuration + }; + options.Configuration.ImageFormatsManager.SetDecoder(format, decoder); + + return ImageSharp.Image.Load(options, this.Bytes); } } diff --git a/tests/ImageSharp.Textures.Tests/TestImages.cs b/tests/ImageSharp.Textures.Tests/TestImages.cs index d3df36c1..fc8d636b 100644 --- a/tests/ImageSharp.Textures.Tests/TestImages.cs +++ b/tests/ImageSharp.Textures.Tests/TestImages.cs @@ -1,16 +1,95 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.Tests +namespace SixLabors.ImageSharp.Textures.Tests; + +/// +/// Class that contains all the relative test image paths in the Images/Input/Formats directory. +/// +public static class TestImages { - /// - /// Class that contains all the relative test image paths in the Images/Input/Formats directory. - /// - public static class TestImages + public static class Astc { - public static class Ktx + public const string InputFolder = "Astc/Input"; + public const string ExpectedFolder = "Astc/Expected"; + public const string HdrFolder = "Astc/HDR"; + } + + public static class Ktx + { + public const string Rgba = "rgba8888.ktx"; + + public static class Astc { - public const string Rgba = "rgba8888.ktx"; + public const string Rgb32_8x8 = "astc-rgba32-8x8.ktx"; + } + } + + public static class Ktx2 + { + public static class Astc + { + // Flat textures with various block sizes + public const string Rgba32_4x4 = "astc_rgba32_4x4.ktx2"; + public const string Rgba32_5x4 = "astc_rgba32_5x4.ktx2"; + public const string Rgba32_5x5 = "astc_rgba32_5x5.ktx2"; + public const string Rgba32_6x5 = "astc_rgba32_6x5.ktx2"; + public const string Rgba32_6x6 = "astc_rgba32_6x6.ktx2"; + public const string Rgba32_8x5 = "astc_rgba32_8x5.ktx2"; + public const string Rgba32_8x6 = "astc_rgba32_8x6.ktx2"; + public const string Rgba32_8x8 = "astc_rgba32_8x8.ktx2"; + public const string Rgba32_10x5 = "astc_rgba32_10x5.ktx2"; + public const string Rgba32_10x6 = "astc_rgba32_10x6.ktx2"; + public const string Rgba32_10x8 = "astc_rgba32_10x8.ktx2"; + public const string Rgba32_10x10 = "astc_rgba32_10x10.ktx2"; + public const string Rgba32_12x10 = "astc_rgba32_12x10.ktx2"; + public const string Rgba32_12x12 = "astc_rgba32_12x12.ktx2"; + + public const string Rgb32_sRgb_4x4 = "valid_ASTC_4x4_SRGB_BLOCK_2D.ktx2"; + public const string Rgb32_sRgb_5x4 = "valid_ASTC_5x4_SRGB_BLOCK_2D.ktx2"; + public const string Rgb32_sRgb_5x5 = "valid_ASTC_5x5_SRGB_BLOCK_2D.ktx2"; + public const string Rgb32_sRgb_6x5 = "valid_ASTC_6x5_SRGB_BLOCK_2D.ktx2"; + public const string Rgb32_sRgb_6x6 = "valid_ASTC_6x6_SRGB_BLOCK_2D.ktx2"; + public const string Rgb32_sRgb_8x5 = "valid_ASTC_8x5_SRGB_BLOCK_2D.ktx2"; + public const string Rgb32_sRgb_8x6 = "valid_ASTC_8x6_SRGB_BLOCK_2D.ktx2"; + public const string Rgb32_sRgb_8x8 = "valid_ASTC_8x8_SRGB_BLOCK_2D.ktx2"; + public const string Rgb32_sRgb_10x5 = "valid_ASTC_10x5_SRGB_BLOCK_2D.ktx2"; + public const string Rgb32_sRgb_10x6 = "valid_ASTC_10x6_SRGB_BLOCK_2D.ktx2"; + public const string Rgb32_sRgb_10x8 = "valid_ASTC_10x8_SRGB_BLOCK_2D.ktx2"; + public const string Rgb32_sRgb_10x10 = "valid_ASTC_10x10_SRGB_BLOCK_2D.ktx2"; + public const string Rgb32_sRgb_12x10 = "valid_ASTC_12x10_SRGB_BLOCK_2D.ktx2"; + public const string Rgb32_sRgb_12x12 = "valid_ASTC_12x12_SRGB_BLOCK_2D.ktx2"; + + public const string Rgb32_Unorm_4x4 = "valid_ASTC_4x4_UNORM_BLOCK_2D.ktx2"; + public const string Rgb32_Unorm_5x4 = "valid_ASTC_5x4_UNORM_BLOCK_2D.ktx2"; + public const string Rgb32_Unorm_5x5 = "valid_ASTC_5x5_UNORM_BLOCK_2D.ktx2"; + public const string Rgb32_Unorm_6x5 = "valid_ASTC_6x5_UNORM_BLOCK_2D.ktx2"; + public const string Rgb32_Unorm_6x6 = "valid_ASTC_6x6_UNORM_BLOCK_2D.ktx2"; + public const string Rgb32_Unorm_8x5 = "valid_ASTC_8x5_UNORM_BLOCK_2D.ktx2"; + public const string Rgb32_Unorm_8x6 = "valid_ASTC_8x6_UNORM_BLOCK_2D.ktx2"; + public const string Rgb32_Unorm_8x8 = "valid_ASTC_8x8_UNORM_BLOCK_2D.ktx2"; + public const string Rgb32_Unorm_10x5 = "valid_ASTC_10x5_UNORM_BLOCK_2D.ktx2"; + public const string Rgb32_Unorm_10x6 = "valid_ASTC_10x6_UNORM_BLOCK_2D.ktx2"; + public const string Rgb32_Unorm_10x8 = "valid_ASTC_10x8_UNORM_BLOCK_2D.ktx2"; + public const string Rgb32_Unorm_10x10 = "valid_ASTC_10x10_UNORM_BLOCK_2D.ktx2"; + public const string Rgb32_Unorm_12x10 = "valid_ASTC_12x10_UNORM_BLOCK_2D.ktx2"; + public const string Rgb32_Unorm_12x12 = "valid_ASTC_12x12_UNORM_BLOCK_2D.ktx2"; + + public const string Rgb32_Srgb_Large = "astc_ldr_10x5_FlightHelmet_baseColor.ktx2"; + + // Textures with several levels of MipMaps + public const string Rgb32_Srgb_6x6_MipMap = "astc_ldr_6x6_arraytex_7_mipmap.ktx2"; + + // Supercompressed textures (ZLIB) + public const string Rgb32_Unorm_4x4_Zlib1 = "valid_ASTC_4x4_UNORM_BLOCK_2D_ZLIB_1.ktx2"; + public const string Rgb32_Unorm_4x4_Zlib9 = "valid_ASTC_4x4_UNORM_BLOCK_2D_ZLIB_9.ktx2"; + + // Supercompressed textures (ZSTD) + public const string Rgb32_Unorm_4x4_Zstd1 = "valid_ASTC_4x4_UNORM_BLOCK_2D_ZSTD_1.ktx2"; + public const string Rgb32_Unorm_4x4_Zstd9 = "valid_ASTC_4x4_UNORM_BLOCK_2D_ZSTD_9.ktx2"; + + // Cubemap textures + public const string Rgb32_Srgb_6x6_Cube = "astc_ldr_cubemap_6x6.ktx2"; } } } diff --git a/tests/ImageSharp.Textures.Tests/TestUtilities/Attributes/GroupOutputAttribute.cs b/tests/ImageSharp.Textures.Tests/TestUtilities/Attributes/GroupOutputAttribute.cs index 6888d67f..ecd3a044 100644 --- a/tests/ImageSharp.Textures.Tests/TestUtilities/Attributes/GroupOutputAttribute.cs +++ b/tests/ImageSharp.Textures.Tests/TestUtilities/Attributes/GroupOutputAttribute.cs @@ -1,18 +1,15 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; +namespace SixLabors.ImageSharp.Textures.Tests.TestUtilities.Attributes; -namespace SixLabors.ImageSharp.Textures.Tests.TestUtilities.Attributes +/// +/// The output produced by this test class should be grouped into the specified subfolder. +/// +[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] +public class GroupOutputAttribute : Attribute { - /// - /// The output produced by this test class should be grouped into the specified subfolder. - /// - [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] - public class GroupOutputAttribute : Attribute - { - public GroupOutputAttribute(string subfolder) => this.Subfolder = subfolder; + public GroupOutputAttribute(string subfolder) => this.Subfolder = subfolder; - public string Subfolder { get; } - } + public string Subfolder { get; } } diff --git a/tests/ImageSharp.Textures.Tests/TestUtilities/Attributes/WithFileAttribute.cs b/tests/ImageSharp.Textures.Tests/TestUtilities/Attributes/WithFileAttribute.cs index b8eed784..dec5951a 100644 --- a/tests/ImageSharp.Textures.Tests/TestUtilities/Attributes/WithFileAttribute.cs +++ b/tests/ImageSharp.Textures.Tests/TestUtilities/Attributes/WithFileAttribute.cs @@ -1,57 +1,52 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; using System.Reflection; using System.Text.RegularExpressions; using SixLabors.ImageSharp.Textures.Tests.Enums; using SixLabors.ImageSharp.Textures.Tests.TestUtilities.TextureProviders; using Xunit.Sdk; -namespace SixLabors.ImageSharp.Textures.Tests.TestUtilities.Attributes +namespace SixLabors.ImageSharp.Textures.Tests.TestUtilities.Attributes; + +public class WithFileAttribute : DataAttribute { - public class WithFileAttribute : DataAttribute + private readonly TestTextureFormat textureFormat; + private readonly TestTextureType textureType; + private readonly TestTextureTool textureTool; + private readonly string inputFile; + private readonly bool isRegex; + + public WithFileAttribute(TestTextureFormat textureFormat, TestTextureType textureType, TestTextureTool textureTool, string inputFile, bool isRegex = false) { - private readonly TestTextureFormat textureFormat; - private readonly TestTextureType textureType; - private readonly TestTextureTool textureTool; - private readonly string inputFile; - private readonly bool isRegex; + this.textureFormat = textureFormat; + this.textureType = textureType; + this.textureTool = textureTool; + this.inputFile = inputFile; + this.isRegex = isRegex; + } - public WithFileAttribute(TestTextureFormat textureFormat, TestTextureType textureType, TestTextureTool textureTool, string inputFile, bool isRegex = false) - { - this.textureFormat = textureFormat; - this.textureType = textureType; - this.textureTool = textureTool; - this.inputFile = inputFile; - this.isRegex = isRegex; - } + public override IEnumerable GetData(MethodInfo testMethod) + { + ArgumentNullException.ThrowIfNull(testMethod); + + string[] featureLevels = this.textureTool == TestTextureTool.TexConv ? ["9.1", "9.2", "9.3", "10.0", "10.1", "11.0", "11.1", "12.0", "12.1"] : [string.Empty]; - public override IEnumerable GetData(MethodInfo testMethod) + foreach (string featureLevel in featureLevels) { - ArgumentNullException.ThrowIfNull(testMethod); + string path = Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.textureFormat.ToString()); - string[] featureLevels = this.textureTool == TestTextureTool.TexConv ? new[] { "9.1", "9.2", "9.3", "10.0", "10.1", "11.0", "11.1", "12.0", "12.1" } : new[] { string.Empty }; + if (!string.IsNullOrEmpty(featureLevel)) + { + path = Path.Combine(path, featureLevel); + } - foreach (string featureLevel in featureLevels) + string[] files = Directory.GetFiles(path); + string[] filteredFiles = [.. files.Where(f => this.isRegex ? new Regex(this.inputFile).IsMatch(Path.GetFileName(f)) : Path.GetFileName(f).Equals(this.inputFile, StringComparison.OrdinalIgnoreCase))]; + foreach (string file in filteredFiles) { - string path = Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.textureFormat.ToString()); - - if (!string.IsNullOrEmpty(featureLevel)) - { - path = Path.Combine(path, featureLevel); - } - - string[] files = Directory.GetFiles(path); - string[] filteredFiles = files.Where(f => this.isRegex ? new Regex(this.inputFile).IsMatch(Path.GetFileName(f)) : Path.GetFileName(f).Equals(this.inputFile, StringComparison.OrdinalIgnoreCase)).ToArray(); - foreach (string file in filteredFiles) - { - var testTextureProvider = new TestTextureProvider(testMethod.Name, this.textureFormat, this.textureType, this.textureTool, file, false); - yield return new object[] { testTextureProvider }; - } + TestTextureProvider testTextureProvider = new(testMethod.Name, this.textureFormat, this.textureType, this.textureTool, file, false); + yield return new object[] { testTextureProvider }; } } } diff --git a/tests/ImageSharp.Textures.Tests/TestUtilities/ImageComparison/ExactImageComparer.cs b/tests/ImageSharp.Textures.Tests/TestUtilities/ImageComparison/ExactImageComparer.cs index 3035fad6..6c6b2cfa 100644 --- a/tests/ImageSharp.Textures.Tests/TestUtilities/ImageComparison/ExactImageComparer.cs +++ b/tests/ImageSharp.Textures.Tests/TestUtilities/ImageComparison/ExactImageComparer.cs @@ -1,62 +1,58 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; -using System.Collections.Generic; using System.Numerics; -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Textures.Tests.TestUtilities.ImageComparison +namespace SixLabors.ImageSharp.Textures.Tests.TestUtilities.ImageComparison; + +public class ExactImageComparer : ImageComparer { - public class ExactImageComparer : ImageComparer - { - public static ExactImageComparer Instance { get; } = new ExactImageComparer(); + public static ExactImageComparer Instance { get; } = new ExactImageComparer(); - public override ImageSimilarityReport CompareImages( - Image expected, - Image actual) + public override ImageSimilarityReport CompareImages( + Image expected, + Image actual) + { + if (expected.Size != actual.Size) { - if (expected.Size != actual.Size) - { - throw new InvalidOperationException("Calling ImageComparer is invalid when dimensions mismatch!"); - } + throw new InvalidOperationException("Calling ImageComparer is invalid when dimensions mismatch!"); + } - int width = actual.Width; + int width = actual.Width; - // TODO: Comparing through Rgba64 may not be robust enough because of the existence of super high precision pixel types. - var aBuffer = new Vector4[width]; - var bBuffer = new Vector4[width]; + // TODO: Comparing through Rgba64 may not be robust enough because of the existence of super high precision pixel types. + Vector4[] aBuffer = new Vector4[width]; + Vector4[] bBuffer = new Vector4[width]; - Buffer2D expectedBuffer = expected.Frames.RootFrame.PixelBuffer; - Buffer2D actualBuffer = actual.Frames.RootFrame.PixelBuffer; + Buffer2D expectedBuffer = expected.Frames.RootFrame.PixelBuffer; + Buffer2D actualBuffer = actual.Frames.RootFrame.PixelBuffer; - var differences = new List(); - ImageSharp.Configuration configuration = expected.Configuration; + List differences = new(); + ImageSharp.Configuration configuration = expected.Configuration; - for (int y = 0; y < actual.Height; y++) - { - Span aSpan = expectedBuffer.DangerousGetRowSpan(y); - Span bSpan = actualBuffer.DangerousGetRowSpan(y); + for (int y = 0; y < actual.Height; y++) + { + Span aSpan = expectedBuffer.DangerousGetRowSpan(y); + Span bSpan = actualBuffer.DangerousGetRowSpan(y); + + PixelOperations.Instance.ToVector4(configuration, aSpan, aBuffer); + PixelOperations.Instance.ToVector4(configuration, bSpan, bBuffer); - PixelOperations.Instance.ToVector4(configuration, aSpan, aBuffer); - PixelOperations.Instance.ToVector4(configuration, bSpan, bBuffer); + for (int x = 0; x < width; x++) + { + Vector4 aPixel = aBuffer[x]; + Vector4 bPixel = bBuffer[x]; - for (int x = 0; x < width; x++) + if (aPixel != bPixel) { - Vector4 aPixel = aBuffer[x]; - Vector4 bPixel = bBuffer[x]; - - if (aPixel != bPixel) - { - var diff = new PixelDifference(new Point(x, y), aPixel, bPixel); - differences.Add(diff); - } + PixelDifference diff = new(new Point(x, y), aPixel, bPixel); + differences.Add(diff); } } - - return new ImageSimilarityReport(expected, actual, differences); } + + return new ImageSimilarityReport(expected, actual, differences); } } diff --git a/tests/ImageSharp.Textures.Tests/TestUtilities/ImageComparison/Exceptions/ImageDimensionsMismatchException.cs b/tests/ImageSharp.Textures.Tests/TestUtilities/ImageComparison/Exceptions/ImageDimensionsMismatchException.cs index f59af42a..d8c53eac 100644 --- a/tests/ImageSharp.Textures.Tests/TestUtilities/ImageComparison/Exceptions/ImageDimensionsMismatchException.cs +++ b/tests/ImageSharp.Textures.Tests/TestUtilities/ImageComparison/Exceptions/ImageDimensionsMismatchException.cs @@ -1,19 +1,18 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.Tests.TestUtilities.ImageComparison.Exceptions +namespace SixLabors.ImageSharp.Textures.Tests.TestUtilities.ImageComparison.Exceptions; + +public class ImageDimensionsMismatchException : ImagesSimilarityException { - public class ImageDimensionsMismatchException : ImagesSimilarityException + public ImageDimensionsMismatchException(Size expectedSize, Size actualSize) + : base($"The image dimensions {actualSize} do not match the expected {expectedSize}!") { - public ImageDimensionsMismatchException(Size expectedSize, Size actualSize) - : base($"The image dimensions {actualSize} do not match the expected {expectedSize}!") - { - this.ExpectedSize = expectedSize; - this.ActualSize = actualSize; - } + this.ExpectedSize = expectedSize; + this.ActualSize = actualSize; + } - public Size ExpectedSize { get; } + public Size ExpectedSize { get; } - public Size ActualSize { get; } - } + public Size ActualSize { get; } } diff --git a/tests/ImageSharp.Textures.Tests/TestUtilities/ImageComparison/Exceptions/ImagesSimilarityException.cs b/tests/ImageSharp.Textures.Tests/TestUtilities/ImageComparison/Exceptions/ImagesSimilarityException.cs index 38c0454a..42286988 100644 --- a/tests/ImageSharp.Textures.Tests/TestUtilities/ImageComparison/Exceptions/ImagesSimilarityException.cs +++ b/tests/ImageSharp.Textures.Tests/TestUtilities/ImageComparison/Exceptions/ImagesSimilarityException.cs @@ -1,15 +1,14 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.Tests.TestUtilities.ImageComparison.Exceptions -{ - using System; +namespace SixLabors.ImageSharp.Textures.Tests.TestUtilities.ImageComparison.Exceptions; + +using System; - public class ImagesSimilarityException : Exception +public class ImagesSimilarityException : Exception +{ + public ImagesSimilarityException(string message) + : base(message) { - public ImagesSimilarityException(string message) - : base(message) - { - } } } diff --git a/tests/ImageSharp.Textures.Tests/TestUtilities/ImageComparison/ImageComparer.cs b/tests/ImageSharp.Textures.Tests/TestUtilities/ImageComparison/ImageComparer.cs index c2b9d745..8eae6e18 100644 --- a/tests/ImageSharp.Textures.Tests/TestUtilities/ImageComparison/ImageComparer.cs +++ b/tests/ImageSharp.Textures.Tests/TestUtilities/ImageComparison/ImageComparer.cs @@ -1,107 +1,103 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.Collections.Generic; -using System.Globalization; -using System.Linq; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Textures.Tests.TestUtilities.ImageComparison.Exceptions; -namespace SixLabors.ImageSharp.Textures.Tests.TestUtilities.ImageComparison +namespace SixLabors.ImageSharp.Textures.Tests.TestUtilities.ImageComparison; + +public abstract class ImageComparer { - public abstract class ImageComparer - { - public static ImageComparer Exact { get; } = Tolerant(0, 0); + public static ImageComparer Exact { get; } = Tolerant(0, 0); - /// - /// Returns an instance of . - /// Individual manhattan pixel difference is only added to total image difference when the individual difference is over 'perPixelManhattanThreshold'. - /// - /// The comparer. - public static ImageComparer Tolerant( - float imageThreshold = TolerantImageComparer.DefaultImageThreshold, - int perPixelManhattanThreshold = 0) => new TolerantImageComparer(imageThreshold, perPixelManhattanThreshold); + /// + /// Returns an instance of . + /// Individual manhattan pixel difference is only added to total image difference when the individual difference is over 'perPixelManhattanThreshold'. + /// + /// The comparer. + public static ImageComparer Tolerant( + float imageThreshold = TolerantImageComparer.DefaultImageThreshold, + int perPixelManhattanThreshold = 0) => new TolerantImageComparer(imageThreshold, perPixelManhattanThreshold); - /// - /// Returns Tolerant(imageThresholdInPercents/100) - /// - /// The comparer. - public static ImageComparer TolerantPercentage(float imageThresholdInPercents, int perPixelManhattanThreshold = 0) - => Tolerant(imageThresholdInPercents / 100F, perPixelManhattanThreshold); + /// + /// Returns Tolerant(imageThresholdInPercents/100) + /// + /// The comparer. + public static ImageComparer TolerantPercentage(float imageThresholdInPercents, int perPixelManhattanThreshold = 0) + => Tolerant(imageThresholdInPercents / 100F, perPixelManhattanThreshold); - public abstract ImageSimilarityReport CompareImages( - Image expected, - Image actual) - where TPixelA : unmanaged, IPixel - where TPixelB : unmanaged, IPixel; - } + public abstract ImageSimilarityReport CompareImages( + Image expected, + Image actual) + where TPixelA : unmanaged, IPixel + where TPixelB : unmanaged, IPixel; +} - public static class ImageComparerExtensions +public static class ImageComparerExtensions +{ + public static ImageSimilarityReport CompareImages( + this ImageComparer comparer, + Image expected, + Image actual) + where TPixelA : unmanaged, IPixel + where TPixelB : unmanaged, IPixel => comparer.CompareImages(expected, actual); + + public static void VerifySimilarity( + this ImageComparer comparer, + Image expected, + Image actual) + where TPixelA : unmanaged, IPixel + where TPixelB : unmanaged, IPixel { - public static ImageSimilarityReport CompareImages( - this ImageComparer comparer, - Image expected, - Image actual) - where TPixelA : unmanaged, IPixel - where TPixelB : unmanaged, IPixel => comparer.CompareImages(expected, actual); + if (expected.Size != actual.Size) + { + throw new ImageDimensionsMismatchException(expected.Size, actual.Size); + } - public static void VerifySimilarity( - this ImageComparer comparer, - Image expected, - Image actual) - where TPixelA : unmanaged, IPixel - where TPixelB : unmanaged, IPixel + if (expected.Frames.Count != actual.Frames.Count) { - if (expected.Size != actual.Size) - { - throw new ImageDimensionsMismatchException(expected.Size, actual.Size); - } + throw new ImagesSimilarityException("Image frame count does not match!"); + } - if (expected.Frames.Count != actual.Frames.Count) - { - throw new ImagesSimilarityException("Image frame count does not match!"); - } + ImageSimilarityReport report = comparer.CompareImages(expected, actual); + if ((report.TotalNormalizedDifference ?? 0F) != 0F) + { + throw new ImagesSimilarityException(report.ToString()); + } + } - ImageSimilarityReport report = comparer.CompareImages(expected, actual); - if ((report.TotalNormalizedDifference ?? 0F) != 0F) - { - throw new ImagesSimilarityException(report.ToString()); - } + public static void VerifySimilarityIgnoreRegion( + this ImageComparer comparer, + Image expected, + Image actual, + Rectangle ignoredRegion) + where TPixelA : unmanaged, IPixel + where TPixelB : unmanaged, IPixel + { + if (expected.Size != actual.Size) + { + throw new ImageDimensionsMismatchException(expected.Size, actual.Size); } - public static void VerifySimilarityIgnoreRegion( - this ImageComparer comparer, - Image expected, - Image actual, - Rectangle ignoredRegion) - where TPixelA : unmanaged, IPixel - where TPixelB : unmanaged, IPixel + if (expected.Frames.Count != actual.Frames.Count) { - if (expected.Size != actual.Size) - { - throw new ImageDimensionsMismatchException(expected.Size, actual.Size); - } + throw new ImagesSimilarityException("Image frame count does not match!"); + } - if (expected.Frames.Count != actual.Frames.Count) - { - throw new ImagesSimilarityException("Image frame count does not match!"); - } + ImageSimilarityReport report = comparer.CompareImages(expected, actual); + if ((report.TotalNormalizedDifference ?? 0F) != 0F) + { + IEnumerable outsideChanges = report.Differences.Where( + x => + !(ignoredRegion.X <= x.Position.X + && x.Position.X <= ignoredRegion.Right + && ignoredRegion.Y <= x.Position.Y + && x.Position.Y <= ignoredRegion.Bottom)); - ImageSimilarityReport report = comparer.CompareImages(expected, actual); - if ((report.TotalNormalizedDifference ?? 0F) != 0F) + if (outsideChanges.Any()) { - IEnumerable outsideChanges = report.Differences.Where( - x => - !(ignoredRegion.X <= x.Position.X - && x.Position.X <= ignoredRegion.Right - && ignoredRegion.Y <= x.Position.Y - && x.Position.Y <= ignoredRegion.Bottom)); - - if (outsideChanges.Any()) - { - var cleanedReport = new ImageSimilarityReport(expected, actual, outsideChanges, null); - throw new ImagesSimilarityException(cleanedReport.ToString()); - } + ImageSimilarityReport cleanedReport = new(expected, actual, outsideChanges, null); + throw new ImagesSimilarityException(cleanedReport.ToString()); } } } diff --git a/tests/ImageSharp.Textures.Tests/TestUtilities/ImageComparison/ImageSimilarityReport.cs b/tests/ImageSharp.Textures.Tests/TestUtilities/ImageComparison/ImageSimilarityReport.cs index 0a1e4d41..23999f91 100644 --- a/tests/ImageSharp.Textures.Tests/TestUtilities/ImageComparison/ImageSimilarityReport.cs +++ b/tests/ImageSharp.Textures.Tests/TestUtilities/ImageComparison/ImageSimilarityReport.cs @@ -1,111 +1,104 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; -using System.Collections.Generic; using System.Globalization; -using System.Linq; using System.Text; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Textures.Tests.TestUtilities.ImageComparison +namespace SixLabors.ImageSharp.Textures.Tests.TestUtilities.ImageComparison; + +public class ImageSimilarityReport { - public class ImageSimilarityReport + // Provide an empty non-generic instance to avoid CA1000 in generic type. + public static ImageSimilarityReport Empty => new(null, null, Array.Empty(), 0f); + + protected ImageSimilarityReport( + object expectedImage, + object actualImage, + IEnumerable differences, + float? totalNormalizedDifference = null) { - // Provide an empty non-generic instance to avoid CA1000 in generic type. - public static ImageSimilarityReport Empty => new ImageSimilarityReport(null, null, Array.Empty(), 0f); - - protected ImageSimilarityReport( - object expectedImage, - object actualImage, - IEnumerable differences, - float? totalNormalizedDifference = null) - { - this.ExpectedImage = expectedImage; - this.ActualImage = actualImage; - this.TotalNormalizedDifference = totalNormalizedDifference; - this.Differences = differences.ToArray(); - } + this.ExpectedImage = expectedImage; + this.ActualImage = actualImage; + this.TotalNormalizedDifference = totalNormalizedDifference; + this.Differences = [.. differences]; + } - public object ExpectedImage { get; } + public object ExpectedImage { get; } - public object ActualImage { get; } + public object ActualImage { get; } - // TODO: This should not be a nullable value! - public float? TotalNormalizedDifference { get; } + // TODO: This should not be a nullable value! + public float? TotalNormalizedDifference { get; } - public string DifferencePercentageString + public string DifferencePercentageString + { + get { - get + if (!this.TotalNormalizedDifference.HasValue) + { + return "?"; + } + else if (this.TotalNormalizedDifference == 0) { - if (!this.TotalNormalizedDifference.HasValue) - { - return "?"; - } - else if (this.TotalNormalizedDifference == 0) - { - return "0%"; - } - else - { - return string.Format(CultureInfo.InvariantCulture, "{0:0.0000}%", this.TotalNormalizedDifference.Value * 100); - } + return "0%"; + } + else + { + return string.Format(CultureInfo.InvariantCulture, "{0:0.0000}%", this.TotalNormalizedDifference.Value * 100); } } + } - public PixelDifference[] Differences { get; } + public PixelDifference[] Differences { get; } - public bool IsEmpty => this.Differences.Length == 0; + public bool IsEmpty => this.Differences.Length == 0; - public override string ToString() - { - return this.IsEmpty ? "[SimilarImages]" : this.PrintDifference(); - } + public override string ToString() => this.IsEmpty ? "[SimilarImages]" : this.PrintDifference(); - private string PrintDifference() + private string PrintDifference() + { + StringBuilder sb = new(); + if (this.TotalNormalizedDifference.HasValue) { - var sb = new StringBuilder(); - if (this.TotalNormalizedDifference.HasValue) - { - sb.AppendLine(); - sb.AppendLine(string.Format(CultureInfo.InvariantCulture, "Total difference: {0}", this.DifferencePercentageString)); - } - - int max = Math.Min(5, this.Differences.Length); + sb.AppendLine(); + sb.AppendLine(string.Format(CultureInfo.InvariantCulture, "Total difference: {0}", this.DifferencePercentageString)); + } - for (int i = 0; i < max; i++) - { - sb.Append(this.Differences[i]); - if (i < max - 1) - { - sb.AppendFormat(CultureInfo.InvariantCulture, ";{0}", Environment.NewLine); - } - } + int max = Math.Min(5, this.Differences.Length); - if (this.Differences.Length >= 5) + for (int i = 0; i < max; i++) + { + sb.Append(this.Differences[i]); + if (i < max - 1) { - sb.Append("..."); + sb.AppendFormat(CultureInfo.InvariantCulture, ";{0}", Environment.NewLine); } - - return sb.ToString(); } - } - public class ImageSimilarityReport : ImageSimilarityReport - where TPixelA : unmanaged, IPixel - where TPixelB : unmanaged, IPixel - { - public ImageSimilarityReport( - Image expectedImage, - Image actualImage, - IEnumerable differences, - float? totalNormalizedDifference = null) - : base(expectedImage, actualImage, differences, totalNormalizedDifference) + if (this.Differences.Length >= 5) { + sb.Append("..."); } - public new Image ExpectedImage => (Image)base.ExpectedImage; + return sb.ToString(); + } +} - public new Image ActualImage => (Image)base.ActualImage; +public class ImageSimilarityReport : ImageSimilarityReport + where TPixelA : unmanaged, IPixel + where TPixelB : unmanaged, IPixel +{ + public ImageSimilarityReport( + Image expectedImage, + Image actualImage, + IEnumerable differences, + float? totalNormalizedDifference = null) + : base(expectedImage, actualImage, differences, totalNormalizedDifference) + { } + + public new Image ExpectedImage => (Image)base.ExpectedImage; + + public new Image ActualImage => (Image)base.ActualImage; } diff --git a/tests/ImageSharp.Textures.Tests/TestUtilities/ImageComparison/PixelDifference.cs b/tests/ImageSharp.Textures.Tests/TestUtilities/ImageComparison/PixelDifference.cs index 46153a6f..3faf66ac 100644 --- a/tests/ImageSharp.Textures.Tests/TestUtilities/ImageComparison/PixelDifference.cs +++ b/tests/ImageSharp.Textures.Tests/TestUtilities/ImageComparison/PixelDifference.cs @@ -3,45 +3,44 @@ using System.Numerics; -namespace SixLabors.ImageSharp.Textures.Tests.TestUtilities.ImageComparison +namespace SixLabors.ImageSharp.Textures.Tests.TestUtilities.ImageComparison; + +public readonly struct PixelDifference { - public readonly struct PixelDifference + public PixelDifference( + Point position, + float redDifference, + float greenDifference, + float blueDifference, + float alphaDifference) { - public PixelDifference( - Point position, - float redDifference, - float greenDifference, - float blueDifference, - float alphaDifference) - { - this.Position = position; - this.RedDifference = redDifference; - this.GreenDifference = greenDifference; - this.BlueDifference = blueDifference; - this.AlphaDifference = alphaDifference; - } - - public PixelDifference(Point position, Vector4 expected, Vector4 actual) - : this( - position, - actual.X - expected.X, - actual.Y - expected.Y, - actual.Z - expected.Z, - actual.W - expected.W) - { - } - - public Point Position { get; } - - public float RedDifference { get; } - - public float GreenDifference { get; } - - public float BlueDifference { get; } - - public float AlphaDifference { get; } - - public override string ToString() => - $"[Δ({this.RedDifference},{this.GreenDifference},{this.BlueDifference},{this.AlphaDifference}) @ ({this.Position.X},{this.Position.Y})]"; + this.Position = position; + this.RedDifference = redDifference; + this.GreenDifference = greenDifference; + this.BlueDifference = blueDifference; + this.AlphaDifference = alphaDifference; } + + public PixelDifference(Point position, Vector4 expected, Vector4 actual) + : this( + position, + actual.X - expected.X, + actual.Y - expected.Y, + actual.Z - expected.Z, + actual.W - expected.W) + { + } + + public Point Position { get; } + + public float RedDifference { get; } + + public float GreenDifference { get; } + + public float BlueDifference { get; } + + public float AlphaDifference { get; } + + public override string ToString() => + $"[Δ({this.RedDifference},{this.GreenDifference},{this.BlueDifference},{this.AlphaDifference}) @ ({this.Position.X},{this.Position.Y})]"; } diff --git a/tests/ImageSharp.Textures.Tests/TestUtilities/ImageComparison/TolerantImageComparer.cs b/tests/ImageSharp.Textures.Tests/TestUtilities/ImageComparison/TolerantImageComparer.cs index acfd577e..bdf4830f 100644 --- a/tests/ImageSharp.Textures.Tests/TestUtilities/ImageComparison/TolerantImageComparer.cs +++ b/tests/ImageSharp.Textures.Tests/TestUtilities/ImageComparison/TolerantImageComparer.cs @@ -1,127 +1,121 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; -using System.Collections.Generic; using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Textures.Tests.TestUtilities.ImageComparison +namespace SixLabors.ImageSharp.Textures.Tests.TestUtilities.ImageComparison; + +public class TolerantImageComparer : ImageComparer { - public class TolerantImageComparer : ImageComparer + // 1% of all pixels in a 100*100 pixel area are allowed to have a difference of 1 unit + // 257 = (1 / 255) * 65535. + public const float DefaultImageThreshold = 257F / (100 * 100 * 65535); + + /// + /// Individual manhattan pixel difference is only added to total image difference when the individual difference is over 'perPixelManhattanThreshold'. + /// + /// The maximal tolerated difference represented by a value between 0.0 and 1.0 scaled to 0 and 65535. + /// Gets the threshold of the individual pixels before they accumulate towards the overall difference. + public TolerantImageComparer(float imageThreshold, int perPixelManhattanThreshold = 0) { - // 1% of all pixels in a 100*100 pixel area are allowed to have a difference of 1 unit - // 257 = (1 / 255) * 65535. - public const float DefaultImageThreshold = 257F / (100 * 100 * 65535); - - /// - /// Individual manhattan pixel difference is only added to total image difference when the individual difference is over 'perPixelManhattanThreshold'. - /// - /// The maximal tolerated difference represented by a value between 0.0 and 1.0 scaled to 0 and 65535. - /// Gets the threshold of the individual pixels before they accumulate towards the overall difference. - public TolerantImageComparer(float imageThreshold, int perPixelManhattanThreshold = 0) - { - Guard.MustBeGreaterThanOrEqualTo(imageThreshold, 0, nameof(imageThreshold)); + Guard.MustBeGreaterThanOrEqualTo(imageThreshold, 0, nameof(imageThreshold)); - this.ImageThreshold = imageThreshold; - this.PerPixelManhattanThreshold = perPixelManhattanThreshold; - } + this.ImageThreshold = imageThreshold; + this.PerPixelManhattanThreshold = perPixelManhattanThreshold; + } - /// - /// - /// Gets the maximal tolerated difference represented by a value between 0.0 and 1.0 scaled to 0 and 65535. - /// Examples of percentage differences on a single pixel: - /// 1. PixelA = (65535,65535,65535,0) PixelB =(0,0,0,65535) leads to 100% difference on a single pixel - /// 2. PixelA = (65535,65535,65535,0) PixelB =(65535,65535,65535,65535) leads to 25% difference on a single pixel - /// 3. PixelA = (65535,65535,65535,0) PixelB =(32767,32767,32767,32767) leads to 50% difference on a single pixel - /// - /// - /// The total differences is the sum of all pixel differences normalized by image dimensions! - /// The individual distances are calculated using the Manhattan function: - /// - /// https://en.wikipedia.org/wiki/Taxicab_geometry - /// - /// ImageThresholdInPercents = 1/255 = 257/65535 means that we allow one unit difference per channel on a 1x1 image - /// ImageThresholdInPercents = 1/(100*100*255) = 257/(100*100*65535) means that we allow only one unit difference per channel on a 100x100 image - /// - /// - public float ImageThreshold { get; } - - /// - /// Gets the threshold of the individual pixels before they accumulate towards the overall difference. - /// For an individual pixel pair the value is the Manhattan distance of pixels: - /// - /// https://en.wikipedia.org/wiki/Taxicab_geometry - /// - /// - public int PerPixelManhattanThreshold { get; } - - public override ImageSimilarityReport CompareImages(Image expected, Image actual) + /// + /// + /// Gets the maximal tolerated difference represented by a value between 0.0 and 1.0 scaled to 0 and 65535. + /// Examples of percentage differences on a single pixel: + /// 1. PixelA = (65535,65535,65535,0) PixelB =(0,0,0,65535) leads to 100% difference on a single pixel + /// 2. PixelA = (65535,65535,65535,0) PixelB =(65535,65535,65535,65535) leads to 25% difference on a single pixel + /// 3. PixelA = (65535,65535,65535,0) PixelB =(32767,32767,32767,32767) leads to 50% difference on a single pixel + /// + /// + /// The total differences is the sum of all pixel differences normalized by image dimensions! + /// The individual distances are calculated using the Manhattan function: + /// + /// https://en.wikipedia.org/wiki/Taxicab_geometry + /// + /// ImageThresholdInPercents = 1/255 = 257/65535 means that we allow one unit difference per channel on a 1x1 image + /// ImageThresholdInPercents = 1/(100*100*255) = 257/(100*100*65535) means that we allow only one unit difference per channel on a 100x100 image + /// + /// + public float ImageThreshold { get; } + + /// + /// Gets the threshold of the individual pixels before they accumulate towards the overall difference. + /// For an individual pixel pair the value is the Manhattan distance of pixels: + /// + /// https://en.wikipedia.org/wiki/Taxicab_geometry + /// + /// + public int PerPixelManhattanThreshold { get; } + + public override ImageSimilarityReport CompareImages(Image expected, Image actual) + { + if (expected.Size != actual.Size) { - if (expected.Size != actual.Size) - { - throw new InvalidOperationException("Calling ImageComparer is invalid when dimensions mismatch!"); - } + throw new InvalidOperationException("Calling ImageComparer is invalid when dimensions mismatch!"); + } - int width = actual.Width; + int width = actual.Width; - // TODO: Comparing through Rgba64 may not robust enough because of the existence of super high precision pixel types. - var aBuffer = new Vector4[width]; - var bBuffer = new Vector4[width]; + // TODO: Comparing through Rgba64 may not robust enough because of the existence of super high precision pixel types. + Vector4[] aBuffer = new Vector4[width]; + Vector4[] bBuffer = new Vector4[width]; - float totalDifference = 0F; + float totalDifference = 0F; - var differences = new List(); - var configuration = new ImageSharp.Configuration(); + List differences = new(); + ImageSharp.Configuration configuration = new(); - Buffer2D expectedBuffer = expected.Frames.RootFrame.PixelBuffer; - Buffer2D actualBuffer = actual.Frames.RootFrame.PixelBuffer; + Buffer2D expectedBuffer = expected.Frames.RootFrame.PixelBuffer; + Buffer2D actualBuffer = actual.Frames.RootFrame.PixelBuffer; - for (int y = 0; y < actual.Height; y++) - { - Span aSpan = expectedBuffer.DangerousGetRowSpan(y); - Span bSpan = actualBuffer.DangerousGetRowSpan(y); + for (int y = 0; y < actual.Height; y++) + { + Span aSpan = expectedBuffer.DangerousGetRowSpan(y); + Span bSpan = actualBuffer.DangerousGetRowSpan(y); - PixelOperations.Instance.ToVector4(configuration, aSpan, aBuffer); - PixelOperations.Instance.ToVector4(configuration, bSpan, bBuffer); + PixelOperations.Instance.ToVector4(configuration, aSpan, aBuffer); + PixelOperations.Instance.ToVector4(configuration, bSpan, bBuffer); - for (int x = 0; x < width; x++) - { - float d = GetManhattanDistanceInRgbaSpace(ref aBuffer[x], ref bBuffer[x]); + for (int x = 0; x < width; x++) + { + float d = GetManhattanDistanceInRgbaSpace(ref aBuffer[x], ref bBuffer[x]); - if (d > this.PerPixelManhattanThreshold) - { - var diff = new PixelDifference(new Point(x, y), aBuffer[x], bBuffer[x]); - differences.Add(diff); + if (d > this.PerPixelManhattanThreshold) + { + PixelDifference diff = new(new Point(x, y), aBuffer[x], bBuffer[x]); + differences.Add(diff); - totalDifference += d; - } + totalDifference += d; } } + } - float normalizedDifference = totalDifference / (actual.Width * (float)actual.Height); - normalizedDifference /= 4F * 65535F; + float normalizedDifference = totalDifference / (actual.Width * (float)actual.Height); + normalizedDifference /= 4F * 65535F; - if (normalizedDifference > this.ImageThreshold) - { - return new ImageSimilarityReport(expected, actual, differences, normalizedDifference); - } - else - { - // Construct an empty generic report explicitly. - return new ImageSimilarityReport(expected, actual, [], 0f); - } + if (normalizedDifference > this.ImageThreshold) + { + return new ImageSimilarityReport(expected, actual, differences, normalizedDifference); } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static float GetManhattanDistanceInRgbaSpace(ref Vector4 a, ref Vector4 b) + else { - return Diff(a.X, b.X) + Diff(a.Y, b.Y) + Diff(a.Z, b.Z) + Diff(a.W, b.W); + // Construct an empty generic report explicitly. + return new ImageSimilarityReport(expected, actual, [], 0f); } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static float Diff(float a, float b) => MathF.Abs((a * 65535F) - (b * 65535F)); } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float GetManhattanDistanceInRgbaSpace(ref Vector4 a, ref Vector4 b) => Diff(a.X, b.X) + Diff(a.Y, b.Y) + Diff(a.Z, b.Z) + Diff(a.W, b.W); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float Diff(float a, float b) => MathF.Abs((a * 65535F) - (b * 65535F)); } diff --git a/tests/ImageSharp.Textures.Tests/TestUtilities/ImageProviders/FileProvider.cs b/tests/ImageSharp.Textures.Tests/TestUtilities/ImageProviders/FileProvider.cs index 1a163be6..5a43a22e 100644 --- a/tests/ImageSharp.Textures.Tests/TestUtilities/ImageProviders/FileProvider.cs +++ b/tests/ImageSharp.Textures.Tests/TestUtilities/ImageProviders/FileProvider.cs @@ -9,210 +9,209 @@ using Xunit.Abstractions; -namespace SixLabors.ImageSharp.Textures.Tests.TestUtilities.ImageProviders +namespace SixLabors.ImageSharp.Textures.Tests.TestUtilities.ImageProviders; + +public abstract partial class TestImageProvider : IXunitSerializable + where TPixel : unmanaged, IPixel { - public abstract partial class TestImageProvider : IXunitSerializable - where TPixel : unmanaged, IPixel + internal sealed class FileProvider : TestImageProvider, IXunitSerializable { - internal sealed class FileProvider : TestImageProvider, IXunitSerializable + // Need PixelTypes in the dictionary key, because result images of TestImageProvider.FileProvider + // are shared between PixelTypes.Color & PixelTypes.Rgba32 + private sealed class Key : IEquatable { - // Need PixelTypes in the dictionary key, because result images of TestImageProvider.FileProvider - // are shared between PixelTypes.Color & PixelTypes.Rgba32 - private sealed class Key : IEquatable + private readonly Tuple commonValues; + + private readonly Dictionary decoderParameters; + + public Key(PixelTypes pixelType, string filePath, int allocatorBufferCapacity, IImageDecoder customDecoder) + { + Type customType = customDecoder?.GetType(); + this.commonValues = new Tuple( + pixelType, + filePath, + customType, + allocatorBufferCapacity); + this.decoderParameters = GetDecoderParameters(customDecoder); + } + + private static Dictionary GetDecoderParameters(IImageDecoder customDecoder) { - private readonly Tuple commonValues; + Type type = customDecoder.GetType(); - private readonly Dictionary decoderParameters; + Dictionary data = new(); - public Key(PixelTypes pixelType, string filePath, int allocatorBufferCapacity, IImageDecoder customDecoder) + while (type != null && type != typeof(object)) { - Type customType = customDecoder?.GetType(); - this.commonValues = new Tuple( - pixelType, - filePath, - customType, - allocatorBufferCapacity); - this.decoderParameters = GetDecoderParameters(customDecoder); + PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance); + foreach (PropertyInfo p in properties) + { + string key = string.Format(CultureInfo.InvariantCulture, "{0}.{1}", type.FullName, p.Name); + object value = p.GetValue(customDecoder); + data[key] = value; + } + + type = type.GetTypeInfo().BaseType; } - private static Dictionary GetDecoderParameters(IImageDecoder customDecoder) + return data; + } + + public bool Equals(Key other) + { + if (other is null) { - Type type = customDecoder.GetType(); + return false; + } - var data = new Dictionary(); + if (ReferenceEquals(this, other)) + { + return true; + } - while (type != null && type != typeof(object)) - { - PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance); - foreach (PropertyInfo p in properties) - { - string key = string.Format(CultureInfo.InvariantCulture, "{0}.{1}", type.FullName, p.Name); - object value = p.GetValue(customDecoder); - data[key] = value; - } - - type = type.GetTypeInfo().BaseType; - } + if (!this.commonValues.Equals(other.commonValues)) + { + return false; + } - return data; + if (this.decoderParameters.Count != other.decoderParameters.Count) + { + return false; } - public bool Equals(Key other) + foreach (KeyValuePair kv in this.decoderParameters) { - if (other is null) + if (!other.decoderParameters.TryGetValue(kv.Key, out object otherVal)) { return false; } - if (ReferenceEquals(this, other)) - { - return true; - } - - if (!this.commonValues.Equals(other.commonValues)) + if (!object.Equals(kv.Value, otherVal)) { return false; } + } - if (this.decoderParameters.Count != other.decoderParameters.Count) - { - return false; - } + return true; + } - foreach (KeyValuePair kv in this.decoderParameters) - { - if (!other.decoderParameters.TryGetValue(kv.Key, out object otherVal)) - { - return false; - } - - if (!object.Equals(kv.Value, otherVal)) - { - return false; - } - } + public override bool Equals(object obj) + { + if (obj is null) + { + return false; + } + if (ReferenceEquals(this, obj)) + { return true; } - public override bool Equals(object obj) + if (obj.GetType() != this.GetType()) { - if (obj is null) - { - return false; - } + return false; + } - if (ReferenceEquals(this, obj)) - { - return true; - } + return this.Equals((Key)obj); + } - if (obj.GetType() != this.GetType()) - { - return false; - } + public override int GetHashCode() => this.commonValues.GetHashCode(); - return this.Equals((Key)obj); - } + public static bool operator ==(Key left, Key right) => Equals(left, right); - public override int GetHashCode() => this.commonValues.GetHashCode(); + public static bool operator !=(Key left, Key right) => !Equals(left, right); + } - public static bool operator ==(Key left, Key right) => Equals(left, right); + private static readonly ConcurrentDictionary> Cache = new(); - public static bool operator !=(Key left, Key right) => !Equals(left, right); - } + // Needed for deserialization! + // ReSharper disable once UnusedMember.Local + public FileProvider() + { + } - private static readonly ConcurrentDictionary> Cache = new ConcurrentDictionary>(); + public FileProvider(string filePath) => this.FilePath = filePath; - // Needed for deserialization! - // ReSharper disable once UnusedMember.Local - public FileProvider() - { - } + /// + /// Gets the file path relative to the "~/tests/images" folder + /// + public string FilePath { get; private set; } - public FileProvider(string filePath) => this.FilePath = filePath; + public override string SourceFileOrDescription => this.FilePath; - /// - /// Gets the file path relative to the "~/tests/images" folder - /// - public string FilePath { get; private set; } + public override Image GetImage() + { + IImageFormat format = TestEnvironment.GetImageFormat(this.FilePath); + IImageDecoder decoder = TestEnvironment.GetReferenceDecoder(this.FilePath); + return this.GetImage(format, decoder); + } - public override string SourceFileOrDescription => this.FilePath; + public override Image GetImage(IImageFormat format, IImageDecoder decoder) + { + Guard.NotNull(format, nameof(format)); + Guard.NotNull(decoder, nameof(decoder)); - public override Image GetImage() + if (!TestEnvironment.Is64BitProcess) { - IImageFormat format = TestEnvironment.GetImageFormat(this.FilePath); - IImageDecoder decoder = TestEnvironment.GetReferenceDecoder(this.FilePath); - return this.GetImage(format, decoder); + return this.LoadImage(format, decoder); } - public override Image GetImage(IImageFormat format, IImageDecoder decoder) - { - Guard.NotNull(format, nameof(format)); - Guard.NotNull(decoder, nameof(decoder)); - - if (!TestEnvironment.Is64BitProcess) - { - return this.LoadImage(format, decoder); - } + // int bufferCapacity = this.Configuration.MemoryAllocator.GetBufferCapacityInBytes(); + int bufferCapacity = 500; + Key key = new(this.PixelType, this.FilePath, bufferCapacity, decoder); - // int bufferCapacity = this.Configuration.MemoryAllocator.GetBufferCapacityInBytes(); - int bufferCapacity = 500; - var key = new Key(this.PixelType, this.FilePath, bufferCapacity, decoder); + Image cachedImage = Cache.GetOrAdd(key, _ => this.LoadImage(format, decoder)); - Image cachedImage = Cache.GetOrAdd(key, _ => this.LoadImage(format, decoder)); - - return cachedImage.Clone(this.Configuration); - } + return cachedImage.Clone(this.Configuration); + } - public override Task> GetImageAsync(IImageFormat format, IImageDecoder decoder) + public override Task> GetImageAsync(IImageFormat format, IImageDecoder decoder) + { + Guard.NotNull(format, nameof(format)); + Guard.NotNull(decoder, nameof(decoder)); + + // Used in small subset of decoder tests, no caching. + string path = Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.FilePath); + ImageSharp.Configuration configuration = this.Configuration.Clone(); + configuration.ImageFormatsManager.SetDecoder(format, decoder); + DecoderOptions options = new() { - Guard.NotNull(format, nameof(format)); - Guard.NotNull(decoder, nameof(decoder)); - - // Used in small subset of decoder tests, no caching. - string path = Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.FilePath); - ImageSharp.Configuration configuration = this.Configuration.Clone(); - configuration.ImageFormatsManager.SetDecoder(format, decoder); - DecoderOptions options = new() - { - Configuration = configuration - }; + Configuration = configuration + }; - return Image.LoadAsync(options, path); - } + return Image.LoadAsync(options, path); + } - public override void Deserialize(IXunitSerializationInfo info) - { - this.FilePath = info.GetValue("path"); + public override void Deserialize(IXunitSerializationInfo info) + { + this.FilePath = info.GetValue("path"); - base.Deserialize(info); // must be called last - } + base.Deserialize(info); // must be called last + } - public override void Serialize(IXunitSerializationInfo info) - { - base.Serialize(info); - info.AddValue("path", this.FilePath); - } + public override void Serialize(IXunitSerializationInfo info) + { + base.Serialize(info); + info.AddValue("path", this.FilePath); + } - private Image LoadImage(IImageFormat format, IImageDecoder decoder) + private Image LoadImage(IImageFormat format, IImageDecoder decoder) + { + TestFile testFile = TestFile.Create(this.FilePath); + ImageSharp.Configuration configuration = this.Configuration.Clone(); + configuration.ImageFormatsManager.SetDecoder(format, decoder); + DecoderOptions options = new() { - TestFile testFile = TestFile.Create(this.FilePath); - ImageSharp.Configuration configuration = this.Configuration.Clone(); - configuration.ImageFormatsManager.SetDecoder(format, decoder); - DecoderOptions options = new() - { - Configuration = configuration - }; + Configuration = configuration + }; - return Image.Load(options, testFile.Bytes); - } + return Image.Load(options, testFile.Bytes); } + } - public static string GetFilePathOrNull(ITestImageProvider provider) - { - var fileProvider = provider as FileProvider; - return fileProvider?.FilePath; - } + public static string GetFilePathOrNull(ITestImageProvider provider) + { + FileProvider fileProvider = provider as FileProvider; + return fileProvider?.FilePath; } } diff --git a/tests/ImageSharp.Textures.Tests/TestUtilities/ImageProviders/ITestImageProvider.cs b/tests/ImageSharp.Textures.Tests/TestUtilities/ImageProviders/ITestImageProvider.cs index 5ff70bdf..0b5cba1c 100644 --- a/tests/ImageSharp.Textures.Tests/TestUtilities/ImageProviders/ITestImageProvider.cs +++ b/tests/ImageSharp.Textures.Tests/TestUtilities/ImageProviders/ITestImageProvider.cs @@ -1,16 +1,15 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.Tests.TestUtilities.ImageProviders +namespace SixLabors.ImageSharp.Textures.Tests.TestUtilities.ImageProviders; + +public interface ITestImageProvider { - public interface ITestImageProvider - { - PixelTypes PixelType { get; } + PixelTypes PixelType { get; } - ImagingTestCaseUtility Utility { get; } + ImagingTestCaseUtility Utility { get; } - string SourceFileOrDescription { get; } + string SourceFileOrDescription { get; } - ImageSharp.Configuration Configuration { get; set; } - } + ImageSharp.Configuration Configuration { get; set; } } diff --git a/tests/ImageSharp.Textures.Tests/TestUtilities/ImageProviders/TestImageProvider.cs b/tests/ImageSharp.Textures.Tests/TestUtilities/ImageProviders/TestImageProvider.cs index 490cc22b..e579794b 100644 --- a/tests/ImageSharp.Textures.Tests/TestUtilities/ImageProviders/TestImageProvider.cs +++ b/tests/ImageSharp.Textures.Tests/TestUtilities/ImageProviders/TestImageProvider.cs @@ -1,10 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Globalization; using System.Reflection; -using System.Threading.Tasks; using Castle.Core.Internal; using SixLabors.ImageSharp.Formats; @@ -13,117 +11,116 @@ using SixLabors.ImageSharp.Textures.Tests.TestUtilities.Attributes; using Xunit.Abstractions; -namespace SixLabors.ImageSharp.Textures.Tests.TestUtilities.ImageProviders +namespace SixLabors.ImageSharp.Textures.Tests.TestUtilities.ImageProviders; + +/// +/// Provides instances for parametric unit tests. +/// +/// The pixel format of the image. +public abstract partial class TestImageProvider : ITestImageProvider, IXunitSerializable + where TPixel : unmanaged, IPixel { + public PixelTypes PixelType { get; private set; } = typeof(TPixel).GetPixelType(); + + public virtual string SourceFileOrDescription => string.Empty; + + public ImageSharp.Configuration Configuration { get; set; } = ImageSharp.Configuration.Default; + /// - /// Provides instances for parametric unit tests. + /// Gets the utility instance to provide information about the test image & manage input/output. /// - /// The pixel format of the image. - public abstract partial class TestImageProvider : ITestImageProvider, IXunitSerializable - where TPixel : unmanaged, IPixel - { - public PixelTypes PixelType { get; private set; } = typeof(TPixel).GetPixelType(); + public ImagingTestCaseUtility Utility { get; private set; } + + public string TypeName { get; private set; } - public virtual string SourceFileOrDescription => string.Empty; + public string MethodName { get; private set; } - public ImageSharp.Configuration Configuration { get; set; } = ImageSharp.Configuration.Default; + public string OutputSubfolderName { get; private set; } - /// - /// Gets the utility instance to provide information about the test image & manage input/output. - /// - public ImagingTestCaseUtility Utility { get; private set; } + /// + /// Returns a file backed provider. + /// + public static TestImageProvider File( + string filePath, + MethodInfo testMethod = null, + PixelTypes pixelTypeOverride = PixelTypes.Undefined) => new FileProvider(filePath).Init(testMethod, pixelTypeOverride); - public string TypeName { get; private set; } + /// + /// Returns an instance to the test case with the necessary traits. + /// + /// A test image. + public abstract Image GetImage(); - public string MethodName { get; private set; } + public virtual Image GetImage(IImageFormat format, IImageDecoder decoder) + => throw new NotSupportedException(string.Format(CultureInfo.InvariantCulture, "Decoder specific GetImage() is not supported with {0}!", this.GetType().Name)); - public string OutputSubfolderName { get; private set; } + public virtual Task> GetImageAsync(IImageFormat format, IImageDecoder decoder) + => throw new NotSupportedException(string.Format(CultureInfo.InvariantCulture, "Decoder specific GetImageAsync() is not supported with {0}!", this.GetType().Name)); - /// - /// Returns a file backed provider. - /// - public static TestImageProvider File( - string filePath, - MethodInfo testMethod = null, - PixelTypes pixelTypeOverride = PixelTypes.Undefined) => new FileProvider(filePath).Init(testMethod, pixelTypeOverride); + /// + /// Returns an instance to the test case with the necessary traits. + /// + /// A test image. + public Image GetImage(Action operationsToApply) + { + Image img = this.GetImage(); + img.Mutate(operationsToApply); + return img; + } - /// - /// Returns an instance to the test case with the necessary traits. - /// - /// A test image. - public abstract Image GetImage(); + public virtual void Deserialize(IXunitSerializationInfo info) + { + PixelTypes pixelType = info.GetValue("PixelType"); + string typeName = info.GetValue("TypeName"); + string methodName = info.GetValue("MethodName"); + string outputSubfolderName = info.GetValue("OutputSubfolderName"); - public virtual Image GetImage(IImageFormat format, IImageDecoder decoder) - => throw new NotSupportedException(string.Format(CultureInfo.InvariantCulture, "Decoder specific GetImage() is not supported with {0}!", this.GetType().Name)); + this.Init(typeName, methodName, outputSubfolderName, pixelType); + } - public virtual Task> GetImageAsync(IImageFormat format, IImageDecoder decoder) - => throw new NotSupportedException(string.Format(CultureInfo.InvariantCulture, "Decoder specific GetImageAsync() is not supported with {0}!", this.GetType().Name)); + public virtual void Serialize(IXunitSerializationInfo info) + { + info.AddValue("PixelType", this.PixelType); + info.AddValue("TypeName", this.TypeName); + info.AddValue("MethodName", this.MethodName); + info.AddValue("OutputSubfolderName", this.OutputSubfolderName); + } - /// - /// Returns an instance to the test case with the necessary traits. - /// - /// A test image. - public Image GetImage(Action operationsToApply) + protected TestImageProvider Init( + string typeName, + string methodName, + string outputSubfolderName, + PixelTypes pixelTypeOverride) + { + if (pixelTypeOverride != PixelTypes.Undefined) { - Image img = this.GetImage(); - img.Mutate(operationsToApply); - return img; + this.PixelType = pixelTypeOverride; } - public virtual void Deserialize(IXunitSerializationInfo info) - { - PixelTypes pixelType = info.GetValue("PixelType"); - string typeName = info.GetValue("TypeName"); - string methodName = info.GetValue("MethodName"); - string outputSubfolderName = info.GetValue("OutputSubfolderName"); - - this.Init(typeName, methodName, outputSubfolderName, pixelType); - } + this.TypeName = typeName; + this.MethodName = methodName; + this.OutputSubfolderName = outputSubfolderName; - public virtual void Serialize(IXunitSerializationInfo info) + this.Utility = new ImagingTestCaseUtility { - info.AddValue("PixelType", this.PixelType); - info.AddValue("TypeName", this.TypeName); - info.AddValue("MethodName", this.MethodName); - info.AddValue("OutputSubfolderName", this.OutputSubfolderName); - } + SourceFileOrDescription = this.SourceFileOrDescription, + PixelTypeName = this.PixelType.ToString() + }; - protected TestImageProvider Init( - string typeName, - string methodName, - string outputSubfolderName, - PixelTypes pixelTypeOverride) + if (methodName != null) { - if (pixelTypeOverride != PixelTypes.Undefined) - { - this.PixelType = pixelTypeOverride; - } - - this.TypeName = typeName; - this.MethodName = methodName; - this.OutputSubfolderName = outputSubfolderName; - - this.Utility = new ImagingTestCaseUtility - { - SourceFileOrDescription = this.SourceFileOrDescription, - PixelTypeName = this.PixelType.ToString() - }; - - if (methodName != null) - { - this.Utility.Init(typeName, methodName, outputSubfolderName); - } - - return this; + this.Utility.Init(typeName, methodName, outputSubfolderName); } - protected TestImageProvider Init(MethodInfo testMethod, PixelTypes pixelTypeOverride) - { - string subfolder = testMethod?.DeclaringType.GetAttribute()?.Subfolder - ?? string.Empty; - return this.Init(testMethod?.DeclaringType.Name, testMethod?.Name, subfolder, pixelTypeOverride); - } + return this; + } - public override string ToString() => string.Format(CultureInfo.InvariantCulture, "{0}[{1}]", this.SourceFileOrDescription, this.PixelType); + protected TestImageProvider Init(MethodInfo testMethod, PixelTypes pixelTypeOverride) + { + string subfolder = testMethod?.DeclaringType.GetAttribute()?.Subfolder + ?? string.Empty; + return this.Init(testMethod?.DeclaringType.Name, testMethod?.Name, subfolder, pixelTypeOverride); } + + public override string ToString() => string.Format(CultureInfo.InvariantCulture, "{0}[{1}]", this.SourceFileOrDescription, this.PixelType); } diff --git a/tests/ImageSharp.Textures.Tests/TestUtilities/ImagingTestCaseUtility.cs b/tests/ImageSharp.Textures.Tests/TestUtilities/ImagingTestCaseUtility.cs index e143076e..2ee4a638 100644 --- a/tests/ImageSharp.Textures.Tests/TestUtilities/ImagingTestCaseUtility.cs +++ b/tests/ImageSharp.Textures.Tests/TestUtilities/ImagingTestCaseUtility.cs @@ -1,322 +1,317 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; -using System.Collections.Generic; using System.Globalization; -using System.IO; -using System.Linq; using System.Reflection; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Textures.Tests.TestUtilities +namespace SixLabors.ImageSharp.Textures.Tests.TestUtilities; + +/// +/// Utility class to provide information about the test image & the test case for the test code, +/// and help managing IO. +/// +public class ImagingTestCaseUtility { /// - /// Utility class to provide information about the test image & the test case for the test code, - /// and help managing IO. + /// Gets or sets the name of the TPixel in the owner. /// - public class ImagingTestCaseUtility - { - /// - /// Gets or sets the name of the TPixel in the owner. - /// - public string PixelTypeName { get; set; } = string.Empty; - - /// - /// Gets or sets the name of the file which is provided by. - /// Or a short string describing the image in the case of a non-file based image provider. - /// - public string SourceFileOrDescription { get; set; } = string.Empty; - - /// - /// Gets or sets the test group name. - /// By default this is the name of the test class, but it's possible to change it. - /// - public string TestGroupName { get; set; } = string.Empty; - - public string OutputSubfolderName { get; set; } = string.Empty; - - /// - /// Gets or sets the name of the test case (by default). - /// - public string TestName { get; set; } = string.Empty; - - private string GetTestOutputFileNameImpl( - string extension, - string details, - bool appendPixelTypeToFileName, - bool appendSourceFileOrDescription) - { - if (string.IsNullOrWhiteSpace(extension)) - { - extension = null; - } + public string PixelTypeName { get; set; } = string.Empty; - string fn = appendSourceFileOrDescription - ? Path.GetFileNameWithoutExtension(this.SourceFileOrDescription) - : string.Empty; + /// + /// Gets or sets the name of the file which is provided by. + /// Or a short string describing the image in the case of a non-file based image provider. + /// + public string SourceFileOrDescription { get; set; } = string.Empty; - if (string.IsNullOrWhiteSpace(extension)) - { - extension = Path.GetExtension(this.SourceFileOrDescription); - } + /// + /// Gets or sets the test group name. + /// By default this is the name of the test class, but it's possible to change it. + /// + public string TestGroupName { get; set; } = string.Empty; - if (string.IsNullOrWhiteSpace(extension)) - { - extension = ".bmp"; - } + public string OutputSubfolderName { get; set; } = string.Empty; - extension = extension.ToLowerInvariant(); + /// + /// Gets or sets the name of the test case (by default). + /// + public string TestName { get; set; } = string.Empty; - if (extension[0] != '.') - { - extension = '.' + extension; - } + private string GetTestOutputFileNameImpl( + string extension, + string details, + bool appendPixelTypeToFileName, + bool appendSourceFileOrDescription) + { + if (string.IsNullOrWhiteSpace(extension)) + { + extension = null; + } - if (fn != string.Empty) - { - fn = '_' + fn; - } + string fn = appendSourceFileOrDescription + ? Path.GetFileNameWithoutExtension(this.SourceFileOrDescription) + : string.Empty; - string pixName = string.Empty; + if (string.IsNullOrWhiteSpace(extension)) + { + extension = Path.GetExtension(this.SourceFileOrDescription); + } - if (appendPixelTypeToFileName) - { - pixName = this.PixelTypeName; + if (string.IsNullOrWhiteSpace(extension)) + { + extension = ".bmp"; + } - if (pixName != string.Empty) - { - pixName = '_' + pixName; - } - } + extension = extension.ToLowerInvariant(); - details ??= string.Empty; - if (details != string.Empty) - { - details = '_' + details; - } + if (extension[0] != '.') + { + extension = '.' + extension; + } - return string.Format( - CultureInfo.InvariantCulture, - "{0}{1}{2}{3}{4}{5}{6}", - this.GetTestOutputDir(), - Path.DirectorySeparatorChar, - this.TestName, - pixName, - fn, - details, - extension); + if (fn != string.Empty) + { + fn = '_' + fn; } - /// - /// Gets the recommended file name for the output of the test - /// - /// The required extension - /// The settings modifying the output path - /// A boolean indicating whether to append the pixel type to output file name. - /// A boolean indicating whether to append SourceFileOrDescription to the test output file name. - /// The file test name - public string GetTestOutputFileName( - string extension = null, - object testOutputDetails = null, - bool appendPixelTypeToFileName = true, - bool appendSourceFileOrDescription = true) + string pixName = string.Empty; + + if (appendPixelTypeToFileName) { - string detailsString = null; + pixName = this.PixelTypeName; - if (testOutputDetails is FormattableString fs) - { - detailsString = FormattableString.Invariant(fs); - } - else if (testOutputDetails is string s) - { - detailsString = s; - } - else if (testOutputDetails != null) + if (pixName != string.Empty) { - Type type = testOutputDetails.GetType(); - TypeInfo info = type.GetTypeInfo(); - if (info.IsPrimitive || info.IsEnum || type == typeof(decimal)) - { - detailsString = string.Format(CultureInfo.InvariantCulture, "{0}", testOutputDetails); - } - else - { - IEnumerable properties = testOutputDetails.GetType().GetRuntimeProperties(); - - detailsString = string.Join( - "_", - properties.ToDictionary(x => x.Name, x => x.GetValue(testOutputDetails)) - .Select(x => string.Format(CultureInfo.InvariantCulture, "{0}-{1}", x.Key, x.Value))); - } + pixName = '_' + pixName; } - - return this.GetTestOutputFileNameImpl( - extension, - detailsString, - appendPixelTypeToFileName, - appendSourceFileOrDescription); } - /// - /// Encodes image by the format matching the required extension, than saves it to the recommended output file. - /// - /// The image instance. - /// The requested extension. - /// Optional encoder. - /// Additional information to append to the test output file name. - /// A value indicating whether to append the pixel type to the test output file name. - /// A boolean indicating whether to append SourceFileOrDescription to the test output file name. - /// The path to the saved image file. - public string SaveTestOutputFile( - Image image, - string extension = null, - IImageEncoder encoder = null, - object testOutputDetails = null, - bool appendPixelTypeToFileName = true, - bool appendSourceFileOrDescription = true) + details ??= string.Empty; + if (details != string.Empty) { - string path = this.GetTestOutputFileName( - extension, - testOutputDetails, - appendPixelTypeToFileName, - appendSourceFileOrDescription); + details = '_' + details; + } - encoder ??= TestEnvironment.GetReferenceEncoder(path); + return string.Format( + CultureInfo.InvariantCulture, + "{0}{1}{2}{3}{4}{5}{6}", + this.GetTestOutputDir(), + Path.DirectorySeparatorChar, + this.TestName, + pixName, + fn, + details, + extension); + } - using (FileStream stream = File.OpenWrite(path)) - { - image.Save(stream, encoder); - } + /// + /// Gets the recommended file name for the output of the test + /// + /// The required extension + /// The settings modifying the output path + /// A boolean indicating whether to append the pixel type to output file name. + /// A boolean indicating whether to append SourceFileOrDescription to the test output file name. + /// The file test name + public string GetTestOutputFileName( + string extension = null, + object testOutputDetails = null, + bool appendPixelTypeToFileName = true, + bool appendSourceFileOrDescription = true) + { + string detailsString = null; - return path; + if (testOutputDetails is FormattableString fs) + { + detailsString = FormattableString.Invariant(fs); } - - public IEnumerable GetTestOutputFileNamesMultiFrame( - int frameCount, - string extension = null, - object testOutputDetails = null, - bool appendPixelTypeToFileName = true, - bool appendSourceFileOrDescription = true) + else if (testOutputDetails is string s) { - string baseDir = this.GetTestOutputFileName(string.Empty, testOutputDetails, appendPixelTypeToFileName, appendSourceFileOrDescription); - - if (!Directory.Exists(baseDir)) + detailsString = s; + } + else if (testOutputDetails != null) + { + Type type = testOutputDetails.GetType(); + TypeInfo info = type.GetTypeInfo(); + if (info.IsPrimitive || info.IsEnum || type == typeof(decimal)) { - Directory.CreateDirectory(baseDir); + detailsString = string.Format(CultureInfo.InvariantCulture, "{0}", testOutputDetails); } - - for (int i = 0; i < frameCount; i++) + else { - string filePath = string.Format(CultureInfo.InvariantCulture, "{0}/{1:D2}.{2}", baseDir, i, extension); - yield return filePath; + IEnumerable properties = testOutputDetails.GetType().GetRuntimeProperties(); + + detailsString = string.Join( + "_", + properties.ToDictionary(x => x.Name, x => x.GetValue(testOutputDetails)) + .Select(x => string.Format(CultureInfo.InvariantCulture, "{0}-{1}", x.Key, x.Value))); } } - public string[] SaveTestOutputFileMultiFrame( - Image image, - string extension = "png", - IImageEncoder encoder = null, - object testOutputDetails = null, - bool appendPixelTypeToFileName = true) - where TPixel : unmanaged, IPixel - { - encoder ??= TestEnvironment.GetReferenceEncoder(string.Format(CultureInfo.InvariantCulture, "foo.{0}", extension)); + return this.GetTestOutputFileNameImpl( + extension, + detailsString, + appendPixelTypeToFileName, + appendSourceFileOrDescription); + } - string[] files = this.GetTestOutputFileNamesMultiFrame( - image.Frames.Count, - extension, - testOutputDetails, - appendPixelTypeToFileName).ToArray(); + /// + /// Encodes image by the format matching the required extension, than saves it to the recommended output file. + /// + /// The image instance. + /// The requested extension. + /// Optional encoder. + /// Additional information to append to the test output file name. + /// A value indicating whether to append the pixel type to the test output file name. + /// A boolean indicating whether to append SourceFileOrDescription to the test output file name. + /// The path to the saved image file. + public string SaveTestOutputFile( + Image image, + string extension = null, + IImageEncoder encoder = null, + object testOutputDetails = null, + bool appendPixelTypeToFileName = true, + bool appendSourceFileOrDescription = true) + { + string path = this.GetTestOutputFileName( + extension, + testOutputDetails, + appendPixelTypeToFileName, + appendSourceFileOrDescription); - for (int i = 0; i < image.Frames.Count; i++) - { - using Image frameImage = image.Frames.CloneFrame(i); - string filePath = files[i]; - using FileStream stream = File.OpenWrite(filePath); - frameImage.Save(stream, encoder); - } + encoder ??= TestEnvironment.GetReferenceEncoder(path); - return files; + using (FileStream stream = File.OpenWrite(path)) + { + image.Save(stream, encoder); } - internal string GetReferenceOutputFileName( - string extension, - object testOutputDetails, - bool appendPixelTypeToFileName, - bool appendSourceFileOrDescription) => - TestEnvironment.GetReferenceOutputFileName( - this.GetTestOutputFileName(extension, testOutputDetails, appendPixelTypeToFileName, appendSourceFileOrDescription)); + return path; + } + + public IEnumerable GetTestOutputFileNamesMultiFrame( + int frameCount, + string extension = null, + object testOutputDetails = null, + bool appendPixelTypeToFileName = true, + bool appendSourceFileOrDescription = true) + { + string baseDir = this.GetTestOutputFileName(string.Empty, testOutputDetails, appendPixelTypeToFileName, appendSourceFileOrDescription); - internal void Init(string typeName, string methodName, string outputSubfolderName) + if (!Directory.Exists(baseDir)) { - this.TestGroupName = typeName; - this.TestName = methodName; - this.OutputSubfolderName = outputSubfolderName; + Directory.CreateDirectory(baseDir); } - internal string GetTestOutputDir() + for (int i = 0; i < frameCount; i++) { - string testGroupName = Path.GetFileNameWithoutExtension(this.TestGroupName); + string filePath = string.Format(CultureInfo.InvariantCulture, "{0}/{1:D2}.{2}", baseDir, i, extension); + yield return filePath; + } + } - if (!string.IsNullOrEmpty(this.OutputSubfolderName)) - { - testGroupName = Path.Combine(this.OutputSubfolderName, testGroupName); - } + public string[] SaveTestOutputFileMultiFrame( + Image image, + string extension = "png", + IImageEncoder encoder = null, + object testOutputDetails = null, + bool appendPixelTypeToFileName = true) + where TPixel : unmanaged, IPixel + { + encoder ??= TestEnvironment.GetReferenceEncoder(string.Format(CultureInfo.InvariantCulture, "foo.{0}", extension)); - return TestEnvironment.CreateOutputDirectory(testGroupName); + string[] files = [.. this.GetTestOutputFileNamesMultiFrame( + image.Frames.Count, + extension, + testOutputDetails, + appendPixelTypeToFileName)]; + + for (int i = 0; i < image.Frames.Count; i++) + { + using Image frameImage = image.Frames.CloneFrame(i); + string filePath = files[i]; + using FileStream stream = File.OpenWrite(filePath); + frameImage.Save(stream, encoder); } - public static void ModifyPixel(Image img, int x, int y, byte perChannelChange) - where TPixel : unmanaged, IPixel => ModifyPixel(img.Frames.RootFrame, x, y, perChannelChange); + return files; + } - public static void ModifyPixel(ImageFrame img, int x, int y, byte perChannelChange) - where TPixel : unmanaged, IPixel + internal string GetReferenceOutputFileName( + string extension, + object testOutputDetails, + bool appendPixelTypeToFileName, + bool appendSourceFileOrDescription) => + TestEnvironment.GetReferenceOutputFileName( + this.GetTestOutputFileName(extension, testOutputDetails, appendPixelTypeToFileName, appendSourceFileOrDescription)); + + internal void Init(string typeName, string methodName, string outputSubfolderName) + { + this.TestGroupName = typeName; + this.TestName = methodName; + this.OutputSubfolderName = outputSubfolderName; + } + + internal string GetTestOutputDir() + { + string testGroupName = Path.GetFileNameWithoutExtension(this.TestGroupName); + + if (!string.IsNullOrEmpty(this.OutputSubfolderName)) { - TPixel pixel = img[x, y]; - Rgba64 rgbaPixel = default; - rgbaPixel.FromScaledVector4(pixel.ToScaledVector4()); - ushort change = (ushort)Math.Round(perChannelChange / 255F * 65535F); + testGroupName = Path.Combine(this.OutputSubfolderName, testGroupName); + } - if (rgbaPixel.R + perChannelChange <= 255) - { - rgbaPixel.R += change; - } - else - { - rgbaPixel.R -= change; - } + return TestEnvironment.CreateOutputDirectory(testGroupName); + } - if (rgbaPixel.G + perChannelChange <= 255) - { - rgbaPixel.G += change; - } - else - { - rgbaPixel.G -= change; - } + public static void ModifyPixel(Image img, int x, int y, byte perChannelChange) + where TPixel : unmanaged, IPixel => ModifyPixel(img.Frames.RootFrame, x, y, perChannelChange); - if (rgbaPixel.B + perChannelChange <= 255) - { - rgbaPixel.B += perChannelChange; - } - else - { - rgbaPixel.B -= perChannelChange; - } + public static void ModifyPixel(ImageFrame img, int x, int y, byte perChannelChange) + where TPixel : unmanaged, IPixel + { + TPixel pixel = img[x, y]; + Rgba64 rgbaPixel = default; + rgbaPixel.FromScaledVector4(pixel.ToScaledVector4()); + ushort change = (ushort)Math.Round(perChannelChange / 255F * 65535F); - if (rgbaPixel.A + perChannelChange <= 255) - { - rgbaPixel.A += perChannelChange; - } - else - { - rgbaPixel.A -= perChannelChange; - } + if (rgbaPixel.R + perChannelChange <= 255) + { + rgbaPixel.R += change; + } + else + { + rgbaPixel.R -= change; + } + + if (rgbaPixel.G + perChannelChange <= 255) + { + rgbaPixel.G += change; + } + else + { + rgbaPixel.G -= change; + } - pixel.FromRgba64(rgbaPixel); - img[x, y] = pixel; + if (rgbaPixel.B + perChannelChange <= 255) + { + rgbaPixel.B += perChannelChange; } + else + { + rgbaPixel.B -= perChannelChange; + } + + if (rgbaPixel.A + perChannelChange <= 255) + { + rgbaPixel.A += perChannelChange; + } + else + { + rgbaPixel.A -= perChannelChange; + } + + pixel.FromRgba64(rgbaPixel); + img[x, y] = pixel; } } diff --git a/tests/ImageSharp.Textures.Tests/TestUtilities/PixelTypes.cs b/tests/ImageSharp.Textures.Tests/TestUtilities/PixelTypes.cs index 6499cf5f..3e9b9fc1 100644 --- a/tests/ImageSharp.Textures.Tests/TestUtilities/PixelTypes.cs +++ b/tests/ImageSharp.Textures.Tests/TestUtilities/PixelTypes.cs @@ -1,79 +1,76 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; - -namespace SixLabors.ImageSharp.Textures.Tests.TestUtilities +namespace SixLabors.ImageSharp.Textures.Tests.TestUtilities; + +/// +/// Flags that are mapped to PackedPixel types. +/// They trigger the desired parametrization for . +/// +[Flags] +public enum PixelTypes { - /// - /// Flags that are mapped to PackedPixel types. - /// They trigger the desired parametrization for . - /// - [Flags] - public enum PixelTypes - { #pragma warning disable SA1602 // Enumeration items should be documented - Undefined = 0, + Undefined = 0, - A8 = 1 << 0, + A8 = 1 << 0, - Argb32 = 1 << 1, + Argb32 = 1 << 1, - Bgr565 = 1 << 2, + Bgr565 = 1 << 2, - Bgra4444 = 1 << 3, + Bgra4444 = 1 << 3, - Byte4 = 1 << 4, + Byte4 = 1 << 4, - HalfSingle = 1 << 5, + HalfSingle = 1 << 5, - HalfVector2 = 1 << 6, + HalfVector2 = 1 << 6, - HalfVector4 = 1 << 7, + HalfVector4 = 1 << 7, - NormalizedByte2 = 1 << 8, + NormalizedByte2 = 1 << 8, - NormalizedByte4 = 1 << 9, + NormalizedByte4 = 1 << 9, - NormalizedShort4 = 1 << 10, + NormalizedShort4 = 1 << 10, - Rg32 = 1 << 11, + Rg32 = 1 << 11, - Rgba1010102 = 1 << 12, + Rgba1010102 = 1 << 12, - Rgba32 = 1 << 13, + Rgba32 = 1 << 13, - Rgba64 = 1 << 14, + Rgba64 = 1 << 14, - RgbaVector = 1 << 15, + RgbaVector = 1 << 15, - Short2 = 1 << 16, + Short2 = 1 << 16, - Short4 = 1 << 17, + Short4 = 1 << 17, - Rgb24 = 1 << 18, + Rgb24 = 1 << 18, - Bgr24 = 1 << 19, + Bgr24 = 1 << 19, - Bgra32 = 1 << 20, + Bgra32 = 1 << 20, - Rgb48 = 1 << 21, + Rgb48 = 1 << 21, - Bgra5551 = 1 << 22, + Bgra5551 = 1 << 22, - L8 = 1 << 23, + L8 = 1 << 23, - L16 = 1 << 24, + L16 = 1 << 24, - La16 = 1 << 25, + La16 = 1 << 25, - La32 = 1 << 26, + La32 = 1 << 26, - // TODO: Add multi-flag entries by rules defined in PackedPixelConverterHelper + // TODO: Add multi-flag entries by rules defined in PackedPixelConverterHelper - // "All" is handled as a separate, individual case instead of using bitwise OR - All = 30 + // "All" is handled as a separate, individual case instead of using bitwise OR + All = 30 #pragma warning restore SA1602 // Enumeration items should be documented - } } diff --git a/tests/ImageSharp.Textures.Tests/TestUtilities/TestEnvironment.cs b/tests/ImageSharp.Textures.Tests/TestUtilities/TestEnvironment.cs index bc01a8be..5fc33ebb 100644 --- a/tests/ImageSharp.Textures.Tests/TestUtilities/TestEnvironment.cs +++ b/tests/ImageSharp.Textures.Tests/TestUtilities/TestEnvironment.cs @@ -1,333 +1,329 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; using System.Diagnostics; -using System.IO; -using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Png; -namespace SixLabors.ImageSharp.Textures.Tests.TestUtilities +namespace SixLabors.ImageSharp.Textures.Tests.TestUtilities; + +public static class TestEnvironment { - public static class TestEnvironment - { - private static readonly Lazy ConfigurationLazy = new Lazy(CreateDefaultConfiguration); + private static readonly Lazy ConfigurationLazy = new(CreateDefaultConfiguration); - internal static ImageSharp.Configuration Configuration => ConfigurationLazy.Value; + internal static ImageSharp.Configuration Configuration => ConfigurationLazy.Value; - private const string ImageSharpTexturesSolutionFileName = "ImageSharp.Textures.sln"; + private const string ImageSharpTexturesSolutionFileName = "ImageSharp.Textures.sln"; - private const string InputImagesRelativePath = @"tests\Images\Input"; + private const string InputImagesRelativePath = @"tests\Images\Input"; - private const string ActualOutputDirectoryRelativePath = @"tests\Images\ActualOutput"; + private const string ActualOutputDirectoryRelativePath = @"tests\Images\ActualOutput"; - private const string ReferenceOutputDirectoryRelativePath = @"tests\Images\ReferenceOutput"; + private const string ReferenceOutputDirectoryRelativePath = @"tests\Images\ReferenceOutput"; - private const string ToolsDirectoryRelativePath = @"tests\Images\External\tools"; + private const string ToolsDirectoryRelativePath = @"tests\Images\External\tools"; - private static readonly Lazy SolutionDirectoryFullPathLazy = new Lazy(GetSolutionDirectoryFullPathImpl); + private static readonly Lazy SolutionDirectoryFullPathLazy = new(GetSolutionDirectoryFullPathImpl); - private static readonly Lazy NetCoreVersionLazy = new Lazy(GetNetCoreVersion); + private static readonly Lazy NetCoreVersionLazy = new(GetNetCoreVersion); - static TestEnvironment() => PrepareRemoteExecutor(); + static TestEnvironment() => PrepareRemoteExecutor(); - /// - /// Gets the .NET Core version, if running on .NET Core, otherwise returns an empty string. - /// - internal static string NetCoreVersion => NetCoreVersionLazy.Value; + /// + /// Gets the .NET Core version, if running on .NET Core, otherwise returns an empty string. + /// + internal static string NetCoreVersion => NetCoreVersionLazy.Value; - // ReSharper disable once InconsistentNaming + // ReSharper disable once InconsistentNaming - /// - /// Gets a value indicating whether test execution runs on CI. - /// + /// + /// Gets a value indicating whether test execution runs on CI. + /// #if ENV_CI - internal static bool RunsOnCI => true; + internal static bool RunsOnCI => true; #else - internal static bool RunsOnCI => false; + internal static bool RunsOnCI => false; #endif - /// - /// Gets a value indicating whether test execution is running with code coverage testing enabled. - /// + /// + /// Gets a value indicating whether test execution is running with code coverage testing enabled. + /// #if ENV_CODECOV - internal static bool RunsWithCodeCoverage => true; + internal static bool RunsWithCodeCoverage => true; #else - internal static bool RunsWithCodeCoverage => false; + internal static bool RunsWithCodeCoverage => false; #endif - internal static string SolutionDirectoryFullPath => SolutionDirectoryFullPathLazy.Value; + internal static string SolutionDirectoryFullPath => SolutionDirectoryFullPathLazy.Value; - private static readonly FileInfo TestAssemblyFile = - new FileInfo(typeof(TestEnvironment).GetTypeInfo().Assembly.Location); + private static readonly FileInfo TestAssemblyFile = + new(typeof(TestEnvironment).GetTypeInfo().Assembly.Location); - private static string GetSolutionDirectoryFullPathImpl() - { - DirectoryInfo directory = TestAssemblyFile.Directory; + private static string GetSolutionDirectoryFullPathImpl() + { + DirectoryInfo directory = TestAssemblyFile.Directory; - while (!directory.EnumerateFiles(ImageSharpTexturesSolutionFileName).Any()) + while (!directory.EnumerateFiles(ImageSharpTexturesSolutionFileName).Any()) + { + try { - try - { - directory = directory.Parent; - } - catch (Exception ex) - { - throw new DirectoryNotFoundException( - $"Unable to find ImageSharp solution directory from {TestAssemblyFile} because of {ex.GetType().Name}!", - ex); - } - - if (directory == null) - { - throw new DirectoryNotFoundException($"Unable to find ImageSharp solution directory from {TestAssemblyFile}!"); - } + directory = directory.Parent; + } + catch (Exception ex) + { + throw new DirectoryNotFoundException( + $"Unable to find ImageSharp solution directory from {TestAssemblyFile} because of {ex.GetType().Name}!", + ex); } - return directory.FullName; + if (directory == null) + { + throw new DirectoryNotFoundException($"Unable to find ImageSharp solution directory from {TestAssemblyFile}!"); + } } - private static string GetFullPath(string relativePath) => - Path.Combine(SolutionDirectoryFullPath, relativePath) - .Replace('\\', Path.DirectorySeparatorChar); + return directory.FullName; + } + + private static string GetFullPath(string relativePath) => + Path.Combine(SolutionDirectoryFullPath, relativePath) + .Replace('\\', Path.DirectorySeparatorChar); - /// - /// Gets the correct full path to the Input Images directory. - /// - internal static string InputImagesDirectoryFullPath => GetFullPath(InputImagesRelativePath); + /// + /// Gets the correct full path to the Input Images directory. + /// + internal static string InputImagesDirectoryFullPath => GetFullPath(InputImagesRelativePath); - /// - /// Gets the correct full path to the Actual Output directory. (To be written to by the test cases.) - /// - internal static string ActualOutputDirectoryFullPath => GetFullPath(ActualOutputDirectoryRelativePath); + /// + /// Gets the correct full path to the Actual Output directory. (To be written to by the test cases.) + /// + internal static string ActualOutputDirectoryFullPath => GetFullPath(ActualOutputDirectoryRelativePath); - /// - /// Gets the correct full path to the Expected Output directory. (To compare the test results to.) - /// - internal static string ReferenceOutputDirectoryFullPath => GetFullPath(ReferenceOutputDirectoryRelativePath); + /// + /// Gets the correct full path to the Expected Output directory. (To compare the test results to.) + /// + internal static string ReferenceOutputDirectoryFullPath => GetFullPath(ReferenceOutputDirectoryRelativePath); - internal static string ToolsDirectoryFullPath => GetFullPath(ToolsDirectoryRelativePath); + internal static string ToolsDirectoryFullPath => GetFullPath(ToolsDirectoryRelativePath); - internal static string GetReferenceOutputFileName(string actualOutputFileName) => - actualOutputFileName.Replace("ActualOutput", @"ReferenceOutput").Replace('\\', Path.DirectorySeparatorChar); + internal static string GetReferenceOutputFileName(string actualOutputFileName) => + actualOutputFileName.Replace("ActualOutput", @"ReferenceOutput").Replace('\\', Path.DirectorySeparatorChar); - internal static bool IsLinux => RuntimeInformation.IsOSPlatform(OSPlatform.Linux); + internal static bool IsLinux => RuntimeInformation.IsOSPlatform(OSPlatform.Linux); - internal static bool IsOSX => RuntimeInformation.IsOSPlatform(OSPlatform.OSX); + internal static bool IsOSX => RuntimeInformation.IsOSPlatform(OSPlatform.OSX); - internal static bool IsMono => Type.GetType("Mono.Runtime") != null; // https://stackoverflow.com/a/721194 + internal static bool IsMono => Type.GetType("Mono.Runtime") != null; // https://stackoverflow.com/a/721194 - internal static bool IsWindows => RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + internal static bool IsWindows => RuntimeInformation.IsOSPlatform(OSPlatform.Windows); - internal static bool Is64BitProcess => IntPtr.Size == 8; + internal static bool Is64BitProcess => IntPtr.Size == 8; - internal static bool IsFramework => string.IsNullOrEmpty(NetCoreVersion); + internal static bool IsFramework => string.IsNullOrEmpty(NetCoreVersion); - /// - /// A dummy operation to enforce the execution of the static constructor. - /// - internal static void EnsureSharedInitializersDone() + /// + /// A dummy operation to enforce the execution of the static constructor. + /// + internal static void EnsureSharedInitializersDone() + { + } + + /// + /// Creates the image output directory. + /// + /// The path. + /// The path parts. + /// + /// The . + /// + internal static string CreateOutputDirectory(string path, params string[] pathParts) + { + path = Path.Combine(ActualOutputDirectoryFullPath, path); + + if (pathParts != null && pathParts.Length > 0) { + path = Path.Combine(path, Path.Combine(pathParts)); } - /// - /// Creates the image output directory. - /// - /// The path. - /// The path parts. - /// - /// The . - /// - internal static string CreateOutputDirectory(string path, params string[] pathParts) + if (!Directory.Exists(path)) { - path = Path.Combine(ActualOutputDirectoryFullPath, path); - - if (pathParts != null && pathParts.Length > 0) - { - path = Path.Combine(path, Path.Combine(pathParts)); - } + Directory.CreateDirectory(path); + } - if (!Directory.Exists(path)) - { - Directory.CreateDirectory(path); - } + return path; + } - return path; + /// + /// Creates Microsoft.DotNet.RemoteExecutor.exe.config for .NET framework, + /// When running in 32 bits, enforces 32 bit execution of Microsoft.DotNet.RemoteExecutor.exe + /// with the help of CorFlags.exe found in Windows SDK. + /// + private static void PrepareRemoteExecutor() + { + if (!IsFramework) + { + return; } - /// - /// Creates Microsoft.DotNet.RemoteExecutor.exe.config for .NET framework, - /// When running in 32 bits, enforces 32 bit execution of Microsoft.DotNet.RemoteExecutor.exe - /// with the help of CorFlags.exe found in Windows SDK. - /// - private static void PrepareRemoteExecutor() + string remoteExecutorConfigPath = + Path.Combine(TestAssemblyFile.DirectoryName, "Microsoft.DotNet.RemoteExecutor.exe.config"); + + if (File.Exists(remoteExecutorConfigPath)) { - if (!IsFramework) - { - return; - } + // Already initialized + return; + } - string remoteExecutorConfigPath = - Path.Combine(TestAssemblyFile.DirectoryName, "Microsoft.DotNet.RemoteExecutor.exe.config"); + string testProjectConfigPath = TestAssemblyFile.FullName + ".config"; + if (File.Exists(testProjectConfigPath)) + { + File.Copy(testProjectConfigPath, remoteExecutorConfigPath); + } - if (File.Exists(remoteExecutorConfigPath)) - { - // Already initialized - return; - } + if (Is64BitProcess) + { + return; + } - string testProjectConfigPath = TestAssemblyFile.FullName + ".config"; - if (File.Exists(testProjectConfigPath)) - { - File.Copy(testProjectConfigPath, remoteExecutorConfigPath); - } + EnsureRemoteExecutorIs32Bit(); + } - if (Is64BitProcess) - { - return; - } + /// + /// Locate and run CorFlags.exe /32Bit+ + /// https://docs.microsoft.com/en-us/dotnet/framework/tools/corflags-exe-corflags-conversion-tool + /// + private static void EnsureRemoteExecutorIs32Bit() + { + string windowsSdksDir = Path.Combine( + Environment.GetEnvironmentVariable("PROGRAMFILES(x86)"), + "Microsoft SDKs", + "Windows"); - EnsureRemoteExecutorIs32Bit(); - } + FileInfo corFlagsFile = Find(new DirectoryInfo(windowsSdksDir), "CorFlags.exe"); - /// - /// Locate and run CorFlags.exe /32Bit+ - /// https://docs.microsoft.com/en-us/dotnet/framework/tools/corflags-exe-corflags-conversion-tool - /// - private static void EnsureRemoteExecutorIs32Bit() - { - string windowsSdksDir = Path.Combine( - Environment.GetEnvironmentVariable("PROGRAMFILES(x86)"), - "Microsoft SDKs", - "Windows"); + string remoteExecutorPath = Path.Combine(TestAssemblyFile.DirectoryName, "Microsoft.DotNet.RemoteExecutor.exe"); - FileInfo corFlagsFile = Find(new DirectoryInfo(windowsSdksDir), "CorFlags.exe"); + string remoteExecutorTmpPath = $"{remoteExecutorPath}._tmp"; - string remoteExecutorPath = Path.Combine(TestAssemblyFile.DirectoryName, "Microsoft.DotNet.RemoteExecutor.exe"); + if (File.Exists(remoteExecutorTmpPath)) + { + // Already initialized + return; + } - string remoteExecutorTmpPath = $"{remoteExecutorPath}._tmp"; + File.Copy(remoteExecutorPath, remoteExecutorTmpPath); - if (File.Exists(remoteExecutorTmpPath)) - { - // Already initialized - return; - } + string args = $"{remoteExecutorTmpPath} /32Bit+ /Force"; - File.Copy(remoteExecutorPath, remoteExecutorTmpPath); + ProcessStartInfo si = new() + { + FileName = corFlagsFile.FullName, + Arguments = args, + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true + }; + + using Process proc = Process.Start(si); + proc.WaitForExit(); + string standardOutput = proc.StandardOutput.ReadToEnd(); + string standardError = proc.StandardError.ReadToEnd(); + + if (proc.ExitCode != 0) + { + throw new Exception( + $@"Failed to run {si.FileName} {si.Arguments}:\n STDOUT: {standardOutput}\n STDERR: {standardError}"); + } - string args = $"{remoteExecutorTmpPath} /32Bit+ /Force"; + File.Delete(remoteExecutorPath); + File.Copy(remoteExecutorTmpPath, remoteExecutorPath); - var si = new ProcessStartInfo() - { - FileName = corFlagsFile.FullName, - Arguments = args, - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true - }; - - using var proc = Process.Start(si); - proc.WaitForExit(); - string standardOutput = proc.StandardOutput.ReadToEnd(); - string standardError = proc.StandardError.ReadToEnd(); - - if (proc.ExitCode != 0) + static FileInfo Find(DirectoryInfo root, string name) + { + FileInfo fi = root.EnumerateFiles(name).FirstOrDefault(); + if (fi != null) { - throw new Exception( - $@"Failed to run {si.FileName} {si.Arguments}:\n STDOUT: {standardOutput}\n STDERR: {standardError}"); + return fi; } - File.Delete(remoteExecutorPath); - File.Copy(remoteExecutorTmpPath, remoteExecutorPath); - - static FileInfo Find(DirectoryInfo root, string name) + foreach (DirectoryInfo dir in root.EnumerateDirectories()) { - FileInfo fi = root.EnumerateFiles(name).FirstOrDefault(); + fi = Find(dir, name); if (fi != null) { return fi; } - - foreach (DirectoryInfo dir in root.EnumerateDirectories()) - { - fi = Find(dir, name); - if (fi != null) - { - return fi; - } - } - - return null; } - } - internal static IImageDecoder GetReferenceDecoder(string filePath) - { - IImageFormat format = GetImageFormat(filePath); - return Configuration.ImageFormatsManager.GetDecoder(format); - } - - internal static IImageEncoder GetReferenceEncoder(string filePath) - { - IImageFormat format = GetImageFormat(filePath); - return Configuration.ImageFormatsManager.GetEncoder(format); + return null; } + } - internal static IImageFormat GetImageFormat(string filePath) - { - string extension = Path.GetExtension(filePath); + internal static IImageDecoder GetReferenceDecoder(string filePath) + { + IImageFormat format = GetImageFormat(filePath); + return Configuration.ImageFormatsManager.GetDecoder(format); + } - if (!Configuration.ImageFormatsManager.TryFindFormatByFileExtension(extension, out IImageFormat format)) - { - throw new NotSupportedException($"No image format found for extension '{extension}'!"); - } + internal static IImageEncoder GetReferenceEncoder(string filePath) + { + IImageFormat format = GetImageFormat(filePath); + return Configuration.ImageFormatsManager.GetEncoder(format); + } - return format; - } + internal static IImageFormat GetImageFormat(string filePath) + { + string extension = Path.GetExtension(filePath); - private static void ConfigureCodecs( - this ImageSharp.Configuration cfg, - IImageFormat imageFormat, - IImageDecoder decoder, - IImageEncoder encoder, - IImageFormatDetector detector) + if (!Configuration.ImageFormatsManager.TryFindFormatByFileExtension(extension, out IImageFormat format)) { - cfg.ImageFormatsManager.SetDecoder(imageFormat, decoder); - cfg.ImageFormatsManager.SetEncoder(imageFormat, encoder); - cfg.ImageFormatsManager.AddImageFormatDetector(detector); + throw new NotSupportedException($"No image format found for extension '{extension}'!"); } - private static ImageSharp.Configuration CreateDefaultConfiguration() - { - var cfg = new ImageSharp.Configuration(); + return format; + } - cfg.ConfigureCodecs( - PngFormat.Instance, - PngDecoder.Instance, - new PngEncoder(), - new PngImageFormatDetector()); + private static void ConfigureCodecs( + this ImageSharp.Configuration cfg, + IImageFormat imageFormat, + IImageDecoder decoder, + IImageEncoder encoder, + IImageFormatDetector detector) + { + cfg.ImageFormatsManager.SetDecoder(imageFormat, decoder); + cfg.ImageFormatsManager.SetEncoder(imageFormat, encoder); + cfg.ImageFormatsManager.AddImageFormatDetector(detector); + } - return cfg; - } + private static ImageSharp.Configuration CreateDefaultConfiguration() + { + ImageSharp.Configuration cfg = new(); - /// - /// Solution borrowed from: - /// https://github.com/dotnet/BenchmarkDotNet/issues/448#issuecomment-308424100 - /// - private static string GetNetCoreVersion() - { - Assembly assembly = typeof(System.Runtime.GCSettings).GetTypeInfo().Assembly; - string[] assemblyPath = assembly.Location.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries); - int netCoreAppIndex = Array.IndexOf(assemblyPath, "Microsoft.NETCore.App"); - if (netCoreAppIndex > 0 && netCoreAppIndex < assemblyPath.Length - 2) - { - return assemblyPath[netCoreAppIndex + 1]; - } + cfg.ConfigureCodecs( + PngFormat.Instance, + PngDecoder.Instance, + new PngEncoder(), + new PngImageFormatDetector()); - return string.Empty; + return cfg; + } + + /// + /// Solution borrowed from: + /// https://github.com/dotnet/BenchmarkDotNet/issues/448#issuecomment-308424100 + /// + private static string GetNetCoreVersion() + { + Assembly assembly = typeof(System.Runtime.GCSettings).GetTypeInfo().Assembly; + string[] assemblyPath = assembly.Location.Split(['/', '\\'], StringSplitOptions.RemoveEmptyEntries); + int netCoreAppIndex = Array.IndexOf(assemblyPath, "Microsoft.NETCore.App"); + if (netCoreAppIndex > 0 && netCoreAppIndex < assemblyPath.Length - 2) + { + return assemblyPath[netCoreAppIndex + 1]; } + + return string.Empty; } } diff --git a/tests/ImageSharp.Textures.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Textures.Tests/TestUtilities/TestImageExtensions.cs index 5218853d..cb20b941 100644 --- a/tests/ImageSharp.Textures.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Textures.Tests/TestUtilities/TestImageExtensions.cs @@ -1,229 +1,226 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; -using System.IO; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Textures.Tests.TestUtilities.ImageComparison; using SixLabors.ImageSharp.Textures.Tests.TestUtilities.TextureProviders; -namespace SixLabors.ImageSharp.Textures.Tests.TestUtilities +namespace SixLabors.ImageSharp.Textures.Tests.TestUtilities; + +public static class TestImageExtensions { - public static class TestImageExtensions + public static void DebugSave( + this Image image, + ITestTextureProvider provider, + FormattableString testOutputDetails, + string extension = "png", + bool appendPixelTypeToFileName = false, + bool appendSourceFileOrDescription = false, + IImageEncoder encoder = null) => image.DebugSave( + provider, + (object)testOutputDetails, + extension, + appendPixelTypeToFileName, + appendSourceFileOrDescription, + encoder); + + /// + /// Saves the image only when not running in the CI server. + /// + /// The image. + /// The image provider. + /// Details to be concatenated to the test output file, describing the parameters of the test. + /// The extension. + /// A boolean indicating whether to append the pixel type to the output file name. + /// A boolean indicating whether to append SourceFileOrDescription to the test output file name. + /// Custom encoder to use. + /// The input image. + public static Image DebugSave( + this Image image, + ITestTextureProvider provider, + object testOutputDetails = null, + string extension = "png", + bool appendPixelTypeToFileName = false, + bool appendSourceFileOrDescription = false, + IImageEncoder encoder = null) { - public static void DebugSave( - this Image image, - ITestTextureProvider provider, - FormattableString testOutputDetails, - string extension = "png", - bool appendPixelTypeToFileName = false, - bool appendSourceFileOrDescription = false, - IImageEncoder encoder = null) => image.DebugSave( - provider, - (object)testOutputDetails, - extension, - appendPixelTypeToFileName, - appendSourceFileOrDescription, - encoder); - - /// - /// Saves the image only when not running in the CI server. - /// - /// The image. - /// The image provider. - /// Details to be concatenated to the test output file, describing the parameters of the test. - /// The extension. - /// A boolean indicating whether to append the pixel type to the output file name. - /// A boolean indicating whether to append SourceFileOrDescription to the test output file name. - /// Custom encoder to use. - /// The input image. - public static Image DebugSave( - this Image image, - ITestTextureProvider provider, - object testOutputDetails = null, - string extension = "png", - bool appendPixelTypeToFileName = false, - bool appendSourceFileOrDescription = false, - IImageEncoder encoder = null) + if (TestEnvironment.RunsOnCI) { - if (TestEnvironment.RunsOnCI) - { - return image; - } - - // We are running locally then we want to save it out - provider.Utility.SaveTestOutputFile( - image, - extension, - testOutputDetails: testOutputDetails, - appendPixelTypeToFileName: appendPixelTypeToFileName, - appendSourceFileOrDescription: appendSourceFileOrDescription, - encoder: encoder); return image; } - public static void DebugSave( - this Image image, - ITestTextureProvider provider, - IImageEncoder encoder, - FormattableString testOutputDetails, - bool appendPixelTypeToFileName = false) => image.DebugSave(provider, encoder, (object)testOutputDetails, appendPixelTypeToFileName); - - /// - /// Saves the image only when not running in the CI server. - /// - /// The image - /// The image provider - /// The image encoder - /// Details to be concatenated to the test output file, describing the parameters of the test. - /// A boolean indicating whether to append the pixel type to the output file name. - public static void DebugSave( - this Image image, - ITestTextureProvider provider, - IImageEncoder encoder, - object testOutputDetails = null, - bool appendPixelTypeToFileName = false) + // We are running locally then we want to save it out + provider.Utility.SaveTestOutputFile( + image, + extension, + testOutputDetails: testOutputDetails, + appendPixelTypeToFileName: appendPixelTypeToFileName, + appendSourceFileOrDescription: appendSourceFileOrDescription, + encoder: encoder); + return image; + } + + public static void DebugSave( + this Image image, + ITestTextureProvider provider, + IImageEncoder encoder, + FormattableString testOutputDetails, + bool appendPixelTypeToFileName = false) => image.DebugSave(provider, encoder, (object)testOutputDetails, appendPixelTypeToFileName); + + /// + /// Saves the image only when not running in the CI server. + /// + /// The image + /// The image provider + /// The image encoder + /// Details to be concatenated to the test output file, describing the parameters of the test. + /// A boolean indicating whether to append the pixel type to the output file name. + public static void DebugSave( + this Image image, + ITestTextureProvider provider, + IImageEncoder encoder, + object testOutputDetails = null, + bool appendPixelTypeToFileName = false) + { + if (TestEnvironment.RunsOnCI) { - if (TestEnvironment.RunsOnCI) - { - return; - } - - // We are running locally then we want to save it out - provider.Utility.SaveTestOutputFile( - image, - encoder: encoder, - testOutputDetails: testOutputDetails, - appendPixelTypeToFileName: appendPixelTypeToFileName); + return; } - public static Image CompareToReferenceOutput( - this Image image, - ITestTextureProvider provider, - FormattableString testOutputDetails, - string extension = "png", - bool appendPixelTypeToFileName = false, - bool appendSourceFileOrDescription = false) - where TPixel : unmanaged, IPixel => image.CompareToReferenceOutput( - provider, - (object)testOutputDetails, - extension, - appendPixelTypeToFileName, - appendSourceFileOrDescription); - - /// - /// Compares the image against the expected Reference output, throws an exception if the images are not similar enough. - /// The output file should be named identically to the output produced by . - /// - /// The pixel format. - /// The image which should be compared to the reference image. - /// The image provider. - /// Details to be concatenated to the test output file, describing the parameters of the test. - /// The extension - /// A boolean indicating whether to append the pixel type to the output file name. - /// A boolean indicating whether to append to the test output file name. - /// The image. - public static Image CompareToReferenceOutput( - this Image image, - ITestTextureProvider provider, - object testOutputDetails = null, - string extension = "png", - bool appendPixelTypeToFileName = false, - bool appendSourceFileOrDescription = false) - where TPixel : unmanaged, IPixel => CompareToReferenceOutput( - image, - ImageComparer.Tolerant(), - provider, - testOutputDetails, - extension, - appendPixelTypeToFileName, - appendSourceFileOrDescription); - - public static Image CompareToReferenceOutput( - this Image image, - ImageComparer comparer, - ITestTextureProvider provider, - FormattableString testOutputDetails, - string extension = "png", - bool appendPixelTypeToFileName = false) - where TPixel : unmanaged, IPixel => image.CompareToReferenceOutput( - comparer, - provider, - (object)testOutputDetails, - extension, - appendPixelTypeToFileName); - - /// - /// Compares the image against the expected Reference output, throws an exception if the images are not similar enough. - /// The output file should be named identically to the output produced by . - /// - /// The pixel format. - /// The image which should be compared to the reference output. - /// The to use. - /// The image provider. - /// Details to be concatenated to the test output file, describing the parameters of the test. - /// The extension - /// A boolean indicating whether to append the pixel type to the output file name. - /// A boolean indicating whether to append SourceFileOrDescription to the test output file name. - /// A custom decoder. - /// The image. - public static Image CompareToReferenceOutput( - this Image image, - ImageComparer comparer, - ITestTextureProvider provider, - object testOutputDetails = null, - string extension = "png", - bool appendPixelTypeToFileName = false, - bool appendSourceFileOrDescription = false, - IImageDecoder decoder = null) - where TPixel : unmanaged, IPixel - { - using (Image referenceImage = GetReferenceOutputImage( - provider, - testOutputDetails, - extension, - appendPixelTypeToFileName, - appendSourceFileOrDescription, - decoder)) - { - comparer.VerifySimilarity(referenceImage, image); - } + // We are running locally then we want to save it out + provider.Utility.SaveTestOutputFile( + image, + encoder: encoder, + testOutputDetails: testOutputDetails, + appendPixelTypeToFileName: appendPixelTypeToFileName); + } - return image; + public static Image CompareToReferenceOutput( + this Image image, + ITestTextureProvider provider, + FormattableString testOutputDetails, + string extension = "png", + bool appendPixelTypeToFileName = false, + bool appendSourceFileOrDescription = false) + where TPixel : unmanaged, IPixel => image.CompareToReferenceOutput( + provider, + (object)testOutputDetails, + extension, + appendPixelTypeToFileName, + appendSourceFileOrDescription); + + /// + /// Compares the image against the expected Reference output, throws an exception if the images are not similar enough. + /// The output file should be named identically to the output produced by . + /// + /// The pixel format. + /// The image which should be compared to the reference image. + /// The image provider. + /// Details to be concatenated to the test output file, describing the parameters of the test. + /// The extension + /// A boolean indicating whether to append the pixel type to the output file name. + /// A boolean indicating whether to append to the test output file name. + /// The image. + public static Image CompareToReferenceOutput( + this Image image, + ITestTextureProvider provider, + object testOutputDetails = null, + string extension = "png", + bool appendPixelTypeToFileName = false, + bool appendSourceFileOrDescription = false) + where TPixel : unmanaged, IPixel => CompareToReferenceOutput( + image, + ImageComparer.Tolerant(), + provider, + testOutputDetails, + extension, + appendPixelTypeToFileName, + appendSourceFileOrDescription); + + public static Image CompareToReferenceOutput( + this Image image, + ImageComparer comparer, + ITestTextureProvider provider, + FormattableString testOutputDetails, + string extension = "png", + bool appendPixelTypeToFileName = false) + where TPixel : unmanaged, IPixel => image.CompareToReferenceOutput( + comparer, + provider, + (object)testOutputDetails, + extension, + appendPixelTypeToFileName); + + /// + /// Compares the image against the expected Reference output, throws an exception if the images are not similar enough. + /// The output file should be named identically to the output produced by . + /// + /// The pixel format. + /// The image which should be compared to the reference output. + /// The to use. + /// The image provider. + /// Details to be concatenated to the test output file, describing the parameters of the test. + /// The extension + /// A boolean indicating whether to append the pixel type to the output file name. + /// A boolean indicating whether to append SourceFileOrDescription to the test output file name. + /// A custom decoder. + /// The image. + public static Image CompareToReferenceOutput( + this Image image, + ImageComparer comparer, + ITestTextureProvider provider, + object testOutputDetails = null, + string extension = "png", + bool appendPixelTypeToFileName = false, + bool appendSourceFileOrDescription = false, + IImageDecoder decoder = null) + where TPixel : unmanaged, IPixel + { + using (Image referenceImage = GetReferenceOutputImage( + provider, + testOutputDetails, + extension, + appendPixelTypeToFileName, + appendSourceFileOrDescription, + decoder)) + { + comparer.VerifySimilarity(referenceImage, image); } - public static Image GetReferenceOutputImage( - this ITestTextureProvider provider, - object testOutputDetails = null, - string extension = "png", - bool appendPixelTypeToFileName = false, - bool appendSourceFileOrDescription = false, - IImageDecoder decoder = null) - where TPixel : unmanaged, IPixel + return image; + } + + public static Image GetReferenceOutputImage( + this ITestTextureProvider provider, + object testOutputDetails = null, + string extension = "png", + bool appendPixelTypeToFileName = false, + bool appendSourceFileOrDescription = false, + IImageDecoder decoder = null) + where TPixel : unmanaged, IPixel + { + string referenceOutputFile = provider.Utility.GetReferenceOutputFileName( + extension, + testOutputDetails, + appendPixelTypeToFileName, + appendSourceFileOrDescription); + + if (!File.Exists(referenceOutputFile)) { - string referenceOutputFile = provider.Utility.GetReferenceOutputFileName( - extension, - testOutputDetails, - appendPixelTypeToFileName, - appendSourceFileOrDescription); - - if (!File.Exists(referenceOutputFile)) - { - throw new FileNotFoundException($"Reference output file {referenceOutputFile} is missing", referenceOutputFile); - } - - IImageFormat format = TestEnvironment.GetImageFormat(referenceOutputFile); - decoder ??= TestEnvironment.GetReferenceDecoder(referenceOutputFile); - - ImageSharp.Configuration configuration = ImageSharp.Configuration.Default.Clone(); - configuration.ImageFormatsManager.SetDecoder(format, decoder); - DecoderOptions options = new() - { - Configuration = configuration - }; - - return Image.Load(options, referenceOutputFile); + throw new FileNotFoundException($"Reference output file {referenceOutputFile} is missing", referenceOutputFile); } + + IImageFormat format = TestEnvironment.GetImageFormat(referenceOutputFile); + decoder ??= TestEnvironment.GetReferenceDecoder(referenceOutputFile); + + ImageSharp.Configuration configuration = ImageSharp.Configuration.Default.Clone(); + configuration.ImageFormatsManager.SetDecoder(format, decoder); + DecoderOptions options = new() + { + Configuration = configuration + }; + + return Image.Load(options, referenceOutputFile); } } diff --git a/tests/ImageSharp.Textures.Tests/TestUtilities/TestUtils.cs b/tests/ImageSharp.Textures.Tests/TestUtilities/TestUtils.cs index cd3e4c62..710631b9 100644 --- a/tests/ImageSharp.Textures.Tests/TestUtilities/TestUtils.cs +++ b/tests/ImageSharp.Textures.Tests/TestUtilities/TestUtils.cs @@ -1,104 +1,98 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; -using System.Collections.Generic; using System.Globalization; -using System.Linq; using System.Reflection; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Textures.Tests.TestUtilities +namespace SixLabors.ImageSharp.Textures.Tests.TestUtilities; + +/// +/// Various utility and extension methods. +/// +public static class TestUtils { - /// - /// Various utility and extension methods. - /// - public static class TestUtils - { - private static readonly Dictionary ClrTypes2PixelTypes = new Dictionary(); + private static readonly Dictionary ClrTypes2PixelTypes = []; - private static readonly Assembly ImageSharpAssembly = typeof(Rgba32).GetTypeInfo().Assembly; + private static readonly Assembly ImageSharpAssembly = typeof(Rgba32).GetTypeInfo().Assembly; - private static readonly Dictionary PixelTypes2ClrTypes = new Dictionary(); + private static readonly Dictionary PixelTypes2ClrTypes = []; - private static readonly PixelTypes[] AllConcretePixelTypes = GetAllPixelTypes() - .Except(new[] { PixelTypes.Undefined, PixelTypes.All }) - .ToArray(); + private static readonly PixelTypes[] AllConcretePixelTypes = [.. GetAllPixelTypes().Except(new[] { PixelTypes.Undefined, PixelTypes.All })]; - static TestUtils() + static TestUtils() + { + // Add Rgba32 Our default. + Type defaultPixelFormatType = typeof(Rgba32); + PixelTypes2ClrTypes[PixelTypes.Rgba32] = defaultPixelFormatType; + ClrTypes2PixelTypes[defaultPixelFormatType] = PixelTypes.Rgba32; + + // Add PixelFormat types + string nameSpace = typeof(A8).FullName; + nameSpace = nameSpace[..(nameSpace.Length - typeof(A8).Name.Length - 1)]; + foreach (PixelTypes pt in AllConcretePixelTypes.Where(pt => pt != PixelTypes.Rgba32)) { - // Add Rgba32 Our default. - Type defaultPixelFormatType = typeof(Rgba32); - PixelTypes2ClrTypes[PixelTypes.Rgba32] = defaultPixelFormatType; - ClrTypes2PixelTypes[defaultPixelFormatType] = PixelTypes.Rgba32; - - // Add PixelFormat types - string nameSpace = typeof(A8).FullName; - nameSpace = nameSpace.Substring(0, nameSpace.Length - typeof(A8).Name.Length - 1); - foreach (PixelTypes pt in AllConcretePixelTypes.Where(pt => pt != PixelTypes.Rgba32)) - { - string typeName = $"{nameSpace}.{pt}"; - Type t = ImageSharpAssembly.GetType(typeName); - PixelTypes2ClrTypes[pt] = t ?? throw new InvalidOperationException($"Could not find: {typeName}"); - ClrTypes2PixelTypes[t] = pt; - } + string typeName = $"{nameSpace}.{pt}"; + Type t = ImageSharpAssembly.GetType(typeName); + PixelTypes2ClrTypes[pt] = t ?? throw new InvalidOperationException($"Could not find: {typeName}"); + ClrTypes2PixelTypes[t] = pt; } + } - public static bool HasFlag(this PixelTypes pixelTypes, PixelTypes flag) => (pixelTypes & flag) == flag; + public static bool HasFlag(this PixelTypes pixelTypes, PixelTypes flag) => (pixelTypes & flag) == flag; - public static string ToCsv(this IEnumerable items, string separator = ",") => string.Join(separator, items.Select(o => string.Format(CultureInfo.InvariantCulture, "{0}", o))); + public static string ToCsv(this IEnumerable items, string separator = ",") => string.Join(separator, items.Select(o => string.Format(CultureInfo.InvariantCulture, "{0}", o))); - public static Type GetClrType(this PixelTypes pixelType) => PixelTypes2ClrTypes[pixelType]; + public static Type GetClrType(this PixelTypes pixelType) => PixelTypes2ClrTypes[pixelType]; - /// - /// Returns the enumerations for the given type. - /// - /// The pixel type. - public static PixelTypes GetPixelType(this Type colorStructClrType) => ClrTypes2PixelTypes[colorStructClrType]; + /// + /// Returns the enumerations for the given type. + /// + /// The pixel type. + public static PixelTypes GetPixelType(this Type colorStructClrType) => ClrTypes2PixelTypes[colorStructClrType]; - public static IEnumerable> ExpandAllTypes(this PixelTypes pixelTypes) + public static IEnumerable> ExpandAllTypes(this PixelTypes pixelTypes) + { + if (pixelTypes == PixelTypes.Undefined) { - if (pixelTypes == PixelTypes.Undefined) - { - return Enumerable.Empty>(); - } - else if (pixelTypes == PixelTypes.All) - { - // TODO: Need to return unknown types here without forcing CLR to load all types in ImageSharp assembly - return PixelTypes2ClrTypes; - } + return Enumerable.Empty>(); + } + else if (pixelTypes == PixelTypes.All) + { + // TODO: Need to return unknown types here without forcing CLR to load all types in ImageSharp assembly + return PixelTypes2ClrTypes; + } - var result = new Dictionary(); - foreach (PixelTypes pt in AllConcretePixelTypes) + Dictionary result = new(); + foreach (PixelTypes pt in AllConcretePixelTypes) + { + if (pixelTypes.HasAll(pt)) { - if (pixelTypes.HasAll(pt)) - { - result[pt] = pt.GetClrType(); - } + result[pt] = pt.GetClrType(); } - - return result; } - internal static bool HasAll(this PixelTypes pixelTypes, PixelTypes flagsToCheck) => - (pixelTypes & flagsToCheck) == flagsToCheck; - - /// - /// Enumerate all available -s - /// - /// The pixel types - internal static PixelTypes[] GetAllPixelTypes() => (PixelTypes[])Enum.GetValues(typeof(PixelTypes)); + return result; + } - internal static Color GetColorByName(string colorName) - { - var f = (FieldInfo)typeof(Color).GetMember(colorName)[0]; - return (Color)f.GetValue(null); - } + internal static bool HasAll(this PixelTypes pixelTypes, PixelTypes flagsToCheck) => + (pixelTypes & flagsToCheck) == flagsToCheck; - internal static TPixel GetPixelOfNamedColor(string colorName) - where TPixel : unmanaged, IPixel => - GetColorByName(colorName).ToPixel(); + /// + /// Enumerate all available -s + /// + /// The pixel types + internal static PixelTypes[] GetAllPixelTypes() => (PixelTypes[])Enum.GetValues(typeof(PixelTypes)); - public static string AsInvariantString(this FormattableString formattable) => FormattableString.Invariant(formattable); + internal static Color GetColorByName(string colorName) + { + FieldInfo f = (FieldInfo)typeof(Color).GetMember(colorName)[0]; + return (Color)f.GetValue(null); } + + internal static TPixel GetPixelOfNamedColor(string colorName) + where TPixel : unmanaged, IPixel => + GetColorByName(colorName).ToPixel(); + + public static string AsInvariantString(this FormattableString formattable) => FormattableString.Invariant(formattable); } diff --git a/tests/ImageSharp.Textures.Tests/TestUtilities/TextureProviders/ITestTextureProvider.cs b/tests/ImageSharp.Textures.Tests/TestUtilities/TextureProviders/ITestTextureProvider.cs index 757107c3..919110f7 100644 --- a/tests/ImageSharp.Textures.Tests/TestUtilities/TextureProviders/ITestTextureProvider.cs +++ b/tests/ImageSharp.Textures.Tests/TestUtilities/TextureProviders/ITestTextureProvider.cs @@ -3,31 +3,30 @@ using SixLabors.ImageSharp.Textures.Tests.Enums; -namespace SixLabors.ImageSharp.Textures.Tests.TestUtilities.TextureProviders +namespace SixLabors.ImageSharp.Textures.Tests.TestUtilities.TextureProviders; + +public interface ITestTextureProvider { - public interface ITestTextureProvider - { - string MethodName { get; } + string MethodName { get; } - /// - /// Gets the utility instance to provide information about the test image & manage input/output. - /// - ImagingTestCaseUtility Utility { get; } + /// + /// Gets the utility instance to provide information about the test image & manage input/output. + /// + ImagingTestCaseUtility Utility { get; } - /// - /// Gets the texture container format. - /// - TestTextureFormat TextureFormat { get; } + /// + /// Gets the texture container format. + /// + TestTextureFormat TextureFormat { get; } - /// - /// Gets the type of the texture, e.g. flat, volume or cubemap. - /// - TestTextureType TextureType { get; } + /// + /// Gets the type of the texture, e.g. flat, volume or cubemap. + /// + TestTextureType TextureType { get; } - TestTextureTool TextureTool { get; } + TestTextureTool TextureTool { get; } - string InputFile { get; } + string InputFile { get; } - bool IsRegex { get; } - } + bool IsRegex { get; } } diff --git a/tests/ImageSharp.Textures.Tests/TestUtilities/TextureProviders/TestTextureProvider.cs b/tests/ImageSharp.Textures.Tests/TestUtilities/TextureProviders/TestTextureProvider.cs index 372164db..296da8db 100644 --- a/tests/ImageSharp.Textures.Tests/TestUtilities/TextureProviders/TestTextureProvider.cs +++ b/tests/ImageSharp.Textures.Tests/TestUtilities/TextureProviders/TestTextureProvider.cs @@ -1,129 +1,126 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.IO; -using System.Text; using System.Globalization; +using System.Text; using SixLabors.ImageSharp.Textures.Formats; using SixLabors.ImageSharp.Textures.Tests.Enums; using SixLabors.ImageSharp.Textures.TextureFormats; -using Xunit; -namespace SixLabors.ImageSharp.Textures.Tests.TestUtilities.TextureProviders +namespace SixLabors.ImageSharp.Textures.Tests.TestUtilities.TextureProviders; + +public class TestTextureProvider : ITestTextureProvider { - public class TestTextureProvider : ITestTextureProvider - { - public string MethodName { get; } + public string MethodName { get; } - /// - public ImagingTestCaseUtility Utility { get; private set; } + /// + public ImagingTestCaseUtility Utility { get; private set; } - /// - public TestTextureFormat TextureFormat { get; } + /// + public TestTextureFormat TextureFormat { get; } - /// - public TestTextureType TextureType { get; } + /// + public TestTextureType TextureType { get; } - /// - public TestTextureTool TextureTool { get; } + /// + public TestTextureTool TextureTool { get; } - public string InputFile { get; } + public string InputFile { get; } - public bool IsRegex { get; } + public bool IsRegex { get; } - public virtual Texture GetTexture(ITextureDecoder decoder) - { - using FileStream fileStream = File.OpenRead(this.InputFile); + public virtual Texture GetTexture(ITextureDecoder decoder) + { + using FileStream fileStream = File.OpenRead(this.InputFile); - Texture result = decoder.DecodeTexture(Configuration.Default, fileStream); + Texture result = decoder.DecodeTexture(Configuration.Default, fileStream); - Assert.True(fileStream.Length == fileStream.Position, "The texture file stream was not read to the end"); + Assert.True(fileStream.Length == fileStream.Position, "The texture file stream was not read to the end"); - return result; - } + return result; + } - public TestTextureProvider( - string methodName, - TestTextureFormat textureFormat, - TestTextureType textureType, - TestTextureTool textureTool, - string inputFile, - bool isRegex) + public TestTextureProvider( + string methodName, + TestTextureFormat textureFormat, + TestTextureType textureType, + TestTextureTool textureTool, + string inputFile, + bool isRegex) + { + this.MethodName = methodName; + this.TextureFormat = textureFormat; + this.TextureType = textureType; + this.TextureTool = textureTool; + this.InputFile = inputFile; + this.IsRegex = isRegex; + this.Utility = new ImagingTestCaseUtility { - this.MethodName = methodName; - this.TextureFormat = textureFormat; - this.TextureType = textureType; - this.TextureTool = textureTool; - this.InputFile = inputFile; - this.IsRegex = isRegex; - this.Utility = new ImagingTestCaseUtility - { - SourceFileOrDescription = inputFile, - TestName = methodName - }; - } + SourceFileOrDescription = inputFile, + TestName = methodName + }; + } - private void SaveMipMaps(MipMap[] mipMaps, string name) - { - string path = Path.Combine(TestEnvironment.ActualOutputDirectoryFullPath, this.TextureFormat.ToString(), this.TextureType.ToString(), this.TextureTool.ToString(), this.MethodName, Path.GetFileNameWithoutExtension(this.InputFile)); + private void SaveMipMaps(MipMap[] mipMaps, string name) + { + string path = Path.Combine(TestEnvironment.ActualOutputDirectoryFullPath, this.TextureFormat.ToString(), this.TextureType.ToString(), this.TextureTool.ToString(), this.MethodName, Path.GetFileNameWithoutExtension(this.InputFile)); - Directory.CreateDirectory(path); + Directory.CreateDirectory(path); - for (int i = 0; i < mipMaps.Length; i++) + for (int i = 0; i < mipMaps.Length; i++) + { + string filename = string.Format(CultureInfo.InvariantCulture, "mipmap-{0}", i + 1); + if (!string.IsNullOrEmpty(name)) { - string filename = string.Format(CultureInfo.InvariantCulture, "mipmap-{0}", i + 1); - if (!string.IsNullOrEmpty(name)) - { - filename = string.Format(CultureInfo.InvariantCulture, "{0}-{1}", filename, name); - } - - using Image image = mipMaps[i].GetImage(); - image.Save(Path.Combine(path, string.Format(CultureInfo.InvariantCulture, "{0}.png", filename))); + filename = string.Format(CultureInfo.InvariantCulture, "{0}-{1}", filename, name); } + + using Image image = mipMaps[i].GetImage(); + image.Save(Path.Combine(path, string.Format(CultureInfo.InvariantCulture, "{0}.png", filename))); } + } - public void SaveTextures(Texture texture) + public void SaveTextures(Texture texture) + { + if (TestEnvironment.RunsOnCI) { - if (TestEnvironment.RunsOnCI) - { - return; - } + return; + } - if (texture is CubemapTexture cubemapTexture) - { - this.SaveMipMaps(cubemapTexture.PositiveX.MipMaps.ToArray(), "positive-x"); - this.SaveMipMaps(cubemapTexture.NegativeX.MipMaps.ToArray(), "negative-x"); - this.SaveMipMaps(cubemapTexture.PositiveY.MipMaps.ToArray(), "positive-y"); - this.SaveMipMaps(cubemapTexture.NegativeY.MipMaps.ToArray(), "negative-y"); - this.SaveMipMaps(cubemapTexture.PositiveZ.MipMaps.ToArray(), "positive-z"); - this.SaveMipMaps(cubemapTexture.NegativeZ.MipMaps.ToArray(), "negative-z"); - } + if (texture is CubemapTexture cubemapTexture) + { + this.SaveMipMaps([.. cubemapTexture.PositiveX.MipMaps], "positive-x"); + this.SaveMipMaps([.. cubemapTexture.NegativeX.MipMaps], "negative-x"); + this.SaveMipMaps([.. cubemapTexture.PositiveY.MipMaps], "positive-y"); + this.SaveMipMaps([.. cubemapTexture.NegativeY.MipMaps], "negative-y"); + this.SaveMipMaps([.. cubemapTexture.PositiveZ.MipMaps], "positive-z"); + this.SaveMipMaps([.. cubemapTexture.NegativeZ.MipMaps], "negative-z"); + } - if (texture is FlatTexture flatTexture) - { - this.SaveMipMaps(flatTexture.MipMaps.ToArray(), null); - } + if (texture is FlatTexture flatTexture) + { + this.SaveMipMaps([.. flatTexture.MipMaps], null); + } - if (texture is VolumeTexture volumeTexture) + if (texture is VolumeTexture volumeTexture) + { + for (int i = 0; i < volumeTexture.Slices.Count; i++) { - for (int i = 0; i < volumeTexture.Slices.Count; i++) - { - this.SaveMipMaps(volumeTexture.Slices[i].MipMaps.ToArray(), string.Format(CultureInfo.InvariantCulture, "slice{0}", i + 1)); - } + this.SaveMipMaps([.. volumeTexture.Slices[i].MipMaps], string.Format(CultureInfo.InvariantCulture, "slice{0}", i + 1)); } } + } - public override string ToString() - { - var stringBuilder = new StringBuilder(); - stringBuilder.AppendLine(); - stringBuilder.AppendLine(string.Format(CultureInfo.InvariantCulture, "Method Name: {0}", this.MethodName)); - stringBuilder.AppendLine(string.Format(CultureInfo.InvariantCulture, "Texture Format: {0}", this.TextureFormat)); - stringBuilder.AppendLine(string.Format(CultureInfo.InvariantCulture, "Texture Type: {0}", this.TextureType)); - stringBuilder.AppendLine(string.Format(CultureInfo.InvariantCulture, "Texture Tool: {0}", this.TextureTool)); - stringBuilder.AppendLine(string.Format(CultureInfo.InvariantCulture, "Input File: {0}", this.InputFile)); - stringBuilder.AppendLine(string.Format(CultureInfo.InvariantCulture, "Is Regex: {0}", this.IsRegex)); - return stringBuilder.ToString(); - } + public override string ToString() + { + StringBuilder stringBuilder = new(); + stringBuilder.AppendLine(); + stringBuilder.AppendLine(string.Format(CultureInfo.InvariantCulture, "Method Name: {0}", this.MethodName)); + stringBuilder.AppendLine(string.Format(CultureInfo.InvariantCulture, "Texture Format: {0}", this.TextureFormat)); + stringBuilder.AppendLine(string.Format(CultureInfo.InvariantCulture, "Texture Type: {0}", this.TextureType)); + stringBuilder.AppendLine(string.Format(CultureInfo.InvariantCulture, "Texture Tool: {0}", this.TextureTool)); + stringBuilder.AppendLine(string.Format(CultureInfo.InvariantCulture, "Input File: {0}", this.InputFile)); + stringBuilder.AppendLine(string.Format(CultureInfo.InvariantCulture, "Is Regex: {0}", this.IsRegex)); + return stringBuilder.ToString(); } } diff --git a/tests/ImageSharp.Textures.Tests/TextureFormats/Decoding/AstcDecoderTests.cs b/tests/ImageSharp.Textures.Tests/TextureFormats/Decoding/AstcDecoderTests.cs new file mode 100644 index 00000000..8d885ef9 --- /dev/null +++ b/tests/ImageSharp.Textures.Tests/TextureFormats/Decoding/AstcDecoderTests.cs @@ -0,0 +1,330 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Textures.TextureFormats.Decoding; + +namespace SixLabors.ImageSharp.Textures.Tests.TextureFormats.Decoding; + +[Trait("Format", "Astc")] +public class AstcDecoderTests +{ + [Theory] + [InlineData(4, 4)] + [InlineData(5, 4)] + [InlineData(5, 5)] + [InlineData(6, 5)] + [InlineData(6, 6)] + [InlineData(8, 5)] + [InlineData(8, 6)] + [InlineData(8, 8)] + [InlineData(10, 5)] + [InlineData(10, 6)] + [InlineData(10, 8)] + [InlineData(10, 10)] + [InlineData(12, 10)] + [InlineData(12, 12)] + public void DecodeBlock_WithValidBlockData_DoesNotThrow(int blockWidth, int blockHeight) + { + byte[] blockData = new byte[AstcDecoder.AstcBlockSize]; // ASTC blocks are always 16 bytes + byte[] decodedPixels = new byte[blockWidth * blockHeight * 4]; + + AstcDecoder.DecodeBlock(blockData, blockWidth, blockHeight, decodedPixels); + + Assert.Equal(blockWidth * blockHeight * 4, decodedPixels.Length); + } + + [Fact] + public void DecodeBlock_WithTooSmallBlockData_ThrowsArgumentException() + { + byte[] blockData = new byte[15]; // Too small + byte[] decodedPixels = new byte[4 * 4 * 4]; + + ArgumentException ex = Assert.Throws(() => + AstcDecoder.DecodeBlock(blockData, 4, 4, decodedPixels)); + + Assert.Contains("16 bytes", ex.Message); + Assert.Contains("blockData", ex.ParamName); + } + + [Fact] + public void DecodeBlock_WithTooLargeBlockData_ThrowsArgumentException() + { + byte[] blockData = new byte[17]; // Too large + byte[] decodedPixels = new byte[4 * 4 * 4]; + + ArgumentException ex = Assert.Throws(() => + AstcDecoder.DecodeBlock(blockData, 4, 4, decodedPixels)); + + Assert.Contains("16 bytes", ex.Message); + Assert.Contains("blockData", ex.ParamName); + } + + [Fact] + public void DecodeBlock_WithEmptyBlockData_ThrowsArgumentException() + { + byte[] blockData = []; + byte[] decodedPixels = new byte[4 * 4 * 4]; + + ArgumentException ex = Assert.Throws(() => + AstcDecoder.DecodeBlock(blockData, 4, 4, decodedPixels)); + + Assert.Contains("blockData", ex.ParamName); + } + + [Fact] + public void DecodeBlock_WithTooSmallOutputBuffer_ThrowsArgumentException() + { + byte[] blockData = new byte[AstcDecoder.AstcBlockSize]; + byte[] decodedPixels = new byte[10]; // Too small for 4x4 block (needs 64 bytes) + + ArgumentException ex = Assert.Throws(() => + AstcDecoder.DecodeBlock(blockData, 4, 4, decodedPixels)); + + Assert.Contains("Output buffer", ex.Message); + Assert.Contains("decodedPixels", ex.ParamName); + } + + [Theory] + [InlineData(3, 3)] + [InlineData(4, 3)] + [InlineData(3, 4)] + [InlineData(7, 7)] + [InlineData(11, 11)] + [InlineData(13, 13)] + [InlineData(16, 16)] + public void DecodeBlock_WithInvalidBlockDimensions_ThrowsArgumentOutOfRangeException(int blockWidth, int blockHeight) + { + byte[] blockData = new byte[AstcDecoder.AstcBlockSize]; + byte[] decodedPixels = new byte[blockWidth * blockHeight * 4]; + + ArgumentOutOfRangeException ex = Assert.Throws(() => + AstcDecoder.DecodeBlock(blockData, blockWidth, blockHeight, decodedPixels)); + + Assert.Contains("Invalid ASTC block dimensions", ex.Message); + } + + [Fact] + public void DecodeBlock_WithZeroBlockWidth_ThrowsArgumentOutOfRangeException() + { + byte[] blockData = new byte[AstcDecoder.AstcBlockSize]; + byte[] decodedPixels = new byte[64]; + + Assert.Throws(() => + AstcDecoder.DecodeBlock(blockData, 0, 4, decodedPixels)); + } + + [Fact] + public void DecodeBlock_WithNegativeBlockWidth_ThrowsArgumentOutOfRangeException() + { + byte[] blockData = new byte[AstcDecoder.AstcBlockSize]; + byte[] decodedPixels = new byte[64]; + + Assert.Throws(() => + AstcDecoder.DecodeBlock(blockData, -1, 4, decodedPixels)); + } + + [Fact] + public void DecodeBlock_WithNegativeBlockHeight_ThrowsArgumentOutOfRangeException() + { + byte[] blockData = new byte[AstcDecoder.AstcBlockSize]; + byte[] decodedPixels = new byte[64]; + + Assert.Throws(() => + AstcDecoder.DecodeBlock(blockData, 4, -1, decodedPixels)); + } + + [Theory] + [InlineData(256, 256, 4, 4)] + [InlineData(512, 512, 8, 8)] + [InlineData(128, 128, 6, 6)] + [InlineData(200, 200, 10, 10)] + public void DecompressImage_WithValidParameters_ReturnsCorrectSizedArray(int width, int height, int blockWidth, int blockHeight) + { + int blocksWide = (width + blockWidth - 1) / blockWidth; + int blocksHigh = (height + blockHeight - 1) / blockHeight; + int totalBlocks = blocksWide * blocksHigh; + byte[] blockData = new byte[totalBlocks * AstcDecoder.AstcBlockSize]; + + byte[] result = AstcDecoder.DecompressImage(blockData, width, height, blockWidth, blockHeight, AstcDecoder.AstcBlockSize); + + Assert.Equal(width * height * 4, result.Length); + } + + [Fact] + public void DecompressImage_WithNullBlockData_ThrowsArgumentNullException() + { + ArgumentNullException ex = Assert.Throws(() => + AstcDecoder.DecompressImage(null, 256, 256, 4, 4, 16)); + + Assert.Equal("blockData", ex.ParamName); + } + + [Fact] + public void DecompressImage_WithZeroWidth_ThrowsArgumentOutOfRangeException() + { + byte[] blockData = new byte[AstcDecoder.AstcBlockSize]; + + ArgumentOutOfRangeException ex = Assert.Throws(() => + AstcDecoder.DecompressImage(blockData, 0, 256, 4, 4, 16)); + + Assert.Equal("width", ex.ParamName); + } + + [Fact] + public void DecompressImage_WithNegativeWidth_ThrowsArgumentOutOfRangeException() + { + byte[] blockData = new byte[AstcDecoder.AstcBlockSize]; + + ArgumentOutOfRangeException ex = Assert.Throws(() => + AstcDecoder.DecompressImage(blockData, -256, 256, 4, 4, 16)); + + Assert.Equal("width", ex.ParamName); + } + + [Fact] + public void DecompressImage_WithZeroHeight_ThrowsArgumentOutOfRangeException() + { + byte[] blockData = new byte[AstcDecoder.AstcBlockSize]; + + ArgumentOutOfRangeException ex = Assert.Throws(() => + AstcDecoder.DecompressImage(blockData, 256, 0, 4, 4, 16)); + + Assert.Equal("height", ex.ParamName); + } + + [Fact] + public void DecompressImage_WithNegativeHeight_ThrowsArgumentOutOfRangeException() + { + byte[] blockData = new byte[AstcDecoder.AstcBlockSize]; + + ArgumentOutOfRangeException ex = Assert.Throws(() => + AstcDecoder.DecompressImage(blockData, 256, -256, 4, 4, 16)); + + Assert.Equal("height", ex.ParamName); + } + + [Theory] + [InlineData(15)] + [InlineData(17)] + [InlineData(32)] + [InlineData(8)] + public void DecompressImage_WithInvalidCompressedBytesPerBlock_ThrowsArgumentOutOfRangeException(byte invalidBytes) + { + byte[] blockData = new byte[invalidBytes * 64]; // 8x8 blocks for 256x256 + + ArgumentOutOfRangeException ex = Assert.Throws(() => + AstcDecoder.DecompressImage(blockData, 256, 256, 4, 4, invalidBytes)); + + Assert.Equal("compressedBytesPerBlock", ex.ParamName); + Assert.Contains("16 bytes", ex.Message); + } + + [Theory] + [InlineData(3, 3)] + [InlineData(4, 3)] + [InlineData(7, 7)] + [InlineData(16, 16)] + public void DecompressImage_WithInvalidBlockDimensions_ThrowsArgumentOutOfRangeException(int blockWidth, int blockHeight) + { + byte[] blockData = new byte[AstcDecoder.AstcBlockSize]; + + ArgumentOutOfRangeException ex = Assert.Throws(() => + AstcDecoder.DecompressImage(blockData, 256, 256, blockWidth, blockHeight, 16)); + + Assert.Contains("Invalid ASTC block dimensions", ex.Message); + } + + [Fact] + public void DecompressImage_WithTooSmallBlockData_ThrowsArgumentException() + { + // For 256x256 with 4x4 blocks, we need 64x64 = 4096 blocks * 16 bytes = 65536 bytes + byte[] blockData = new byte[1000]; // Too small + + ArgumentException ex = Assert.Throws(() => + AstcDecoder.DecompressImage(blockData, 256, 256, 4, 4, 16)); + + Assert.Equal("blockData", ex.ParamName); + Assert.Contains("too small", ex.Message); + } + + [Fact] + public void DecompressImage_WithEmptyBlockData_ThrowsArgumentException() + { + byte[] blockData = []; + + ArgumentException ex = Assert.Throws(() => + AstcDecoder.DecompressImage(blockData, 256, 256, 4, 4, 16)); + + Assert.Equal("blockData", ex.ParamName); + } + + [Theory] + [InlineData(257, 256)] // Width not multiple of block size + [InlineData(256, 257)] // Height not multiple of block size + [InlineData(255, 255)] // Both not multiples + [InlineData(100, 100)] // Different non-multiples + public void DecompressImage_WithNonMultipleImageSizes_ReturnExpectedSize(int width, int height) + { + int blockWidth = 4; + int blockHeight = 4; + int blocksWide = (width + blockWidth - 1) / blockWidth; + int blocksHigh = (height + blockHeight - 1) / blockHeight; + int totalBlocks = blocksWide * blocksHigh; + byte[] blockData = new byte[totalBlocks * 16]; + + byte[] result = AstcDecoder.DecompressImage(blockData, width, height, blockWidth, blockHeight, 16); + + Assert.Equal(width * height * 4, result.Length); + } + + [Theory] + [InlineData(1, 1)] + [InlineData(2, 2)] + [InlineData(3, 3)] + public void DecompressImage_WithVerySmallImages_ReturnExpectedSize(int width, int height) + { + // Even tiny images need at least one block + byte[] blockData = new byte[AstcDecoder.AstcBlockSize]; + + byte[] result = AstcDecoder.DecompressImage(blockData, width, height, 4, 4, 16); + + Assert.Equal(width * height * 4, result.Length); + } + + [Theory] + [InlineData(4096, 4096, 4, 4)] + [InlineData(2048, 2048, 8, 8)] + public void DecompressImage_WithLargeImages_ReturnExpectedSize(int width, int height, int blockWidth, int blockHeight) + { + int blocksWide = (width + blockWidth - 1) / blockWidth; + int blocksHigh = (height + blockHeight - 1) / blockHeight; + int totalBlocks = blocksWide * blocksHigh; + byte[] blockData = new byte[totalBlocks * 16]; + + byte[] result = AstcDecoder.DecompressImage(blockData, width, height, blockWidth, blockHeight, 16); + + Assert.Equal(width * height * 4, result.Length); + } + + [Fact] + public void DecompressImage_WithExactBlockDataSize_ReturnExpectedSize() + { + // 256x256 with 4x4 blocks = 64x64 blocks = 4096 blocks * 16 bytes = 65536 bytes + byte[] blockData = new byte[65536]; + + byte[] result = AstcDecoder.DecompressImage(blockData, 256, 256, 4, 4, 16); + + Assert.Equal(256 * 256 * 4, result.Length); + } + + [Fact] + public void DecompressImage_WithExtraBlockData_ReturnExpectedSize() + { + // More data than needed should work (extra data ignored) + byte[] blockData = new byte[100000]; + + byte[] result = AstcDecoder.DecompressImage(blockData, 256, 256, 4, 4, 16); + + Assert.Equal(256 * 256 * 4, result.Length); + } +} diff --git a/tests/Images/Input/Astc/Expected/A.png b/tests/Images/Input/Astc/Expected/A.png new file mode 100644 index 00000000..7d1a3678 --- /dev/null +++ b/tests/Images/Input/Astc/Expected/A.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a31a312bc20c9696a17e2857526b42b0e9b7d024d624fe91a417b12151a9915b +size 53774 diff --git a/tests/Images/Input/Astc/Expected/FlightHelmet_baseColor.png b/tests/Images/Input/Astc/Expected/FlightHelmet_baseColor.png new file mode 100644 index 00000000..121fa21d --- /dev/null +++ b/tests/Images/Input/Astc/Expected/FlightHelmet_baseColor.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8e5567daf7a3ac83f6ebc04605f3605ac17c19a153b6b8aadd2596690209c34c +size 2778518 diff --git a/tests/Images/Input/Astc/Expected/GoldenGateBridge3/filelist.txt b/tests/Images/Input/Astc/Expected/GoldenGateBridge3/filelist.txt new file mode 100644 index 00000000..56558836 --- /dev/null +++ b/tests/Images/Input/Astc/Expected/GoldenGateBridge3/filelist.txt @@ -0,0 +1,6 @@ +posx.jpg +negx.jpg +posy.jpg +negy.jpg +posz.jpg +negz.jpg diff --git a/tests/Images/Input/Astc/Expected/GoldenGateBridge3/negx.jpg b/tests/Images/Input/Astc/Expected/GoldenGateBridge3/negx.jpg new file mode 100644 index 00000000..4b420e53 --- /dev/null +++ b/tests/Images/Input/Astc/Expected/GoldenGateBridge3/negx.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:681d4598e743ff5ada3e8679c7241b0a338afedee094289fbc88818c44c37c97 +size 743985 diff --git a/tests/Images/Input/Astc/Expected/GoldenGateBridge3/negy.jpg b/tests/Images/Input/Astc/Expected/GoldenGateBridge3/negy.jpg new file mode 100644 index 00000000..ce1c8ac5 --- /dev/null +++ b/tests/Images/Input/Astc/Expected/GoldenGateBridge3/negy.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a0fe09207f833fd7c9939ebaa28921da832f48e5b502135ff4e5f76e98bd2fd3 +size 586742 diff --git a/tests/Images/Input/Astc/Expected/GoldenGateBridge3/negz.jpg b/tests/Images/Input/Astc/Expected/GoldenGateBridge3/negz.jpg new file mode 100644 index 00000000..361149b8 --- /dev/null +++ b/tests/Images/Input/Astc/Expected/GoldenGateBridge3/negz.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1eb32c8055b52ddf230bfe4846f78fa4ce03600541e7d3569d893dda0c14d3d7 +size 646123 diff --git a/tests/Images/Input/Astc/Expected/GoldenGateBridge3/posx.jpg b/tests/Images/Input/Astc/Expected/GoldenGateBridge3/posx.jpg new file mode 100644 index 00000000..3ae03904 --- /dev/null +++ b/tests/Images/Input/Astc/Expected/GoldenGateBridge3/posx.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1a90ffea9ac8bf9bbfab93aa25ff23222a84decdc7d96c58bc6edbb1e9c9cce7 +size 721206 diff --git a/tests/Images/Input/Astc/Expected/GoldenGateBridge3/posy.jpg b/tests/Images/Input/Astc/Expected/GoldenGateBridge3/posy.jpg new file mode 100644 index 00000000..49fd8974 --- /dev/null +++ b/tests/Images/Input/Astc/Expected/GoldenGateBridge3/posy.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a8e77108e821e7f11e16937152a700b6995ee996b7fedb50a4d6e9ee7d3f04db +size 674410 diff --git a/tests/Images/Input/Astc/Expected/GoldenGateBridge3/posz.jpg b/tests/Images/Input/Astc/Expected/GoldenGateBridge3/posz.jpg new file mode 100644 index 00000000..227469be --- /dev/null +++ b/tests/Images/Input/Astc/Expected/GoldenGateBridge3/posz.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d6f08f424f2c93f45c1525f756b1365c0a89651ca107d5fc9649b4370e25bb4b +size 616391 diff --git a/tests/Images/Input/Astc/Expected/GoldenGateBridge3/readme.txt b/tests/Images/Input/Astc/Expected/GoldenGateBridge3/readme.txt new file mode 100644 index 00000000..8b404c27 --- /dev/null +++ b/tests/Images/Input/Astc/Expected/GoldenGateBridge3/readme.txt @@ -0,0 +1,13 @@ +Author +====== + +This is the work of Emil Persson, aka Humus. +http://www.humus.name + + + +License +======= + +This work is licensed under a Creative Commons Attribution 3.0 Unported License. +http://creativecommons.org/licenses/by/3.0/ diff --git a/tests/Images/Input/Astc/Expected/Iron_Bars/Iron_Bars_001_Opacity.jpg b/tests/Images/Input/Astc/Expected/Iron_Bars/Iron_Bars_001_Opacity.jpg new file mode 100644 index 00000000..56f50812 --- /dev/null +++ b/tests/Images/Input/Astc/Expected/Iron_Bars/Iron_Bars_001_Opacity.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d7824d03409aa2059cf00de8c319b56df3a6805d9f76ea7a7c6242da0ff101ed +size 189930 diff --git a/tests/Images/Input/Astc/Expected/Iron_Bars/Iron_Bars_001_ambientOcclusion.jpg b/tests/Images/Input/Astc/Expected/Iron_Bars/Iron_Bars_001_ambientOcclusion.jpg new file mode 100644 index 00000000..88b5d484 --- /dev/null +++ b/tests/Images/Input/Astc/Expected/Iron_Bars/Iron_Bars_001_ambientOcclusion.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fedb7076a37938b044428ac5ac608ff6f665358773c51c5412a8d22e9d1175e0 +size 195262 diff --git a/tests/Images/Input/Astc/Expected/Iron_Bars/Iron_Bars_001_basecolor.jpg b/tests/Images/Input/Astc/Expected/Iron_Bars/Iron_Bars_001_basecolor.jpg new file mode 100644 index 00000000..1a2950f1 --- /dev/null +++ b/tests/Images/Input/Astc/Expected/Iron_Bars/Iron_Bars_001_basecolor.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:35a10620acf7e73ffb55132b9e814ddbbaa45df1c445eefcef06b7458cb29967 +size 108801 diff --git a/tests/Images/Input/Astc/Expected/Iron_Bars/Iron_Bars_001_height.png b/tests/Images/Input/Astc/Expected/Iron_Bars/Iron_Bars_001_height.png new file mode 100644 index 00000000..a0821106 --- /dev/null +++ b/tests/Images/Input/Astc/Expected/Iron_Bars/Iron_Bars_001_height.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d2d24186ce5a5b9c718ad7b6365a7e22680ce85abfdbed8a37ad9e4205d4d012 +size 456564 diff --git a/tests/Images/Input/Astc/Expected/Iron_Bars/Iron_Bars_001_normal.jpg b/tests/Images/Input/Astc/Expected/Iron_Bars/Iron_Bars_001_normal.jpg new file mode 100644 index 00000000..27c64184 --- /dev/null +++ b/tests/Images/Input/Astc/Expected/Iron_Bars/Iron_Bars_001_normal.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:be3169943652ad478fe1f38569feb4aa1da0ff6db37a4cd22fec063fe8aea50f +size 329643 diff --git a/tests/Images/Input/Astc/Expected/Iron_Bars/Iron_Bars_001_normal_unnormalized.png b/tests/Images/Input/Astc/Expected/Iron_Bars/Iron_Bars_001_normal_unnormalized.png new file mode 100644 index 00000000..82ffa417 --- /dev/null +++ b/tests/Images/Input/Astc/Expected/Iron_Bars/Iron_Bars_001_normal_unnormalized.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d525152f126ca682ac2ed54ed01a28300c88072582bc005d0846f556d99fbe24 +size 2143908 diff --git a/tests/Images/Input/Astc/Expected/Iron_Bars/Iron_Bars_001_roughness.jpg b/tests/Images/Input/Astc/Expected/Iron_Bars/Iron_Bars_001_roughness.jpg new file mode 100644 index 00000000..a48d7d33 --- /dev/null +++ b/tests/Images/Input/Astc/Expected/Iron_Bars/Iron_Bars_001_roughness.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:808018bf8dde084ac3e23493c3e26ea7c85335551cab20681454f6702d29eafc +size 118946 diff --git a/tests/Images/Input/Astc/Expected/Iron_Bars/readme.txt b/tests/Images/Input/Astc/Expected/Iron_Bars/readme.txt new file mode 100644 index 00000000..649d4903 --- /dev/null +++ b/tests/Images/Input/Astc/Expected/Iron_Bars/readme.txt @@ -0,0 +1,11 @@ +Author +====== + +This is the work of Katsuagi from https://3dtextures.me. + + +License +======= + +This work is licensed under a Creative Commons Zero 1.0 +http://creativecommons.org/publicdomain/zero/1.0/legalcode diff --git a/tests/Images/Input/Astc/Expected/LA.png b/tests/Images/Input/Astc/Expected/LA.png new file mode 100644 index 00000000..85750bea --- /dev/null +++ b/tests/Images/Input/Astc/Expected/LA.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0c8c1a3ec4c9dce1a7082229f58b596fc4030b6e92a184399f2c2e0abdd0a6ce +size 6996 diff --git a/tests/Images/Input/Astc/Expected/R.png b/tests/Images/Input/Astc/Expected/R.png new file mode 100644 index 00000000..0293e900 --- /dev/null +++ b/tests/Images/Input/Astc/Expected/R.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8247b0f7fe68e3e79383be1e54a10e8c2ac9227c0d938f628604de123799211e +size 3857 diff --git a/tests/Images/Input/Astc/Expected/RG.png b/tests/Images/Input/Astc/Expected/RG.png new file mode 100644 index 00000000..5b70f533 --- /dev/null +++ b/tests/Images/Input/Astc/Expected/RG.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d62d83441c65a4e060fa5574ae485d71bd50ddad08ae201aa3bd286ae58fb230 +size 5630 diff --git a/tests/Images/Input/Astc/Expected/Yokohama3/filelist.txt b/tests/Images/Input/Astc/Expected/Yokohama3/filelist.txt new file mode 100644 index 00000000..56558836 --- /dev/null +++ b/tests/Images/Input/Astc/Expected/Yokohama3/filelist.txt @@ -0,0 +1,6 @@ +posx.jpg +negx.jpg +posy.jpg +negy.jpg +posz.jpg +negz.jpg diff --git a/tests/Images/Input/Astc/Expected/Yokohama3/negx.jpg b/tests/Images/Input/Astc/Expected/Yokohama3/negx.jpg new file mode 100644 index 00000000..e6140aa5 --- /dev/null +++ b/tests/Images/Input/Astc/Expected/Yokohama3/negx.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b29bbd856363b3095f3447a1f47419b8a4d94032e3b8e69377a4f002b064d9b9 +size 1039406 diff --git a/tests/Images/Input/Astc/Expected/Yokohama3/negy.jpg b/tests/Images/Input/Astc/Expected/Yokohama3/negy.jpg new file mode 100644 index 00000000..97c86925 --- /dev/null +++ b/tests/Images/Input/Astc/Expected/Yokohama3/negy.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:63401877a11eeef4016b947e46554f1e6e61ad60db7d5e7bebdd378d1751bb1d +size 994070 diff --git a/tests/Images/Input/Astc/Expected/Yokohama3/negz.jpg b/tests/Images/Input/Astc/Expected/Yokohama3/negz.jpg new file mode 100644 index 00000000..b8f54d55 --- /dev/null +++ b/tests/Images/Input/Astc/Expected/Yokohama3/negz.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b52d48844e642f93443922088143cb466a7b46c9b3d31bb2b6a3f4b91c68787c +size 911570 diff --git a/tests/Images/Input/Astc/Expected/Yokohama3/posx.jpg b/tests/Images/Input/Astc/Expected/Yokohama3/posx.jpg new file mode 100644 index 00000000..6682af4f --- /dev/null +++ b/tests/Images/Input/Astc/Expected/Yokohama3/posx.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1d28f2c6d384e919b93cf94d625677f3231032e691fa3832926f99f8752a8c56 +size 1036660 diff --git a/tests/Images/Input/Astc/Expected/Yokohama3/posy.jpg b/tests/Images/Input/Astc/Expected/Yokohama3/posy.jpg new file mode 100644 index 00000000..7bdeadbd --- /dev/null +++ b/tests/Images/Input/Astc/Expected/Yokohama3/posy.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:823f80f5dafac7c2dccf0b192867433e4e0f27d195541da02e99684ec28f79a4 +size 631474 diff --git a/tests/Images/Input/Astc/Expected/Yokohama3/posz.jpg b/tests/Images/Input/Astc/Expected/Yokohama3/posz.jpg new file mode 100644 index 00000000..7613d5ee --- /dev/null +++ b/tests/Images/Input/Astc/Expected/Yokohama3/posz.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:76cfa0cc922ac66794e5939b1f000edc051fc84200ef4663c4de02766e2f45d1 +size 871498 diff --git a/tests/Images/Input/Astc/Expected/Yokohama3/readme.txt b/tests/Images/Input/Astc/Expected/Yokohama3/readme.txt new file mode 100644 index 00000000..8b404c27 --- /dev/null +++ b/tests/Images/Input/Astc/Expected/Yokohama3/readme.txt @@ -0,0 +1,13 @@ +Author +====== + +This is the work of Emil Persson, aka Humus. +http://www.humus.name + + + +License +======= + +This work is licensed under a Creative Commons Attribution 3.0 Unported License. +http://creativecommons.org/licenses/by/3.0/ diff --git a/tests/Images/Input/Astc/Expected/atlas_small_4x4.bmp b/tests/Images/Input/Astc/Expected/atlas_small_4x4.bmp new file mode 100644 index 00000000..27dbb5ce --- /dev/null +++ b/tests/Images/Input/Astc/Expected/atlas_small_4x4.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:960c183b5a1f139466dea2d3196bc58b426935545bd203793d2a745fd8dbc29e +size 262282 diff --git a/tests/Images/Input/Astc/Expected/atlas_small_5x5.bmp b/tests/Images/Input/Astc/Expected/atlas_small_5x5.bmp new file mode 100644 index 00000000..9dbc9152 --- /dev/null +++ b/tests/Images/Input/Astc/Expected/atlas_small_5x5.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:959bce6386ac4ee035cfbbbdbf9537e3f2a5ebd5795fbd531dea301a4c6f74ba +size 262282 diff --git a/tests/Images/Input/Astc/Expected/atlas_small_6x6.bmp b/tests/Images/Input/Astc/Expected/atlas_small_6x6.bmp new file mode 100644 index 00000000..841123a8 --- /dev/null +++ b/tests/Images/Input/Astc/Expected/atlas_small_6x6.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6e70d5d7a9eb7c280f397eefe5c71a78530508fde1caebabe9fda39a91aa5847 +size 262282 diff --git a/tests/Images/Input/Astc/Expected/atlas_small_8x8.bmp b/tests/Images/Input/Astc/Expected/atlas_small_8x8.bmp new file mode 100644 index 00000000..719856d8 --- /dev/null +++ b/tests/Images/Input/Astc/Expected/atlas_small_8x8.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b3ca7675d2e01e56fc63f2a2b59b2c852d2b5793e21a2e1125ccf81c8cdf3431 +size 262282 diff --git a/tests/Images/Input/Astc/Expected/footprint_10x10.bmp b/tests/Images/Input/Astc/Expected/footprint_10x10.bmp new file mode 100644 index 00000000..6be39794 --- /dev/null +++ b/tests/Images/Input/Astc/Expected/footprint_10x10.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:713880d96d562a92648bb306d57cddfac45805e99853dec6c7cf7ef461cfd795 +size 3210 diff --git a/tests/Images/Input/Astc/Expected/footprint_10x5.bmp b/tests/Images/Input/Astc/Expected/footprint_10x5.bmp new file mode 100644 index 00000000..fc7cd4e9 --- /dev/null +++ b/tests/Images/Input/Astc/Expected/footprint_10x5.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f661da916564a6c8dee8cd6ae6961c95a48416788cf0f52649a31b5ff36bafb5 +size 3210 diff --git a/tests/Images/Input/Astc/Expected/footprint_10x6.bmp b/tests/Images/Input/Astc/Expected/footprint_10x6.bmp new file mode 100644 index 00000000..e41db3ff --- /dev/null +++ b/tests/Images/Input/Astc/Expected/footprint_10x6.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fa618100f436836c42104732573952180f39eafe3c64b87a99b264fb284487dc +size 3210 diff --git a/tests/Images/Input/Astc/Expected/footprint_10x8.bmp b/tests/Images/Input/Astc/Expected/footprint_10x8.bmp new file mode 100644 index 00000000..33c468a4 --- /dev/null +++ b/tests/Images/Input/Astc/Expected/footprint_10x8.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6be48ee1e53b3cc868a23dd3ca9510f8af3f8f47ae1417aee95daed15581a4e3 +size 3210 diff --git a/tests/Images/Input/Astc/Expected/footprint_12x10.bmp b/tests/Images/Input/Astc/Expected/footprint_12x10.bmp new file mode 100644 index 00000000..2e863d5e --- /dev/null +++ b/tests/Images/Input/Astc/Expected/footprint_12x10.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f753526bd5c19656a44b837fac0c535470a7c5b9bef0c4cdbc8b3987fdf348c4 +size 3210 diff --git a/tests/Images/Input/Astc/Expected/footprint_12x12.bmp b/tests/Images/Input/Astc/Expected/footprint_12x12.bmp new file mode 100644 index 00000000..3a6977ff --- /dev/null +++ b/tests/Images/Input/Astc/Expected/footprint_12x12.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c7314cdfe475599d2c55645fad6b51379a129447931ae6b82538711942ecbee4 +size 3210 diff --git a/tests/Images/Input/Astc/Expected/footprint_4x4.bmp b/tests/Images/Input/Astc/Expected/footprint_4x4.bmp new file mode 100644 index 00000000..c5d83d2e --- /dev/null +++ b/tests/Images/Input/Astc/Expected/footprint_4x4.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9a16fbf67b4bb9a0ec3f3e3b78006f39610f7869e0cdb4bd945d660957d1090e +size 3210 diff --git a/tests/Images/Input/Astc/Expected/footprint_5x4.bmp b/tests/Images/Input/Astc/Expected/footprint_5x4.bmp new file mode 100644 index 00000000..9dd29e11 --- /dev/null +++ b/tests/Images/Input/Astc/Expected/footprint_5x4.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dd3a8f13222fa52a43678b3d4269ac5dee826f9f2ffb69061650e5c4300fb1e6 +size 3210 diff --git a/tests/Images/Input/Astc/Expected/footprint_5x5.bmp b/tests/Images/Input/Astc/Expected/footprint_5x5.bmp new file mode 100644 index 00000000..6f9543fc --- /dev/null +++ b/tests/Images/Input/Astc/Expected/footprint_5x5.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:273af3da91ccbfac3de9e04df9582e13808feda0fb263f52389238b96165105d +size 3210 diff --git a/tests/Images/Input/Astc/Expected/footprint_6x5.bmp b/tests/Images/Input/Astc/Expected/footprint_6x5.bmp new file mode 100644 index 00000000..8426e2c2 --- /dev/null +++ b/tests/Images/Input/Astc/Expected/footprint_6x5.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2623e574e64e88268571ec139aea89607fa7a4b149bb740cdcd37c55aac0d0b6 +size 3210 diff --git a/tests/Images/Input/Astc/Expected/footprint_6x6.bmp b/tests/Images/Input/Astc/Expected/footprint_6x6.bmp new file mode 100644 index 00000000..a31d92ef --- /dev/null +++ b/tests/Images/Input/Astc/Expected/footprint_6x6.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b31d247567aa583f7c6cd1b0dc3ec311f97a2fe7893a2c4d82916786717ac622 +size 3210 diff --git a/tests/Images/Input/Astc/Expected/footprint_8x5.bmp b/tests/Images/Input/Astc/Expected/footprint_8x5.bmp new file mode 100644 index 00000000..6cb11fdf --- /dev/null +++ b/tests/Images/Input/Astc/Expected/footprint_8x5.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6d87633a26957084bec6973eb41f8946c5b7bccf2924e1c90c1690b4f926cf74 +size 3210 diff --git a/tests/Images/Input/Astc/Expected/footprint_8x6.bmp b/tests/Images/Input/Astc/Expected/footprint_8x6.bmp new file mode 100644 index 00000000..f0de8688 --- /dev/null +++ b/tests/Images/Input/Astc/Expected/footprint_8x6.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6073c67e6b13bdc2688a2217119eb6bdee85f7507ce98b2d466fbf8db90b37b1 +size 3210 diff --git a/tests/Images/Input/Astc/Expected/footprint_8x8.bmp b/tests/Images/Input/Astc/Expected/footprint_8x8.bmp new file mode 100644 index 00000000..d1f867de --- /dev/null +++ b/tests/Images/Input/Astc/Expected/footprint_8x8.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c2f0ec842b80a95bffc742c66b2e19b85faf7736f0737f9a9da86ae965c839de +size 3210 diff --git a/tests/Images/Input/Astc/Expected/rgb_12x12.bmp b/tests/Images/Input/Astc/Expected/rgb_12x12.bmp new file mode 100644 index 00000000..29f43633 --- /dev/null +++ b/tests/Images/Input/Astc/Expected/rgb_12x12.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1d70f50efa5c0bfc990e024be872ed270f43874e34a43a4d7d8a6fbfbb7f1ddc +size 193674 diff --git a/tests/Images/Input/Astc/Expected/rgb_4x4.bmp b/tests/Images/Input/Astc/Expected/rgb_4x4.bmp new file mode 100644 index 00000000..3874c6d0 --- /dev/null +++ b/tests/Images/Input/Astc/Expected/rgb_4x4.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4ddf8ba01848174bdc1eadfaa4da351e9a41cf8acc238813dfd260219423f785 +size 193674 diff --git a/tests/Images/Input/Astc/Expected/rgb_5x4.bmp b/tests/Images/Input/Astc/Expected/rgb_5x4.bmp new file mode 100644 index 00000000..cf14c284 --- /dev/null +++ b/tests/Images/Input/Astc/Expected/rgb_5x4.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:08d8d5d3181d8a12ec47ed00c477f6fe9f5d93069a0bf2ebfe1b9ce17b2f8f67 +size 193674 diff --git a/tests/Images/Input/Astc/Expected/rgb_6x6.bmp b/tests/Images/Input/Astc/Expected/rgb_6x6.bmp new file mode 100644 index 00000000..1e9dc2e3 --- /dev/null +++ b/tests/Images/Input/Astc/Expected/rgb_6x6.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:54f44a68a25232bd4624400e93385ac3cc332a3cda293cb617911e42630d889a +size 193674 diff --git a/tests/Images/Input/Astc/Expected/rgb_8x8.bmp b/tests/Images/Input/Astc/Expected/rgb_8x8.bmp new file mode 100644 index 00000000..9ec02dd9 --- /dev/null +++ b/tests/Images/Input/Astc/Expected/rgb_8x8.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d612273927b74c4b07ee99ffa5028572b28689fc23f24c4c32ac40c3d6b7a7e6 +size 193674 diff --git a/tests/Images/Input/Astc/HDR/HDR-A-1x1.astc b/tests/Images/Input/Astc/HDR/HDR-A-1x1.astc new file mode 100644 index 00000000..57d5e897 Binary files /dev/null and b/tests/Images/Input/Astc/HDR/HDR-A-1x1.astc differ diff --git a/tests/Images/Input/Astc/HDR/HDR-A-1x1.exr b/tests/Images/Input/Astc/HDR/HDR-A-1x1.exr new file mode 100644 index 00000000..c1f5f61c --- /dev/null +++ b/tests/Images/Input/Astc/HDR/HDR-A-1x1.exr @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5e09a64448ffd3e0a4da4b4c46836242d1446ecdfcd5a15f5feb6a17ba9b4100 +size 335 diff --git a/tests/Images/Input/Astc/HDR/LDR-A-1x1.astc b/tests/Images/Input/Astc/HDR/LDR-A-1x1.astc new file mode 100644 index 00000000..2786dedb Binary files /dev/null and b/tests/Images/Input/Astc/HDR/LDR-A-1x1.astc differ diff --git a/tests/Images/Input/Astc/HDR/LDR-A-1x1.png b/tests/Images/Input/Astc/HDR/LDR-A-1x1.png new file mode 100644 index 00000000..e1eb6355 --- /dev/null +++ b/tests/Images/Input/Astc/HDR/LDR-A-1x1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:335e2a1d19f3d572d5664c899853f4e7eb9caa595ea8888e5f2cddb85f64c9da +size 509 diff --git a/tests/Images/Input/Astc/HDR/hdr-complex.exr b/tests/Images/Input/Astc/HDR/hdr-complex.exr new file mode 100644 index 00000000..9a3d6700 --- /dev/null +++ b/tests/Images/Input/Astc/HDR/hdr-complex.exr @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e6a16a419ec8f487be26cb223c3770923a03e22d9327ae2903c1888761217107 +size 825 diff --git a/tests/Images/Input/Astc/HDR/hdr-tile.astc b/tests/Images/Input/Astc/HDR/hdr-tile.astc new file mode 100644 index 00000000..aa215761 Binary files /dev/null and b/tests/Images/Input/Astc/HDR/hdr-tile.astc differ diff --git a/tests/Images/Input/Astc/HDR/hdr.exr b/tests/Images/Input/Astc/HDR/hdr.exr new file mode 100644 index 00000000..fd39b9b5 --- /dev/null +++ b/tests/Images/Input/Astc/HDR/hdr.exr @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:90aff5c37cdfd98c9ffb3b1b5aa082ebf6f420c0bd3bceeee4b554d18465f030 +size 971 diff --git a/tests/Images/Input/Astc/HDR/hdr.hdr b/tests/Images/Input/Astc/HDR/hdr.hdr new file mode 100644 index 00000000..abf4097c Binary files /dev/null and b/tests/Images/Input/Astc/HDR/hdr.hdr differ diff --git a/tests/Images/Input/Astc/HDR/ldr-tile.astc b/tests/Images/Input/Astc/HDR/ldr-tile.astc new file mode 100644 index 00000000..ab785780 Binary files /dev/null and b/tests/Images/Input/Astc/HDR/ldr-tile.astc differ diff --git a/tests/Images/Input/Astc/Input/atlas_small_4x4.astc b/tests/Images/Input/Astc/Input/atlas_small_4x4.astc new file mode 100644 index 00000000..86ae0471 Binary files /dev/null and b/tests/Images/Input/Astc/Input/atlas_small_4x4.astc differ diff --git a/tests/Images/Input/Astc/Input/atlas_small_5x5.astc b/tests/Images/Input/Astc/Input/atlas_small_5x5.astc new file mode 100644 index 00000000..2831c90e Binary files /dev/null and b/tests/Images/Input/Astc/Input/atlas_small_5x5.astc differ diff --git a/tests/Images/Input/Astc/Input/atlas_small_6x6.astc b/tests/Images/Input/Astc/Input/atlas_small_6x6.astc new file mode 100644 index 00000000..0778f761 Binary files /dev/null and b/tests/Images/Input/Astc/Input/atlas_small_6x6.astc differ diff --git a/tests/Images/Input/Astc/Input/atlas_small_8x8.astc b/tests/Images/Input/Astc/Input/atlas_small_8x8.astc new file mode 100644 index 00000000..a8805f28 Binary files /dev/null and b/tests/Images/Input/Astc/Input/atlas_small_8x8.astc differ diff --git a/tests/Images/Input/Astc/Input/checkerboard.astc b/tests/Images/Input/Astc/Input/checkerboard.astc new file mode 100644 index 00000000..79acdca3 Binary files /dev/null and b/tests/Images/Input/Astc/Input/checkerboard.astc differ diff --git a/tests/Images/Input/Astc/Input/checkered_10.astc b/tests/Images/Input/Astc/Input/checkered_10.astc new file mode 100644 index 00000000..e3b19657 Binary files /dev/null and b/tests/Images/Input/Astc/Input/checkered_10.astc differ diff --git a/tests/Images/Input/Astc/Input/checkered_11.astc b/tests/Images/Input/Astc/Input/checkered_11.astc new file mode 100644 index 00000000..c80c6a46 Binary files /dev/null and b/tests/Images/Input/Astc/Input/checkered_11.astc differ diff --git a/tests/Images/Input/Astc/Input/checkered_12.astc b/tests/Images/Input/Astc/Input/checkered_12.astc new file mode 100644 index 00000000..a82583a9 Binary files /dev/null and b/tests/Images/Input/Astc/Input/checkered_12.astc differ diff --git a/tests/Images/Input/Astc/Input/checkered_4.astc b/tests/Images/Input/Astc/Input/checkered_4.astc new file mode 100644 index 00000000..ac607164 Binary files /dev/null and b/tests/Images/Input/Astc/Input/checkered_4.astc differ diff --git a/tests/Images/Input/Astc/Input/checkered_5.astc b/tests/Images/Input/Astc/Input/checkered_5.astc new file mode 100644 index 00000000..0389c53d Binary files /dev/null and b/tests/Images/Input/Astc/Input/checkered_5.astc differ diff --git a/tests/Images/Input/Astc/Input/checkered_6.astc b/tests/Images/Input/Astc/Input/checkered_6.astc new file mode 100644 index 00000000..210dd340 Binary files /dev/null and b/tests/Images/Input/Astc/Input/checkered_6.astc differ diff --git a/tests/Images/Input/Astc/Input/checkered_7.astc b/tests/Images/Input/Astc/Input/checkered_7.astc new file mode 100644 index 00000000..8ea10f84 Binary files /dev/null and b/tests/Images/Input/Astc/Input/checkered_7.astc differ diff --git a/tests/Images/Input/Astc/Input/checkered_8.astc b/tests/Images/Input/Astc/Input/checkered_8.astc new file mode 100644 index 00000000..3708882d Binary files /dev/null and b/tests/Images/Input/Astc/Input/checkered_8.astc differ diff --git a/tests/Images/Input/Astc/Input/checkered_9.astc b/tests/Images/Input/Astc/Input/checkered_9.astc new file mode 100644 index 00000000..b5c962a1 Binary files /dev/null and b/tests/Images/Input/Astc/Input/checkered_9.astc differ diff --git a/tests/Images/Input/Astc/Input/footprint_10x10.astc b/tests/Images/Input/Astc/Input/footprint_10x10.astc new file mode 100644 index 00000000..6799cd08 Binary files /dev/null and b/tests/Images/Input/Astc/Input/footprint_10x10.astc differ diff --git a/tests/Images/Input/Astc/Input/footprint_10x5.astc b/tests/Images/Input/Astc/Input/footprint_10x5.astc new file mode 100644 index 00000000..cbfe4f26 Binary files /dev/null and b/tests/Images/Input/Astc/Input/footprint_10x5.astc differ diff --git a/tests/Images/Input/Astc/Input/footprint_10x6.astc b/tests/Images/Input/Astc/Input/footprint_10x6.astc new file mode 100644 index 00000000..53e37b4a Binary files /dev/null and b/tests/Images/Input/Astc/Input/footprint_10x6.astc differ diff --git a/tests/Images/Input/Astc/Input/footprint_10x8.astc b/tests/Images/Input/Astc/Input/footprint_10x8.astc new file mode 100644 index 00000000..b9613e58 Binary files /dev/null and b/tests/Images/Input/Astc/Input/footprint_10x8.astc differ diff --git a/tests/Images/Input/Astc/Input/footprint_12x10.astc b/tests/Images/Input/Astc/Input/footprint_12x10.astc new file mode 100644 index 00000000..54f59193 Binary files /dev/null and b/tests/Images/Input/Astc/Input/footprint_12x10.astc differ diff --git a/tests/Images/Input/Astc/Input/footprint_12x12.astc b/tests/Images/Input/Astc/Input/footprint_12x12.astc new file mode 100644 index 00000000..d94d0a6c Binary files /dev/null and b/tests/Images/Input/Astc/Input/footprint_12x12.astc differ diff --git a/tests/Images/Input/Astc/Input/footprint_4x4.astc b/tests/Images/Input/Astc/Input/footprint_4x4.astc new file mode 100644 index 00000000..fdf3cd6b Binary files /dev/null and b/tests/Images/Input/Astc/Input/footprint_4x4.astc differ diff --git a/tests/Images/Input/Astc/Input/footprint_5x4.astc b/tests/Images/Input/Astc/Input/footprint_5x4.astc new file mode 100644 index 00000000..06830594 Binary files /dev/null and b/tests/Images/Input/Astc/Input/footprint_5x4.astc differ diff --git a/tests/Images/Input/Astc/Input/footprint_5x5.astc b/tests/Images/Input/Astc/Input/footprint_5x5.astc new file mode 100644 index 00000000..03c3395a Binary files /dev/null and b/tests/Images/Input/Astc/Input/footprint_5x5.astc differ diff --git a/tests/Images/Input/Astc/Input/footprint_6x5.astc b/tests/Images/Input/Astc/Input/footprint_6x5.astc new file mode 100644 index 00000000..d0d455d0 Binary files /dev/null and b/tests/Images/Input/Astc/Input/footprint_6x5.astc differ diff --git a/tests/Images/Input/Astc/Input/footprint_6x6.astc b/tests/Images/Input/Astc/Input/footprint_6x6.astc new file mode 100644 index 00000000..47e038ef Binary files /dev/null and b/tests/Images/Input/Astc/Input/footprint_6x6.astc differ diff --git a/tests/Images/Input/Astc/Input/footprint_8x5.astc b/tests/Images/Input/Astc/Input/footprint_8x5.astc new file mode 100644 index 00000000..4ef49d65 Binary files /dev/null and b/tests/Images/Input/Astc/Input/footprint_8x5.astc differ diff --git a/tests/Images/Input/Astc/Input/footprint_8x6.astc b/tests/Images/Input/Astc/Input/footprint_8x6.astc new file mode 100644 index 00000000..11355095 Binary files /dev/null and b/tests/Images/Input/Astc/Input/footprint_8x6.astc differ diff --git a/tests/Images/Input/Astc/Input/footprint_8x8.astc b/tests/Images/Input/Astc/Input/footprint_8x8.astc new file mode 100644 index 00000000..bdeb9c9b Binary files /dev/null and b/tests/Images/Input/Astc/Input/footprint_8x8.astc differ diff --git a/tests/Images/Input/Astc/Input/rgb_12x12.astc b/tests/Images/Input/Astc/Input/rgb_12x12.astc new file mode 100644 index 00000000..cc0fc3d3 Binary files /dev/null and b/tests/Images/Input/Astc/Input/rgb_12x12.astc differ diff --git a/tests/Images/Input/Astc/Input/rgb_4x4.astc b/tests/Images/Input/Astc/Input/rgb_4x4.astc new file mode 100644 index 00000000..84527d3a Binary files /dev/null and b/tests/Images/Input/Astc/Input/rgb_4x4.astc differ diff --git a/tests/Images/Input/Astc/Input/rgb_5x4.astc b/tests/Images/Input/Astc/Input/rgb_5x4.astc new file mode 100644 index 00000000..b9e09236 Binary files /dev/null and b/tests/Images/Input/Astc/Input/rgb_5x4.astc differ diff --git a/tests/Images/Input/Astc/Input/rgb_6x6.astc b/tests/Images/Input/Astc/Input/rgb_6x6.astc new file mode 100644 index 00000000..8a70c0fd Binary files /dev/null and b/tests/Images/Input/Astc/Input/rgb_6x6.astc differ diff --git a/tests/Images/Input/Astc/Input/rgb_8x8.astc b/tests/Images/Input/Astc/Input/rgb_8x8.astc new file mode 100644 index 00000000..1bca381d Binary files /dev/null and b/tests/Images/Input/Astc/Input/rgb_8x8.astc differ diff --git a/tests/Images/Input/Ktx/astc-rgba32-8x8.ktx b/tests/Images/Input/Ktx/astc-rgba32-8x8.ktx new file mode 100644 index 00000000..0e2893a4 --- /dev/null +++ b/tests/Images/Input/Ktx/astc-rgba32-8x8.ktx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:046f6f04070340bfaa465129a49e4ea082923a5de83fc9d819d6abea4bf779cf +size 114788 diff --git a/tests/Images/Input/Ktx2/astc_ldr_10x5_FlightHelmet_baseColor.ktx2 b/tests/Images/Input/Ktx2/astc_ldr_10x5_FlightHelmet_baseColor.ktx2 new file mode 100644 index 00000000..0c9ffd3e --- /dev/null +++ b/tests/Images/Input/Ktx2/astc_ldr_10x5_FlightHelmet_baseColor.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:42deecaeef010d228ac8662fe10b2dcf6e3e30bd6955f76f73b89296afd51e84 +size 1345104 diff --git a/tests/Images/Input/Ktx2/astc_ldr_6x6_arraytex_7_mipmap.ktx2 b/tests/Images/Input/Ktx2/astc_ldr_6x6_arraytex_7_mipmap.ktx2 new file mode 100644 index 00000000..d1a3374a --- /dev/null +++ b/tests/Images/Input/Ktx2/astc_ldr_6x6_arraytex_7_mipmap.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3dfb9321bbfe55f1f12c85ec298f595c183212c7082a6ea298b8c4633483537a +size 2192 diff --git a/tests/Images/Input/Ktx2/astc_ldr_cubemap_6x6.ktx2 b/tests/Images/Input/Ktx2/astc_ldr_cubemap_6x6.ktx2 new file mode 100644 index 00000000..9a10d9fe --- /dev/null +++ b/tests/Images/Input/Ktx2/astc_ldr_cubemap_6x6.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:70cdc0097301bb634c86acea3484b4c29f0ce1ebf0f23895fd86917b4ffb54ca +size 11228848 diff --git a/tests/Images/Input/Ktx2/astc_rgba32_10x10.ktx2 b/tests/Images/Input/Ktx2/astc_rgba32_10x10.ktx2 new file mode 100644 index 00000000..2334aeda --- /dev/null +++ b/tests/Images/Input/Ktx2/astc_rgba32_10x10.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:512c8f4d0352492ac70fb8d72c69802980cefd3de0ccfe179653f74028e1a548 +size 11088 diff --git a/tests/Images/Input/Ktx2/astc_rgba32_10x5.ktx2 b/tests/Images/Input/Ktx2/astc_rgba32_10x5.ktx2 new file mode 100644 index 00000000..76bd7adb --- /dev/null +++ b/tests/Images/Input/Ktx2/astc_rgba32_10x5.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:80c0ea7f71b120a09e8c3a1ac39e6677e0cc2a663f30d0c7b349d93386741314 +size 21904 diff --git a/tests/Images/Input/Ktx2/astc_rgba32_10x6.ktx2 b/tests/Images/Input/Ktx2/astc_rgba32_10x6.ktx2 new file mode 100644 index 00000000..26940296 --- /dev/null +++ b/tests/Images/Input/Ktx2/astc_rgba32_10x6.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7fababed3062e1a82b51ad4ca443423fa2cab21ff74166ff24cd5a7583f82da6 +size 18160 diff --git a/tests/Images/Input/Ktx2/astc_rgba32_10x8.ktx2 b/tests/Images/Input/Ktx2/astc_rgba32_10x8.ktx2 new file mode 100644 index 00000000..084e8eaf --- /dev/null +++ b/tests/Images/Input/Ktx2/astc_rgba32_10x8.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e2c61a06ee3f2301c7acc1518808b934373349ba4938cdc3c40057b651143b40 +size 13584 diff --git a/tests/Images/Input/Ktx2/astc_rgba32_12x10.ktx2 b/tests/Images/Input/Ktx2/astc_rgba32_12x10.ktx2 new file mode 100644 index 00000000..3ee93205 --- /dev/null +++ b/tests/Images/Input/Ktx2/astc_rgba32_12x10.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:78353f3d9f0fdf2ecdff1bcae1c51832c00465beb3f2922ac2e6753693329686 +size 9424 diff --git a/tests/Images/Input/Ktx2/astc_rgba32_12x12.ktx2 b/tests/Images/Input/Ktx2/astc_rgba32_12x12.ktx2 new file mode 100644 index 00000000..5b7d251f --- /dev/null +++ b/tests/Images/Input/Ktx2/astc_rgba32_12x12.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:944495207e2b869e0cc47f3b4537b5df284b01ee36066fcaac42fb7ce867c269 +size 8016 diff --git a/tests/Images/Input/Ktx2/astc_rgba32_4x4.ktx2 b/tests/Images/Input/Ktx2/astc_rgba32_4x4.ktx2 new file mode 100644 index 00000000..ae15d16b --- /dev/null +++ b/tests/Images/Input/Ktx2/astc_rgba32_4x4.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e21c1242774ef15aacc2ca950a849c9dc3a3e296515faa1ee876bd51c3306136 +size 65808 diff --git a/tests/Images/Input/Ktx2/astc_rgba32_5x4.ktx2 b/tests/Images/Input/Ktx2/astc_rgba32_5x4.ktx2 new file mode 100644 index 00000000..fdcb8eea --- /dev/null +++ b/tests/Images/Input/Ktx2/astc_rgba32_5x4.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6d8d410a26c834736b596ae08a3a423c9d4bd899cd9729a09b4abc9cbba84b02 +size 53520 diff --git a/tests/Images/Input/Ktx2/astc_rgba32_5x5.ktx2 b/tests/Images/Input/Ktx2/astc_rgba32_5x5.ktx2 new file mode 100644 index 00000000..ec54874c --- /dev/null +++ b/tests/Images/Input/Ktx2/astc_rgba32_5x5.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ed1330f50aa9061a760daefe7285cf4ca71e1105f6e2c867b3f5373735b238b8 +size 43536 diff --git a/tests/Images/Input/Ktx2/astc_rgba32_6x5.ktx2 b/tests/Images/Input/Ktx2/astc_rgba32_6x5.ktx2 new file mode 100644 index 00000000..8f3ab4d6 --- /dev/null +++ b/tests/Images/Input/Ktx2/astc_rgba32_6x5.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2c95a1ba7ced72135f72fa3b1d94a51e2b4c2b42cbd086b6e84774fb5708d4aa +size 36048 diff --git a/tests/Images/Input/Ktx2/astc_rgba32_6x6.ktx2 b/tests/Images/Input/Ktx2/astc_rgba32_6x6.ktx2 new file mode 100644 index 00000000..c5c8b7e2 --- /dev/null +++ b/tests/Images/Input/Ktx2/astc_rgba32_6x6.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:efb357573e877eabc378059496e7572e1de35529db3898dd3100c7a55ba42af3 +size 29856 diff --git a/tests/Images/Input/Ktx2/astc_rgba32_8x5.ktx2 b/tests/Images/Input/Ktx2/astc_rgba32_8x5.ktx2 new file mode 100644 index 00000000..115ef10a --- /dev/null +++ b/tests/Images/Input/Ktx2/astc_rgba32_8x5.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7bf0481dea0eabbe6f1d7dcae23531fb25f3c7bb6c8f2e8c64018cd843de2bd4 +size 26896 diff --git a/tests/Images/Input/Ktx2/astc_rgba32_8x6.ktx2 b/tests/Images/Input/Ktx2/astc_rgba32_8x6.ktx2 new file mode 100644 index 00000000..6f0c684f --- /dev/null +++ b/tests/Images/Input/Ktx2/astc_rgba32_8x6.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:419038f24a8e6516a376dcbcb4c733969e5649acf478b598125acaba7b84a272 +size 22288 diff --git a/tests/Images/Input/Ktx2/astc_rgba32_8x8.ktx2 b/tests/Images/Input/Ktx2/astc_rgba32_8x8.ktx2 new file mode 100644 index 00000000..305fbf1b --- /dev/null +++ b/tests/Images/Input/Ktx2/astc_rgba32_8x8.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bf90e732eb27f5ab902fc04cd149d04af59e00667932dbe898391607457da83b +size 16656 diff --git a/tests/Images/Input/Ktx2/valid_ASTC_10x10_SRGB_BLOCK_2D.ktx2 b/tests/Images/Input/Ktx2/valid_ASTC_10x10_SRGB_BLOCK_2D.ktx2 new file mode 100644 index 00000000..44fa4a48 --- /dev/null +++ b/tests/Images/Input/Ktx2/valid_ASTC_10x10_SRGB_BLOCK_2D.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:179183db8cb2f1d66bf064b5555790906c0e83e6034db53a322b383acfafcd33 +size 288 diff --git a/tests/Images/Input/Ktx2/valid_ASTC_10x10_UNORM_BLOCK_2D.ktx2 b/tests/Images/Input/Ktx2/valid_ASTC_10x10_UNORM_BLOCK_2D.ktx2 new file mode 100644 index 00000000..b5bb9290 --- /dev/null +++ b/tests/Images/Input/Ktx2/valid_ASTC_10x10_UNORM_BLOCK_2D.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:533bab3b7990c5f283595f35e7340ef77975372661c8541b369f0bc51754ecc3 +size 288 diff --git a/tests/Images/Input/Ktx2/valid_ASTC_10x5_SRGB_BLOCK_2D.ktx2 b/tests/Images/Input/Ktx2/valid_ASTC_10x5_SRGB_BLOCK_2D.ktx2 new file mode 100644 index 00000000..b8f3744e --- /dev/null +++ b/tests/Images/Input/Ktx2/valid_ASTC_10x5_SRGB_BLOCK_2D.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b8e9ac64c222117a5ee9dc65c0b5488e0bca98d16ad714d2c91f55f93e3a31be +size 352 diff --git a/tests/Images/Input/Ktx2/valid_ASTC_10x5_UNORM_BLOCK_2D.ktx2 b/tests/Images/Input/Ktx2/valid_ASTC_10x5_UNORM_BLOCK_2D.ktx2 new file mode 100644 index 00000000..40e13fb5 --- /dev/null +++ b/tests/Images/Input/Ktx2/valid_ASTC_10x5_UNORM_BLOCK_2D.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:516a7a67c584836c2f01bce00bea27fef5f5a3d2d0c7afe93dafeb5c169fc355 +size 352 diff --git a/tests/Images/Input/Ktx2/valid_ASTC_10x6_SRGB_BLOCK_2D.ktx2 b/tests/Images/Input/Ktx2/valid_ASTC_10x6_SRGB_BLOCK_2D.ktx2 new file mode 100644 index 00000000..65285e5c --- /dev/null +++ b/tests/Images/Input/Ktx2/valid_ASTC_10x6_SRGB_BLOCK_2D.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0314740d4e2d869d4935aa5eb16f0e6c195fb01ab4ef17c34069946a04850af1 +size 320 diff --git a/tests/Images/Input/Ktx2/valid_ASTC_10x6_UNORM_BLOCK_2D.ktx2 b/tests/Images/Input/Ktx2/valid_ASTC_10x6_UNORM_BLOCK_2D.ktx2 new file mode 100644 index 00000000..74adaf23 --- /dev/null +++ b/tests/Images/Input/Ktx2/valid_ASTC_10x6_UNORM_BLOCK_2D.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f87cdb572e3dfd17b66f1beb7821b56b1e7838effc4dbdc8c20c450d58b1b8c6 +size 320 diff --git a/tests/Images/Input/Ktx2/valid_ASTC_10x8_SRGB_BLOCK_2D.ktx2 b/tests/Images/Input/Ktx2/valid_ASTC_10x8_SRGB_BLOCK_2D.ktx2 new file mode 100644 index 00000000..119a0f0b --- /dev/null +++ b/tests/Images/Input/Ktx2/valid_ASTC_10x8_SRGB_BLOCK_2D.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fe27c258349ba760e2bcf20eb1fee6a1b332be8d4e5364c2eadbf6381d6a640f +size 288 diff --git a/tests/Images/Input/Ktx2/valid_ASTC_10x8_UNORM_BLOCK_2D.ktx2 b/tests/Images/Input/Ktx2/valid_ASTC_10x8_UNORM_BLOCK_2D.ktx2 new file mode 100644 index 00000000..95829e40 --- /dev/null +++ b/tests/Images/Input/Ktx2/valid_ASTC_10x8_UNORM_BLOCK_2D.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:42b7708149d39b9eecc76b60c10b6d79ae9b2e5904cbe77799d299b201123df2 +size 288 diff --git a/tests/Images/Input/Ktx2/valid_ASTC_12x10_SRGB_BLOCK_2D.ktx2 b/tests/Images/Input/Ktx2/valid_ASTC_12x10_SRGB_BLOCK_2D.ktx2 new file mode 100644 index 00000000..e265b30a --- /dev/null +++ b/tests/Images/Input/Ktx2/valid_ASTC_12x10_SRGB_BLOCK_2D.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5b9638a8adac865a9955b9c13af3f80dd30f77711c1b8931c5b9bef95ffab4a8 +size 288 diff --git a/tests/Images/Input/Ktx2/valid_ASTC_12x10_UNORM_BLOCK_2D.ktx2 b/tests/Images/Input/Ktx2/valid_ASTC_12x10_UNORM_BLOCK_2D.ktx2 new file mode 100644 index 00000000..7a88ecdd --- /dev/null +++ b/tests/Images/Input/Ktx2/valid_ASTC_12x10_UNORM_BLOCK_2D.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2bb0bb30011574a796786d1d93ecbccd1cc91d585f4a685ecc6627dea61d98cc +size 288 diff --git a/tests/Images/Input/Ktx2/valid_ASTC_12x12_SRGB_BLOCK_2D.ktx2 b/tests/Images/Input/Ktx2/valid_ASTC_12x12_SRGB_BLOCK_2D.ktx2 new file mode 100644 index 00000000..46e0c53f --- /dev/null +++ b/tests/Images/Input/Ktx2/valid_ASTC_12x12_SRGB_BLOCK_2D.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ad07ce4c69e459141ef864f1d8eaf86f851ddd2b283073890c6ffb6b501dfee6 +size 288 diff --git a/tests/Images/Input/Ktx2/valid_ASTC_12x12_UNORM_BLOCK_2D.ktx2 b/tests/Images/Input/Ktx2/valid_ASTC_12x12_UNORM_BLOCK_2D.ktx2 new file mode 100644 index 00000000..372866cf --- /dev/null +++ b/tests/Images/Input/Ktx2/valid_ASTC_12x12_UNORM_BLOCK_2D.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:52c52e9581bf36e9dc0e1c178e21e2c4c25eb520a3a4ded3b7ff534abda83667 +size 288 diff --git a/tests/Images/Input/Ktx2/valid_ASTC_4x4_SRGB_BLOCK_2D.ktx2 b/tests/Images/Input/Ktx2/valid_ASTC_4x4_SRGB_BLOCK_2D.ktx2 new file mode 100644 index 00000000..77635ccf --- /dev/null +++ b/tests/Images/Input/Ktx2/valid_ASTC_4x4_SRGB_BLOCK_2D.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e4a8bb46326439994e152e620700f12b0d090d7069702ceb2f15320693aea9f9 +size 480 diff --git a/tests/Images/Input/Ktx2/valid_ASTC_4x4_UNORM_BLOCK_2D.ktx2 b/tests/Images/Input/Ktx2/valid_ASTC_4x4_UNORM_BLOCK_2D.ktx2 new file mode 100644 index 00000000..a268409f --- /dev/null +++ b/tests/Images/Input/Ktx2/valid_ASTC_4x4_UNORM_BLOCK_2D.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:89871b4df0314bd68b3fb30b8fa7de9702029bed255bc0f22d09864ff8242362 +size 480 diff --git a/tests/Images/Input/Ktx2/valid_ASTC_4x4_UNORM_BLOCK_2D_ZLIB_1.ktx2 b/tests/Images/Input/Ktx2/valid_ASTC_4x4_UNORM_BLOCK_2D_ZLIB_1.ktx2 new file mode 100644 index 00000000..ac28ec16 --- /dev/null +++ b/tests/Images/Input/Ktx2/valid_ASTC_4x4_UNORM_BLOCK_2D_ZLIB_1.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ce6c7aff34040079b4067b6ee19b7fc338621f9e453b0e6d379bcac4e0d7c4c8 +size 299 diff --git a/tests/Images/Input/Ktx2/valid_ASTC_4x4_UNORM_BLOCK_2D_ZLIB_9.ktx2 b/tests/Images/Input/Ktx2/valid_ASTC_4x4_UNORM_BLOCK_2D_ZLIB_9.ktx2 new file mode 100644 index 00000000..e15efa89 --- /dev/null +++ b/tests/Images/Input/Ktx2/valid_ASTC_4x4_UNORM_BLOCK_2D_ZLIB_9.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:68cce8fa95fd5484817c4a70f34c97ebb25a09a1f0a7db731e13e92bff1908e4 +size 299 diff --git a/tests/Images/Input/Ktx2/valid_ASTC_4x4_UNORM_BLOCK_2D_ZSTD_1.ktx2 b/tests/Images/Input/Ktx2/valid_ASTC_4x4_UNORM_BLOCK_2D_ZSTD_1.ktx2 new file mode 100644 index 00000000..0ed01195 --- /dev/null +++ b/tests/Images/Input/Ktx2/valid_ASTC_4x4_UNORM_BLOCK_2D_ZSTD_1.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7b9f8cba0659da234461cb621d10b5f02a01592e352385e429e8d7d3d8bed929 +size 297 diff --git a/tests/Images/Input/Ktx2/valid_ASTC_4x4_UNORM_BLOCK_2D_ZSTD_9.ktx2 b/tests/Images/Input/Ktx2/valid_ASTC_4x4_UNORM_BLOCK_2D_ZSTD_9.ktx2 new file mode 100644 index 00000000..112fcf56 --- /dev/null +++ b/tests/Images/Input/Ktx2/valid_ASTC_4x4_UNORM_BLOCK_2D_ZSTD_9.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1f3a8c9b7b5c5e426f9a6986274c266230671d27cd7b10b6d1c0650b9833ac56 +size 297 diff --git a/tests/Images/Input/Ktx2/valid_ASTC_5x4_SRGB_BLOCK_2D.ktx2 b/tests/Images/Input/Ktx2/valid_ASTC_5x4_SRGB_BLOCK_2D.ktx2 new file mode 100644 index 00000000..46869ef7 --- /dev/null +++ b/tests/Images/Input/Ktx2/valid_ASTC_5x4_SRGB_BLOCK_2D.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:79ffea95e9cd8c4d7524bfe9b6862c1795c724b19392208fb7b4ecd33a667cdf +size 480 diff --git a/tests/Images/Input/Ktx2/valid_ASTC_5x4_UNORM_BLOCK_2D.ktx2 b/tests/Images/Input/Ktx2/valid_ASTC_5x4_UNORM_BLOCK_2D.ktx2 new file mode 100644 index 00000000..26e917cf --- /dev/null +++ b/tests/Images/Input/Ktx2/valid_ASTC_5x4_UNORM_BLOCK_2D.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ffcb506ca972e68c470af50aaf26e9c687c6a63cd68a1ede00dcfe5ecb786e27 +size 480 diff --git a/tests/Images/Input/Ktx2/valid_ASTC_5x5_SRGB_BLOCK_2D.ktx2 b/tests/Images/Input/Ktx2/valid_ASTC_5x5_SRGB_BLOCK_2D.ktx2 new file mode 100644 index 00000000..245b0336 --- /dev/null +++ b/tests/Images/Input/Ktx2/valid_ASTC_5x5_SRGB_BLOCK_2D.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d68cb3cf797346691d7de4313870b58f04b290f562e7dc797527f8594cd53f08 +size 480 diff --git a/tests/Images/Input/Ktx2/valid_ASTC_5x5_UNORM_BLOCK_2D.ktx2 b/tests/Images/Input/Ktx2/valid_ASTC_5x5_UNORM_BLOCK_2D.ktx2 new file mode 100644 index 00000000..7a3be45b --- /dev/null +++ b/tests/Images/Input/Ktx2/valid_ASTC_5x5_UNORM_BLOCK_2D.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a43bf736c6e7a88453284ad3820e545859582cc50a969940549bdd59a113395e +size 480 diff --git a/tests/Images/Input/Ktx2/valid_ASTC_6x5_SRGB_BLOCK_2D.ktx2 b/tests/Images/Input/Ktx2/valid_ASTC_6x5_SRGB_BLOCK_2D.ktx2 new file mode 100644 index 00000000..8465bf20 --- /dev/null +++ b/tests/Images/Input/Ktx2/valid_ASTC_6x5_SRGB_BLOCK_2D.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:95c42b019169a33646886a28246d54a5435a75350ca514f4fe1eb4be9522d48b +size 416 diff --git a/tests/Images/Input/Ktx2/valid_ASTC_6x5_UNORM_BLOCK_2D.ktx2 b/tests/Images/Input/Ktx2/valid_ASTC_6x5_UNORM_BLOCK_2D.ktx2 new file mode 100644 index 00000000..cc236894 --- /dev/null +++ b/tests/Images/Input/Ktx2/valid_ASTC_6x5_UNORM_BLOCK_2D.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:321c7133a313b87da891b56571ed26e96f09a993daead2ef16d65fa660b9c9a3 +size 416 diff --git a/tests/Images/Input/Ktx2/valid_ASTC_6x6_SRGB_BLOCK_2D.ktx2 b/tests/Images/Input/Ktx2/valid_ASTC_6x6_SRGB_BLOCK_2D.ktx2 new file mode 100644 index 00000000..b34b1fa4 --- /dev/null +++ b/tests/Images/Input/Ktx2/valid_ASTC_6x6_SRGB_BLOCK_2D.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:38fc0dbc5920b4c90c6982b8b4459447dd86a76ef496349a33230fab9d57c8d8 +size 368 diff --git a/tests/Images/Input/Ktx2/valid_ASTC_6x6_UNORM_BLOCK_2D.ktx2 b/tests/Images/Input/Ktx2/valid_ASTC_6x6_UNORM_BLOCK_2D.ktx2 new file mode 100644 index 00000000..248b9a43 --- /dev/null +++ b/tests/Images/Input/Ktx2/valid_ASTC_6x6_UNORM_BLOCK_2D.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:536e580ad30969214a7c8510fe89a61e9c632be535225dd61f31e606822b1086 +size 368 diff --git a/tests/Images/Input/Ktx2/valid_ASTC_8x5_SRGB_BLOCK_2D.ktx2 b/tests/Images/Input/Ktx2/valid_ASTC_8x5_SRGB_BLOCK_2D.ktx2 new file mode 100644 index 00000000..c7f321af --- /dev/null +++ b/tests/Images/Input/Ktx2/valid_ASTC_8x5_SRGB_BLOCK_2D.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eab82cb6499bff8c402361880603ec4db484e5fe7bf6f3d6d15d9b78c9da5ebc +size 352 diff --git a/tests/Images/Input/Ktx2/valid_ASTC_8x5_UNORM_BLOCK_2D.ktx2 b/tests/Images/Input/Ktx2/valid_ASTC_8x5_UNORM_BLOCK_2D.ktx2 new file mode 100644 index 00000000..b0ec3d69 --- /dev/null +++ b/tests/Images/Input/Ktx2/valid_ASTC_8x5_UNORM_BLOCK_2D.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b1c0aaf84c461da681571ce1335146acc0e88d6669853a29577b10dd8476a6a9 +size 352 diff --git a/tests/Images/Input/Ktx2/valid_ASTC_8x6_SRGB_BLOCK_2D.ktx2 b/tests/Images/Input/Ktx2/valid_ASTC_8x6_SRGB_BLOCK_2D.ktx2 new file mode 100644 index 00000000..7e612cae --- /dev/null +++ b/tests/Images/Input/Ktx2/valid_ASTC_8x6_SRGB_BLOCK_2D.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b4715a22d329b107b8e84224489f9743b42cbaf740d1213df9a35112b8008291 +size 320 diff --git a/tests/Images/Input/Ktx2/valid_ASTC_8x6_UNORM_BLOCK_2D.ktx2 b/tests/Images/Input/Ktx2/valid_ASTC_8x6_UNORM_BLOCK_2D.ktx2 new file mode 100644 index 00000000..3e08c23a --- /dev/null +++ b/tests/Images/Input/Ktx2/valid_ASTC_8x6_UNORM_BLOCK_2D.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:425fef4e127b8b505482cffbcbdc3279c87d2860062377b6fdb9219301eb51f2 +size 320 diff --git a/tests/Images/Input/Ktx2/valid_ASTC_8x8_SRGB_BLOCK_2D.ktx2 b/tests/Images/Input/Ktx2/valid_ASTC_8x8_SRGB_BLOCK_2D.ktx2 new file mode 100644 index 00000000..ad606fdd --- /dev/null +++ b/tests/Images/Input/Ktx2/valid_ASTC_8x8_SRGB_BLOCK_2D.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ef826457d9c0c6797aabad6530398feccead690213a556fe0960769b6aaf98a8 +size 288 diff --git a/tests/Images/Input/Ktx2/valid_ASTC_8x8_UNORM_BLOCK_2D.ktx2 b/tests/Images/Input/Ktx2/valid_ASTC_8x8_UNORM_BLOCK_2D.ktx2 new file mode 100644 index 00000000..60da1caf --- /dev/null +++ b/tests/Images/Input/Ktx2/valid_ASTC_8x8_UNORM_BLOCK_2D.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ee6be1f8eebb06f6d312d20848d3bed7f7ffb70b12163a4b822826f523a906a9 +size 288 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_All_Faces_negX.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_All_Faces_negX.png new file mode 100644 index 00000000..2f898e90 --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_All_Faces_negX.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:04535fdbc632fc946f1bdce0229bbe17f15ef9ede7b089d893a7539d3428093b +size 4456232 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_All_Faces_negY.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_All_Faces_negY.png new file mode 100644 index 00000000..653b279e --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_All_Faces_negY.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8615a15cfbac32d0c440520e70af99bd1af3c64b6651f4dffe990007afe470d5 +size 4520170 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_All_Faces_negZ.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_All_Faces_negZ.png new file mode 100644 index 00000000..464ad67a --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_All_Faces_negZ.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:76e41968dc617f55c19913a844790f432d345fd7de069b8ae91ce8b258536961 +size 4188624 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_All_Faces_posX.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_All_Faces_posX.png new file mode 100644 index 00000000..a0afbf06 --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_All_Faces_posX.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4a244bc0d7e9ff1b82dae09678f18f0dc7ed7c082757c402bcfe2f62afbf8a4f +size 4433772 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_All_Faces_posY.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_All_Faces_posY.png new file mode 100644 index 00000000..c8554e46 --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_All_Faces_posY.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ddc6b41b993735daf6bc947cb90347ff5d387aa236ed3a2d614a296c0d9f2797 +size 2820406 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_All_Faces_posZ.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_All_Faces_posZ.png new file mode 100644 index 00000000..3820a0e4 --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_All_Faces_posZ.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3cc4b3ecdb34d3ba6f02c6ea5f78a7fa99904ab81ec0538863694ad1fdfb8b33 +size 3910835 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_MipMaps_0.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_MipMaps_0.png new file mode 100644 index 00000000..9211d075 --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_MipMaps_0.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cfd253f555e9d6b5a3493c93f6bfdfcfa7d9cab759d7994e544dd5b72f0753a9 +size 107 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_MipMaps_1.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_MipMaps_1.png new file mode 100644 index 00000000..00b6da1a --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_MipMaps_1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c0313d2900eae51bd5f56b609839d130f6c6ec17320f7595973a36bbcdee84df +size 100 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_MipMaps_2.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_MipMaps_2.png new file mode 100644 index 00000000..47c4dead --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_MipMaps_2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b708d4e34181c551722491a8d68b6b2d79de3a041f3b79cb49728bed60c015e1 +size 99 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_MipMaps_3.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_MipMaps_3.png new file mode 100644 index 00000000..134de01e --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_MipMaps_3.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cd189117b29ddb0fbcaf8def1a5d90c0426be7241008cbea0aa470d184bb619b +size 97 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_MipMaps_4.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_MipMaps_4.png new file mode 100644 index 00000000..8af3b126 --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_MipMaps_4.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:aef68645bf32626a51b9420b91a3ad2f1af01cfa8aebb929d2144c1f3944b624 +size 91 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Blocksizes.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Blocksizes.png new file mode 100644 index 00000000..598f93a5 --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Blocksizes.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:72bf89c439781bb8e9bf582589fec9e2dcfd3581472d1d8182b445a1099b5879 +size 164227 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_10x10.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_10x10.png new file mode 100644 index 00000000..b7859c46 --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_10x10.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b77dd1564ec9452df5e4b8c71be4b65809ee513b84e83a385b27d1c53b6d2f07 +size 475 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_10x5.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_10x5.png new file mode 100644 index 00000000..637f711f --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_10x5.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:40f0e6160fda4625f56a9c2a4da0154781aa61b5aeb03c426c92cfe565b3dab8 +size 396 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_10x6.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_10x6.png new file mode 100644 index 00000000..052f1ac6 --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_10x6.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2baccdb9e775a730fa1ad38beedb2f3674602c3c583f6fdf6d35baf2d76d2da1 +size 438 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_10x8.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_10x8.png new file mode 100644 index 00000000..593a0365 --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_10x8.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ed3bd9fcd8b1bf5e9214204deee1adbd30af5649f3549fcdf3248e99b226b9b2 +size 384 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_12x10.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_12x10.png new file mode 100644 index 00000000..c5d7b0bb --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_12x10.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b4de083f86ecedca4298d8db172000fe56edf7993deeebad718b20679b93fa11 +size 443 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_12x12.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_12x12.png new file mode 100644 index 00000000..3d2787f9 --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_12x12.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b80ef91c2e480f370b49a110e36385c437ab83f2ae2bc84034240d896a392a8b +size 457 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_4x4.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_4x4.png new file mode 100644 index 00000000..a123f4f4 --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_4x4.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b9cff4006b0483486df0d4d32bd55328b615987dfe65c76a2676bea86424a234 +size 384 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_5x4.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_5x4.png new file mode 100644 index 00000000..a12a7a69 --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_5x4.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6179453f57d3711594c64f4e4ee913d460e81c537b85b1ed240d36b168d4f3ce +size 460 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_5x5.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_5x5.png new file mode 100644 index 00000000..29cd2256 --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_5x5.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fd375c9b74a64145d3093a4020efaae0c1ab443fbb885d103aeb7a3d57970c9c +size 589 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_6x5.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_6x5.png new file mode 100644 index 00000000..34e8d5cb --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_6x5.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e1d235bf6d5a4685ddd3fa31fc912994c7acb098e5445174b8c5516c06fffc92 +size 544 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_6x6.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_6x6.png new file mode 100644 index 00000000..f0368e89 --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_6x6.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5583508ef1dbb46d35e002ba0841c3c45523df512d4183a4a811dbc59d1fde05 +size 504 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_8x5.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_8x5.png new file mode 100644 index 00000000..c6c09b56 --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_8x5.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:71e3d9dc1307a2afac70b4eaf1b3c4f2be8e013d79533984613ca12a0e60a7b8 +size 435 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_8x6.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_8x6.png new file mode 100644 index 00000000..a52503c9 --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_8x6.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bceeb7a936cd629ce909d2b7a99ab4d1686b7baeab41179e12bd3354fcb6f1c7 +size 520 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_8x8.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_8x8.png new file mode 100644 index 00000000..98c95207 --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_8x8.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:56d817a096705e39cb76f5505ddf34c1d012d16bfc40fc43939317bb0af632c8 +size 385 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_Large.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_Large.png new file mode 100644 index 00000000..04cebc1a --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Srgb_Large.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6274ba573a2f6dae924e1aa40b586ca89329d5846cd5cf31b29188586e68c285 +size 1450103 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Supercompressed_ZLIB_1.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Supercompressed_ZLIB_1.png new file mode 100644 index 00000000..6bb0f52c --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Supercompressed_ZLIB_1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8a908971e007eb33f0990f0fa63c0ba6a8faaa8034bd0ed161b2aa092a59b78a +size 381 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Supercompressed_ZLIB_9.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Supercompressed_ZLIB_9.png new file mode 100644 index 00000000..6bb0f52c --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Supercompressed_ZLIB_9.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8a908971e007eb33f0990f0fa63c0ba6a8faaa8034bd0ed161b2aa092a59b78a +size 381 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Supercompressed_ZSTD_1.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Supercompressed_ZSTD_1.png new file mode 100644 index 00000000..6bb0f52c --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Supercompressed_ZSTD_1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8a908971e007eb33f0990f0fa63c0ba6a8faaa8034bd0ed161b2aa092a59b78a +size 381 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Supercompressed_ZSTD_9.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Supercompressed_ZSTD_9.png new file mode 100644 index 00000000..6bb0f52c --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Supercompressed_ZSTD_9.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8a908971e007eb33f0990f0fa63c0ba6a8faaa8034bd0ed161b2aa092a59b78a +size 381 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Unorm_10x10.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Unorm_10x10.png new file mode 100644 index 00000000..d0f0d932 --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Unorm_10x10.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b8378b4b6a70c4414f9f549b009e01ddc934c28623e985e0818b357a68a96b5c +size 373 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Unorm_10x5.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Unorm_10x5.png new file mode 100644 index 00000000..c9943a80 --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Unorm_10x5.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:57140275641276969e3e6d16f4d9ef36975026267d81072fc270ef1fffd75ea9 +size 388 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Unorm_10x6.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Unorm_10x6.png new file mode 100644 index 00000000..a8421169 --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Unorm_10x6.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6beb53de172583ed13a37499f7c7df85283911c572bea8149eeac1ee7ebdeee5 +size 350 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Unorm_10x8.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Unorm_10x8.png new file mode 100644 index 00000000..e0645247 --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Unorm_10x8.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:271e3e222374be3e48597759d72566a908afdb422c77175b7c2149c5836ce0b0 +size 352 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Unorm_12x10.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Unorm_12x10.png new file mode 100644 index 00000000..da1775e8 --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Unorm_12x10.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:70c48f806cb55b20ccc44e57714506453c34789b20bde10feea51b2c56d742f1 +size 485 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Unorm_12x12.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Unorm_12x12.png new file mode 100644 index 00000000..67d37ce3 --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Unorm_12x12.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6dd4e0fa30523f6b4ba6d0a8142d09440b71e6a42e6c3bc272539444175c316e +size 558 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Unorm_4x4.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Unorm_4x4.png new file mode 100644 index 00000000..6bb0f52c --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Unorm_4x4.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8a908971e007eb33f0990f0fa63c0ba6a8faaa8034bd0ed161b2aa092a59b78a +size 381 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Unorm_5x4.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Unorm_5x4.png new file mode 100644 index 00000000..2f548c43 --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Unorm_5x4.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bcdeaf19046832ea27917c042f1b801820980ddaa86fdf58ab713f130dccd0d7 +size 421 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Unorm_5x5.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Unorm_5x5.png new file mode 100644 index 00000000..590b3da9 --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Unorm_5x5.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:be4c2d4c8658ac6cb7897bafd184d17bcc80007b90267703b2c36a4a625b34a1 +size 562 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Unorm_6x5.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Unorm_6x5.png new file mode 100644 index 00000000..eb7083aa --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Unorm_6x5.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:14f87a9677902b09218e7ac844e392304fd14b90ac9bb6a41015477bfdadf4b1 +size 481 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Unorm_6x6.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Unorm_6x6.png new file mode 100644 index 00000000..b40edcdc --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Unorm_6x6.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b895db282773205885753c650a89eaea773cce21d47c8558d4c33f5f64ab0ab3 +size 465 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Unorm_8x5.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Unorm_8x5.png new file mode 100644 index 00000000..404ca851 --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Unorm_8x5.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:601b2f20a5d891c947f40940def171bbc36741cbd2708dadd335130376f0b227 +size 389 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Unorm_8x6.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Unorm_8x6.png new file mode 100644 index 00000000..1ff3182c --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Unorm_8x6.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8074f9f74efc1f1775e14ff9514762ec9764b2ed16ee45cf6dd5330aa1da9aeb +size 385 diff --git a/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Unorm_8x8.png b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Unorm_8x8.png new file mode 100644 index 00000000..cb6f1d82 --- /dev/null +++ b/tests/Images/ReferenceOutput/Ktx2AstcDecoder_CanDecode_Rgba32_Unorm_8x8.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0bbae80913ee6f5c24f04da0612364bda8e092f5349fe2acf494c9ac9bed005e +size 352 diff --git a/tests/Images/ReferenceOutput/KtxAstcDecoder_CanDecode_Rgba32_Blocksizes.png b/tests/Images/ReferenceOutput/KtxAstcDecoder_CanDecode_Rgba32_Blocksizes.png new file mode 100644 index 00000000..6a18547c --- /dev/null +++ b/tests/Images/ReferenceOutput/KtxAstcDecoder_CanDecode_Rgba32_Blocksizes.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ebd9984f80e75676d74bdb22d32c923a6c248df13b8817dcecb8e0627707cc2e +size 82338 diff --git a/tests/Images/TestEnvironment.cs b/tests/Images/TestEnvironment.cs index 79a36a4c..ab418d34 100644 --- a/tests/Images/TestEnvironment.cs +++ b/tests/Images/TestEnvironment.cs @@ -1,88 +1,84 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; -using System.IO; -using System.Linq; using System.Reflection; -namespace SixLabors.ImageSharp.Textures +namespace SixLabors.ImageSharp.Textures; + +public static class TestEnvironment { - public static class TestEnvironment - { - private const string ImageSharpTexturesSolutionFileName = "ImageSharp.Textures.sln"; + private const string ImageSharpTexturesSolutionFileName = "ImageSharp.Textures.sln"; - private const string ToolsRelativePath = @"tests\Tools"; + private const string ToolsRelativePath = @"tests\Tools"; - private const string InputImagesRelativePath = @"tests\Images\Input"; + private const string InputImagesRelativePath = @"tests\Images\Input"; - private const string BaselineDirectoryRelativePath = @"tests\Images\Baseline"; + private const string BaselineDirectoryRelativePath = @"tests\Images\Baseline"; - private const string ActualOutputDirectoryRelativePath = @"tests\Images\ActualOutput"; + private const string ActualOutputDirectoryRelativePath = @"tests\Images\ActualOutput"; - private static readonly Lazy SolutionDirectoryFullPathLazy = new Lazy(GetSolutionDirectoryFullPathImpl); + private static readonly Lazy SolutionDirectoryFullPathLazy = new(GetSolutionDirectoryFullPathImpl); - private static readonly Lazy RunsOnCiLazy = new Lazy(() => bool.TryParse(Environment.GetEnvironmentVariable("CI"), out bool isCi) && isCi); + private static readonly Lazy RunsOnCiLazy = new(() => bool.TryParse(Environment.GetEnvironmentVariable("CI"), out bool isCi) && isCi); - /// - /// Gets a value indicating whether test execution runs on CI. - /// - internal static bool RunsOnCi => RunsOnCiLazy.Value; + /// + /// Gets a value indicating whether test execution runs on CI. + /// + internal static bool RunsOnCi => RunsOnCiLazy.Value; - internal static string SolutionDirectoryFullPath => SolutionDirectoryFullPathLazy.Value; + internal static string SolutionDirectoryFullPath => SolutionDirectoryFullPathLazy.Value; - private static string GetSolutionDirectoryFullPathImpl() - { - string assemblyLocation = typeof(TestEnvironment).GetTypeInfo().Assembly.Location; + private static string GetSolutionDirectoryFullPathImpl() + { + string assemblyLocation = typeof(TestEnvironment).GetTypeInfo().Assembly.Location; - var assemblyFile = new FileInfo(assemblyLocation); + FileInfo assemblyFile = new(assemblyLocation); - DirectoryInfo directory = assemblyFile.Directory; + DirectoryInfo directory = assemblyFile.Directory; - while (!directory.EnumerateFiles(ImageSharpTexturesSolutionFileName).Any()) + while (!directory.EnumerateFiles(ImageSharpTexturesSolutionFileName).Any()) + { + try { - try - { - directory = directory.Parent; - } - catch (Exception ex) - { - throw new DirectoryNotFoundException( - $"Unable to find ImageSharp solution directory from {assemblyLocation} because of {ex.GetType().Name}!", - ex); - } - - if (directory == null) - { - throw new DirectoryNotFoundException($"Unable to find ImageSharp solution directory from {assemblyLocation}!"); - } + directory = directory.Parent; + } + catch (Exception ex) + { + throw new DirectoryNotFoundException( + $"Unable to find ImageSharp solution directory from {assemblyLocation} because of {ex.GetType().Name}!", + ex); } - return directory.FullName; + if (directory == null) + { + throw new DirectoryNotFoundException($"Unable to find ImageSharp solution directory from {assemblyLocation}!"); + } } - private static string GetFullPath(string relativePath) => - Path.Combine(SolutionDirectoryFullPath, relativePath) - .Replace('\\', Path.DirectorySeparatorChar); - - /// - /// Gets the correct full path to the Tools directory. - /// - internal static string ToolsDirectoryFullPath => GetFullPath(ToolsRelativePath); - - /// - /// Gets the correct full path to the Input Images directory. - /// - internal static string InputImagesDirectoryFullPath => GetFullPath(InputImagesRelativePath); - - /// - /// Gets the correct full path to the Baseline directory. - /// - internal static string BaselineDirectoryFullPath => GetFullPath(BaselineDirectoryRelativePath); - - /// - /// Gets the correct full path to the Actual Output directory. (To be written to by the test cases.) - /// - internal static string ActualOutputDirectoryFullPath => GetFullPath(ActualOutputDirectoryRelativePath); + return directory.FullName; } + + private static string GetFullPath(string relativePath) => + Path.Combine(SolutionDirectoryFullPath, relativePath) + .Replace('\\', Path.DirectorySeparatorChar); + + /// + /// Gets the correct full path to the Tools directory. + /// + internal static string ToolsDirectoryFullPath => GetFullPath(ToolsRelativePath); + + /// + /// Gets the correct full path to the Input Images directory. + /// + internal static string InputImagesDirectoryFullPath => GetFullPath(InputImagesRelativePath); + + /// + /// Gets the correct full path to the Baseline directory. + /// + internal static string BaselineDirectoryFullPath => GetFullPath(BaselineDirectoryRelativePath); + + /// + /// Gets the correct full path to the Actual Output directory. (To be written to by the test cases.) + /// + internal static string ActualOutputDirectoryFullPath => GetFullPath(ActualOutputDirectoryRelativePath); } diff --git a/tests/Images/TestTextures.cs b/tests/Images/TestTextures.cs index 6f57d93d..91e23a2d 100644 --- a/tests/Images/TestTextures.cs +++ b/tests/Images/TestTextures.cs @@ -1,12 +1,11 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Textures.Tests +namespace SixLabors.ImageSharp.Textures.Tests; + +public static class TestTextures { - public static class TestTextures + public static class Dds { - public static class Dds - { - } } }