Browse Source

Complete conversion APIs

pull/2751/head
James Jackson-South 2 years ago
parent
commit
9a0e289d25
  1. 1
      src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
  2. 10
      src/ImageSharp/Formats/Bmp/BmpMetadata.cs
  3. 20
      src/ImageSharp/Formats/Bmp/MetadataExtensions.cs
  4. 20
      src/ImageSharp/Formats/EncodingType.cs
  5. 7
      src/ImageSharp/Formats/FormatConnectingMetadata.cs
  6. 49
      src/ImageSharp/Formats/Gif/GifEncoderCore.cs
  7. 28
      src/ImageSharp/Formats/Gif/GifMetadata.cs
  8. 97
      src/ImageSharp/Formats/Gif/MetadataExtensions.cs
  9. 2
      src/ImageSharp/Formats/IFormatFrameMetadata.cs
  10. 2
      src/ImageSharp/Formats/IFormatMetadata.cs
  11. 21
      src/ImageSharp/Formats/ImageDecoder.cs
  12. 3
      src/ImageSharp/Formats/Jpeg/JpegMetadata.cs
  13. 21
      src/ImageSharp/Formats/Jpeg/MetadataExtensions.cs
  14. 25
      src/ImageSharp/Formats/Pbm/IPbmEncoderOptions.cs
  15. 20
      src/ImageSharp/Formats/Pbm/MetadataExtensions.cs
  16. 2
      src/ImageSharp/Formats/Pbm/PbmEncoder.cs
  17. 1
      src/ImageSharp/Formats/Pbm/PbmEncoderCore.cs
  18. 2
      src/ImageSharp/Formats/Pbm/PbmMetadata.cs
  19. 84
      src/ImageSharp/Formats/Png/MetadataExtensions.cs
  20. 2
      src/ImageSharp/Formats/Png/PngDecoder.cs
  21. 56
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  22. 24
      src/ImageSharp/Formats/Png/PngFrameMetadata.cs
  23. 35
      src/ImageSharp/Formats/Png/PngMetadata.cs
  24. 20
      src/ImageSharp/Formats/Qoi/MetadataExtensions.cs
  25. 2
      src/ImageSharp/Formats/Qoi/QoiMetadata.cs
  26. 20
      src/ImageSharp/Formats/Tga/MetadataExtensions.cs
  27. 27
      src/ImageSharp/Formats/Tiff/MetadataExtensions.cs
  28. 1
      src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
  29. 20
      src/ImageSharp/Formats/Webp/Chunks/WebpFrameData.cs
  30. 2
      src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs
  31. 6
      src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs
  32. 75
      src/ImageSharp/Formats/Webp/MetadataExtensions.cs
  33. 4
      src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs
  34. 22
      src/ImageSharp/Formats/Webp/WebpBlendMethod.cs
  35. 50
      src/ImageSharp/Formats/Webp/WebpCommonUtils.cs
  36. 20
      src/ImageSharp/Formats/Webp/WebpDisposalMethod.cs
  37. 23
      src/ImageSharp/Formats/Webp/WebpEncoderCore.cs
  38. 4
      src/ImageSharp/Formats/Webp/WebpFileFormatType.cs
  39. 37
      src/ImageSharp/Formats/Webp/WebpFrameMetadata.cs
  40. 17
      src/ImageSharp/Formats/Webp/WebpMetadata.cs
  41. 2
      src/ImageSharp/Formats/_Generated/ImageExtensions.Save.cs
  42. 19
      src/ImageSharp/Formats/_Generated/ImageExtensions.Save.tt
  43. 283
      src/ImageSharp/Formats/_Generated/ImageMetadataExtensions.cs
  44. 77
      src/ImageSharp/Formats/_Generated/ImageMetadataExtensions.tt
  45. 24
      src/ImageSharp/Formats/_Generated/_Formats.ttinclude
  46. 13
      src/ImageSharp/ImageSharp.csproj
  47. 59
      src/ImageSharp/Metadata/ImageFrameMetadata.cs
  48. 52
      src/ImageSharp/Metadata/ImageMetadata.cs
  49. 4
      src/ImageSharp/PixelFormats/PixelTypeInfo.cs
  50. 2
      tests/ImageSharp.Tests/Formats/Bmp/BmpMetadataTests.cs
  51. 26
      tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs
  52. 1
      tests/ImageSharp.Tests/Formats/Pbm/PbmMetadataTests.cs
  53. 8
      tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs
  54. 28
      tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs

1
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;

10
src/ImageSharp/Formats/Bmp/BmpMetadata.cs

@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp;
/// <summary>
/// Provides Bmp specific metadata information for the image.
/// </summary>
public class BmpMetadata : IFormatMetadata<BmpMetadata>, IFormatFrameMetadata<BmpMetadata>
public class BmpMetadata : IFormatMetadata<BmpMetadata>
{
/// <summary>
/// Initializes a new instance of the <see cref="BmpMetadata"/> class.
@ -66,10 +66,6 @@ public class BmpMetadata : IFormatMetadata<BmpMetadata>, IFormatFrameMetadata<Bm
};
}
/// <inheritdoc/>
public static BmpMetadata FromFormatConnectingFrameMetadata(FormatConnectingFrameMetadata metadata)
=> new();
/// <inheritdoc/>
public PixelTypeInfo GetPixelTypeInfo()
{
@ -140,10 +136,6 @@ public class BmpMetadata : IFormatMetadata<BmpMetadata>, IFormatFrameMetadata<Bm
PixelTypeInfo = this.GetPixelTypeInfo()
};
/// <inheritdoc/>
public FormatConnectingFrameMetadata ToFormatConnectingFrameMetadata()
=> new();
/// <inheritdoc/>
IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone();

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

@ -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;
/// <summary>
/// Extension methods for the <see cref="ImageMetadata"/> type.
/// </summary>
public static partial class MetadataExtensions
{
/// <summary>
/// Gets the bmp format specific metadata for the image.
/// </summary>
/// <param name="metadata">The metadata this method extends.</param>
/// <returns>The <see cref="BmpMetadata"/>.</returns>
public static BmpMetadata GetBmpMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(BmpFormat.Instance);
}

20
src/ImageSharp/Formats/EncodingType.cs

@ -0,0 +1,20 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats;
/// <summary>
/// Provides a way to specify the type of encoding to be used.
/// </summary>
public enum EncodingType
{
/// <summary>
/// Lossless encoding, which compresses data without any loss of information.
/// </summary>
Lossless,
/// <summary>
/// Lossy encoding, which compresses data by discarding some of it.
/// </summary>
Lossy
}

7
src/ImageSharp/Formats/FormatConnectingMetadata.cs

@ -11,7 +11,12 @@ namespace SixLabors.ImageSharp.Formats;
public class FormatConnectingMetadata
{
/// <summary>
/// Gets the quality.
/// Gets the encoding type.
/// </summary>
public EncodingType EncodingType { get; init; }
/// <summary>
/// Gets the quality to use when <see cref="EncodingType"/> is <see cref="EncodingType.Lossy"/>.
/// </summary>
/// <remarks>
/// The value is usually between 1 and 100. Defaults to 100.

49
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<TPixel>(Image<TPixel> image)
where TPixel : unmanaged, IPixel<TPixel>
{
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<TPixel>(ImageFrame<TPixel> frame, int transparencyIndex)
where TPixel : unmanaged, IPixel<TPixel>
{
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<TPixel>(

28
src/ImageSharp/Formats/Gif/GifMetadata.cs

@ -69,34 +69,6 @@ public class GifMetadata : IFormatMetadata<GifMetadata>
/// </summary>
public IList<string> Comments { get; set; } = [];
internal static GifMetadata FromAnimatedMetadata(AnimatedImageMetadata metadata)
{
int index = 0;
Color background = metadata.BackgroundColor;
if (metadata.ColorTable.HasValue)
{
ReadOnlySpan<Color> 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),
};
}
/// <inheritdoc/>
public static GifMetadata FromFormatConnectingMetadata(FormatConnectingMetadata metadata)
{

97
src/ImageSharp/Formats/Gif/MetadataExtensions.cs

@ -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;
/// <summary>
/// Extension methods for the <see cref="ImageMetadata"/> type.
/// </summary>
public static partial class MetadataExtensions
{
/// <summary>
/// Gets the gif format specific metadata for the image.
/// </summary>
/// <param name="source">The metadata this method extends.</param>
/// <returns>The <see cref="GifMetadata"/>.</returns>
public static GifMetadata GetGifMetadata(this ImageMetadata source)
=> source.GetFormatMetadata(GifFormat.Instance);
/// <summary>
/// Gets the gif format specific metadata for the image.
/// </summary>
/// <param name="source">The metadata this method extends.</param>
/// <param name="metadata">
/// 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.
/// </param>
/// <returns>
/// <see langword="true"/> if the gif metadata exists; otherwise, <see langword="false"/>.
/// </returns>
public static bool TryGetGifMetadata(this ImageMetadata source, [NotNullWhen(true)] out GifMetadata? metadata)
=> source.TryGetFormatMetadata(GifFormat.Instance, out metadata);
/// <summary>
/// Gets the gif format specific metadata for the image frame.
/// </summary>
/// <param name="source">The metadata this method extends.</param>
/// <returns>The <see cref="GifFrameMetadata"/>.</returns>
public static GifFrameMetadata GetGifMetadata(this ImageFrameMetadata source)
=> source.GetFormatMetadata(GifFormat.Instance);
/// <summary>
/// Gets the gif format specific metadata for the image frame.
/// </summary>
/// <param name="source">The metadata this method extends.</param>
/// <param name="metadata">
/// 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.
/// </param>
/// <returns>
/// <see langword="true"/> if the gif frame metadata exists; otherwise, <see langword="false"/>.
/// </returns>
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,
};
}
}

2
src/ImageSharp/Formats/IFormatFrameMetadata.cs

@ -20,7 +20,7 @@ public interface IFormatFrameMetadata : IDeepCloneable
/// </summary>
/// <typeparam name="TSelf">The metadata type implementing this interface.</typeparam>
public interface IFormatFrameMetadata<TSelf> : IFormatFrameMetadata, IDeepCloneable<TSelf>
where TSelf : class, IFormatFrameMetadata, new()
where TSelf : class, IFormatFrameMetadata
{
/// <summary>
/// Creates a new instance of the <typeparamref name="TSelf"/> class from the given <see cref="FormatConnectingFrameMetadata"/>.

2
src/ImageSharp/Formats/IFormatMetadata.cs

@ -28,7 +28,7 @@ public interface IFormatMetadata : IDeepCloneable
/// </summary>
/// <typeparam name="TSelf">The metadata type implementing this interface.</typeparam>
public interface IFormatMetadata<TSelf> : IFormatMetadata, IDeepCloneable<TSelf>
where TSelf : class, IFormatMetadata, new()
where TSelf : class, IFormatMetadata
{
/// <summary>
/// Creates a new instance of the <typeparamref name="TSelf"/> class from the given <see cref="FormatConnectingMetadata"/>.

21
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<T> PeformActionAndResetPosition(Stream s, long position, CancellationToken ct)
Task<T> 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<T> CopyToMemoryStreamAndActionAsync<T>(
@ -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;
}
}
}
}

3
src/ImageSharp/Formats/Jpeg/JpegMetadata.cs

@ -112,7 +112,7 @@ public class JpegMetadata : IFormatMetadata<JpegMetadata>
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<JpegMetadata>
public FormatConnectingMetadata ToFormatConnectingMetadata()
=> new()
{
EncodingType = EncodingType.Lossy,
PixelTypeInfo = this.GetPixelTypeInfo(),
Quality = this.Quality,
};

21
src/ImageSharp/Formats/Jpeg/MetadataExtensions.cs

@ -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;
/// <summary>
/// Extension methods for the <see cref="ImageMetadata"/> type.
/// </summary>
public static partial class MetadataExtensions
{
/// <summary>
/// Gets the jpeg format specific metadata for the image.
/// </summary>
/// <param name="metadata">The metadata this method extends.</param>
/// <returns>The <see cref="JpegMetadata"/>.</returns>
public static JpegMetadata GetJpegMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(JpegFormat.Instance);
}

25
src/ImageSharp/Formats/Pbm/IPbmEncoderOptions.cs

@ -1,25 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Pbm;
/// <summary>
/// Configuration options for use during PBM encoding.
/// </summary>
internal interface IPbmEncoderOptions
{
/// <summary>
/// Gets the encoding of the pixels.
/// </summary>
PbmEncoding? Encoding { get; }
/// <summary>
/// Gets the Color type of the resulting image.
/// </summary>
PbmColorType? ColorType { get; }
/// <summary>
/// Gets the Data Type of the pixel components.
/// </summary>
PbmComponentType? ComponentType { get; }
}

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

@ -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;
/// <summary>
/// Extension methods for the <see cref="ImageMetadata"/> type.
/// </summary>
public static partial class MetadataExtensions
{
/// <summary>
/// Gets the pbm format specific metadata for the image.
/// </summary>
/// <param name="metadata">The metadata this method extends.</param>
/// <returns>The <see cref="PbmMetadata"/>.</returns>
public static PbmMetadata GetPbmMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(PbmFormat.Instance);
}

2
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;
/// <summary>

1
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;

2
src/ImageSharp/Formats/Pbm/PbmMetadata.cs

@ -46,7 +46,7 @@ public class PbmMetadata : IFormatMetadata<PbmMetadata>
public static PbmMetadata FromFormatConnectingMetadata(FormatConnectingMetadata metadata)
{
PbmColorType color;
PixelColorType colorType = metadata.PixelTypeInfo.ColorType ?? PixelColorType.Luminance;
PixelColorType colorType = metadata.PixelTypeInfo.ColorType;
switch (colorType)
{

84
src/ImageSharp/Formats/Png/MetadataExtensions.cs

@ -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;
/// <summary>
/// Extension methods for the <see cref="ImageMetadata"/> type.
/// </summary>
public static partial class MetadataExtensions
{
/// <summary>
/// Gets the png format specific metadata for the image.
/// </summary>
/// <param name="source">The metadata this method extends.</param>
/// <returns>The <see cref="PngMetadata"/>.</returns>
public static PngMetadata GetPngMetadata(this ImageMetadata source) => source.GetFormatMetadata(PngFormat.Instance);
/// <summary>
/// Gets the png format specific metadata for the image.
/// </summary>
/// <param name="source">The metadata this method extends.</param>
/// <param name="metadata">The metadata.</param>
/// <returns>
/// <see langword="true"/> if the png metadata exists; otherwise, <see langword="false"/>.
/// </returns>
public static bool TryGetPngMetadata(this ImageMetadata source, [NotNullWhen(true)] out PngMetadata? metadata)
=> source.TryGetFormatMetadata(PngFormat.Instance, out metadata);
/// <summary>
/// Gets the png format specific metadata for the image frame.
/// </summary>
/// <param name="source">The metadata this method extends.</param>
/// <returns>The <see cref="PngFrameMetadata"/>.</returns>
public static PngFrameMetadata GetPngMetadata(this ImageFrameMetadata source) => source.GetFormatMetadata(PngFormat.Instance);
/// <summary>
/// Gets the png format specific metadata for the image frame.
/// </summary>
/// <param name="source">The metadata this method extends.</param>
/// <param name="metadata">The metadata.</param>
/// <returns>
/// <see langword="true"/> if the png frame metadata exists; otherwise, <see langword="false"/>.
/// </returns>
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,
};
}

2
src/ImageSharp/Formats/Png/PngDecoder.cs

@ -101,5 +101,5 @@ public sealed class PngDecoder : SpecializedImageDecoder<PngDecoderOptions>
}
/// <inheritdoc/>
protected override PngDecoderOptions CreateDefaultSpecializedOptions(DecoderOptions options) => new PngDecoderOptions() { GeneralOptions = options };
protected override PngDecoderOptions CreateDefaultSpecializedOptions(DecoderOptions options) => new() { GeneralOptions = options };
}

56
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<TPixel>(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<TPixel>? 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<TPixel>(Image<TPixel> image)
where TPixel : unmanaged, IPixel<TPixel>
{
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<TPixel>(ImageFrame<TPixel> frame)
where TPixel : unmanaged, IPixel<TPixel>
{
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();
}
/// <summary>
/// Convert transparent pixels, to transparent black pixels, which can yield to better compression in some cases.
/// </summary>

24
src/ImageSharp/Formats/Png/PngFrameMetadata.cs

@ -57,22 +57,6 @@ public class PngFrameMetadata : IFormatFrameMetadata<PngFrameMetadata>
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,
};
/// <inheritdoc/>
public static PngFrameMetadata FromFormatConnectingFrameMetadata(FormatConnectingFrameMetadata metadata)
=> new()
@ -105,4 +89,12 @@ public class PngFrameMetadata : IFormatFrameMetadata<PngFrameMetadata>
/// <inheritdoc/>
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,
};
}

35
src/ImageSharp/Formats/Png/PngMetadata.cs

@ -90,38 +90,6 @@ public class PngMetadata : IFormatMetadata<PngMetadata>
/// </summary>
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,
};
}
/// <inheritdoc/>
public static PngMetadata FromFormatConnectingMetadata(FormatConnectingMetadata metadata)
{
@ -145,8 +113,7 @@ public class PngMetadata : IFormatMetadata<PngMetadata>
}
PngColorType color;
PixelColorType colorType =
metadata.PixelTypeInfo.ColorType ?? PixelColorType.RGB | PixelColorType.Alpha;
PixelColorType colorType = metadata.PixelTypeInfo.ColorType;
switch (colorType)
{

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

@ -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;
/// <summary>
/// Extension methods for the <see cref="ImageMetadata"/> type.
/// </summary>
public static partial class MetadataExtensions
{
/// <summary>
/// Gets the qoi format specific metadata for the image.
/// </summary>
/// <param name="metadata">The metadata this method extends.</param>
/// <returns>The <see cref="QoiMetadata"/>.</returns>
public static QoiMetadata GetQoiMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(QoiFormat.Instance);
}

2
src/ImageSharp/Formats/Qoi/QoiMetadata.cs

@ -40,7 +40,7 @@ public class QoiMetadata : IFormatMetadata<QoiMetadata>
/// <inheritdoc/>
public static QoiMetadata FromFormatConnectingMetadata(FormatConnectingMetadata metadata)
{
PixelColorType color = metadata.PixelTypeInfo.ColorType ?? PixelColorType.RGB;
PixelColorType color = metadata.PixelTypeInfo.ColorType;
if (color.HasFlag(PixelColorType.Alpha))
{

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

@ -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;
/// <summary>
/// Extension methods for the <see cref="ImageMetadata"/> type.
/// </summary>
public static partial class MetadataExtensions
{
/// <summary>
/// Gets the tga format specific metadata for the image.
/// </summary>
/// <param name="metadata">The metadata this method extends.</param>
/// <returns>The <see cref="TgaMetadata"/>.</returns>
public static TgaMetadata GetTgaMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(TgaFormat.Instance);
}

27
src/ImageSharp/Formats/Tiff/MetadataExtensions.cs

@ -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;
/// <summary>
/// Extension methods for the <see cref="ImageMetadata"/> type.
/// </summary>
public static partial class MetadataExtensions
{
/// <summary>
/// Gets the tiff format specific metadata for the image.
/// </summary>
/// <param name="metadata">The metadata this method extends.</param>
/// <returns>The <see cref="TiffMetadata"/>.</returns>
public static TiffMetadata GetTiffMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(TiffFormat.Instance);
/// <summary>
/// Gets the tiff format specific metadata for the image frame.
/// </summary>
/// <param name="metadata">The metadata this method extends.</param>
/// <returns>The <see cref="TiffFrameMetadata"/>.</returns>
public static TiffFrameMetadata GetTiffMetadata(this ImageFrameMetadata metadata) => metadata.GetFormatMetadata(TiffFormat.Instance);
}

1
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:

20
src/ImageSharp/Formats/Webp/Chunks/WebpFrameData.cs

@ -10,7 +10,7 @@ internal readonly struct WebpFrameData
/// </summary>
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
/// <summary>
/// Gets how transparent pixels of the current frame are to be blended with corresponding pixels of the previous canvas.
/// </summary>
public WebpBlendMethod BlendingMethod { get; }
public FrameBlendMode BlendingMethod { get; }
/// <summary>
/// Gets how the current frame is to be treated after it has been displayed (before rendering the next frame) on the canvas.
/// </summary>
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<byte> 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;
}
}

2
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);
}

6
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<TPixel>
{
ImageFrame<TPixel> 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);
}
/// <summary>
@ -462,7 +462,7 @@ internal class Vp8Encoder : IDisposable
// Extract and encode alpha channel data, if present.
int alphaDataSize = 0;
bool alphaCompressionSucceeded = false;
Span<byte> alphaData = Span<byte>.Empty;
Span<byte> alphaData = [];
IMemoryOwner<byte> encodedAlphaData = null;
try
{

75
src/ImageSharp/Formats/Webp/MetadataExtensions.cs

@ -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;
/// <summary>
/// Extension methods for the <see cref="ImageMetadata"/> type.
/// </summary>
public static partial class MetadataExtensions
{
/// <summary>
/// Gets the webp format specific metadata for the image.
/// </summary>
/// <param name="metadata">The metadata this method extends.</param>
/// <returns>The <see cref="WebpMetadata"/>.</returns>
public static WebpMetadata GetWebpMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(WebpFormat.Instance);
/// <summary>
/// Gets the webp format specific metadata for the image.
/// </summary>
/// <param name="source">The metadata this method extends.</param>
/// <param name="metadata">The metadata.</param>
/// <returns>
/// <see langword="true"/> if the webp metadata exists; otherwise, <see langword="false"/>.
/// </returns>
public static bool TryGetWebpMetadata(this ImageMetadata source, [NotNullWhen(true)] out WebpMetadata? metadata)
=> source.TryGetFormatMetadata(WebpFormat.Instance, out metadata);
/// <summary>
/// Gets the webp format specific metadata for the image frame.
/// </summary>
/// <param name="metadata">The metadata this method extends.</param>
/// <returns>The <see cref="WebpFrameMetadata"/>.</returns>
public static WebpFrameMetadata GetWebpMetadata(this ImageFrameMetadata metadata) => metadata.GetFormatMetadata(WebpFormat.Instance);
/// <summary>
/// Gets the webp format specific metadata for the image frame.
/// </summary>
/// <param name="source">The metadata this method extends.</param>
/// <param name="metadata">The metadata.</param>
/// <returns>
/// <see langword="true"/> if the webp frame metadata exists; otherwise, <see langword="false"/>.
/// </returns>
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,
};
}

4
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<TPixel> decodedImageFrame = this.DecodeImageFrameData<TPixel>(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;

22
src/ImageSharp/Formats/Webp/WebpBlendMethod.cs

@ -1,22 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Webp;
/// <summary>
/// Indicates how transparent pixels of the current frame are to be blended with corresponding pixels of the previous canvas.
/// </summary>
public enum WebpBlendMethod
{
/// <summary>
/// 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.
/// </summary>
Source = 0,
/// <summary>
/// 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.
/// </summary>
Over = 1,
}

50
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;
/// </summary>
internal static class WebpCommonUtils
{
public static WebpMetadata GetWebpMetadata<TPixel>(Image<TPixel> image)
where TPixel : unmanaged, IPixel<TPixel>
{
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<TPixel>(ImageFrame<TPixel> frame)
where TPixel : unmanaged, IPixel<TPixel>
{
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();
}
/// <summary>
/// Checks if the pixel row is not opaque.
/// </summary>

20
src/ImageSharp/Formats/Webp/WebpDisposalMethod.cs

@ -1,20 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Webp;
/// <summary>
/// Indicates how the current frame is to be treated after it has been displayed (before rendering the next frame) on the canvas.
/// </summary>
public enum WebpDisposalMethod
{
/// <summary>
/// Do not dispose. Leave the canvas as is.
/// </summary>
DoNotDispose = 0,
/// <summary>
/// Dispose to background color. Fill the rectangle on the canvas covered by the current frame with background color specified in the ANIM chunk.
/// </summary>
RestoreToBackground = 1
}

23
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<TPixel> 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<TPixel>? prev = previousDisposal == WebpDisposalMethod.RestoreToBackground ? null : previousFrame;
ImageFrame<TPixel>? prev = previousDisposal == FrameDisposalMode.RestoreToBackground ? null : previousFrame;
ImageFrame<TPixel> currentFrame = image.Frames[i];
ImageFrame<TPixel>? 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<TPixel> 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<TPixel>? prev = previousDisposal == WebpDisposalMethod.RestoreToBackground ? null : previousFrame;
ImageFrame<TPixel>? prev = previousDisposal == FrameDisposalMode.RestoreToBackground ? null : previousFrame;
ImageFrame<TPixel> currentFrame = image.Frames[i];
ImageFrame<TPixel>? 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(

4
src/ImageSharp/Formats/Webp/WebpFileFormatType.cs

@ -9,12 +9,12 @@ namespace SixLabors.ImageSharp.Formats.Webp;
public enum WebpFileFormatType
{
/// <summary>
/// The lossless webp format.
/// The lossless Webp format, which compresses data without any loss of information.
/// </summary>
Lossless,
/// <summary>
/// The lossy webp format.
/// The lossy Webp format, which compresses data by discarding some of it.
/// </summary>
Lossy,
}

37
src/ImageSharp/Formats/Webp/WebpFrameMetadata.cs

@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.Webp;
/// <summary>
/// Provides webp specific metadata information for the image frame.
/// </summary>
public class WebpFrameMetadata : IDeepCloneable
public class WebpFrameMetadata : IFormatFrameMetadata<WebpFrameMetadata>
{
/// <summary>
/// Initializes a new instance of the <see cref="WebpFrameMetadata"/> class.
@ -29,12 +29,12 @@ public class WebpFrameMetadata : IDeepCloneable
/// <summary>
/// Gets or sets how transparent pixels of the current frame are to be blended with corresponding pixels of the previous canvas.
/// </summary>
public WebpBlendMethod BlendMethod { get; set; }
public FrameBlendMode BlendMethod { get; set; }
/// <summary>
/// Gets or sets how the current frame is to be treated after it has been displayed (before rendering the next frame) on the canvas.
/// </summary>
public WebpDisposalMethod DisposalMethod { get; set; }
public FrameDisposalMode DisposalMethod { get; set; }
/// <summary>
/// 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; }
/// <inheritdoc/>
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)
};
/// <inheritdoc/>
public FormatConnectingFrameMetadata ToFormatConnectingFrameMetadata()
=> new()
{
ColorTableMode = FrameColorTableMode.Global,
Duration = TimeSpan.FromMilliseconds(this.FrameDelay),
DisposalMode = this.DisposalMethod,
BlendMode = this.BlendMethod,
};
/// <inheritdoc/>
IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone();
/// <inheritdoc/>
public WebpFrameMetadata DeepClone() => new(this);
private static FrameDisposalMode GetMode(FrameDisposalMode mode) => mode switch
{
FrameDisposalMode.RestoreToBackground => FrameDisposalMode.RestoreToBackground,
FrameDisposalMode.DoNotDispose => FrameDisposalMode.DoNotDispose,
_ => FrameDisposalMode.DoNotDispose,
};
}

17
src/ImageSharp/Formats/Webp/WebpMetadata.cs

@ -54,24 +54,16 @@ public class WebpMetadata : IFormatMetadata<WebpMetadata>
/// 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 <see cref="WebpDisposalMethod.RestoreToBackground"/>.
/// The background color is also used when the Disposal method is <see cref="FrameDisposalMode.RestoreToBackground"/>.
/// </summary>
public Color BackgroundColor { get; set; }
internal static WebpMetadata FromAnimatedMetadata(AnimatedImageMetadata metadata)
=> new()
{
FileFormat = WebpFileFormatType.Lossless,
BackgroundColor = metadata.BackgroundColor,
RepeatCount = metadata.RepeatCount
};
/// <inheritdoc/>
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<WebpMetadata>
{
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<WebpMetadata>
public FormatConnectingMetadata ToFormatConnectingMetadata()
=> new()
{
EncodingType = this.FileFormat == WebpFileFormatType.Lossless ? EncodingType.Lossless : EncodingType.Lossy,
PixelTypeInfo = this.GetPixelTypeInfo(),
ColorTableMode = FrameColorTableMode.Global,
RepeatCount = this.RepeatCount,

2
src/ImageSharp/Formats/ImageExtensions.Save.cs → src/ImageSharp/Formats/_Generated/ImageExtensions.Save.cs

@ -2,8 +2,6 @@
// Licensed under the Six Labors Split License.
// <auto-generated />
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Formats.Jpeg;

19
src/ImageSharp/Formats/ImageExtensions.Save.tt → 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.
// <auto-generated />
using SixLabors.ImageSharp.Advanced;
<#
var formats = new []{
"Bmp",
"Gif",
"Jpeg",
"Pbm",
"Png",
"Qoi",
"Tga",
"Tiff",
"Webp",
};
foreach (string fmt in formats)
{
#>

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

@ -0,0 +1,283 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
// <auto-generated />
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;
/// <summary>
/// Extension methods for the <see cref="ImageMetadata"/> and <see cref="ImageFrameMetadata"/> types.
/// </summary>
public static class ImageMetadataExtensions
{
/// <summary>
/// Gets the <see cref="BmpMetadata"/> from <paramref name="source"/>.<br/>
/// If none is found, an instance is created either by conversion from the decoded image format metadata
/// or the requested format default constructor.
/// This instance will be added to the metadata for future requests.
/// </summary>
/// <param name="source">The image metadata.</param>
/// <returns>
/// The <see cref="BmpMetadata"/>
/// </returns>
public static BmpMetadata GetBmpMetadata(this ImageMetadata source) => source.GetFormatMetadata(BmpFormat.Instance);
/// <summary>
/// Creates a new cloned instance of <see cref="BmpMetadata"/> from the <paramref name="source"/>.
/// The instance is created via <see cref="GetBmpMetadata(ImageMetadata)"/>
/// </summary>
/// <param name="source">The image metadata.</param>
/// <returns>The new <see cref="BmpMetadata"/></returns>
public static BmpMetadata CloneBmpMetadata(this ImageMetadata source) => source.CloneFormatMetadata(BmpFormat.Instance);
/// <summary>
/// Gets the <see cref="GifMetadata"/> from <paramref name="source"/>.<br/>
/// If none is found, an instance is created either by conversion from the decoded image format metadata
/// or the requested format default constructor.
/// This instance will be added to the metadata for future requests.
/// </summary>
/// <param name="source">The image metadata.</param>
/// <returns>
/// The <see cref="GifMetadata"/>
/// </returns>
public static GifMetadata GetGifMetadata(this ImageMetadata source) => source.GetFormatMetadata(GifFormat.Instance);
/// <summary>
/// Creates a new cloned instance of <see cref="GifMetadata"/> from the <paramref name="source"/>.
/// The instance is created via <see cref="GetGifMetadata(ImageMetadata)"/>
/// </summary>
/// <param name="source">The image metadata.</param>
/// <returns>The new <see cref="GifMetadata"/></returns>
public static GifMetadata CloneGifMetadata(this ImageMetadata source) => source.CloneFormatMetadata(GifFormat.Instance);
/// <summary>
/// Gets the <see cref="JpegMetadata"/> from <paramref name="source"/>.<br/>
/// If none is found, an instance is created either by conversion from the decoded image format metadata
/// or the requested format default constructor.
/// This instance will be added to the metadata for future requests.
/// </summary>
/// <param name="source">The image metadata.</param>
/// <returns>
/// The <see cref="JpegMetadata"/>
/// </returns>
public static JpegMetadata GetJpegMetadata(this ImageMetadata source) => source.GetFormatMetadata(JpegFormat.Instance);
/// <summary>
/// Creates a new cloned instance of <see cref="JpegMetadata"/> from the <paramref name="source"/>.
/// The instance is created via <see cref="GetJpegMetadata(ImageMetadata)"/>
/// </summary>
/// <param name="source">The image metadata.</param>
/// <returns>The new <see cref="JpegMetadata"/></returns>
public static JpegMetadata CloneJpegMetadata(this ImageMetadata source) => source.CloneFormatMetadata(JpegFormat.Instance);
/// <summary>
/// Gets the <see cref="PbmMetadata"/> from <paramref name="source"/>.<br/>
/// If none is found, an instance is created either by conversion from the decoded image format metadata
/// or the requested format default constructor.
/// This instance will be added to the metadata for future requests.
/// </summary>
/// <param name="source">The image metadata.</param>
/// <returns>
/// The <see cref="PbmMetadata"/>
/// </returns>
public static PbmMetadata GetPbmMetadata(this ImageMetadata source) => source.GetFormatMetadata(PbmFormat.Instance);
/// <summary>
/// Creates a new cloned instance of <see cref="PbmMetadata"/> from the <paramref name="source"/>.
/// The instance is created via <see cref="GetPbmMetadata(ImageMetadata)"/>
/// </summary>
/// <param name="source">The image metadata.</param>
/// <returns>The new <see cref="PbmMetadata"/></returns>
public static PbmMetadata ClonePbmMetadata(this ImageMetadata source) => source.CloneFormatMetadata(PbmFormat.Instance);
/// <summary>
/// Gets the <see cref="PngMetadata"/> from <paramref name="source"/>.<br/>
/// If none is found, an instance is created either by conversion from the decoded image format metadata
/// or the requested format default constructor.
/// This instance will be added to the metadata for future requests.
/// </summary>
/// <param name="source">The image metadata.</param>
/// <returns>
/// The <see cref="PngMetadata"/>
/// </returns>
public static PngMetadata GetPngMetadata(this ImageMetadata source) => source.GetFormatMetadata(PngFormat.Instance);
/// <summary>
/// Creates a new cloned instance of <see cref="PngMetadata"/> from the <paramref name="source"/>.
/// The instance is created via <see cref="GetPngMetadata(ImageMetadata)"/>
/// </summary>
/// <param name="source">The image metadata.</param>
/// <returns>The new <see cref="PngMetadata"/></returns>
public static PngMetadata ClonePngMetadata(this ImageMetadata source) => source.CloneFormatMetadata(PngFormat.Instance);
/// <summary>
/// Gets the <see cref="QoiMetadata"/> from <paramref name="source"/>.<br/>
/// If none is found, an instance is created either by conversion from the decoded image format metadata
/// or the requested format default constructor.
/// This instance will be added to the metadata for future requests.
/// </summary>
/// <param name="source">The image metadata.</param>
/// <returns>
/// The <see cref="QoiMetadata"/>
/// </returns>
public static QoiMetadata GetQoiMetadata(this ImageMetadata source) => source.GetFormatMetadata(QoiFormat.Instance);
/// <summary>
/// Creates a new cloned instance of <see cref="QoiMetadata"/> from the <paramref name="source"/>.
/// The instance is created via <see cref="GetQoiMetadata(ImageMetadata)"/>
/// </summary>
/// <param name="source">The image metadata.</param>
/// <returns>The new <see cref="QoiMetadata"/></returns>
public static QoiMetadata CloneQoiMetadata(this ImageMetadata source) => source.CloneFormatMetadata(QoiFormat.Instance);
/// <summary>
/// Gets the <see cref="TgaMetadata"/> from <paramref name="source"/>.<br/>
/// If none is found, an instance is created either by conversion from the decoded image format metadata
/// or the requested format default constructor.
/// This instance will be added to the metadata for future requests.
/// </summary>
/// <param name="source">The image metadata.</param>
/// <returns>
/// The <see cref="TgaMetadata"/>
/// </returns>
public static TgaMetadata GetTgaMetadata(this ImageMetadata source) => source.GetFormatMetadata(TgaFormat.Instance);
/// <summary>
/// Creates a new cloned instance of <see cref="TgaMetadata"/> from the <paramref name="source"/>.
/// The instance is created via <see cref="GetTgaMetadata(ImageMetadata)"/>
/// </summary>
/// <param name="source">The image metadata.</param>
/// <returns>The new <see cref="TgaMetadata"/></returns>
public static TgaMetadata CloneTgaMetadata(this ImageMetadata source) => source.CloneFormatMetadata(TgaFormat.Instance);
/// <summary>
/// Gets the <see cref="TiffMetadata"/> from <paramref name="source"/>.<br/>
/// If none is found, an instance is created either by conversion from the decoded image format metadata
/// or the requested format default constructor.
/// This instance will be added to the metadata for future requests.
/// </summary>
/// <param name="source">The image metadata.</param>
/// <returns>
/// The <see cref="TiffMetadata"/>
/// </returns>
public static TiffMetadata GetTiffMetadata(this ImageMetadata source) => source.GetFormatMetadata(TiffFormat.Instance);
/// <summary>
/// Creates a new cloned instance of <see cref="TiffMetadata"/> from the <paramref name="source"/>.
/// The instance is created via <see cref="GetTiffMetadata(ImageMetadata)"/>
/// </summary>
/// <param name="source">The image metadata.</param>
/// <returns>The new <see cref="TiffMetadata"/></returns>
public static TiffMetadata CloneTiffMetadata(this ImageMetadata source) => source.CloneFormatMetadata(TiffFormat.Instance);
/// <summary>
/// Gets the <see cref="WebpMetadata"/> from <paramref name="source"/>.<br/>
/// If none is found, an instance is created either by conversion from the decoded image format metadata
/// or the requested format default constructor.
/// This instance will be added to the metadata for future requests.
/// </summary>
/// <param name="source">The image metadata.</param>
/// <returns>
/// The <see cref="WebpMetadata"/>
/// </returns>
public static WebpMetadata GetWebpMetadata(this ImageMetadata source) => source.GetFormatMetadata(WebpFormat.Instance);
/// <summary>
/// Creates a new cloned instance of <see cref="WebpMetadata"/> from the <paramref name="source"/>.
/// The instance is created via <see cref="GetWebpMetadata(ImageMetadata)"/>
/// </summary>
/// <param name="source">The image metadata.</param>
/// <returns>The new <see cref="WebpMetadata"/></returns>
public static WebpMetadata CloneWebpMetadata(this ImageMetadata source) => source.CloneFormatMetadata(WebpFormat.Instance);
/// <summary>
/// Gets the <see cref="GifFrameMetadata"/> from <paramref name="source"/>.<br/>
/// If none is found, an instance is created either by conversion from the decoded image format metadata
/// or the requested format default constructor.
/// This instance will be added to the metadata for future requests.
/// </summary>
/// <param name="source">The image frame metadata.</param>
/// <returns>
/// The <see cref="GifFrameMetadata"/>
/// </returns>
public static GifFrameMetadata GetGifMetadata(this ImageFrameMetadata source) => source.GetFormatMetadata(GifFormat.Instance);
/// <summary>
/// Creates a new cloned instance of <see cref="GifMetadata"/> from the <paramref name="source"/>.
/// The instance is created via <see cref="GetGifMetadata(ImageFrameMetadata)"/>
/// </summary>
/// <param name="source">The image frame metadata.</param>
/// <returns>The new <see cref="GifFrameMetadata"/></returns>
public static GifFrameMetadata CloneGifMetadata(this ImageFrameMetadata source) => source.CloneFormatMetadata(GifFormat.Instance);
/// <summary>
/// Gets the <see cref="PngFrameMetadata"/> from <paramref name="source"/>.<br/>
/// If none is found, an instance is created either by conversion from the decoded image format metadata
/// or the requested format default constructor.
/// This instance will be added to the metadata for future requests.
/// </summary>
/// <param name="source">The image frame metadata.</param>
/// <returns>
/// The <see cref="PngFrameMetadata"/>
/// </returns>
public static PngFrameMetadata GetPngMetadata(this ImageFrameMetadata source) => source.GetFormatMetadata(PngFormat.Instance);
/// <summary>
/// Creates a new cloned instance of <see cref="PngMetadata"/> from the <paramref name="source"/>.
/// The instance is created via <see cref="GetPngMetadata(ImageFrameMetadata)"/>
/// </summary>
/// <param name="source">The image frame metadata.</param>
/// <returns>The new <see cref="PngFrameMetadata"/></returns>
public static PngFrameMetadata ClonePngMetadata(this ImageFrameMetadata source) => source.CloneFormatMetadata(PngFormat.Instance);
/// <summary>
/// Gets the <see cref="TiffFrameMetadata"/> from <paramref name="source"/>.<br/>
/// If none is found, an instance is created either by conversion from the decoded image format metadata
/// or the requested format default constructor.
/// This instance will be added to the metadata for future requests.
/// </summary>
/// <param name="source">The image frame metadata.</param>
/// <returns>
/// The <see cref="TiffFrameMetadata"/>
/// </returns>
public static TiffFrameMetadata GetTiffMetadata(this ImageFrameMetadata source) => source.GetFormatMetadata(TiffFormat.Instance);
/// <summary>
/// Creates a new cloned instance of <see cref="TiffMetadata"/> from the <paramref name="source"/>.
/// The instance is created via <see cref="GetTiffMetadata(ImageFrameMetadata)"/>
/// </summary>
/// <param name="source">The image frame metadata.</param>
/// <returns>The new <see cref="TiffFrameMetadata"/></returns>
public static TiffFrameMetadata CloneTiffMetadata(this ImageFrameMetadata source) => source.CloneFormatMetadata(TiffFormat.Instance);
/// <summary>
/// Gets the <see cref="WebpFrameMetadata"/> from <paramref name="source"/>.<br/>
/// If none is found, an instance is created either by conversion from the decoded image format metadata
/// or the requested format default constructor.
/// This instance will be added to the metadata for future requests.
/// </summary>
/// <param name="source">The image frame metadata.</param>
/// <returns>
/// The <see cref="WebpFrameMetadata"/>
/// </returns>
public static WebpFrameMetadata GetWebpMetadata(this ImageFrameMetadata source) => source.GetFormatMetadata(WebpFormat.Instance);
/// <summary>
/// Creates a new cloned instance of <see cref="WebpMetadata"/> from the <paramref name="source"/>.
/// The instance is created via <see cref="GetWebpMetadata(ImageFrameMetadata)"/>
/// </summary>
/// <param name="source">The image frame metadata.</param>
/// <returns>The new <see cref="WebpFrameMetadata"/></returns>
public static WebpFrameMetadata CloneWebpMetadata(this ImageFrameMetadata source) => source.CloneFormatMetadata(WebpFormat.Instance);
}

77
src/ImageSharp/Formats/_Generated/ImageMetadataExtensions.tt

@ -0,0 +1,77 @@
<#@include file="_Formats.ttinclude" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
// <auto-generated />
using SixLabors.ImageSharp.Metadata;
<#
foreach (string fmt in formats)
{
#>
using SixLabors.ImageSharp.Formats.<#= fmt #>;
<#
}
#>
namespace SixLabors.ImageSharp;
/// <summary>
/// Extension methods for the <see cref="ImageMetadata"/> and <see cref="ImageFrameMetadata"/> types.
/// </summary>
public static class ImageMetadataExtensions
{
<#
foreach (string fmt in formats)
{
#>
/// <summary>
/// Gets the <see cref="<#= fmt #>Metadata"/> from <paramref name="source"/>.<br/>
/// If none is found, an instance is created either by conversion from the decoded image format metadata
/// or the requested format default constructor.
/// This instance will be added to the metadata for future requests.
/// </summary>
/// <param name="source">The image metadata.</param>
/// <returns>
/// The <see cref="<#= fmt #>Metadata"/>
/// </returns>
public static <#= fmt #>Metadata Get<#= fmt #>Metadata(this ImageMetadata source) => source.GetFormatMetadata(<#= fmt #>Format.Instance);
/// <summary>
/// Creates a new cloned instance of <see cref="<#= fmt #>Metadata"/> from the <paramref name="source"/>.
/// The instance is created via <see cref="Get<#= fmt #>Metadata(ImageMetadata)"/>
/// </summary>
/// <param name="source">The image metadata.</param>
/// <returns>The new <see cref="<#= fmt #>Metadata"/></returns>
public static <#= fmt #>Metadata Clone<#= fmt #>Metadata(this ImageMetadata source) => source.CloneFormatMetadata(<#= fmt #>Format.Instance);
<#
}
#>
<#
foreach (string fmt in frameFormats)
{
#>
/// <summary>
/// Gets the <see cref="<#= fmt #>FrameMetadata"/> from <paramref name="source"/>.<br/>
/// If none is found, an instance is created either by conversion from the decoded image format metadata
/// or the requested format default constructor.
/// This instance will be added to the metadata for future requests.
/// </summary>
/// <param name="source">The image frame metadata.</param>
/// <returns>
/// The <see cref="<#= fmt #>FrameMetadata"/>
/// </returns>
public static <#= fmt #>FrameMetadata Get<#= fmt #>Metadata(this ImageFrameMetadata source) => source.GetFormatMetadata(<#= fmt #>Format.Instance);
/// <summary>
/// Creates a new cloned instance of <see cref="<#= fmt #>Metadata"/> from the <paramref name="source"/>.
/// The instance is created via <see cref="Get<#= fmt #>Metadata(ImageFrameMetadata)"/>
/// </summary>
/// <param name="source">The image frame metadata.</param>
/// <returns>The new <see cref="<#= fmt #>FrameMetadata"/></returns>
public static <#= fmt #>FrameMetadata Clone<#= fmt #>Metadata(this ImageFrameMetadata source) => source.CloneFormatMetadata(<#= fmt #>Format.Instance);
<#
}
#>
}

24
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",
};
#>

13
src/ImageSharp/ImageSharp.csproj

@ -52,6 +52,11 @@
</ItemGroup>
<ItemGroup>
<Compile Update="Formats\_Generated\ImageMetadataExtensions.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>ImageMetadataExtensions.tt</DependentUpon>
</Compile>
<Compile Update="Formats\Jpeg\Components\Block8x8F.Generated.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
@ -142,7 +147,7 @@
<AutoGen>True</AutoGen>
<DependentUpon>PorterDuffFunctions.Generated.tt</DependentUpon>
</Compile>
<Compile Update="Formats\ImageExtensions.Save.cs">
<Compile Update="Formats\_Generated\ImageExtensions.Save.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>ImageExtensions.Save.tt</DependentUpon>
@ -150,6 +155,10 @@
</ItemGroup>
<ItemGroup>
<None Update="Formats\_Generated\ImageMetadataExtensions.tt">
<LastGenOutput>ImageMetadataExtensions.cs</LastGenOutput>
<Generator>TextTemplatingFileGenerator</Generator>
</None>
<None Update="Formats\Jpeg\Components\Block8x8F.Generated.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>Block8x8F.Generated.cs</LastGenOutput>
@ -222,7 +231,7 @@
<LastGenOutput>DefaultPixelBlenders.Generated.cs</LastGenOutput>
<Generator>TextTemplatingFileGenerator</Generator>
</None>
<None Update="Formats\ImageExtensions.Save.tt">
<None Update="Formats\_Generated\ImageExtensions.Save.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>ImageExtensions.Save.cs</LastGenOutput>
</None>

59
src/ImageSharp/Metadata/ImageFrameMetadata.cs

@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Metadata;
/// </summary>
public sealed class ImageFrameMetadata : IDeepCloneable<ImageFrameMetadata>
{
private readonly Dictionary<IImageFormat, IDeepCloneable> formatMetadata = new();
private readonly Dictionary<IImageFormat, IFormatFrameMetadata> formatMetadata = [];
/// <summary>
/// Initializes a new instance of the <see cref="ImageFrameMetadata"/> class.
@ -35,9 +35,9 @@ public sealed class ImageFrameMetadata : IDeepCloneable<ImageFrameMetadata>
{
DebugGuard.NotNull(other, nameof(other));
foreach (KeyValuePair<IImageFormat, IDeepCloneable> meta in other.formatMetadata)
foreach (KeyValuePair<IImageFormat, IFormatFrameMetadata> 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<ImageFrameMetadata>
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;
}
/// <summary>
@ -72,12 +76,19 @@ public sealed class ImageFrameMetadata : IDeepCloneable<ImageFrameMetadata>
/// </summary>
public CicpProfile? CicpProfile { get; set; }
/// <summary>
/// Gets the original format, if any, the image was decode from.
/// </summary>
public IImageFormat? DecodedImageFormat { get; internal set; }
/// <inheritdoc/>
public ImageFrameMetadata DeepClone() => new(this);
/// <summary>
/// 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.<br/>
/// If none is found, an instance is created either by conversion from the decoded image format metadata
/// or the requested format default constructor.
/// This instance will be added to the metadata for future requests.
/// </summary>
/// <typeparam name="TFormatMetadata">The type of format metadata.</typeparam>
/// <typeparam name="TFormatFrameMetadata">The type of format frame metadata.</typeparam>
@ -87,43 +98,37 @@ public sealed class ImageFrameMetadata : IDeepCloneable<ImageFrameMetadata>
/// </returns>
public TFormatFrameMetadata GetFormatMetadata<TFormatMetadata, TFormatFrameMetadata>(IImageFormat<TFormatMetadata, TFormatFrameMetadata> key)
where TFormatMetadata : class
where TFormatFrameMetadata : class, IDeepCloneable
where TFormatFrameMetadata : class, IFormatFrameMetadata<TFormatFrameMetadata>
{
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;
}
/// <summary>
/// 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 <see cref="GetFormatMetadata{TFormatMetadata, TFormatFrameMetadata}(IImageFormat{TFormatMetadata, TFormatFrameMetadata})"/>.
/// </summary>
/// <typeparam name="TFormatMetadata">The type of format metadata.</typeparam>
/// <typeparam name="TFormatMetadata">The type of metadata.</typeparam>
/// <typeparam name="TFormatFrameMetadata">The type of format frame metadata.</typeparam>
/// <param name="key">The key of the value to get.</param>
/// <param name="metadata">
/// 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.
/// </param>
/// <returns>
/// <see langword="true"/> if the frame metadata exists for the specified key; otherwise, <see langword="false"/>.
/// The <typeparamref name="TFormatMetadata"/>.
/// </returns>
public bool TryGetFormatMetadata<TFormatMetadata, TFormatFrameMetadata>(IImageFormat<TFormatMetadata, TFormatFrameMetadata> key, out TFormatFrameMetadata? metadata)
public TFormatFrameMetadata CloneFormatMetadata<TFormatMetadata, TFormatFrameMetadata>(IImageFormat<TFormatMetadata, TFormatFrameMetadata> 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<TFormatFrameMetadata>
=> ((IDeepCloneable<TFormatFrameMetadata>)this.GetFormatMetadata(key)).DeepClone();
}

52
src/ImageSharp/Metadata/ImageMetadata.cs

@ -33,7 +33,7 @@ public sealed class ImageMetadata : IDeepCloneable<ImageMetadata>
/// </summary>
public const PixelResolutionUnit DefaultPixelResolutionUnits = PixelResolutionUnit.PixelsPerInch;
private readonly Dictionary<IImageFormat, IDeepCloneable> formatMetadata = [];
private readonly Dictionary<IImageFormat, IFormatMetadata> formatMetadata = [];
private double horizontalResolution;
private double verticalResolution;
@ -60,9 +60,9 @@ public sealed class ImageMetadata : IDeepCloneable<ImageMetadata>
this.VerticalResolution = other.VerticalResolution;
this.ResolutionUnits = other.ResolutionUnits;
foreach (KeyValuePair<IImageFormat, IDeepCloneable> meta in other.formatMetadata)
foreach (KeyValuePair<IImageFormat, IFormatMetadata> 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<ImageMetadata>
public IImageFormat? DecodedImageFormat { get; internal set; }
/// <summary>
/// Gets the metadata value associated with the specified key.
/// Gets the metadata value associated with the specified key.<br/>
/// If none is found, an instance is created either by conversion from the decoded image format metadata
/// or the requested format default constructor.
/// This instance will be added to the metadata for future requests.
/// </summary>
/// <typeparam name="TFormatMetadata">The type of metadata.</typeparam>
/// <param name="key">The key of the value to get.</param>
@ -178,47 +181,44 @@ public sealed class ImageMetadata : IDeepCloneable<ImageMetadata>
/// The <typeparamref name="TFormatMetadata"/>.
/// </returns>
public TFormatMetadata GetFormatMetadata<TFormatMetadata>(IImageFormat<TFormatMetadata> key)
where TFormatMetadata : class, IDeepCloneable
where TFormatMetadata : class, IFormatMetadata<TFormatMetadata>
{
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;
}
/// <summary>
/// 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 <see cref="GetFormatMetadata{TFormatMetadata}(IImageFormat{TFormatMetadata})"/>.
/// </summary>
/// <typeparam name="TFormatMetadata">The type of format metadata.</typeparam>
/// <typeparam name="TFormatMetadata">The type of metadata.</typeparam>
/// <param name="key">The key of the value to get.</param>
/// <param name="metadata">
/// 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.
/// </param>
/// <returns>
/// <see langword="true"/> if the frame metadata exists for the specified key; otherwise, <see langword="false"/>.
/// The <typeparamref name="TFormatMetadata"/>.
/// </returns>
public bool TryGetFormatMetadata<TFormatMetadata>(IImageFormat<TFormatMetadata> 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<TFormatMetadata>(IImageFormat<TFormatMetadata> key)
where TFormatMetadata : class, IFormatMetadata<TFormatMetadata>, new()
=> ((IDeepCloneable<TFormatMetadata>)this.GetFormatMetadata(key)).DeepClone();
/// <inheritdoc/>
public ImageMetadata DeepClone() => new(this);
/// TODO: This should be called on save.
/// <summary>
/// Synchronizes the profiles with the current metadata.
/// </summary>

4
src/ImageSharp/PixelFormats/PixelTypeInfo.cs

@ -31,13 +31,13 @@ public readonly struct PixelTypeInfo(int bitsPerPixel)
/// <summary>
/// Gets the pixel color type.
/// </summary>
public PixelColorType? ColorType { get; init; }
public PixelColorType ColorType { get; init; }
/// <summary>
/// Gets the pixel alpha transparency behavior.
/// <see langword="null"/> means unknown, unspecified.
/// </summary>
public PixelAlphaRepresentation? AlphaRepresentation { get; init; }
public PixelAlphaRepresentation AlphaRepresentation { get; init; }
/// <summary>
/// Creates a new <see cref="PixelTypeInfo"/> instance.

2
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;

26
tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs

@ -242,33 +242,11 @@ public class GifEncoderTests
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage();
int count = 0;
foreach (ImageFrame<TPixel> 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<TPixel> image2 = Image.Load<TPixel>(fs);
Assert.Equal(image.Frames.Count, image2.Frames.Count);
count = 0;
foreach (ImageFrame<TPixel> 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;

1
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;

8
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;

28
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<TPixel> 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<TPixel> 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<TPixel> 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<TPixel> 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<TPixel> 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<TPixel> 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<TPixel> 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<TPixel> 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));
}

Loading…
Cancel
Save