Browse Source

Adjust exr decoder / encoder to ImageSharp changes

pull/3096/head
Brian Popow 2 months ago
parent
commit
fa52ab740f
  1. 4
      ImageSharp.sln
  2. 2
      src/ImageSharp/Configuration.cs
  3. 2
      src/ImageSharp/Formats/OpenExr/Compression/Decompressors/ZipExrCompression.cs
  4. 4
      src/ImageSharp/Formats/OpenExr/ExrConfigurationModule.cs
  5. 36
      src/ImageSharp/Formats/OpenExr/ExrDecoder.cs
  6. 86
      src/ImageSharp/Formats/OpenExr/ExrDecoderCore.cs
  7. 2
      src/ImageSharp/Formats/OpenExr/ExrDecoderOptions.cs
  8. 22
      src/ImageSharp/Formats/OpenExr/ExrEncoder.cs
  9. 75
      src/ImageSharp/Formats/OpenExr/ExrEncoderCore.cs
  10. 85
      src/ImageSharp/Formats/OpenExr/ExrHeaderAttributes.cs
  11. 12
      src/ImageSharp/Formats/OpenExr/ExrImageFormatDetector.cs
  12. 16
      src/ImageSharp/Formats/OpenExr/ExrMetadata.cs
  13. 20
      src/ImageSharp/Formats/OpenExr/MetadataExtensions.cs
  14. 100
      src/ImageSharp/Formats/_Generated/ImageExtensions.Save.cs
  15. 21
      src/ImageSharp/Formats/_Generated/ImageMetadataExtensions.cs
  16. 3
      src/ImageSharp/Formats/_Generated/_Formats.ttinclude
  17. 10
      src/ImageSharp/ImageSharp.csproj
  18. 88
      src/ImageSharp/PixelFormats/PixelImplementations/Rgb96.cs
  19. 95
      src/ImageSharp/PixelFormats/PixelImplementations/Rgba128.cs
  20. 12
      tests/ImageSharp.Tests/Formats/Exr/ExrDecoderTests.cs
  21. 108
      tests/ImageSharp.Tests/Formats/Exr/ImageExtensionsTest.cs
  22. 2
      tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs

4
ImageSharp.sln

@ -37,8 +37,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{815C0625-CD3
ProjectSection(SolutionItems) = preProject
src\Directory.Build.props = src\Directory.Build.props
src\Directory.Build.targets = src\Directory.Build.targets
src\README.md = src\README.md
src\ImageSharp.ruleset = src\ImageSharp.ruleset
src\README.md = src\README.md
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp", "src\ImageSharp\ImageSharp.csproj", "{2AA31A1F-142C-43F4-8687-09ABCA4B3A26}"
@ -215,6 +215,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "issues", "issues", "{5C9B68
ProjectSection(SolutionItems) = preProject
tests\Images\Input\Jpg\issues\issue-1076-invalid-subsampling.jpg = tests\Images\Input\Jpg\issues\issue-1076-invalid-subsampling.jpg
tests\Images\Input\Jpg\issues\issue-1221-identify-multi-frame.jpg = tests\Images\Input\Jpg\issues\issue-1221-identify-multi-frame.jpg
tests\Images\Input\Jpg\issues\issue-2067-comment.jpg = tests\Images\Input\Jpg\issues\issue-2067-comment.jpg
tests\Images\Input\Jpg\issues\issue1006-incorrect-resize.jpg = tests\Images\Input\Jpg\issues\issue1006-incorrect-resize.jpg
tests\Images\Input\Jpg\issues\issue1049-exif-resize.jpg = tests\Images\Input\Jpg\issues\issue1049-exif-resize.jpg
tests\Images\Input\Jpg\issues\Issue159-MissingFF00-Progressive-Bedroom.jpg = tests\Images\Input\Jpg\issues\Issue159-MissingFF00-Progressive-Bedroom.jpg
@ -238,7 +239,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "issues", "issues", "{5C9B68
tests\Images\Input\Jpg\issues\issue750-exif-tranform.jpg = tests\Images\Input\Jpg\issues\issue750-exif-tranform.jpg
tests\Images\Input\Jpg\issues\Issue845-Incorrect-Quality99.jpg = tests\Images\Input\Jpg\issues\Issue845-Incorrect-Quality99.jpg
tests\Images\Input\Jpg\issues\issue855-incorrect-colorspace.jpg = tests\Images\Input\Jpg\issues\issue855-incorrect-colorspace.jpg
tests\Images\Input\Jpg\issues\issue-2067-comment.jpg = tests\Images\Input\Jpg\issues\issue-2067-comment.jpg
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "fuzz", "fuzz", "{516A3532-6AC2-417B-AD79-9BD5D0D378A0}"

2
src/ImageSharp/Configuration.cs

@ -226,7 +226,7 @@ public sealed class Configuration
new TgaConfigurationModule(),
new TiffConfigurationModule(),
new WebpConfigurationModule(),
new ExrConfigurationModule());
new ExrConfigurationModule(),
new QoiConfigurationModule(),
new IcoConfigurationModule(),
new CurConfigurationModule());

2
src/ImageSharp/Formats/OpenExr/Compression/Decompressors/ZipExrCompression.cs

@ -29,7 +29,7 @@ internal class ZipExrCompression : ExrBaseDecompressor
return left > 0 ? left : 0;
});
inflateStream.AllocateNewBytes((int)this.UncompressedBytes, true);
DeflateStream dataStream = inflateStream.CompressedStream;
DeflateStream dataStream = inflateStream.CompressedStream!;
int totalRead = 0;
while (totalRead < buffer.Length)

4
src/ImageSharp/Formats/OpenExr/ExrConfigurationModule.cs

@ -6,13 +6,13 @@ namespace SixLabors.ImageSharp.Formats.OpenExr;
/// <summary>
/// Registers the image encoders, decoders and mime type detectors for the OpenExr format.
/// </summary>
public sealed class ExrConfigurationModule : IConfigurationModule
public sealed class ExrConfigurationModule : IImageFormatConfigurationModule
{
/// <inheritdoc/>
public void Configure(Configuration configuration)
{
configuration.ImageFormatsManager.SetEncoder(ExrFormat.Instance, new ExrEncoder());
configuration.ImageFormatsManager.SetDecoder(ExrFormat.Instance, new ExrDecoder());
configuration.ImageFormatsManager.SetDecoder(ExrFormat.Instance, ExrDecoder.Instance);
configuration.ImageFormatsManager.AddImageFormatDetector(new ExrImageFormatDetector());
}
}

36
src/ImageSharp/Formats/OpenExr/ExrDecoder.cs

@ -8,39 +8,41 @@ namespace SixLabors.ImageSharp.Formats.OpenExr;
/// <summary>
/// Image decoder for generating an image out of a OpenExr stream.
/// </summary>
public class ExrDecoder : IImageDecoderSpecialized<ExrDecoderOptions>
public class ExrDecoder : ImageDecoder
{
private ExrDecoder()
{
}
/// <summary>
/// Gets the shared instance.
/// </summary>
public static ExrDecoder Instance { get; } = new();
/// <inheritdoc/>
IImageInfo IImageInfoDetector.Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
protected override ImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));
return new ExrDecoderCore(new() { GeneralOptions = options }).Identify(options.Configuration, stream, cancellationToken);
return new ExrDecoderCore(new ExrDecoderOptions { GeneralOptions = options }).Identify(options.Configuration, stream, cancellationToken);
}
/// <inheritdoc/>
Image<TPixel> IImageDecoder.Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> ((IImageDecoderSpecialized<ExrDecoderOptions>)this).Decode<TPixel>(new() { GeneralOptions = options }, stream, cancellationToken);
/// <inheritdoc/>
Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> ((IImageDecoderSpecialized<ExrDecoderOptions>)this).Decode(new() { GeneralOptions = options }, stream, cancellationToken);
/// <inheritdoc/>
Image<TPixel> IImageDecoderSpecialized<ExrDecoderOptions>.Decode<TPixel>(ExrDecoderOptions options, Stream stream, CancellationToken cancellationToken)
//// <inheritdoc/>
protected override Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));
Image<TPixel> image = new ExrDecoderCore(options).Decode<TPixel>(options.GeneralOptions.Configuration, stream, cancellationToken);
ExrDecoderCore decoder = new(new ExrDecoderOptions { GeneralOptions = options });
Image<TPixel> image = decoder.Decode<TPixel>(options.Configuration, stream, cancellationToken);
ImageDecoderUtilities.Resize(options.GeneralOptions, image);
ScaleToTargetSize(options, image);
return image;
}
/// <inheritdoc/>
Image IImageDecoderSpecialized<ExrDecoderOptions>.Decode(ExrDecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> ((IImageDecoderSpecialized<ExrDecoderOptions>)this).Decode<Rgba32>(options, stream, cancellationToken);
protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode<Rgba32>(options, stream, cancellationToken);
}

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

@ -1,5 +1,6 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#nullable disable
using System.Buffers;
using System.Buffers.Binary;
@ -16,7 +17,7 @@ namespace SixLabors.ImageSharp.Formats.OpenExr;
/// <summary>
/// Performs the OpenExr decoding operation.
/// </summary>
internal sealed class ExrDecoderCore : IImageDecoderInternals
internal sealed class ExrDecoderCore : ImageDecoderCore
{
/// <summary>
/// Reusable buffer.
@ -43,15 +44,12 @@ internal sealed class ExrDecoderCore : IImageDecoderInternals
/// </summary>
/// <param name="options">The options.</param>
public ExrDecoderCore(ExrDecoderOptions options)
: base(options.GeneralOptions)
{
this.Options = options.GeneralOptions;
this.configuration = options.GeneralOptions.Configuration;
this.memoryAllocator = this.configuration.MemoryAllocator;
}
/// <inheritdoc />
public DecoderOptions Options { get; }
/// <summary>
/// Gets the dimensions of the image.
/// </summary>
@ -84,8 +82,7 @@ internal sealed class ExrDecoderCore : IImageDecoderInternals
private ExrHeaderAttributes HeaderAttributes { get; set; }
/// <inheritdoc />
public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
protected override Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken)
{
this.ReadExrHeader(stream);
if (!this.IsSupportedCompression())
@ -162,7 +159,7 @@ internal sealed class ExrDecoderCore : IImageDecoderInternals
for (int x = 0; x < width; x++)
{
HalfVector4 pixelValue = new(redPixelData[x], greenPixelData[x], bluePixelData[x], hasAlpha ? alphaPixelData[x] : 1.0f);
color.FromVector4(pixelValue.ToVector4());
TPixel.FromVector4(pixelValue.ToVector4());
pixelRow[x] = color;
}
}
@ -219,7 +216,7 @@ internal sealed class ExrDecoderCore : IImageDecoderInternals
for (int x = 0; x < width; x++)
{
Rgba128 pixelValue = new(redPixelData[x], greenPixelData[x], bluePixelData[x], hasAlpha ? alphaPixelData[x] : uint.MaxValue);
color.FromVector4(pixelValue.ToVector4());
TPixel.FromVector4(pixelValue.ToVector4());
pixelRow[x] = color;
}
}
@ -366,13 +363,13 @@ internal sealed class ExrDecoderCore : IImageDecoderInternals
}
/// <inheritdoc />
public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
protected override ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
{
ExrHeaderAttributes header = this.ReadExrHeader(stream);
int bitsPerPixel = this.CalculateBitsPerPixel();
return new ImageInfo(new PixelTypeInfo(bitsPerPixel), this.Width, this.Height, new ImageMetadata());
return new ImageInfo(new Size((int)header.ScreenWindowWidth, (int)header.AspectRatio), this.metadata);
}
private int CalculateBitsPerPixel()
@ -430,15 +427,10 @@ internal sealed class ExrDecoderCore : IImageDecoderInternals
this.HeaderAttributes = this.ParseHeaderAttributes(stream);
if (!this.HeaderAttributes.IsValid())
{
ExrThrowHelper.ThrowInvalidImageHeader();
}
this.Width = this.HeaderAttributes.DataWindow.Value.XMax - this.HeaderAttributes.DataWindow.Value.XMin + 1;
this.Height = this.HeaderAttributes.DataWindow.Value.YMax - this.HeaderAttributes.DataWindow.Value.YMin + 1;
this.Width = this.HeaderAttributes.DataWindow.XMax - this.HeaderAttributes.DataWindow.XMin + 1;
this.Height = this.HeaderAttributes.DataWindow.YMax - this.HeaderAttributes.DataWindow.YMin + 1;
this.Channels = this.HeaderAttributes.Channels;
this.Compression = this.HeaderAttributes.Compression.GetValueOrDefault();
this.Compression = this.HeaderAttributes.Compression;
this.metadata = new ImageMetadata();
@ -448,50 +440,54 @@ internal sealed class ExrDecoderCore : IImageDecoderInternals
private ExrHeaderAttributes ParseHeaderAttributes(BufferedReadStream stream)
{
ExrAttribute attribute = this.ReadAttribute(stream);
ExrHeaderAttributes header = new();
IList<ExrChannelInfo> channels = null;
ExrBox2i? dataWindow = null;
ExrCompressionType? compression = null;
ExrBox2i? displayWindow = null;
ExrLineOrder? lineOrder = null;
float? aspectRatio = null;
float? screenWindowCenterX = null;
float? screenWindowCenterY = null;
float? screenWindowWidth = null;
uint? tileXSize = null;
uint? tileYSize = null;
int? chunkCount = null;
while (!attribute.Equals(ExrAttribute.EmptyAttribute))
{
switch (attribute.Name)
{
case ExrConstants.AttributeNames.Channels:
IList<ExrChannelInfo> channels = this.ReadChannelList(stream, attribute.Length);
header.Channels = channels;
channels = this.ReadChannelList(stream, attribute.Length);
break;
case ExrConstants.AttributeNames.Compression:
header.Compression = (ExrCompressionType)stream.ReadByte();
compression = (ExrCompressionType)stream.ReadByte();
break;
case ExrConstants.AttributeNames.DataWindow:
ExrBox2i dataWindow = this.ReadBoxInteger(stream);
header.DataWindow = dataWindow;
dataWindow = this.ReadBoxInteger(stream);
break;
case ExrConstants.AttributeNames.DisplayWindow:
ExrBox2i displayWindow = this.ReadBoxInteger(stream);
header.DisplayWindow = displayWindow;
displayWindow = this.ReadBoxInteger(stream);
break;
case ExrConstants.AttributeNames.LineOrder:
ExrLineOrder lineOrder = (ExrLineOrder)stream.ReadByte();
header.LineOrder = lineOrder;
lineOrder = (ExrLineOrder)stream.ReadByte();
break;
case ExrConstants.AttributeNames.PixelAspectRatio:
float aspectRatio = stream.ReadSingle(this.buffer);
header.AspectRatio = aspectRatio;
aspectRatio = stream.ReadSingle(this.buffer);
break;
case ExrConstants.AttributeNames.ScreenWindowCenter:
float screenWindowCenterX = stream.ReadSingle(this.buffer);
float screenWindowCenterY = stream.ReadSingle(this.buffer);
header.ScreenWindowCenter = new PointF(screenWindowCenterX, screenWindowCenterY);
screenWindowCenterX = stream.ReadSingle(this.buffer);
screenWindowCenterY = stream.ReadSingle(this.buffer);
break;
case ExrConstants.AttributeNames.ScreenWindowWidth:
float screenWindowWidth = stream.ReadSingle(this.buffer);
header.ScreenWindowWidth = screenWindowWidth;
screenWindowWidth = stream.ReadSingle(this.buffer);
break;
case ExrConstants.AttributeNames.Tiles:
header.TileXSize = this.ReadUnsignedInteger(stream);
header.TileYSize = this.ReadUnsignedInteger(stream);
tileXSize = this.ReadUnsignedInteger(stream);
tileYSize = this.ReadUnsignedInteger(stream);
break;
case ExrConstants.AttributeNames.ChunkCount:
header.ChunkCount = this.ReadSignedInteger(stream);
chunkCount = this.ReadSignedInteger(stream);
break;
default:
// Skip unknown attribute bytes.
@ -502,6 +498,18 @@ internal sealed class ExrDecoderCore : IImageDecoderInternals
attribute = this.ReadAttribute(stream);
}
ExrHeaderAttributes header = new(
channels,
compression.Value,
dataWindow.Value,
displayWindow.Value,
lineOrder.Value,
aspectRatio.Value,
screenWindowWidth.Value,
new PointF(screenWindowCenterX.Value, screenWindowCenterY.Value),
tileXSize,
tileYSize,
chunkCount);
return header;
}

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

@ -9,5 +9,5 @@ namespace SixLabors.ImageSharp.Formats.OpenExr;
public sealed class ExrDecoderOptions : ISpecializedDecoderOptions
{
/// <inheritdoc/>
public DecoderOptions GeneralOptions { get; set; } = new();
public DecoderOptions GeneralOptions { get; init; } = new();
}

22
src/ImageSharp/Formats/OpenExr/ExrEncoder.cs

@ -1,34 +1,22 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.OpenExr;
/// <summary>
/// Image encoder for writing an image to a stream in the OpenExr Format.
/// </summary>
public sealed class ExrEncoder : IImageEncoder, IExrEncoderOptions
public sealed class ExrEncoder : ImageEncoder
{
/// <summary>
/// Gets or sets the pixel type of the image.
/// </summary>
public ExrPixelType? PixelType { get; set; }
/// <inheritdoc/>
public void Encode<TPixel>(Image<TPixel> image, Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
{
ExrEncoderCore encoder = new(this, image.GetMemoryAllocator());
encoder.Encode(image, stream);
}
/// <inheritdoc/>
public Task EncodeAsync<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
/// <inheritdoc />
protected override void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
{
ExrEncoderCore encoder = new(this, image.GetMemoryAllocator());
return encoder.EncodeAsync(image, stream, cancellationToken);
ExrEncoderCore encoder = new(this, image.Configuration, image.Configuration.MemoryAllocator);
encoder.Encode(image, stream, cancellationToken);
}
}

75
src/ImageSharp/Formats/OpenExr/ExrEncoderCore.cs

@ -5,6 +5,7 @@ using System.Buffers;
using System.Buffers.Binary;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Threading.Channels;
using SixLabors.ImageSharp.Formats.OpenExr.Compression;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
@ -15,7 +16,7 @@ namespace SixLabors.ImageSharp.Formats.OpenExr;
/// <summary>
/// Image encoder for writing an image to a stream in the OpenExr format.
/// </summary>
internal sealed class ExrEncoderCore : IImageEncoderInternals
internal sealed class ExrEncoderCore
{
/// <summary>
/// Reusable buffer.
@ -27,6 +28,16 @@ internal sealed class ExrEncoderCore : IImageEncoderInternals
/// </summary>
private readonly MemoryAllocator memoryAllocator;
/// <summary>
/// The global configuration.
/// </summary>
private Configuration configuration;
/// <summary>
/// The encoder with options.
/// </summary>
private readonly ExrEncoder encoder;
/// <summary>
/// The pixel type of the image.
/// </summary>
@ -35,12 +46,14 @@ internal sealed class ExrEncoderCore : IImageEncoderInternals
/// <summary>
/// Initializes a new instance of the <see cref="ExrEncoderCore"/> class.
/// </summary>
/// <param name="options">The encoder options.</param>
/// <param name="encoder">The encoder with options.</param>
/// <param name="configuration">The configuration.</param>
/// <param name="memoryAllocator">The memory manager.</param>
public ExrEncoderCore(IExrEncoderOptions options, MemoryAllocator memoryAllocator)
public ExrEncoderCore(ExrEncoder encoder, Configuration configuration, MemoryAllocator memoryAllocator)
{
this.configuration = configuration;
this.encoder = encoder;
this.memoryAllocator = memoryAllocator;
this.pixelType = options.PixelType;
}
/// <summary>
@ -63,22 +76,28 @@ internal sealed class ExrEncoderCore : IImageEncoderInternals
this.pixelType ??= exrMetadata.PixelType;
int width = image.Width;
int height = image.Height;
var header = new ExrHeaderAttributes()
{
Compression = ExrCompressionType.None,
AspectRatio = 1.0f,
DataWindow = new ExrBox2i(0, 0, width - 1, height - 1),
DisplayWindow = new ExrBox2i(0, 0, width - 1, height - 1),
LineOrder = ExrLineOrder.IncreasingY,
ScreenWindowCenter = new PointF(0.0f, 0.0f),
ScreenWindowWidth = 1,
Channels = new List<ExrChannelInfo>()
{
new(ExrConstants.ChannelNames.Blue, this.pixelType.Value, 0, 1, 1),
new(ExrConstants.ChannelNames.Green, this.pixelType.Value, 0, 1, 1),
new(ExrConstants.ChannelNames.Red, this.pixelType.Value, 0, 1, 1),
}
};
ExrCompressionType compression = ExrCompressionType.None;
float aspectRatio = 1.0f;
ExrBox2i dataWindow = new(0, 0, width - 1, height - 1);
ExrBox2i displayWindow = new(0, 0, width - 1, height - 1);
ExrLineOrder lineOrder = ExrLineOrder.IncreasingY;
PointF screenWindowCenter = new(0.0f, 0.0f);
int screenWindowWidth = 1;
List<ExrChannelInfo> channels =
[
new(ExrConstants.ChannelNames.Blue, this.pixelType.Value, 0, 1, 1),
new(ExrConstants.ChannelNames.Green, this.pixelType.Value, 0, 1, 1),
new(ExrConstants.ChannelNames.Red, this.pixelType.Value, 0, 1, 1),
];
ExrHeaderAttributes header = new(
channels,
compression,
dataWindow,
displayWindow,
lineOrder,
aspectRatio,
screenWindowWidth,
screenWindowCenter);
// Write magick bytes.
BinaryPrimitives.WriteInt32LittleEndian(this.buffer, ExrConstants.MagickBytes);
@ -171,7 +190,7 @@ internal sealed class ExrEncoderCore : IImageEncoderInternals
for (int x = 0; x < width; x++)
{
Vector4 vector4 = pixelRowSpan[x].ToVector4();
rgb.FromVector4(vector4);
Rgb96.FromVector4(vector4);
redBuffer[x] = rgb.R;
greenBuffer[x] = rgb.G;
@ -193,13 +212,13 @@ internal sealed class ExrEncoderCore : IImageEncoderInternals
private void WriteHeader(Stream stream, ExrHeaderAttributes header)
{
this.WriteChannels(stream, header.Channels);
this.WriteCompression(stream, header.Compression.Value);
this.WriteDataWindow(stream, header.DataWindow.Value);
this.WriteDisplayWindow(stream, header.DisplayWindow.Value);
this.WritePixelAspectRatio(stream, header.AspectRatio.Value);
this.WriteLineOrder(stream, header.LineOrder.Value);
this.WriteScreenWindowCenter(stream, header.ScreenWindowCenter.Value);
this.WriteScreenWindowWidth(stream, header.ScreenWindowWidth.Value);
this.WriteCompression(stream, header.Compression);
this.WriteDataWindow(stream, header.DataWindow);
this.WriteDisplayWindow(stream, header.DisplayWindow);
this.WritePixelAspectRatio(stream, header.AspectRatio);
this.WriteLineOrder(stream, header.LineOrder);
this.WriteScreenWindowCenter(stream, header.ScreenWindowCenter);
this.WriteScreenWindowWidth(stream, header.ScreenWindowWidth);
stream.WriteByte(0);
}

85
src/ImageSharp/Formats/OpenExr/ExrHeaderAttributes.cs

@ -7,70 +7,51 @@ namespace SixLabors.ImageSharp.Formats.OpenExr;
internal class ExrHeaderAttributes
{
public ExrHeaderAttributes(
IList<ExrChannelInfo> channels,
ExrCompressionType compression,
ExrBox2i dataWindow,
ExrBox2i displayWindow,
ExrLineOrder lineOrder,
float aspectRatio,
float screenWindowWidth,
PointF screenWindowCenter,
uint? tileXSize = null,
uint? tileYSize = null,
int? chunkCount = null)
{
this.Channels = channels;
this.Compression = compression;
this.DataWindow = dataWindow;
this.DisplayWindow = displayWindow;
this.LineOrder = lineOrder;
this.AspectRatio = aspectRatio;
this.ScreenWindowWidth = screenWindowWidth;
this.ScreenWindowCenter = screenWindowCenter;
this.TileXSize = tileXSize;
this.TileYSize = tileYSize;
this.ChunkCount = chunkCount;
}
public IList<ExrChannelInfo> Channels { get; set; }
public ExrCompressionType? Compression { get; set; }
public ExrCompressionType Compression { get; set; }
public ExrBox2i? DataWindow { get; set; }
public ExrBox2i DataWindow { get; set; }
public ExrBox2i? DisplayWindow { get; set; }
public ExrBox2i DisplayWindow { get; set; }
public ExrLineOrder? LineOrder { get; set; }
public ExrLineOrder LineOrder { get; set; }
public float? AspectRatio { get; set; }
public float AspectRatio { get; set; }
public float? ScreenWindowWidth { get; set; }
public float ScreenWindowWidth { get; set; }
public PointF? ScreenWindowCenter { get; set; }
public PointF ScreenWindowCenter { get; set; }
public uint? TileXSize { get; set; }
public uint? TileYSize { get; set; }
public int? ChunkCount { get; set; }
public bool IsValid()
{
if (!this.Compression.HasValue)
{
return false;
}
if (!this.DataWindow.HasValue)
{
return false;
}
if (!this.DisplayWindow.HasValue)
{
return false;
}
if (!this.LineOrder.HasValue)
{
return false;
}
if (!this.AspectRatio.HasValue)
{
return false;
}
if (!this.ScreenWindowWidth.HasValue)
{
return false;
}
if (!this.ScreenWindowCenter.HasValue)
{
return false;
}
if (this.Channels is null)
{
return false;
}
return true;
}
}

12
src/ImageSharp/Formats/OpenExr/ExrImageFormatDetector.cs

@ -2,6 +2,8 @@
// Licensed under the Six Labors Split License.
using System.Buffers.Binary;
using System.Diagnostics.CodeAnalysis;
using SixLabors.ImageSharp.Formats.Qoi;
namespace SixLabors.ImageSharp.Formats.OpenExr;
@ -13,9 +15,6 @@ public sealed class ExrImageFormatDetector : IImageFormatDetector
/// <inheritdoc/>
public int HeaderSize => 4;
/// <inheritdoc/>
public IImageFormat DetectFormat(ReadOnlySpan<byte> header) => this.IsSupportedFileFormat(header) ? ExrFormat.Instance : null;
private bool IsSupportedFileFormat(ReadOnlySpan<byte> header)
{
if (header.Length >= this.HeaderSize)
@ -26,4 +25,11 @@ public sealed class ExrImageFormatDetector : IImageFormatDetector
return false;
}
/// <inheritdoc/>
public bool TryDetectFormat(ReadOnlySpan<byte> header, [NotNullWhen(true)] out IImageFormat? format)
{
format = this.IsSupportedFileFormat(header) ? QoiFormat.Instance : null;
return format != null;
}
}

16
src/ImageSharp/Formats/OpenExr/ExrMetadata.cs

@ -1,12 +1,15 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Numerics;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.OpenExr;
/// <summary>
/// Provides OpenExr specific metadata information for the image.
/// </summary>
public class ExrMetadata : IDeepCloneable
public class ExrMetadata : IFormatMetadata<ExrMetadata>
{
/// <summary>
/// Initializes a new instance of the <see cref="ExrMetadata"/> class.
@ -26,6 +29,17 @@ public class ExrMetadata : IDeepCloneable
/// </summary>
public ExrPixelType PixelType { get; set; } = ExrPixelType.Float;
public static ExrMetadata FromFormatConnectingMetadata(FormatConnectingMetadata metadata) => throw new NotImplementedException();
public void AfterImageApply<TPixel>(Image<TPixel> destination, Matrix4x4 matrix)
where TPixel : unmanaged, IPixel<TPixel> => throw new NotImplementedException();
/// <inheritdoc/>
public IDeepCloneable DeepClone() => new ExrMetadata(this);
public PixelTypeInfo GetPixelTypeInfo() => throw new NotImplementedException();
public FormatConnectingMetadata ToFormatConnectingMetadata() => throw new NotImplementedException();
ExrMetadata IDeepCloneable<ExrMetadata>.DeepClone() => throw new NotImplementedException();
}

20
src/ImageSharp/Formats/OpenExr/MetadataExtensions.cs

@ -1,20 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.OpenExr;
using SixLabors.ImageSharp.Metadata;
namespace SixLabors.ImageSharp;
/// <summary>
/// Extension methods for the <see cref="ImageMetadata"/> type.
/// </summary>
public static partial class MetadataExtensions
{
/// <summary>
/// Gets the open exr format specific metadata for the image.
/// </summary>
/// <param name="metadata">The metadata this method extends.</param>
/// <returns>The <see cref="ExrMetadata"/>.</returns>
public static ExrMetadata GetExrMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(ExrFormat.Instance);
}

100
src/ImageSharp/Formats/_Generated/ImageExtensions.Save.cs

@ -7,13 +7,13 @@ using SixLabors.ImageSharp.Formats.Cur;
using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Formats.Ico;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.OpenExr;
using SixLabors.ImageSharp.Formats.Pbm;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Formats.Qoi;
using SixLabors.ImageSharp.Formats.Tga;
using SixLabors.ImageSharp.Formats.Tiff;
using SixLabors.ImageSharp.Formats.Webp;
using SixLabors.ImageSharp.Formats.OpenExr;
namespace SixLabors.ImageSharp;
@ -1144,24 +1144,95 @@ public static partial class ImageExtensions
encoder ?? source.Configuration.ImageFormatsManager.GetEncoder(WebpFormat.Instance),
cancellationToken);
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(TiffFormat.Instance),
cancellationToken);
/// <summary>
/// Saves the image to the given stream with the Exr format.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="path">The file path to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
public static void SaveAsExr(this Image source, string path) => SaveAsExr(source, path, default);
/// <summary>
/// Saves the image to the given stream with the Open Exr format.
/// Saves the image to the given stream with the Exr format.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</param>
/// <param name="path">The file path to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsExrAsync(this Image source, string path) => SaveAsExrAsync(source, path, default);
/// <summary>
/// Saves the image to the given stream with the Exr format.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="path">The file path to save the image to.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsExrAsync(this Image source, string path, CancellationToken cancellationToken)
=> SaveAsExrAsync(source, path, default, cancellationToken);
/// <summary>
/// Saves the image to the given stream with the Exr format.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="path">The file path to save the image to.</param>
/// <param name="encoder">The encoder to save the image with.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
public static void SaveAsExr(this Image source, string path, ExrEncoder encoder) =>
source.Save(
path,
encoder ?? source.Configuration.ImageFormatsManager.GetEncoder(ExrFormat.Instance));
/// <summary>
/// Saves the image to the given stream with the Exr format.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="path">The file path to save the image to.</param>
/// <param name="encoder">The encoder to save the image with.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsExrAsync(this Image source, string path, ExrEncoder encoder, CancellationToken cancellationToken = default)
=> source.SaveAsync(
path,
encoder ?? source.Configuration.ImageFormatsManager.GetEncoder(ExrFormat.Instance),
cancellationToken);
/// <summary>
/// Saves the image to the given stream with the Exr format.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAsExr(this Image source, Stream stream)
=> SaveAsExr(source, stream, default);
/// <summary>
/// Saves the image to the given stream with the Exr format.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static void SaveAsOpenExr(this Image source, Stream stream, ExrEncoder encoder)
public static Task SaveAsExrAsync(this Image source, Stream stream, CancellationToken cancellationToken = default)
=> SaveAsExrAsync(source, stream, default, cancellationToken);
/// <summary>
/// Saves the image to the given stream with the Exr format.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</param>
/// <param name="encoder">The encoder to save the image with.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAsExr(this Image source, Stream stream, ExrEncoder encoder)
=> source.Save(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(ExrFormat.Instance));
stream,
encoder ?? source.Configuration.ImageFormatsManager.GetEncoder(ExrFormat.Instance));
/// <summary>
/// Saves the image to the given stream with the Open Exr format.
/// Saves the image to the given stream with the Exr format.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</param>
@ -1169,11 +1240,10 @@ public static partial class ImageExtensions
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsOpenExrAsync(this Image source, Stream stream, ExrEncoder encoder, CancellationToken cancellationToken = default) =>
source.SaveAsync(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(ExrFormat.Instance),
cancellationToken);
public static Task SaveAsExrAsync(this Image source, Stream stream, ExrEncoder encoder, CancellationToken cancellationToken = default)
=> source.SaveAsync(
stream,
encoder ?? source.Configuration.ImageFormatsManager.GetEncoder(ExrFormat.Instance),
cancellationToken);
}

21
src/ImageSharp/Formats/_Generated/ImageMetadataExtensions.cs

@ -14,6 +14,7 @@ using SixLabors.ImageSharp.Formats.Qoi;
using SixLabors.ImageSharp.Formats.Tga;
using SixLabors.ImageSharp.Formats.Tiff;
using SixLabors.ImageSharp.Formats.Webp;
using SixLabors.ImageSharp.Formats.OpenExr;
namespace SixLabors.ImageSharp;
@ -242,6 +243,26 @@ public static class ImageMetadataExtensions
/// <returns>The new <see cref="WebpMetadata"/></returns>
public static WebpMetadata CloneWebpMetadata(this ImageMetadata source) => source.CloneFormatMetadata(WebpFormat.Instance);
/// <summary>
/// Gets the <see cref="ExrMetadata"/> from <paramref name="source"/>.<br/>
/// If none is found, an instance is created either by conversion from the decoded image format metadata
/// or the requested format default constructor.
/// This instance will be added to the metadata for future requests.
/// </summary>
/// <param name="source">The image metadata.</param>
/// <returns>
/// The <see cref="ExrMetadata"/>
/// </returns>
public static ExrMetadata GetExrMetadata(this ImageMetadata source) => source.GetFormatMetadata(ExrFormat.Instance);
/// <summary>
/// Creates a new cloned instance of <see cref="ExrMetadata"/> from the <paramref name="source"/>.
/// The instance is created via <see cref="GetExrMetadata(ImageMetadata)"/>
/// </summary>
/// <param name="source">The image metadata.</param>
/// <returns>The new <see cref="ExrMetadata"/></returns>
public static ExrMetadata CloneExrMetadata(this ImageMetadata source) => source.CloneFormatMetadata(ExrFormat.Instance);
/// <summary>
/// Gets the <see cref="CurFrameMetadata"/> from <paramref name="source"/>.<br/>

3
src/ImageSharp/Formats/_Generated/_Formats.ttinclude

@ -14,7 +14,8 @@
"Qoi",
"Tga",
"Tiff",
"Webp"
"Webp",
"Exr"
];
private static readonly string[] frameFormats = [

10
src/ImageSharp/ImageSharp.csproj

@ -56,6 +56,11 @@
<AutoGen>True</AutoGen>
<DependentUpon>InlineArray.tt</DependentUpon>
</Compile>
<Compile Update="Formats\_Generated\ImageExtensions.Save.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>ImageExtensions.Save.tt</DependentUpon>
</Compile>
<Compile Update="Formats\_Generated\ImageMetadataExtensions.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
@ -141,11 +146,6 @@
<AutoGen>True</AutoGen>
<DependentUpon>PorterDuffFunctions.Generated.tt</DependentUpon>
</Compile>
<Compile Update="Formats\_Generated\ImageExtensions.Save.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>ImageExtensions.Save.tt</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>

88
src/ImageSharp/PixelFormats/PixelImplementations/Rgb96.cs

@ -72,27 +72,10 @@ public partial struct Rgb96 : IPixel<Rgb96>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(Rgb96 left, Rgb96 right) => !left.Equals(right);
/// <inheritdoc />
public PixelOperations<Rgb96> CreatePixelOperations() => new();
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToScaledVector4() => this.ToVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FromVector4(Vector4 vector)
{
vector = Numerics.Clamp(vector, Vector4.Zero, Vector4.One);
this.R = (uint)(vector.X * Max);
this.G = (uint)(vector.Y * Max);
this.B = (uint)(vector.Z * Max);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToVector4() => new(
@ -103,71 +86,48 @@ public partial struct Rgb96 : IPixel<Rgb96>
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4());
public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4());
public bool Equals(Rgb96 other) => this.R.Equals(other.R) && this.G.Equals(other.G) && this.B.Equals(other.B);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4());
public override string ToString() => FormattableString.Invariant($"Rgb96({this.R}, {this.G}, {this.B})");
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4());
public static PixelOperations<Rgb96> CreatePixelOperations() => throw new NotImplementedException();
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4());
public static Rgb96 FromScaledVector4(Vector4 source) => throw new NotImplementedException();
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4());
public static Rgb96 FromVector4(Vector4 source) => throw new NotImplementedException();
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4());
public static Rgb96 FromAbgr32(Abgr32 source) => throw new NotImplementedException();
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4());
public static Rgb96 FromArgb32(Argb32 source) => throw new NotImplementedException();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4());
public static Rgb96 FromBgra5551(Bgra5551 source) => throw new NotImplementedException();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4());
public static Rgb96 FromBgr24(Bgr24 source) => throw new NotImplementedException();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4());
public static Rgb96 FromBgra32(Bgra32 source) => throw new NotImplementedException();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4());
public static Rgb96 FromL8(L8 source) => throw new NotImplementedException();
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4());
public static Rgb96 FromL16(L16 source) => throw new NotImplementedException();
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4());
public static Rgb96 FromLa16(La16 source) => throw new NotImplementedException();
/// <inheritdoc />
public override bool Equals(object obj) => obj is Rgb96 other && this.Equals(other);
public static Rgb96 FromLa32(La32 source) => throw new NotImplementedException();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B);
public static Rgb96 FromRgb24(Rgb24 source) => throw new NotImplementedException();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(Rgb96 other) => this.R.Equals(other.R) && this.G.Equals(other.G) && this.B.Equals(other.B);
public static Rgb96 FromRgba32(Rgba32 source) => throw new NotImplementedException();
/// <inheritdoc />
public override string ToString() => FormattableString.Invariant($"Rgb96({this.R}, {this.G}, {this.B})");
public static Rgb96 FromRgb48(Rgb48 source) => throw new NotImplementedException();
public static Rgb96 FromRgba64(Rgba64 source) => throw new NotImplementedException();
public static PixelTypeInfo GetPixelTypeInfo() => throw new NotImplementedException();
public Rgba32 ToRgba32() => throw new NotImplementedException();
}

95
src/ImageSharp/PixelFormats/PixelImplementations/Rgba128.cs

@ -79,28 +79,6 @@ public partial struct Rgba128 : IPixel<Rgba128>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(Rgba128 left, Rgba128 right) => !left.Equals(right);
/// <inheritdoc />
public PixelOperations<Rgba128> CreatePixelOperations() => new();
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToScaledVector4() => this.ToVector4();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FromVector4(Vector4 vector)
{
vector = Numerics.Clamp(vector, Vector4.Zero, Vector4.One);
this.R = (uint)(vector.X * Max);
this.G = (uint)(vector.Y * Max);
this.B = (uint)(vector.Z * Max);
this.A = (uint)(vector.W * Max);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToVector4() => new(
@ -111,71 +89,50 @@ public partial struct Rgba128 : IPixel<Rgba128>
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4());
public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B, this.A);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4());
public bool Equals(Rgba128 other) => this.R.Equals(other.R) && this.G.Equals(other.G) && this.B.Equals(other.B) && this.A.Equals(other.A);
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4());
public override string ToString() => FormattableString.Invariant($"Rgba128({this.R}, {this.G}, {this.B}, {this.A})");
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4());
public static PixelOperations<Rgba128> CreatePixelOperations() => throw new NotImplementedException();
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4());
public static Rgba128 FromScaledVector4(Vector4 source) => throw new NotImplementedException();
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4());
public static Rgba128 FromVector4(Vector4 source) => throw new NotImplementedException();
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4());
public static Rgba128 FromAbgr32(Abgr32 source) => throw new NotImplementedException();
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4());
public static Rgba128 FromArgb32(Argb32 source) => throw new NotImplementedException();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4());
public static Rgba128 FromBgra5551(Bgra5551 source) => throw new NotImplementedException();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4());
public static Rgba128 FromBgr24(Bgr24 source) => throw new NotImplementedException();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4());
public static Rgba128 FromBgra32(Bgra32 source) => throw new NotImplementedException();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4());
public static Rgba128 FromL8(L8 source) => throw new NotImplementedException();
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4());
public static Rgba128 FromL16(L16 source) => throw new NotImplementedException();
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4());
public static Rgba128 FromLa16(La16 source) => throw new NotImplementedException();
/// <inheritdoc />
public override bool Equals(object obj) => obj is Rgba128 other && this.Equals(other);
public static Rgba128 FromLa32(La32 source) => throw new NotImplementedException();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B, this.A);
public static Rgba128 FromRgb24(Rgb24 source) => throw new NotImplementedException();
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(Rgba128 other) => this.R.Equals(other.R) && this.G.Equals(other.G) && this.B.Equals(other.B) && this.A.Equals(other.A);
public static Rgba128 FromRgba32(Rgba32 source) => throw new NotImplementedException();
/// <inheritdoc />
public override string ToString() => FormattableString.Invariant($"Rgba128({this.R}, {this.G}, {this.B}, {this.A})");
public static Rgba128 FromRgb48(Rgb48 source) => throw new NotImplementedException();
public static Rgba128 FromRgba64(Rgba64 source) => throw new NotImplementedException();
public static PixelTypeInfo GetPixelTypeInfo() => throw new NotImplementedException();
public Rgba32 ToRgba32() => throw new NotImplementedException();
public Vector4 ToScaledVector4() => throw new NotImplementedException();
}

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

@ -10,14 +10,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Exr;
[ValidateDisposedMemoryAllocations]
public class ExrDecoderTests
{
private static ExrDecoder ExrDecoder => new();
[Theory]
[WithFile(TestImages.Exr.Uncompressed, PixelTypes.Rgba32)]
public void ExrDecoder_CanDecode_Uncompressed<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(ExrDecoder);
using Image<TPixel> image = provider.GetImage(ExrDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider);
}
@ -27,7 +25,7 @@ public class ExrDecoderTests
public void ExrDecoder_CanDecode_ZipCompressed<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(ExrDecoder);
using Image<TPixel> image = provider.GetImage(ExrDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider);
}
@ -37,7 +35,7 @@ public class ExrDecoderTests
public void ExrDecoder_CanDecode_ZipsCompressed<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(ExrDecoder);
using Image<TPixel> image = provider.GetImage(ExrDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider);
}
@ -47,7 +45,7 @@ public class ExrDecoderTests
public void ExrDecoder_CanDecode_RunLengthCompressed<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(ExrDecoder);
using Image<TPixel> image = provider.GetImage(ExrDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider);
}
@ -57,7 +55,7 @@ public class ExrDecoderTests
public void ExrDecoder_CanDecode_B44Compressed<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(ExrDecoder);
using Image<TPixel> image = provider.GetImage(ExrDecoder.Instance);
image.DebugSave(provider);
image.CompareToOriginal(provider);
}

108
tests/ImageSharp.Tests/Formats/Exr/ImageExtensionsTest.cs

@ -11,56 +11,126 @@ namespace SixLabors.ImageSharp.Tests.Formats.Exr;
public class ImageExtensionsTest
{
[Fact]
public void SaveAsExr_Stream()
public void SaveAsExr_Path()
{
using var memoryStream = new MemoryStream();
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensionsTest));
string file = Path.Combine(dir, "SaveAsExr_Path.exr");
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
image.SaveAsOpenExr(memoryStream, new ExrEncoder());
image.SaveAsExr(file);
}
memoryStream.Position = 0;
IImageFormat format = Image.DetectFormat(file);
Assert.True(format is ExrFormat);
}
[Fact]
public async Task SaveAsExrAsync_Path()
{
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensionsTest));
string file = Path.Combine(dir, "SaveAsExrAsync_Path.exr");
using (Image.Load(memoryStream, out IImageFormat mime))
using (Image<Rgba32> image = new(10, 10))
{
Assert.Equal("image/x-exr", mime.DefaultMimeType);
await image.SaveAsExrAsync(file);
}
IImageFormat format = Image.DetectFormat(file);
Assert.True(format is ExrFormat);
}
[Fact]
public void SaveAsExr_Stream_Encoder()
public void SaveAsExr_Path_Encoder()
{
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensions));
string file = Path.Combine(dir, "SaveAsExr_Path_Encoder.exr");
using (Image<Rgba32> image = new(10, 10))
{
image.SaveAsExr(file, new ExrEncoder());
}
IImageFormat format = Image.DetectFormat(file);
Assert.True(format is ExrFormat);
}
[Fact]
public async Task SaveAsExrAsync_Path_Encoder()
{
string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageExtensions));
string file = Path.Combine(dir, "SaveAsExrAsync_Path_Encoder.tiff");
using (Image<Rgba32> image = new(10, 10))
{
await image.SaveAsExrAsync(file, new ExrEncoder());
}
IImageFormat format = Image.DetectFormat(file);
Assert.True(format is ExrFormat);
}
[Fact]
public void SaveAsExr_Stream()
{
using var memoryStream = new MemoryStream();
using MemoryStream memoryStream = new();
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
image.SaveAsOpenExr(memoryStream, new ExrEncoder());
image.SaveAsExr(memoryStream);
}
memoryStream.Position = 0;
using (Image.Load(memoryStream, out IImageFormat mime))
IImageFormat format = Image.DetectFormat(memoryStream);
Assert.True(format is ExrFormat);
}
[Fact]
public async Task SaveAsExrAsync_StreamAsync()
{
using MemoryStream memoryStream = new();
using (Image<Rgba32> image = new(10, 10))
{
Assert.Equal("image/x-exr", mime.DefaultMimeType);
await image.SaveAsExrAsync(memoryStream);
}
memoryStream.Position = 0;
IImageFormat format = Image.DetectFormat(memoryStream);
Assert.True(format is ExrFormat);
}
[Fact]
public async Task SaveAsExrAsync_Stream_Encoder()
public void SaveAsExr_Stream_Encoder()
{
using var memoryStream = new MemoryStream();
using MemoryStream memoryStream = new();
using (var image = new Image<Rgba32>(10, 10))
using (Image<Rgba32> image = new(10, 10))
{
await image.SaveAsOpenExrAsync(memoryStream, new ExrEncoder());
image.SaveAsTiff(memoryStream, new ExrEncoder());
}
memoryStream.Position = 0;
using (Image.Load(memoryStream, out IImageFormat mime))
IImageFormat format = Image.DetectFormat(memoryStream);
Assert.True(format is ExrFormat);
}
[Fact]
public async Task SaveAsExrAsync_Stream_Encoder()
{
using MemoryStream memoryStream = new();
using (Image<Rgba32> image = new(10, 10))
{
Assert.Equal("image/x-exr", mime.DefaultMimeType);
await image.SaveAsExrAsync(memoryStream, new ExrEncoder());
}
memoryStream.Position = 0;
IImageFormat format = Image.DetectFormat(memoryStream);
Assert.True(format is ExrFormat);
}
}

2
tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs

@ -64,7 +64,7 @@ public static partial class TestEnvironment
new TgaConfigurationModule(),
new WebpConfigurationModule(),
new TiffConfigurationModule(),
new ExrConfigurationModule());
new ExrConfigurationModule(),
new QoiConfigurationModule());
IImageEncoder pngEncoder = IsWindows ? SystemDrawingReferenceEncoder.Png : new ImageSharpPngEncoderWithDefaultConfiguration();

Loading…
Cancel
Save