Browse Source

Add support for decoding RLE exr images

pull/3096/head
Brian Popow 5 years ago
parent
commit
85501f3c00
  1. 94
      src/ImageSharp/Formats/OpenExr/Compression/Compressors/RunLengthCompression.cs
  2. 32
      src/ImageSharp/Formats/OpenExr/Compression/Compressors/ZipsExrCompression.cs
  3. 23
      src/ImageSharp/Formats/OpenExr/Compression/ExrBaseDecompressor.cs
  4. 2
      src/ImageSharp/Formats/OpenExr/Compression/ExrDecompressorFactory.cs
  5. 3
      src/ImageSharp/Formats/OpenExr/ExrDecoderCore.cs

94
src/ImageSharp/Formats/OpenExr/Compression/Compressors/RunLengthCompression.cs

@ -0,0 +1,94 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.OpenExr.Compression.Compressors
{
internal class RunLengthCompression : ExrBaseDecompressor
{
private readonly IMemoryOwner<byte> tmpBuffer;
public RunLengthCompression(MemoryAllocator allocator, uint uncompressedBytes)
: base(allocator, uncompressedBytes) => this.tmpBuffer = allocator.Allocate<byte>((int)uncompressedBytes);
public override void Decompress(BufferedReadStream stream, uint compressedBytes, Span<byte> buffer)
{
Span<byte> uncompressed = this.tmpBuffer.GetSpan();
int maxLength = (int)this.UncompressedBytes;
int offset = 0;
while (compressedBytes > 0)
{
byte nextByte = ReadNextByte(stream);
sbyte input = (sbyte)nextByte;
if (input < 0)
{
int count = -input;
compressedBytes -= (uint)(count + 1);
if ((maxLength -= count) < 0)
{
return;
}
// Check the input buffer is big enough to contain 'count' bytes of remaining data.
if (compressedBytes < 0)
{
return;
}
for (int i = 0; i < count; i++)
{
uncompressed[offset + i] = ReadNextByte(stream);
}
offset += count;
}
else
{
int count = input;
byte value = ReadNextByte(stream);
compressedBytes -= 2;
if ((maxLength -= count + 1) < 0)
{
return;
}
// Check the input buffer is big enough to contain byte to be duplicated.
if (compressedBytes < 0)
{
return;
}
for (int i = 0; i < count + 1; i++)
{
uncompressed[offset + i] = value;
}
offset += count + 1;
}
}
Reconstruct(uncompressed, this.UncompressedBytes);
Interleave(uncompressed, this.UncompressedBytes, buffer);
}
private static byte ReadNextByte(BufferedReadStream stream)
{
int nextByte = stream.ReadByte();
if (nextByte == -1)
{
ExrThrowHelper.ThrowInvalidImageContentException("Not enough data to decompress RLE image!");
}
return (byte)nextByte;
}
protected override void Dispose(bool disposing) => this.tmpBuffer.Dispose();
}
}

32
src/ImageSharp/Formats/OpenExr/Compression/Compressors/ZipsExrCompression.cs

@ -19,6 +19,8 @@ namespace SixLabors.ImageSharp.Formats.OpenExr.Compression.Compressors
public override void Decompress(BufferedReadStream stream, uint compressedBytes, Span<byte> buffer)
{
Span<byte> uncompressed = this.tmpBuffer.GetSpan();
long pos = stream.Position;
using var deframeStream = new ZlibInflateStream(
stream,
@ -30,11 +32,10 @@ namespace SixLabors.ImageSharp.Formats.OpenExr.Compression.Compressors
deframeStream.AllocateNewBytes((int)this.UncompressedBytes, true);
DeflateStream dataStream = deframeStream.CompressedStream;
Span<byte> tmp = this.tmpBuffer.GetSpan();
int totalRead = 0;
while (totalRead < buffer.Length)
{
int bytesRead = dataStream.Read(tmp, totalRead, buffer.Length - totalRead);
int bytesRead = dataStream.Read(uncompressed, totalRead, buffer.Length - totalRead);
if (bytesRead <= 0)
{
break;
@ -43,31 +44,8 @@ namespace SixLabors.ImageSharp.Formats.OpenExr.Compression.Compressors
totalRead += bytesRead;
}
Reconstruct(tmp, this.UncompressedBytes);
Interleave(tmp, this.UncompressedBytes, buffer);
}
private static void Reconstruct(Span<byte> buffer, uint unCompressedBytes)
{
int offset = 0;
for (int i = 0; i < unCompressedBytes - 1; i++)
{
byte d = (byte)(buffer[offset] + (buffer[offset + 1] - 128));
buffer[offset + 1] = d;
offset++;
}
}
private static void Interleave(Span<byte> source, uint unCompressedBytes, Span<byte> output)
{
int sourceOffset = 0;
int offset0 = 0;
int offset1 = (int)((unCompressedBytes + 1) / 2);
while (sourceOffset < unCompressedBytes)
{
output[sourceOffset++] = source[offset0++];
output[sourceOffset++] = source[offset1++];
}
Reconstruct(uncompressed, this.UncompressedBytes);
Interleave(uncompressed, this.UncompressedBytes, buffer);
}
protected override void Dispose(bool disposing) => this.tmpBuffer.Dispose();

23
src/ImageSharp/Formats/OpenExr/Compression/ExrBaseDecompressor.cs

@ -15,5 +15,28 @@ namespace SixLabors.ImageSharp.Formats.OpenExr.Compression
}
public abstract void Decompress(BufferedReadStream stream, uint compressedBytes, Span<byte> buffer);
protected static void Reconstruct(Span<byte> buffer, uint unCompressedBytes)
{
int offset = 0;
for (int i = 0; i < unCompressedBytes - 1; i++)
{
byte d = (byte)(buffer[offset] + (buffer[offset + 1] - 128));
buffer[offset + 1] = d;
offset++;
}
}
protected static void Interleave(Span<byte> source, uint unCompressedBytes, Span<byte> output)
{
int sourceOffset = 0;
int offset0 = 0;
int offset1 = (int)((unCompressedBytes + 1) / 2);
while (sourceOffset < unCompressedBytes)
{
output[sourceOffset++] = source[offset0++];
output[sourceOffset++] = source[offset1++];
}
}
}
}

2
src/ImageSharp/Formats/OpenExr/Compression/ExrDecompressorFactory.cs

@ -16,6 +16,8 @@ namespace SixLabors.ImageSharp.Formats.OpenExr.Compression
return new NoneExrCompression(memoryAllocator, bytesPerRow);
case ExrCompressionType.Zips:
return new ZipsExrCompression(memoryAllocator, bytesPerRow);
case ExrCompressionType.RunLengthEncoded:
return new RunLengthCompression(memoryAllocator, bytesPerRow);
default:
throw ExrThrowHelper.NotSupportedDecompressor(nameof(method));
}

3
src/ImageSharp/Formats/OpenExr/ExrDecoderCore.cs

@ -9,6 +9,7 @@ using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using SixLabors.ImageSharp.Formats.OpenExr.Compression;
using SixLabors.ImageSharp.Formats.OpenExr.Compression.Compressors;
using SixLabors.ImageSharp.Formats.Pbm;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
@ -76,7 +77,7 @@ namespace SixLabors.ImageSharp.Formats.OpenExr
{
this.ReadExrHeader(stream);
if (this.Compression is not ExrCompressionType.None and not ExrCompressionType.Zips)
if (this.Compression is not ExrCompressionType.None and not ExrCompressionType.Zips and not ExrCompressionType.RunLengthEncoded)
{
ExrThrowHelper.ThrowNotSupported($"Compression {this.Compression} is not yet supported");
}

Loading…
Cancel
Save