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