diff --git a/src/ImageSharp/Formats/OpenExr/Compression/Compressors/ZipExrCompression.cs b/src/ImageSharp/Formats/OpenExr/Compression/Compressors/ZipExrCompression.cs
index 508a5b4ac7..65a2ea3485 100644
--- a/src/ImageSharp/Formats/OpenExr/Compression/Compressors/ZipExrCompression.cs
+++ b/src/ImageSharp/Formats/OpenExr/Compression/Compressors/ZipExrCompression.cs
@@ -44,6 +44,11 @@ namespace SixLabors.ImageSharp.Formats.OpenExr.Compression.Compressors
totalRead += bytesRead;
}
+ if (totalRead == 0)
+ {
+ ExrThrowHelper.ThrowInvalidImageContentException("Could not read zip compressed image data!");
+ }
+
Reconstruct(uncompressed, (uint)totalRead);
Interleave(uncompressed, (uint)totalRead, buffer);
}
diff --git a/src/ImageSharp/Formats/OpenExr/ExrConstants.cs b/src/ImageSharp/Formats/OpenExr/ExrConstants.cs
index 079adec7ed..ad6b1763d9 100644
--- a/src/ImageSharp/Formats/OpenExr/ExrConstants.cs
+++ b/src/ImageSharp/Formats/OpenExr/ExrConstants.cs
@@ -74,6 +74,8 @@ namespace SixLabors.ImageSharp.Formats.OpenExr
public const string Blue = "B";
public const string Alpha = "A";
+
+ public const string Luminance = "Y";
}
}
}
diff --git a/src/ImageSharp/Formats/OpenExr/ExrDecoderCore.cs b/src/ImageSharp/Formats/OpenExr/ExrDecoderCore.cs
index aadbe425e5..a41ceb0766 100644
--- a/src/ImageSharp/Formats/OpenExr/ExrDecoderCore.cs
+++ b/src/ImageSharp/Formats/OpenExr/ExrDecoderCore.cs
@@ -70,18 +70,16 @@ namespace SixLabors.ImageSharp.Formats.OpenExr
private ExrCompressionType Compression { get; set; }
+ private ExrImageType ImageType { get; set; }
+
///
public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel
{
this.ReadExrHeader(stream);
-
- if (this.Compression is not ExrCompressionType.None and not ExrCompressionType.Zips and not ExrCompressionType.Zip and not ExrCompressionType.RunLengthEncoded)
- {
- ExrThrowHelper.ThrowNotSupported($"Compression {this.Compression} is not yet supported");
- }
-
+ this.IsSupportedCompression();
ExrPixelType pixelType = this.ValidateChannels();
+ this.ReadImageType();
var image = new Image(this.Configuration, this.Width, this.Height, this.metadata);
Buffer2D pixels = image.GetRootFramePixelBuffer();
@@ -197,6 +195,22 @@ namespace SixLabors.ImageSharp.Formats.OpenExr
break;
+ case ExrConstants.ChannelNames.Luminance:
+ switch (channel.PixelType)
+ {
+ case ExrPixelType.Half:
+ offset += this.ReadPixelRowChannelHalfSingle(decompressedPixelData.Slice(offset), redPixelData);
+ break;
+ case ExrPixelType.Float:
+ offset += this.ReadPixelRowChannelSingle(decompressedPixelData.Slice(offset), redPixelData);
+ break;
+ }
+
+ redPixelData.CopyTo(bluePixelData);
+ redPixelData.CopyTo(greenPixelData);
+
+ break;
+
default:
// Skip unknown channel.
int channelDataSizeInBytes = channel.PixelType is ExrPixelType.Float or ExrPixelType.UnsignedInt ? 4 : 2;
@@ -418,41 +432,9 @@ namespace SixLabors.ImageSharp.Formats.OpenExr
}
// Find pixel the type of any channel which is R, G, B or A.
- ExrPixelType? pixelType = null;
- for (int i = 0; i < this.Channels.Count; i++)
- {
- if (this.Channels[i].ChannelName.Equals(ExrConstants.ChannelNames.Blue) ||
- this.Channels[i].ChannelName.Equals(ExrConstants.ChannelNames.Green) ||
- this.Channels[i].ChannelName.Equals(ExrConstants.ChannelNames.Red) ||
- this.Channels[i].ChannelName.Equals(ExrConstants.ChannelNames.Alpha))
- {
- pixelType = this.Channels[i].PixelType;
- break;
- }
- }
-
- if (!pixelType.HasValue)
- {
- ExrThrowHelper.ThrowInvalidImageContentException("Pixel channel data is unknown! Only R, G, B and A are supported.");
- }
+ ExrPixelType pixelType = this.FindPixelType();
- for (int i = 0; i < this.Channels.Count; i++)
- {
- // Ignore channels which we cannot interpret.
- if (!(this.Channels[i].ChannelName.Equals(ExrConstants.ChannelNames.Blue) ||
- this.Channels[i].ChannelName.Equals(ExrConstants.ChannelNames.Green) ||
- this.Channels[i].ChannelName.Equals(ExrConstants.ChannelNames.Red)))
- {
- continue;
- }
-
- if (pixelType != this.Channels[i].PixelType)
- {
- ExrThrowHelper.ThrowInvalidImageContentException("All channels are expected to have the same pixel data!");
- }
- }
-
- return pixelType.Value;
+ return pixelType;
}
private ExrHeader ReadExrHeader(BufferedReadStream stream)
@@ -640,6 +622,103 @@ namespace SixLabors.ImageSharp.Formats.OpenExr
return str.ToString();
}
+ private ExrPixelType FindPixelType()
+ {
+ ExrPixelType? pixelType = null;
+ for (int i = 0; i < this.Channels.Count; i++)
+ {
+ if (this.Channels[i].ChannelName.Equals(ExrConstants.ChannelNames.Blue) ||
+ this.Channels[i].ChannelName.Equals(ExrConstants.ChannelNames.Green) ||
+ this.Channels[i].ChannelName.Equals(ExrConstants.ChannelNames.Red) ||
+ this.Channels[i].ChannelName.Equals(ExrConstants.ChannelNames.Alpha) ||
+ this.Channels[i].ChannelName.Equals(ExrConstants.ChannelNames.Luminance))
+ {
+ if (!pixelType.HasValue)
+ {
+ pixelType = this.Channels[i].PixelType;
+ }
+ else
+ {
+ if (pixelType != this.Channels[i].PixelType)
+ {
+ ExrThrowHelper.ThrowNotSupported("Pixel channel data is expected to be the same for all channels.");
+ }
+ }
+ }
+ }
+
+ if (!pixelType.HasValue)
+ {
+ ExrThrowHelper.ThrowNotSupported("Pixel channel data is unknown! Only R, G, B, A and Y are supported.");
+ }
+
+ return pixelType.Value;
+ }
+
+ private void IsSupportedCompression()
+ {
+ if (this.Compression is not ExrCompressionType.None and not ExrCompressionType.Zips and not ExrCompressionType.Zip and not ExrCompressionType.RunLengthEncoded)
+ {
+ ExrThrowHelper.ThrowNotSupported($"Compression {this.Compression} is not yet supported");
+ }
+ }
+
+ private void ReadImageType()
+ {
+ bool hasRedChannel = false;
+ bool hasGreenChannel = false;
+ bool hasBlueChannel = false;
+ bool hasAlphaChannel = false;
+ bool hasLuminance = false;
+ foreach (ExrChannelInfo channelInfo in this.Channels)
+ {
+ if (channelInfo.ChannelName.Equals("A"))
+ {
+ hasAlphaChannel = true;
+ }
+
+ if (channelInfo.ChannelName.Equals("R"))
+ {
+ hasRedChannel = true;
+ }
+
+ if (channelInfo.ChannelName.Equals("G"))
+ {
+ hasGreenChannel = true;
+ }
+
+ if (channelInfo.ChannelName.Equals("B"))
+ {
+ hasBlueChannel = true;
+ }
+
+ if (channelInfo.ChannelName.Equals("Y"))
+ {
+ hasLuminance = true;
+ }
+ }
+
+ if (hasRedChannel && hasGreenChannel && hasBlueChannel && hasAlphaChannel)
+ {
+ this.ImageType = ExrImageType.Rgba;
+ return;
+ }
+
+ if (hasRedChannel && hasGreenChannel && hasBlueChannel)
+ {
+ this.ImageType = ExrImageType.Rgb;
+ return;
+ }
+
+ if (hasLuminance && this.Channels.Count == 1)
+ {
+ this.ImageType = ExrImageType.Gray;
+ return;
+ }
+
+ ExrThrowHelper.ThrowNotSupported("The image contains channels, which are not supported!");
+ }
+
private bool HasAlpha()
{
foreach (ExrChannelInfo channelInfo in this.Channels)
@@ -658,7 +737,7 @@ namespace SixLabors.ImageSharp.Formats.OpenExr
uint bytesPerRow = 0;
foreach (ExrChannelInfo channelInfo in this.Channels)
{
- if (channelInfo.ChannelName.Equals("A") || channelInfo.ChannelName.Equals("R") || channelInfo.ChannelName.Equals("G") || channelInfo.ChannelName.Equals("B"))
+ if (channelInfo.ChannelName.Equals("A") || channelInfo.ChannelName.Equals("R") || channelInfo.ChannelName.Equals("G") || channelInfo.ChannelName.Equals("B") || channelInfo.ChannelName.Equals("Y"))
{
if (channelInfo.PixelType == ExrPixelType.Half)
{
diff --git a/src/ImageSharp/Formats/OpenExr/ExrImageType.cs b/src/ImageSharp/Formats/OpenExr/ExrImageType.cs
new file mode 100644
index 0000000000..e4e9c8a649
--- /dev/null
+++ b/src/ImageSharp/Formats/OpenExr/ExrImageType.cs
@@ -0,0 +1,16 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Formats.OpenExr
+{
+ internal enum ExrImageType
+ {
+ Unknown = 0,
+
+ Rgb = 1,
+
+ Rgba = 2,
+
+ Gray = 3,
+ }
+}