From 9a0e289d25f54ef89dfea1e85ca8d673a745a70c Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 13 Jun 2024 11:01:35 +1000 Subject: [PATCH] Complete conversion APIs --- src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs | 1 - src/ImageSharp/Formats/Bmp/BmpMetadata.cs | 10 +- .../Formats/Bmp/MetadataExtensions.cs | 20 -- src/ImageSharp/Formats/EncodingType.cs | 20 ++ .../Formats/FormatConnectingMetadata.cs | 7 +- src/ImageSharp/Formats/Gif/GifEncoderCore.cs | 49 +-- src/ImageSharp/Formats/Gif/GifMetadata.cs | 28 -- .../Formats/Gif/MetadataExtensions.cs | 97 ------ .../Formats/IFormatFrameMetadata.cs | 2 +- src/ImageSharp/Formats/IFormatMetadata.cs | 2 +- src/ImageSharp/Formats/ImageDecoder.cs | 21 +- src/ImageSharp/Formats/Jpeg/JpegMetadata.cs | 3 +- .../Formats/Jpeg/MetadataExtensions.cs | 21 -- .../Formats/Pbm/IPbmEncoderOptions.cs | 25 -- .../Formats/Pbm/MetadataExtensions.cs | 20 -- src/ImageSharp/Formats/Pbm/PbmEncoder.cs | 2 - src/ImageSharp/Formats/Pbm/PbmEncoderCore.cs | 1 - src/ImageSharp/Formats/Pbm/PbmMetadata.cs | 2 +- .../Formats/Png/MetadataExtensions.cs | 84 ------ src/ImageSharp/Formats/Png/PngDecoder.cs | 2 +- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 56 +--- .../Formats/Png/PngFrameMetadata.cs | 24 +- src/ImageSharp/Formats/Png/PngMetadata.cs | 35 +-- .../Formats/Qoi/MetadataExtensions.cs | 20 -- src/ImageSharp/Formats/Qoi/QoiMetadata.cs | 2 +- .../Formats/Tga/MetadataExtensions.cs | 20 -- .../Formats/Tiff/MetadataExtensions.cs | 27 -- .../Formats/Tiff/TiffEncoderCore.cs | 1 - .../Formats/Webp/Chunks/WebpFrameData.cs | 20 +- .../Formats/Webp/Lossless/Vp8LEncoder.cs | 2 +- .../Formats/Webp/Lossy/Vp8Encoder.cs | 6 +- .../Formats/Webp/MetadataExtensions.cs | 75 ----- .../Formats/Webp/WebpAnimationDecoder.cs | 4 +- .../Formats/Webp/WebpBlendMethod.cs | 22 -- .../Formats/Webp/WebpCommonUtils.cs | 50 ---- .../Formats/Webp/WebpDisposalMethod.cs | 20 -- .../Formats/Webp/WebpEncoderCore.cs | 23 +- .../Formats/Webp/WebpFileFormatType.cs | 4 +- .../Formats/Webp/WebpFrameMetadata.cs | 37 ++- src/ImageSharp/Formats/Webp/WebpMetadata.cs | 17 +- .../{ => _Generated}/ImageExtensions.Save.cs | 2 - .../{ => _Generated}/ImageExtensions.Save.tt | 19 +- .../_Generated/ImageMetadataExtensions.cs | 283 ++++++++++++++++++ .../_Generated/ImageMetadataExtensions.tt | 77 +++++ .../Formats/_Generated/_Formats.ttinclude | 24 ++ src/ImageSharp/ImageSharp.csproj | 13 +- src/ImageSharp/Metadata/ImageFrameMetadata.cs | 59 ++-- src/ImageSharp/Metadata/ImageMetadata.cs | 52 ++-- src/ImageSharp/PixelFormats/PixelTypeInfo.cs | 4 +- .../Formats/Bmp/BmpMetadataTests.cs | 2 +- .../Formats/Gif/GifEncoderTests.cs | 26 +- .../Formats/Pbm/PbmMetadataTests.cs | 1 - .../Formats/Png/PngEncoderTests.cs | 8 +- .../Formats/WebP/WebpEncoderTests.cs | 28 +- 54 files changed, 606 insertions(+), 874 deletions(-) delete mode 100644 src/ImageSharp/Formats/Bmp/MetadataExtensions.cs create mode 100644 src/ImageSharp/Formats/EncodingType.cs delete mode 100644 src/ImageSharp/Formats/Gif/MetadataExtensions.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/MetadataExtensions.cs delete mode 100644 src/ImageSharp/Formats/Pbm/IPbmEncoderOptions.cs delete mode 100644 src/ImageSharp/Formats/Pbm/MetadataExtensions.cs delete mode 100644 src/ImageSharp/Formats/Png/MetadataExtensions.cs delete mode 100644 src/ImageSharp/Formats/Qoi/MetadataExtensions.cs delete mode 100644 src/ImageSharp/Formats/Tga/MetadataExtensions.cs delete mode 100644 src/ImageSharp/Formats/Tiff/MetadataExtensions.cs delete mode 100644 src/ImageSharp/Formats/Webp/MetadataExtensions.cs delete mode 100644 src/ImageSharp/Formats/Webp/WebpBlendMethod.cs delete mode 100644 src/ImageSharp/Formats/Webp/WebpDisposalMethod.cs rename src/ImageSharp/Formats/{ => _Generated}/ImageExtensions.Save.cs (99%) rename src/ImageSharp/Formats/{ => _Generated}/ImageExtensions.Save.tt (95%) create mode 100644 src/ImageSharp/Formats/_Generated/ImageMetadataExtensions.cs create mode 100644 src/ImageSharp/Formats/_Generated/ImageMetadataExtensions.tt create mode 100644 src/ImageSharp/Formats/_Generated/_Formats.ttinclude diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index 24a4fa2f57..298bb8dbca 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -4,7 +4,6 @@ using System.Buffers; using System.Buffers.Binary; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; diff --git a/src/ImageSharp/Formats/Bmp/BmpMetadata.cs b/src/ImageSharp/Formats/Bmp/BmpMetadata.cs index bad47bd0d5..c19ab44cf7 100644 --- a/src/ImageSharp/Formats/Bmp/BmpMetadata.cs +++ b/src/ImageSharp/Formats/Bmp/BmpMetadata.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp; /// /// Provides Bmp specific metadata information for the image. /// -public class BmpMetadata : IFormatMetadata, IFormatFrameMetadata +public class BmpMetadata : IFormatMetadata { /// /// Initializes a new instance of the class. @@ -66,10 +66,6 @@ public class BmpMetadata : IFormatMetadata, IFormatFrameMetadata - public static BmpMetadata FromFormatConnectingFrameMetadata(FormatConnectingFrameMetadata metadata) - => new(); - /// public PixelTypeInfo GetPixelTypeInfo() { @@ -140,10 +136,6 @@ public class BmpMetadata : IFormatMetadata, IFormatFrameMetadata - public FormatConnectingFrameMetadata ToFormatConnectingFrameMetadata() - => new(); - /// IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); diff --git a/src/ImageSharp/Formats/Bmp/MetadataExtensions.cs b/src/ImageSharp/Formats/Bmp/MetadataExtensions.cs deleted file mode 100644 index 5297d0c989..0000000000 --- a/src/ImageSharp/Formats/Bmp/MetadataExtensions.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.Formats.Bmp; -using SixLabors.ImageSharp.Metadata; - -namespace SixLabors.ImageSharp; - -/// -/// Extension methods for the type. -/// -public static partial class MetadataExtensions -{ - /// - /// Gets the bmp format specific metadata for the image. - /// - /// The metadata this method extends. - /// The . - public static BmpMetadata GetBmpMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(BmpFormat.Instance); -} diff --git a/src/ImageSharp/Formats/EncodingType.cs b/src/ImageSharp/Formats/EncodingType.cs new file mode 100644 index 0000000000..f4567ca43d --- /dev/null +++ b/src/ImageSharp/Formats/EncodingType.cs @@ -0,0 +1,20 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats; + +/// +/// Provides a way to specify the type of encoding to be used. +/// +public enum EncodingType +{ + /// + /// Lossless encoding, which compresses data without any loss of information. + /// + Lossless, + + /// + /// Lossy encoding, which compresses data by discarding some of it. + /// + Lossy +} diff --git a/src/ImageSharp/Formats/FormatConnectingMetadata.cs b/src/ImageSharp/Formats/FormatConnectingMetadata.cs index 2acbe96990..07579c09e8 100644 --- a/src/ImageSharp/Formats/FormatConnectingMetadata.cs +++ b/src/ImageSharp/Formats/FormatConnectingMetadata.cs @@ -11,7 +11,12 @@ namespace SixLabors.ImageSharp.Formats; public class FormatConnectingMetadata { /// - /// Gets the quality. + /// Gets the encoding type. + /// + public EncodingType EncodingType { get; init; } + + /// + /// Gets the quality to use when is . /// /// /// The value is usually between 1 and 100. Defaults to 100. diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs index 5fd20d6ae2..90177ccc1b 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs @@ -5,8 +5,6 @@ using System.Buffers; using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Formats.Png; -using SixLabors.ImageSharp.Formats.Webp; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Xmp; @@ -85,7 +83,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); - GifMetadata gifMetadata = GetGifMetadata(image); + GifMetadata gifMetadata = image.Metadata.CloneGifMetadata(); this.colorTableMode ??= gifMetadata.ColorTableMode; bool useGlobalTable = this.colorTableMode == FrameColorTableMode.Global; @@ -178,56 +176,17 @@ internal sealed class GifEncoderCore : IImageEncoderInternals quantized?.Dispose(); } - private static GifMetadata GetGifMetadata(Image image) - where TPixel : unmanaged, IPixel - { - if (image.Metadata.TryGetGifMetadata(out GifMetadata? gif)) - { - return gif.DeepClone(); - } - - if (image.Metadata.TryGetPngMetadata(out PngMetadata? png)) - { - AnimatedImageMetadata ani = png.ToAnimatedImageMetadata(); - return GifMetadata.FromAnimatedMetadata(ani); - } - - if (image.Metadata.TryGetWebpMetadata(out WebpMetadata? webp)) - { - AnimatedImageMetadata ani = webp.ToAnimatedImageMetadata(); - return GifMetadata.FromAnimatedMetadata(ani); - } - - // Return explicit new instance so we do not mutate the original metadata. - return new(); - } - private static GifFrameMetadata GetGifFrameMetadata(ImageFrame frame, int transparencyIndex) where TPixel : unmanaged, IPixel { - GifFrameMetadata? metadata = null; - if (frame.Metadata.TryGetGifMetadata(out GifFrameMetadata? gif)) - { - metadata = gif.DeepClone(); - } - else if (frame.Metadata.TryGetPngMetadata(out PngFrameMetadata? png)) - { - AnimatedImageFrameMetadata ani = png.ToAnimatedImageFrameMetadata(); - metadata = GifFrameMetadata.FromAnimatedMetadata(ani); - } - else if (frame.Metadata.TryGetWebpFrameMetadata(out WebpFrameMetadata? webp)) - { - AnimatedImageFrameMetadata ani = webp.ToAnimatedImageFrameMetadata(); - metadata = GifFrameMetadata.FromAnimatedMetadata(ani); - } - - if (metadata?.ColorTableMode == FrameColorTableMode.Global && transparencyIndex > -1) + GifFrameMetadata metadata = frame.Metadata.CloneGifMetadata(); + if (metadata.ColorTableMode == FrameColorTableMode.Global && transparencyIndex > -1) { metadata.HasTransparency = true; metadata.TransparencyIndex = ClampIndex(transparencyIndex); } - return metadata ?? new(); + return metadata; } private void EncodeAdditionalFrames( diff --git a/src/ImageSharp/Formats/Gif/GifMetadata.cs b/src/ImageSharp/Formats/Gif/GifMetadata.cs index 90d3312c85..565038b55a 100644 --- a/src/ImageSharp/Formats/Gif/GifMetadata.cs +++ b/src/ImageSharp/Formats/Gif/GifMetadata.cs @@ -69,34 +69,6 @@ public class GifMetadata : IFormatMetadata /// public IList Comments { get; set; } = []; - internal static GifMetadata FromAnimatedMetadata(AnimatedImageMetadata metadata) - { - int index = 0; - Color background = metadata.BackgroundColor; - if (metadata.ColorTable.HasValue) - { - ReadOnlySpan colorTable = metadata.ColorTable.Value.Span; - for (int i = 0; i < colorTable.Length; i++) - { - if (background != colorTable[i]) - { - continue; - } - - index = i; - break; - } - } - - return new() - { - GlobalColorTable = metadata.ColorTable, - ColorTableMode = metadata.ColorTableMode, - RepeatCount = metadata.RepeatCount, - BackgroundColorIndex = (byte)Numerics.Clamp(index, 0, 255), - }; - } - /// public static GifMetadata FromFormatConnectingMetadata(FormatConnectingMetadata metadata) { diff --git a/src/ImageSharp/Formats/Gif/MetadataExtensions.cs b/src/ImageSharp/Formats/Gif/MetadataExtensions.cs deleted file mode 100644 index 0f2d281f16..0000000000 --- a/src/ImageSharp/Formats/Gif/MetadataExtensions.cs +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Diagnostics.CodeAnalysis; -using SixLabors.ImageSharp.Formats; -using SixLabors.ImageSharp.Formats.Gif; -using SixLabors.ImageSharp.Metadata; - -namespace SixLabors.ImageSharp; - -/// -/// Extension methods for the type. -/// -public static partial class MetadataExtensions -{ - /// - /// Gets the gif format specific metadata for the image. - /// - /// The metadata this method extends. - /// The . - public static GifMetadata GetGifMetadata(this ImageMetadata source) - => source.GetFormatMetadata(GifFormat.Instance); - - /// - /// Gets the gif format specific metadata for the image. - /// - /// The metadata this method extends. - /// - /// When this method returns, contains the metadata associated with the specified image, - /// if found; otherwise, the default value for the type of the metadata parameter. - /// This parameter is passed uninitialized. - /// - /// - /// if the gif metadata exists; otherwise, . - /// - public static bool TryGetGifMetadata(this ImageMetadata source, [NotNullWhen(true)] out GifMetadata? metadata) - => source.TryGetFormatMetadata(GifFormat.Instance, out metadata); - - /// - /// Gets the gif format specific metadata for the image frame. - /// - /// The metadata this method extends. - /// The . - public static GifFrameMetadata GetGifMetadata(this ImageFrameMetadata source) - => source.GetFormatMetadata(GifFormat.Instance); - - /// - /// Gets the gif format specific metadata for the image frame. - /// - /// The metadata this method extends. - /// - /// When this method returns, contains the metadata associated with the specified frame, - /// if found; otherwise, the default value for the type of the metadata parameter. - /// This parameter is passed uninitialized. - /// - /// - /// if the gif frame metadata exists; otherwise, . - /// - public static bool TryGetGifMetadata(this ImageFrameMetadata source, [NotNullWhen(true)] out GifFrameMetadata? metadata) - => source.TryGetFormatMetadata(GifFormat.Instance, out metadata); - - internal static AnimatedImageMetadata ToAnimatedImageMetadata(this GifMetadata source) - { - Color background = Color.Transparent; - if (source.GlobalColorTable != null) - { - background = source.GlobalColorTable.Value.Span[source.BackgroundColorIndex]; - } - - return new() - { - ColorTable = source.GlobalColorTable, - ColorTableMode = source.ColorTableMode, - RepeatCount = source.RepeatCount, - BackgroundColor = background, - }; - } - - internal static AnimatedImageFrameMetadata ToAnimatedImageFrameMetadata(this GifFrameMetadata source) - { - // For most scenarios we would consider the blend method to be 'Over' however if a frame has a disposal method of 'RestoreToBackground' or - // has a local palette with 256 colors and is not transparent we should use 'Source'. - bool blendSource = source.DisposalMode == FrameDisposalMode.RestoreToBackground || (source.LocalColorTable?.Length == 256 && !source.HasTransparency); - - // If the color table is global and frame has no transparency. Consider it 'Source' also. - blendSource |= source.ColorTableMode == FrameColorTableMode.Global && !source.HasTransparency; - - return new() - { - ColorTable = source.LocalColorTable, - ColorTableMode = source.ColorTableMode, - Duration = TimeSpan.FromMilliseconds(source.FrameDelay * 10), - DisposalMode = source.DisposalMode, - BlendMode = blendSource ? FrameBlendMode.Source : FrameBlendMode.Over, - }; - } -} diff --git a/src/ImageSharp/Formats/IFormatFrameMetadata.cs b/src/ImageSharp/Formats/IFormatFrameMetadata.cs index 4c5321073d..4eef93ad34 100644 --- a/src/ImageSharp/Formats/IFormatFrameMetadata.cs +++ b/src/ImageSharp/Formats/IFormatFrameMetadata.cs @@ -20,7 +20,7 @@ public interface IFormatFrameMetadata : IDeepCloneable /// /// The metadata type implementing this interface. public interface IFormatFrameMetadata : IFormatFrameMetadata, IDeepCloneable - where TSelf : class, IFormatFrameMetadata, new() + where TSelf : class, IFormatFrameMetadata { /// /// Creates a new instance of the class from the given . diff --git a/src/ImageSharp/Formats/IFormatMetadata.cs b/src/ImageSharp/Formats/IFormatMetadata.cs index e0c32e8b26..8d695306e4 100644 --- a/src/ImageSharp/Formats/IFormatMetadata.cs +++ b/src/ImageSharp/Formats/IFormatMetadata.cs @@ -28,7 +28,7 @@ public interface IFormatMetadata : IDeepCloneable /// /// The metadata type implementing this interface. public interface IFormatMetadata : IFormatMetadata, IDeepCloneable - where TSelf : class, IFormatMetadata, new() + where TSelf : class, IFormatMetadata { /// /// Creates a new instance of the class from the given . diff --git a/src/ImageSharp/Formats/ImageDecoder.cs b/src/ImageSharp/Formats/ImageDecoder.cs index ebb45d7013..4e79f79efc 100644 --- a/src/ImageSharp/Formats/ImageDecoder.cs +++ b/src/ImageSharp/Formats/ImageDecoder.cs @@ -2,6 +2,7 @@ // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.IO; +using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; @@ -231,7 +232,7 @@ public abstract class ImageDecoder : IImageDecoder throw new NotSupportedException("Cannot read from the stream."); } - Task PeformActionAndResetPosition(Stream s, long position, CancellationToken ct) + Task PerformActionAndResetPosition(Stream s, long position, CancellationToken ct) { try { @@ -263,15 +264,15 @@ public abstract class ImageDecoder : IImageDecoder // code below to copy the stream to an in-memory buffer before invoking the action. if (stream is MemoryStream ms) { - return PeformActionAndResetPosition(ms, ms.Position, cancellationToken); + return PerformActionAndResetPosition(ms, ms.Position, cancellationToken); } if (stream is ChunkedMemoryStream cms) { - return PeformActionAndResetPosition(cms, cms.Position, cancellationToken); + return PerformActionAndResetPosition(cms, cms.Position, cancellationToken); } - return CopyToMemoryStreamAndActionAsync(options, stream, PeformActionAndResetPosition, cancellationToken); + return CopyToMemoryStreamAndActionAsync(options, stream, PerformActionAndResetPosition, cancellationToken); } private static async Task CopyToMemoryStreamAndActionAsync( @@ -282,7 +283,7 @@ public abstract class ImageDecoder : IImageDecoder { long position = stream.CanSeek ? stream.Position : 0; Configuration configuration = options.Configuration; - using ChunkedMemoryStream memoryStream = new(configuration.MemoryAllocator); + await using ChunkedMemoryStream memoryStream = new(configuration.MemoryAllocator); await stream.CopyToAsync(memoryStream, configuration.StreamProcessingBufferSize, cancellationToken).ConfigureAwait(false); memoryStream.Position = 0; return await action(memoryStream, position, cancellationToken).ConfigureAwait(false); @@ -293,6 +294,11 @@ public abstract class ImageDecoder : IImageDecoder if (configuration.ImageFormatsManager.TryFindFormatByDecoder(this, out IImageFormat? format)) { image.Metadata.DecodedImageFormat = format; + + foreach (ImageFrame frame in image.Frames) + { + frame.Metadata.DecodedImageFormat = format; + } } } @@ -301,6 +307,11 @@ public abstract class ImageDecoder : IImageDecoder if (configuration.ImageFormatsManager.TryFindFormatByDecoder(this, out IImageFormat? format)) { info.Metadata.DecodedImageFormat = format; + + foreach (ImageFrameMetadata frame in info.FrameMetadataCollection) + { + frame.DecodedImageFormat = format; + } } } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs b/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs index 8b123fa35d..f2f34ec496 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs @@ -112,7 +112,7 @@ public class JpegMetadata : IFormatMetadata public static JpegMetadata FromFormatConnectingMetadata(FormatConnectingMetadata metadata) { JpegColorType color; - PixelColorType colorType = metadata.PixelTypeInfo.ColorType ?? PixelColorType.YCbCr; + PixelColorType colorType = metadata.PixelTypeInfo.ColorType; switch (colorType) { case PixelColorType.Luminance: @@ -194,6 +194,7 @@ public class JpegMetadata : IFormatMetadata public FormatConnectingMetadata ToFormatConnectingMetadata() => new() { + EncodingType = EncodingType.Lossy, PixelTypeInfo = this.GetPixelTypeInfo(), Quality = this.Quality, }; diff --git a/src/ImageSharp/Formats/Jpeg/MetadataExtensions.cs b/src/ImageSharp/Formats/Jpeg/MetadataExtensions.cs deleted file mode 100644 index 7330e74b79..0000000000 --- a/src/ImageSharp/Formats/Jpeg/MetadataExtensions.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Text; -using SixLabors.ImageSharp.Formats.Jpeg; -using SixLabors.ImageSharp.Metadata; - -namespace SixLabors.ImageSharp; - -/// -/// Extension methods for the type. -/// -public static partial class MetadataExtensions -{ - /// - /// Gets the jpeg format specific metadata for the image. - /// - /// The metadata this method extends. - /// The . - public static JpegMetadata GetJpegMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(JpegFormat.Instance); -} diff --git a/src/ImageSharp/Formats/Pbm/IPbmEncoderOptions.cs b/src/ImageSharp/Formats/Pbm/IPbmEncoderOptions.cs deleted file mode 100644 index 7039ef2620..0000000000 --- a/src/ImageSharp/Formats/Pbm/IPbmEncoderOptions.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Pbm; - -/// -/// Configuration options for use during PBM encoding. -/// -internal interface IPbmEncoderOptions -{ - /// - /// Gets the encoding of the pixels. - /// - PbmEncoding? Encoding { get; } - - /// - /// Gets the Color type of the resulting image. - /// - PbmColorType? ColorType { get; } - - /// - /// Gets the Data Type of the pixel components. - /// - PbmComponentType? ComponentType { get; } -} diff --git a/src/ImageSharp/Formats/Pbm/MetadataExtensions.cs b/src/ImageSharp/Formats/Pbm/MetadataExtensions.cs deleted file mode 100644 index 6d44e91a50..0000000000 --- a/src/ImageSharp/Formats/Pbm/MetadataExtensions.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.Formats.Pbm; -using SixLabors.ImageSharp.Metadata; - -namespace SixLabors.ImageSharp; - -/// -/// Extension methods for the type. -/// -public static partial class MetadataExtensions -{ - /// - /// Gets the pbm format specific metadata for the image. - /// - /// The metadata this method extends. - /// The . - public static PbmMetadata GetPbmMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(PbmFormat.Instance); -} diff --git a/src/ImageSharp/Formats/Pbm/PbmEncoder.cs b/src/ImageSharp/Formats/Pbm/PbmEncoder.cs index 8258c91655..f7a9d79366 100644 --- a/src/ImageSharp/Formats/Pbm/PbmEncoder.cs +++ b/src/ImageSharp/Formats/Pbm/PbmEncoder.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Advanced; - namespace SixLabors.ImageSharp.Formats.Pbm; /// diff --git a/src/ImageSharp/Formats/Pbm/PbmEncoderCore.cs b/src/ImageSharp/Formats/Pbm/PbmEncoderCore.cs index b6e31a3c28..5ae37d4e69 100644 --- a/src/ImageSharp/Formats/Pbm/PbmEncoderCore.cs +++ b/src/ImageSharp/Formats/Pbm/PbmEncoderCore.cs @@ -2,7 +2,6 @@ // Licensed under the Six Labors Split License. using System.Buffers.Text; -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Pbm; diff --git a/src/ImageSharp/Formats/Pbm/PbmMetadata.cs b/src/ImageSharp/Formats/Pbm/PbmMetadata.cs index 49a36de41e..9b23aecac2 100644 --- a/src/ImageSharp/Formats/Pbm/PbmMetadata.cs +++ b/src/ImageSharp/Formats/Pbm/PbmMetadata.cs @@ -46,7 +46,7 @@ public class PbmMetadata : IFormatMetadata public static PbmMetadata FromFormatConnectingMetadata(FormatConnectingMetadata metadata) { PbmColorType color; - PixelColorType colorType = metadata.PixelTypeInfo.ColorType ?? PixelColorType.Luminance; + PixelColorType colorType = metadata.PixelTypeInfo.ColorType; switch (colorType) { diff --git a/src/ImageSharp/Formats/Png/MetadataExtensions.cs b/src/ImageSharp/Formats/Png/MetadataExtensions.cs deleted file mode 100644 index 6da1fb5fdb..0000000000 --- a/src/ImageSharp/Formats/Png/MetadataExtensions.cs +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Diagnostics.CodeAnalysis; -using SixLabors.ImageSharp.Formats; -using SixLabors.ImageSharp.Formats.Png; -using SixLabors.ImageSharp.Metadata; - -namespace SixLabors.ImageSharp; - -/// -/// Extension methods for the type. -/// -public static partial class MetadataExtensions -{ - /// - /// Gets the png format specific metadata for the image. - /// - /// The metadata this method extends. - /// The . - public static PngMetadata GetPngMetadata(this ImageMetadata source) => source.GetFormatMetadata(PngFormat.Instance); - - /// - /// Gets the png format specific metadata for the image. - /// - /// The metadata this method extends. - /// The metadata. - /// - /// if the png metadata exists; otherwise, . - /// - public static bool TryGetPngMetadata(this ImageMetadata source, [NotNullWhen(true)] out PngMetadata? metadata) - => source.TryGetFormatMetadata(PngFormat.Instance, out metadata); - - /// - /// Gets the png format specific metadata for the image frame. - /// - /// The metadata this method extends. - /// The . - public static PngFrameMetadata GetPngMetadata(this ImageFrameMetadata source) => source.GetFormatMetadata(PngFormat.Instance); - - /// - /// Gets the png format specific metadata for the image frame. - /// - /// The metadata this method extends. - /// The metadata. - /// - /// if the png frame metadata exists; otherwise, . - /// - public static bool TryGetPngMetadata(this ImageFrameMetadata source, [NotNullWhen(true)] out PngFrameMetadata? metadata) - => source.TryGetFormatMetadata(PngFormat.Instance, out metadata); - - internal static AnimatedImageMetadata ToAnimatedImageMetadata(this PngMetadata source) - => new() - { - ColorTable = source.ColorTable, - ColorTableMode = FrameColorTableMode.Global, - RepeatCount = (ushort)Numerics.Clamp(source.RepeatCount, 0, ushort.MaxValue), - }; - - internal static AnimatedImageFrameMetadata ToAnimatedImageFrameMetadata(this PngFrameMetadata source) - { - double delay = source.FrameDelay.ToDouble(); - if (double.IsNaN(delay)) - { - delay = 0; - } - - return new() - { - ColorTableMode = FrameColorTableMode.Global, - Duration = TimeSpan.FromMilliseconds(delay * 1000), - DisposalMode = GetMode(source.DisposalMode), - BlendMode = source.BlendMode, - }; - } - - private static FrameDisposalMode GetMode(FrameDisposalMode method) => method switch - { - FrameDisposalMode.DoNotDispose => FrameDisposalMode.DoNotDispose, - FrameDisposalMode.RestoreToBackground => FrameDisposalMode.RestoreToBackground, - FrameDisposalMode.RestoreToPrevious => FrameDisposalMode.RestoreToPrevious, - _ => FrameDisposalMode.DoNotDispose, - }; -} diff --git a/src/ImageSharp/Formats/Png/PngDecoder.cs b/src/ImageSharp/Formats/Png/PngDecoder.cs index e62a14b5e0..cfea0e6020 100644 --- a/src/ImageSharp/Formats/Png/PngDecoder.cs +++ b/src/ImageSharp/Formats/Png/PngDecoder.cs @@ -101,5 +101,5 @@ public sealed class PngDecoder : SpecializedImageDecoder } /// - protected override PngDecoderOptions CreateDefaultSpecializedOptions(DecoderOptions options) => new PngDecoderOptions() { GeneralOptions = options }; + protected override PngDecoderOptions CreateDefaultSpecializedOptions(DecoderOptions options) => new() { GeneralOptions = options }; } diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index d1528d08b4..a5ab739882 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -9,10 +9,8 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Compression.Zlib; -using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Png.Chunks; using SixLabors.ImageSharp.Formats.Png.Filters; -using SixLabors.ImageSharp.Formats.Webp; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; @@ -160,7 +158,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable this.height = image.Height; ImageMetadata metadata = image.Metadata; - PngMetadata pngMetadata = GetPngMetadata(image); + PngMetadata pngMetadata = metadata.ClonePngMetadata(); this.SanitizeAndSetEncoderOptions(this.encoder, pngMetadata, out this.use16Bit, out this.bytesPerPixel); stream.Write(PngConstants.HeaderBytes); @@ -211,7 +209,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable { // Write the first animated frame. currentFrame = image.Frames[currentFrameIndex]; - PngFrameMetadata frameMetadata = GetPngFrameMetadata(currentFrame); + PngFrameMetadata frameMetadata = currentFrame.Metadata.GetPngMetadata(); FrameDisposalMode previousDisposal = frameMetadata.DisposalMode; FrameControl frameControl = this.WriteFrameControlChunk(stream, frameMetadata, currentFrame.Bounds(), 0); uint sequenceNumber = 1; @@ -241,7 +239,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable currentFrame = image.Frames[currentFrameIndex]; ImageFrame? nextFrame = currentFrameIndex < image.Frames.Count - 1 ? image.Frames[currentFrameIndex + 1] : null; - frameMetadata = GetPngFrameMetadata(currentFrame); + frameMetadata = currentFrame.Metadata.GetPngMetadata(); bool blend = frameMetadata.BlendMode == FrameBlendMode.Over; (bool difference, Rectangle bounds) = @@ -288,54 +286,6 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable this.currentScanline?.Dispose(); } - private static PngMetadata GetPngMetadata(Image image) - where TPixel : unmanaged, IPixel - { - if (image.Metadata.TryGetPngMetadata(out PngMetadata? png)) - { - return png.DeepClone(); - } - - if (image.Metadata.TryGetGifMetadata(out GifMetadata? gif)) - { - AnimatedImageMetadata ani = gif.ToAnimatedImageMetadata(); - return PngMetadata.FromAnimatedMetadata(ani); - } - - if (image.Metadata.TryGetWebpMetadata(out WebpMetadata? webp)) - { - AnimatedImageMetadata ani = webp.ToAnimatedImageMetadata(); - return PngMetadata.FromAnimatedMetadata(ani); - } - - // Return explicit new instance so we do not mutate the original metadata. - return new(); - } - - private static PngFrameMetadata GetPngFrameMetadata(ImageFrame frame) - where TPixel : unmanaged, IPixel - { - if (frame.Metadata.TryGetPngMetadata(out PngFrameMetadata? png)) - { - return png.DeepClone(); - } - - if (frame.Metadata.TryGetGifMetadata(out GifFrameMetadata? gif)) - { - AnimatedImageFrameMetadata ani = gif.ToAnimatedImageFrameMetadata(); - return PngFrameMetadata.FromAnimatedMetadata(ani); - } - - if (frame.Metadata.TryGetWebpFrameMetadata(out WebpFrameMetadata? webp)) - { - AnimatedImageFrameMetadata ani = webp.ToAnimatedImageFrameMetadata(); - return PngFrameMetadata.FromAnimatedMetadata(ani); - } - - // Return explicit new instance so we do not mutate the original metadata. - return new(); - } - /// /// Convert transparent pixels, to transparent black pixels, which can yield to better compression in some cases. /// diff --git a/src/ImageSharp/Formats/Png/PngFrameMetadata.cs b/src/ImageSharp/Formats/Png/PngFrameMetadata.cs index de141909ad..c142a1c8e0 100644 --- a/src/ImageSharp/Formats/Png/PngFrameMetadata.cs +++ b/src/ImageSharp/Formats/Png/PngFrameMetadata.cs @@ -57,22 +57,6 @@ public class PngFrameMetadata : IFormatFrameMetadata this.BlendMode = frameControl.BlendMode; } - internal static PngFrameMetadata FromAnimatedMetadata(AnimatedImageFrameMetadata metadata) - => new() - { - FrameDelay = new(metadata.Duration.TotalMilliseconds / 1000), - DisposalMode = GetMode(metadata.DisposalMode), - BlendMode = metadata.BlendMode, - }; - - private static FrameDisposalMode GetMode(FrameDisposalMode mode) => mode switch - { - FrameDisposalMode.RestoreToBackground => FrameDisposalMode.RestoreToBackground, - FrameDisposalMode.RestoreToPrevious => FrameDisposalMode.RestoreToPrevious, - FrameDisposalMode.DoNotDispose => FrameDisposalMode.DoNotDispose, - _ => FrameDisposalMode.DoNotDispose, - }; - /// public static PngFrameMetadata FromFormatConnectingFrameMetadata(FormatConnectingFrameMetadata metadata) => new() @@ -105,4 +89,12 @@ public class PngFrameMetadata : IFormatFrameMetadata /// public PngFrameMetadata DeepClone() => new(this); + + private static FrameDisposalMode GetMode(FrameDisposalMode mode) => mode switch + { + FrameDisposalMode.RestoreToBackground => FrameDisposalMode.RestoreToBackground, + FrameDisposalMode.RestoreToPrevious => FrameDisposalMode.RestoreToPrevious, + FrameDisposalMode.DoNotDispose => FrameDisposalMode.DoNotDispose, + _ => FrameDisposalMode.DoNotDispose, + }; } diff --git a/src/ImageSharp/Formats/Png/PngMetadata.cs b/src/ImageSharp/Formats/Png/PngMetadata.cs index d72dc51841..beb4ca5ae8 100644 --- a/src/ImageSharp/Formats/Png/PngMetadata.cs +++ b/src/ImageSharp/Formats/Png/PngMetadata.cs @@ -90,38 +90,6 @@ public class PngMetadata : IFormatMetadata /// public bool AnimateRootFrame { get; set; } = true; - internal static PngMetadata FromAnimatedMetadata(AnimatedImageMetadata metadata) - { - // Should the conversion be from a format that uses a 24bit palette entries (gif) - // we need to clone and adjust the color table to allow for transparency. - Color[]? colorTable = metadata.ColorTable?.ToArray(); - if (colorTable != null) - { - for (int i = 0; i < colorTable.Length; i++) - { - ref Color c = ref colorTable[i]; - if (c != metadata.BackgroundColor) - { - continue; - } - - // Png treats background as fully empty - c = Color.Transparent; - break; - } - } - - return new() - { - ColorType = colorTable != null ? PngColorType.Palette : PngColorType.RgbWithAlpha, - BitDepth = colorTable != null - ? (PngBitDepth)Numerics.Clamp(ColorNumerics.GetBitsNeededForColorDepth(colorTable.Length), 1, 8) - : PngBitDepth.Bit8, - ColorTable = colorTable, - RepeatCount = metadata.RepeatCount, - }; - } - /// public static PngMetadata FromFormatConnectingMetadata(FormatConnectingMetadata metadata) { @@ -145,8 +113,7 @@ public class PngMetadata : IFormatMetadata } PngColorType color; - PixelColorType colorType = - metadata.PixelTypeInfo.ColorType ?? PixelColorType.RGB | PixelColorType.Alpha; + PixelColorType colorType = metadata.PixelTypeInfo.ColorType; switch (colorType) { diff --git a/src/ImageSharp/Formats/Qoi/MetadataExtensions.cs b/src/ImageSharp/Formats/Qoi/MetadataExtensions.cs deleted file mode 100644 index 1e0fa88997..0000000000 --- a/src/ImageSharp/Formats/Qoi/MetadataExtensions.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.Formats.Qoi; -using SixLabors.ImageSharp.Metadata; - -namespace SixLabors.ImageSharp; - -/// -/// Extension methods for the type. -/// -public static partial class MetadataExtensions -{ - /// - /// Gets the qoi format specific metadata for the image. - /// - /// The metadata this method extends. - /// The . - public static QoiMetadata GetQoiMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(QoiFormat.Instance); -} diff --git a/src/ImageSharp/Formats/Qoi/QoiMetadata.cs b/src/ImageSharp/Formats/Qoi/QoiMetadata.cs index c8bcd97480..e2062014d7 100644 --- a/src/ImageSharp/Formats/Qoi/QoiMetadata.cs +++ b/src/ImageSharp/Formats/Qoi/QoiMetadata.cs @@ -40,7 +40,7 @@ public class QoiMetadata : IFormatMetadata /// public static QoiMetadata FromFormatConnectingMetadata(FormatConnectingMetadata metadata) { - PixelColorType color = metadata.PixelTypeInfo.ColorType ?? PixelColorType.RGB; + PixelColorType color = metadata.PixelTypeInfo.ColorType; if (color.HasFlag(PixelColorType.Alpha)) { diff --git a/src/ImageSharp/Formats/Tga/MetadataExtensions.cs b/src/ImageSharp/Formats/Tga/MetadataExtensions.cs deleted file mode 100644 index 8d5e357641..0000000000 --- a/src/ImageSharp/Formats/Tga/MetadataExtensions.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.Formats.Tga; -using SixLabors.ImageSharp.Metadata; - -namespace SixLabors.ImageSharp; - -/// -/// Extension methods for the type. -/// -public static partial class MetadataExtensions -{ - /// - /// Gets the tga format specific metadata for the image. - /// - /// The metadata this method extends. - /// The . - public static TgaMetadata GetTgaMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(TgaFormat.Instance); -} diff --git a/src/ImageSharp/Formats/Tiff/MetadataExtensions.cs b/src/ImageSharp/Formats/Tiff/MetadataExtensions.cs deleted file mode 100644 index b06f5dd470..0000000000 --- a/src/ImageSharp/Formats/Tiff/MetadataExtensions.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.Formats.Tiff; -using SixLabors.ImageSharp.Metadata; - -namespace SixLabors.ImageSharp; - -/// -/// Extension methods for the type. -/// -public static partial class MetadataExtensions -{ - /// - /// Gets the tiff format specific metadata for the image. - /// - /// The metadata this method extends. - /// The . - public static TiffMetadata GetTiffMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(TiffFormat.Instance); - - /// - /// Gets the tiff format specific metadata for the image frame. - /// - /// The metadata this method extends. - /// The . - public static TiffFrameMetadata GetTiffMetadata(this ImageFrameMetadata metadata) => metadata.GetFormatMetadata(TiffFormat.Instance); -} diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 546ff7947d..c8eeb1e31f 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -395,7 +395,6 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals case TiffBitsPerPixel.Bit42: case TiffBitsPerPixel.Bit48: // Encoding not yet supported bits per pixel will default to 24 bits. - this.SetEncoderOptions(TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb, compression, predictor); break; case TiffBitsPerPixel.Bit64: diff --git a/src/ImageSharp/Formats/Webp/Chunks/WebpFrameData.cs b/src/ImageSharp/Formats/Webp/Chunks/WebpFrameData.cs index c8ff579a89..66662569c3 100644 --- a/src/ImageSharp/Formats/Webp/Chunks/WebpFrameData.cs +++ b/src/ImageSharp/Formats/Webp/Chunks/WebpFrameData.cs @@ -10,7 +10,7 @@ internal readonly struct WebpFrameData /// public const uint HeaderSize = 16; - public WebpFrameData(uint dataSize, uint x, uint y, uint width, uint height, uint duration, WebpBlendMethod blendingMethod, WebpDisposalMethod disposalMethod) + public WebpFrameData(uint dataSize, uint x, uint y, uint width, uint height, uint duration, FrameBlendMode blendingMethod, FrameDisposalMode disposalMethod) { this.DataSize = dataSize; this.X = x; @@ -30,12 +30,12 @@ internal readonly struct WebpFrameData width, height, duration, - (flags & 2) == 0 ? WebpBlendMethod.Over : WebpBlendMethod.Source, - (flags & 1) == 1 ? WebpDisposalMethod.RestoreToBackground : WebpDisposalMethod.DoNotDispose) + (flags & 2) == 0 ? FrameBlendMode.Over : FrameBlendMode.Source, + (flags & 1) == 1 ? FrameDisposalMode.RestoreToBackground : FrameDisposalMode.DoNotDispose) { } - public WebpFrameData(uint x, uint y, uint width, uint height, uint duration, WebpBlendMethod blendingMethod, WebpDisposalMethod disposalMethod) + public WebpFrameData(uint x, uint y, uint width, uint height, uint duration, FrameBlendMode blendingMethod, FrameDisposalMode disposalMethod) : this(0, x, y, width, height, duration, blendingMethod, disposalMethod) { } @@ -74,12 +74,12 @@ internal readonly struct WebpFrameData /// /// Gets how transparent pixels of the current frame are to be blended with corresponding pixels of the previous canvas. /// - public WebpBlendMethod BlendingMethod { get; } + public FrameBlendMode BlendingMethod { get; } /// /// Gets how the current frame is to be treated after it has been displayed (before rendering the next frame) on the canvas. /// - public WebpDisposalMethod DisposalMethod { get; } + public FrameDisposalMode DisposalMethod { get; } public Rectangle Bounds => new((int)this.X, (int)this.Y, (int)this.Width, (int)this.Height); @@ -91,13 +91,13 @@ internal readonly struct WebpFrameData { byte flags = 0; - if (this.BlendingMethod is WebpBlendMethod.Source) + if (this.BlendingMethod is FrameBlendMode.Source) { // Set blending flag. flags |= 2; } - if (this.DisposalMethod is WebpDisposalMethod.RestoreToBackground) + if (this.DisposalMethod is FrameDisposalMode.RestoreToBackground) { // Set disposal flag. flags |= 1; @@ -124,7 +124,7 @@ internal readonly struct WebpFrameData { Span buffer = stackalloc byte[4]; - WebpFrameData data = new( + return new( dataSize: WebpChunkParsingUtils.ReadChunkSize(stream, buffer), x: WebpChunkParsingUtils.ReadUInt24LittleEndian(stream, buffer) * 2, y: WebpChunkParsingUtils.ReadUInt24LittleEndian(stream, buffer) * 2, @@ -132,7 +132,5 @@ internal readonly struct WebpFrameData height: WebpChunkParsingUtils.ReadUInt24LittleEndian(stream, buffer) + 1, duration: WebpChunkParsingUtils.ReadUInt24LittleEndian(stream, buffer), flags: stream.ReadByte()); - - return data; } } diff --git a/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs b/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs index f658e40f6f..58d007265b 100644 --- a/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs +++ b/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs @@ -259,7 +259,7 @@ internal class Vp8LEncoder : IDisposable if (hasAnimation) { - WebpMetadata webpMetadata = WebpCommonUtils.GetWebpMetadata(image); + WebpMetadata webpMetadata = image.Metadata.GetWebpMetadata(); BitWriterBase.WriteAnimationParameter(stream, webpMetadata.BackgroundColor, webpMetadata.RepeatCount); } diff --git a/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs b/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs index 40d91ecf1a..7e1ef93cfe 100644 --- a/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs +++ b/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs @@ -332,7 +332,7 @@ internal class Vp8Encoder : IDisposable if (hasAnimation) { - WebpMetadata webpMetadata = WebpCommonUtils.GetWebpMetadata(image); + WebpMetadata webpMetadata = image.Metadata.GetWebpMetadata(); BitWriterBase.WriteAnimationParameter(stream, webpMetadata.BackgroundColor, webpMetadata.RepeatCount); } @@ -376,7 +376,7 @@ internal class Vp8Encoder : IDisposable where TPixel : unmanaged, IPixel { ImageFrame frame = image.Frames.RootFrame; - this.Encode(stream, frame, image.Bounds, WebpCommonUtils.GetWebpFrameMetadata(frame), false, image); + this.Encode(stream, frame, image.Bounds, frame.Metadata.GetWebpMetadata(), false, image); } /// @@ -462,7 +462,7 @@ internal class Vp8Encoder : IDisposable // Extract and encode alpha channel data, if present. int alphaDataSize = 0; bool alphaCompressionSucceeded = false; - Span alphaData = Span.Empty; + Span alphaData = []; IMemoryOwner encodedAlphaData = null; try { diff --git a/src/ImageSharp/Formats/Webp/MetadataExtensions.cs b/src/ImageSharp/Formats/Webp/MetadataExtensions.cs deleted file mode 100644 index 731d3f1ff2..0000000000 --- a/src/ImageSharp/Formats/Webp/MetadataExtensions.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Diagnostics.CodeAnalysis; -using SixLabors.ImageSharp.Formats; -using SixLabors.ImageSharp.Formats.Webp; -using SixLabors.ImageSharp.Metadata; - -namespace SixLabors.ImageSharp; - -/// -/// Extension methods for the type. -/// -public static partial class MetadataExtensions -{ - /// - /// Gets the webp format specific metadata for the image. - /// - /// The metadata this method extends. - /// The . - public static WebpMetadata GetWebpMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(WebpFormat.Instance); - - /// - /// Gets the webp format specific metadata for the image. - /// - /// The metadata this method extends. - /// The metadata. - /// - /// if the webp metadata exists; otherwise, . - /// - public static bool TryGetWebpMetadata(this ImageMetadata source, [NotNullWhen(true)] out WebpMetadata? metadata) - => source.TryGetFormatMetadata(WebpFormat.Instance, out metadata); - - /// - /// Gets the webp format specific metadata for the image frame. - /// - /// The metadata this method extends. - /// The . - public static WebpFrameMetadata GetWebpMetadata(this ImageFrameMetadata metadata) => metadata.GetFormatMetadata(WebpFormat.Instance); - - /// - /// Gets the webp format specific metadata for the image frame. - /// - /// The metadata this method extends. - /// The metadata. - /// - /// if the webp frame metadata exists; otherwise, . - /// - public static bool TryGetWebpFrameMetadata(this ImageFrameMetadata source, [NotNullWhen(true)] out WebpFrameMetadata? metadata) - => source.TryGetFormatMetadata(WebpFormat.Instance, out metadata); - - internal static AnimatedImageMetadata ToAnimatedImageMetadata(this WebpMetadata source) - => new() - { - ColorTableMode = FrameColorTableMode.Global, - RepeatCount = source.RepeatCount, - BackgroundColor = source.BackgroundColor - }; - - internal static AnimatedImageFrameMetadata ToAnimatedImageFrameMetadata(this WebpFrameMetadata source) - => new() - { - ColorTableMode = FrameColorTableMode.Global, - Duration = TimeSpan.FromMilliseconds(source.FrameDelay), - DisposalMode = GetMode(source.DisposalMethod), - BlendMode = source.BlendMethod == WebpBlendMethod.Over ? FrameBlendMode.Over : FrameBlendMode.Source, - }; - - private static FrameDisposalMode GetMode(WebpDisposalMethod method) => method switch - { - WebpDisposalMethod.RestoreToBackground => FrameDisposalMode.RestoreToBackground, - WebpDisposalMethod.DoNotDispose => FrameDisposalMode.DoNotDispose, - _ => FrameDisposalMode.DoNotDispose, - }; -} diff --git a/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs b/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs index 70372fe985..72405e480e 100644 --- a/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs +++ b/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs @@ -195,14 +195,14 @@ internal class WebpAnimationDecoder : IDisposable Rectangle regionRectangle = frameData.Bounds; - if (frameData.DisposalMethod is WebpDisposalMethod.RestoreToBackground) + if (frameData.DisposalMethod is FrameDisposalMode.RestoreToBackground) { this.RestoreToBackground(imageFrame, backgroundColor); } using Buffer2D decodedImageFrame = this.DecodeImageFrameData(frameData, webpInfo); - bool blend = previousFrame != null && frameData.BlendingMethod == WebpBlendMethod.Over; + bool blend = previousFrame != null && frameData.BlendingMethod == FrameBlendMode.Over; DrawDecodedImageFrameOnCanvas(decodedImageFrame, imageFrame, regionRectangle, blend); previousFrame = currentFrame ?? image.Frames.RootFrame; diff --git a/src/ImageSharp/Formats/Webp/WebpBlendMethod.cs b/src/ImageSharp/Formats/Webp/WebpBlendMethod.cs deleted file mode 100644 index f16f7650c7..0000000000 --- a/src/ImageSharp/Formats/Webp/WebpBlendMethod.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Webp; - -/// -/// Indicates how transparent pixels of the current frame are to be blended with corresponding pixels of the previous canvas. -/// -public enum WebpBlendMethod -{ - /// - /// Do not blend. After disposing of the previous frame, - /// render the current frame on the canvas by overwriting the rectangle covered by the current frame. - /// - Source = 0, - - /// - /// Use alpha blending. After disposing of the previous frame, render the current frame on the canvas using alpha-blending. - /// If the current frame does not have an alpha channel, assume alpha value of 255, effectively replacing the rectangle. - /// - Over = 1, -} diff --git a/src/ImageSharp/Formats/Webp/WebpCommonUtils.cs b/src/ImageSharp/Formats/Webp/WebpCommonUtils.cs index 49482260bb..a1e9821c09 100644 --- a/src/ImageSharp/Formats/Webp/WebpCommonUtils.cs +++ b/src/ImageSharp/Formats/Webp/WebpCommonUtils.cs @@ -4,8 +4,6 @@ using System.Runtime.InteropServices; using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.X86; -using SixLabors.ImageSharp.Formats.Gif; -using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Webp; @@ -15,54 +13,6 @@ namespace SixLabors.ImageSharp.Formats.Webp; /// internal static class WebpCommonUtils { - public static WebpMetadata GetWebpMetadata(Image image) - where TPixel : unmanaged, IPixel - { - if (image.Metadata.TryGetWebpMetadata(out WebpMetadata? webp)) - { - return (WebpMetadata)webp.DeepClone(); - } - - if (image.Metadata.TryGetGifMetadata(out GifMetadata? gif)) - { - AnimatedImageMetadata ani = gif.ToAnimatedImageMetadata(); - return WebpMetadata.FromAnimatedMetadata(ani); - } - - if (image.Metadata.TryGetPngMetadata(out PngMetadata? png)) - { - AnimatedImageMetadata ani = png.ToAnimatedImageMetadata(); - return WebpMetadata.FromAnimatedMetadata(ani); - } - - // Return explicit new instance so we do not mutate the original metadata. - return new(); - } - - public static WebpFrameMetadata GetWebpFrameMetadata(ImageFrame frame) - where TPixel : unmanaged, IPixel - { - if (frame.Metadata.TryGetWebpFrameMetadata(out WebpFrameMetadata? webp)) - { - return (WebpFrameMetadata)webp.DeepClone(); - } - - if (frame.Metadata.TryGetGifMetadata(out GifFrameMetadata? gif)) - { - AnimatedImageFrameMetadata ani = gif.ToAnimatedImageFrameMetadata(); - return WebpFrameMetadata.FromAnimatedMetadata(ani); - } - - if (frame.Metadata.TryGetPngMetadata(out PngFrameMetadata? png)) - { - AnimatedImageFrameMetadata ani = png.ToAnimatedImageFrameMetadata(); - return WebpFrameMetadata.FromAnimatedMetadata(ani); - } - - // Return explicit new instance so we do not mutate the original metadata. - return new(); - } - /// /// Checks if the pixel row is not opaque. /// diff --git a/src/ImageSharp/Formats/Webp/WebpDisposalMethod.cs b/src/ImageSharp/Formats/Webp/WebpDisposalMethod.cs deleted file mode 100644 index 47cc83951d..0000000000 --- a/src/ImageSharp/Formats/Webp/WebpDisposalMethod.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Webp; - -/// -/// Indicates how the current frame is to be treated after it has been displayed (before rendering the next frame) on the canvas. -/// -public enum WebpDisposalMethod -{ - /// - /// Do not dispose. Leave the canvas as is. - /// - DoNotDispose = 0, - - /// - /// Dispose to background color. Fill the rectangle on the canvas covered by the current frame with background color specified in the ANIM chunk. - /// - RestoreToBackground = 1 -} diff --git a/src/ImageSharp/Formats/Webp/WebpEncoderCore.cs b/src/ImageSharp/Formats/Webp/WebpEncoderCore.cs index d29759f9a1..21ce444b11 100644 --- a/src/ImageSharp/Formats/Webp/WebpEncoderCore.cs +++ b/src/ImageSharp/Formats/Webp/WebpEncoderCore.cs @@ -5,6 +5,7 @@ using SixLabors.ImageSharp.Formats.Webp.Chunks; using SixLabors.ImageSharp.Formats.Webp.Lossless; using SixLabors.ImageSharp.Formats.Webp.Lossy; using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Webp; @@ -124,7 +125,7 @@ internal sealed class WebpEncoderCore : IImageEncoderInternals } else { - WebpMetadata webpMetadata = WebpCommonUtils.GetWebpMetadata(image); + WebpMetadata webpMetadata = image.Metadata.GetWebpMetadata(); lossless = webpMetadata.FileFormat == WebpFileFormatType.Lossless; } @@ -150,12 +151,12 @@ internal sealed class WebpEncoderCore : IImageEncoderInternals // Encode the first frame. ImageFrame previousFrame = image.Frames.RootFrame; - WebpFrameMetadata frameMetadata = WebpCommonUtils.GetWebpFrameMetadata(previousFrame); + WebpFrameMetadata frameMetadata = previousFrame.Metadata.GetWebpMetadata(); hasAlpha |= encoder.Encode(previousFrame, previousFrame.Bounds(), frameMetadata, stream, hasAnimation); if (hasAnimation) { - WebpDisposalMethod previousDisposal = frameMetadata.DisposalMethod; + FrameDisposalMode previousDisposal = frameMetadata.DisposalMethod; // Encode additional frames // This frame is reused to store de-duplicated pixel buffers. @@ -163,12 +164,12 @@ internal sealed class WebpEncoderCore : IImageEncoderInternals for (int i = 1; i < image.Frames.Count; i++) { - ImageFrame? prev = previousDisposal == WebpDisposalMethod.RestoreToBackground ? null : previousFrame; + ImageFrame? prev = previousDisposal == FrameDisposalMode.RestoreToBackground ? null : previousFrame; ImageFrame currentFrame = image.Frames[i]; ImageFrame? nextFrame = i < image.Frames.Count - 1 ? image.Frames[i + 1] : null; - frameMetadata = WebpCommonUtils.GetWebpFrameMetadata(currentFrame); - bool blend = frameMetadata.BlendMethod == WebpBlendMethod.Over; + frameMetadata = currentFrame.Metadata.GetWebpMetadata(); + bool blend = frameMetadata.BlendMethod == FrameBlendMode.Over; (bool difference, Rectangle bounds) = AnimationUtilities.DeDuplicatePixels( @@ -227,8 +228,8 @@ internal sealed class WebpEncoderCore : IImageEncoderInternals // Encode the first frame. ImageFrame previousFrame = image.Frames.RootFrame; - WebpFrameMetadata frameMetadata = WebpCommonUtils.GetWebpFrameMetadata(previousFrame); - WebpDisposalMethod previousDisposal = frameMetadata.DisposalMethod; + WebpFrameMetadata frameMetadata = previousFrame.Metadata.GetWebpMetadata(); + FrameDisposalMode previousDisposal = frameMetadata.DisposalMethod; hasAlpha |= encoder.EncodeAnimation(previousFrame, stream, previousFrame.Bounds(), frameMetadata); @@ -238,12 +239,12 @@ internal sealed class WebpEncoderCore : IImageEncoderInternals for (int i = 1; i < image.Frames.Count; i++) { - ImageFrame? prev = previousDisposal == WebpDisposalMethod.RestoreToBackground ? null : previousFrame; + ImageFrame? prev = previousDisposal == FrameDisposalMode.RestoreToBackground ? null : previousFrame; ImageFrame currentFrame = image.Frames[i]; ImageFrame? nextFrame = i < image.Frames.Count - 1 ? image.Frames[i + 1] : null; - frameMetadata = WebpCommonUtils.GetWebpFrameMetadata(currentFrame); - bool blend = frameMetadata.BlendMethod == WebpBlendMethod.Over; + frameMetadata = currentFrame.Metadata.GetWebpMetadata(); + bool blend = frameMetadata.BlendMethod == FrameBlendMode.Over; (bool difference, Rectangle bounds) = AnimationUtilities.DeDuplicatePixels( diff --git a/src/ImageSharp/Formats/Webp/WebpFileFormatType.cs b/src/ImageSharp/Formats/Webp/WebpFileFormatType.cs index 1ed9bbb431..6f606cdf46 100644 --- a/src/ImageSharp/Formats/Webp/WebpFileFormatType.cs +++ b/src/ImageSharp/Formats/Webp/WebpFileFormatType.cs @@ -9,12 +9,12 @@ namespace SixLabors.ImageSharp.Formats.Webp; public enum WebpFileFormatType { /// - /// The lossless webp format. + /// The lossless Webp format, which compresses data without any loss of information. /// Lossless, /// - /// The lossy webp format. + /// The lossy Webp format, which compresses data by discarding some of it. /// Lossy, } diff --git a/src/ImageSharp/Formats/Webp/WebpFrameMetadata.cs b/src/ImageSharp/Formats/Webp/WebpFrameMetadata.cs index cd1b5d5905..45e182d223 100644 --- a/src/ImageSharp/Formats/Webp/WebpFrameMetadata.cs +++ b/src/ImageSharp/Formats/Webp/WebpFrameMetadata.cs @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.Webp; /// /// Provides webp specific metadata information for the image frame. /// -public class WebpFrameMetadata : IDeepCloneable +public class WebpFrameMetadata : IFormatFrameMetadata { /// /// Initializes a new instance of the class. @@ -29,12 +29,12 @@ public class WebpFrameMetadata : IDeepCloneable /// /// Gets or sets how transparent pixels of the current frame are to be blended with corresponding pixels of the previous canvas. /// - public WebpBlendMethod BlendMethod { get; set; } + public FrameBlendMode BlendMethod { get; set; } /// /// Gets or sets how the current frame is to be treated after it has been displayed (before rendering the next frame) on the canvas. /// - public WebpDisposalMethod DisposalMethod { get; set; } + public FrameDisposalMode DisposalMethod { get; set; } /// /// Gets or sets the frame duration. The time to wait before displaying the next frame, @@ -43,13 +43,34 @@ public class WebpFrameMetadata : IDeepCloneable public uint FrameDelay { get; set; } /// - public IDeepCloneable DeepClone() => new WebpFrameMetadata(this); - - internal static WebpFrameMetadata FromAnimatedMetadata(AnimatedImageFrameMetadata metadata) + public static WebpFrameMetadata FromFormatConnectingFrameMetadata(FormatConnectingFrameMetadata metadata) => new() { FrameDelay = (uint)metadata.Duration.TotalMilliseconds, - BlendMethod = metadata.BlendMode == FrameBlendMode.Source ? WebpBlendMethod.Source : WebpBlendMethod.Over, - DisposalMethod = metadata.DisposalMode == FrameDisposalMode.RestoreToBackground ? WebpDisposalMethod.RestoreToBackground : WebpDisposalMethod.DoNotDispose + BlendMethod = metadata.BlendMode, + DisposalMethod = GetMode(metadata.DisposalMode) + }; + + /// + public FormatConnectingFrameMetadata ToFormatConnectingFrameMetadata() + => new() + { + ColorTableMode = FrameColorTableMode.Global, + Duration = TimeSpan.FromMilliseconds(this.FrameDelay), + DisposalMode = this.DisposalMethod, + BlendMode = this.BlendMethod, }; + + /// + IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); + + /// + public WebpFrameMetadata DeepClone() => new(this); + + private static FrameDisposalMode GetMode(FrameDisposalMode mode) => mode switch + { + FrameDisposalMode.RestoreToBackground => FrameDisposalMode.RestoreToBackground, + FrameDisposalMode.DoNotDispose => FrameDisposalMode.DoNotDispose, + _ => FrameDisposalMode.DoNotDispose, + }; } diff --git a/src/ImageSharp/Formats/Webp/WebpMetadata.cs b/src/ImageSharp/Formats/Webp/WebpMetadata.cs index a42435fd4a..33ebbbf6dc 100644 --- a/src/ImageSharp/Formats/Webp/WebpMetadata.cs +++ b/src/ImageSharp/Formats/Webp/WebpMetadata.cs @@ -54,24 +54,16 @@ public class WebpMetadata : IFormatMetadata /// Gets or sets the default background color of the canvas when animating. /// This color may be used to fill the unused space on the canvas around the frames, /// as well as the transparent pixels of the first frame. - /// The background color is also used when the Disposal method is . + /// The background color is also used when the Disposal method is . /// public Color BackgroundColor { get; set; } - internal static WebpMetadata FromAnimatedMetadata(AnimatedImageMetadata metadata) - => new() - { - FileFormat = WebpFileFormatType.Lossless, - BackgroundColor = metadata.BackgroundColor, - RepeatCount = metadata.RepeatCount - }; - /// public static WebpMetadata FromFormatConnectingMetadata(FormatConnectingMetadata metadata) { WebpBitsPerPixel bitsPerPixel; WebpColorType color; - PixelColorType colorType = metadata.PixelTypeInfo.ColorType ?? PixelColorType.RGB | PixelColorType.Alpha; + PixelColorType colorType = metadata.PixelTypeInfo.ColorType; switch (colorType) { case PixelColorType.RGB: @@ -100,9 +92,9 @@ public class WebpMetadata : IFormatMetadata { BitsPerPixel = bitsPerPixel, ColorType = color, - FileFormat = WebpFileFormatType.Lossless, BackgroundColor = metadata.BackgroundColor, - RepeatCount = metadata.RepeatCount + RepeatCount = metadata.RepeatCount, + FileFormat = metadata.EncodingType == EncodingType.Lossless ? WebpFileFormatType.Lossless : WebpFileFormatType.Lossy }; } @@ -146,6 +138,7 @@ public class WebpMetadata : IFormatMetadata public FormatConnectingMetadata ToFormatConnectingMetadata() => new() { + EncodingType = this.FileFormat == WebpFileFormatType.Lossless ? EncodingType.Lossless : EncodingType.Lossy, PixelTypeInfo = this.GetPixelTypeInfo(), ColorTableMode = FrameColorTableMode.Global, RepeatCount = this.RepeatCount, diff --git a/src/ImageSharp/Formats/ImageExtensions.Save.cs b/src/ImageSharp/Formats/_Generated/ImageExtensions.Save.cs similarity index 99% rename from src/ImageSharp/Formats/ImageExtensions.Save.cs rename to src/ImageSharp/Formats/_Generated/ImageExtensions.Save.cs index 7e5989d6fc..5d7f84acfb 100644 --- a/src/ImageSharp/Formats/ImageExtensions.Save.cs +++ b/src/ImageSharp/Formats/_Generated/ImageExtensions.Save.cs @@ -2,8 +2,6 @@ // Licensed under the Six Labors Split License. // -using SixLabors.ImageSharp.Advanced; - using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Jpeg; diff --git a/src/ImageSharp/Formats/ImageExtensions.Save.tt b/src/ImageSharp/Formats/_Generated/ImageExtensions.Save.tt similarity index 95% rename from src/ImageSharp/Formats/ImageExtensions.Save.tt rename to src/ImageSharp/Formats/_Generated/ImageExtensions.Save.tt index d4f1ed233b..144dd83625 100644 --- a/src/ImageSharp/Formats/ImageExtensions.Save.tt +++ b/src/ImageSharp/Formats/_Generated/ImageExtensions.Save.tt @@ -1,25 +1,8 @@ -<#@ template language="C#" #> +<#@include file="_Formats.ttinclude" #> <#@ import namespace="System.Text" #> <#@ import namespace="System.Collections.Generic" #> -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - // -using SixLabors.ImageSharp.Advanced; - <# - var formats = new []{ - "Bmp", - "Gif", - "Jpeg", - "Pbm", - "Png", - "Qoi", - "Tga", - "Tiff", - "Webp", - }; - foreach (string fmt in formats) { #> diff --git a/src/ImageSharp/Formats/_Generated/ImageMetadataExtensions.cs b/src/ImageSharp/Formats/_Generated/ImageMetadataExtensions.cs new file mode 100644 index 0000000000..826f5905b1 --- /dev/null +++ b/src/ImageSharp/Formats/_Generated/ImageMetadataExtensions.cs @@ -0,0 +1,283 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +// +using SixLabors.ImageSharp.Metadata; +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.Formats.Jpeg; +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; + +namespace SixLabors.ImageSharp; + +/// +/// Extension methods for the and types. +/// +public static class ImageMetadataExtensions +{ + /// + /// 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 BmpMetadata GetBmpMetadata(this ImageMetadata source) => source.GetFormatMetadata(BmpFormat.Instance); + + /// + /// Creates a new cloned instance of from the . + /// The instance is created via + /// + /// The image metadata. + /// The new + public static BmpMetadata CloneBmpMetadata(this ImageMetadata source) => source.CloneFormatMetadata(BmpFormat.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 GifMetadata GetGifMetadata(this ImageMetadata source) => source.GetFormatMetadata(GifFormat.Instance); + + /// + /// Creates a new cloned instance of from the . + /// The instance is created via + /// + /// The image metadata. + /// The new + public static GifMetadata CloneGifMetadata(this ImageMetadata source) => source.CloneFormatMetadata(GifFormat.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 JpegMetadata GetJpegMetadata(this ImageMetadata source) => source.GetFormatMetadata(JpegFormat.Instance); + + /// + /// Creates a new cloned instance of from the . + /// The instance is created via + /// + /// The image metadata. + /// The new + public static JpegMetadata CloneJpegMetadata(this ImageMetadata source) => source.CloneFormatMetadata(JpegFormat.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 PbmMetadata GetPbmMetadata(this ImageMetadata source) => source.GetFormatMetadata(PbmFormat.Instance); + + /// + /// Creates a new cloned instance of from the . + /// The instance is created via + /// + /// The image metadata. + /// The new + public static PbmMetadata ClonePbmMetadata(this ImageMetadata source) => source.CloneFormatMetadata(PbmFormat.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 PngMetadata GetPngMetadata(this ImageMetadata source) => source.GetFormatMetadata(PngFormat.Instance); + + /// + /// Creates a new cloned instance of from the . + /// The instance is created via + /// + /// The image metadata. + /// The new + public static PngMetadata ClonePngMetadata(this ImageMetadata source) => source.CloneFormatMetadata(PngFormat.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 QoiMetadata GetQoiMetadata(this ImageMetadata source) => source.GetFormatMetadata(QoiFormat.Instance); + + /// + /// Creates a new cloned instance of from the . + /// The instance is created via + /// + /// The image metadata. + /// The new + public static QoiMetadata CloneQoiMetadata(this ImageMetadata source) => source.CloneFormatMetadata(QoiFormat.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 TgaMetadata GetTgaMetadata(this ImageMetadata source) => source.GetFormatMetadata(TgaFormat.Instance); + + /// + /// Creates a new cloned instance of from the . + /// The instance is created via + /// + /// The image metadata. + /// The new + public static TgaMetadata CloneTgaMetadata(this ImageMetadata source) => source.CloneFormatMetadata(TgaFormat.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 TiffMetadata GetTiffMetadata(this ImageMetadata source) => source.GetFormatMetadata(TiffFormat.Instance); + + /// + /// Creates a new cloned instance of from the . + /// The instance is created via + /// + /// The image metadata. + /// The new + public static TiffMetadata CloneTiffMetadata(this ImageMetadata source) => source.CloneFormatMetadata(TiffFormat.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 WebpMetadata GetWebpMetadata(this ImageMetadata source) => source.GetFormatMetadata(WebpFormat.Instance); + + /// + /// Creates a new cloned instance of from the . + /// The instance is created via + /// + /// The image metadata. + /// 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 frame metadata. + /// + /// The + /// + public static GifFrameMetadata GetGifMetadata(this ImageFrameMetadata source) => source.GetFormatMetadata(GifFormat.Instance); + + /// + /// Creates a new cloned instance of from the . + /// The instance is created via + /// + /// The image frame metadata. + /// The new + public static GifFrameMetadata CloneGifMetadata(this ImageFrameMetadata source) => source.CloneFormatMetadata(GifFormat.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 frame metadata. + /// + /// The + /// + public static PngFrameMetadata GetPngMetadata(this ImageFrameMetadata source) => source.GetFormatMetadata(PngFormat.Instance); + + /// + /// Creates a new cloned instance of from the . + /// The instance is created via + /// + /// The image frame metadata. + /// The new + public static PngFrameMetadata ClonePngMetadata(this ImageFrameMetadata source) => source.CloneFormatMetadata(PngFormat.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 frame metadata. + /// + /// The + /// + public static TiffFrameMetadata GetTiffMetadata(this ImageFrameMetadata source) => source.GetFormatMetadata(TiffFormat.Instance); + + /// + /// Creates a new cloned instance of from the . + /// The instance is created via + /// + /// The image frame metadata. + /// The new + public static TiffFrameMetadata CloneTiffMetadata(this ImageFrameMetadata source) => source.CloneFormatMetadata(TiffFormat.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 frame metadata. + /// + /// The + /// + public static WebpFrameMetadata GetWebpMetadata(this ImageFrameMetadata source) => source.GetFormatMetadata(WebpFormat.Instance); + + /// + /// Creates a new cloned instance of from the . + /// The instance is created via + /// + /// The image frame metadata. + /// The new + public static WebpFrameMetadata CloneWebpMetadata(this ImageFrameMetadata source) => source.CloneFormatMetadata(WebpFormat.Instance); +} diff --git a/src/ImageSharp/Formats/_Generated/ImageMetadataExtensions.tt b/src/ImageSharp/Formats/_Generated/ImageMetadataExtensions.tt new file mode 100644 index 0000000000..e4db85ed59 --- /dev/null +++ b/src/ImageSharp/Formats/_Generated/ImageMetadataExtensions.tt @@ -0,0 +1,77 @@ +<#@include file="_Formats.ttinclude" #> +<#@ import namespace="System.Text" #> +<#@ import namespace="System.Collections.Generic" #> +// +using SixLabors.ImageSharp.Metadata; +<# + foreach (string fmt in formats) + { +#> +using SixLabors.ImageSharp.Formats.<#= fmt #>; +<# + + } +#> + +namespace SixLabors.ImageSharp; + +/// +/// Extension methods for the and types. +/// +public static class ImageMetadataExtensions +{ +<# + foreach (string fmt in formats) + { +#> + /// + /// 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 <#= fmt #>Metadata Get<#= fmt #>Metadata(this ImageMetadata source) => source.GetFormatMetadata(<#= fmt #>Format.Instance); + + /// + /// Creates a new cloned instance of from the . + /// The instance is created via + /// + /// The image metadata. + /// The new + public static <#= fmt #>Metadata Clone<#= fmt #>Metadata(this ImageMetadata source) => source.CloneFormatMetadata(<#= fmt #>Format.Instance); + +<# + } +#> +<# + foreach (string fmt in frameFormats) + { +#> + + /// + /// 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 frame metadata. + /// + /// The + /// + public static <#= fmt #>FrameMetadata Get<#= fmt #>Metadata(this ImageFrameMetadata source) => source.GetFormatMetadata(<#= fmt #>Format.Instance); + + /// + /// Creates a new cloned instance of from the . + /// The instance is created via + /// + /// The image frame metadata. + /// The new + public static <#= fmt #>FrameMetadata Clone<#= fmt #>Metadata(this ImageFrameMetadata source) => source.CloneFormatMetadata(<#= fmt #>Format.Instance); +<# + } +#> +} diff --git a/src/ImageSharp/Formats/_Generated/_Formats.ttinclude b/src/ImageSharp/Formats/_Generated/_Formats.ttinclude new file mode 100644 index 0000000000..24ac66a70b --- /dev/null +++ b/src/ImageSharp/Formats/_Generated/_Formats.ttinclude @@ -0,0 +1,24 @@ +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ assembly name="System.Core" #> +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. +<#+ + private static readonly string[] formats = new []{ + "Bmp", + "Gif", + "Jpeg", + "Pbm", + "Png", + "Qoi", + "Tga", + "Tiff", + "Webp", + }; + + private static readonly string[] frameFormats = new []{ + "Gif", + "Png", + "Tiff", + "Webp", + }; +#> diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index 6096bd33e3..d3c4034717 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -52,6 +52,11 @@ + + True + True + ImageMetadataExtensions.tt + True True @@ -142,7 +147,7 @@ True PorterDuffFunctions.Generated.tt - + True True ImageExtensions.Save.tt @@ -150,6 +155,10 @@ + + ImageMetadataExtensions.cs + TextTemplatingFileGenerator + TextTemplatingFileGenerator Block8x8F.Generated.cs @@ -222,7 +231,7 @@ DefaultPixelBlenders.Generated.cs TextTemplatingFileGenerator - + TextTemplatingFileGenerator ImageExtensions.Save.cs diff --git a/src/ImageSharp/Metadata/ImageFrameMetadata.cs b/src/ImageSharp/Metadata/ImageFrameMetadata.cs index 1c0330d5d0..afda71879f 100644 --- a/src/ImageSharp/Metadata/ImageFrameMetadata.cs +++ b/src/ImageSharp/Metadata/ImageFrameMetadata.cs @@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Metadata; ///
public sealed class ImageFrameMetadata : IDeepCloneable { - private readonly Dictionary formatMetadata = new(); + private readonly Dictionary formatMetadata = []; /// /// Initializes a new instance of the class. @@ -35,9 +35,9 @@ public sealed class ImageFrameMetadata : IDeepCloneable { DebugGuard.NotNull(other, nameof(other)); - foreach (KeyValuePair meta in other.formatMetadata) + foreach (KeyValuePair meta in other.formatMetadata) { - this.formatMetadata.Add(meta.Key, meta.Value.DeepClone()); + this.formatMetadata.Add(meta.Key, (IFormatFrameMetadata)meta.Value.DeepClone()); } this.ExifProfile = other.ExifProfile?.DeepClone(); @@ -45,6 +45,10 @@ public sealed class ImageFrameMetadata : IDeepCloneable this.IptcProfile = other.IptcProfile?.DeepClone(); this.XmpProfile = other.XmpProfile?.DeepClone(); this.CicpProfile = other.CicpProfile?.DeepClone(); + + // NOTE: This clone is actually shallow but we share the same format + // instances for all images in the configuration. + this.DecodedImageFormat = other.DecodedImageFormat; } /// @@ -72,12 +76,19 @@ public sealed class ImageFrameMetadata : IDeepCloneable /// public CicpProfile? CicpProfile { get; set; } + /// + /// Gets the original format, if any, the image was decode from. + /// + public IImageFormat? DecodedImageFormat { get; internal set; } + /// public ImageFrameMetadata DeepClone() => new(this); /// - /// Gets the metadata value associated with the specified key. This method will always return a result creating - /// a new instance and binding it to the frame metadata if none is found. + /// Gets the metadata value associated with the specified key.
+ /// 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 type of format metadata. /// The type of format frame metadata. @@ -87,43 +98,37 @@ public sealed class ImageFrameMetadata : IDeepCloneable /// public TFormatFrameMetadata GetFormatMetadata(IImageFormat key) where TFormatMetadata : class - where TFormatFrameMetadata : class, IDeepCloneable + where TFormatFrameMetadata : class, IFormatFrameMetadata { - if (this.formatMetadata.TryGetValue(key, out IDeepCloneable? meta)) + if (this.formatMetadata.TryGetValue(key, out IFormatFrameMetadata? meta)) { return (TFormatFrameMetadata)meta; } + // None found. Check if we have a decoded format to convert from. + if (this.DecodedImageFormat is not null + && this.formatMetadata.TryGetValue(this.DecodedImageFormat, out IFormatFrameMetadata? decodedMetadata)) + { + return TFormatFrameMetadata.FromFormatConnectingFrameMetadata(decodedMetadata.ToFormatConnectingFrameMetadata()); + } + TFormatFrameMetadata newMeta = key.CreateDefaultFormatFrameMetadata(); this.formatMetadata[key] = newMeta; return newMeta; } /// - /// Gets the metadata value associated with the specified key. + /// Creates a new instance the metadata value associated with the specified key. + /// The instance is created from a clone generated via . /// - /// The type of format metadata. + /// The type of metadata. /// The type of format frame metadata. /// The key of the value to get. - /// - /// When this method returns, contains the metadata associated with the specified key, - /// if the key is found; otherwise, the default value for the type of the metadata parameter. - /// This parameter is passed uninitialized. - /// /// - /// if the frame metadata exists for the specified key; otherwise, . + /// The . /// - public bool TryGetFormatMetadata(IImageFormat key, out TFormatFrameMetadata? metadata) + public TFormatFrameMetadata CloneFormatMetadata(IImageFormat key) where TFormatMetadata : class - where TFormatFrameMetadata : class, IDeepCloneable - { - if (this.formatMetadata.TryGetValue(key, out IDeepCloneable? meta)) - { - metadata = (TFormatFrameMetadata)meta; - return true; - } - - metadata = default; - return false; - } + where TFormatFrameMetadata : class, IFormatFrameMetadata + => ((IDeepCloneable)this.GetFormatMetadata(key)).DeepClone(); } diff --git a/src/ImageSharp/Metadata/ImageMetadata.cs b/src/ImageSharp/Metadata/ImageMetadata.cs index ceeea42cfc..b521f8eeed 100644 --- a/src/ImageSharp/Metadata/ImageMetadata.cs +++ b/src/ImageSharp/Metadata/ImageMetadata.cs @@ -33,7 +33,7 @@ public sealed class ImageMetadata : IDeepCloneable ///
public const PixelResolutionUnit DefaultPixelResolutionUnits = PixelResolutionUnit.PixelsPerInch; - private readonly Dictionary formatMetadata = []; + private readonly Dictionary formatMetadata = []; private double horizontalResolution; private double verticalResolution; @@ -60,9 +60,9 @@ public sealed class ImageMetadata : IDeepCloneable this.VerticalResolution = other.VerticalResolution; this.ResolutionUnits = other.ResolutionUnits; - foreach (KeyValuePair meta in other.formatMetadata) + foreach (KeyValuePair meta in other.formatMetadata) { - this.formatMetadata.Add(meta.Key, meta.Value.DeepClone()); + this.formatMetadata.Add(meta.Key, (IFormatMetadata)meta.Value.DeepClone()); } this.ExifProfile = other.ExifProfile?.DeepClone(); @@ -170,7 +170,10 @@ public sealed class ImageMetadata : IDeepCloneable public IImageFormat? DecodedImageFormat { get; internal set; } /// - /// Gets the metadata value associated with the specified key. + /// Gets the metadata value associated with the specified key.
+ /// 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 type of metadata. /// The key of the value to get. @@ -178,47 +181,44 @@ public sealed class ImageMetadata : IDeepCloneable /// The . /// public TFormatMetadata GetFormatMetadata(IImageFormat key) - where TFormatMetadata : class, IDeepCloneable + where TFormatMetadata : class, IFormatMetadata { - if (this.formatMetadata.TryGetValue(key, out IDeepCloneable? meta)) + // Check for existing metadata. + if (this.formatMetadata.TryGetValue(key, out IFormatMetadata? meta)) { return (TFormatMetadata)meta; } + // None found. Check if we have a decoded format to convert from. + if (this.DecodedImageFormat is not null + && this.formatMetadata.TryGetValue(this.DecodedImageFormat, out IFormatMetadata? decodedMetadata)) + { + return TFormatMetadata.FromFormatConnectingMetadata(decodedMetadata.ToFormatConnectingMetadata()); + } + + // Fall back to a default instance. TFormatMetadata newMeta = key.CreateDefaultFormatMetadata(); this.formatMetadata[key] = newMeta; return newMeta; } /// - /// Gets the metadata value associated with the specified key. + /// Creates a new instance the metadata value associated with the specified key. + /// The instance is created from a clone generated via . /// - /// The type of format metadata. + /// The type of metadata. /// The key of the value to get. - /// - /// When this method returns, contains the metadata associated with the specified key, - /// if the key is found; otherwise, the default value for the type of the metadata parameter. - /// This parameter is passed uninitialized. - /// /// - /// if the frame metadata exists for the specified key; otherwise, . + /// The . /// - public bool TryGetFormatMetadata(IImageFormat key, out TFormatMetadata? metadata) - where TFormatMetadata : class, IDeepCloneable - { - if (this.formatMetadata.TryGetValue(key, out IDeepCloneable? meta)) - { - metadata = (TFormatMetadata)meta; - return true; - } - - metadata = default; - return false; - } + public TFormatMetadata CloneFormatMetadata(IImageFormat key) + where TFormatMetadata : class, IFormatMetadata, new() + => ((IDeepCloneable)this.GetFormatMetadata(key)).DeepClone(); /// public ImageMetadata DeepClone() => new(this); + /// TODO: This should be called on save. /// /// Synchronizes the profiles with the current metadata. /// diff --git a/src/ImageSharp/PixelFormats/PixelTypeInfo.cs b/src/ImageSharp/PixelFormats/PixelTypeInfo.cs index 7cd1284f48..7865b9900e 100644 --- a/src/ImageSharp/PixelFormats/PixelTypeInfo.cs +++ b/src/ImageSharp/PixelFormats/PixelTypeInfo.cs @@ -31,13 +31,13 @@ public readonly struct PixelTypeInfo(int bitsPerPixel) /// /// Gets the pixel color type. /// - public PixelColorType? ColorType { get; init; } + public PixelColorType ColorType { get; init; } /// /// Gets the pixel alpha transparency behavior. /// means unknown, unspecified. /// - public PixelAlphaRepresentation? AlphaRepresentation { get; init; } + public PixelAlphaRepresentation AlphaRepresentation { get; init; } /// /// Creates a new instance. diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpMetadataTests.cs index f7a398ec14..64564ae1d8 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpMetadataTests.cs @@ -16,7 +16,7 @@ public class BmpMetadataTests { BmpMetadata meta = new() { BitsPerPixel = BmpBitsPerPixel.Bit24, InfoHeaderType = BmpInfoHeaderType.Os2Version2 }; - BmpMetadata clone = (BmpMetadata)meta.DeepClone(); + BmpMetadata clone = meta.DeepClone(); clone.BitsPerPixel = BmpBitsPerPixel.Bit32; clone.InfoHeaderType = BmpInfoHeaderType.WinVersion2; diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs index aeb3bab7bb..77ac51e8a1 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs @@ -242,33 +242,11 @@ public class GifEncoderTests where TPixel : unmanaged, IPixel { using Image image = provider.GetImage(); - - int count = 0; - foreach (ImageFrame frame in image.Frames) - { - if (frame.Metadata.TryGetGifMetadata(out GifFrameMetadata _)) - { - count++; - } - } - provider.Utility.SaveTestOutputFile(image, extension: "gif"); using FileStream fs = File.OpenRead(provider.Utility.GetTestOutputFileName("gif")); using Image image2 = Image.Load(fs); - Assert.Equal(image.Frames.Count, image2.Frames.Count); - - count = 0; - foreach (ImageFrame frame in image2.Frames) - { - if (frame.Metadata.TryGetGifMetadata(out GifFrameMetadata _)) - { - count++; - } - } - - Assert.Equal(image2.Frames.Count, count); } [Theory] @@ -358,10 +336,10 @@ public class GifEncoderTests switch (webpF.DisposalMethod) { - case WebpDisposalMethod.RestoreToBackground: + case FrameDisposalMode.RestoreToBackground: Assert.Equal(FrameDisposalMode.RestoreToBackground, gifF.DisposalMode); break; - case WebpDisposalMethod.DoNotDispose: + case FrameDisposalMode.DoNotDispose: default: Assert.Equal(FrameDisposalMode.DoNotDispose, gifF.DisposalMode); break; diff --git a/tests/ImageSharp.Tests/Formats/Pbm/PbmMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Pbm/PbmMetadataTests.cs index a69d9d9ba7..243a62d592 100644 --- a/tests/ImageSharp.Tests/Formats/Pbm/PbmMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Pbm/PbmMetadataTests.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Pbm; using static SixLabors.ImageSharp.Tests.TestImages.Pbm; diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index f40e537a15..009327c17f 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -526,10 +526,10 @@ public partial class PngEncoderTests switch (webpF.BlendMethod) { - case WebpBlendMethod.Source: + case FrameBlendMode.Source: Assert.Equal(FrameBlendMode.Source, pngF.BlendMode); break; - case WebpBlendMethod.Over: + case FrameBlendMode.Over: default: Assert.Equal(FrameBlendMode.Over, pngF.BlendMode); break; @@ -537,10 +537,10 @@ public partial class PngEncoderTests switch (webpF.DisposalMethod) { - case WebpDisposalMethod.RestoreToBackground: + case FrameDisposalMode.RestoreToBackground: Assert.Equal(FrameDisposalMode.RestoreToBackground, pngF.DisposalMode); break; - case WebpDisposalMethod.DoNotDispose: + case FrameDisposalMode.DoNotDispose: default: Assert.Equal(FrameDisposalMode.DoNotDispose, pngF.DisposalMode); break; diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs index 4d72363075..aff1b35946 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs @@ -98,13 +98,13 @@ public class WebpEncoderTests switch (gifF.DisposalMode) { case FrameDisposalMode.RestoreToBackground: - Assert.Equal(WebpDisposalMethod.RestoreToBackground, webpF.DisposalMethod); + Assert.Equal(FrameDisposalMode.RestoreToBackground, webpF.DisposalMethod); break; case FrameDisposalMode.RestoreToPrevious: case FrameDisposalMode.Unspecified: case FrameDisposalMode.DoNotDispose: default: - Assert.Equal(WebpDisposalMethod.DoNotDispose, webpF.DisposalMethod); + Assert.Equal(FrameDisposalMode.DoNotDispose, webpF.DisposalMethod); break; } } @@ -147,22 +147,22 @@ public class WebpEncoderTests switch (pngF.BlendMode) { case FrameBlendMode.Source: - Assert.Equal(WebpBlendMethod.Source, webpF.BlendMethod); + Assert.Equal(FrameBlendMode.Source, webpF.BlendMethod); break; case FrameBlendMode.Over: default: - Assert.Equal(WebpBlendMethod.Over, webpF.BlendMethod); + Assert.Equal(FrameBlendMode.Over, webpF.BlendMethod); break; } switch (pngF.DisposalMode) { case FrameDisposalMode.RestoreToBackground: - Assert.Equal(WebpDisposalMethod.RestoreToBackground, webpF.DisposalMethod); + Assert.Equal(FrameDisposalMode.RestoreToBackground, webpF.DisposalMethod); break; case FrameDisposalMode.DoNotDispose: default: - Assert.Equal(WebpDisposalMethod.DoNotDispose, webpF.DisposalMethod); + Assert.Equal(FrameDisposalMode.DoNotDispose, webpF.DisposalMethod); break; } } @@ -220,7 +220,7 @@ public class WebpEncoderTests }; using Image image = provider.GetImage(); - string testOutputDetails = string.Concat("lossless", "_q", quality); + string testOutputDetails = $"lossless_q{quality}"; image.VerifyEncoder(provider, "webp", testOutputDetails, encoder); } @@ -250,7 +250,7 @@ public class WebpEncoderTests }; using Image image = provider.GetImage(); - string testOutputDetails = string.Concat("lossless", "_m", method, "_q", quality); + string testOutputDetails = $"lossless_m{method}_q{quality}"; image.VerifyEncoder(provider, "webp", testOutputDetails, encoder); } @@ -290,7 +290,7 @@ public class WebpEncoderTests }; using Image image = provider.GetImage(); - string testOutputDetails = string.Concat("nearlossless", "_q", nearLosslessQuality); + string testOutputDetails = $"nearlossless_q{nearLosslessQuality}"; image.VerifyEncoder(provider, "webp", testOutputDetails, encoder, customComparer: GetComparer(nearLosslessQuality)); } @@ -314,7 +314,7 @@ public class WebpEncoderTests }; using Image image = provider.GetImage(); - string testOutputDetails = string.Concat("lossless", "_m", method); + string testOutputDetails = $"lossless_m{method}"; image.VerifyEncoder(provider, "webp", testOutputDetails, encoder); } @@ -344,7 +344,7 @@ public class WebpEncoderTests }; using Image image = provider.GetImage(); - string testOutputDetails = string.Concat("lossy", "_q", quality); + string testOutputDetails = $"lossy_q{quality}"; image.VerifyEncoder(provider, "webp", testOutputDetails, encoder, customComparer: GetComparer(quality)); } @@ -364,7 +364,7 @@ public class WebpEncoderTests }; using Image image = provider.GetImage(); - string testOutputDetails = string.Concat("lossy", "_f", filterStrength); + string testOutputDetails = $"lossy_f{filterStrength}"; image.VerifyEncoder(provider, "webp", testOutputDetails, encoder, customComparer: GetComparer(75)); } @@ -384,7 +384,7 @@ public class WebpEncoderTests }; using Image image = provider.GetImage(); - string testOutputDetails = string.Concat("lossy", "_sns", snsStrength); + string testOutputDetails = $"lossy_sns{snsStrength}"; image.VerifyEncoder(provider, "webp", testOutputDetails, encoder, customComparer: GetComparer(75)); } @@ -414,7 +414,7 @@ public class WebpEncoderTests }; using Image image = provider.GetImage(); - string testOutputDetails = string.Concat("lossy", "_m", method, "_q", quality); + string testOutputDetails = $"lossy_m{method}_q{quality}"; image.VerifyEncoder(provider, "webp", testOutputDetails, encoder, customComparer: GetComparer(quality)); }