Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ internal class NoneExrCompressor : ExrBaseCompressor
/// <param name="allocator">The memory allocator.</param>
/// <param name="bytesPerBlock">Bytes per row block.</param>
/// <param name="bytesPerRow">Bytes per pixel row.</param>
public NoneExrCompressor(Stream output, MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow)
: base(output, allocator, bytesPerBlock, bytesPerRow)
/// <param name="rowsPerBlock">The pixel rows per block.</param>
/// <param name="width">The witdh of one row in pixels.</param>
public NoneExrCompressor(Stream output, MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow, uint rowsPerBlock, int width)
: base(output, allocator, bytesPerBlock, bytesPerRow, rowsPerBlock, width)
{
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@ internal class ZipExrCompressor : ExrBaseCompressor
/// <param name="allocator">The memory allocator.</param>
/// <param name="bytesPerBlock">The bytes per block.</param>
/// <param name="bytesPerRow">The bytes per row.</param>
/// <param name="rowsPerBlock">The pixel rows per block.</param>
/// <param name="width">The witdh of one row in pixels.</param>
/// <param name="compressionLevel">The compression level for deflate compression.</param>
public ZipExrCompressor(Stream output, MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow, DeflateCompressionLevel compressionLevel)
: base(output, allocator, bytesPerBlock, bytesPerRow)
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<byte>((int)bytesPerBlock);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@ namespace SixLabors.ImageSharp.Formats.Exr.Compression.Decompressors;
/// </summary>
internal class B44ExrCompression : ExrBaseDecompressor
{
private readonly int width;

private readonly uint rowsPerBlock;

private readonly int channelCount;

private readonly byte[] scratch = new byte[14];
Expand All @@ -31,14 +27,12 @@ internal class B44ExrCompression : ExrBaseDecompressor
/// <param name="allocator">The memory allocator.</param>
/// <param name="bytesPerBlock">The bytes per pixel row block.</param>
/// <param name="bytesPerRow">The bytes per row.</param>
/// <param name="rowsPerBlock">The rows per block.</param>
/// <param name="rowsPerBlock">The pixel rows per block.</param>
/// <param name="width">The width of a pixel row in pixels.</param>
/// <param name="channelCount">The number of channels of the image.</param>
public B44ExrCompression(MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow, uint rowsPerBlock, int width, int channelCount)
: base(allocator, bytesPerBlock, bytesPerRow)
: base(allocator, bytesPerBlock, bytesPerRow, rowsPerBlock, width)
{
this.width = width;
this.rowsPerBlock = rowsPerBlock;
this.channelCount = channelCount;
this.tmpBuffer = allocator.Allocate<ushort>((int)(width * rowsPerBlock * channelCount));
}
Expand All @@ -52,26 +46,27 @@ public override void Decompress(BufferedReadStream stream, uint compressedBytes,
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<ushort> row0 = decompressed.Slice(outputOffset, this.width);
outputOffset += this.width;
Span<ushort> row1 = decompressed.Slice(outputOffset, this.width);
outputOffset += this.width;
Span<ushort> row2 = decompressed.Slice(outputOffset, this.width);
outputOffset += this.width;
Span<ushort> row3 = decompressed.Slice(outputOffset, this.width);
outputOffset += this.width;
Span<ushort> row0 = decompressed.Slice(outputOffset, this.Width);
outputOffset += this.Width;
Span<ushort> row1 = decompressed.Slice(outputOffset, this.Width);
outputOffset += this.Width;
Span<ushort> row2 = decompressed.Slice(outputOffset, this.Width);
outputOffset += this.Width;
Span<ushort> 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)
{
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);
Expand All @@ -89,8 +84,8 @@ public override void Decompress(BufferedReadStream stream, uint compressedBytes,
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..]);
Expand All @@ -100,12 +95,12 @@ public override void Decompress(BufferedReadStream stream, uint compressedBytes,
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..]);
}
Expand All @@ -124,16 +119,16 @@ public override void Decompress(BufferedReadStream stream, uint compressedBytes,
// 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;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ internal class NoneExrCompression : ExrBaseDecompressor
/// <param name="allocator">The memory allocator.</param>
/// <param name="bytesPerBlock">The bytes per pixel row block.</param>
/// <param name="bytesPerRow">The bytes per pixel row.</param>
public NoneExrCompression(MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow)
: base(allocator, bytesPerBlock, bytesPerRow)
/// <param name="rowsPerBlock">The pixel rows per block.</param>
/// <param name="width">The number of pixels per row.</param>
public NoneExrCompression(MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow, uint rowsPerBlock, int width)
: base(allocator, bytesPerBlock, bytesPerRow, rowsPerBlock, width)
{
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// Implementation of PXR24 decompressor for EXR image data.
/// </summary>
internal class Pxr24Compression : ExrBaseDecompressor
{
private readonly IMemoryOwner<byte> tmpBuffer;

private readonly int channelCount;

private readonly ExrPixelType pixelType;

/// <summary>
/// Initializes a new instance of the <see cref="Pxr24Compression" /> class.
/// </summary>
/// <param name="allocator">The memory allocator.</param>
/// <param name="bytesPerBlock">The bytes per pixel row block.</param>
/// <param name="bytesPerRow">The bytes per pixel row.</param>
/// <param name="rowsPerBlock">The pixel rows per block.</param>
/// <param name="width">The witdh of one row in pixels.</param>
/// <param name="channelCount">The number of channels for a pixel.</param>
/// <param name="pixelType">The pixel type.</param>
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<byte>((int)bytesPerBlock);
this.channelCount = channelCount;
this.pixelType = pixelType;
}

/// <inheritdoc/>
public override void Decompress(BufferedReadStream stream, uint compressedBytes, Span<byte> buffer)
{
Span<byte> uncompressed = this.tmpBuffer.GetSpan();
Span<ushort> outputBufferHalf = MemoryMarshal.Cast<byte, ushort>(buffer);
Span<uint> outputBufferFloat = MemoryMarshal.Cast<byte, uint>(buffer);
Span<uint> outputBufferUint = MemoryMarshal.Cast<byte, uint>(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;
}
}
}
}
}

/// <inheritdoc/>
protected override void Dispose(bool disposing) => this.tmpBuffer.Dispose();
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,16 @@ internal class RunLengthExrCompression : ExrBaseDecompressor
{
private readonly IMemoryOwner<byte> tmpBuffer;

private readonly ushort[] s = new ushort[16];

/// <summary>
/// Initializes a new instance of the <see cref="RunLengthExrCompression" /> class.
/// </summary>
/// <param name="allocator">The memory allocator.</param>
/// <param name="bytesPerBlock">The bytes per pixel row block.</param>
/// <param name="bytesPerRow">The bytes per row.</param>
public RunLengthExrCompression(MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow)
: base(allocator, bytesPerBlock, bytesPerRow) => this.tmpBuffer = allocator.Allocate<byte>((int)bytesPerBlock);
/// <param name="rowsPerBlock">The pixel rows per block.</param>
/// <param name="width">The witdh of one row in pixels.</param>
public RunLengthExrCompression(MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow, uint rowsPerBlock, int width)
: base(allocator, bytesPerBlock, bytesPerRow, rowsPerBlock, width) => this.tmpBuffer = allocator.Allocate<byte>((int)bytesPerBlock);

/// <inheritdoc/>
public override void Decompress(BufferedReadStream stream, uint compressedBytes, Span<byte> buffer)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -22,41 +20,18 @@ internal class ZipExrCompression : ExrBaseDecompressor
/// <param name="allocator">The memory allocator.</param>
/// <param name="bytesPerBlock">The bytes per pixel row block.</param>
/// <param name="bytesPerRow">The bytes per pixel row.</param>
public ZipExrCompression(MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow)
: base(allocator, bytesPerBlock, bytesPerRow) => this.tmpBuffer = allocator.Allocate<byte>((int)bytesPerBlock);
/// <param name="rowsPerBlock">The pixel rows per block.</param>
/// <param name="width">The witdh of one row in pixels.</param>
public ZipExrCompression(MemoryAllocator allocator, uint bytesPerBlock, uint bytesPerRow, uint rowsPerBlock, int width)
: base(allocator, bytesPerBlock, bytesPerRow, rowsPerBlock, width) => this.tmpBuffer = allocator.Allocate<byte>((int)bytesPerBlock);

/// <inheritdoc/>
public override void Decompress(BufferedReadStream stream, uint compressedBytes, Span<byte> buffer)
{
Span<byte> 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);
Expand Down
Loading
Loading