diff --git a/src/ImageSharp/Formats/Exr/Constants/ExrImageDataType.cs b/src/ImageSharp/Formats/Exr/Constants/ExrImageDataType.cs
index d63a8e2198..9453a7d9cd 100644
--- a/src/ImageSharp/Formats/Exr/Constants/ExrImageDataType.cs
+++ b/src/ImageSharp/Formats/Exr/Constants/ExrImageDataType.cs
@@ -3,13 +3,28 @@
namespace SixLabors.ImageSharp.Formats.Exr.Constants;
-internal enum ExrImageDataType
+///
+/// This enum represents the type of pixel data in the EXR image.
+///
+public enum ExrImageDataType
{
+ ///
+ /// The pixel data is unknown.
+ ///
Unknown = 0,
+ ///
+ /// The pixel data has 3 channels: red, green and blue.
+ ///
Rgb = 1,
+ ///
+ /// The pixel data has four channels: red, green, blue and a alpha channel.
+ ///
Rgba = 2,
+ ///
+ /// There is only one channel with the luminance.
+ ///
Gray = 3,
}
diff --git a/src/ImageSharp/Formats/Exr/ExrDecoderCore.cs b/src/ImageSharp/Formats/Exr/ExrDecoderCore.cs
index 9cff42a4e0..1127b146c5 100644
--- a/src/ImageSharp/Formats/Exr/ExrDecoderCore.cs
+++ b/src/ImageSharp/Formats/Exr/ExrDecoderCore.cs
@@ -79,6 +79,11 @@ internal sealed class ExrDecoderCore : ImageDecoderCore
///
private ExrCompression Compression { get; set; }
+ ///
+ /// Gets or sets the image data type, either RGB, RGBA or gray.
+ ///
+ private ExrImageDataType ImageDataType { get; set; }
+
///
/// Gets or sets the pixel type.
///
@@ -403,6 +408,59 @@ internal sealed class ExrDecoderCore : ImageDecoderCore
return pixelType;
}
+ private ExrImageDataType ReadImageDataType()
+ {
+ 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", StringComparison.Ordinal))
+ {
+ hasAlphaChannel = true;
+ }
+
+ if (channelInfo.ChannelName.Equals("R", StringComparison.Ordinal))
+ {
+ hasRedChannel = true;
+ }
+
+ if (channelInfo.ChannelName.Equals("G", StringComparison.Ordinal))
+ {
+ hasGreenChannel = true;
+ }
+
+ if (channelInfo.ChannelName.Equals("B", StringComparison.Ordinal))
+ {
+ hasBlueChannel = true;
+ }
+
+ if (channelInfo.ChannelName.Equals("Y", StringComparison.Ordinal))
+ {
+ hasLuminance = true;
+ }
+ }
+
+ if (hasRedChannel && hasGreenChannel && hasBlueChannel && hasAlphaChannel)
+ {
+ return ExrImageDataType.Rgba;
+ }
+
+ if (hasRedChannel && hasGreenChannel && hasBlueChannel)
+ {
+ return ExrImageDataType.Rgb;
+ }
+
+ if (hasLuminance && this.Channels.Count == 1)
+ {
+ return ExrImageDataType.Gray;
+ }
+
+ return ExrImageDataType.Unknown;
+ }
+
private ExrHeaderAttributes ReadExrHeader(BufferedReadStream stream)
{
// Skip over the magick bytes, we already know its an EXR image.
@@ -431,11 +489,13 @@ internal sealed class ExrDecoderCore : ImageDecoderCore
this.Channels = this.HeaderAttributes.Channels;
this.Compression = this.HeaderAttributes.Compression;
this.PixelType = this.ValidateChannels();
+ this.ImageDataType = this.ReadImageDataType();
this.metadata = new ImageMetadata();
this.exrMetadata = this.metadata.GetExrMetadata();
this.exrMetadata.PixelType = this.PixelType;
+ this.exrMetadata.ImageDataType = this.ImageDataType;
return this.HeaderAttributes;
}
diff --git a/src/ImageSharp/Formats/Exr/ExrMetadata.cs b/src/ImageSharp/Formats/Exr/ExrMetadata.cs
index b32da8f038..7abba6da89 100644
--- a/src/ImageSharp/Formats/Exr/ExrMetadata.cs
+++ b/src/ImageSharp/Formats/Exr/ExrMetadata.cs
@@ -30,12 +30,69 @@ public class ExrMetadata : IFormatMetadata
///
public ExrPixelType PixelType { get; set; } = ExrPixelType.Half;
+ ///
+ /// Gets or sets the image data type, either RGB, RGBA or gray.
+ ///
+ public ExrImageDataType ImageDataType { get; set; } = ExrImageDataType.Unknown;
+
///
- public void AfterImageApply(Image destination, Matrix4x4 matrix)
- where TPixel : unmanaged, IPixel => throw new NotImplementedException();
+ public PixelTypeInfo GetPixelTypeInfo()
+ {
+ bool hasAlpha = this.ImageDataType is ExrImageDataType.Rgba;
+
+ int bitsPerComponent = 32;
+ int bitsPerPixel = hasAlpha ? bitsPerComponent * 4 : bitsPerComponent * 3;
+ if (this.PixelType == ExrPixelType.Half)
+ {
+ bitsPerComponent = 16;
+ bitsPerPixel = hasAlpha ? bitsPerComponent * 4 : bitsPerComponent * 3;
+ }
+
+ PixelAlphaRepresentation alpha = hasAlpha ? PixelAlphaRepresentation.Unassociated : PixelAlphaRepresentation.None;
+ PixelColorType color = PixelColorType.RGB;
+
+ int componentsCount = 0;
+ int[] precision = [];
+ switch (this.ImageDataType)
+ {
+ case ExrImageDataType.Rgb:
+ color = PixelColorType.RGB;
+ componentsCount = 3;
+ precision = new int[componentsCount];
+ precision[0] = bitsPerComponent;
+ precision[1] = bitsPerComponent;
+ precision[2] = bitsPerComponent;
+ break;
+ case ExrImageDataType.Rgba:
+ color = PixelColorType.RGB | PixelColorType.Alpha;
+ componentsCount = 4;
+ precision = new int[componentsCount];
+ precision[0] = bitsPerComponent;
+ precision[1] = bitsPerComponent;
+ precision[2] = bitsPerComponent;
+ precision[3] = bitsPerComponent;
+ break;
+ case ExrImageDataType.Gray:
+ color = PixelColorType.Luminance;
+ componentsCount = 1;
+ precision = new int[componentsCount];
+ precision[0] = bitsPerComponent;
+ break;
+ }
+
+ PixelComponentInfo info = PixelComponentInfo.Create(componentsCount, bitsPerPixel, precision);
+ return new PixelTypeInfo(bitsPerPixel)
+ {
+ AlphaRepresentation = alpha,
+ ComponentInfo = info,
+ ColorType = color
+ };
+ }
+
///
- public PixelTypeInfo GetPixelTypeInfo() => throw new NotImplementedException();
+ public void AfterImageApply(Image destination, Matrix4x4 matrix)
+ where TPixel : unmanaged, IPixel => throw new NotImplementedException();
///
public FormatConnectingMetadata ToFormatConnectingMetadata() => throw new NotImplementedException();
diff --git a/tests/ImageSharp.Tests/Formats/Exr/ExrDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Exr/ExrDecoderTests.cs
index 0077523ad2..0d6ea213a6 100644
--- a/tests/ImageSharp.Tests/Formats/Exr/ExrDecoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Exr/ExrDecoderTests.cs
@@ -30,7 +30,20 @@ public class ExrDecoderTests
[Theory]
[InlineData(TestImages.Exr.Uncompressed)]
- public void ExrDecoder_Identify_DetectsCorrectPixelType(string imagePath)
+ public void ExrDecoder_Identify_DetectsCorrectPixelType_Half(string imagePath)
+ {
+ TestFile testFile = TestFile.Create(imagePath);
+ using MemoryStream stream = new(testFile.Bytes, false);
+ ImageInfo imageInfo = Image.Identify(stream);
+ ExrMetadata exrMetaData = imageInfo.Metadata.GetExrMetadata();
+
+ Assert.NotNull(imageInfo);
+ Assert.Equal(ExrPixelType.Half, exrMetaData.PixelType);
+ }
+
+ [Theory]
+ [InlineData(TestImages.Exr.UncompressedFloatRgb)]
+ public void ExrDecoder_Identify_DetectsCorrectPixelType_Float(string imagePath)
{
TestFile testFile = TestFile.Create(imagePath);
using MemoryStream stream = new(testFile.Bytes, false);
@@ -41,6 +54,19 @@ public class ExrDecoderTests
Assert.Equal(ExrPixelType.Float, exrMetaData.PixelType);
}
+ [Theory]
+ [InlineData(TestImages.Exr.UncompressedUintRgb)]
+ public void ExrDecoder_Identify_DetectsCorrectPixelType_Int(string imagePath)
+ {
+ TestFile testFile = TestFile.Create(imagePath);
+ using MemoryStream stream = new(testFile.Bytes, false);
+ ImageInfo imageInfo = Image.Identify(stream);
+ ExrMetadata exrMetaData = imageInfo.Metadata.GetExrMetadata();
+
+ Assert.NotNull(imageInfo);
+ Assert.Equal(ExrPixelType.UnsignedInt, exrMetaData.PixelType);
+ }
+
[Theory]
[WithFile(TestImages.Exr.Uncompressed, PixelTypes.Rgba32)]
public void ExrDecoder_CanDecode_Uncompressed_Rgba_ExrPixelType_Half(TestImageProvider provider)