diff --git a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs
index a451e111d2..e2c4258e2a 100644
--- a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs
+++ b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs
@@ -76,6 +76,15 @@ public static class AdvancedImageExtensions
public static Task AcceptVisitorAsync(this Image source, IImageVisitorAsync visitor, CancellationToken cancellationToken = default)
=> source.AcceptAsync(visitor, cancellationToken);
+ ///
+ /// Accepts a to implement a double-dispatch pattern in order to
+ /// apply pixel-specific operations on non-generic instances
+ ///
+ /// The source image frame.
+ /// The image visitor.
+ public static void AcceptVisitor(this ImageFrame source, IImageFrameVisitor visitor)
+ => source.Accept(visitor);
+
///
/// Gets the representation of the pixels as a containing the backing pixel data of the image
/// stored in row major order, as a list of contiguous blocks in the source image's pixel format.
diff --git a/src/ImageSharp/Advanced/IImageFrameVisitor.cs b/src/ImageSharp/Advanced/IImageFrameVisitor.cs
new file mode 100644
index 0000000000..f2522110d5
--- /dev/null
+++ b/src/ImageSharp/Advanced/IImageFrameVisitor.cs
@@ -0,0 +1,21 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Advanced;
+
+///
+/// A visitor to implement a double-dispatch pattern in order to apply pixel-specific operations
+/// on non-generic instances.
+///
+public interface IImageFrameVisitor
+{
+ ///
+ /// Provides a pixel-specific implementation for a given operation.
+ ///
+ /// The image frame.
+ /// The pixel type.
+ public void Visit(ImageFrame frame)
+ where TPixel : unmanaged, IPixel;
+}
diff --git a/src/ImageSharp/Advanced/IImageVisitor.cs b/src/ImageSharp/Advanced/IImageVisitor.cs
index 5e8a4e4512..aee6909675 100644
--- a/src/ImageSharp/Advanced/IImageVisitor.cs
+++ b/src/ImageSharp/Advanced/IImageVisitor.cs
@@ -16,7 +16,7 @@ public interface IImageVisitor
///
/// The image.
/// The pixel type.
- void Visit(Image image)
+ public void Visit(Image image)
where TPixel : unmanaged, IPixel;
}
@@ -33,6 +33,6 @@ public interface IImageVisitorAsync
/// The token to monitor for cancellation requests.
/// The pixel type.
/// A representing the asynchronous operation.
- Task VisitAsync(Image image, CancellationToken cancellationToken)
+ public Task VisitAsync(Image image, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel;
}
diff --git a/src/ImageSharp/Formats/Exr/Compression/Compressors/NoneExrCompressor.cs b/src/ImageSharp/Formats/Exr/Compression/Compressors/NoneExrCompressor.cs
index 58768e990f..8aca0da65e 100644
--- a/src/ImageSharp/Formats/Exr/Compression/Compressors/NoneExrCompressor.cs
+++ b/src/ImageSharp/Formats/Exr/Compression/Compressors/NoneExrCompressor.cs
@@ -17,8 +17,10 @@ internal class NoneExrCompressor : ExrBaseCompressor
/// 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)
+ /// The pixel rows per block.
+ /// The witdh of one row in pixels.
+ public NoneExrCompressor(Stream output, MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow, uint rowsPerBlock, int width)
+ : base(output, allocator, bytesPerBlock, bytesPerRow, rowsPerBlock, width)
{
}
diff --git a/src/ImageSharp/Formats/Exr/Compression/Compressors/ZipExrCompressor.cs b/src/ImageSharp/Formats/Exr/Compression/Compressors/ZipExrCompressor.cs
index ef7285da0c..01248cc395 100644
--- a/src/ImageSharp/Formats/Exr/Compression/Compressors/ZipExrCompressor.cs
+++ b/src/ImageSharp/Formats/Exr/Compression/Compressors/ZipExrCompressor.cs
@@ -24,9 +24,11 @@ internal class ZipExrCompressor : ExrBaseCompressor
/// The memory allocator.
/// The bytes per block.
/// The bytes per row.
+ /// The pixel rows per block.
+ /// The witdh of one row in pixels.
/// The compression level for deflate compression.
- public ZipExrCompressor(Stream output, MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow, DeflateCompressionLevel compressionLevel)
- : base(output, allocator, bytesPerBlock, bytesPerRow)
+ public ZipExrCompressor(Stream output, MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow, uint rowsPerBlock, int width, DeflateCompressionLevel compressionLevel)
+ : base(output, allocator, bytesPerBlock, bytesPerRow, rowsPerBlock, width)
{
this.compressionLevel = compressionLevel;
this.buffer = allocator.Allocate((int)bytesPerBlock);
diff --git a/src/ImageSharp/Formats/Exr/Compression/Decompressors/B44ExrCompression.cs b/src/ImageSharp/Formats/Exr/Compression/Decompressors/B44ExrCompression.cs
index aa4944649c..06df7b6a25 100644
--- a/src/ImageSharp/Formats/Exr/Compression/Decompressors/B44ExrCompression.cs
+++ b/src/ImageSharp/Formats/Exr/Compression/Decompressors/B44ExrCompression.cs
@@ -13,10 +13,6 @@ namespace SixLabors.ImageSharp.Formats.Exr.Compression.Decompressors;
///
internal class B44ExrCompression : ExrBaseDecompressor
{
- private readonly int width;
-
- private readonly uint rowsPerBlock;
-
private readonly int channelCount;
private readonly byte[] scratch = new byte[14];
@@ -31,14 +27,12 @@ internal class B44ExrCompression : ExrBaseDecompressor
/// The memory allocator.
/// The bytes per pixel row block.
/// The bytes per row.
- /// The rows per block.
+ /// The pixel 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)
+ : base(allocator, bytesPerBlock, bytesPerRow, rowsPerBlock, width)
{
- this.width = width;
- this.rowsPerBlock = rowsPerBlock;
this.channelCount = channelCount;
this.tmpBuffer = allocator.Allocate((int)(width * rowsPerBlock * channelCount));
}
@@ -52,19 +46,19 @@ internal class B44ExrCompression : ExrBaseDecompressor
int bytesLeft = (int)compressedBytes;
for (int i = 0; i < this.channelCount && bytesLeft > 0; i++)
{
- for (int y = 0; y < this.rowsPerBlock; y += 4)
+ for (int y = 0; y < this.RowsPerBlock; y += 4)
{
- Span row0 = decompressed.Slice(outputOffset, this.width);
- outputOffset += this.width;
- Span row1 = decompressed.Slice(outputOffset, this.width);
- outputOffset += this.width;
- Span row2 = decompressed.Slice(outputOffset, this.width);
- outputOffset += this.width;
- Span row3 = decompressed.Slice(outputOffset, this.width);
- outputOffset += this.width;
+ Span row0 = decompressed.Slice(outputOffset, this.Width);
+ outputOffset += this.Width;
+ Span row1 = decompressed.Slice(outputOffset, this.Width);
+ outputOffset += this.Width;
+ Span row2 = decompressed.Slice(outputOffset, this.Width);
+ outputOffset += this.Width;
+ Span row3 = decompressed.Slice(outputOffset, this.Width);
+ outputOffset += this.Width;
int rowOffset = 0;
- for (int x = 0; x < this.width && bytesLeft > 0; x += 4)
+ for (int x = 0; x < this.Width && bytesLeft > 0; x += 4)
{
int bytesRead = stream.Read(this.scratch, 0, 3);
if (bytesRead == 0)
@@ -72,6 +66,7 @@ internal class B44ExrCompression : ExrBaseDecompressor
ExrThrowHelper.ThrowInvalidImageContentException("Could not read enough data from the stream!");
}
+ // Check if 3-byte encoded flat field.
if (this.scratch[2] >= 13 << 2)
{
Unpack3(this.scratch, this.s);
@@ -89,8 +84,8 @@ internal class B44ExrCompression : ExrBaseDecompressor
bytesLeft -= 14;
}
- int n = x + 3 < this.width ? 4 : this.width - x;
- if (y + 3 < this.rowsPerBlock)
+ int n = x + 3 < this.Width ? 4 : this.Width - x;
+ if (y + 3 < this.RowsPerBlock)
{
this.s.AsSpan(0, n).CopyTo(row0[rowOffset..]);
this.s.AsSpan(4, n).CopyTo(row1[rowOffset..]);
@@ -100,12 +95,12 @@ internal class B44ExrCompression : ExrBaseDecompressor
else
{
this.s.AsSpan(0, n).CopyTo(row0[rowOffset..]);
- if (y + 1 < this.rowsPerBlock)
+ if (y + 1 < this.RowsPerBlock)
{
this.s.AsSpan(4, n).CopyTo(row1[rowOffset..]);
}
- if (y + 2 < this.rowsPerBlock)
+ if (y + 2 < this.RowsPerBlock)
{
this.s.AsSpan(8, n).CopyTo(row2[rowOffset..]);
}
@@ -124,16 +119,16 @@ internal class B44ExrCompression : ExrBaseDecompressor
// Rearrange the decompressed data such that the data for each scan line form a contiguous block.
int offsetDecompressed = 0;
int offsetOutput = 0;
- int blockSize = (int)(this.width * this.rowsPerBlock);
- for (int y = 0; y < this.rowsPerBlock; y++)
+ int blockSize = (int)(this.Width * this.RowsPerBlock);
+ for (int y = 0; y < this.RowsPerBlock; y++)
{
for (int i = 0; i < this.channelCount; i++)
{
- decompressed.Slice(offsetDecompressed + (i * blockSize), this.width).CopyTo(outputBuffer[offsetOutput..]);
- offsetOutput += this.width;
+ decompressed.Slice(offsetDecompressed + (i * blockSize), this.Width).CopyTo(outputBuffer[offsetOutput..]);
+ offsetOutput += this.Width;
}
- offsetDecompressed += this.width;
+ offsetDecompressed += this.Width;
}
}
diff --git a/src/ImageSharp/Formats/Exr/Compression/Decompressors/NoneExrCompression.cs b/src/ImageSharp/Formats/Exr/Compression/Decompressors/NoneExrCompression.cs
index c15ffbe325..19edb31afe 100644
--- a/src/ImageSharp/Formats/Exr/Compression/Decompressors/NoneExrCompression.cs
+++ b/src/ImageSharp/Formats/Exr/Compression/Decompressors/NoneExrCompression.cs
@@ -17,8 +17,10 @@ internal class NoneExrCompression : ExrBaseDecompressor
/// 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)
+ /// The pixel rows per block.
+ /// The number of pixels per row.
+ public NoneExrCompression(MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow, uint rowsPerBlock, int width)
+ : base(allocator, bytesPerBlock, bytesPerRow, rowsPerBlock, width)
{
}
diff --git a/src/ImageSharp/Formats/Exr/Compression/Decompressors/Pxr24Compression.cs b/src/ImageSharp/Formats/Exr/Compression/Decompressors/Pxr24Compression.cs
new file mode 100644
index 0000000000..f45b660e7d
--- /dev/null
+++ b/src/ImageSharp/Formats/Exr/Compression/Decompressors/Pxr24Compression.cs
@@ -0,0 +1,153 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System.Buffers;
+using System.Runtime.InteropServices;
+using SixLabors.ImageSharp.Formats.Exr.Constants;
+using SixLabors.ImageSharp.IO;
+using SixLabors.ImageSharp.Memory;
+
+namespace SixLabors.ImageSharp.Formats.Exr.Compression.Decompressors;
+
+///
+/// Implementation of PXR24 decompressor for EXR image data.
+///
+internal class Pxr24Compression : ExrBaseDecompressor
+{
+ private readonly IMemoryOwner tmpBuffer;
+
+ private readonly int channelCount;
+
+ private readonly ExrPixelType pixelType;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The memory allocator.
+ /// The bytes per pixel row block.
+ /// The bytes per pixel row.
+ /// The pixel rows per block.
+ /// The witdh of one row in pixels.
+ /// The number of channels for a pixel.
+ /// The pixel type.
+ public Pxr24Compression(MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow, uint rowsPerBlock, int width, int channelCount, ExrPixelType pixelType)
+ : base(allocator, bytesPerBlock, bytesPerRow, rowsPerBlock, width)
+ {
+ this.tmpBuffer = allocator.Allocate((int)bytesPerBlock);
+ this.channelCount = channelCount;
+ this.pixelType = pixelType;
+ }
+
+ ///
+ public override void Decompress(BufferedReadStream stream, uint compressedBytes, Span buffer)
+ {
+ Span uncompressed = this.tmpBuffer.GetSpan();
+ Span outputBufferHalf = MemoryMarshal.Cast(buffer);
+ Span outputBufferFloat = MemoryMarshal.Cast(buffer);
+ Span outputBufferUint = MemoryMarshal.Cast(buffer);
+
+ uint uncompressedBytes = this.BytesPerBlock;
+ UndoZipCompression(stream, compressedBytes, uncompressed, uncompressedBytes);
+
+ int lastIn = 0;
+ int outputOffset = 0;
+ for (int y = 0; y < this.RowsPerBlock; y++)
+ {
+ for (int c = 0; c < this.channelCount; c++)
+ {
+ switch (this.pixelType)
+ {
+ case ExrPixelType.UnsignedInt:
+ {
+ int offsetT0 = lastIn;
+ lastIn += this.Width;
+ int offsetT1 = lastIn;
+ lastIn += this.Width;
+ int offsetT2 = lastIn;
+ lastIn += this.Width;
+ int offsetT3 = lastIn;
+ lastIn += this.Width;
+
+ uint pixel = 0;
+ for (int x = 0; x < this.Width; x++)
+ {
+ uint t0 = uncompressed[offsetT0];
+ uint t1 = uncompressed[offsetT1];
+ uint t2 = uncompressed[offsetT2];
+ uint t3 = uncompressed[offsetT3];
+ uint diff = (t0 << 24) | (t1 << 16) | (t2 << 8) | t3;
+
+ pixel += diff;
+ outputBufferUint[outputOffset] = pixel;
+
+ offsetT0++;
+ offsetT1++;
+ offsetT2++;
+ offsetT3++;
+ outputOffset++;
+ }
+
+ break;
+ }
+
+ case ExrPixelType.Half:
+ {
+ int offsetT0 = lastIn;
+ lastIn += this.Width;
+ int offsetT1 = lastIn;
+ lastIn += this.Width;
+
+ uint pixel = 0;
+ for (int x = 0; x < this.Width; x++)
+ {
+ uint t0 = uncompressed[offsetT0];
+ uint t1 = uncompressed[offsetT1];
+ uint diff = (t0 << 8) | t1;
+
+ pixel += diff;
+ outputBufferHalf[outputOffset] = (ushort)pixel;
+
+ offsetT0++;
+ offsetT1++;
+ outputOffset++;
+ }
+
+ break;
+ }
+
+ case ExrPixelType.Float:
+ {
+ int offsetT0 = lastIn;
+ lastIn += this.Width;
+ int offsetT1 = lastIn;
+ lastIn += this.Width;
+ int offsetT2 = lastIn;
+ lastIn += this.Width;
+
+ uint pixel = 0;
+ for (int x = 0; x < this.Width; x++)
+ {
+ uint t0 = uncompressed[offsetT0];
+ uint t1 = uncompressed[offsetT1];
+ uint t2 = uncompressed[offsetT2];
+ uint diff = (t0 << 24) | (t1 << 16) | (t2 << 8);
+
+ pixel += diff;
+ outputBufferFloat[outputOffset] = pixel;
+
+ offsetT0++;
+ offsetT1++;
+ offsetT2++;
+ outputOffset++;
+ }
+
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ ///
+ protected override void Dispose(bool disposing) => this.tmpBuffer.Dispose();
+}
diff --git a/src/ImageSharp/Formats/Exr/Compression/Decompressors/RunLengthExrCompression.cs b/src/ImageSharp/Formats/Exr/Compression/Decompressors/RunLengthExrCompression.cs
index 489493d82f..f548a81810 100644
--- a/src/ImageSharp/Formats/Exr/Compression/Decompressors/RunLengthExrCompression.cs
+++ b/src/ImageSharp/Formats/Exr/Compression/Decompressors/RunLengthExrCompression.cs
@@ -14,16 +14,16 @@ 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);
+ /// The pixel rows per block.
+ /// The witdh of one row in pixels.
+ public RunLengthExrCompression(MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow, uint rowsPerBlock, int width)
+ : base(allocator, bytesPerBlock, bytesPerRow, rowsPerBlock, width) => this.tmpBuffer = allocator.Allocate((int)bytesPerBlock);
///
public override void Decompress(BufferedReadStream stream, uint compressedBytes, Span buffer)
diff --git a/src/ImageSharp/Formats/Exr/Compression/Decompressors/ZipExrCompression.cs b/src/ImageSharp/Formats/Exr/Compression/Decompressors/ZipExrCompression.cs
index dafe3d8321..8bab76f402 100644
--- a/src/ImageSharp/Formats/Exr/Compression/Decompressors/ZipExrCompression.cs
+++ b/src/ImageSharp/Formats/Exr/Compression/Decompressors/ZipExrCompression.cs
@@ -2,8 +2,6 @@
// Licensed under the Six Labors Split License.
using System.Buffers;
-using System.IO.Compression;
-using SixLabors.ImageSharp.Compression.Zlib;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
@@ -22,41 +20,18 @@ internal class ZipExrCompression : ExrBaseDecompressor
/// 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);
+ /// The pixel rows per block.
+ /// The witdh of one row in pixels.
+ public ZipExrCompression(MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow, uint rowsPerBlock, int width)
+ : base(allocator, bytesPerBlock, bytesPerRow, rowsPerBlock, width) => this.tmpBuffer = allocator.Allocate((int)bytesPerBlock);
///
public override void Decompress(BufferedReadStream stream, uint compressedBytes, Span buffer)
{
Span uncompressed = this.tmpBuffer.GetSpan();
- long pos = stream.Position;
- using ZlibInflateStream inflateStream = new(
- stream,
- () =>
- {
- int left = (int)(compressedBytes - (stream.Position - pos));
- return left > 0 ? left : 0;
- });
- inflateStream.AllocateNewBytes((int)this.BytesPerBlock, true);
- using DeflateStream dataStream = inflateStream.CompressedStream!;
-
- int totalRead = 0;
- while (totalRead < buffer.Length)
- {
- int bytesRead = dataStream.Read(uncompressed, totalRead, buffer.Length - totalRead);
- if (bytesRead <= 0)
- {
- break;
- }
-
- totalRead += bytesRead;
- }
-
- if (totalRead == 0)
- {
- ExrThrowHelper.ThrowInvalidImageContentException("Could not read enough data for zip compressed image data!");
- }
+ uint uncompressedBytes = (uint)buffer.Length;
+ int totalRead = UndoZipCompression(stream, compressedBytes, uncompressed, uncompressedBytes);
Reconstruct(uncompressed, (uint)totalRead);
Interleave(uncompressed, (uint)totalRead, buffer);
diff --git a/src/ImageSharp/Formats/Exr/Compression/ExrBaseCompression.cs b/src/ImageSharp/Formats/Exr/Compression/ExrBaseCompression.cs
index b116dffc65..72d5be980b 100644
--- a/src/ImageSharp/Formats/Exr/Compression/ExrBaseCompression.cs
+++ b/src/ImageSharp/Formats/Exr/Compression/ExrBaseCompression.cs
@@ -18,11 +18,15 @@ internal abstract class ExrBaseCompression : IDisposable
/// The memory allocator.
/// The bytes per block.
/// The bytes per row.
- protected ExrBaseCompression(MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow)
+ /// The number of pixel rows per block.
+ /// The number of pixels of a row.
+ protected ExrBaseCompression(MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow, uint rowsPerBlock, int width)
{
this.Allocator = allocator;
this.BytesPerBlock = bytesPerBlock;
this.BytesPerRow = bytesPerRow;
+ this.RowsPerBlock = rowsPerBlock;
+ this.Width = width;
}
///
@@ -45,6 +49,11 @@ internal abstract class ExrBaseCompression : IDisposable
///
public uint BytesPerBlock { get; }
+ ///
+ /// Gets the number of pixel rows per block.
+ ///
+ public uint RowsPerBlock { get; }
+
///
/// Gets the image width.
///
diff --git a/src/ImageSharp/Formats/Exr/Compression/ExrBaseDecompressor.cs b/src/ImageSharp/Formats/Exr/Compression/ExrBaseDecompressor.cs
index efe3c7877f..1b2d95ba53 100644
--- a/src/ImageSharp/Formats/Exr/Compression/ExrBaseDecompressor.cs
+++ b/src/ImageSharp/Formats/Exr/Compression/ExrBaseDecompressor.cs
@@ -1,6 +1,8 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
+using System.IO.Compression;
+using SixLabors.ImageSharp.Compression.Zlib;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
@@ -17,8 +19,10 @@ internal abstract class ExrBaseDecompressor : ExrBaseCompression
/// The memory allocator.
/// The bytes per row block.
/// The bytes per row.
- protected ExrBaseDecompressor(MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow)
- : base(allocator, bytesPerBlock, bytesPerRow)
+ /// The pixel rows per block.
+ /// The number of pixels per row.
+ protected ExrBaseDecompressor(MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow, uint rowsPerBlock, int width)
+ : base(allocator, bytesPerBlock, bytesPerRow, rowsPerBlock, width)
{
}
@@ -30,6 +34,47 @@ internal abstract class ExrBaseDecompressor : ExrBaseCompression
/// The buffer to write the decompressed data to.
public abstract void Decompress(BufferedReadStream stream, uint compressedBytes, Span buffer);
+ ///
+ /// Decompresses zip compressed data.
+ ///
+ /// The buffered stream to decompress.
+ /// The compressed bytes.
+ /// The buffer to write the uncompressed data to.
+ /// The uncompressed bytes.
+ /// The total bytes read from the stream.
+ protected static int UndoZipCompression(BufferedReadStream stream, uint compressedBytes, Span uncompressed, uint uncompressedBytes)
+ {
+ long pos = stream.Position;
+ using ZlibInflateStream inflateStream = new(
+ stream,
+ () =>
+ {
+ int left = (int)(compressedBytes - (stream.Position - pos));
+ return left > 0 ? left : 0;
+ });
+ inflateStream.AllocateNewBytes((int)compressedBytes, true);
+ using DeflateStream dataStream = inflateStream.CompressedStream!;
+
+ int totalRead = 0;
+ while (totalRead < uncompressedBytes)
+ {
+ int bytesRead = dataStream.Read(uncompressed, totalRead, (int)uncompressedBytes - totalRead);
+ if (bytesRead <= 0)
+ {
+ break;
+ }
+
+ totalRead += bytesRead;
+ }
+
+ if (totalRead == 0)
+ {
+ ExrThrowHelper.ThrowInvalidImageContentException("Could not read enough data for zip compressed EXR image data!");
+ }
+
+ return totalRead;
+ }
+
///
/// Integrate over all differences to the previous value in order to
/// reconstruct sample values.
diff --git a/src/ImageSharp/Formats/Exr/Compression/ExrCompressorFactory.cs b/src/ImageSharp/Formats/Exr/Compression/ExrCompressorFactory.cs
index b1b7d2e8e6..0a7d4157f2 100644
--- a/src/ImageSharp/Formats/Exr/Compression/ExrCompressorFactory.cs
+++ b/src/ImageSharp/Formats/Exr/Compression/ExrCompressorFactory.cs
@@ -21,6 +21,8 @@ internal static class ExrCompressorFactory
/// The output stream.
/// The bytes per block.
/// The bytes per row.
+ /// The pixel rows per block.
+ /// The witdh of one row in pixels.
/// The deflate compression level.
/// A compressor for EXR image data.
public static ExrBaseCompressor Create(
@@ -29,11 +31,13 @@ internal static class ExrCompressorFactory
Stream output,
uint bytesPerBlock,
uint bytesPerRow,
+ uint rowsPerBlock,
+ int width,
DeflateCompressionLevel compressionLevel = DeflateCompressionLevel.DefaultCompression) => method switch
{
- 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),
+ ExrCompression.None => new NoneExrCompressor(output, allocator, bytesPerBlock, bytesPerRow, rowsPerBlock, width),
+ ExrCompression.Zips => new ZipExrCompressor(output, allocator, bytesPerBlock, bytesPerRow, rowsPerBlock, width, compressionLevel),
+ ExrCompression.Zip => new ZipExrCompressor(output, allocator, bytesPerBlock, bytesPerRow, rowsPerBlock, width, 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 62519666b5..353d1af117 100644
--- a/src/ImageSharp/Formats/Exr/Compression/ExrDecompressorFactory.cs
+++ b/src/ImageSharp/Formats/Exr/Compression/ExrDecompressorFactory.cs
@@ -22,6 +22,7 @@ internal static class ExrDecompressorFactory
/// The bytes per row.
/// The rows per block.
/// The number of image channels.
+ /// The pixel type.
/// Decompressor for EXR image data.
public static ExrBaseDecompressor Create(
ExrCompression method,
@@ -30,13 +31,15 @@ internal static class ExrDecompressorFactory
uint bytesPerBlock,
uint bytesPerRow,
uint rowsPerBlock,
- int channelCount) => method switch
+ int channelCount,
+ ExrPixelType pixelType) => method switch
{
- 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.None => new NoneExrCompression(memoryAllocator, bytesPerBlock, bytesPerRow, rowsPerBlock, width),
+ ExrCompression.Zips => new ZipExrCompression(memoryAllocator, bytesPerBlock, bytesPerRow, rowsPerBlock, width),
+ ExrCompression.Zip => new ZipExrCompression(memoryAllocator, bytesPerBlock, bytesPerRow, rowsPerBlock, width),
+ ExrCompression.RunLengthEncoded => new RunLengthExrCompression(memoryAllocator, bytesPerBlock, bytesPerRow, rowsPerBlock, width),
ExrCompression.B44 => new B44ExrCompression(memoryAllocator, bytesPerBlock, bytesPerRow, rowsPerBlock, width, channelCount),
+ ExrCompression.Pxr24 => new Pxr24Compression(memoryAllocator, bytesPerBlock, bytesPerRow, rowsPerBlock, width, channelCount, pixelType),
_ => throw ExrThrowHelper.NotSupportedDecompressor(nameof(method)),
};
}
diff --git a/src/ImageSharp/Formats/Exr/ExrBaseCompressor.cs b/src/ImageSharp/Formats/Exr/ExrBaseCompressor.cs
index 1dcdec5be0..9e3fe8ec65 100644
--- a/src/ImageSharp/Formats/Exr/ExrBaseCompressor.cs
+++ b/src/ImageSharp/Formats/Exr/ExrBaseCompressor.cs
@@ -14,8 +14,10 @@ internal abstract class ExrBaseCompressor : ExrBaseCompression
/// The memory allocator.
/// Bytes per row block.
/// Bytes per pixel row.
- protected ExrBaseCompressor(Stream output, MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow)
- : base(allocator, bytesPerBlock, bytesPerRow)
+ /// The pixel rows per block.
+ /// The number of pixels per row.
+ protected ExrBaseCompressor(Stream output, MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow, uint rowsPerBlock, int width)
+ : base(allocator, bytesPerBlock, bytesPerRow, rowsPerBlock, width)
=> this.Output = output;
///
diff --git a/src/ImageSharp/Formats/Exr/ExrDecoderCore.cs b/src/ImageSharp/Formats/Exr/ExrDecoderCore.cs
index 0b75154c2c..2e60f3a5b9 100644
--- a/src/ImageSharp/Formats/Exr/ExrDecoderCore.cs
+++ b/src/ImageSharp/Formats/Exr/ExrDecoderCore.cs
@@ -154,7 +154,15 @@ internal sealed class ExrDecoderCore : ImageDecoderCore
Span bluePixelData = rowBuffer.GetSpan().Slice(width * 2, width);
Span alphaPixelData = rowBuffer.GetSpan().Slice(width * 3, width);
- using ExrBaseDecompressor decompressor = ExrDecompressorFactory.Create(this.Compression, this.memoryAllocator, width, bytesPerBlock, bytesPerRow, rowsPerBlock, channelCount);
+ using ExrBaseDecompressor decompressor = ExrDecompressorFactory.Create(
+ this.Compression,
+ this.memoryAllocator,
+ width,
+ bytesPerBlock,
+ bytesPerRow,
+ rowsPerBlock,
+ channelCount,
+ this.PixelType);
int decodedRows = 0;
while (decodedRows < height)
@@ -219,7 +227,15 @@ internal sealed class ExrDecoderCore : ImageDecoderCore
Span bluePixelData = rowBuffer.GetSpan().Slice(width * 2, width);
Span alphaPixelData = rowBuffer.GetSpan().Slice(width * 3, width);
- using ExrBaseDecompressor decompressor = ExrDecompressorFactory.Create(this.Compression, this.memoryAllocator, width, bytesPerBlock, bytesPerRow, rowsPerBlock, channelCount);
+ using ExrBaseDecompressor decompressor = ExrDecompressorFactory.Create(
+ this.Compression,
+ this.memoryAllocator,
+ width,
+ bytesPerBlock,
+ bytesPerRow,
+ rowsPerBlock,
+ channelCount,
+ this.PixelType);
int decodedRows = 0;
while (decodedRows < height)
@@ -846,7 +862,7 @@ internal sealed class ExrDecoderCore : ImageDecoderCore
/// 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,
+ ExrCompression.None or ExrCompression.Zip or ExrCompression.Zips or ExrCompression.RunLengthEncoded or ExrCompression.B44 or ExrCompression.Pxr24 => true,
_ => false,
};
diff --git a/src/ImageSharp/Formats/Exr/ExrEncoderCore.cs b/src/ImageSharp/Formats/Exr/ExrEncoderCore.cs
index 04ca5e40c8..e2750d8972 100644
--- a/src/ImageSharp/Formats/Exr/ExrEncoderCore.cs
+++ b/src/ImageSharp/Formats/Exr/ExrEncoderCore.cs
@@ -181,7 +181,7 @@ internal sealed class ExrEncoderCore
Span blueBuffer = rgbBuffer.GetSpan().Slice(width * 2, width);
Span alphaBuffer = rgbBuffer.GetSpan().Slice(width * 3, width);
- using ExrBaseCompressor compressor = ExrCompressorFactory.Create(compression, this.memoryAllocator, stream, bytesPerBlock, bytesPerRow);
+ using ExrBaseCompressor compressor = ExrCompressorFactory.Create(compression, this.memoryAllocator, stream, bytesPerBlock, bytesPerRow, rowsPerBlock, width);
ulong[] rowOffsets = new ulong[height];
for (uint y = 0; y < height; y += rowsPerBlock)
@@ -273,7 +273,7 @@ internal sealed class ExrEncoderCore
Span blueBuffer = rgbBuffer.GetSpan().Slice(width * 2, width);
Span alphaBuffer = rgbBuffer.GetSpan().Slice(width * 3, width);
- using ExrBaseCompressor compressor = ExrCompressorFactory.Create(compression, this.memoryAllocator, stream, bytesPerBlock, bytesPerRow);
+ using ExrBaseCompressor compressor = ExrCompressorFactory.Create(compression, this.memoryAllocator, stream, bytesPerBlock, bytesPerRow, rowsPerBlock, width);
Rgba128 rgb = default;
ulong[] rowOffsets = new ulong[height];
diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
index d2883e2811..49640fae08 100644
--- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
+++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
@@ -303,7 +303,7 @@ internal sealed class GifEncoderCore
this.WriteGraphicalControlExtension(metadata, stream);
Buffer2D indices = ((IPixelSource)quantized).PixelBuffer;
- Rectangle interest = indices.FullRectangle();
+ Rectangle interest = indices.Bounds;
bool useLocal = this.colorTableMode == FrameColorTableMode.Local || (metadata.ColorTableMode == FrameColorTableMode.Local);
int bitDepth = ColorNumerics.GetBitsNeededForColorDepth(quantized.Palette.Length);
diff --git a/src/ImageSharp/ImageFrame.cs b/src/ImageSharp/ImageFrame.cs
index cd6fa6d98a..2d726dc4a0 100644
--- a/src/ImageSharp/ImageFrame.cs
+++ b/src/ImageSharp/ImageFrame.cs
@@ -71,6 +71,19 @@ public abstract partial class ImageFrame : IConfigurationProvider, IDisposable
/// Whether to dispose of managed and unmanaged objects.
protected abstract void Dispose(bool disposing);
+ ///
+ /// Accepts a .
+ /// Implemented by invoking
+ /// with the pixel type of the image.
+ ///
+ /// The visitor.
+ internal abstract void Accept(IImageFrameVisitor visitor);
+
+ ///
+ /// Copies the pixel data of the image frame to a of a specific pixel type.
+ ///
+ /// The pixel type of the destination buffer.
+ /// The buffer to copy the pixel data to.
internal abstract void CopyPixelsTo(Buffer2D destination)
where TDestinationPixel : unmanaged, IPixel;
diff --git a/src/ImageSharp/ImageFrame{TPixel}.cs b/src/ImageSharp/ImageFrame{TPixel}.cs
index 4e25c4e581..9029d3eec2 100644
--- a/src/ImageSharp/ImageFrame{TPixel}.cs
+++ b/src/ImageSharp/ImageFrame{TPixel}.cs
@@ -339,6 +339,10 @@ public sealed class ImageFrame : ImageFrame, IPixelSource
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal ref TPixel GetPixelReference(int x, int y) => ref this.PixelBuffer[x, y];
+ ///
+ internal override void Accept(IImageFrameVisitor visitor)
+ => visitor.Visit(this);
+
///
/// Copies the pixels to a of the same size.
///
@@ -346,7 +350,7 @@ public sealed class ImageFrame : ImageFrame, IPixelSource
/// ImageFrame{TPixel}.CopyTo(): target must be of the same size!
internal void CopyTo(Buffer2D target)
{
- if (this.Size != target.Size())
+ if (this.Size != target.Size)
{
throw new ArgumentException("ImageFrame.CopyTo(): target must be of the same size!", nameof(target));
}
@@ -363,8 +367,8 @@ public sealed class ImageFrame : ImageFrame, IPixelSource
{
Guard.NotNull(source, nameof(source));
- Buffer2D.SwapOrCopyContent(this.PixelBuffer, source.PixelBuffer);
- this.UpdateSize(this.PixelBuffer.Size());
+ _ = Buffer2D.SwapOrCopyContent(this.PixelBuffer, source.PixelBuffer);
+ this.UpdateSize(this.PixelBuffer.Size);
}
///
@@ -475,10 +479,7 @@ public sealed class ImageFrame : ImageFrame, IPixelSource
/// Clears the bitmap.
///
/// The value to initialize the bitmap with.
- internal void Clear(TPixel value)
- {
- this.PixelBuffer.Clear(value);
- }
+ internal void Clear(TPixel value) => this.PixelBuffer.Clear(value);
[MethodImpl(InliningOptions.ShortMethod)]
private void VerifyCoords(int x, int y)
diff --git a/src/ImageSharp/Image{TPixel}.cs b/src/ImageSharp/Image{TPixel}.cs
index 18db343563..4881518c9c 100644
--- a/src/ImageSharp/Image{TPixel}.cs
+++ b/src/ImageSharp/Image{TPixel}.cs
@@ -2,7 +2,6 @@
// Licensed under the Six Labors Split License.
using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
diff --git a/src/ImageSharp/Memory/Allocators/MemoryAllocator.cs b/src/ImageSharp/Memory/Allocators/MemoryAllocator.cs
index d6badf9282..8eaf0b6d69 100644
--- a/src/ImageSharp/Memory/Allocators/MemoryAllocator.cs
+++ b/src/ImageSharp/Memory/Allocators/MemoryAllocator.cs
@@ -12,8 +12,6 @@ namespace SixLabors.ImageSharp.Memory;
public abstract class MemoryAllocator
{
private const int OneGigabyte = 1 << 30;
- private long accumulativeAllocatedBytes;
- private int trackingSuppressionCount;
///
/// Gets the default platform-specific global instance that
@@ -25,44 +23,9 @@ public abstract class MemoryAllocator
///
public static MemoryAllocator Default { get; } = Create();
- ///
- /// Gets the maximum number of bytes that can be allocated by a memory group.
- ///
- ///
- /// The allocation limit is determined by the process architecture: 4 GB for 64-bit processes and
- /// 1 GB for 32-bit processes.
- ///
- internal long MemoryGroupAllocationLimitBytes { get; private protected set; } = Environment.Is64BitProcess ? 4L * OneGigabyte : OneGigabyte;
-
- ///
- /// Gets the maximum allowed total allocation size, in bytes, for the current process.
- ///
- ///
- /// Defaults to , effectively imposing no limit on total allocations.
- /// This property can be set to enforce a cap on total memory usage across all allocations made through this allocator instance, providing
- /// a safeguard against excessive memory consumption.
- /// When the cumulative size of active allocations exceeds this limit, an will be thrown to
- /// prevent further allocations and signal that the limit has been breached.
- ///
- internal long AccumulativeAllocationLimitBytes { get; private protected set; } = long.MaxValue;
+ internal long MemoryGroupAllocationLimitBytes { get; private set; } = Environment.Is64BitProcess ? 4L * OneGigabyte : OneGigabyte;
- ///
- /// Gets the maximum size, in bytes, that can be allocated for a single buffer.
- ///
- ///
- /// The single buffer allocation limit is set to 1 GB by default.
- ///
- internal int SingleBufferAllocationLimitBytes { get; private protected set; } = OneGigabyte;
-
- ///
- /// Gets a value indicating whether change tracking is currently suppressed for this instance.
- ///
- ///
- /// When change tracking is suppressed, modifications to the object will not be recorded or
- /// trigger change notifications. This property is used internally to temporarily disable tracking during
- /// batch updates or initialization.
- ///
- private bool IsTrackingSuppressed => Volatile.Read(ref this.trackingSuppressionCount) > 0;
+ internal int SingleBufferAllocationLimitBytes { get; private set; } = OneGigabyte;
///
/// Gets the length of the largest contiguous buffer that can be handled by this allocator instance in bytes.
@@ -90,11 +53,6 @@ public abstract class MemoryAllocator
allocator.SingleBufferAllocationLimitBytes = (int)Math.Min(allocator.SingleBufferAllocationLimitBytes, allocator.MemoryGroupAllocationLimitBytes);
}
- if (options.AccumulativeAllocationLimitMegabytes.HasValue)
- {
- allocator.AccumulativeAllocationLimitBytes = options.AccumulativeAllocationLimitMegabytes.Value * 1024L * 1024L;
- }
-
return allocator;
}
@@ -114,10 +72,6 @@ public abstract class MemoryAllocator
/// Releases all retained resources not being in use.
/// Eg: by resetting array pools and letting GC to free the arrays.
///
- ///
- /// This does not dispose active allocations; callers are responsible for disposing all
- /// instances to release memory.
- ///
public virtual void ReleaseRetainedResources()
{
}
@@ -148,137 +102,11 @@ public abstract class MemoryAllocator
InvalidMemoryOperationException.ThrowAllocationOverLimitException(totalLengthInBytes, this.MemoryGroupAllocationLimitBytes);
}
- long totalLengthInBytesLong = (long)totalLengthInBytes;
- this.ReserveAllocation(totalLengthInBytesLong);
-
- using (this.SuppressTracking())
- {
- try
- {
- MemoryGroup group = this.AllocateGroupCore(totalLength, totalLengthInBytesLong, bufferAlignment, options);
- group.SetAllocationTracking(this, totalLengthInBytesLong);
- return group;
- }
- catch
- {
- this.ReleaseAccumulatedBytes(totalLengthInBytesLong);
- throw;
- }
- }
+ // Cast to long is safe because we already checked that the total length is within the limit.
+ return this.AllocateGroupCore(totalLength, (long)totalLengthInBytes, bufferAlignment, options);
}
internal virtual MemoryGroup AllocateGroupCore(long totalLengthInElements, long totalLengthInBytes, int bufferAlignment, AllocationOptions options)
where T : struct
=> MemoryGroup.Allocate(this, totalLengthInElements, bufferAlignment, options);
-
- ///
- /// Tracks the allocation of an instance after reserving bytes.
- ///
- /// Type of the data stored in the buffer.
- /// The allocation to track.
- /// The allocation size in bytes.
- /// The tracked allocation.
- protected IMemoryOwner TrackAllocation(IMemoryOwner owner, ulong lengthInBytes)
- where T : struct
- {
- if (this.IsTrackingSuppressed || lengthInBytes == 0)
- {
- return owner;
- }
-
- return new TrackingMemoryOwner(owner, this, (long)lengthInBytes);
- }
-
- ///
- /// Reserves accumulative allocation bytes before creating the underlying buffer.
- ///
- /// The number of bytes to reserve.
- protected void ReserveAllocation(long lengthInBytes)
- {
- if (this.IsTrackingSuppressed || lengthInBytes <= 0)
- {
- return;
- }
-
- long total = Interlocked.Add(ref this.accumulativeAllocatedBytes, lengthInBytes);
- if (total > this.AccumulativeAllocationLimitBytes)
- {
- _ = Interlocked.Add(ref this.accumulativeAllocatedBytes, -lengthInBytes);
- InvalidMemoryOperationException.ThrowAllocationOverLimitException((ulong)lengthInBytes, this.AccumulativeAllocationLimitBytes);
- }
- }
-
- ///
- /// Releases accumulative allocation bytes previously tracked by this allocator.
- ///
- /// The number of bytes to release.
- internal void ReleaseAccumulatedBytes(long lengthInBytes)
- {
- if (lengthInBytes <= 0)
- {
- return;
- }
-
- _ = Interlocked.Add(ref this.accumulativeAllocatedBytes, -lengthInBytes);
- }
-
- ///
- /// Suppresses accumulative allocation tracking for the lifetime of the returned scope.
- ///
- /// An that restores tracking when disposed.
- internal IDisposable SuppressTracking() => new TrackingSuppressionScope(this);
-
- ///
- /// Temporarily suppresses accumulative allocation tracking within a scope.
- ///
- private sealed class TrackingSuppressionScope : IDisposable
- {
- private MemoryAllocator? allocator;
-
- public TrackingSuppressionScope(MemoryAllocator allocator)
- {
- this.allocator = allocator;
- _ = Interlocked.Increment(ref allocator.trackingSuppressionCount);
- }
-
- public void Dispose()
- {
- if (this.allocator != null)
- {
- _ = Interlocked.Decrement(ref this.allocator.trackingSuppressionCount);
- this.allocator = null;
- }
- }
- }
-
- ///
- /// Wraps an to release accumulative tracking on dispose.
- ///
- private sealed class TrackingMemoryOwner : IMemoryOwner
- where T : struct
- {
- private IMemoryOwner? owner;
- private readonly MemoryAllocator allocator;
- private readonly long lengthInBytes;
-
- public TrackingMemoryOwner(IMemoryOwner owner, MemoryAllocator allocator, long lengthInBytes)
- {
- this.owner = owner;
- this.allocator = allocator;
- this.lengthInBytes = lengthInBytes;
- }
-
- public Memory Memory => this.owner?.Memory ?? Memory.Empty;
-
- public void Dispose()
- {
- // Ensure only one caller disposes the inner owner and releases the reservation.
- IMemoryOwner? inner = Interlocked.Exchange(ref this.owner, null);
- if (inner != null)
- {
- inner.Dispose();
- this.allocator.ReleaseAccumulatedBytes(this.lengthInBytes);
- }
- }
- }
}
diff --git a/src/ImageSharp/Memory/Allocators/MemoryAllocatorOptions.cs b/src/ImageSharp/Memory/Allocators/MemoryAllocatorOptions.cs
index 3339203dac..d9ba62c1ef 100644
--- a/src/ImageSharp/Memory/Allocators/MemoryAllocatorOptions.cs
+++ b/src/ImageSharp/Memory/Allocators/MemoryAllocatorOptions.cs
@@ -10,7 +10,6 @@ public struct MemoryAllocatorOptions
{
private int? maximumPoolSizeMegabytes;
private int? allocationLimitMegabytes;
- private int? accumulativeAllocationLimitMegabytes;
///
/// Gets or sets a value defining the maximum size of the 's internal memory pool
@@ -18,7 +17,7 @@ public struct MemoryAllocatorOptions
///
public int? MaximumPoolSizeMegabytes
{
- readonly get => this.maximumPoolSizeMegabytes;
+ get => this.maximumPoolSizeMegabytes;
set
{
if (value.HasValue)
@@ -36,7 +35,7 @@ public struct MemoryAllocatorOptions
///
public int? AllocationLimitMegabytes
{
- readonly get => this.allocationLimitMegabytes;
+ get => this.allocationLimitMegabytes;
set
{
if (value.HasValue)
@@ -47,29 +46,4 @@ public struct MemoryAllocatorOptions
this.allocationLimitMegabytes = value;
}
}
-
- ///
- /// Gets or sets a value defining the maximum total size that can be allocated by the allocator in Megabytes.
- /// means platform default: 2GB on 32-bit processes, 8GB on 64-bit processes.
- ///
- public int? AccumulativeAllocationLimitMegabytes
- {
- readonly get => this.accumulativeAllocationLimitMegabytes;
- set
- {
- if (value.HasValue)
- {
- Guard.MustBeGreaterThan(value.Value, 0, nameof(this.AccumulativeAllocationLimitMegabytes));
- if (this.AllocationLimitMegabytes.HasValue)
- {
- Guard.MustBeGreaterThanOrEqualTo(
- value.Value,
- this.AllocationLimitMegabytes.Value,
- nameof(this.AccumulativeAllocationLimitMegabytes));
- }
- }
-
- this.accumulativeAllocationLimitMegabytes = value;
- }
- }
}
diff --git a/src/ImageSharp/Memory/Allocators/SimpleGcMemoryAllocator.cs b/src/ImageSharp/Memory/Allocators/SimpleGcMemoryAllocator.cs
index 9cbb15713a..675afe8b9f 100644
--- a/src/ImageSharp/Memory/Allocators/SimpleGcMemoryAllocator.cs
+++ b/src/ImageSharp/Memory/Allocators/SimpleGcMemoryAllocator.cs
@@ -12,32 +12,6 @@ namespace SixLabors.ImageSharp.Memory;
///
public sealed class SimpleGcMemoryAllocator : MemoryAllocator
{
- ///
- /// Initializes a new instance of the class with default limits.
- ///
- public SimpleGcMemoryAllocator()
- : this(default)
- {
- }
-
- ///
- /// Initializes a new instance of the class with custom limits.
- ///
- /// The to apply.
- public SimpleGcMemoryAllocator(MemoryAllocatorOptions options)
- {
- if (options.AllocationLimitMegabytes.HasValue)
- {
- this.MemoryGroupAllocationLimitBytes = options.AllocationLimitMegabytes.Value * 1024L * 1024L;
- this.SingleBufferAllocationLimitBytes = (int)Math.Min(this.SingleBufferAllocationLimitBytes, this.MemoryGroupAllocationLimitBytes);
- }
-
- if (options.AccumulativeAllocationLimitMegabytes.HasValue)
- {
- this.AccumulativeAllocationLimitBytes = options.AccumulativeAllocationLimitMegabytes.Value * 1024L * 1024L;
- }
- }
-
///
protected internal override int GetBufferCapacityInBytes() => int.MaxValue;
@@ -55,18 +29,6 @@ public sealed class SimpleGcMemoryAllocator : MemoryAllocator
InvalidMemoryOperationException.ThrowAllocationOverLimitException(lengthInBytes, this.SingleBufferAllocationLimitBytes);
}
- long lengthInBytesLong = (long)lengthInBytes;
- this.ReserveAllocation(lengthInBytesLong);
-
- try
- {
- IMemoryOwner buffer = new BasicArrayBuffer(new T[length]);
- return this.TrackAllocation(buffer, lengthInBytes);
- }
- catch
- {
- this.ReleaseAccumulatedBytes(lengthInBytesLong);
- throw;
- }
+ return new BasicArrayBuffer(new T[length]);
}
}
diff --git a/src/ImageSharp/Memory/Allocators/UniformUnmanagedMemoryPoolMemoryAllocator.cs b/src/ImageSharp/Memory/Allocators/UniformUnmanagedMemoryPoolMemoryAllocator.cs
index e34860b37b..d5cd329f1b 100644
--- a/src/ImageSharp/Memory/Allocators/UniformUnmanagedMemoryPoolMemoryAllocator.cs
+++ b/src/ImageSharp/Memory/Allocators/UniformUnmanagedMemoryPoolMemoryAllocator.cs
@@ -92,24 +92,13 @@ internal sealed class UniformUnmanagedMemoryPoolMemoryAllocator : MemoryAllocato
if (lengthInBytes <= (ulong)this.sharedArrayPoolThresholdInBytes)
{
- long lengthInBytesLong = (long)lengthInBytes;
- this.ReserveAllocation(lengthInBytesLong);
-
- try
- {
- SharedArrayPoolBuffer buffer = new(length);
- if (options.Has(AllocationOptions.Clean))
- {
- buffer.GetSpan().Clear();
- }
-
- return this.TrackAllocation(buffer, lengthInBytes);
- }
- catch
+ SharedArrayPoolBuffer buffer = new(length);
+ if (options.Has(AllocationOptions.Clean))
{
- this.ReleaseAccumulatedBytes(lengthInBytesLong);
- throw;
+ buffer.GetSpan().Clear();
}
+
+ return buffer;
}
if (lengthInBytes <= (ulong)this.poolBufferSizeInBytes)
@@ -117,38 +106,12 @@ internal sealed class UniformUnmanagedMemoryPoolMemoryAllocator : MemoryAllocato
UnmanagedMemoryHandle mem = this.pool.Rent();
if (mem.IsValid)
{
- long lengthInBytesLong = (long)lengthInBytes;
- this.ReserveAllocation(lengthInBytesLong);
-
- try
- {
- UnmanagedBuffer buffer = this.pool.CreateGuardedBuffer(mem, length, options.Has(AllocationOptions.Clean));
- return this.TrackAllocation(buffer, lengthInBytes);
- }
- catch
- {
- this.ReleaseAccumulatedBytes(lengthInBytesLong);
- throw;
- }
+ UnmanagedBuffer buffer = this.pool.CreateGuardedBuffer(mem, length, options.Has(AllocationOptions.Clean));
+ return buffer;
}
}
- long nonPooledLengthInBytesLong = (long)lengthInBytes;
- this.ReserveAllocation(nonPooledLengthInBytesLong);
-
- try
- {
- using (this.nonPoolAllocator.SuppressTracking())
- {
- IMemoryOwner nonPooled = this.nonPoolAllocator.Allocate(length, options);
- return this.TrackAllocation(nonPooled, lengthInBytes);
- }
- }
- catch
- {
- this.ReleaseAccumulatedBytes(nonPooledLengthInBytesLong);
- throw;
- }
+ return this.nonPoolAllocator.Allocate(length, options);
}
///
@@ -181,10 +144,7 @@ internal sealed class UniformUnmanagedMemoryPoolMemoryAllocator : MemoryAllocato
return poolGroup;
}
- using (this.nonPoolAllocator.SuppressTracking())
- {
- return MemoryGroup.Allocate(this.nonPoolAllocator, totalLengthInElements, bufferAlignment, options);
- }
+ return MemoryGroup.Allocate(this.nonPoolAllocator, totalLengthInElements, bufferAlignment, options);
}
public override void ReleaseRetainedResources() => this.pool.Release();
diff --git a/src/ImageSharp/Memory/Allocators/UnmanagedMemoryAllocator.cs b/src/ImageSharp/Memory/Allocators/UnmanagedMemoryAllocator.cs
index 73cef8b5ee..daf1a79925 100644
--- a/src/ImageSharp/Memory/Allocators/UnmanagedMemoryAllocator.cs
+++ b/src/ImageSharp/Memory/Allocators/UnmanagedMemoryAllocator.cs
@@ -2,7 +2,6 @@
// Licensed under the Six Labors Split License.
using System.Buffers;
-using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory.Internals;
namespace SixLabors.ImageSharp.Memory;
@@ -20,26 +19,13 @@ internal class UnmanagedMemoryAllocator : MemoryAllocator
protected internal override int GetBufferCapacityInBytes() => this.bufferCapacityInBytes;
public override IMemoryOwner Allocate(int length, AllocationOptions options = AllocationOptions.None)
- where T : struct
{
- ulong lengthInBytes = (ulong)length * (ulong)Unsafe.SizeOf();
- long lengthInBytesLong = (long)lengthInBytes;
- this.ReserveAllocation(lengthInBytesLong);
-
- try
+ UnmanagedBuffer buffer = UnmanagedBuffer.Allocate(length);
+ if (options.Has(AllocationOptions.Clean))
{
- UnmanagedBuffer buffer = UnmanagedBuffer.Allocate(length);
- if (options.Has(AllocationOptions.Clean))
- {
- buffer.GetSpan().Clear();
- }
-
- return this.TrackAllocation(buffer, lengthInBytes);
- }
- catch
- {
- this.ReleaseAccumulatedBytes(lengthInBytesLong);
- throw;
+ buffer.GetSpan().Clear();
}
+
+ return buffer;
}
}
diff --git a/src/ImageSharp/Memory/Buffer2DExtensions.cs b/src/ImageSharp/Memory/Buffer2DExtensions.cs
index 96b9bc172f..290d978d0b 100644
--- a/src/ImageSharp/Memory/Buffer2DExtensions.cs
+++ b/src/ImageSharp/Memory/Buffer2DExtensions.cs
@@ -104,60 +104,40 @@ public static class Buffer2DExtensions
}
///
- /// Returns a representing the full area of the buffer.
- ///
- /// The element type
- /// The
- /// The
- internal static Rectangle FullRectangle(this Buffer2D buffer)
- where T : struct
- => new(0, 0, buffer.Width, buffer.Height);
-
- ///
- /// Return a to the subregion represented by 'rectangle'
+ /// Return a to the subregion represented by .
///
/// The element type
/// The
/// The rectangle subregion
/// The
- internal static Buffer2DRegion GetRegion(this Buffer2D buffer, Rectangle rectangle)
+ public static Buffer2DRegion GetRegion(this Buffer2D buffer, Rectangle rectangle)
where T : unmanaged =>
new(buffer, rectangle);
- internal static Buffer2DRegion GetRegion(this Buffer2D buffer, int x, int y, int width, int height)
+ ///
+ /// Return a to the specified area of .
+ ///
+ /// The element type.
+ /// The .
+ /// The X coordinate of the region.
+ /// The Y coordinate of the region.
+ /// The region width.
+ /// The region height.
+ /// The .
+ public static Buffer2DRegion GetRegion(this Buffer2D buffer, int x, int y, int width, int height)
where T : unmanaged =>
new(buffer, new Rectangle(x, y, width, height));
///
- /// Return a to the whole area of 'buffer'
+ /// Return a to the whole area of .
///
/// The element type
/// The
/// The
- internal static Buffer2DRegion GetRegion(this Buffer2D buffer)
+ public static Buffer2DRegion GetRegion(this Buffer2D buffer)
where T : unmanaged =>
new(buffer);
- ///
- /// Returns the size of the buffer.
- ///
- /// The element type
- /// The
- /// The of the buffer
- internal static Size Size(this Buffer2D buffer)
- where T : struct =>
- new(buffer.Width, buffer.Height);
-
- ///
- /// Gets the bounds of the buffer.
- ///
- /// The element type
- /// The
- /// The
- internal static Rectangle Bounds(this Buffer2D buffer)
- where T : struct =>
- new(0, 0, buffer.Width, buffer.Height);
-
[Conditional("DEBUG")]
private static void CheckColumnRegionsDoNotOverlap(
Buffer2D buffer,
diff --git a/src/ImageSharp/Memory/Buffer2DRegion{T}.cs b/src/ImageSharp/Memory/Buffer2DRegion{T}.cs
index 7bfb6f5731..c78c4ce9ee 100644
--- a/src/ImageSharp/Memory/Buffer2DRegion{T}.cs
+++ b/src/ImageSharp/Memory/Buffer2DRegion{T}.cs
@@ -15,17 +15,17 @@ public readonly struct Buffer2DRegion
/// Initializes a new instance of the struct.
///
/// The .
- /// The defining a rectangular area within the buffer.
+ /// The defining a rectangular area within the buffer.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Buffer2DRegion(Buffer2D buffer, Rectangle rectangle)
+ public Buffer2DRegion(Buffer2D buffer, Rectangle bounds)
{
- DebugGuard.MustBeGreaterThanOrEqualTo(rectangle.X, 0, nameof(rectangle));
- DebugGuard.MustBeGreaterThanOrEqualTo(rectangle.Y, 0, nameof(rectangle));
- DebugGuard.MustBeLessThanOrEqualTo(rectangle.Width, buffer.Width, nameof(rectangle));
- DebugGuard.MustBeLessThanOrEqualTo(rectangle.Height, buffer.Height, nameof(rectangle));
+ DebugGuard.MustBeGreaterThanOrEqualTo(bounds.X, 0, nameof(bounds));
+ DebugGuard.MustBeGreaterThanOrEqualTo(bounds.Y, 0, nameof(bounds));
+ DebugGuard.MustBeLessThanOrEqualTo(bounds.Width, buffer.Width, nameof(bounds));
+ DebugGuard.MustBeLessThanOrEqualTo(bounds.Height, buffer.Height, nameof(bounds));
this.Buffer = buffer;
- this.Rectangle = rectangle;
+ this.Bounds = bounds;
}
///
@@ -34,15 +34,10 @@ public readonly struct Buffer2DRegion
/// The .
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Buffer2DRegion(Buffer2D buffer)
- : this(buffer, buffer.FullRectangle())
+ : this(buffer, buffer.Bounds)
{
}
- ///
- /// Gets the rectangle specifying the boundaries of the area in .
- ///
- public Rectangle Rectangle { get; }
-
///
/// Gets the being pointed by this instance.
///
@@ -51,12 +46,12 @@ public readonly struct Buffer2DRegion
///
/// Gets the width
///
- public int Width => this.Rectangle.Width;
+ public int Width => this.Bounds.Width;
///
/// Gets the height
///
- public int Height => this.Rectangle.Height;
+ public int Height => this.Bounds.Height;
///
/// Gets the number of elements between row starts in .
@@ -66,12 +61,17 @@ public readonly struct Buffer2DRegion
///
/// Gets the size of the area.
///
- internal Size Size => this.Rectangle.Size;
+ public Size Size => this.Bounds.Size;
+
+ ///
+ /// Gets the rectangle specifying the boundaries of the area in .
+ ///
+ public Rectangle Bounds { get; }
///
/// Gets a value indicating whether the area refers to the entire
///
- internal bool IsFullBufferArea => this.Size == this.Buffer.Size();
+ internal bool IsFullBufferArea => this.Size == this.Buffer.Size;
///
/// Gets or sets a value at the given index.
@@ -79,7 +79,7 @@ public readonly struct Buffer2DRegion
/// The position inside a row
/// The row index
/// The reference to the value
- internal ref T this[int x, int y] => ref this.Buffer[x + this.Rectangle.X, y + this.Rectangle.Y];
+ internal ref T this[int x, int y] => ref this.Buffer[x + this.Bounds.X, y + this.Bounds.Y];
///
/// Gets a span to row 'y' inside this area.
@@ -89,9 +89,9 @@ public readonly struct Buffer2DRegion
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span DangerousGetRowSpan(int y)
{
- int yy = this.Rectangle.Y + y;
- int xx = this.Rectangle.X;
- int width = this.Rectangle.Width;
+ int yy = this.Bounds.Y + y;
+ int xx = this.Bounds.X;
+ int width = this.Bounds.Width;
return this.Buffer.DangerousGetRowSpan(yy).Slice(xx, width);
}
@@ -114,16 +114,16 @@ public readonly struct Buffer2DRegion
///
/// Returns a subregion as . (Similar to .)
///
- /// The specifying the boundaries of the subregion
+ /// The specifying the boundaries of the subregion
/// The subregion
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Buffer2DRegion GetSubRegion(Rectangle rectangle)
{
- DebugGuard.MustBeLessThanOrEqualTo(rectangle.Width, this.Rectangle.Width, nameof(rectangle));
- DebugGuard.MustBeLessThanOrEqualTo(rectangle.Height, this.Rectangle.Height, nameof(rectangle));
+ DebugGuard.MustBeLessThanOrEqualTo(rectangle.Width, this.Bounds.Width, nameof(rectangle));
+ DebugGuard.MustBeLessThanOrEqualTo(rectangle.Height, this.Bounds.Height, nameof(rectangle));
- int x = this.Rectangle.X + rectangle.X;
- int y = this.Rectangle.Y + rectangle.Y;
+ int x = this.Bounds.X + rectangle.X;
+ int y = this.Bounds.Y + rectangle.Y;
rectangle = new Rectangle(x, y, rectangle.Width, rectangle.Height);
return new Buffer2DRegion(this.Buffer, rectangle);
}
@@ -135,8 +135,8 @@ public readonly struct Buffer2DRegion
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal ref T GetReferenceToOrigin()
{
- int y = this.Rectangle.Y;
- int x = this.Rectangle.X;
+ int y = this.Bounds.Y;
+ int x = this.Bounds.X;
return ref this.Buffer.DangerousGetRowSpan(y)[x];
}
@@ -152,7 +152,7 @@ public readonly struct Buffer2DRegion
return;
}
- for (int y = 0; y < this.Rectangle.Height; y++)
+ for (int y = 0; y < this.Bounds.Height; y++)
{
Span row = this.DangerousGetRowSpan(y);
row.Clear();
@@ -172,7 +172,7 @@ public readonly struct Buffer2DRegion
return;
}
- for (int y = 0; y < this.Rectangle.Height; y++)
+ for (int y = 0; y < this.Bounds.Height; y++)
{
Span row = this.DangerousGetRowSpan(y);
row.Fill(value);
diff --git a/src/ImageSharp/Memory/Buffer2D{T}.cs b/src/ImageSharp/Memory/Buffer2D{T}.cs
index fe997a4fbf..cfd7664f01 100644
--- a/src/ImageSharp/Memory/Buffer2D{T}.cs
+++ b/src/ImageSharp/Memory/Buffer2D{T}.cs
@@ -39,20 +39,30 @@ public sealed class Buffer2D : IDisposable
Guard.MustBeGreaterThanOrEqualTo(rowStride, width, nameof(rowStride));
this.FastMemoryGroup = memoryGroup;
- this.Width = width;
- this.Height = height;
+ this.Size = new Size(width, height);
this.RowStride = rowStride;
}
///
/// Gets the width.
///
- public int Width { get; private set; }
+ public int Width => this.Size.Width;
///
/// Gets the height.
///
- public int Height { get; private set; }
+ public int Height => this.Size.Height;
+
+ ///
+ /// Gets the size of the buffer.
+ ///
+ public Size Size { get; private set; }
+
+ ///
+ /// Gets the bounds of the buffer.
+ ///
+ /// The
+ public Rectangle Bounds => new(0, 0, this.Width, this.Height);
///
/// Gets the number of elements between row starts in the backing memory.
@@ -402,8 +412,7 @@ public sealed class Buffer2D : IDisposable
source.CopyTo(destination);
}
- (destination.Width, source.Width) = (source.Width, destination.Width);
- (destination.Height, source.Height) = (source.Height, destination.Height);
+ (destination.Size, source.Size) = (source.Size, destination.Size);
(destination.RowStride, source.RowStride) = (source.RowStride, destination.RowStride);
return swapped;
}
diff --git a/src/ImageSharp/Memory/DiscontiguousBuffers/IMemoryGroup{T}.cs b/src/ImageSharp/Memory/DiscontiguousBuffers/IMemoryGroup{T}.cs
index 03f26aab04..7e9719ea75 100644
--- a/src/ImageSharp/Memory/DiscontiguousBuffers/IMemoryGroup{T}.cs
+++ b/src/ImageSharp/Memory/DiscontiguousBuffers/IMemoryGroup{T}.cs
@@ -15,12 +15,12 @@ public interface IMemoryGroup : IReadOnlyList>
/// Gets the number of elements per contiguous sub-buffer preceding the last buffer.
/// The last buffer is allowed to be smaller.
///
- public int BufferLength { get; }
+ int BufferLength { get; }
///
/// Gets the aggregate number of elements in the group.
///
- public long TotalLength { get; }
+ long TotalLength { get; }
///
/// Gets a value indicating whether the group has been invalidated.
@@ -29,7 +29,7 @@ public interface IMemoryGroup : IReadOnlyList>
/// Invalidation usually occurs when an image processor capable to alter the image dimensions replaces
/// the image buffers internally.
///
- public bool IsValid { get; }
+ bool IsValid { get; }
///
/// Returns a value-type implementing an allocation-free enumerator of the memory groups in the current
@@ -39,5 +39,5 @@ public interface IMemoryGroup : IReadOnlyList>
/// implementation, which is still available when casting to one of the underlying interfaces.
///
/// A new instance mapping the current values in use.
- public new MemoryGroupEnumerator GetEnumerator();
+ new MemoryGroupEnumerator GetEnumerator();
}
diff --git a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Consumed.cs b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Consumed.cs
index 75e93ce7f8..950e2a019e 100644
--- a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Consumed.cs
+++ b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Consumed.cs
@@ -31,23 +31,23 @@ internal abstract partial class MemoryGroup
///
[MethodImpl(InliningOptions.ShortMethod)]
- public override MemoryGroupEnumerator GetEnumerator() => new(this);
+ public override MemoryGroupEnumerator GetEnumerator()
+ {
+ return new MemoryGroupEnumerator(this);
+ }
///
IEnumerator> IEnumerable>.GetEnumerator()
-
+ {
/* The runtime sees the Array class as if it implemented the
* type-generic collection interfaces explicitly, so here we
* can just cast the source array to IList> (or to
* an equivalent type), and invoke the generic GetEnumerator
* method directly from that interface reference. This saves
* having to create our own iterator block here. */
- => ((IList>)this.source).GetEnumerator();
-
- public override void Dispose()
- {
- this.View.Invalidate();
- this.ReleaseAllocationTracking();
+ return ((IList>)this.source).GetEnumerator();
}
+
+ public override void Dispose() => this.View.Invalidate();
}
}
diff --git a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Owned.cs b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Owned.cs
index be92272bbe..af896ee0e1 100644
--- a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Owned.cs
+++ b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Owned.cs
@@ -73,8 +73,8 @@ internal abstract partial class MemoryGroup
result[i] = currentBuffer;
}
- ObservedBuffer lastBuffer = ObservedBuffer.Create(pooledBuffers[^1], sizeOfLastBuffer, options);
- result[^1] = lastBuffer;
+ ObservedBuffer lastBuffer = ObservedBuffer.Create(pooledBuffers[pooledBuffers.Length - 1], sizeOfLastBuffer, options);
+ result[result.Length - 1] = lastBuffer;
return result;
}
@@ -155,7 +155,6 @@ internal abstract partial class MemoryGroup
}
}
- this.ReleaseAllocationTracking();
this.memoryOwners = null;
this.IsValid = false;
this.groupLifetimeGuard = null;
diff --git a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs
index aa0e188959..6dd99fcb02 100644
--- a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs
+++ b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs
@@ -22,9 +22,6 @@ internal abstract partial class MemoryGroup : IMemoryGroup, IDisposable
private static readonly int ElementSize = Unsafe.SizeOf();
private MemoryGroupSpanCache memoryGroupSpanCache;
- private MemoryAllocator? trackingAllocator;
- private long trackingLengthInBytes;
- private int trackingReleased;
private MemoryGroup(int bufferLength, long totalLength)
{
@@ -55,45 +52,16 @@ internal abstract partial class MemoryGroup : IMemoryGroup, IDisposable
///
public abstract MemoryGroupEnumerator GetEnumerator();
- ///
- /// Configures allocation tracking by specifying the allocator and the length, in bytes, to be tracked.
- ///
- /// The memory allocator to use for tracking allocations.
- /// The length, in bytes, of the memory region to track. Must be greater than or equal to zero.
- ///
- /// Intended for initialization; callers should avoid changing tracking state concurrently with disposal.
- ///
- internal void SetAllocationTracking(MemoryAllocator allocator, long lengthInBytes)
- {
- this.trackingAllocator = allocator;
- this.trackingLengthInBytes = lengthInBytes;
- }
-
- ///
- /// Releases any resources or tracking information associated with allocation tracking for this instance.
- ///
- ///
- /// This method is intended to be called when allocation tracking is no longer needed. It is safe
- /// to call multiple times; subsequent calls after the first have no effect, even when called concurrently.
- ///
- internal void ReleaseAllocationTracking()
- {
- if (Interlocked.Exchange(ref this.trackingReleased, 1) == 0 && this.trackingAllocator != null)
- {
- this.trackingAllocator.ReleaseAccumulatedBytes(this.trackingLengthInBytes);
- this.trackingAllocator = null;
- }
- }
-
///
IEnumerator> IEnumerable>.GetEnumerator()
-
+ {
/* This method is implemented in each derived class.
* Implementing the method here as non-abstract and throwing,
* then reimplementing it explicitly in each derived class, is
* a workaround for the lack of support for abstract explicit
* interface method implementations in C#. */
- => throw new NotImplementedException($"The type {this.GetType()} needs to override IEnumerable>.GetEnumerator()");
+ throw new NotImplementedException($"The type {this.GetType()} needs to override IEnumerable>.GetEnumerator()");
+ }
///
IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable>)this).GetEnumerator();
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs
index 0a4d386550..a0a8c2173e 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs
@@ -59,7 +59,7 @@ internal sealed class ResizeWorker : IDisposable
{
this.configuration = configuration;
this.source = source;
- this.sourceRectangle = source.Rectangle;
+ this.sourceRectangle = source.Bounds;
this.conversionModifiers = conversionModifiers;
this.horizontalKernelMap = horizontalKernelMap;
this.verticalKernelMap = verticalKernelMap;
diff --git a/tests/ImageSharp.Tests/Formats/Exr/ExrDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Exr/ExrDecoderTests.cs
index b81dba7555..1ee0cc582b 100644
--- a/tests/ImageSharp.Tests/Formats/Exr/ExrDecoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Exr/ExrDecoderTests.cs
@@ -36,8 +36,7 @@ public class ExrDecoderTests
ExrMetadata exrMetaData = image.Metadata.GetExrMetadata();
image.DebugSave(provider);
- // There is a 0,0059% difference to the Reference decoder.
- image.CompareToOriginal(provider, ImageComparer.Tolerant(0.0005f), ReferenceDecoder);
+ image.CompareToOriginal(provider, ImageComparer.Exact, ReferenceDecoder);
Assert.Equal(ExrPixelType.Float, exrMetaData.PixelType);
}
@@ -119,6 +118,29 @@ public class ExrDecoderTests
image.CompareToOriginal(provider, ImageComparer.Exact, ReferenceDecoder);
}
+ [Theory]
+ [WithFile(TestImages.Exr.Pxr24Half, PixelTypes.Rgba32)]
+ [WithFile(TestImages.Exr.Pxr24Float, PixelTypes.Rgba32)]
+ public void ExrDecoder_CanDecode_Pxr24Compressed(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel
+ {
+ using Image image = provider.GetImage(ExrDecoder.Instance);
+ image.DebugSave(provider);
+ image.CompareToOriginal(provider, ImageComparer.Exact, ReferenceDecoder);
+ }
+
+ [Theory]
+ [WithFile(TestImages.Exr.Pxr24Uint, PixelTypes.Rgba32)]
+ public void ExrDecoder_CanDecode_Pxr24Compressed_ExrPixelType_Uint(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel
+ {
+ using Image image = provider.GetImage(ExrDecoder.Instance);
+ image.DebugSave(provider);
+
+ // Compare to Reference here instead using the reference decoder, since uint pixel type is not supported by the Reference decoder.
+ image.CompareToReferenceOutput(provider);
+ }
+
[Theory]
[WithFile(TestImages.Exr.B44, PixelTypes.Rgba32)]
public void ExrDecoder_CanDecode_B44Compressed(TestImageProvider provider)
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs
index 903a6b0762..1610df6eb4 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs
@@ -119,7 +119,7 @@ public class SpectralJpegTests
this.Output.WriteLine($"Component{i}: [total: {total} | average: {average}]");
averageDifference += average;
totalDifference += total;
- Size s = libJpegComponent.SpectralBlocks.Size();
+ Size s = libJpegComponent.SpectralBlocks.Size;
tolerance += s.Width * s.Height;
}
diff --git a/tests/ImageSharp.Tests/Memory/Allocators/SimpleGcMemoryAllocatorTests.cs b/tests/ImageSharp.Tests/Memory/Allocators/SimpleGcMemoryAllocatorTests.cs
index c33e6d1070..0e791c5d97 100644
--- a/tests/ImageSharp.Tests/Memory/Allocators/SimpleGcMemoryAllocatorTests.cs
+++ b/tests/ImageSharp.Tests/Memory/Allocators/SimpleGcMemoryAllocatorTests.cs
@@ -48,50 +48,6 @@ public class SimpleGcMemoryAllocatorTests
}
}
- [Fact]
- public void Allocate_AccumulativeLimit_ReleasesOnOwnerDispose()
- {
- SimpleGcMemoryAllocator allocator = new(new MemoryAllocatorOptions
- {
- AccumulativeAllocationLimitMegabytes = 1
- });
- const int oneMb = 1 << 20;
-
- // Reserve the full limit with a single owner.
- IMemoryOwner b0 = allocator.Allocate(oneMb);
-
- // Additional allocation should exceed the limit while the owner is live.
- Assert.Throws(() => allocator.Allocate(1));
-
- // Disposing the owner releases the reservation.
- b0.Dispose();
-
- // Allocation should succeed after the reservation is released.
- allocator.Allocate(oneMb).Dispose();
- }
-
- [Fact]
- public void AllocateGroup_AccumulativeLimit_ReleasesOnGroupDispose()
- {
- SimpleGcMemoryAllocator allocator = new(new MemoryAllocatorOptions
- {
- AccumulativeAllocationLimitMegabytes = 1
- });
- const int oneMb = 1 << 20;
-
- // Reserve the full limit with a single group.
- MemoryGroup g0 = allocator.AllocateGroup(oneMb, 1024);
-
- // Additional allocation should exceed the limit while the group is live.
- Assert.Throws(() => allocator.AllocateGroup(1, 1024));
-
- // Disposing the group releases the reservation.
- g0.Dispose();
-
- // Allocation should succeed after the reservation is released.
- allocator.AllocateGroup(oneMb, 1024).Dispose();
- }
-
[StructLayout(LayoutKind.Explicit, Size = 512)]
private struct BigStruct
{
diff --git a/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedPoolMemoryAllocatorTests.cs b/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedPoolMemoryAllocatorTests.cs
index 06da3505e1..1d185a0de9 100644
--- a/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedPoolMemoryAllocatorTests.cs
+++ b/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedPoolMemoryAllocatorTests.cs
@@ -16,8 +16,8 @@ public class UniformUnmanagedPoolMemoryAllocatorTests
{
public class BufferTests1 : BufferTestSuite
{
- private static UniformUnmanagedMemoryPoolMemoryAllocator CreateMemoryAllocator() =>
- new(
+ private static MemoryAllocator CreateMemoryAllocator() =>
+ new UniformUnmanagedMemoryPoolMemoryAllocator(
sharedArrayPoolThresholdInBytes: 1024,
poolBufferSizeInBytes: 2048,
maxPoolSizeInBytes: 2048 * 4,
@@ -31,8 +31,8 @@ public class UniformUnmanagedPoolMemoryAllocatorTests
public class BufferTests2 : BufferTestSuite
{
- private static UniformUnmanagedMemoryPoolMemoryAllocator CreateMemoryAllocator() =>
- new(
+ private static MemoryAllocator CreateMemoryAllocator() =>
+ new UniformUnmanagedMemoryPoolMemoryAllocator(
sharedArrayPoolThresholdInBytes: 512,
poolBufferSizeInBytes: 1024,
maxPoolSizeInBytes: 1024 * 4,
@@ -179,8 +179,8 @@ public class UniformUnmanagedPoolMemoryAllocatorTests
g1.Dispose();
// Do some unmanaged allocations to make sure new non-pooled unmanaged allocations will grab different memory:
- IntPtr dummy1 = Marshal.AllocHGlobal(checked((IntPtr)B(8)));
- IntPtr dummy2 = Marshal.AllocHGlobal(checked((IntPtr)B(8)));
+ IntPtr dummy1 = Marshal.AllocHGlobal((IntPtr)B(8));
+ IntPtr dummy2 = Marshal.AllocHGlobal((IntPtr)B(8));
using MemoryGroup g2 = allocator.AllocateGroup(B(8), 1024);
using MemoryGroup g3 = allocator.AllocateGroup(B(8), 1024);
@@ -433,50 +433,6 @@ public class UniformUnmanagedPoolMemoryAllocatorTests
Assert.Throws(() => allocator.AllocateGroup(5 * oneMb, 1024));
}
- [Fact]
- public void Allocate_AccumulativeLimit_ReleasesOnOwnerDispose()
- {
- MemoryAllocator allocator = MemoryAllocator.Create(new MemoryAllocatorOptions
- {
- AccumulativeAllocationLimitMegabytes = 1
- });
- const int oneMb = 1 << 20;
-
- // Reserve the full limit with a single owner.
- IMemoryOwner b0 = allocator.Allocate(oneMb);
-
- // Additional allocation should exceed the limit while the owner is live.
- Assert.Throws(() => allocator.Allocate(1));
-
- // Disposing the owner releases the reservation.
- b0.Dispose();
-
- // Allocation should succeed after the reservation is released.
- allocator.Allocate(oneMb).Dispose();
- }
-
- [Fact]
- public void AllocateGroup_AccumulativeLimit_ReleasesOnGroupDispose()
- {
- MemoryAllocator allocator = MemoryAllocator.Create(new MemoryAllocatorOptions
- {
- AccumulativeAllocationLimitMegabytes = 1
- });
- const int oneMb = 1 << 20;
-
- // Reserve the full limit with a single group.
- MemoryGroup g0 = allocator.AllocateGroup(oneMb, 1024);
-
- // Additional allocation should exceed the limit while the group is live.
- Assert.Throws(() => allocator.AllocateGroup(1, 1024));
-
- // Disposing the group releases the reservation.
- g0.Dispose();
-
- // Allocation should succeed after the reservation is released.
- allocator.AllocateGroup(oneMb, 1024).Dispose();
- }
-
[ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))]
public void MemoryAllocator_Create_SetHighLimit()
{
diff --git a/tests/ImageSharp.Tests/Memory/Buffer2DTests.SwapOrCopyContent.cs b/tests/ImageSharp.Tests/Memory/Buffer2DTests.SwapOrCopyContent.cs
index caf935cc47..6e9069a45e 100644
--- a/tests/ImageSharp.Tests/Memory/Buffer2DTests.SwapOrCopyContent.cs
+++ b/tests/ImageSharp.Tests/Memory/Buffer2DTests.SwapOrCopyContent.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors.
+// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Memory;
@@ -15,26 +15,24 @@ public partial class Buffer2DTests
[Fact]
public void SwapOrCopyContent_WhenBothAllocated()
{
- using (Buffer2D a = this.memoryAllocator.Allocate2D(10, 5, AllocationOptions.Clean))
- using (Buffer2D b = this.memoryAllocator.Allocate2D(3, 7, AllocationOptions.Clean))
- {
- a[1, 3] = 666;
- b[1, 3] = 444;
+ using Buffer2D a = this.memoryAllocator.Allocate2D(10, 5, AllocationOptions.Clean);
+ using Buffer2D b = this.memoryAllocator.Allocate2D(3, 7, AllocationOptions.Clean);
+ a[1, 3] = 666;
+ b[1, 3] = 444;
- Memory aa = a.FastMemoryGroup.Single();
- Memory bb = b.FastMemoryGroup.Single();
+ Memory aa = a.FastMemoryGroup.Single();
+ Memory bb = b.FastMemoryGroup.Single();
- Buffer2D.SwapOrCopyContent(a, b);
+ Buffer2D.SwapOrCopyContent(a, b);
- Assert.Equal(bb, a.FastMemoryGroup.Single());
- Assert.Equal(aa, b.FastMemoryGroup.Single());
+ Assert.Equal(bb, a.FastMemoryGroup.Single());
+ Assert.Equal(aa, b.FastMemoryGroup.Single());
- Assert.Equal(new Size(3, 7), a.Size());
- Assert.Equal(new Size(10, 5), b.Size());
+ Assert.Equal(new Size(3, 7), a.Size);
+ Assert.Equal(new Size(10, 5), b.Size);
- Assert.Equal(666, b[1, 3]);
- Assert.Equal(444, a[1, 3]);
- }
+ Assert.Equal(666, b[1, 3]);
+ Assert.Equal(444, a[1, 3]);
}
[Fact]
@@ -171,7 +169,7 @@ public partial class Buffer2DTests
bool swap = Buffer2D.SwapOrCopyContent(dest, source);
Assert.False(swap);
- Assert.Equal(new Size(3, 2), dest.Size());
+ Assert.Equal(new Size(3, 2), dest.Size);
Assert.Equal(6, dest[2, 1]);
}
@@ -191,7 +189,7 @@ public partial class Buffer2DTests
bool swap = Buffer2D.SwapOrCopyContent(dest, source);
Assert.False(swap);
- Assert.Equal(new Size(1, 5), dest.Size());
+ Assert.Equal(new Size(1, 5), dest.Size);
Assert.Equal(1, dest[0, 0]);
Assert.Equal(2, dest[0, 1]);
Assert.Equal(3, dest[0, 2]);
@@ -215,7 +213,7 @@ public partial class Buffer2DTests
bool swap = Buffer2D.SwapOrCopyContent(dest, source);
Assert.False(swap);
- Assert.Equal(new Size(2, 2), dest.Size());
+ Assert.Equal(new Size(2, 2), dest.Size);
Assert.Equal(1, dest[0, 0]);
Assert.Equal(2, dest[1, 0]);
Assert.Equal(3, dest[0, 1]);
diff --git a/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs b/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs
index be7edbacca..a44d6ce1a1 100644
--- a/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs
+++ b/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs
@@ -17,7 +17,7 @@ public class BufferAreaTests
Buffer2DRegion area = new(buffer, rectangle);
Assert.Equal(buffer, area.Buffer);
- Assert.Equal(rectangle, area.Rectangle);
+ Assert.Equal(rectangle, area.Bounds);
}
private Buffer2D CreateTestBuffer(int w, int h)
@@ -90,7 +90,7 @@ public class BufferAreaTests
Rectangle expectedRect = new(10, 12, 5, 5);
Assert.Equal(buffer, area1.Buffer);
- Assert.Equal(expectedRect, area1.Rectangle);
+ Assert.Equal(expectedRect, area1.Bounds);
int value00 = (12 * 100) + 10;
Assert.Equal(value00, area1[0, 0]);
@@ -147,9 +147,9 @@ public class BufferAreaTests
Assert.Equal(0, buffer[5, 5]);
Assert.Equal(0, buffer[14, 14]);
- for (int y = region.Rectangle.Y; y < region.Rectangle.Bottom; y++)
+ for (int y = region.Bounds.Y; y < region.Bounds.Bottom; y++)
{
- Span span = buffer.DangerousGetRowSpan(y).Slice(region.Rectangle.X, region.Width);
+ Span span = buffer.DangerousGetRowSpan(y).Slice(region.Bounds.X, region.Width);
Assert.True(span.SequenceEqual(new int[region.Width]));
}
}
diff --git a/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.Allocate.cs b/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.Allocate.cs
index cca2230e6f..678a089a85 100644
--- a/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.Allocate.cs
+++ b/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.Allocate.cs
@@ -98,15 +98,7 @@ public partial class MemoryGroupTests
[InlineData(AllocationOptions.Clean)]
public unsafe void Allocate_FromPool_AllocationOptionsAreApplied(AllocationOptions options)
{
- // Disable trimming to avoid buffers being freed between Return and TryAllocate by the
- // trim timer or the Gen2 GC callback.
- UniformUnmanagedMemoryPool pool = new(
- 10,
- 5,
- new UniformUnmanagedMemoryPool.TrimSettings
- {
- Rate = 0
- });
+ UniformUnmanagedMemoryPool pool = new(10, 5);
UnmanagedMemoryHandle[] buffers = pool.Rent(5);
foreach (UnmanagedMemoryHandle b in buffers)
{
diff --git a/tests/ImageSharp.Tests/Processing/IntegralImageTests.cs b/tests/ImageSharp.Tests/Processing/IntegralImageTests.cs
index 81ba1693d2..a0ea99a9d0 100644
--- a/tests/ImageSharp.Tests/Processing/IntegralImageTests.cs
+++ b/tests/ImageSharp.Tests/Processing/IntegralImageTests.cs
@@ -76,7 +76,7 @@ public class IntegralImageTests : BaseImageOperationsExtensionTest
Buffer2D integralBuffer,
Func getPixel)
where TPixel : unmanaged, IPixel
- => VerifySumValues(provider, integralBuffer, integralBuffer.Bounds(), getPixel);
+ => VerifySumValues(provider, integralBuffer, integralBuffer.Bounds, getPixel);
private static void VerifySumValues(
TestImageProvider provider,
diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs
index 1a8cf948e8..2624d1cdad 100644
--- a/tests/ImageSharp.Tests/TestImages.cs
+++ b/tests/ImageSharp.Tests/TestImages.cs
@@ -1388,13 +1388,16 @@ public static class TestImages
public const string Benchmark = "Exr/Calliphora_benchmark.exr";
public const string Uncompressed = "Exr/Calliphora_uncompressed.exr";
public const string UncompressedRgba = "Exr/Calliphora_uncompressed_rgba.exr";
- public const string UncompressedFloatRgb = "Exr/rgb_float32_uncompressed.exr";
+ public const string UncompressedFloatRgb = "Exr/Calliphora_float_uncompressed.exr";
public const string UncompressedUintRgb = "Exr/Calliphora_uint32_uncompressed.exr";
public const string UintRgba = "Exr/rgba_uint_uncompressed.exr";
public const string Zip = "Exr/Calliphora_zip.exr";
public const string Zips = "Exr/Calliphora_zips.exr";
public const string Rle = "Exr/Calliphora_rle.exr";
public const string B44 = "Exr/Calliphora_b44.exr";
+ public const string Pxr24Half = "Exr/Calliphora_half_pxr24.exr";
+ public const string Pxr24Float = "Exr/Calliphora_float_pxr24.exr";
+ public const string Pxr24Uint = "Exr/Calliphora_uint_pxr24.exr";
public const string Rgb = "Exr/Calliphora_rgb.exr";
public const string Gray = "Exr/Calliphora_gray.exr";
}
diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs
index 4a34529dd3..be2356d657 100644
--- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs
+++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs
@@ -505,7 +505,7 @@ public static class TestImageExtensions
public static void CompareBuffers(Buffer2D expected, Buffer2D actual)
where T : struct, IEquatable
{
- Assert.True(expected.Size() == actual.Size(), "Buffer sizes are not equal!");
+ Assert.True(expected.Size == actual.Size, "Buffer sizes are not equal!");
for (int y = 0; y < expected.Height; y++)
{
diff --git a/tests/Images/External/ReferenceOutput/ExrDecoderTests/ExrDecoder_CanDecode_Pxr24Compressed_ExrPixelType_Uint_Rgba32_Calliphora_uint_pxr24.png b/tests/Images/External/ReferenceOutput/ExrDecoderTests/ExrDecoder_CanDecode_Pxr24Compressed_ExrPixelType_Uint_Rgba32_Calliphora_uint_pxr24.png
new file mode 100644
index 0000000000..8f31f0d8d0
--- /dev/null
+++ b/tests/Images/External/ReferenceOutput/ExrDecoderTests/ExrDecoder_CanDecode_Pxr24Compressed_ExrPixelType_Uint_Rgba32_Calliphora_uint_pxr24.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f66bf45b5d80e412314341567b8cf240d56b5872f01ce9f3b0d6034d85e8e942
+size 163327
diff --git a/tests/Images/Input/Exr/Calliphora_float_pxr24.exr b/tests/Images/Input/Exr/Calliphora_float_pxr24.exr
new file mode 100644
index 0000000000..3c5d61e535
--- /dev/null
+++ b/tests/Images/Input/Exr/Calliphora_float_pxr24.exr
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ca6c29eb36395b923298d2bf4ba1f73fd7f081f7b1af2e72b94b92ad2acb638b
+size 226997
diff --git a/tests/Images/Input/Exr/Calliphora_float_uncompressed.exr b/tests/Images/Input/Exr/Calliphora_float_uncompressed.exr
new file mode 100644
index 0000000000..04a5c035dd
--- /dev/null
+++ b/tests/Images/Input/Exr/Calliphora_float_uncompressed.exr
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:926996c647c1c54921db0a623e58e5edeb92b447cb4eceb53b8c0e86fd24f11e
+size 720281
diff --git a/tests/Images/Input/Exr/Calliphora_half_pxr24.exr b/tests/Images/Input/Exr/Calliphora_half_pxr24.exr
new file mode 100644
index 0000000000..17a90699b0
--- /dev/null
+++ b/tests/Images/Input/Exr/Calliphora_half_pxr24.exr
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:927778a73832b5ad68473e46df6be52076d98294271f2cd0ead66691db41b7bf
+size 195063
diff --git a/tests/Images/Input/Exr/Calliphora_uint_pxr24.exr b/tests/Images/Input/Exr/Calliphora_uint_pxr24.exr
new file mode 100644
index 0000000000..fd858abb04
--- /dev/null
+++ b/tests/Images/Input/Exr/Calliphora_uint_pxr24.exr
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:fc72685224ba818ac77d914f5e4c91162503f88f775e16b5c745baef6bc7b790
+size 187914
diff --git a/tests/Images/Input/Exr/rgb_float32_uncompressed.exr b/tests/Images/Input/Exr/rgb_float32_uncompressed.exr
deleted file mode 100644
index 489758bb04..0000000000
--- a/tests/Images/Input/Exr/rgb_float32_uncompressed.exr
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:5ab8b71824ca384fc2cde1f74a6f34c38169fa328ccc67f17d08333e9de42f55
-size 243558