Browse Source

Add support for writing exr with zip compression with 16 rows per block

pull/3096/head
Brian Popow 2 months ago
parent
commit
f9df91108a
  1. 6
      src/ImageSharp/Formats/Exr/Compression/Compressors/ZipExrCompressor.cs
  2. 4
      src/ImageSharp/Formats/Exr/ExrDecoderCore.cs
  3. 78
      src/ImageSharp/Formats/Exr/ExrEncoderCore.cs

6
src/ImageSharp/Formats/Exr/Compression/Compressors/ZipExrCompressor.cs

@ -29,8 +29,8 @@ internal class ZipExrCompressor : ExrBaseCompressor
public override uint CompressRowBlock(Span<byte> rows, int rowCount)
{
// Re-oder pixel values.
int n = rows.Length;
Span<byte> reordered = this.buffer.GetSpan();
Span<byte> reordered = this.buffer.GetSpan()[..(int)(rowCount * this.BytesPerRow)];
int n = reordered.Length;
int t1 = 0;
int t2 = (n + 1) >> 1;
for (int i = 0; i < n; i++)
@ -42,7 +42,7 @@ internal class ZipExrCompressor : ExrBaseCompressor
// Predictor.
Span<byte> predicted = reordered;
byte p = predicted[0];
for (int i = 1; i < rows.Length; i++)
for (int i = 1; i < predicted.Length; i++)
{
int d = (predicted[i] - p + 128 + 256) & 255;
p = predicted[i];

4
src/ImageSharp/Formats/Exr/ExrDecoderCore.cs

@ -142,7 +142,7 @@ internal sealed class ExrDecoderCore : ImageDecoderCore
using IMemoryOwner<float> rowBuffer = this.memoryAllocator.Allocate<float>(width * 4);
using IMemoryOwner<byte> decompressedPixelDataBuffer = this.memoryAllocator.Allocate<byte>((int)bytesPerBlock);
Span<byte> decompressedPixelData = decompressedPixelDataBuffer.GetSpan();
Span<float> redPixelData = rowBuffer.GetSpan().Slice(0, width);
Span<float> redPixelData = rowBuffer.GetSpan()[..width];
Span<float> greenPixelData = rowBuffer.GetSpan().Slice(width, width);
Span<float> bluePixelData = rowBuffer.GetSpan().Slice(width * 2, width);
Span<float> alphaPixelData = rowBuffer.GetSpan().Slice(width * 3, width);
@ -195,7 +195,7 @@ internal sealed class ExrDecoderCore : ImageDecoderCore
using IMemoryOwner<uint> rowBuffer = this.memoryAllocator.Allocate<uint>(width * 4);
using IMemoryOwner<byte> decompressedPixelDataBuffer = this.memoryAllocator.Allocate<byte>((int)bytesPerBlock);
Span<byte> decompressedPixelData = decompressedPixelDataBuffer.GetSpan();
Span<uint> redPixelData = rowBuffer.GetSpan().Slice(0, width);
Span<uint> redPixelData = rowBuffer.GetSpan()[..width];
Span<uint> greenPixelData = rowBuffer.GetSpan().Slice(width, width);
Span<uint> bluePixelData = rowBuffer.GetSpan().Slice(width * 2, width);
Span<uint> alphaPixelData = rowBuffer.GetSpan().Slice(width * 3, width);

78
src/ImageSharp/Formats/Exr/ExrEncoderCore.cs

@ -172,41 +172,47 @@ internal sealed class ExrEncoderCore
using ExrBaseCompressor compressor = ExrCompressorFactory.Create(compression, this.memoryAllocator, stream, bytesPerBlock, bytesPerRow);
ulong[] rowOffsets = new ulong[height];
for (int y = 0; y < height; y++)
for (uint y = 0; y < height; y += rowsPerBlock)
{
rowOffsets[y] = (ulong)stream.Position;
// Write row index.
BinaryPrimitives.WriteUInt32LittleEndian(this.buffer, (uint)y);
BinaryPrimitives.WriteUInt32LittleEndian(this.buffer, y);
stream.Write(this.buffer.AsSpan(0, 4));
// At this point, it is not yet known how much bytes the compressed data will take up, keep stream position.
long pixelDataSizePos = stream.Position;
Span<TPixel> pixelRowSpan = pixels.DangerousGetRowSpan(y);
stream.Position = pixelDataSizePos + 4;
for (int x = 0; x < width; x++)
uint rowsInBlockCount = 0;
for (uint rowIndex = y; rowIndex < y + rowsPerBlock && rowIndex < height; rowIndex++)
{
Vector4 vector4 = pixelRowSpan[x].ToVector4();
redBuffer[x] = vector4.X;
greenBuffer[x] = vector4.Y;
blueBuffer[x] = vector4.Z;
}
Span<TPixel> pixelRowSpan = pixels.DangerousGetRowSpan((int)rowIndex);
for (int x = 0; x < width; x++)
{
Vector4 vector4 = pixelRowSpan[x].ToVector4();
redBuffer[x] = vector4.X;
greenBuffer[x] = vector4.Y;
blueBuffer[x] = vector4.Z;
}
// Write pixel data to buffer.
Span<byte> rowBlockSpan = rowBlockBuffer.GetSpan();
switch (this.pixelType)
{
case ExrPixelType.Float:
this.WriteSingleRow(rowBlockSpan, width, blueBuffer, greenBuffer, redBuffer);
break;
case ExrPixelType.Half:
this.WriteHalfSingleRow(rowBlockSpan, width, blueBuffer, greenBuffer, redBuffer);
break;
// Write pixel data to row block buffer.
Span<byte> rowBlockSpan = rowBlockBuffer.GetSpan().Slice((int)(rowsInBlockCount * bytesPerRow), (int)bytesPerRow);
switch (this.pixelType)
{
case ExrPixelType.Float:
this.WriteSingleRow(rowBlockSpan, width, blueBuffer, greenBuffer, redBuffer);
break;
case ExrPixelType.Half:
this.WriteHalfSingleRow(rowBlockSpan, width, blueBuffer, greenBuffer, redBuffer);
break;
}
rowsInBlockCount++;
}
// Write compressed pixel row data to the stream.
uint compressedBytes = compressor.CompressRowBlock(rowBlockSpan, (int)rowsPerBlock);
uint compressedBytes = compressor.CompressRowBlock(rowBlockBuffer.GetSpan(), (int)rowsInBlockCount);
long positionAfterPixelData = stream.Position;
// Write pixel row data size.
@ -243,34 +249,40 @@ internal sealed class ExrEncoderCore
Rgb96 rgb = default;
ulong[] rowOffsets = new ulong[height];
for (int y = 0; y < height; y++)
for (uint y = 0; y < height; y += rowsPerBlock)
{
rowOffsets[y] = (ulong)stream.Position;
// Write row index.
BinaryPrimitives.WriteUInt32LittleEndian(this.buffer, (uint)y);
BinaryPrimitives.WriteUInt32LittleEndian(this.buffer, y);
stream.Write(this.buffer.AsSpan(0, 4));
// At this point, it is not yet known how much bytes the compressed data will take up, keep stream position.
long pixelDataSizePos = stream.Position;
Span<TPixel> pixelRowSpan = pixels.DangerousGetRowSpan(y);
stream.Position = pixelDataSizePos + 4;
for (int x = 0; x < width; x++)
uint rowsInBlockCount = 0;
for (uint rowIndex = y; rowIndex < y + rowsPerBlock && rowIndex < height; rowIndex++)
{
Vector4 vector4 = pixelRowSpan[x].ToVector4();
Rgb96.FromVector4(vector4);
Span<TPixel> pixelRowSpan = pixels.DangerousGetRowSpan((int)rowIndex);
for (int x = 0; x < width; x++)
{
Vector4 vector4 = pixelRowSpan[x].ToVector4();
Rgb96.FromVector4(vector4);
redBuffer[x] = rgb.R;
greenBuffer[x] = rgb.G;
blueBuffer[x] = rgb.B;
}
redBuffer[x] = rgb.R;
greenBuffer[x] = rgb.G;
blueBuffer[x] = rgb.B;
}
// Write row data to buffer.
this.WriteUnsignedIntRow(rowBlockBuffer.GetSpan(), width, blueBuffer, greenBuffer, redBuffer);
// Write row data to row block buffer.
Span<byte> rowBlockSpan = rowBlockBuffer.GetSpan().Slice((int)(rowsInBlockCount * bytesPerRow), (int)bytesPerRow);
this.WriteUnsignedIntRow(rowBlockSpan, width, blueBuffer, greenBuffer, redBuffer);
rowsInBlockCount++;
}
// Write pixel row data compressed to the stream.
uint compressedBytes = compressor.CompressRowBlock(rowBlockBuffer.GetSpan(), 1);
uint compressedBytes = compressor.CompressRowBlock(rowBlockBuffer.GetSpan(), (int)rowsInBlockCount);
long positionAfterPixelData = stream.Position;
// Write pixel row data size.

Loading…
Cancel
Save