diff --git a/src/ImageSharp/Formats/Exr/Compression/Compressors/NoCompressor.cs b/src/ImageSharp/Formats/Exr/Compression/Compressors/NoCompressor.cs
new file mode 100644
index 0000000000..9a9bc8fff6
--- /dev/null
+++ b/src/ImageSharp/Formats/Exr/Compression/Compressors/NoCompressor.cs
@@ -0,0 +1,8 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+namespace SixLabors.ImageSharp.Formats.Exr.Compression.Compressors;
+
+internal class NoCompressor
+{
+}
diff --git a/src/ImageSharp/Formats/Exr/Compression/Compressors/ZipCompressor.cs b/src/ImageSharp/Formats/Exr/Compression/Compressors/ZipCompressor.cs
new file mode 100644
index 0000000000..d205c2f1a3
--- /dev/null
+++ b/src/ImageSharp/Formats/Exr/Compression/Compressors/ZipCompressor.cs
@@ -0,0 +1,47 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using SixLabors.ImageSharp.Compression.Zlib;
+using SixLabors.ImageSharp.Formats.Exr.Constants;
+using SixLabors.ImageSharp.Memory;
+
+namespace SixLabors.ImageSharp.Formats.Exr.Compression.Compressors;
+
+internal class ZipCompressor : ExrBaseCompressor
+{
+ private readonly DeflateCompressionLevel compressionLevel;
+
+ private readonly MemoryStream memoryStream = new();
+
+ public ZipCompressor(Stream output, MemoryAllocator allocator, uint bytesPerBlock, DeflateCompressionLevel compressionLevel)
+ : base(output, allocator, bytesPerBlock)
+ => this.compressionLevel = compressionLevel;
+
+ ///
+ public override ExrCompression Method => ExrCompression.Zip;
+
+ ///
+ public override void Initialize(int rowsPerStrip)
+ {
+ }
+
+ ///
+ public override void CompressStrip(Span rows, int height)
+ {
+ this.memoryStream.Seek(0, SeekOrigin.Begin);
+ using (ZlibDeflateStream stream = new(this.Allocator, this.memoryStream, this.compressionLevel))
+ {
+ stream.Write(rows);
+ stream.Flush();
+ }
+
+ int size = (int)this.memoryStream.Position;
+ byte[] buffer = this.memoryStream.GetBuffer();
+ this.Output.Write(buffer, 0, size);
+ }
+
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ }
+}
diff --git a/src/ImageSharp/Formats/Exr/Compression/Decompressors/B44Compression.cs b/src/ImageSharp/Formats/Exr/Compression/Decompressors/B44Compression.cs
index b8cb468142..d0f9eb6efe 100644
--- a/src/ImageSharp/Formats/Exr/Compression/Decompressors/B44Compression.cs
+++ b/src/ImageSharp/Formats/Exr/Compression/Decompressors/B44Compression.cs
@@ -24,8 +24,8 @@ internal class B44Compression : ExrBaseDecompressor
private IMemoryOwner tmpBuffer;
- public B44Compression(MemoryAllocator allocator, uint uncompressedBytes, int width, int height, uint rowsPerBlock, int channelCount)
- : base(allocator, uncompressedBytes)
+ public B44Compression(MemoryAllocator allocator, uint bytesPerBlock, int width, int height, uint rowsPerBlock, int channelCount)
+ : base(allocator, bytesPerBlock)
{
this.width = width;
this.height = height;
diff --git a/src/ImageSharp/Formats/Exr/Compression/Decompressors/NoneExrCompression.cs b/src/ImageSharp/Formats/Exr/Compression/Decompressors/NoneExrCompression.cs
index 6b36dffa2b..75bf67cb24 100644
--- a/src/ImageSharp/Formats/Exr/Compression/Decompressors/NoneExrCompression.cs
+++ b/src/ImageSharp/Formats/Exr/Compression/Decompressors/NoneExrCompression.cs
@@ -8,13 +8,13 @@ namespace SixLabors.ImageSharp.Formats.Exr.Compression.Decompressors;
internal class NoneExrCompression : ExrBaseDecompressor
{
- public NoneExrCompression(MemoryAllocator allocator, uint uncompressedBytes)
- : base(allocator, uncompressedBytes)
+ public NoneExrCompression(MemoryAllocator allocator, uint bytesPerBlock)
+ : base(allocator, bytesPerBlock)
{
}
public override void Decompress(BufferedReadStream stream, uint compressedBytes, Span buffer)
- => stream.Read(buffer, 0, Math.Min(buffer.Length, (int)this.UncompressedBytes));
+ => stream.Read(buffer, 0, Math.Min(buffer.Length, (int)this.BytesPerBlock));
protected override void Dispose(bool disposing)
{
diff --git a/src/ImageSharp/Formats/Exr/Compression/Decompressors/RunLengthCompression.cs b/src/ImageSharp/Formats/Exr/Compression/Decompressors/RunLengthCompression.cs
index f1c7bb759b..15538a813e 100644
--- a/src/ImageSharp/Formats/Exr/Compression/Decompressors/RunLengthCompression.cs
+++ b/src/ImageSharp/Formats/Exr/Compression/Decompressors/RunLengthCompression.cs
@@ -19,7 +19,7 @@ internal class RunLengthCompression : ExrBaseDecompressor
public override void Decompress(BufferedReadStream stream, uint compressedBytes, Span buffer)
{
Span uncompressed = this.tmpBuffer.GetSpan();
- int maxLength = (int)this.UncompressedBytes;
+ int maxLength = (int)this.BytesPerBlock;
int offset = 0;
while (compressedBytes > 0)
{
@@ -63,8 +63,8 @@ internal class RunLengthCompression : ExrBaseDecompressor
}
}
- Reconstruct(uncompressed, this.UncompressedBytes);
- Interleave(uncompressed, this.UncompressedBytes, buffer);
+ Reconstruct(uncompressed, this.BytesPerBlock);
+ Interleave(uncompressed, this.BytesPerBlock, buffer);
}
private static byte ReadNextByte(BufferedReadStream stream)
diff --git a/src/ImageSharp/Formats/Exr/Compression/Decompressors/ZipExrCompression.cs b/src/ImageSharp/Formats/Exr/Compression/Decompressors/ZipExrCompression.cs
index 4d9b42732f..95d4c03137 100644
--- a/src/ImageSharp/Formats/Exr/Compression/Decompressors/ZipExrCompression.cs
+++ b/src/ImageSharp/Formats/Exr/Compression/Decompressors/ZipExrCompression.cs
@@ -13,8 +13,8 @@ internal class ZipExrCompression : ExrBaseDecompressor
{
private readonly IMemoryOwner tmpBuffer;
- public ZipExrCompression(MemoryAllocator allocator, uint uncompressedBytes)
- : base(allocator, uncompressedBytes) => this.tmpBuffer = allocator.Allocate((int)uncompressedBytes);
+ public ZipExrCompression(MemoryAllocator allocator, uint bytesPerBlock)
+ : base(allocator, bytesPerBlock) => this.tmpBuffer = allocator.Allocate((int)bytesPerBlock);
public override void Decompress(BufferedReadStream stream, uint compressedBytes, Span buffer)
{
@@ -28,7 +28,7 @@ internal class ZipExrCompression : ExrBaseDecompressor
int left = (int)(compressedBytes - (stream.Position - pos));
return left > 0 ? left : 0;
});
- inflateStream.AllocateNewBytes((int)this.UncompressedBytes, true);
+ inflateStream.AllocateNewBytes((int)this.BytesPerBlock, true);
DeflateStream dataStream = inflateStream.CompressedStream!;
int totalRead = 0;
diff --git a/src/ImageSharp/Formats/Exr/Compression/ExrBaseCompression.cs b/src/ImageSharp/Formats/Exr/Compression/ExrBaseCompression.cs
index 3dab1006ed..e510135c2c 100644
--- a/src/ImageSharp/Formats/Exr/Compression/ExrBaseCompression.cs
+++ b/src/ImageSharp/Formats/Exr/Compression/ExrBaseCompression.cs
@@ -9,10 +9,10 @@ internal abstract class ExrBaseCompression : IDisposable
{
private bool isDisposed;
- protected ExrBaseCompression(MemoryAllocator allocator, uint bytePerRow)
+ protected ExrBaseCompression(MemoryAllocator allocator, uint bytesPerBlock)
{
this.Allocator = allocator;
- this.UncompressedBytes = bytePerRow;
+ this.BytesPerBlock = bytesPerBlock;
}
///
@@ -21,9 +21,24 @@ internal abstract class ExrBaseCompression : IDisposable
protected MemoryAllocator Allocator { get; }
///
- /// Gets the uncompressed bytes.
+ /// Gets the bits per pixel.
///
- public uint UncompressedBytes { get; }
+ public int BitsPerPixel { get; }
+
+ ///
+ /// Gets the bytes per row.
+ ///
+ public int BytesPerRow { get; }
+
+ ///
+ /// Gets the uncompressed bytes per block.
+ ///
+ public uint BytesPerBlock { get; }
+
+ ///
+ /// Gets the image width.
+ ///
+ public int Width { get; }
///
public void Dispose()
diff --git a/src/ImageSharp/Formats/Exr/Compression/ExrBaseDecompressor.cs b/src/ImageSharp/Formats/Exr/Compression/ExrBaseDecompressor.cs
index 12edcc1046..0dd7d01108 100644
--- a/src/ImageSharp/Formats/Exr/Compression/ExrBaseDecompressor.cs
+++ b/src/ImageSharp/Formats/Exr/Compression/ExrBaseDecompressor.cs
@@ -8,8 +8,8 @@ namespace SixLabors.ImageSharp.Formats.Exr.Compression;
internal abstract class ExrBaseDecompressor : ExrBaseCompression
{
- protected ExrBaseDecompressor(MemoryAllocator allocator, uint bytePerRow)
- : base(allocator, bytePerRow)
+ protected ExrBaseDecompressor(MemoryAllocator allocator, uint bytesPerBlock)
+ : base(allocator, bytesPerBlock)
{
}
diff --git a/src/ImageSharp/Formats/Exr/Compression/ExrCompressorFactory.cs b/src/ImageSharp/Formats/Exr/Compression/ExrCompressorFactory.cs
new file mode 100644
index 0000000000..fc403be3a0
--- /dev/null
+++ b/src/ImageSharp/Formats/Exr/Compression/ExrCompressorFactory.cs
@@ -0,0 +1,26 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using SixLabors.ImageSharp.Compression.Zlib;
+using SixLabors.ImageSharp.Formats.Exr.Compression.Compressors;
+using SixLabors.ImageSharp.Formats.Exr.Constants;
+using SixLabors.ImageSharp.Memory;
+
+namespace SixLabors.ImageSharp.Formats.Exr.Compression;
+
+internal static class ExrCompressorFactory
+{
+ public static ExrBaseCompressor Create(
+ ExrCompression method,
+ Stream output,
+ MemoryAllocator allocator,
+ int width,
+ DeflateCompressionLevel compressionLevel)
+ {
+ switch (method)
+ {
+ default:
+ throw ExrThrowHelper.NotSupportedCompressor(method.ToString());
+ }
+ }
+}
diff --git a/src/ImageSharp/Formats/Exr/Compression/ExrDecompressorFactory.cs b/src/ImageSharp/Formats/Exr/Compression/ExrDecompressorFactory.cs
index a293787fad..6764a2a130 100644
--- a/src/ImageSharp/Formats/Exr/Compression/ExrDecompressorFactory.cs
+++ b/src/ImageSharp/Formats/Exr/Compression/ExrDecompressorFactory.cs
@@ -2,26 +2,27 @@
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Exr.Compression.Decompressors;
+using SixLabors.ImageSharp.Formats.Exr.Constants;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Exr.Compression;
internal static class ExrDecompressorFactory
{
- public static ExrBaseDecompressor Create(ExrCompressionType method, MemoryAllocator memoryAllocator, uint uncompressedBytes, int width, int height, uint rowsPerBlock, int channelCount)
+ public static ExrBaseDecompressor Create(ExrCompression method, MemoryAllocator memoryAllocator, uint bytesPerBlock, int width, int height, uint rowsPerBlock, int channelCount)
{
switch (method)
{
- case ExrCompressionType.None:
- return new NoneExrCompression(memoryAllocator, uncompressedBytes);
- case ExrCompressionType.Zips:
- return new ZipExrCompression(memoryAllocator, uncompressedBytes);
- case ExrCompressionType.Zip:
- return new ZipExrCompression(memoryAllocator, uncompressedBytes);
- case ExrCompressionType.RunLengthEncoded:
- return new RunLengthCompression(memoryAllocator, uncompressedBytes);
- case ExrCompressionType.B44:
- return new B44Compression(memoryAllocator, uncompressedBytes, width, height, rowsPerBlock, channelCount);
+ case ExrCompression.None:
+ return new NoneExrCompression(memoryAllocator, bytesPerBlock);
+ case ExrCompression.Zips:
+ return new ZipExrCompression(memoryAllocator, bytesPerBlock);
+ case ExrCompression.Zip:
+ return new ZipExrCompression(memoryAllocator, bytesPerBlock);
+ case ExrCompression.RunLengthEncoded:
+ return new RunLengthCompression(memoryAllocator, bytesPerBlock);
+ case ExrCompression.B44:
+ return new B44Compression(memoryAllocator, bytesPerBlock, width, height, rowsPerBlock, channelCount);
default:
throw ExrThrowHelper.NotSupportedDecompressor(nameof(method));
}
diff --git a/src/ImageSharp/Formats/Exr/Compression/ExrCompressionType.cs b/src/ImageSharp/Formats/Exr/Constants/ExrCompression.cs
similarity index 91%
rename from src/ImageSharp/Formats/Exr/Compression/ExrCompressionType.cs
rename to src/ImageSharp/Formats/Exr/Constants/ExrCompression.cs
index a7b584ed4c..d0964bf33b 100644
--- a/src/ImageSharp/Formats/Exr/Compression/ExrCompressionType.cs
+++ b/src/ImageSharp/Formats/Exr/Constants/ExrCompression.cs
@@ -1,9 +1,12 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
-namespace SixLabors.ImageSharp.Formats.Exr.Compression;
+namespace SixLabors.ImageSharp.Formats.Exr.Constants;
-internal enum ExrCompressionType
+///
+/// Enumeration representing the compression formats defined by the EXR file-format.
+///
+public enum ExrCompression
{
///
/// Pixel data is not compressed.
diff --git a/src/ImageSharp/Formats/Exr/ExrBaseCompressor.cs b/src/ImageSharp/Formats/Exr/ExrBaseCompressor.cs
new file mode 100644
index 0000000000..c2a752c614
--- /dev/null
+++ b/src/ImageSharp/Formats/Exr/ExrBaseCompressor.cs
@@ -0,0 +1,43 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using SixLabors.ImageSharp.Formats.Exr.Constants;
+using SixLabors.ImageSharp.Memory;
+
+namespace SixLabors.ImageSharp.Formats.Exr.Compression;
+
+internal abstract class ExrBaseCompressor : ExrBaseCompression
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The output stream to write the compressed image to.
+ /// The memory allocator.
+ /// Bytes per block.
+ protected ExrBaseCompressor(Stream output, MemoryAllocator allocator, uint bytesPerBlock)
+ : base(allocator, bytesPerBlock)
+ => this.Output = output;
+
+ ///
+ /// Gets the compression method to use.
+ ///
+ public abstract ExrCompression Method { get; }
+
+ ///
+ /// Gets the output stream to write the compressed image to.
+ ///
+ public Stream Output { get; }
+
+ ///
+ /// Does any initialization required for the compression.
+ ///
+ /// The number of rows per strip.
+ public abstract void Initialize(int rowsPerStrip);
+
+ ///
+ /// Compresses a strip of the image.
+ ///
+ /// Image rows to compress.
+ /// Image height.
+ public abstract void CompressStrip(Span rows, int height);
+}
diff --git a/src/ImageSharp/Formats/Exr/ExrDecoderCore.cs b/src/ImageSharp/Formats/Exr/ExrDecoderCore.cs
index 38e9c3ec90..7f53d8a24b 100644
--- a/src/ImageSharp/Formats/Exr/ExrDecoderCore.cs
+++ b/src/ImageSharp/Formats/Exr/ExrDecoderCore.cs
@@ -79,7 +79,7 @@ internal sealed class ExrDecoderCore : ImageDecoderCore
///
/// Gets or sets the compression method.
///
- private ExrCompressionType Compression { get; set; }
+ private ExrCompression Compression { get; set; }
///
/// Gets or sets the image data type, either RGB, RGBA or gray.
@@ -453,7 +453,7 @@ internal sealed class ExrDecoderCore : ImageDecoderCore
IList channels = null;
ExrBox2i? dataWindow = null;
- ExrCompressionType? compression = null;
+ ExrCompression? compression = null;
ExrBox2i? displayWindow = null;
ExrLineOrder? lineOrder = null;
float? aspectRatio = null;
@@ -471,7 +471,7 @@ internal sealed class ExrDecoderCore : ImageDecoderCore
channels = this.ReadChannelList(stream, attribute.Length);
break;
case ExrConstants.AttributeNames.Compression:
- compression = (ExrCompressionType)stream.ReadByte();
+ compression = (ExrCompression)stream.ReadByte();
break;
case ExrConstants.AttributeNames.DataWindow:
dataWindow = this.ReadBoxInteger(stream);
@@ -648,11 +648,11 @@ internal sealed class ExrDecoderCore : ImageDecoderCore
{
switch (this.Compression)
{
- case ExrCompressionType.None:
- case ExrCompressionType.Zip:
- case ExrCompressionType.Zips:
- case ExrCompressionType.RunLengthEncoded:
- case ExrCompressionType.B44:
+ case ExrCompression.None:
+ case ExrCompression.Zip:
+ case ExrCompression.Zips:
+ case ExrCompression.RunLengthEncoded:
+ case ExrCompression.B44:
return true;
}
@@ -757,12 +757,12 @@ internal sealed class ExrDecoderCore : ImageDecoderCore
{
switch (this.Compression)
{
- case ExrCompressionType.Zip:
- case ExrCompressionType.Pxr24:
+ case ExrCompression.Zip:
+ case ExrCompression.Pxr24:
return 16;
- case ExrCompressionType.B44:
- case ExrCompressionType.B44A:
- case ExrCompressionType.Piz:
+ case ExrCompression.B44:
+ case ExrCompression.B44A:
+ case ExrCompression.Piz:
return 32;
default:
diff --git a/src/ImageSharp/Formats/Exr/ExrEncoder.cs b/src/ImageSharp/Formats/Exr/ExrEncoder.cs
index 15e10ba2fa..2ea1b91161 100644
--- a/src/ImageSharp/Formats/Exr/ExrEncoder.cs
+++ b/src/ImageSharp/Formats/Exr/ExrEncoder.cs
@@ -15,6 +15,11 @@ public sealed class ExrEncoder : ImageEncoder
///
public ExrPixelType? PixelType { get; set; }
+ ///
+ /// Gets the compression type to use.
+ ///
+ public ExrCompression? Compression { get; init; }
+
///
protected override void Encode(Image image, Stream stream, CancellationToken cancellationToken)
{
diff --git a/src/ImageSharp/Formats/Exr/ExrEncoderCore.cs b/src/ImageSharp/Formats/Exr/ExrEncoderCore.cs
index 400915d220..ff5287b752 100644
--- a/src/ImageSharp/Formats/Exr/ExrEncoderCore.cs
+++ b/src/ImageSharp/Formats/Exr/ExrEncoderCore.cs
@@ -6,7 +6,6 @@ using System.Buffers.Binary;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Threading.Channels;
-using SixLabors.ImageSharp.Formats.Exr.Compression;
using SixLabors.ImageSharp.Formats.Exr.Constants;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
@@ -77,7 +76,7 @@ internal sealed class ExrEncoderCore
this.pixelType ??= exrMetadata.PixelType;
int width = image.Width;
int height = image.Height;
- ExrCompressionType compression = ExrCompressionType.None;
+ ExrCompression compression = ExrCompression.None;
float aspectRatio = 1.0f;
ExrBox2i dataWindow = new(0, 0, width - 1, height - 1);
ExrBox2i displayWindow = new(0, 0, width - 1, height - 1);
@@ -311,7 +310,7 @@ internal sealed class ExrEncoderCore
stream.WriteByte(0);
}
- private void WriteCompression(Stream stream, ExrCompressionType compression)
+ private void WriteCompression(Stream stream, ExrCompression compression)
{
this.WriteAttributeInformation(stream, ExrConstants.AttributeNames.Compression, ExrConstants.AttibuteTypes.Compression, 1);
stream.WriteByte((byte)compression);
diff --git a/src/ImageSharp/Formats/Exr/ExrHeaderAttributes.cs b/src/ImageSharp/Formats/Exr/ExrHeaderAttributes.cs
index 35611bb6eb..afda62bf92 100644
--- a/src/ImageSharp/Formats/Exr/ExrHeaderAttributes.cs
+++ b/src/ImageSharp/Formats/Exr/ExrHeaderAttributes.cs
@@ -1,7 +1,6 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
-using SixLabors.ImageSharp.Formats.Exr.Compression;
using SixLabors.ImageSharp.Formats.Exr.Constants;
namespace SixLabors.ImageSharp.Formats.Exr;
@@ -10,7 +9,7 @@ internal class ExrHeaderAttributes
{
public ExrHeaderAttributes(
IList channels,
- ExrCompressionType compression,
+ ExrCompression compression,
ExrBox2i dataWindow,
ExrBox2i displayWindow,
ExrLineOrder lineOrder,
@@ -36,7 +35,7 @@ internal class ExrHeaderAttributes
public IList Channels { get; set; }
- public ExrCompressionType Compression { get; set; }
+ public ExrCompression Compression { get; set; }
public ExrBox2i DataWindow { get; set; }
diff --git a/src/ImageSharp/Formats/Exr/ExrThrowHelper.cs b/src/ImageSharp/Formats/Exr/ExrThrowHelper.cs
index f668dcfefd..51419ec95c 100644
--- a/src/ImageSharp/Formats/Exr/ExrThrowHelper.cs
+++ b/src/ImageSharp/Formats/Exr/ExrThrowHelper.cs
@@ -1,6 +1,8 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
+using System.Diagnostics.CodeAnalysis;
+
namespace SixLabors.ImageSharp.Formats.Exr;
///
@@ -8,15 +10,24 @@ namespace SixLabors.ImageSharp.Formats.Exr;
///
internal static class ExrThrowHelper
{
+ [DoesNotReturn]
public static Exception NotSupportedDecompressor(string compressionType) => throw new NotSupportedException($"Not supported decoder compression method: {compressionType}");
+ [DoesNotReturn]
public static void ThrowInvalidImageContentException(string errorMessage) => throw new InvalidImageContentException(errorMessage);
+ [DoesNotReturn]
public static void ThrowNotSupportedVersion() => throw new NotSupportedException("Unsupported EXR version");
+ [DoesNotReturn]
public static void ThrowNotSupported(string msg) => throw new NotSupportedException(msg);
+ [DoesNotReturn]
public static void ThrowInvalidImageHeader() => throw new InvalidImageContentException("Invalid EXR image header");
+ [DoesNotReturn]
public static void ThrowInvalidImageHeader(string msg) => throw new InvalidImageContentException(msg);
+
+ [DoesNotReturn]
+ public static Exception NotSupportedCompressor(string compressionType) => throw new NotSupportedException($"Not supported encoder compression method: {compressionType}");
}