From 1e69aa45ab4eaed35397d118770726fdc3af702f Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 15 Apr 2026 18:35:44 +0200 Subject: [PATCH] Add additional doc strings --- .../Compressors/NoneExrCompressor.cs | 10 + .../Compressors/ZipExrCompressor.cs | 11 + .../Decompressors/B44ExrCompression.cs | 44 +++- .../Decompressors/NoneExrCompression.cs | 11 +- .../Decompressors/RunLengthExrCompression.cs | 16 +- .../Decompressors/ZipExrCompression.cs | 11 +- .../Exr/Compression/ExrBaseCompression.cs | 9 + .../Exr/Compression/ExrBaseDecompressor.cs | 27 +++ .../Exr/Compression/ExrCompressorFactory.cs | 33 +-- .../Exr/Compression/ExrDecompressorFactory.cs | 39 ++-- .../Formats/Exr/Constants/ExrImageType.cs | 10 + .../Formats/Exr/Constants/ExrLineOrder.cs | 12 + src/ImageSharp/Formats/Exr/ExrAttribute.cs | 18 ++ src/ImageSharp/Formats/Exr/ExrBox2i.cs | 22 ++ src/ImageSharp/Formats/Exr/ExrChannelInfo.cs | 35 ++- src/ImageSharp/Formats/Exr/ExrDecoderCore.cs | 215 ++++++++++++++---- src/ImageSharp/Formats/Exr/ExrEncoderCore.cs | 186 +++++++++++++-- .../Formats/Exr/ExrHeaderAttributes.cs | 14 ++ 18 files changed, 606 insertions(+), 117 deletions(-) diff --git a/src/ImageSharp/Formats/Exr/Compression/Compressors/NoneExrCompressor.cs b/src/ImageSharp/Formats/Exr/Compression/Compressors/NoneExrCompressor.cs index 21a50c5311..58768e990f 100644 --- a/src/ImageSharp/Formats/Exr/Compression/Compressors/NoneExrCompressor.cs +++ b/src/ImageSharp/Formats/Exr/Compression/Compressors/NoneExrCompressor.cs @@ -5,8 +5,18 @@ using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Exr.Compression.Compressors; +/// +/// Compressor for EXR image data which does not use any compression method. +/// internal class NoneExrCompressor : ExrBaseCompressor { + /// + /// Initializes a new instance of the class. + /// + /// The output stream to write the compressed image data to. + /// The memory allocator. + /// Bytes per row block. + /// Bytes per pixel row. public NoneExrCompressor(Stream output, MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow) : base(output, allocator, bytesPerBlock, bytesPerRow) { diff --git a/src/ImageSharp/Formats/Exr/Compression/Compressors/ZipExrCompressor.cs b/src/ImageSharp/Formats/Exr/Compression/Compressors/ZipExrCompressor.cs index d24b8f7dd7..ef7285da0c 100644 --- a/src/ImageSharp/Formats/Exr/Compression/Compressors/ZipExrCompressor.cs +++ b/src/ImageSharp/Formats/Exr/Compression/Compressors/ZipExrCompressor.cs @@ -6,6 +6,9 @@ using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Exr.Compression.Compressors; +/// +/// Compressor for EXR image data using the ZIP compression. +/// internal class ZipExrCompressor : ExrBaseCompressor { private readonly DeflateCompressionLevel compressionLevel; @@ -14,6 +17,14 @@ internal class ZipExrCompressor : ExrBaseCompressor private readonly System.Buffers.IMemoryOwner buffer; + /// + /// Initializes a new instance of the class. + /// + /// The stream to write the compressed data to. + /// The memory allocator. + /// The bytes per block. + /// The bytes per row. + /// The compression level for deflate compression. public ZipExrCompressor(Stream output, MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow, DeflateCompressionLevel compressionLevel) : base(output, allocator, bytesPerBlock, bytesPerRow) { diff --git a/src/ImageSharp/Formats/Exr/Compression/Decompressors/B44ExrCompression.cs b/src/ImageSharp/Formats/Exr/Compression/Decompressors/B44ExrCompression.cs index e5b735a395..aa4944649c 100644 --- a/src/ImageSharp/Formats/Exr/Compression/Decompressors/B44ExrCompression.cs +++ b/src/ImageSharp/Formats/Exr/Compression/Decompressors/B44ExrCompression.cs @@ -8,6 +8,9 @@ using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Exr.Compression.Decompressors; +/// +/// Implementation of B44 decompressor for EXR image data. +/// internal class B44ExrCompression : ExrBaseDecompressor { private readonly int width; @@ -22,6 +25,15 @@ internal class B44ExrCompression : ExrBaseDecompressor private readonly IMemoryOwner tmpBuffer; + /// + /// Initializes a new instance of the class. + /// + /// The memory allocator. + /// The bytes per pixel row block. + /// The bytes per row. + /// The rows per block. + /// The width of a pixel row in pixels. + /// The number of channels of the image. public B44ExrCompression(MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow, uint rowsPerBlock, int width, int channelCount) : base(allocator, bytesPerBlock, bytesPerRow) { @@ -57,7 +69,7 @@ internal class B44ExrCompression : ExrBaseDecompressor int bytesRead = stream.Read(this.scratch, 0, 3); if (bytesRead == 0) { - ExrThrowHelper.ThrowInvalidImageContentException("Could not read enough data from the stream"); + ExrThrowHelper.ThrowInvalidImageContentException("Could not read enough data from the stream!"); } if (this.scratch[2] >= 13 << 2) @@ -70,7 +82,7 @@ internal class B44ExrCompression : ExrBaseDecompressor bytesRead = stream.Read(this.scratch, 3, 11); if (bytesRead == 0) { - ExrThrowHelper.ThrowInvalidImageContentException("Could not read enough data from the stream"); + ExrThrowHelper.ThrowInvalidImageContentException("Could not read enough data from the stream!"); } Unpack14(this.scratch, this.s); @@ -80,22 +92,22 @@ internal class B44ExrCompression : ExrBaseDecompressor int n = x + 3 < this.width ? 4 : this.width - x; if (y + 3 < this.rowsPerBlock) { - this.s.AsSpan(0, n).CopyTo(row0.Slice(rowOffset)); - this.s.AsSpan(4, n).CopyTo(row1.Slice(rowOffset)); - this.s.AsSpan(8, n).CopyTo(row2.Slice(rowOffset)); - this.s.AsSpan(12, n).CopyTo(row3.Slice(rowOffset)); + this.s.AsSpan(0, n).CopyTo(row0[rowOffset..]); + this.s.AsSpan(4, n).CopyTo(row1[rowOffset..]); + this.s.AsSpan(8, n).CopyTo(row2[rowOffset..]); + this.s.AsSpan(12, n).CopyTo(row3[rowOffset..]); } else { - this.s.AsSpan(0, n).CopyTo(row0.Slice(rowOffset)); + this.s.AsSpan(0, n).CopyTo(row0[rowOffset..]); if (y + 1 < this.rowsPerBlock) { - this.s.AsSpan(4, n).CopyTo(row1.Slice(rowOffset)); + this.s.AsSpan(4, n).CopyTo(row1[rowOffset..]); } if (y + 2 < this.rowsPerBlock) { - this.s.AsSpan(8, n).CopyTo(row2.Slice(rowOffset)); + this.s.AsSpan(8, n).CopyTo(row2[rowOffset..]); } } @@ -117,7 +129,7 @@ internal class B44ExrCompression : ExrBaseDecompressor { for (int i = 0; i < this.channelCount; i++) { - decompressed.Slice(offsetDecompressed + (i * blockSize), this.width).CopyTo(outputBuffer.Slice(offsetOutput)); + decompressed.Slice(offsetDecompressed + (i * blockSize), this.width).CopyTo(outputBuffer[offsetOutput..]); offsetOutput += this.width; } @@ -125,7 +137,11 @@ internal class B44ExrCompression : ExrBaseDecompressor } } - // Unpack a 14-byte block into 4 by 4 16-bit pixels. + /// + /// Unpack a 14-byte block into 4 by 4 16-bit pixels. + /// + /// The source byte data to unpack. + /// Destintation buffer. private static void Unpack14(Span b, Span s) { s[0] = (ushort)((b[0] << 8) | b[1]); @@ -165,7 +181,11 @@ internal class B44ExrCompression : ExrBaseDecompressor } } - // Unpack a 3-byte block into 4 by 4 identical 16-bit pixels. + /// + /// // Unpack a 3-byte block into 4 by 4 identical 16-bit pixels. + /// + /// The source byte data to unpack. + /// The destination buffer. private static void Unpack3(Span b, Span s) { s[0] = (ushort)((b[0] << 8) | b[1]); diff --git a/src/ImageSharp/Formats/Exr/Compression/Decompressors/NoneExrCompression.cs b/src/ImageSharp/Formats/Exr/Compression/Decompressors/NoneExrCompression.cs index fb4fa5d832..c15ffbe325 100644 --- a/src/ImageSharp/Formats/Exr/Compression/Decompressors/NoneExrCompression.cs +++ b/src/ImageSharp/Formats/Exr/Compression/Decompressors/NoneExrCompression.cs @@ -6,8 +6,17 @@ using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Exr.Compression.Decompressors; +/// +/// Decompressor for EXR image data which do not use any compression. +/// internal class NoneExrCompression : ExrBaseDecompressor { + /// + /// Initializes a new instance of the class. + /// + /// The memory allocator. + /// The bytes per pixel row block. + /// The bytes per pixel row. public NoneExrCompression(MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow) : base(allocator, bytesPerBlock, bytesPerRow) { @@ -19,7 +28,7 @@ internal class NoneExrCompression : ExrBaseDecompressor int bytesRead = stream.Read(buffer, 0, Math.Min(buffer.Length, (int)this.BytesPerBlock)); if (bytesRead != (int)this.BytesPerBlock) { - ExrThrowHelper.ThrowInvalidImageContentException("Could not read enough pixel data!"); + ExrThrowHelper.ThrowInvalidImageContentException("Could not read enough pixel data from the stream!"); } } diff --git a/src/ImageSharp/Formats/Exr/Compression/Decompressors/RunLengthExrCompression.cs b/src/ImageSharp/Formats/Exr/Compression/Decompressors/RunLengthExrCompression.cs index 12f5fc8ab6..489493d82f 100644 --- a/src/ImageSharp/Formats/Exr/Compression/Decompressors/RunLengthExrCompression.cs +++ b/src/ImageSharp/Formats/Exr/Compression/Decompressors/RunLengthExrCompression.cs @@ -7,12 +7,21 @@ using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Exr.Compression.Decompressors; +/// +/// Implementation of RLE decompressor for EXR images. +/// internal class RunLengthExrCompression : ExrBaseDecompressor { private readonly IMemoryOwner tmpBuffer; private readonly ushort[] s = new ushort[16]; + /// + /// Initializes a new instance of the class. + /// + /// The memory allocator. + /// The bytes per pixel row block. + /// The bytes per row. public RunLengthExrCompression(MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow) : base(allocator, bytesPerBlock, bytesPerRow) => this.tmpBuffer = allocator.Allocate((int)bytesPerBlock); @@ -68,12 +77,17 @@ internal class RunLengthExrCompression : ExrBaseDecompressor Interleave(uncompressed, this.BytesPerBlock, buffer); } + /// + /// Reads the next byte from the stream. + /// + /// The stream. + /// The next byte. private static byte ReadNextByte(BufferedReadStream stream) { int nextByte = stream.ReadByte(); if (nextByte == -1) { - ExrThrowHelper.ThrowInvalidImageContentException("Not enough data to decompress RLE image!"); + ExrThrowHelper.ThrowInvalidImageContentException("Not enough data to decompress RLE encoded EXR image!"); } return (byte)nextByte; diff --git a/src/ImageSharp/Formats/Exr/Compression/Decompressors/ZipExrCompression.cs b/src/ImageSharp/Formats/Exr/Compression/Decompressors/ZipExrCompression.cs index b8dc5efa8e..dafe3d8321 100644 --- a/src/ImageSharp/Formats/Exr/Compression/Decompressors/ZipExrCompression.cs +++ b/src/ImageSharp/Formats/Exr/Compression/Decompressors/ZipExrCompression.cs @@ -9,10 +9,19 @@ using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Exr.Compression.Decompressors; +/// +/// Implementation of zhe Zip decompressor for EXR image data. +/// internal class ZipExrCompression : ExrBaseDecompressor { private readonly IMemoryOwner tmpBuffer; + /// + /// Initializes a new instance of the class. + /// + /// The memory allocator. + /// The bytes per pixel row block. + /// The bytes per pixel row. public ZipExrCompression(MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow) : base(allocator, bytesPerBlock, bytesPerRow) => this.tmpBuffer = allocator.Allocate((int)bytesPerBlock); @@ -46,7 +55,7 @@ internal class ZipExrCompression : ExrBaseDecompressor if (totalRead == 0) { - ExrThrowHelper.ThrowInvalidImageContentException("Could not read zip compressed image data!"); + ExrThrowHelper.ThrowInvalidImageContentException("Could not read enough data for zip compressed image data!"); } Reconstruct(uncompressed, (uint)totalRead); diff --git a/src/ImageSharp/Formats/Exr/Compression/ExrBaseCompression.cs b/src/ImageSharp/Formats/Exr/Compression/ExrBaseCompression.cs index 57e6b2a26c..b116dffc65 100644 --- a/src/ImageSharp/Formats/Exr/Compression/ExrBaseCompression.cs +++ b/src/ImageSharp/Formats/Exr/Compression/ExrBaseCompression.cs @@ -5,10 +5,19 @@ using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Exr.Compression; +/// +/// Base class for EXR compression. +/// internal abstract class ExrBaseCompression : IDisposable { private bool isDisposed; + /// + /// Initializes a new instance of the class. + /// + /// The memory allocator. + /// The bytes per block. + /// The bytes per row. protected ExrBaseCompression(MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow) { this.Allocator = allocator; diff --git a/src/ImageSharp/Formats/Exr/Compression/ExrBaseDecompressor.cs b/src/ImageSharp/Formats/Exr/Compression/ExrBaseDecompressor.cs index 1bbf36d768..efe3c7877f 100644 --- a/src/ImageSharp/Formats/Exr/Compression/ExrBaseDecompressor.cs +++ b/src/ImageSharp/Formats/Exr/Compression/ExrBaseDecompressor.cs @@ -6,15 +6,36 @@ using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Exr.Compression; +/// +/// The base EXR decompressor class. +/// internal abstract class ExrBaseDecompressor : ExrBaseCompression { + /// + /// Initializes a new instance of the class. + /// + /// The memory allocator. + /// The bytes per row block. + /// The bytes per row. protected ExrBaseDecompressor(MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow) : base(allocator, bytesPerBlock, bytesPerRow) { } + /// + /// Decompresses the specified stream. + /// + /// The buffered stream to decompress. + /// The compressed bytes. + /// The buffer to write the decompressed data to. public abstract void Decompress(BufferedReadStream stream, uint compressedBytes, Span buffer); + /// + /// Integrate over all differences to the previous value in order to + /// reconstruct sample values. + /// + /// The buffer with the data. + /// The un compressed bytes. protected static void Reconstruct(Span buffer, uint unCompressedBytes) { int offset = 0; @@ -26,6 +47,12 @@ internal abstract class ExrBaseDecompressor : ExrBaseCompression } } + /// + /// Interleaves the input data. + /// + /// The source data. + /// The uncompressed bytes. + /// The output to write to. protected static void Interleave(Span source, uint unCompressedBytes, Span output) { int sourceOffset = 0; diff --git a/src/ImageSharp/Formats/Exr/Compression/ExrCompressorFactory.cs b/src/ImageSharp/Formats/Exr/Compression/ExrCompressorFactory.cs index 24f396e16f..b1b7d2e8e6 100644 --- a/src/ImageSharp/Formats/Exr/Compression/ExrCompressorFactory.cs +++ b/src/ImageSharp/Formats/Exr/Compression/ExrCompressorFactory.cs @@ -8,27 +8,32 @@ using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Exr.Compression; +/// +/// Factory class for creating a compressor for EXR image data. +/// internal static class ExrCompressorFactory { + /// + /// Creates the specified exr data compressor. + /// + /// The compression method. + /// The memory allocator. + /// The output stream. + /// The bytes per block. + /// The bytes per row. + /// The deflate compression level. + /// A compressor for EXR image data. public static ExrBaseCompressor Create( ExrCompression method, MemoryAllocator allocator, Stream output, uint bytesPerBlock, uint bytesPerRow, - DeflateCompressionLevel compressionLevel = DeflateCompressionLevel.DefaultCompression) - { - switch (method) + DeflateCompressionLevel compressionLevel = DeflateCompressionLevel.DefaultCompression) => method switch { - case ExrCompression.None: - return new NoneExrCompressor(output, allocator, bytesPerBlock, bytesPerRow); - case ExrCompression.Zips: - return new ZipExrCompressor(output, allocator, bytesPerBlock, bytesPerRow, compressionLevel); - case ExrCompression.Zip: - return new ZipExrCompressor(output, allocator, bytesPerBlock, bytesPerRow, compressionLevel); - - default: - throw ExrThrowHelper.NotSupportedCompressor(method.ToString()); - } - } + ExrCompression.None => new NoneExrCompressor(output, allocator, bytesPerBlock, bytesPerRow), + ExrCompression.Zips => new ZipExrCompressor(output, allocator, bytesPerBlock, bytesPerRow, compressionLevel), + ExrCompression.Zip => new ZipExrCompressor(output, allocator, bytesPerBlock, bytesPerRow, compressionLevel), + _ => throw ExrThrowHelper.NotSupportedCompressor(method.ToString()), + }; } diff --git a/src/ImageSharp/Formats/Exr/Compression/ExrDecompressorFactory.cs b/src/ImageSharp/Formats/Exr/Compression/ExrDecompressorFactory.cs index 2696a289cd..62519666b5 100644 --- a/src/ImageSharp/Formats/Exr/Compression/ExrDecompressorFactory.cs +++ b/src/ImageSharp/Formats/Exr/Compression/ExrDecompressorFactory.cs @@ -7,8 +7,22 @@ using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Exr.Compression; +/// +/// The Factory class for creating a EXR data decompressor. +/// internal static class ExrDecompressorFactory { + /// + /// Creates a decomprssor for a specific EXR compression type. + /// + /// The compression method. + /// The memory allocator. + /// The width in pixels of the image. + /// The bytes per block. + /// The bytes per row. + /// The rows per block. + /// The number of image channels. + /// Decompressor for EXR image data. public static ExrBaseDecompressor Create( ExrCompression method, MemoryAllocator memoryAllocator, @@ -16,22 +30,13 @@ internal static class ExrDecompressorFactory uint bytesPerBlock, uint bytesPerRow, uint rowsPerBlock, - int channelCount) - { - switch (method) + int channelCount) => method switch { - case ExrCompression.None: - return new NoneExrCompression(memoryAllocator, bytesPerBlock, bytesPerRow); - case ExrCompression.Zips: - return new ZipExrCompression(memoryAllocator, bytesPerBlock, bytesPerRow); - case ExrCompression.Zip: - return new ZipExrCompression(memoryAllocator, bytesPerBlock, bytesPerRow); - case ExrCompression.RunLengthEncoded: - return new RunLengthExrCompression(memoryAllocator, bytesPerBlock, bytesPerRow); - case ExrCompression.B44: - return new B44ExrCompression(memoryAllocator, bytesPerBlock, bytesPerRow, rowsPerBlock, width, channelCount); - default: - throw ExrThrowHelper.NotSupportedDecompressor(nameof(method)); - } - } + ExrCompression.None => new NoneExrCompression(memoryAllocator, bytesPerBlock, bytesPerRow), + ExrCompression.Zips => new ZipExrCompression(memoryAllocator, bytesPerBlock, bytesPerRow), + ExrCompression.Zip => new ZipExrCompression(memoryAllocator, bytesPerBlock, bytesPerRow), + ExrCompression.RunLengthEncoded => new RunLengthExrCompression(memoryAllocator, bytesPerBlock, bytesPerRow), + ExrCompression.B44 => new B44ExrCompression(memoryAllocator, bytesPerBlock, bytesPerRow, rowsPerBlock, width, channelCount), + _ => throw ExrThrowHelper.NotSupportedDecompressor(nameof(method)), + }; } diff --git a/src/ImageSharp/Formats/Exr/Constants/ExrImageType.cs b/src/ImageSharp/Formats/Exr/Constants/ExrImageType.cs index beeabe35e1..9e2cd009ff 100644 --- a/src/ImageSharp/Formats/Exr/Constants/ExrImageType.cs +++ b/src/ImageSharp/Formats/Exr/Constants/ExrImageType.cs @@ -3,9 +3,19 @@ namespace SixLabors.ImageSharp.Formats.Exr.Constants; +/// +/// Enum for the differnt exr image type. +/// internal enum ExrImageType { + /// + /// The image data is stored in scan lines. + /// ScanLine = 0, + /// + /// The image data is stored in tile. + /// This is not yet supported. + /// Tiled = 1 } diff --git a/src/ImageSharp/Formats/Exr/Constants/ExrLineOrder.cs b/src/ImageSharp/Formats/Exr/Constants/ExrLineOrder.cs index 56573472ca..73e86d6ef1 100644 --- a/src/ImageSharp/Formats/Exr/Constants/ExrLineOrder.cs +++ b/src/ImageSharp/Formats/Exr/Constants/ExrLineOrder.cs @@ -3,11 +3,23 @@ namespace SixLabors.ImageSharp.Formats.Exr.Constants; +/// +/// Enum for the different scan line ordering. +/// internal enum ExrLineOrder : byte { + /// + /// The scan lines are written from top-to-bottom. + /// IncreasingY = 0, + /// + /// The scan lines are written from bottom-to-top. + /// DecreasingY = 1, + /// + /// The Scan lines are written in no particular oder. + /// RandomY = 2 } diff --git a/src/ImageSharp/Formats/Exr/ExrAttribute.cs b/src/ImageSharp/Formats/Exr/ExrAttribute.cs index b4e95f1d47..093c973919 100644 --- a/src/ImageSharp/Formats/Exr/ExrAttribute.cs +++ b/src/ImageSharp/Formats/Exr/ExrAttribute.cs @@ -5,11 +5,20 @@ using System.Diagnostics; namespace SixLabors.ImageSharp.Formats.Exr; +/// +/// Repressents an exr image attribute. +/// [DebuggerDisplay("Name: {Name}, Type: {Type}, Length: {Length}")] internal class ExrAttribute { public static readonly ExrAttribute EmptyAttribute = new(string.Empty, string.Empty, 0); + /// + /// Initializes a new instance of the class. + /// + /// The name of the attribute. + /// The type of the attribute. + /// The length in bytes. public ExrAttribute(string name, string type, int length) { this.Name = name; @@ -17,9 +26,18 @@ internal class ExrAttribute this.Length = length; } + /// + /// Gets the name of the attribute. + /// public string Name { get; } + /// + /// Gets the type of the attribute. + /// public string Type { get; } + /// + /// Gets the length in bytes of the attribute. + /// public int Length { get; } } diff --git a/src/ImageSharp/Formats/Exr/ExrBox2i.cs b/src/ImageSharp/Formats/Exr/ExrBox2i.cs index 032e60d929..c9c53d6486 100644 --- a/src/ImageSharp/Formats/Exr/ExrBox2i.cs +++ b/src/ImageSharp/Formats/Exr/ExrBox2i.cs @@ -5,9 +5,19 @@ using System.Diagnostics; namespace SixLabors.ImageSharp.Formats.Exr; +/// +/// Integer region definition. +/// [DebuggerDisplay("xMin: {XMin}, yMin: {YMin}, xMax: {XMax}, yMax: {YMax}")] internal readonly struct ExrBox2i { + /// + /// Initializes a new instance of the struct. + /// + /// The minimum x value. + /// The minimum y value. + /// The maximum x value. + /// The maximum y value. public ExrBox2i(int xMin, int yMin, int xMax, int yMax) { this.XMin = xMin; @@ -16,11 +26,23 @@ internal readonly struct ExrBox2i this.YMax = yMax; } + /// + /// Gets the minimum x value. + /// public int XMin { get; } + /// + /// Gets the minimum y value. + /// public int YMin { get; } + /// + /// Gets the maximum x value. + /// public int XMax { get; } + /// + /// Gets the maximum y value. + /// public int YMax { get; } } diff --git a/src/ImageSharp/Formats/Exr/ExrChannelInfo.cs b/src/ImageSharp/Formats/Exr/ExrChannelInfo.cs index d4f5825b99..5aae64f283 100644 --- a/src/ImageSharp/Formats/Exr/ExrChannelInfo.cs +++ b/src/ImageSharp/Formats/Exr/ExrChannelInfo.cs @@ -7,26 +7,55 @@ using SixLabors.ImageSharp.Formats.Exr.Constants; namespace SixLabors.ImageSharp.Formats.Exr; +/// +/// Information about a pixel channel. +/// [DebuggerDisplay("Name: {ChannelName}, PixelType: {PixelType}")] [StructLayout(LayoutKind.Sequential, Pack = 1)] internal readonly struct ExrChannelInfo { - public ExrChannelInfo(string channelName, ExrPixelType pixelType, byte pLinear, int xSampling, int ySampling) + /// + /// Initializes a new instance of the struct. + /// + /// Name of the channel. + /// The type of the pixel data. + /// Linear flag, possible values are 0 and 1. + /// X sampling. + /// Y sampling. + public ExrChannelInfo(string channelName, ExrPixelType pixelType, byte linear, int xSampling, int ySampling) { this.ChannelName = channelName; this.PixelType = pixelType; - this.PLinear = pLinear; + this.Linear = linear; this.XSampling = xSampling; this.YSampling = ySampling; } + /// + /// Gets the channel name. + /// public string ChannelName { get; } + + /// + /// Gets the type of the pixel data. + /// public ExrPixelType PixelType { get; } - public byte PLinear { get; } + /// + /// Gets the linear flag. Hint to lossy compression methods that indicates whether + /// human perception of the quantity represented by this channel + /// is closer to linear or closer to logarithmic. + /// + public byte Linear { get; } + /// + /// Gets the x sampling value. + /// public int XSampling { get; } + /// + /// Gets the y sampling value. + /// public int YSampling { get; } } diff --git a/src/ImageSharp/Formats/Exr/ExrDecoderCore.cs b/src/ImageSharp/Formats/Exr/ExrDecoderCore.cs index a4fa4b0ac2..0b75154c2c 100644 --- a/src/ImageSharp/Formats/Exr/ExrDecoderCore.cs +++ b/src/ImageSharp/Formats/Exr/ExrDecoderCore.cs @@ -120,6 +120,21 @@ internal sealed class ExrDecoderCore : ImageDecoderCore return image; } + /// + protected override ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) + { + ExrHeaderAttributes header = this.ReadExrHeader(stream); + + return new ImageInfo(new Size(header.DataWindow.XMax, header.DataWindow.YMax), this.metadata); + } + + /// + /// Decodes image data with floating point pixel data. + /// + /// The type of the pixels. + /// The stream to read from. + /// The pixel buffer. + /// The cancellation token. private void DecodeFloatingPointPixelData(BufferedReadStream stream, Buffer2D pixels, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { @@ -178,6 +193,13 @@ internal sealed class ExrDecoderCore : ImageDecoderCore } } + /// + /// Decodes image data with unsigned int pixel data. + /// + /// The type of the pixels. + /// The stream to read from. + /// The pixel buffer. + /// The cancellation token. private void DecodeUnsignedIntPixelData(BufferedReadStream stream, Buffer2D pixels, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { @@ -236,6 +258,18 @@ internal sealed class ExrDecoderCore : ImageDecoderCore } } + /// + /// Reads float image channel data. + /// + /// The stream to read from. + /// The channel info. + /// The decompressed pixel data. + /// The red channel pixel data. + /// The green channel pixel data. + /// The blue channel pixel data. + /// The alpha channel pixel data. + /// The width of a row in pixels. + /// The bytes read. private static int ReadFloatChannelData( BufferedReadStream stream, ExrChannelInfo channel, @@ -275,6 +309,18 @@ internal sealed class ExrDecoderCore : ImageDecoderCore } } + /// + /// Reads UINT image channel data. + /// + /// The stream to read from. + /// The channel info. + /// The decompressed pixel data. + /// The red channel pixel data. + /// The green channel pixel data. + /// The blue channel pixel data. + /// The alpha channel pixel data. + /// The width of a row in pixels. + /// The bytes read. private int ReadUnsignedIntChannelData( BufferedReadStream stream, ExrChannelInfo channel, @@ -313,6 +359,14 @@ internal sealed class ExrDecoderCore : ImageDecoderCore } } + /// + /// Reads the channel data for pixel type HALF or FLOAT. + /// + /// The channel info. + /// The decompressed pixel data. + /// The pixel data as float. + /// The width in pixel of a row. + /// The bytes read. private static int ReadChannelData(ExrChannelInfo channel, Span decompressedPixelData, Span pixelData, int width) => channel.PixelType switch { ExrPixelType.Half => ReadPixelRowChannelHalfSingle(decompressedPixelData, pixelData, width), @@ -320,12 +374,27 @@ internal sealed class ExrDecoderCore : ImageDecoderCore _ => 0, }; + /// + /// Reads the channel data for pixel type UINT. + /// + /// The channel info. + /// The decompressed pixel data. + /// The pixel data as uint. + /// The width in pixels. + /// The bytes read. private static int ReadChannelData(ExrChannelInfo channel, Span decompressedPixelData, Span pixelData, int width) => channel.PixelType switch { ExrPixelType.UnsignedInt => ReadPixelRowChannelUnsignedInt(decompressedPixelData, pixelData, width), _ => 0, }; + /// + /// Reads a pixel row with the pixel data being 16 bit half values. + /// + /// The decompressed pixel data. + /// The channel data as float. + /// The width of a row in pixels. + /// The bytes read. private static int ReadPixelRowChannelHalfSingle(Span decompressedPixelData, Span channelData, int width) { int offset = 0; @@ -339,6 +408,13 @@ internal sealed class ExrDecoderCore : ImageDecoderCore return offset; } + /// + /// Reads a pixel row with 32 bit float pixel data. + /// + /// The decompressed pixel data. + /// The pixel data as float. + /// The width in pixels of a row. + /// The bytes read. private static int ReadPixelRowChannelSingle(Span decompressedPixelData, Span channelData, int width) { int offset = 0; @@ -352,6 +428,13 @@ internal sealed class ExrDecoderCore : ImageDecoderCore return offset; } + /// + /// Reads a pixel row with the pixel typ UINT. + /// + /// The decompressed pixel bytes. + /// The uint pixel data. + /// The width of a row in pixels. + /// The bytes read. private static int ReadPixelRowChannelUnsignedInt(Span decompressedPixelData, Span channelData, int width) { int offset = 0; @@ -364,14 +447,10 @@ internal sealed class ExrDecoderCore : ImageDecoderCore return offset; } - /// - protected override ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) - { - ExrHeaderAttributes header = this.ReadExrHeader(stream); - - return new ImageInfo(new Size(header.DataWindow.XMax, header.DataWindow.YMax), this.metadata); - } - + /// + /// Validates that all image channels have the same type and are among the supported pixel types. + /// + /// The pixel type. private ExrPixelType ValidateChannels() { if (this.Channels.Count == 0) @@ -380,12 +459,42 @@ internal sealed class ExrDecoderCore : ImageDecoderCore } // Find pixel the type of any channel which is R, G, B or A. - ExrPixelType pixelType = this.FindPixelType(); + ExrPixelType? pixelType = null; + for (int i = 0; i < this.Channels.Count; i++) + { + if (this.Channels[i].ChannelName.Equals(ExrConstants.ChannelNames.Blue, StringComparison.Ordinal) || + this.Channels[i].ChannelName.Equals(ExrConstants.ChannelNames.Green, StringComparison.Ordinal) || + this.Channels[i].ChannelName.Equals(ExrConstants.ChannelNames.Red, StringComparison.Ordinal) || + this.Channels[i].ChannelName.Equals(ExrConstants.ChannelNames.Alpha, StringComparison.Ordinal) || + this.Channels[i].ChannelName.Equals(ExrConstants.ChannelNames.Luminance, StringComparison.Ordinal)) + { + if (!pixelType.HasValue) + { + pixelType = this.Channels[i].PixelType; + } + else + { + if (pixelType != this.Channels[i].PixelType) + { + ExrThrowHelper.ThrowNotSupported("Pixel channel data is expected to be the same for all channels."); + } + } + } + } - return pixelType; + if (!pixelType.HasValue) + { + ExrThrowHelper.ThrowNotSupported("Pixel channel data is unknown! Only R, G, B, A and Y are supported."); + } + + return pixelType.Value; } - private ExrImageDataType ReadImageDataType() + /// + /// Determines the type image from the channel information. + /// + /// The image data type. + private ExrImageDataType DetermineImageDataType() { bool hasRedChannel = false; bool hasGreenChannel = false; @@ -438,6 +547,12 @@ internal sealed class ExrDecoderCore : ImageDecoderCore return ExrImageDataType.Unknown; } + /// + /// Reads the exr image header. + /// + /// + /// The stream. + /// The image header attributes. private ExrHeaderAttributes ReadExrHeader(BufferedReadStream stream) { // Skip over the magick bytes, we already know its an EXR image. @@ -471,7 +586,7 @@ internal sealed class ExrDecoderCore : ImageDecoderCore this.Channels = this.HeaderAttributes.Channels; this.Compression = this.HeaderAttributes.Compression; this.PixelType = this.ValidateChannels(); - this.ImageDataType = this.ReadImageDataType(); + this.ImageDataType = this.DetermineImageDataType(); this.metadata = new ImageMetadata(); @@ -483,6 +598,11 @@ internal sealed class ExrDecoderCore : ImageDecoderCore return this.HeaderAttributes; } + /// + /// Parses the image header attributes. + /// + /// The stream to read from. + /// The image header attributes. private ExrHeaderAttributes ParseHeaderAttributes(BufferedReadStream stream) { ExrAttribute attribute = this.ReadAttribute(stream); @@ -599,6 +719,11 @@ internal sealed class ExrDecoderCore : ImageDecoderCore return header; } + /// + /// Reads a attrbute from the stream, which consist of a name, a type and a size in bytes. + /// + /// The stream to read from. + /// A attribute. private ExrAttribute ReadAttribute(BufferedReadStream stream) { string attributeName = ReadString(stream); @@ -608,12 +733,16 @@ internal sealed class ExrDecoderCore : ImageDecoderCore } string attributeType = ReadString(stream); - int attributeSize = this.ReadSignedInteger(stream); return new ExrAttribute(attributeName, attributeType, attributeSize); } + /// + /// Reads a box attribute, which is a xMin, xMax and yMin, yMax value. + /// + /// The stream to reaad from. + /// A box struct. private ExrBox2i ReadBoxInteger(BufferedReadStream stream) { int xMin = this.ReadSignedInteger(stream); @@ -624,6 +753,12 @@ internal sealed class ExrDecoderCore : ImageDecoderCore return new ExrBox2i(xMin, yMin, xMax, yMax); } + /// + /// Reads the channel list from the stream. + /// + /// The stream to read from. + /// The size in bytes of the channel list attribute. + /// The channel list. private List ReadChannelList(BufferedReadStream stream, int attributeSize) { List channels = []; @@ -637,12 +772,18 @@ internal sealed class ExrDecoderCore : ImageDecoderCore // Last byte should be a null byte. if (stream.ReadByte() == -1) { - ExrThrowHelper.ThrowInvalidImageContentException("Could not read enough data to read exr channel list!"); + ExrThrowHelper.ThrowInvalidImageContentException("Could not read enough data to read the exr channel list!"); } return channels; } + /// + /// Reads the channel information from the stream. + /// + /// The stream to read from. + /// The bytes read. + /// Channel info. private ExrChannelInfo ReadChannelInfo(BufferedReadStream stream, out int bytesRead) { string channelName = ReadString(stream); @@ -670,6 +811,11 @@ internal sealed class ExrDecoderCore : ImageDecoderCore return new ExrChannelInfo(channelName, pixelType, pLinear, xSampling, ySampling); } + /// + /// Reads a the string from the stream. + /// + /// The stream to read from. + /// A string. private static string ReadString(BufferedReadStream stream) { StringBuilder str = new(); @@ -694,45 +840,20 @@ internal sealed class ExrDecoderCore : ImageDecoderCore return str.ToString(); } - private ExrPixelType FindPixelType() - { - ExrPixelType? pixelType = null; - for (int i = 0; i < this.Channels.Count; i++) - { - if (this.Channels[i].ChannelName.Equals(ExrConstants.ChannelNames.Blue, StringComparison.Ordinal) || - this.Channels[i].ChannelName.Equals(ExrConstants.ChannelNames.Green, StringComparison.Ordinal) || - this.Channels[i].ChannelName.Equals(ExrConstants.ChannelNames.Red, StringComparison.Ordinal) || - this.Channels[i].ChannelName.Equals(ExrConstants.ChannelNames.Alpha, StringComparison.Ordinal) || - this.Channels[i].ChannelName.Equals(ExrConstants.ChannelNames.Luminance, StringComparison.Ordinal)) - { - if (!pixelType.HasValue) - { - pixelType = this.Channels[i].PixelType; - } - else - { - if (pixelType != this.Channels[i].PixelType) - { - ExrThrowHelper.ThrowNotSupported("Pixel channel data is expected to be the same for all channels."); - } - } - } - } - - if (!pixelType.HasValue) - { - ExrThrowHelper.ThrowNotSupported("Pixel channel data is unknown! Only R, G, B, A and Y are supported."); - } - - return pixelType.Value; - } - + /// + /// Determines whether the compression is supported. + /// + /// True if the compression is supported; otherwise, false>. private bool IsSupportedCompression() => this.Compression switch { ExrCompression.None or ExrCompression.Zip or ExrCompression.Zips or ExrCompression.RunLengthEncoded or ExrCompression.B44 => true, _ => false, }; + /// + /// Determines whether this image has alpha channel. + /// + /// True if this image has a alpha channel; otherwise, false. private bool HasAlpha() { foreach (ExrChannelInfo channelInfo in this.Channels) diff --git a/src/ImageSharp/Formats/Exr/ExrEncoderCore.cs b/src/ImageSharp/Formats/Exr/ExrEncoderCore.cs index 9f86e79e65..56584132b8 100644 --- a/src/ImageSharp/Formats/Exr/ExrEncoderCore.cs +++ b/src/ImageSharp/Formats/Exr/ExrEncoderCore.cs @@ -147,6 +147,18 @@ internal sealed class ExrEncoderCore } } + /// + /// Encodes and writes pixel data with float pixel data to the stream. + /// + /// The type of the pixels. + /// The stream to write to. + /// The pixel bufer. + /// The width of the image in pixels. + /// The height of the image in pixels. + /// The imagechannels. + /// The compression to use. + /// The cancellation token. + /// The array of pixel row offsets. private ulong[] EncodeFloatingPointPixelData( Stream stream, Buffer2D pixels, @@ -227,6 +239,18 @@ internal sealed class ExrEncoderCore return rowOffsets; } + /// + /// Encodes and writes pixel data with the unsigned int pixel type to the stream. + /// + /// The type of the pixels. + /// The stream to write to. + /// The pixel bufer. + /// The width of the image in pixels. + /// The height of the image in pixels. + /// The imagechannels. + /// The compression to use. + /// The cancellation token. + /// The array of pixel row offsets. private ulong[] EncodeUnsignedIntPixelData( Stream stream, Buffer2D pixels, @@ -301,6 +325,11 @@ internal sealed class ExrEncoderCore return rowOffsets; } + /// + /// Writes the image header to the stream. + /// + /// The stream to write to. + /// The header. private void WriteHeader(Stream stream, ExrHeaderAttributes header) { this.WriteChannels(stream, header.Channels); @@ -314,6 +343,15 @@ internal sealed class ExrEncoderCore stream.WriteByte(0); } + /// + /// Writes a row of pixels with the FLOAT pixel type to a buffer. + /// + /// The buffer to write to. + /// The width of a row in pixels. + /// The alpha channel buffer. + /// The blue channel buffer. + /// The green channel buffer. + /// The red channel buffer. private static void WriteSingleRow(Span buffer, int width, Span alphaBuffer, Span blueBuffer, Span greenBuffer, Span redBuffer) { int offset = 0; @@ -342,6 +380,15 @@ internal sealed class ExrEncoderCore } } + /// + /// Writes a row of pixels with the HALF pixel type to a buffer. + /// + /// The buffer to write to. + /// The width of a row in pixels. + /// The alpha channel buffer. + /// The blue channel buffer. + /// The green channel buffer. + /// The red channel buffer. private static void WriteHalfSingleRow(Span buffer, int width, Span alphaBuffer, Span blueBuffer, Span greenBuffer, Span redBuffer) { int offset = 0; @@ -370,6 +417,15 @@ internal sealed class ExrEncoderCore } } + /// + /// Writes a row of pixels with unsigned int pixel data to a buffer. + /// + /// The buffer to write to. + /// The width of the row in pixels. + /// The alpha channel buffer. + /// The blue channel buffer. + /// The green channel buffer. + /// The red channel buffer. private static void WriteUnsignedIntRow(Span buffer, int width, Span alphaBuffer, Span blueBuffer, Span greenBuffer, Span redBuffer) { int offset = 0; @@ -398,6 +454,12 @@ internal sealed class ExrEncoderCore } } + /// + /// Writes the row offsets to the stream. + /// + /// The stream to write to. + /// The height in pixels of the image. + /// The row offsets. private void WriteRowOffsets(Stream stream, int height, ulong[] rowOffsets) { for (int i = 0; i < height; i++) @@ -407,6 +469,11 @@ internal sealed class ExrEncoderCore } } + /// + /// Writes the channel infos to the stream. + /// + /// The stream to write to. + /// The channels. private void WriteChannels(Stream stream, IList channels) { int attributeSize = 0; @@ -429,24 +496,70 @@ internal sealed class ExrEncoderCore stream.WriteByte(0); } + /// + /// Writes info about a single channel to the stream. + /// + /// The stream to write to. + /// The channel information. + private void WriteChannelInfo(Stream stream, ExrChannelInfo channelInfo) + { + WriteString(stream, channelInfo.ChannelName); + + BinaryPrimitives.WriteInt32LittleEndian(this.buffer, (int)channelInfo.PixelType); + stream.Write(this.buffer.AsSpan(0, 4)); + + stream.WriteByte(channelInfo.Linear); + + // Next 3 bytes are reserved and will set to zero. + stream.WriteByte(0); + stream.WriteByte(0); + stream.WriteByte(0); + + BinaryPrimitives.WriteInt32LittleEndian(this.buffer, channelInfo.XSampling); + stream.Write(this.buffer.AsSpan(0, 4)); + + BinaryPrimitives.WriteInt32LittleEndian(this.buffer, channelInfo.YSampling); + stream.Write(this.buffer.AsSpan(0, 4)); + } + + /// + /// Writes the compression type to the stream. + /// + /// The stream to write to. + /// The compression type. private void WriteCompression(Stream stream, ExrCompression compression) { this.WriteAttributeInformation(stream, ExrConstants.AttributeNames.Compression, ExrConstants.AttibuteTypes.Compression, 1); stream.WriteByte((byte)compression); } + /// + /// Writes the pixel aspect ratio to the stream. + /// + /// The stream to write to. + /// The aspect ratio. private void WritePixelAspectRatio(Stream stream, float aspectRatio) { this.WriteAttributeInformation(stream, ExrConstants.AttributeNames.PixelAspectRatio, ExrConstants.AttibuteTypes.Float, 4); this.WriteSingle(stream, aspectRatio); } + /// + /// Writes the line order to the stream. + /// + /// The stream to write to. + /// The line order. private void WriteLineOrder(Stream stream, ExrLineOrder lineOrder) { this.WriteAttributeInformation(stream, ExrConstants.AttributeNames.LineOrder, ExrConstants.AttibuteTypes.LineOrder, 1); stream.WriteByte((byte)lineOrder); } + /// + /// Writes the screen window center to the stream. + /// + /// The stream to write to. + /// The screen window center. private void WriteScreenWindowCenter(Stream stream, PointF screenWindowCenter) { this.WriteAttributeInformation(stream, ExrConstants.AttributeNames.ScreenWindowCenter, ExrConstants.AttibuteTypes.TwoFloat, 8); @@ -454,24 +567,46 @@ internal sealed class ExrEncoderCore this.WriteSingle(stream, screenWindowCenter.Y); } + /// + /// Writes the screen width to the stream. + /// + /// The stream to write to. + /// Width of the screen window. private void WriteScreenWindowWidth(Stream stream, float screenWindowWidth) { this.WriteAttributeInformation(stream, ExrConstants.AttributeNames.ScreenWindowWidth, ExrConstants.AttibuteTypes.Float, 4); this.WriteSingle(stream, screenWindowWidth); } + /// + /// Writes the data window to the stream. + /// + /// The stream to write to. + /// The data window. private void WriteDataWindow(Stream stream, ExrBox2i dataWindow) { this.WriteAttributeInformation(stream, ExrConstants.AttributeNames.DataWindow, ExrConstants.AttibuteTypes.BoxInt, 16); this.WriteBoxInteger(stream, dataWindow); } + /// + /// Writes the display window to the stream. + /// + /// The stream to write to. + /// The display window. private void WriteDisplayWindow(Stream stream, ExrBox2i displayWindow) { this.WriteAttributeInformation(stream, ExrConstants.AttributeNames.DisplayWindow, ExrConstants.AttibuteTypes.BoxInt, 16); this.WriteBoxInteger(stream, displayWindow); } + /// + /// Writes attribute information to the stream. + /// + /// The stream to write to. + /// The name of the attribute. + /// The type of the attribute. + /// The size in bytes of the attribute. private void WriteAttributeInformation(Stream stream, string name, string type, int size) { // Write attribute name. @@ -485,27 +620,11 @@ internal sealed class ExrEncoderCore stream.Write(this.buffer.AsSpan(0, 4)); } - private void WriteChannelInfo(Stream stream, ExrChannelInfo channelInfo) - { - WriteString(stream, channelInfo.ChannelName); - - BinaryPrimitives.WriteInt32LittleEndian(this.buffer, (int)channelInfo.PixelType); - stream.Write(this.buffer.AsSpan(0, 4)); - - stream.WriteByte(channelInfo.PLinear); - - // Next 3 bytes are reserved and will set to zero. - stream.WriteByte(0); - stream.WriteByte(0); - stream.WriteByte(0); - - BinaryPrimitives.WriteInt32LittleEndian(this.buffer, channelInfo.XSampling); - stream.Write(this.buffer.AsSpan(0, 4)); - - BinaryPrimitives.WriteInt32LittleEndian(this.buffer, channelInfo.YSampling); - stream.Write(this.buffer.AsSpan(0, 4)); - } - + /// + /// Writes a string to the stream. + /// + /// The stream to write to. + /// The string to write. private static void WriteString(Stream stream, string str) { foreach (char c in str) @@ -517,6 +636,11 @@ internal sealed class ExrEncoderCore stream.WriteByte(0); } + /// + /// Writes box struct with xmin, xmax, ymin and y max to the stream. + /// + /// The stream to write to. + /// The box to write. private void WriteBoxInteger(Stream stream, ExrBox2i box) { BinaryPrimitives.WriteInt32LittleEndian(this.buffer, box.XMin); @@ -532,6 +656,11 @@ internal sealed class ExrEncoderCore stream.Write(this.buffer.AsSpan(0, 4)); } + /// + /// Writes 32 bit float value to the stream. + /// + /// The stream to write to. + /// The float value to write. [MethodImpl(InliningOptions.ShortMethod)] private unsafe void WriteSingle(Stream stream, float value) { @@ -539,9 +668,19 @@ internal sealed class ExrEncoderCore stream.Write(this.buffer.AsSpan(0, 4)); } + /// + /// Writes a 32 bit float value to a buffer. + /// + /// The buffer to write to. + /// The float value to write. [MethodImpl(InliningOptions.ShortMethod)] private static unsafe void WriteSingleToBuffer(Span buffer, float value) => BinaryPrimitives.WriteInt32LittleEndian(buffer, *(int*)&value); + /// + /// Writes a 16 bit float value to a buffer. + /// + /// The buffer to write to. + /// The float value to write. [MethodImpl(InliningOptions.ShortMethod)] private static void WriteHalfSingleToBuffer(Span buffer, float value) { @@ -549,6 +688,11 @@ internal sealed class ExrEncoderCore BinaryPrimitives.WriteUInt16LittleEndian(buffer, valueAsShort); } + /// + /// Writes one unsigned int to a buffer. + /// + /// The buffer to write to. + /// The uint value to write. [MethodImpl(InliningOptions.ShortMethod)] private static void WriteUnsignedIntToBuffer(Span buffer, uint value) => BinaryPrimitives.WriteUInt32LittleEndian(buffer, value); } diff --git a/src/ImageSharp/Formats/Exr/ExrHeaderAttributes.cs b/src/ImageSharp/Formats/Exr/ExrHeaderAttributes.cs index cdcddd1175..9a6ce7056f 100644 --- a/src/ImageSharp/Formats/Exr/ExrHeaderAttributes.cs +++ b/src/ImageSharp/Formats/Exr/ExrHeaderAttributes.cs @@ -11,6 +11,20 @@ namespace SixLabors.ImageSharp.Formats.Exr; /// internal class ExrHeaderAttributes { + /// + /// Initializes a new instance of the class. + /// + /// The image channels. + /// The compression used. + /// The data window. + /// The display window. + /// The line order. + /// The aspect ratio. + /// Width of the screen window. + /// The screen window center. + /// Size of the tile in x dimension. + /// Size of the tile in y dimension. + /// The chunk count. public ExrHeaderAttributes( IList channels, ExrCompression compression,