diff --git a/src/ImageSharp/Formats/Exr/Compression/Decompressors/B44ExrCompression.cs b/src/ImageSharp/Formats/Exr/Compression/Decompressors/B44ExrCompression.cs
index 2b5b740569..e58eca4e98 100644
--- a/src/ImageSharp/Formats/Exr/Compression/Decompressors/B44ExrCompression.cs
+++ b/src/ImageSharp/Formats/Exr/Compression/Decompressors/B44ExrCompression.cs
@@ -39,7 +39,7 @@ internal class B44ExrCompression : ExrBaseDecompressor
}
///
- public override void Decompress(BufferedReadStream stream, uint compressedBytes, Span buffer)
+ public override void Decompress(BufferedReadStream stream, uint compressedBytes, uint uncompressedBytes, Span buffer)
{
Span outputBuffer = MemoryMarshal.Cast(buffer);
Span decompressed = this.tmpBuffer.GetSpan();
diff --git a/src/ImageSharp/Formats/Exr/Compression/Decompressors/NoneExrCompression.cs b/src/ImageSharp/Formats/Exr/Compression/Decompressors/NoneExrCompression.cs
index 19edb31afe..b1a2094dab 100644
--- a/src/ImageSharp/Formats/Exr/Compression/Decompressors/NoneExrCompression.cs
+++ b/src/ImageSharp/Formats/Exr/Compression/Decompressors/NoneExrCompression.cs
@@ -25,10 +25,10 @@ internal class NoneExrCompression : ExrBaseDecompressor
}
///
- public override void Decompress(BufferedReadStream stream, uint compressedBytes, Span buffer)
+ public override void Decompress(BufferedReadStream stream, uint compressedBytes, uint uncompressedBytes, Span buffer)
{
- int bytesRead = stream.Read(buffer, 0, Math.Min(buffer.Length, (int)this.BytesPerBlock));
- if (bytesRead != (int)this.BytesPerBlock)
+ int bytesRead = stream.Read(buffer[..(int)uncompressedBytes]);
+ if (bytesRead != uncompressedBytes)
{
ExrThrowHelper.ThrowInvalidImageContentException("Could not read enough pixel data from the stream!");
}
diff --git a/src/ImageSharp/Formats/Exr/Compression/Decompressors/Pxr24Compression.cs b/src/ImageSharp/Formats/Exr/Compression/Decompressors/Pxr24Compression.cs
index f45b660e7d..d621c893e5 100644
--- a/src/ImageSharp/Formats/Exr/Compression/Decompressors/Pxr24Compression.cs
+++ b/src/ImageSharp/Formats/Exr/Compression/Decompressors/Pxr24Compression.cs
@@ -39,19 +39,20 @@ internal class Pxr24Compression : ExrBaseDecompressor
}
///
- public override void Decompress(BufferedReadStream stream, uint compressedBytes, Span buffer)
+ public override void Decompress(BufferedReadStream stream, uint compressedBytes, uint uncompressedBytes, Span buffer)
{
- Span uncompressed = this.tmpBuffer.GetSpan();
+ uint rowCount = uncompressedBytes / this.BytesPerRow;
+ uint packedBytes = this.pixelType == ExrPixelType.Float ? (uncompressedBytes / 4) * 3 : uncompressedBytes;
+ Span uncompressed = this.tmpBuffer.GetSpan()[..(int)packedBytes];
Span outputBufferHalf = MemoryMarshal.Cast(buffer);
Span outputBufferFloat = MemoryMarshal.Cast(buffer);
Span outputBufferUint = MemoryMarshal.Cast(buffer);
- uint uncompressedBytes = this.BytesPerBlock;
- UndoZipCompression(stream, compressedBytes, uncompressed, uncompressedBytes);
+ UndoZipCompression(stream, compressedBytes, uncompressed, packedBytes);
int lastIn = 0;
int outputOffset = 0;
- for (int y = 0; y < this.RowsPerBlock; y++)
+ for (uint y = 0; y < rowCount; y++)
{
for (int c = 0; c < this.channelCount; c++)
{
diff --git a/src/ImageSharp/Formats/Exr/Compression/Decompressors/RunLengthExrCompression.cs b/src/ImageSharp/Formats/Exr/Compression/Decompressors/RunLengthExrCompression.cs
index f548a81810..5ffe83af3c 100644
--- a/src/ImageSharp/Formats/Exr/Compression/Decompressors/RunLengthExrCompression.cs
+++ b/src/ImageSharp/Formats/Exr/Compression/Decompressors/RunLengthExrCompression.cs
@@ -26,7 +26,7 @@ internal class RunLengthExrCompression : ExrBaseDecompressor
: base(allocator, bytesPerBlock, bytesPerRow, rowsPerBlock, width) => this.tmpBuffer = allocator.Allocate((int)bytesPerBlock);
///
- public override void Decompress(BufferedReadStream stream, uint compressedBytes, Span buffer)
+ public override void Decompress(BufferedReadStream stream, uint compressedBytes, uint uncompressedBytes, Span buffer)
{
Span uncompressed = this.tmpBuffer.GetSpan();
int maxLength = (int)this.BytesPerBlock;
diff --git a/src/ImageSharp/Formats/Exr/Compression/Decompressors/ZipExrCompression.cs b/src/ImageSharp/Formats/Exr/Compression/Decompressors/ZipExrCompression.cs
index 8bab76f402..41fda54366 100644
--- a/src/ImageSharp/Formats/Exr/Compression/Decompressors/ZipExrCompression.cs
+++ b/src/ImageSharp/Formats/Exr/Compression/Decompressors/ZipExrCompression.cs
@@ -26,15 +26,14 @@ internal class ZipExrCompression : ExrBaseDecompressor
: base(allocator, bytesPerBlock, bytesPerRow, rowsPerBlock, width) => this.tmpBuffer = allocator.Allocate((int)bytesPerBlock);
///
- public override void Decompress(BufferedReadStream stream, uint compressedBytes, Span buffer)
+ public override void Decompress(BufferedReadStream stream, uint compressedBytes, uint uncompressedBytes, Span buffer)
{
- Span uncompressed = this.tmpBuffer.GetSpan();
+ Span uncompressed = this.tmpBuffer.GetSpan()[..(int)uncompressedBytes];
- uint uncompressedBytes = (uint)buffer.Length;
int totalRead = UndoZipCompression(stream, compressedBytes, uncompressed, uncompressedBytes);
- Reconstruct(uncompressed, (uint)totalRead);
- Interleave(uncompressed, (uint)totalRead, buffer);
+ Reconstruct(uncompressed, uncompressedBytes);
+ Interleave(uncompressed, uncompressedBytes, buffer);
}
///
diff --git a/src/ImageSharp/Formats/Exr/Compression/ExrBaseDecompressor.cs b/src/ImageSharp/Formats/Exr/Compression/ExrBaseDecompressor.cs
index 2f1320e587..52a8164c7a 100644
--- a/src/ImageSharp/Formats/Exr/Compression/ExrBaseDecompressor.cs
+++ b/src/ImageSharp/Formats/Exr/Compression/ExrBaseDecompressor.cs
@@ -31,8 +31,9 @@ internal abstract class ExrBaseDecompressor : ExrBaseCompression
///
/// The buffered stream to decompress.
/// The compressed bytes.
+ /// The expected byte count for the current block.
/// The buffer to write the decompressed data to.
- public abstract void Decompress(BufferedReadStream stream, uint compressedBytes, Span buffer);
+ public abstract void Decompress(BufferedReadStream stream, uint compressedBytes, uint uncompressedBytes, Span buffer);
///
/// Decompresses zip compressed data.
@@ -67,9 +68,9 @@ internal abstract class ExrBaseDecompressor : ExrBaseCompression
totalRead += bytesRead;
}
- if (totalRead != uncompressedBytes)
+ if (totalRead != uncompressedBytes || dataStream.ReadByte() != -1)
{
- ExrThrowHelper.ThrowInvalidImageContentException("Could not read enough data for zip compressed EXR image data!");
+ ExrThrowHelper.ThrowInvalidImageContentException("ZIP compressed EXR block has an invalid decompressed length.");
}
return totalRead;
diff --git a/src/ImageSharp/Formats/Exr/ExrDecoderCore.cs b/src/ImageSharp/Formats/Exr/ExrDecoderCore.cs
index 680e8d333d..9dd333d92f 100644
--- a/src/ImageSharp/Formats/Exr/ExrDecoderCore.cs
+++ b/src/ImageSharp/Formats/Exr/ExrDecoderCore.cs
@@ -193,9 +193,16 @@ internal sealed class ExrDecoderCore : ImageDecoderCore
this.ValidateChunkOffset(rowOffset, stream);
stream.Position = (long)rowOffset;
uint rowStartIndex = this.ReadUnsignedInteger(stream);
+ if (rowStartIndex >= height)
+ {
+ ExrThrowHelper.ThrowInvalidImageContentException("EXR chunk row index is outside the data window.");
+ }
uint compressedBytesCount = this.ReadUnsignedInteger(stream);
- decompressor.Decompress(stream, compressedBytesCount, decompressedPixelData);
+ uint rowsInBlock = Math.Min(rowsPerBlock, (uint)height - rowStartIndex);
+ uint uncompressedBytesCount = (uint)(bytesPerRow * rowsInBlock);
+
+ decompressor.Decompress(stream, compressedBytesCount, uncompressedBytesCount, decompressedPixelData);
int offset = 0;
for (uint rowIndex = rowStartIndex; rowIndex < rowStartIndex + rowsPerBlock && rowIndex < height; rowIndex++)
@@ -274,9 +281,16 @@ internal sealed class ExrDecoderCore : ImageDecoderCore
this.ValidateChunkOffset(rowOffset, stream);
stream.Position = (long)rowOffset;
uint rowStartIndex = this.ReadUnsignedInteger(stream);
+ if (rowStartIndex >= height)
+ {
+ ExrThrowHelper.ThrowInvalidImageContentException("EXR chunk row index is outside the data window.");
+ }
uint compressedBytesCount = this.ReadUnsignedInteger(stream);
- decompressor.Decompress(stream, compressedBytesCount, decompressedPixelData);
+ uint rowsInBlock = Math.Min(rowsPerBlock, (uint)height - rowStartIndex);
+ uint uncompressedBytesCount = (uint)(bytesPerRow * rowsInBlock);
+
+ decompressor.Decompress(stream, compressedBytesCount, uncompressedBytesCount, decompressedPixelData);
int offset = 0;
for (uint rowIndex = rowStartIndex; rowIndex < rowStartIndex + rowsPerBlock && rowIndex < height; rowIndex++)
diff --git a/tests/ImageSharp.Tests/Formats/Exr/ExrZipDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Exr/ExrZipDecoderTests.cs
index afce3257fc..23d9ccedd4 100644
--- a/tests/ImageSharp.Tests/Formats/Exr/ExrZipDecoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Exr/ExrZipDecoderTests.cs
@@ -33,7 +33,7 @@ public class ExrZipDecoderTests
private static byte[] BuildExr(byte[] compressed)
{
- const int width = 64;
+ const int width = 256;
const int height = 1;
using MemoryStream output = new();