Browse Source

Decode EXR images with 32 bit float pixel data

pull/3096/head
Brian Popow 4 years ago
parent
commit
d3993f7bc0
  1. 2
      src/ImageSharp/Formats/OpenExr/ExrCompression.cs
  2. 72
      src/ImageSharp/Formats/OpenExr/ExrDecoderCore.cs

2
src/ImageSharp/Formats/OpenExr/ExrCompression.cs

@ -3,7 +3,7 @@
namespace SixLabors.ImageSharp.Formats.OpenExr namespace SixLabors.ImageSharp.Formats.OpenExr
{ {
internal enum ExrCompression : int internal enum ExrCompression
{ {
None = 0 None = 0
} }

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

@ -66,19 +66,25 @@ namespace SixLabors.ImageSharp.Formats.OpenExr
private IList<ExrChannelInfo> Channels { get; set; } private IList<ExrChannelInfo> Channels { get; set; }
private ExrCompression Compression { get; set; }
/// <inheritdoc /> /// <inheritdoc />
public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken) public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
ExrHeader header = this.ReadExrHeader(stream); ExrHeader header = this.ReadExrHeader(stream);
if (this.Compression != ExrCompression.None)
{
ExrThrowHelper.ThrowNotSupported("Only uncompressed EXR images are supported");
}
int bitsPerPixel = CalculateBitsPerPixel(header.Channels); int bitsPerPixel = CalculateBitsPerPixel(header.Channels);
var image = new Image<TPixel>(this.Configuration, this.Width, this.Height, this.metadata); var image = new Image<TPixel>(this.Configuration, this.Width, this.Height, this.metadata);
Buffer2D<TPixel> pixels = image.GetRootFramePixelBuffer(); Buffer2D<TPixel> pixels = image.GetRootFramePixelBuffer();
// TODO: we for now assume the image pixel type is HalfSingle.
using IMemoryOwner<float> rowBuffer = this.memoryAllocator.Allocate<float>(this.Width * 3); using IMemoryOwner<float> rowBuffer = this.memoryAllocator.Allocate<float>(this.Width * 3);
Span<float> redPixelData = rowBuffer.GetSpan().Slice(0, this.Width); Span<float> redPixelData = rowBuffer.GetSpan().Slice(0, this.Width);
Span<float> bluePixelData = rowBuffer.GetSpan().Slice(this.Width, this.Width); Span<float> bluePixelData = rowBuffer.GetSpan().Slice(this.Width, this.Width);
@ -102,36 +108,52 @@ namespace SixLabors.ImageSharp.Formats.OpenExr
for (int channelIdx = 0; channelIdx < this.Channels.Count; channelIdx++) for (int channelIdx = 0; channelIdx < this.Channels.Count; channelIdx++)
{ {
switch (this.Channels[channelIdx].ChannelName) ExrChannelInfo channel = this.Channels[channelIdx];
switch (channel.ChannelName)
{ {
case "R": case "R":
for (int x = 0; x < this.Width; x++) switch (channel.PixelType)
{ {
redPixelData[x] = stream.ReadHalfSingle(this.buffer); case ExrPixelType.Half:
this.ReadPixelRowChannelHalfSingle(stream, redPixelData);
break;
case ExrPixelType.Float:
this.ReadPixelRowChannelSingle(stream, redPixelData);
break;
} }
break; break;
case "B": case "B":
for (int x = 0; x < this.Width; x++) switch (channel.PixelType)
{ {
bluePixelData[x] = stream.ReadHalfSingle(this.buffer); case ExrPixelType.Half:
this.ReadPixelRowChannelHalfSingle(stream, bluePixelData);
break;
case ExrPixelType.Float:
this.ReadPixelRowChannelSingle(stream, bluePixelData);
break;
} }
break; break;
case "G": case "G":
for (int x = 0; x < this.Width; x++) switch (channel.PixelType)
{ {
greenPixelData[x] = stream.ReadHalfSingle(this.buffer); case ExrPixelType.Half:
this.ReadPixelRowChannelHalfSingle(stream, greenPixelData);
break;
case ExrPixelType.Float:
this.ReadPixelRowChannelSingle(stream, greenPixelData);
break;
} }
break; break;
default: default:
// Skip unknown channel. // Skip unknown channel.
// TODO: can we assume the same data size as the others here? int channelDataSizeInBytes = channel.PixelType is ExrPixelType.Float or ExrPixelType.Uint ? 4 : 2;
stream.Position += this.Width * 2; stream.Position += this.Width * channelDataSizeInBytes;
break; break;
} }
} }
@ -149,6 +171,22 @@ namespace SixLabors.ImageSharp.Formats.OpenExr
return image; return image;
} }
private void ReadPixelRowChannelHalfSingle(BufferedReadStream stream, Span<float> channelData)
{
for (int x = 0; x < this.Width; x++)
{
channelData[x] = stream.ReadHalfSingle(this.buffer);
}
}
private void ReadPixelRowChannelSingle(BufferedReadStream stream, Span<float> channelData)
{
for (int x = 0; x < this.Width; x++)
{
channelData[x] = stream.ReadSingle(this.buffer);
}
}
/// <inheritdoc /> /// <inheritdoc />
public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
{ {
@ -182,14 +220,10 @@ namespace SixLabors.ImageSharp.Formats.OpenExr
ExrThrowHelper.ThrowInvalidImageHeader(); ExrThrowHelper.ThrowInvalidImageHeader();
} }
if (header.Channels.Count != 3)
{
ExrThrowHelper.ThrowNotSupported("Only 3 channels are supported!");
}
this.Width = header.DataWindow.Value.xMax - header.DataWindow.Value.xMin + 1; this.Width = header.DataWindow.Value.xMax - header.DataWindow.Value.xMin + 1;
this.Height = header.DataWindow.Value.yMax - header.DataWindow.Value.yMin + 1; this.Height = header.DataWindow.Value.yMax - header.DataWindow.Value.yMin + 1;
this.Channels = header.Channels; this.Channels = header.Channels;
this.Compression = header.Compression.GetValueOrDefault();
this.metadata = new ImageMetadata(); this.metadata = new ImageMetadata();
@ -210,13 +244,7 @@ namespace SixLabors.ImageSharp.Formats.OpenExr
header.Channels = channels; header.Channels = channels;
break; break;
case "compression": case "compression":
var compression = (ExrCompression)stream.ReadByte(); header.Compression = (ExrCompression)stream.ReadByte();
if (compression != ExrCompression.None)
{
ExrThrowHelper.ThrowNotSupported("Only uncompressed EXR images are supported");
}
header.Compression = compression;
break; break;
case "dataWindow": case "dataWindow":
ExrBox2i dataWindow = this.ReadBox2i(stream); ExrBox2i dataWindow = this.ReadBox2i(stream);

Loading…
Cancel
Save