Browse Source

Add additional doc strings

pull/3096/head
Brian Popow 3 weeks ago
parent
commit
1e69aa45ab
  1. 10
      src/ImageSharp/Formats/Exr/Compression/Compressors/NoneExrCompressor.cs
  2. 11
      src/ImageSharp/Formats/Exr/Compression/Compressors/ZipExrCompressor.cs
  3. 44
      src/ImageSharp/Formats/Exr/Compression/Decompressors/B44ExrCompression.cs
  4. 11
      src/ImageSharp/Formats/Exr/Compression/Decompressors/NoneExrCompression.cs
  5. 16
      src/ImageSharp/Formats/Exr/Compression/Decompressors/RunLengthExrCompression.cs
  6. 11
      src/ImageSharp/Formats/Exr/Compression/Decompressors/ZipExrCompression.cs
  7. 9
      src/ImageSharp/Formats/Exr/Compression/ExrBaseCompression.cs
  8. 27
      src/ImageSharp/Formats/Exr/Compression/ExrBaseDecompressor.cs
  9. 33
      src/ImageSharp/Formats/Exr/Compression/ExrCompressorFactory.cs
  10. 39
      src/ImageSharp/Formats/Exr/Compression/ExrDecompressorFactory.cs
  11. 10
      src/ImageSharp/Formats/Exr/Constants/ExrImageType.cs
  12. 12
      src/ImageSharp/Formats/Exr/Constants/ExrLineOrder.cs
  13. 18
      src/ImageSharp/Formats/Exr/ExrAttribute.cs
  14. 22
      src/ImageSharp/Formats/Exr/ExrBox2i.cs
  15. 35
      src/ImageSharp/Formats/Exr/ExrChannelInfo.cs
  16. 215
      src/ImageSharp/Formats/Exr/ExrDecoderCore.cs
  17. 186
      src/ImageSharp/Formats/Exr/ExrEncoderCore.cs
  18. 14
      src/ImageSharp/Formats/Exr/ExrHeaderAttributes.cs

10
src/ImageSharp/Formats/Exr/Compression/Compressors/NoneExrCompressor.cs

@ -5,8 +5,18 @@ using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Exr.Compression.Compressors;
/// <summary>
/// Compressor for EXR image data which does not use any compression method.
/// </summary>
internal class NoneExrCompressor : ExrBaseCompressor
{
/// <summary>
/// Initializes a new instance of the <see cref="NoneExrCompressor"/> class.
/// </summary>
/// <param name="output">The output stream to write the compressed image data to.</param>
/// <param name="allocator">The memory allocator.</param>
/// <param name="bytesPerBlock">Bytes per row block.</param>
/// <param name="bytesPerRow">Bytes per pixel row.</param>
public NoneExrCompressor(Stream output, MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow)
: base(output, allocator, bytesPerBlock, bytesPerRow)
{

11
src/ImageSharp/Formats/Exr/Compression/Compressors/ZipExrCompressor.cs

@ -6,6 +6,9 @@ using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Exr.Compression.Compressors;
/// <summary>
/// Compressor for EXR image data using the ZIP compression.
/// </summary>
internal class ZipExrCompressor : ExrBaseCompressor
{
private readonly DeflateCompressionLevel compressionLevel;
@ -14,6 +17,14 @@ internal class ZipExrCompressor : ExrBaseCompressor
private readonly System.Buffers.IMemoryOwner<byte> buffer;
/// <summary>
/// Initializes a new instance of the <see cref="ZipExrCompressor"/> class.
/// </summary>
/// <param name="output">The stream to write the compressed data to.</param>
/// <param name="allocator">The memory allocator.</param>
/// <param name="bytesPerBlock">The bytes per block.</param>
/// <param name="bytesPerRow">The bytes per row.</param>
/// <param name="compressionLevel">The compression level for deflate compression.</param>
public ZipExrCompressor(Stream output, MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow, DeflateCompressionLevel compressionLevel)
: base(output, allocator, bytesPerBlock, bytesPerRow)
{

44
src/ImageSharp/Formats/Exr/Compression/Decompressors/B44ExrCompression.cs

@ -8,6 +8,9 @@ using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Exr.Compression.Decompressors;
/// <summary>
/// Implementation of B44 decompressor for EXR image data.
/// </summary>
internal class B44ExrCompression : ExrBaseDecompressor
{
private readonly int width;
@ -22,6 +25,15 @@ internal class B44ExrCompression : ExrBaseDecompressor
private readonly IMemoryOwner<ushort> tmpBuffer;
/// <summary>
/// Initializes a new instance of the <see cref="B44ExrCompression" /> class.
/// </summary>
/// <param name="allocator">The memory allocator.</param>
/// <param name="bytesPerBlock">The bytes per pixel row block.</param>
/// <param name="bytesPerRow">The bytes per row.</param>
/// <param name="rowsPerBlock">The rows per block.</param>
/// <param name="width">The width of a pixel row in pixels.</param>
/// <param name="channelCount">The number of channels of the image.</param>
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.
/// <summary>
/// Unpack a 14-byte block into 4 by 4 16-bit pixels.
/// </summary>
/// <param name="b">The source byte data to unpack.</param>
/// <param name="s">Destintation buffer.</param>
private static void Unpack14(Span<byte> b, Span<ushort> 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.
/// <summary>
/// // Unpack a 3-byte block into 4 by 4 identical 16-bit pixels.
/// </summary>
/// <param name="b">The source byte data to unpack.</param>
/// <param name="s">The destination buffer.</param>
private static void Unpack3(Span<byte> b, Span<ushort> s)
{
s[0] = (ushort)((b[0] << 8) | b[1]);

11
src/ImageSharp/Formats/Exr/Compression/Decompressors/NoneExrCompression.cs

@ -6,8 +6,17 @@ using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Exr.Compression.Decompressors;
/// <summary>
/// Decompressor for EXR image data which do not use any compression.
/// </summary>
internal class NoneExrCompression : ExrBaseDecompressor
{
/// <summary>
/// Initializes a new instance of the <see cref="NoneExrCompression" /> class.
/// </summary>
/// <param name="allocator">The memory allocator.</param>
/// <param name="bytesPerBlock">The bytes per pixel row block.</param>
/// <param name="bytesPerRow">The bytes per pixel row.</param>
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!");
}
}

16
src/ImageSharp/Formats/Exr/Compression/Decompressors/RunLengthExrCompression.cs

@ -7,12 +7,21 @@ using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Exr.Compression.Decompressors;
/// <summary>
/// Implementation of RLE decompressor for EXR images.
/// </summary>
internal class RunLengthExrCompression : ExrBaseDecompressor
{
private readonly IMemoryOwner<byte> tmpBuffer;
private readonly ushort[] s = new ushort[16];
/// <summary>
/// Initializes a new instance of the <see cref="RunLengthExrCompression" /> class.
/// </summary>
/// <param name="allocator">The memory allocator.</param>
/// <param name="bytesPerBlock">The bytes per pixel row block.</param>
/// <param name="bytesPerRow">The bytes per row.</param>
public RunLengthExrCompression(MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow)
: base(allocator, bytesPerBlock, bytesPerRow) => this.tmpBuffer = allocator.Allocate<byte>((int)bytesPerBlock);
@ -68,12 +77,17 @@ internal class RunLengthExrCompression : ExrBaseDecompressor
Interleave(uncompressed, this.BytesPerBlock, buffer);
}
/// <summary>
/// Reads the next byte from the stream.
/// </summary>
/// <param name="stream">The stream.</param>
/// <returns>The next byte.</returns>
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;

11
src/ImageSharp/Formats/Exr/Compression/Decompressors/ZipExrCompression.cs

@ -9,10 +9,19 @@ using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Exr.Compression.Decompressors;
/// <summary>
/// Implementation of zhe Zip decompressor for EXR image data.
/// </summary>
internal class ZipExrCompression : ExrBaseDecompressor
{
private readonly IMemoryOwner<byte> tmpBuffer;
/// <summary>
/// Initializes a new instance of the <see cref="ZipExrCompression" /> class.
/// </summary>
/// <param name="allocator">The memory allocator.</param>
/// <param name="bytesPerBlock">The bytes per pixel row block.</param>
/// <param name="bytesPerRow">The bytes per pixel row.</param>
public ZipExrCompression(MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow)
: base(allocator, bytesPerBlock, bytesPerRow) => this.tmpBuffer = allocator.Allocate<byte>((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);

9
src/ImageSharp/Formats/Exr/Compression/ExrBaseCompression.cs

@ -5,10 +5,19 @@ using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Exr.Compression;
/// <summary>
/// Base class for EXR compression.
/// </summary>
internal abstract class ExrBaseCompression : IDisposable
{
private bool isDisposed;
/// <summary>
/// Initializes a new instance of the <see cref="ExrBaseCompression" /> class.
/// </summary>
/// <param name="allocator">The memory allocator.</param>
/// <param name="bytesPerBlock">The bytes per block.</param>
/// <param name="bytesPerRow">The bytes per row.</param>
protected ExrBaseCompression(MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow)
{
this.Allocator = allocator;

27
src/ImageSharp/Formats/Exr/Compression/ExrBaseDecompressor.cs

@ -6,15 +6,36 @@ using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Exr.Compression;
/// <summary>
/// The base EXR decompressor class.
/// </summary>
internal abstract class ExrBaseDecompressor : ExrBaseCompression
{
/// <summary>
/// Initializes a new instance of the <see cref="ExrBaseDecompressor" /> class.
/// </summary>
/// <param name="allocator">The memory allocator.</param>
/// <param name="bytesPerBlock">The bytes per row block.</param>
/// <param name="bytesPerRow">The bytes per row.</param>
protected ExrBaseDecompressor(MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow)
: base(allocator, bytesPerBlock, bytesPerRow)
{
}
/// <summary>
/// Decompresses the specified stream.
/// </summary>
/// <param name="stream">The buffered stream to decompress.</param>
/// <param name="compressedBytes">The compressed bytes.</param>
/// <param name="buffer">The buffer to write the decompressed data to.</param>
public abstract void Decompress(BufferedReadStream stream, uint compressedBytes, Span<byte> buffer);
/// <summary>
/// Integrate over all differences to the previous value in order to
/// reconstruct sample values.
/// </summary>
/// <param name="buffer">The buffer with the data.</param>
/// <param name="unCompressedBytes">The un compressed bytes.</param>
protected static void Reconstruct(Span<byte> buffer, uint unCompressedBytes)
{
int offset = 0;
@ -26,6 +47,12 @@ internal abstract class ExrBaseDecompressor : ExrBaseCompression
}
}
/// <summary>
/// Interleaves the input data.
/// </summary>
/// <param name="source">The source data.</param>
/// <param name="unCompressedBytes">The uncompressed bytes.</param>
/// <param name="output">The output to write to.</param>
protected static void Interleave(Span<byte> source, uint unCompressedBytes, Span<byte> output)
{
int sourceOffset = 0;

33
src/ImageSharp/Formats/Exr/Compression/ExrCompressorFactory.cs

@ -8,27 +8,32 @@ using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Exr.Compression;
/// <summary>
/// Factory class for creating a compressor for EXR image data.
/// </summary>
internal static class ExrCompressorFactory
{
/// <summary>
/// Creates the specified exr data compressor.
/// </summary>
/// <param name="method">The compression method.</param>
/// <param name="allocator">The memory allocator.</param>
/// <param name="output">The output stream.</param>
/// <param name="bytesPerBlock">The bytes per block.</param>
/// <param name="bytesPerRow">The bytes per row.</param>
/// <param name="compressionLevel">The deflate compression level.</param>
/// <returns>A compressor for EXR image data.</returns>
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()),
};
}

39
src/ImageSharp/Formats/Exr/Compression/ExrDecompressorFactory.cs

@ -7,8 +7,22 @@ using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Exr.Compression;
/// <summary>
/// The Factory class for creating a EXR data decompressor.
/// </summary>
internal static class ExrDecompressorFactory
{
/// <summary>
/// Creates a decomprssor for a specific EXR compression type.
/// </summary>
/// <param name="method">The compression method.</param>
/// <param name="memoryAllocator">The memory allocator.</param>
/// <param name="width">The width in pixels of the image.</param>
/// <param name="bytesPerBlock">The bytes per block.</param>
/// <param name="bytesPerRow">The bytes per row.</param>
/// <param name="rowsPerBlock">The rows per block.</param>
/// <param name="channelCount">The number of image channels.</param>
/// <returns>Decompressor for EXR image data.</returns>
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)),
};
}

10
src/ImageSharp/Formats/Exr/Constants/ExrImageType.cs

@ -3,9 +3,19 @@
namespace SixLabors.ImageSharp.Formats.Exr.Constants;
/// <summary>
/// Enum for the differnt exr image type.
/// </summary>
internal enum ExrImageType
{
/// <summary>
/// The image data is stored in scan lines.
/// </summary>
ScanLine = 0,
/// <summary>
/// The image data is stored in tile.
/// This is not yet supported.
/// </summary>
Tiled = 1
}

12
src/ImageSharp/Formats/Exr/Constants/ExrLineOrder.cs

@ -3,11 +3,23 @@
namespace SixLabors.ImageSharp.Formats.Exr.Constants;
/// <summary>
/// Enum for the different scan line ordering.
/// </summary>
internal enum ExrLineOrder : byte
{
/// <summary>
/// The scan lines are written from top-to-bottom.
/// </summary>
IncreasingY = 0,
/// <summary>
/// The scan lines are written from bottom-to-top.
/// </summary>
DecreasingY = 1,
/// <summary>
/// The Scan lines are written in no particular oder.
/// </summary>
RandomY = 2
}

18
src/ImageSharp/Formats/Exr/ExrAttribute.cs

@ -5,11 +5,20 @@ using System.Diagnostics;
namespace SixLabors.ImageSharp.Formats.Exr;
/// <summary>
/// Repressents an exr image attribute.
/// </summary>
[DebuggerDisplay("Name: {Name}, Type: {Type}, Length: {Length}")]
internal class ExrAttribute
{
public static readonly ExrAttribute EmptyAttribute = new(string.Empty, string.Empty, 0);
/// <summary>
/// Initializes a new instance of the <see cref="ExrAttribute"/> class.
/// </summary>
/// <param name="name">The name of the attribute.</param>
/// <param name="type">The type of the attribute.</param>
/// <param name="length">The length in bytes.</param>
public ExrAttribute(string name, string type, int length)
{
this.Name = name;
@ -17,9 +26,18 @@ internal class ExrAttribute
this.Length = length;
}
/// <summary>
/// Gets the name of the attribute.
/// </summary>
public string Name { get; }
/// <summary>
/// Gets the type of the attribute.
/// </summary>
public string Type { get; }
/// <summary>
/// Gets the length in bytes of the attribute.
/// </summary>
public int Length { get; }
}

22
src/ImageSharp/Formats/Exr/ExrBox2i.cs

@ -5,9 +5,19 @@ using System.Diagnostics;
namespace SixLabors.ImageSharp.Formats.Exr;
/// <summary>
/// Integer region definition.
/// </summary>
[DebuggerDisplay("xMin: {XMin}, yMin: {YMin}, xMax: {XMax}, yMax: {YMax}")]
internal readonly struct ExrBox2i
{
/// <summary>
/// Initializes a new instance of the <see cref="ExrBox2i"/> struct.
/// </summary>
/// <param name="xMin">The minimum x value.</param>
/// <param name="yMin">The minimum y value.</param>
/// <param name="xMax">The maximum x value.</param>
/// <param name="yMax">The maximum y value.</param>
public ExrBox2i(int xMin, int yMin, int xMax, int yMax)
{
this.XMin = xMin;
@ -16,11 +26,23 @@ internal readonly struct ExrBox2i
this.YMax = yMax;
}
/// <summary>
/// Gets the minimum x value.
/// </summary>
public int XMin { get; }
/// <summary>
/// Gets the minimum y value.
/// </summary>
public int YMin { get; }
/// <summary>
/// Gets the maximum x value.
/// </summary>
public int XMax { get; }
/// <summary>
/// Gets the maximum y value.
/// </summary>
public int YMax { get; }
}

35
src/ImageSharp/Formats/Exr/ExrChannelInfo.cs

@ -7,26 +7,55 @@ using SixLabors.ImageSharp.Formats.Exr.Constants;
namespace SixLabors.ImageSharp.Formats.Exr;
/// <summary>
/// Information about a pixel channel.
/// </summary>
[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)
/// <summary>
/// Initializes a new instance of the <see cref="ExrChannelInfo" /> struct.
/// </summary>
/// <param name="channelName">Name of the channel.</param>
/// <param name="pixelType">The type of the pixel data.</param>
/// <param name="linear">Linear flag, possible values are 0 and 1.</param>
/// <param name="xSampling">X sampling.</param>
/// <param name="ySampling">Y sampling.</param>
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;
}
/// <summary>
/// Gets the channel name.
/// </summary>
public string ChannelName { get; }
/// <summary>
/// Gets the type of the pixel data.
/// </summary>
public ExrPixelType PixelType { get; }
public byte PLinear { get; }
/// <summary>
/// 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.
/// </summary>
public byte Linear { get; }
/// <summary>
/// Gets the x sampling value.
/// </summary>
public int XSampling { get; }
/// <summary>
/// Gets the y sampling value.
/// </summary>
public int YSampling { get; }
}

215
src/ImageSharp/Formats/Exr/ExrDecoderCore.cs

@ -120,6 +120,21 @@ internal sealed class ExrDecoderCore : ImageDecoderCore
return image;
}
/// <inheritdoc />
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);
}
/// <summary>
/// Decodes image data with floating point pixel data.
/// </summary>
/// <typeparam name="TPixel">The type of the pixels.</typeparam>
/// <param name="stream">The stream to read from.</param>
/// <param name="pixels">The pixel buffer.</param>
/// <param name="cancellationToken">The cancellation token.</param>
private void DecodeFloatingPointPixelData<TPixel>(BufferedReadStream stream, Buffer2D<TPixel> pixels, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{
@ -178,6 +193,13 @@ internal sealed class ExrDecoderCore : ImageDecoderCore
}
}
/// <summary>
/// Decodes image data with unsigned int pixel data.
/// </summary>
/// <typeparam name="TPixel">The type of the pixels.</typeparam>
/// <param name="stream">The stream to read from.</param>
/// <param name="pixels">The pixel buffer.</param>
/// <param name="cancellationToken">The cancellation token.</param>
private void DecodeUnsignedIntPixelData<TPixel>(BufferedReadStream stream, Buffer2D<TPixel> pixels, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{
@ -236,6 +258,18 @@ internal sealed class ExrDecoderCore : ImageDecoderCore
}
}
/// <summary>
/// Reads float image channel data.
/// </summary>
/// <param name="stream">The stream to read from.</param>
/// <param name="channel">The channel info.</param>
/// <param name="decompressedPixelData">The decompressed pixel data.</param>
/// <param name="redPixelData">The red channel pixel data.</param>
/// <param name="greenPixelData">The green channel pixel data.</param>
/// <param name="bluePixelData">The blue channel pixel data.</param>
/// <param name="alphaPixelData">The alpha channel pixel data.</param>
/// <param name="width">The width of a row in pixels.</param>
/// <returns>The bytes read.</returns>
private static int ReadFloatChannelData(
BufferedReadStream stream,
ExrChannelInfo channel,
@ -275,6 +309,18 @@ internal sealed class ExrDecoderCore : ImageDecoderCore
}
}
/// <summary>
/// Reads UINT image channel data.
/// </summary>
/// <param name="stream">The stream to read from.</param>
/// <param name="channel">The channel info.</param>
/// <param name="decompressedPixelData">The decompressed pixel data.</param>
/// <param name="redPixelData">The red channel pixel data.</param>
/// <param name="greenPixelData">The green channel pixel data.</param>
/// <param name="bluePixelData">The blue channel pixel data.</param>
/// <param name="alphaPixelData">The alpha channel pixel data.</param>
/// <param name="width">The width of a row in pixels.</param>
/// <returns>The bytes read.</returns>
private int ReadUnsignedIntChannelData(
BufferedReadStream stream,
ExrChannelInfo channel,
@ -313,6 +359,14 @@ internal sealed class ExrDecoderCore : ImageDecoderCore
}
}
/// <summary>
/// Reads the channel data for pixel type HALF or FLOAT.
/// </summary>
/// <param name="channel">The channel info.</param>
/// <param name="decompressedPixelData">The decompressed pixel data.</param>
/// <param name="pixelData">The pixel data as float.</param>
/// <param name="width">The width in pixel of a row.</param>
/// <returns>The bytes read.</returns>
private static int ReadChannelData(ExrChannelInfo channel, Span<byte> decompressedPixelData, Span<float> pixelData, int width) => channel.PixelType switch
{
ExrPixelType.Half => ReadPixelRowChannelHalfSingle(decompressedPixelData, pixelData, width),
@ -320,12 +374,27 @@ internal sealed class ExrDecoderCore : ImageDecoderCore
_ => 0,
};
/// <summary>
/// Reads the channel data for pixel type UINT.
/// </summary>
/// <param name="channel">The channel info.</param>
/// <param name="decompressedPixelData">The decompressed pixel data.</param>
/// <param name="pixelData">The pixel data as uint.</param>
/// <param name="width">The width in pixels.</param>
/// <returns>The bytes read.</returns>
private static int ReadChannelData(ExrChannelInfo channel, Span<byte> decompressedPixelData, Span<uint> pixelData, int width) => channel.PixelType switch
{
ExrPixelType.UnsignedInt => ReadPixelRowChannelUnsignedInt(decompressedPixelData, pixelData, width),
_ => 0,
};
/// <summary>
/// Reads a pixel row with the pixel data being 16 bit half values.
/// </summary>
/// <param name="decompressedPixelData">The decompressed pixel data.</param>
/// <param name="channelData">The channel data as float.</param>
/// <param name="width">The width of a row in pixels.</param>
/// <returns>The bytes read.</returns>
private static int ReadPixelRowChannelHalfSingle(Span<byte> decompressedPixelData, Span<float> channelData, int width)
{
int offset = 0;
@ -339,6 +408,13 @@ internal sealed class ExrDecoderCore : ImageDecoderCore
return offset;
}
/// <summary>
/// Reads a pixel row with 32 bit float pixel data.
/// </summary>
/// <param name="decompressedPixelData">The decompressed pixel data.</param>
/// <param name="channelData">The pixel data as float.</param>
/// <param name="width">The width in pixels of a row.</param>
/// <returns>The bytes read.</returns>
private static int ReadPixelRowChannelSingle(Span<byte> decompressedPixelData, Span<float> channelData, int width)
{
int offset = 0;
@ -352,6 +428,13 @@ internal sealed class ExrDecoderCore : ImageDecoderCore
return offset;
}
/// <summary>
/// Reads a pixel row with the pixel typ UINT.
/// </summary>
/// <param name="decompressedPixelData">The decompressed pixel bytes.</param>
/// <param name="channelData">The uint pixel data.</param>
/// <param name="width">The width of a row in pixels.</param>
/// <returns>The bytes read.</returns>
private static int ReadPixelRowChannelUnsignedInt(Span<byte> decompressedPixelData, Span<uint> channelData, int width)
{
int offset = 0;
@ -364,14 +447,10 @@ internal sealed class ExrDecoderCore : ImageDecoderCore
return offset;
}
/// <inheritdoc />
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);
}
/// <summary>
/// Validates that all image channels have the same type and are among the supported pixel types.
/// </summary>
/// <returns>The pixel type.</returns>
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()
/// <summary>
/// Determines the type image from the channel information.
/// </summary>
/// <returns>The image data type.</returns>
private ExrImageDataType DetermineImageDataType()
{
bool hasRedChannel = false;
bool hasGreenChannel = false;
@ -438,6 +547,12 @@ internal sealed class ExrDecoderCore : ImageDecoderCore
return ExrImageDataType.Unknown;
}
/// <summary>
/// Reads the exr image header.
/// <see href="https://openexr.com/en/latest/OpenEXRFileLayout.html#header-attributes-all-files/"/>
/// </summary>
/// <param name="stream">The stream.</param>
/// <returns>The image header attributes.</returns>
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;
}
/// <summary>
/// Parses the image header attributes.
/// </summary>
/// <param name="stream">The stream to read from.</param>
/// <returns>The image header attributes.</returns>
private ExrHeaderAttributes ParseHeaderAttributes(BufferedReadStream stream)
{
ExrAttribute attribute = this.ReadAttribute(stream);
@ -599,6 +719,11 @@ internal sealed class ExrDecoderCore : ImageDecoderCore
return header;
}
/// <summary>
/// Reads a attrbute from the stream, which consist of a name, a type and a size in bytes.
/// </summary>
/// <param name="stream">The stream to read from.</param>
/// <returns>A attribute.</returns>
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);
}
/// <summary>
/// Reads a box attribute, which is a xMin, xMax and yMin, yMax value.
/// </summary>
/// <param name="stream">The stream to reaad from.</param>
/// <returns>A box struct.</returns>
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);
}
/// <summary>
/// Reads the channel list from the stream.
/// </summary>
/// <param name="stream">The stream to read from.</param>
/// <param name="attributeSize">The size in bytes of the channel list attribute.</param>
/// <returns>The channel list.</returns>
private List<ExrChannelInfo> ReadChannelList(BufferedReadStream stream, int attributeSize)
{
List<ExrChannelInfo> 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;
}
/// <summary>
/// Reads the channel information from the stream.
/// </summary>
/// <param name="stream">The stream to read from.</param>
/// <param name="bytesRead">The bytes read.</param>
/// <returns>Channel info.</returns>
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);
}
/// <summary>
/// Reads a the string from the stream.
/// </summary>
/// <param name="stream">The stream to read from.</param>
/// <returns>A string.</returns>
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;
}
/// <summary>
/// Determines whether the compression is supported.
/// </summary>
/// <returns> True if the compression is supported; otherwise, false>. </returns>
private bool IsSupportedCompression() => this.Compression switch
{
ExrCompression.None or ExrCompression.Zip or ExrCompression.Zips or ExrCompression.RunLengthEncoded or ExrCompression.B44 => true,
_ => false,
};
/// <summary>
/// Determines whether this image has alpha channel.
/// </summary>
/// <returns> True if this image has a alpha channel; otherwise, false. </returns>
private bool HasAlpha()
{
foreach (ExrChannelInfo channelInfo in this.Channels)

186
src/ImageSharp/Formats/Exr/ExrEncoderCore.cs

@ -147,6 +147,18 @@ internal sealed class ExrEncoderCore
}
}
/// <summary>
/// Encodes and writes pixel data with float pixel data to the stream.
/// </summary>
/// <typeparam name="TPixel">The type of the pixels.</typeparam>
/// <param name="stream">The stream to write to.</param>
/// <param name="pixels">The pixel bufer.</param>
/// <param name="width">The width of the image in pixels.</param>
/// <param name="height">The height of the image in pixels.</param>
/// <param name="channels">The imagechannels.</param>
/// <param name="compression">The compression to use.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The array of pixel row offsets.</returns>
private ulong[] EncodeFloatingPointPixelData<TPixel>(
Stream stream,
Buffer2D<TPixel> pixels,
@ -227,6 +239,18 @@ internal sealed class ExrEncoderCore
return rowOffsets;
}
/// <summary>
/// Encodes and writes pixel data with the unsigned int pixel type to the stream.
/// </summary>
/// <typeparam name="TPixel">The type of the pixels.</typeparam>
/// <param name="stream">The stream to write to.</param>
/// <param name="pixels">The pixel bufer.</param>
/// <param name="width">The width of the image in pixels.</param>
/// <param name="height">The height of the image in pixels.</param>
/// <param name="channels">The imagechannels.</param>
/// <param name="compression">The compression to use.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The array of pixel row offsets.</returns>
private ulong[] EncodeUnsignedIntPixelData<TPixel>(
Stream stream,
Buffer2D<TPixel> pixels,
@ -301,6 +325,11 @@ internal sealed class ExrEncoderCore
return rowOffsets;
}
/// <summary>
/// Writes the image header to the stream.
/// </summary>
/// <param name="stream">The stream to write to.</param>
/// <param name="header">The header.</param>
private void WriteHeader(Stream stream, ExrHeaderAttributes header)
{
this.WriteChannels(stream, header.Channels);
@ -314,6 +343,15 @@ internal sealed class ExrEncoderCore
stream.WriteByte(0);
}
/// <summary>
/// Writes a row of pixels with the FLOAT pixel type to a buffer.
/// </summary>
/// <param name="buffer">The buffer to write to.</param>
/// <param name="width">The width of a row in pixels.</param>
/// <param name="alphaBuffer">The alpha channel buffer.</param>
/// <param name="blueBuffer">The blue channel buffer.</param>
/// <param name="greenBuffer">The green channel buffer.</param>
/// <param name="redBuffer">The red channel buffer.</param>
private static void WriteSingleRow(Span<byte> buffer, int width, Span<float> alphaBuffer, Span<float> blueBuffer, Span<float> greenBuffer, Span<float> redBuffer)
{
int offset = 0;
@ -342,6 +380,15 @@ internal sealed class ExrEncoderCore
}
}
/// <summary>
/// Writes a row of pixels with the HALF pixel type to a buffer.
/// </summary>
/// <param name="buffer">The buffer to write to.</param>
/// <param name="width">The width of a row in pixels.</param>
/// <param name="alphaBuffer">The alpha channel buffer.</param>
/// <param name="blueBuffer">The blue channel buffer.</param>
/// <param name="greenBuffer">The green channel buffer.</param>
/// <param name="redBuffer">The red channel buffer.</param>
private static void WriteHalfSingleRow(Span<byte> buffer, int width, Span<float> alphaBuffer, Span<float> blueBuffer, Span<float> greenBuffer, Span<float> redBuffer)
{
int offset = 0;
@ -370,6 +417,15 @@ internal sealed class ExrEncoderCore
}
}
/// <summary>
/// Writes a row of pixels with unsigned int pixel data to a buffer.
/// </summary>
/// <param name="buffer">The buffer to write to.</param>
/// <param name="width">The width of the row in pixels.</param>
/// <param name="alphaBuffer">The alpha channel buffer.</param>
/// <param name="blueBuffer">The blue channel buffer.</param>
/// <param name="greenBuffer">The green channel buffer.</param>
/// <param name="redBuffer">The red channel buffer.</param>
private static void WriteUnsignedIntRow(Span<byte> buffer, int width, Span<uint> alphaBuffer, Span<uint> blueBuffer, Span<uint> greenBuffer, Span<uint> redBuffer)
{
int offset = 0;
@ -398,6 +454,12 @@ internal sealed class ExrEncoderCore
}
}
/// <summary>
/// Writes the row offsets to the stream.
/// </summary>
/// <param name="stream">The stream to write to.</param>
/// <param name="height">The height in pixels of the image.</param>
/// <param name="rowOffsets">The row offsets.</param>
private void WriteRowOffsets(Stream stream, int height, ulong[] rowOffsets)
{
for (int i = 0; i < height; i++)
@ -407,6 +469,11 @@ internal sealed class ExrEncoderCore
}
}
/// <summary>
/// Writes the channel infos to the stream.
/// </summary>
/// <param name="stream">The stream to write to.</param>
/// <param name="channels">The channels.</param>
private void WriteChannels(Stream stream, IList<ExrChannelInfo> channels)
{
int attributeSize = 0;
@ -429,24 +496,70 @@ internal sealed class ExrEncoderCore
stream.WriteByte(0);
}
/// <summary>
/// Writes info about a single channel to the stream.
/// </summary>
/// <param name="stream">The stream to write to.</param>
/// <param name="channelInfo">The channel information.</param>
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));
}
/// <summary>
/// Writes the compression type to the stream.
/// </summary>
/// <param name="stream">The stream to write to.</param>
/// <param name="compression">The compression type.</param>
private void WriteCompression(Stream stream, ExrCompression compression)
{
this.WriteAttributeInformation(stream, ExrConstants.AttributeNames.Compression, ExrConstants.AttibuteTypes.Compression, 1);
stream.WriteByte((byte)compression);
}
/// <summary>
/// Writes the pixel aspect ratio to the stream.
/// </summary>
/// <param name="stream">The stream to write to.</param>
/// <param name="aspectRatio">The aspect ratio.</param>
private void WritePixelAspectRatio(Stream stream, float aspectRatio)
{
this.WriteAttributeInformation(stream, ExrConstants.AttributeNames.PixelAspectRatio, ExrConstants.AttibuteTypes.Float, 4);
this.WriteSingle(stream, aspectRatio);
}
/// <summary>
/// Writes the line order to the stream.
/// </summary>
/// <param name="stream">The stream to write to.</param>
/// <param name="lineOrder">The line order.</param>
private void WriteLineOrder(Stream stream, ExrLineOrder lineOrder)
{
this.WriteAttributeInformation(stream, ExrConstants.AttributeNames.LineOrder, ExrConstants.AttibuteTypes.LineOrder, 1);
stream.WriteByte((byte)lineOrder);
}
/// <summary>
/// Writes the screen window center to the stream.
/// </summary>
/// <param name="stream">The stream to write to.</param>
/// <param name="screenWindowCenter">The screen window center.</param>
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);
}
/// <summary>
/// Writes the screen width to the stream.
/// </summary>
/// <param name="stream">The stream to write to.</param>
/// <param name="screenWindowWidth">Width of the screen window.</param>
private void WriteScreenWindowWidth(Stream stream, float screenWindowWidth)
{
this.WriteAttributeInformation(stream, ExrConstants.AttributeNames.ScreenWindowWidth, ExrConstants.AttibuteTypes.Float, 4);
this.WriteSingle(stream, screenWindowWidth);
}
/// <summary>
/// Writes the data window to the stream.
/// </summary>
/// <param name="stream">The stream to write to.</param>
/// <param name="dataWindow">The data window.</param>
private void WriteDataWindow(Stream stream, ExrBox2i dataWindow)
{
this.WriteAttributeInformation(stream, ExrConstants.AttributeNames.DataWindow, ExrConstants.AttibuteTypes.BoxInt, 16);
this.WriteBoxInteger(stream, dataWindow);
}
/// <summary>
/// Writes the display window to the stream.
/// </summary>
/// <param name="stream">The stream to write to.</param>
/// <param name="displayWindow">The display window.</param>
private void WriteDisplayWindow(Stream stream, ExrBox2i displayWindow)
{
this.WriteAttributeInformation(stream, ExrConstants.AttributeNames.DisplayWindow, ExrConstants.AttibuteTypes.BoxInt, 16);
this.WriteBoxInteger(stream, displayWindow);
}
/// <summary>
/// Writes attribute information to the stream.
/// </summary>
/// <param name="stream">The stream to write to.</param>
/// <param name="name">The name of the attribute.</param>
/// <param name="type">The type of the attribute.</param>
/// <param name="size">The size in bytes of the attribute.</param>
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));
}
/// <summary>
/// Writes a string to the stream.
/// </summary>
/// <param name="stream">The stream to write to.</param>
/// <param name="str">The string to write.</param>
private static void WriteString(Stream stream, string str)
{
foreach (char c in str)
@ -517,6 +636,11 @@ internal sealed class ExrEncoderCore
stream.WriteByte(0);
}
/// <summary>
/// Writes box struct with xmin, xmax, ymin and y max to the stream.
/// </summary>
/// <param name="stream">The stream to write to.</param>
/// <param name="box">The box to write.</param>
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));
}
/// <summary>
/// Writes 32 bit float value to the stream.
/// </summary>
/// <param name="stream">The stream to write to.</param>
/// <param name="value">The float value to write.</param>
[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));
}
/// <summary>
/// Writes a 32 bit float value to a buffer.
/// </summary>
/// <param name="buffer">The buffer to write to.</param>
/// <param name="value">The float value to write.</param>
[MethodImpl(InliningOptions.ShortMethod)]
private static unsafe void WriteSingleToBuffer(Span<byte> buffer, float value) => BinaryPrimitives.WriteInt32LittleEndian(buffer, *(int*)&value);
/// <summary>
/// Writes a 16 bit float value to a buffer.
/// </summary>
/// <param name="buffer">The buffer to write to.</param>
/// <param name="value">The float value to write.</param>
[MethodImpl(InliningOptions.ShortMethod)]
private static void WriteHalfSingleToBuffer(Span<byte> buffer, float value)
{
@ -549,6 +688,11 @@ internal sealed class ExrEncoderCore
BinaryPrimitives.WriteUInt16LittleEndian(buffer, valueAsShort);
}
/// <summary>
/// Writes one unsigned int to a buffer.
/// </summary>
/// <param name="buffer">The buffer to write to.</param>
/// <param name="value">The uint value to write.</param>
[MethodImpl(InliningOptions.ShortMethod)]
private static void WriteUnsignedIntToBuffer(Span<byte> buffer, uint value) => BinaryPrimitives.WriteUInt32LittleEndian(buffer, value);
}

14
src/ImageSharp/Formats/Exr/ExrHeaderAttributes.cs

@ -11,6 +11,20 @@ namespace SixLabors.ImageSharp.Formats.Exr;
/// </summary>
internal class ExrHeaderAttributes
{
/// <summary>
/// Initializes a new instance of the <see cref="ExrHeaderAttributes" /> class.
/// </summary>
/// <param name="channels">The image channels.</param>
/// <param name="compression">The compression used.</param>
/// <param name="dataWindow">The data window.</param>
/// <param name="displayWindow">The display window.</param>
/// <param name="lineOrder">The line order.</param>
/// <param name="aspectRatio">The aspect ratio.</param>
/// <param name="screenWindowWidth">Width of the screen window.</param>
/// <param name="screenWindowCenter">The screen window center.</param>
/// <param name="tileXSize">Size of the tile in x dimension.</param>
/// <param name="tileYSize">Size of the tile in y dimension.</param>
/// <param name="chunkCount">The chunk count.</param>
public ExrHeaderAttributes(
IList<ExrChannelInfo> channels,
ExrCompression compression,

Loading…
Cancel
Save