Browse Source

Implement GetPixelTypeInfo() for EXR

pull/3096/head
Brian Popow 2 months ago
parent
commit
c8c3656aaf
  1. 17
      src/ImageSharp/Formats/Exr/Constants/ExrImageDataType.cs
  2. 60
      src/ImageSharp/Formats/Exr/ExrDecoderCore.cs
  3. 63
      src/ImageSharp/Formats/Exr/ExrMetadata.cs
  4. 28
      tests/ImageSharp.Tests/Formats/Exr/ExrDecoderTests.cs

17
src/ImageSharp/Formats/Exr/Constants/ExrImageDataType.cs

@ -3,13 +3,28 @@
namespace SixLabors.ImageSharp.Formats.Exr.Constants;
internal enum ExrImageDataType
/// <summary>
/// This enum represents the type of pixel data in the EXR image.
/// </summary>
public enum ExrImageDataType
{
/// <summary>
/// The pixel data is unknown.
/// </summary>
Unknown = 0,
/// <summary>
/// The pixel data has 3 channels: red, green and blue.
/// </summary>
Rgb = 1,
/// <summary>
/// The pixel data has four channels: red, green, blue and a alpha channel.
/// </summary>
Rgba = 2,
/// <summary>
/// There is only one channel with the luminance.
/// </summary>
Gray = 3,
}

60
src/ImageSharp/Formats/Exr/ExrDecoderCore.cs

@ -79,6 +79,11 @@ internal sealed class ExrDecoderCore : ImageDecoderCore
/// </summary>
private ExrCompression Compression { get; set; }
/// <summary>
/// Gets or sets the image data type, either RGB, RGBA or gray.
/// </summary>
private ExrImageDataType ImageDataType { get; set; }
/// <summary>
/// Gets or sets the pixel type.
/// </summary>
@ -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;
}

63
src/ImageSharp/Formats/Exr/ExrMetadata.cs

@ -30,12 +30,69 @@ public class ExrMetadata : IFormatMetadata<ExrMetadata>
/// </summary>
public ExrPixelType PixelType { get; set; } = ExrPixelType.Half;
/// <summary>
/// Gets or sets the image data type, either RGB, RGBA or gray.
/// </summary>
public ExrImageDataType ImageDataType { get; set; } = ExrImageDataType.Unknown;
/// <inheritdoc/>
public void AfterImageApply<TPixel>(Image<TPixel> destination, Matrix4x4 matrix)
where TPixel : unmanaged, IPixel<TPixel> => 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
};
}
/// <inheritdoc/>
public PixelTypeInfo GetPixelTypeInfo() => throw new NotImplementedException();
public void AfterImageApply<TPixel>(Image<TPixel> destination, Matrix4x4 matrix)
where TPixel : unmanaged, IPixel<TPixel> => throw new NotImplementedException();
/// <inheritdoc/>
public FormatConnectingMetadata ToFormatConnectingMetadata() => throw new NotImplementedException();

28
tests/ImageSharp.Tests/Formats/Exr/ExrDecoderTests.cs

@ -30,7 +30,20 @@ public class ExrDecoderTests
[Theory]
[InlineData(TestImages.Exr.Uncompressed)]
public void ExrDecoder_Identify_DetectsCorrectPixelType<TPixel>(string imagePath)
public void ExrDecoder_Identify_DetectsCorrectPixelType_Half<TPixel>(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<TPixel>(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<TPixel>(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<TPixel>(TestImageProvider<TPixel> provider)

Loading…
Cancel
Save