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/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/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/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/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