From fa52ab740fe97a6de382ee20792d2565ec36a512 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sat, 14 Mar 2026 18:05:55 +0100 Subject: [PATCH] Adjust exr decoder / encoder to ImageSharp changes --- ImageSharp.sln | 4 +- src/ImageSharp/Configuration.cs | 2 +- .../Decompressors/ZipExrCompression.cs | 2 +- .../Formats/OpenExr/ExrConfigurationModule.cs | 4 +- src/ImageSharp/Formats/OpenExr/ExrDecoder.cs | 36 +++--- .../Formats/OpenExr/ExrDecoderCore.cs | 86 +++++++------- .../Formats/OpenExr/ExrDecoderOptions.cs | 2 +- src/ImageSharp/Formats/OpenExr/ExrEncoder.cs | 22 +--- .../Formats/OpenExr/ExrEncoderCore.cs | 75 +++++++----- .../Formats/OpenExr/ExrHeaderAttributes.cs | 85 ++++++-------- .../Formats/OpenExr/ExrImageFormatDetector.cs | 12 +- src/ImageSharp/Formats/OpenExr/ExrMetadata.cs | 16 ++- .../Formats/OpenExr/MetadataExtensions.cs | 20 ---- .../_Generated/ImageExtensions.Save.cs | 100 +++++++++++++--- .../_Generated/ImageMetadataExtensions.cs | 21 ++++ .../Formats/_Generated/_Formats.ttinclude | 3 +- src/ImageSharp/ImageSharp.csproj | 10 +- .../PixelImplementations/Rgb96.cs | 88 ++++---------- .../PixelImplementations/Rgba128.cs | 95 +++++---------- .../Formats/Exr/ExrDecoderTests.cs | 12 +- .../Formats/Exr/ImageExtensionsTest.cs | 108 +++++++++++++++--- .../TestUtilities/TestEnvironment.Formats.cs | 2 +- 22 files changed, 440 insertions(+), 365 deletions(-) delete mode 100644 src/ImageSharp/Formats/OpenExr/MetadataExtensions.cs diff --git a/ImageSharp.sln b/ImageSharp.sln index 7ccd92c07d..13dd2fba7e 100644 --- a/ImageSharp.sln +++ b/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}" diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index abcbcb58f8..7a2374fb6a 100644 --- a/src/ImageSharp/Configuration.cs +++ b/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()); diff --git a/src/ImageSharp/Formats/OpenExr/Compression/Decompressors/ZipExrCompression.cs b/src/ImageSharp/Formats/OpenExr/Compression/Decompressors/ZipExrCompression.cs index e315af8e8f..1797bd44b3 100644 --- a/src/ImageSharp/Formats/OpenExr/Compression/Decompressors/ZipExrCompression.cs +++ b/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) diff --git a/src/ImageSharp/Formats/OpenExr/ExrConfigurationModule.cs b/src/ImageSharp/Formats/OpenExr/ExrConfigurationModule.cs index 0b0058805d..c0322728aa 100644 --- a/src/ImageSharp/Formats/OpenExr/ExrConfigurationModule.cs +++ b/src/ImageSharp/Formats/OpenExr/ExrConfigurationModule.cs @@ -6,13 +6,13 @@ namespace SixLabors.ImageSharp.Formats.OpenExr; /// /// Registers the image encoders, decoders and mime type detectors for the OpenExr format. /// -public sealed class ExrConfigurationModule : IConfigurationModule +public sealed class ExrConfigurationModule : IImageFormatConfigurationModule { /// 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()); } } diff --git a/src/ImageSharp/Formats/OpenExr/ExrDecoder.cs b/src/ImageSharp/Formats/OpenExr/ExrDecoder.cs index dcbe80c277..84d441eb72 100644 --- a/src/ImageSharp/Formats/OpenExr/ExrDecoder.cs +++ b/src/ImageSharp/Formats/OpenExr/ExrDecoder.cs @@ -8,39 +8,41 @@ namespace SixLabors.ImageSharp.Formats.OpenExr; /// /// Image decoder for generating an image out of a OpenExr stream. /// -public class ExrDecoder : IImageDecoderSpecialized +public class ExrDecoder : ImageDecoder { + private ExrDecoder() + { + } + + /// + /// Gets the shared instance. + /// + public static ExrDecoder Instance { get; } = new(); + /// - 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); } - /// - Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - => ((IImageDecoderSpecialized)this).Decode(new() { GeneralOptions = options }, stream, cancellationToken); - - /// - Image IImageDecoder.Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) - => ((IImageDecoderSpecialized)this).Decode(new() { GeneralOptions = options }, stream, cancellationToken); - - /// - Image IImageDecoderSpecialized.Decode(ExrDecoderOptions options, Stream stream, CancellationToken cancellationToken) + //// + protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) { Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); - Image image = new ExrDecoderCore(options).Decode(options.GeneralOptions.Configuration, stream, cancellationToken); + ExrDecoderCore decoder = new(new ExrDecoderOptions { GeneralOptions = options }); + Image image = decoder.Decode(options.Configuration, stream, cancellationToken); - ImageDecoderUtilities.Resize(options.GeneralOptions, image); + ScaleToTargetSize(options, image); return image; } /// - Image IImageDecoderSpecialized.Decode(ExrDecoderOptions options, Stream stream, CancellationToken cancellationToken) - => ((IImageDecoderSpecialized)this).Decode(options, stream, cancellationToken); + protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode(options, stream, cancellationToken); } diff --git a/src/ImageSharp/Formats/OpenExr/ExrDecoderCore.cs b/src/ImageSharp/Formats/OpenExr/ExrDecoderCore.cs index 3e554fb74d..6eaf56fde3 100644 --- a/src/ImageSharp/Formats/OpenExr/ExrDecoderCore.cs +++ b/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; /// /// Performs the OpenExr decoding operation. /// -internal sealed class ExrDecoderCore : IImageDecoderInternals +internal sealed class ExrDecoderCore : ImageDecoderCore { /// /// Reusable buffer. @@ -43,15 +44,12 @@ internal sealed class ExrDecoderCore : IImageDecoderInternals /// /// The options. public ExrDecoderCore(ExrDecoderOptions options) + : base(options.GeneralOptions) { - this.Options = options.GeneralOptions; this.configuration = options.GeneralOptions.Configuration; this.memoryAllocator = this.configuration.MemoryAllocator; } - /// - public DecoderOptions Options { get; } - /// /// Gets the dimensions of the image. /// @@ -84,8 +82,7 @@ internal sealed class ExrDecoderCore : IImageDecoderInternals private ExrHeaderAttributes HeaderAttributes { get; set; } /// - public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel + protected override Image Decode(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 } /// - 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 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 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; } diff --git a/src/ImageSharp/Formats/OpenExr/ExrDecoderOptions.cs b/src/ImageSharp/Formats/OpenExr/ExrDecoderOptions.cs index f29e9cfb92..c0b419b937 100644 --- a/src/ImageSharp/Formats/OpenExr/ExrDecoderOptions.cs +++ b/src/ImageSharp/Formats/OpenExr/ExrDecoderOptions.cs @@ -9,5 +9,5 @@ namespace SixLabors.ImageSharp.Formats.OpenExr; public sealed class ExrDecoderOptions : ISpecializedDecoderOptions { /// - public DecoderOptions GeneralOptions { get; set; } = new(); + public DecoderOptions GeneralOptions { get; init; } = new(); } diff --git a/src/ImageSharp/Formats/OpenExr/ExrEncoder.cs b/src/ImageSharp/Formats/OpenExr/ExrEncoder.cs index 54145afdac..9960efd686 100644 --- a/src/ImageSharp/Formats/OpenExr/ExrEncoder.cs +++ b/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; /// /// Image encoder for writing an image to a stream in the OpenExr Format. /// -public sealed class ExrEncoder : IImageEncoder, IExrEncoderOptions +public sealed class ExrEncoder : ImageEncoder { /// /// Gets or sets the pixel type of the image. /// public ExrPixelType? PixelType { get; set; } - /// - public void Encode(Image image, Stream stream) - where TPixel : unmanaged, IPixel - { - ExrEncoderCore encoder = new(this, image.GetMemoryAllocator()); - encoder.Encode(image, stream); - } - - /// - public Task EncodeAsync(Image image, Stream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel + /// + protected override void Encode(Image 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); } } diff --git a/src/ImageSharp/Formats/OpenExr/ExrEncoderCore.cs b/src/ImageSharp/Formats/OpenExr/ExrEncoderCore.cs index 21fb0ade49..740b71e12b 100644 --- a/src/ImageSharp/Formats/OpenExr/ExrEncoderCore.cs +++ b/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; /// /// Image encoder for writing an image to a stream in the OpenExr format. /// -internal sealed class ExrEncoderCore : IImageEncoderInternals +internal sealed class ExrEncoderCore { /// /// Reusable buffer. @@ -27,6 +28,16 @@ internal sealed class ExrEncoderCore : IImageEncoderInternals /// private readonly MemoryAllocator memoryAllocator; + /// + /// The global configuration. + /// + private Configuration configuration; + + /// + /// The encoder with options. + /// + private readonly ExrEncoder encoder; + /// /// The pixel type of the image. /// @@ -35,12 +46,14 @@ internal sealed class ExrEncoderCore : IImageEncoderInternals /// /// Initializes a new instance of the class. /// - /// The encoder options. + /// The encoder with options. + /// The configuration. /// The memory manager. - 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; } /// @@ -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() - { - 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 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); } diff --git a/src/ImageSharp/Formats/OpenExr/ExrHeaderAttributes.cs b/src/ImageSharp/Formats/OpenExr/ExrHeaderAttributes.cs index b3b3cdb604..4cec4fbc58 100644 --- a/src/ImageSharp/Formats/OpenExr/ExrHeaderAttributes.cs +++ b/src/ImageSharp/Formats/OpenExr/ExrHeaderAttributes.cs @@ -7,70 +7,51 @@ namespace SixLabors.ImageSharp.Formats.OpenExr; internal class ExrHeaderAttributes { + public ExrHeaderAttributes( + IList 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 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; - } } diff --git a/src/ImageSharp/Formats/OpenExr/ExrImageFormatDetector.cs b/src/ImageSharp/Formats/OpenExr/ExrImageFormatDetector.cs index c14ab947a9..2f97e74048 100644 --- a/src/ImageSharp/Formats/OpenExr/ExrImageFormatDetector.cs +++ b/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 /// public int HeaderSize => 4; - /// - public IImageFormat DetectFormat(ReadOnlySpan header) => this.IsSupportedFileFormat(header) ? ExrFormat.Instance : null; - private bool IsSupportedFileFormat(ReadOnlySpan header) { if (header.Length >= this.HeaderSize) @@ -26,4 +25,11 @@ public sealed class ExrImageFormatDetector : IImageFormatDetector return false; } + + /// + public bool TryDetectFormat(ReadOnlySpan header, [NotNullWhen(true)] out IImageFormat? format) + { + format = this.IsSupportedFileFormat(header) ? QoiFormat.Instance : null; + return format != null; + } } diff --git a/src/ImageSharp/Formats/OpenExr/ExrMetadata.cs b/src/ImageSharp/Formats/OpenExr/ExrMetadata.cs index 8b090d6842..9cb58e9e67 100644 --- a/src/ImageSharp/Formats/OpenExr/ExrMetadata.cs +++ b/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; /// /// Provides OpenExr specific metadata information for the image. /// -public class ExrMetadata : IDeepCloneable +public class ExrMetadata : IFormatMetadata { /// /// Initializes a new instance of the class. @@ -26,6 +29,17 @@ public class ExrMetadata : IDeepCloneable /// public ExrPixelType PixelType { get; set; } = ExrPixelType.Float; + public static ExrMetadata FromFormatConnectingMetadata(FormatConnectingMetadata metadata) => throw new NotImplementedException(); + + public void AfterImageApply(Image destination, Matrix4x4 matrix) + where TPixel : unmanaged, IPixel => throw new NotImplementedException(); + /// public IDeepCloneable DeepClone() => new ExrMetadata(this); + + public PixelTypeInfo GetPixelTypeInfo() => throw new NotImplementedException(); + + public FormatConnectingMetadata ToFormatConnectingMetadata() => throw new NotImplementedException(); + + ExrMetadata IDeepCloneable.DeepClone() => throw new NotImplementedException(); } diff --git a/src/ImageSharp/Formats/OpenExr/MetadataExtensions.cs b/src/ImageSharp/Formats/OpenExr/MetadataExtensions.cs deleted file mode 100644 index 2af0a57b07..0000000000 --- a/src/ImageSharp/Formats/OpenExr/MetadataExtensions.cs +++ /dev/null @@ -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; - -/// -/// Extension methods for the type. -/// -public static partial class MetadataExtensions -{ - /// - /// Gets the open exr format specific metadata for the image. - /// - /// The metadata this method extends. - /// The . - public static ExrMetadata GetExrMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(ExrFormat.Instance); -} diff --git a/src/ImageSharp/Formats/_Generated/ImageExtensions.Save.cs b/src/ImageSharp/Formats/_Generated/ImageExtensions.Save.cs index f3aaf6182b..ca4ad0e06c 100644 --- a/src/ImageSharp/Formats/_Generated/ImageExtensions.Save.cs +++ b/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); + /// + /// Saves the image to the given stream with the Exr format. + /// + /// The image this method extends. + /// The file path to save the image to. + /// Thrown if the path is null. + public static void SaveAsExr(this Image source, string path) => SaveAsExr(source, path, default); /// - /// Saves the image to the given stream with the Open Exr format. + /// Saves the image to the given stream with the Exr format. /// /// The image this method extends. - /// The stream to save the image to. + /// The file path to save the image to. + /// Thrown if the path is null. + /// A representing the asynchronous operation. + public static Task SaveAsExrAsync(this Image source, string path) => SaveAsExrAsync(source, path, default); + + /// + /// Saves the image to the given stream with the Exr format. + /// + /// The image this method extends. + /// The file path to save the image to. + /// The token to monitor for cancellation requests. + /// Thrown if the path is null. + /// A representing the asynchronous operation. + public static Task SaveAsExrAsync(this Image source, string path, CancellationToken cancellationToken) + => SaveAsExrAsync(source, path, default, cancellationToken); + + /// + /// Saves the image to the given stream with the Exr format. + /// + /// The image this method extends. + /// The file path to save the image to. /// The encoder to save the image with. + /// Thrown if the path is null. + public static void SaveAsExr(this Image source, string path, ExrEncoder encoder) => + source.Save( + path, + encoder ?? source.Configuration.ImageFormatsManager.GetEncoder(ExrFormat.Instance)); + + /// + /// Saves the image to the given stream with the Exr format. + /// + /// The image this method extends. + /// The file path to save the image to. + /// The encoder to save the image with. + /// The token to monitor for cancellation requests. + /// Thrown if the path is null. + /// A representing the asynchronous operation. + 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); + + /// + /// Saves the image to the given stream with the Exr format. + /// + /// The image this method extends. + /// The stream to save the image to. + /// Thrown if the stream is null. + public static void SaveAsExr(this Image source, Stream stream) + => SaveAsExr(source, stream, default); + + /// + /// Saves the image to the given stream with the Exr format. + /// + /// The image this method extends. + /// The stream to save the image to. + /// The token to monitor for cancellation requests. /// Thrown if the stream is null. /// A representing the asynchronous operation. - 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); + + /// + /// Saves the image to the given stream with the Exr format. + /// + /// The image this method extends. + /// The stream to save the image to. + /// The encoder to save the image with. + /// Thrown if the stream is null. + 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)); /// - /// Saves the image to the given stream with the Open Exr format. + /// Saves the image to the given stream with the Exr format. /// /// The image this method extends. /// The stream to save the image to. @@ -1169,11 +1240,10 @@ public static partial class ImageExtensions /// The token to monitor for cancellation requests. /// Thrown if the stream is null. /// A representing the asynchronous operation. - 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); } diff --git a/src/ImageSharp/Formats/_Generated/ImageMetadataExtensions.cs b/src/ImageSharp/Formats/_Generated/ImageMetadataExtensions.cs index e35d00ed39..31a25fcede 100644 --- a/src/ImageSharp/Formats/_Generated/ImageMetadataExtensions.cs +++ b/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 /// The new public static WebpMetadata CloneWebpMetadata(this ImageMetadata source) => source.CloneFormatMetadata(WebpFormat.Instance); + /// + /// Gets the from .
+ /// 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. + ///
+ /// The image metadata. + /// + /// The + /// + public static ExrMetadata GetExrMetadata(this ImageMetadata source) => source.GetFormatMetadata(ExrFormat.Instance); + + /// + /// Creates a new cloned instance of from the . + /// The instance is created via + /// + /// The image metadata. + /// The new + public static ExrMetadata CloneExrMetadata(this ImageMetadata source) => source.CloneFormatMetadata(ExrFormat.Instance); + /// /// Gets the from .
diff --git a/src/ImageSharp/Formats/_Generated/_Formats.ttinclude b/src/ImageSharp/Formats/_Generated/_Formats.ttinclude index 2d6129c4c0..c1c69c5b5b 100644 --- a/src/ImageSharp/Formats/_Generated/_Formats.ttinclude +++ b/src/ImageSharp/Formats/_Generated/_Formats.ttinclude @@ -14,7 +14,8 @@ "Qoi", "Tga", "Tiff", - "Webp" + "Webp", + "Exr" ]; private static readonly string[] frameFormats = [ diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index 2c7172387f..32a5f0073e 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -56,6 +56,11 @@ True InlineArray.tt + + True + True + ImageExtensions.Save.tt + True True @@ -141,11 +146,6 @@ True PorterDuffFunctions.Generated.tt - - True - True - ImageExtensions.Save.tt - diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb96.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb96.cs index 620e305a21..165f13b07e 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb96.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb96.cs @@ -72,27 +72,10 @@ public partial struct Rgb96 : IPixel [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Rgb96 left, Rgb96 right) => !left.Equals(right); - /// - public PixelOperations CreatePixelOperations() => new(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); - /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToScaledVector4() => this.ToVector4(); - /// - [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); - } - /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() => new( @@ -103,71 +86,48 @@ public partial struct Rgb96 : IPixel /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra5551(Bgra5551 source) => this.FromScaledVector4(source.ToScaledVector4()); + public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B); /// [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); /// - [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})"); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + public static PixelOperations CreatePixelOperations() => throw new NotImplementedException(); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); + public static Rgb96 FromScaledVector4(Vector4 source) => throw new NotImplementedException(); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + public static Rgb96 FromVector4(Vector4 source) => throw new NotImplementedException(); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + public static Rgb96 FromAbgr32(Abgr32 source) => throw new NotImplementedException(); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); + public static Rgb96 FromArgb32(Argb32 source) => throw new NotImplementedException(); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + public static Rgb96 FromBgra5551(Bgra5551 source) => throw new NotImplementedException(); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + public static Rgb96 FromBgr24(Bgr24 source) => throw new NotImplementedException(); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + public static Rgb96 FromBgra32(Bgra32 source) => throw new NotImplementedException(); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); + public static Rgb96 FromL8(L8 source) => throw new NotImplementedException(); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + public static Rgb96 FromL16(L16 source) => throw new NotImplementedException(); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + public static Rgb96 FromLa16(La16 source) => throw new NotImplementedException(); - /// - public override bool Equals(object obj) => obj is Rgb96 other && this.Equals(other); + public static Rgb96 FromLa32(La32 source) => throw new NotImplementedException(); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B); + public static Rgb96 FromRgb24(Rgb24 source) => throw new NotImplementedException(); - /// - [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(); - /// - 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(); } diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba128.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba128.cs index 912a62db0c..fd094be599 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba128.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba128.cs @@ -79,28 +79,6 @@ public partial struct Rgba128 : IPixel [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Rgba128 left, Rgba128 right) => !left.Equals(right); - /// - public PixelOperations CreatePixelOperations() => new(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() => this.ToVector4(); - - /// - [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); - } - /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() => new( @@ -111,71 +89,50 @@ public partial struct Rgba128 : IPixel /// [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); /// [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); /// - [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})"); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + public static PixelOperations CreatePixelOperations() => throw new NotImplementedException(); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL8(L8 source) => this.FromScaledVector4(source.ToScaledVector4()); + public static Rgba128 FromScaledVector4(Vector4 source) => throw new NotImplementedException(); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromL16(L16 source) => this.FromScaledVector4(source.ToScaledVector4()); + public static Rgba128 FromVector4(Vector4 source) => throw new NotImplementedException(); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa16(La16 source) => this.FromScaledVector4(source.ToScaledVector4()); + public static Rgba128 FromAbgr32(Abgr32 source) => throw new NotImplementedException(); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromLa32(La32 source) => this.FromScaledVector4(source.ToScaledVector4()); + public static Rgba128 FromArgb32(Argb32 source) => throw new NotImplementedException(); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + public static Rgba128 FromBgra5551(Bgra5551 source) => throw new NotImplementedException(); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + public static Rgba128 FromBgr24(Bgr24 source) => throw new NotImplementedException(); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromAbgr32(Abgr32 source) => this.FromScaledVector4(source.ToScaledVector4()); + public static Rgba128 FromBgra32(Bgra32 source) => throw new NotImplementedException(); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba32(ref Rgba32 dest) => dest.FromScaledVector4(this.ToScaledVector4()); + public static Rgba128 FromL8(L8 source) => throw new NotImplementedException(); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + public static Rgba128 FromL16(L16 source) => throw new NotImplementedException(); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + public static Rgba128 FromLa16(La16 source) => throw new NotImplementedException(); - /// - public override bool Equals(object obj) => obj is Rgba128 other && this.Equals(other); + public static Rgba128 FromLa32(La32 source) => throw new NotImplementedException(); - /// - [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(); - /// - [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(); - /// - 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(); } diff --git a/tests/ImageSharp.Tests/Formats/Exr/ExrDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Exr/ExrDecoderTests.cs index 29b75bc9cd..0391ab7c8c 100644 --- a/tests/ImageSharp.Tests/Formats/Exr/ExrDecoderTests.cs +++ b/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(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(ExrDecoder); + using Image image = provider.GetImage(ExrDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider); } @@ -27,7 +25,7 @@ public class ExrDecoderTests public void ExrDecoder_CanDecode_ZipCompressed(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(ExrDecoder); + using Image image = provider.GetImage(ExrDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider); } @@ -37,7 +35,7 @@ public class ExrDecoderTests public void ExrDecoder_CanDecode_ZipsCompressed(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(ExrDecoder); + using Image image = provider.GetImage(ExrDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider); } @@ -47,7 +45,7 @@ public class ExrDecoderTests public void ExrDecoder_CanDecode_RunLengthCompressed(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(ExrDecoder); + using Image image = provider.GetImage(ExrDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider); } @@ -57,7 +55,7 @@ public class ExrDecoderTests public void ExrDecoder_CanDecode_B44Compressed(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(ExrDecoder); + using Image image = provider.GetImage(ExrDecoder.Instance); image.DebugSave(provider); image.CompareToOriginal(provider); } diff --git a/tests/ImageSharp.Tests/Formats/Exr/ImageExtensionsTest.cs b/tests/ImageSharp.Tests/Formats/Exr/ImageExtensionsTest.cs index 926f5ca909..78ce845d36 100644 --- a/tests/ImageSharp.Tests/Formats/Exr/ImageExtensionsTest.cs +++ b/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(10, 10)) + using (Image 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 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 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 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(10, 10)) + using (Image 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 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(10, 10)) + using (Image 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 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); } } diff --git a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs index 4dc3def1f8..9ced51608b 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs +++ b/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();