diff --git a/src/ImageSharp/Formats/FormatConnectingMetadata.cs b/src/ImageSharp/Formats/FormatConnectingMetadata.cs
index baf0a35457..9cfe40f385 100644
--- a/src/ImageSharp/Formats/FormatConnectingMetadata.cs
+++ b/src/ImageSharp/Formats/FormatConnectingMetadata.cs
@@ -45,7 +45,7 @@ public class FormatConnectingMetadata
/// Gets 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 mode is .
+ /// The background color is also used when a frame disposal mode is .
///
///
/// Defaults to .
diff --git a/src/ImageSharp/Formats/Gif/GifEncoder.cs b/src/ImageSharp/Formats/Gif/GifEncoder.cs
index 6cb8f9d8ce..37b585c618 100644
--- a/src/ImageSharp/Formats/Gif/GifEncoder.cs
+++ b/src/ImageSharp/Formats/Gif/GifEncoder.cs
@@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.Gif;
///
/// Image encoder for writing image data to a stream in gif format.
///
-public sealed class GifEncoder : QuantizingImageEncoder
+public sealed class GifEncoder : QuantizingAnimatedImageEncoder
{
///
/// Gets the color table mode: Global or local.
diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
index 2e05ef782f..0ed7e8c98d 100644
--- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
+++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
@@ -54,6 +54,19 @@ internal sealed class GifEncoderCore
///
private readonly IPixelSamplingStrategy pixelSamplingStrategy;
+ ///
+ /// 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 a frame disposal mode is .
+ ///
+ private readonly Color? backgroundColor;
+
+ ///
+ /// The number of times any animation is repeated.
+ ///
+ private readonly ushort? repeatCount;
+
///
/// Initializes a new instance of the class.
///
@@ -68,6 +81,8 @@ public GifEncoderCore(Configuration configuration, GifEncoder encoder)
this.hasQuantizer = encoder.Quantizer is not null;
this.colorTableMode = encoder.ColorTableMode;
this.pixelSamplingStrategy = encoder.PixelSamplingStrategy;
+ this.backgroundColor = encoder.BackgroundColor;
+ this.repeatCount = encoder.RepeatCount;
}
///
@@ -141,9 +156,12 @@ public void Encode(Image image, Stream stream, CancellationToken
frameMetadata.TransparencyIndex = ClampIndex(derivedTransparencyIndex);
}
- byte backgroundIndex = derivedTransparencyIndex >= 0
- ? frameMetadata.TransparencyIndex
- : gifMetadata.BackgroundColorIndex;
+ if (!TryGetBackgroundIndex(quantized, this.backgroundColor, out byte backgroundIndex))
+ {
+ backgroundIndex = derivedTransparencyIndex >= 0
+ ? frameMetadata.TransparencyIndex
+ : gifMetadata.BackgroundColorIndex;
+ }
// Get the number of bits.
int bitDepth = ColorNumerics.GetBitsNeededForColorDepth(quantized.Palette.Length);
@@ -161,7 +179,7 @@ public void Encode(Image image, Stream stream, CancellationToken
// Write application extensions.
XmpProfile? xmpProfile = image.Metadata.XmpProfile ?? image.Frames.RootFrame.Metadata.XmpProfile;
- this.WriteApplicationExtensions(stream, image.Frames.Count, gifMetadata.RepeatCount, xmpProfile);
+ this.WriteApplicationExtensions(stream, image.Frames.Count, this.repeatCount ?? gifMetadata.RepeatCount, xmpProfile);
}
this.EncodeFirstFrame(stream, frameMetadata, quantized);
@@ -169,7 +187,13 @@ public void Encode(Image image, Stream stream, CancellationToken
// Capture the global palette for reuse on subsequent frames and cleanup the quantized frame.
TPixel[] globalPalette = image.Frames.Count == 1 ? [] : quantized.Palette.ToArray();
- this.EncodeAdditionalFrames(stream, image, globalPalette, derivedTransparencyIndex, frameMetadata.DisposalMode);
+ this.EncodeAdditionalFrames(
+ stream,
+ image,
+ globalPalette,
+ derivedTransparencyIndex,
+ frameMetadata.DisposalMode,
+ cancellationToken);
stream.WriteByte(GifConstants.EndIntroducer);
@@ -194,7 +218,8 @@ private void EncodeAdditionalFrames(
Image image,
ReadOnlyMemory globalPalette,
int globalTransparencyIndex,
- FrameDisposalMode previousDisposalMode)
+ FrameDisposalMode previousDisposalMode,
+ CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel
{
if (image.Frames.Count == 1)
@@ -213,6 +238,16 @@ private void EncodeAdditionalFrames(
for (int i = 1; i < image.Frames.Count; i++)
{
+ if (cancellationToken.IsCancellationRequested)
+ {
+ if (hasPaletteQuantizer)
+ {
+ paletteQuantizer.Dispose();
+ }
+
+ return;
+ }
+
// Gather the metadata for this frame.
ImageFrame currentFrame = image.Frames[i];
ImageFrame? nextFrame = i < image.Frames.Count - 1 ? image.Frames[i + 1] : null;
@@ -291,6 +326,10 @@ private void EncodeAdditionalFrame(
ImageFrame? previous = previousDisposalMode == FrameDisposalMode.RestoreToBackground ? null : previousFrame;
+ Color background = metadata.DisposalMode == FrameDisposalMode.RestoreToBackground
+ ? this.backgroundColor ?? Color.Transparent
+ : Color.Transparent;
+
// Deduplicate and quantize the frame capturing only required parts.
(bool difference, Rectangle bounds) =
AnimationUtilities.DeDuplicatePixels(
@@ -299,7 +338,7 @@ private void EncodeAdditionalFrame(
currentFrame,
nextFrame,
encodingFrame,
- Color.Transparent,
+ background,
true);
using IndexedImageFrame quantized = this.QuantizeAdditionalFrameAndUpdateMetadata(
@@ -428,14 +467,12 @@ private IndexedImageFrame QuantizeAdditionalFrameAndUpdateMetadata (byte)Numerics.Clamp(value, byte.MinValue, byte.MaxValue);
///
- /// Returns the index of the most transparent color in the palette.
+ /// Returns the index of the transparent color in the palette.
///
/// The current quantized frame.
/// The current gif frame metadata.
/// The pixel format.
- ///
- /// The .
- ///
+ /// The .
private static int GetTransparentIndex(IndexedImageFrame? quantized, GifFrameMetadata? metadata)
where TPixel : unmanaged, IPixel
{
@@ -463,6 +500,47 @@ private static int GetTransparentIndex(IndexedImageFrame? quanti
return index;
}
+ ///
+ /// Returns the index of the background color in the palette.
+ ///
+ /// The current quantized frame.
+ /// The background color to match.
+ /// The index in the palette of the background color.
+ /// The pixel format.
+ /// The .
+ private static bool TryGetBackgroundIndex(
+ IndexedImageFrame? quantized,
+ Color? background,
+ out byte index)
+ where TPixel : unmanaged, IPixel
+ {
+ int match = -1;
+ if (quantized != null && background.HasValue)
+ {
+ TPixel backgroundPixel = background.Value.ToPixel();
+ ReadOnlySpan palette = quantized.Palette.Span;
+ for (int i = 0; i < palette.Length; i++)
+ {
+ if (!backgroundPixel.Equals(palette[i]))
+ {
+ continue;
+ }
+
+ match = i;
+ break;
+ }
+ }
+
+ if (match >= 0)
+ {
+ index = (byte)Numerics.Clamp(match, 0, 255);
+ return true;
+ }
+
+ index = 0;
+ return false;
+ }
+
///
/// Writes the file header signature and version to the stream.
///
diff --git a/src/ImageSharp/Formats/IAnimatedImageEncoder.cs b/src/ImageSharp/Formats/IAnimatedImageEncoder.cs
new file mode 100644
index 0000000000..44431aa9a4
--- /dev/null
+++ b/src/ImageSharp/Formats/IAnimatedImageEncoder.cs
@@ -0,0 +1,43 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+namespace SixLabors.ImageSharp.Formats;
+
+///
+/// Defines the contract for all image encoders that allow encoding animation sequences.
+///
+public interface IAnimatedImageEncoder
+{
+ ///
+ /// Gets the default background color of the canvas when animating in supported encoders.
+ /// 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 a frame disposal mode is .
+ ///
+ Color? BackgroundColor { get; }
+
+ ///
+ /// Gets the number of times any animation is repeated in supported encoders.
+ ///
+ ushort? RepeatCount { get; }
+
+ ///
+ /// Gets a value indicating whether the root frame is shown as part of the animated sequence in supported encoders.
+ ///
+ bool? AnimateRootFrame { get; }
+}
+
+///
+/// Acts as a base class for all image encoders that allow encoding animation sequences.
+///
+public abstract class AnimatedImageEncoder : ImageEncoder, IAnimatedImageEncoder
+{
+ ///
+ public Color? BackgroundColor { get; init; }
+
+ ///
+ public ushort? RepeatCount { get; init; }
+
+ ///
+ public bool? AnimateRootFrame { get; init; } = true;
+}
diff --git a/src/ImageSharp/Formats/IQuantizingImageEncoder.cs b/src/ImageSharp/Formats/IQuantizingImageEncoder.cs
new file mode 100644
index 0000000000..e88b3ecf02
--- /dev/null
+++ b/src/ImageSharp/Formats/IQuantizingImageEncoder.cs
@@ -0,0 +1,50 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using SixLabors.ImageSharp.Processing.Processors.Quantization;
+
+namespace SixLabors.ImageSharp.Formats;
+
+///
+/// Defines the contract for all image encoders that allow color palette generation via quantization.
+///
+public interface IQuantizingImageEncoder
+{
+ ///
+ /// Gets the quantizer used to generate the color palette.
+ ///
+ IQuantizer? Quantizer { get; }
+
+ ///
+ /// Gets the used for quantization when building color palettes.
+ ///
+ IPixelSamplingStrategy PixelSamplingStrategy { get; }
+}
+
+///
+/// Acts as a base class for all image encoders that allow color palette generation via quantization.
+///
+public abstract class QuantizingImageEncoder : ImageEncoder, IQuantizingImageEncoder
+{
+ ///
+ public IQuantizer? Quantizer { get; init; }
+
+ ///
+ public IPixelSamplingStrategy PixelSamplingStrategy { get; init; } = new DefaultPixelSamplingStrategy();
+}
+
+///
+/// Acts as a base class for all image encoders that allow color palette generation via quantization when
+/// encoding animation sequences.
+///
+public abstract class QuantizingAnimatedImageEncoder : QuantizingImageEncoder, IAnimatedImageEncoder
+{
+ ///
+ public Color? BackgroundColor { get; }
+
+ ///
+ public ushort? RepeatCount { get; }
+
+ ///
+ public bool? AnimateRootFrame { get; }
+}
diff --git a/src/ImageSharp/Formats/Png/PngEncoder.cs b/src/ImageSharp/Formats/Png/PngEncoder.cs
index dcbaf3140d..d9f71e1b56 100644
--- a/src/ImageSharp/Formats/Png/PngEncoder.cs
+++ b/src/ImageSharp/Formats/Png/PngEncoder.cs
@@ -1,6 +1,5 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
-#nullable disable
using SixLabors.ImageSharp.Processing.Processors.Quantization;
@@ -9,7 +8,7 @@ namespace SixLabors.ImageSharp.Formats.Png;
///
/// Image encoder for writing image data to a stream in png format.
///
-public class PngEncoder : QuantizingImageEncoder
+public class PngEncoder : QuantizingAnimatedImageEncoder
{
///
/// Initializes a new instance of the class.
diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs
index 978b9184e9..398c80634c 100644
--- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs
+++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs
@@ -123,6 +123,24 @@ internal sealed class PngEncoderCore : IDisposable
///
private int derivedTransparencyIndex = -1;
+ ///
+ /// 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 a frame disposal mode is .
+ ///
+ private readonly Color? backgroundColor;
+
+ ///
+ /// The number of times any animation is repeated.
+ ///
+ private readonly ushort? repeatCount;
+
+ ///
+ /// Whether the root frame is shown as part of the animated sequence.
+ ///
+ private readonly bool? animateRootFrame;
+
///
/// A reusable Crc32 hashing instance.
///
@@ -139,6 +157,9 @@ public PngEncoderCore(Configuration configuration, PngEncoder encoder)
this.memoryAllocator = configuration.MemoryAllocator;
this.encoder = encoder;
this.quantizer = encoder.Quantizer;
+ this.backgroundColor = encoder.BackgroundColor;
+ this.repeatCount = encoder.RepeatCount;
+ this.animateRootFrame = encoder.AnimateRootFrame;
}
///
@@ -171,7 +192,7 @@ public void Encode(Image image, Stream stream, CancellationToken
if (clearTransparency)
{
currentFrame = clonedFrame = currentFrame.Clone();
- ClearTransparentPixels(currentFrame);
+ ClearTransparentPixels(currentFrame, Color.Transparent);
}
// Do not move this. We require an accurate bit depth for the header chunk.
@@ -194,11 +215,15 @@ public void Encode(Image image, Stream stream, CancellationToken
if (image.Frames.Count > 1)
{
- this.WriteAnimationControlChunk(stream, (uint)(image.Frames.Count - (pngMetadata.AnimateRootFrame ? 0 : 1)), pngMetadata.RepeatCount);
+ this.WriteAnimationControlChunk(
+ stream,
+ (uint)(image.Frames.Count - (pngMetadata.AnimateRootFrame ? 0 : 1)),
+ this.repeatCount ?? pngMetadata.RepeatCount);
}
// If the first frame isn't animated, write it as usual and skip it when writing animated frames
- if (!pngMetadata.AnimateRootFrame || image.Frames.Count == 1)
+ bool userAnimateRootFrame = this.animateRootFrame == true;
+ if ((!userAnimateRootFrame && !pngMetadata.AnimateRootFrame) || image.Frames.Count == 1)
{
FrameControl frameControl = new((uint)this.width, (uint)this.height);
this.WriteDataChunks(frameControl, currentFrame.PixelBuffer.GetRegion(), quantized, stream, false);
@@ -235,12 +260,20 @@ public void Encode(Image image, Stream stream, CancellationToken
for (; currentFrameIndex < image.Frames.Count; currentFrameIndex++)
{
+ if (cancellationToken.IsCancellationRequested)
+ {
+ break;
+ }
+
ImageFrame? prev = previousDisposal == FrameDisposalMode.RestoreToBackground ? null : previousFrame;
currentFrame = image.Frames[currentFrameIndex];
ImageFrame? nextFrame = currentFrameIndex < image.Frames.Count - 1 ? image.Frames[currentFrameIndex + 1] : null;
frameMetadata = currentFrame.Metadata.GetPngMetadata();
bool blend = frameMetadata.BlendMode == FrameBlendMode.Over;
+ Color background = frameMetadata.DisposalMode == FrameDisposalMode.RestoreToBackground
+ ? this.backgroundColor ?? Color.Transparent
+ : Color.Transparent;
(bool difference, Rectangle bounds) =
AnimationUtilities.DeDuplicatePixels(
@@ -249,12 +282,12 @@ public void Encode(Image image, Stream stream, CancellationToken
currentFrame,
nextFrame,
encodingFrame,
- Color.Transparent,
+ background,
blend);
if (clearTransparency)
{
- ClearTransparentPixels(encodingFrame);
+ ClearTransparentPixels(encodingFrame, background);
}
// Each frame control sequence number must be incremented by the number of frame data chunks that follow.
@@ -291,12 +324,13 @@ public void Dispose()
///
/// The type of the pixel.
/// The cloned image frame where the transparent pixels will be changed.
- private static void ClearTransparentPixels(ImageFrame clone)
+ /// The color to replace transparent pixels with.
+ private static void ClearTransparentPixels(ImageFrame clone, Color color)
where TPixel : unmanaged, IPixel
=> clone.ProcessPixelRows(accessor =>
{
// TODO: We should be able to speed this up with SIMD and masking.
- Rgba32 transparent = Color.Transparent.ToPixel();
+ Rgba32 transparent = color.ToPixel();
for (int y = 0; y < accessor.Height; y++)
{
Span span = accessor.GetRowSpan(y);
diff --git a/src/ImageSharp/Formats/QuantizingImageEncoder.cs b/src/ImageSharp/Formats/QuantizingImageEncoder.cs
deleted file mode 100644
index 330d8988c7..0000000000
--- a/src/ImageSharp/Formats/QuantizingImageEncoder.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (c) Six Labors.
-// Licensed under the Six Labors Split License.
-
-using SixLabors.ImageSharp.Processing.Processors.Quantization;
-
-namespace SixLabors.ImageSharp.Formats;
-
-///
-/// Acts as a base class for all image encoders that allow color palette generation via quantization.
-///
-public abstract class QuantizingImageEncoder : ImageEncoder
-{
- ///
- /// Gets the quantizer used to generate the color palette.
- ///
- public IQuantizer? Quantizer { get; init; }
-
- ///
- /// Gets the used for quantization when building color palettes.
- ///
- public IPixelSamplingStrategy PixelSamplingStrategy { get; init; } = new DefaultPixelSamplingStrategy();
-}
diff --git a/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs b/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs
index 244691e77e..e077249696 100644
--- a/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs
+++ b/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs
@@ -236,7 +236,7 @@ public Vp8LEncoder(
///
public Vp8LHashChain HashChain { get; }
- public WebpVp8X EncodeHeader(Image image, Stream stream, bool hasAnimation)
+ public WebpVp8X EncodeHeader(Image image, Stream stream, bool hasAnimation, ushort? repeatCount)
where TPixel : unmanaged, IPixel
{
// Write bytes from the bit-writer buffer to the stream.
@@ -258,7 +258,7 @@ public WebpVp8X EncodeHeader(Image image, Stream stream, bool ha
if (hasAnimation)
{
WebpMetadata webpMetadata = image.Metadata.GetWebpMetadata();
- BitWriterBase.WriteAnimationParameter(stream, webpMetadata.BackgroundColor, webpMetadata.RepeatCount);
+ BitWriterBase.WriteAnimationParameter(stream, webpMetadata.BackgroundColor, repeatCount ?? webpMetadata.RepeatCount);
}
return vp8x;
@@ -315,8 +315,8 @@ public bool Encode(ImageFrame frame, Rectangle bounds, WebpFrame
(uint)bounds.Width,
(uint)bounds.Height,
frameMetadata.FrameDelay,
- frameMetadata.BlendMethod,
- frameMetadata.DisposalMethod)
+ frameMetadata.BlendMode,
+ frameMetadata.DisposalMode)
.WriteHeaderTo(stream);
}
diff --git a/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs b/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs
index 3ad72f7d00..d22d357fe3 100644
--- a/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs
+++ b/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs
@@ -495,8 +495,8 @@ private bool Encode(Stream stream, ImageFrame frame, Rectangle b
(uint)bounds.Width,
(uint)bounds.Height,
frameMetadata.FrameDelay,
- frameMetadata.BlendMethod,
- frameMetadata.DisposalMethod)
+ frameMetadata.BlendMode,
+ frameMetadata.DisposalMode)
.WriteHeaderTo(stream);
}
diff --git a/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs b/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs
index 72405e480e..bfaaa831e1 100644
--- a/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs
+++ b/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs
@@ -220,8 +220,8 @@ private static void SetFrameMetadata(ImageFrameMetadata meta, WebpFrameData fram
{
WebpFrameMetadata frameMetadata = meta.GetWebpMetadata();
frameMetadata.FrameDelay = frameData.Duration;
- frameMetadata.BlendMethod = frameData.BlendingMethod;
- frameMetadata.DisposalMethod = frameData.DisposalMethod;
+ frameMetadata.BlendMode = frameData.BlendingMethod;
+ frameMetadata.DisposalMode = frameData.DisposalMethod;
}
///
diff --git a/src/ImageSharp/Formats/Webp/WebpEncoder.cs b/src/ImageSharp/Formats/Webp/WebpEncoder.cs
index bc93df3a5b..226719c792 100644
--- a/src/ImageSharp/Formats/Webp/WebpEncoder.cs
+++ b/src/ImageSharp/Formats/Webp/WebpEncoder.cs
@@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.Webp;
///
/// Image encoder for writing an image to a stream in the Webp format.
///
-public sealed class WebpEncoder : ImageEncoder
+public sealed class WebpEncoder : AnimatedImageEncoder
{
///
/// Gets the webp file format used. Either lossless or lossy.
diff --git a/src/ImageSharp/Formats/Webp/WebpEncoderCore.cs b/src/ImageSharp/Formats/Webp/WebpEncoderCore.cs
index 733801d636..37d2ae0a19 100644
--- a/src/ImageSharp/Formats/Webp/WebpEncoderCore.cs
+++ b/src/ImageSharp/Formats/Webp/WebpEncoderCore.cs
@@ -5,7 +5,6 @@
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;
@@ -78,6 +77,19 @@ internal sealed class WebpEncoderCore
///
private readonly WebpFileFormatType? fileFormat;
+ ///
+ /// 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 a frame disposal mode is .
+ ///
+ private readonly Color? backgroundColor;
+
+ ///
+ /// The number of times any animation is repeated.
+ ///
+ private readonly ushort? repeatCount;
+
///
/// The global configuration.
///
@@ -103,6 +115,8 @@ public WebpEncoderCore(WebpEncoder encoder, Configuration configuration)
this.skipMetadata = encoder.SkipMetadata;
this.nearLossless = encoder.NearLossless;
this.nearLosslessQuality = encoder.NearLosslessQuality;
+ this.backgroundColor = encoder.BackgroundColor;
+ this.repeatCount = encoder.RepeatCount;
}
///
@@ -147,7 +161,7 @@ public void Encode(Image image, Stream stream, CancellationToken
long initialPosition = stream.Position;
bool hasAlpha = false;
- WebpVp8X vp8x = encoder.EncodeHeader(image, stream, hasAnimation);
+ WebpVp8X vp8x = encoder.EncodeHeader(image, stream, hasAnimation, this.repeatCount);
// Encode the first frame.
ImageFrame previousFrame = image.Frames.RootFrame;
@@ -156,7 +170,7 @@ public void Encode(Image image, Stream stream, CancellationToken
if (hasAnimation)
{
- FrameDisposalMode previousDisposal = frameMetadata.DisposalMethod;
+ FrameDisposalMode previousDisposal = frameMetadata.DisposalMode;
// Encode additional frames
// This frame is reused to store de-duplicated pixel buffers.
@@ -164,12 +178,20 @@ public void Encode(Image image, Stream stream, CancellationToken
for (int i = 1; i < image.Frames.Count; i++)
{
+ if (cancellationToken.IsCancellationRequested)
+ {
+ break;
+ }
+
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 = currentFrame.Metadata.GetWebpMetadata();
- bool blend = frameMetadata.BlendMethod == FrameBlendMode.Over;
+ bool blend = frameMetadata.BlendMode == FrameBlendMode.Over;
+ Color background = frameMetadata.DisposalMode == FrameDisposalMode.RestoreToBackground
+ ? this.backgroundColor ?? Color.Transparent
+ : Color.Transparent;
(bool difference, Rectangle bounds) =
AnimationUtilities.DeDuplicatePixels(
@@ -178,7 +200,7 @@ public void Encode(Image image, Stream stream, CancellationToken
currentFrame,
nextFrame,
encodingFrame,
- Color.Transparent,
+ background,
blend,
ClampingMode.Even);
@@ -197,7 +219,7 @@ public void Encode(Image image, Stream stream, CancellationToken
hasAlpha |= animatedEncoder.Encode(encodingFrame, bounds, frameMetadata, stream, hasAnimation);
previousFrame = currentFrame;
- previousDisposal = frameMetadata.DisposalMethod;
+ previousDisposal = frameMetadata.DisposalMode;
}
}
@@ -229,7 +251,7 @@ public void Encode(Image image, Stream stream, CancellationToken
// Encode the first frame.
ImageFrame previousFrame = image.Frames.RootFrame;
WebpFrameMetadata frameMetadata = previousFrame.Metadata.GetWebpMetadata();
- FrameDisposalMode previousDisposal = frameMetadata.DisposalMethod;
+ FrameDisposalMode previousDisposal = frameMetadata.DisposalMode;
hasAlpha |= encoder.EncodeAnimation(previousFrame, stream, previousFrame.Bounds(), frameMetadata);
@@ -239,12 +261,20 @@ public void Encode(Image image, Stream stream, CancellationToken
for (int i = 1; i < image.Frames.Count; i++)
{
+ if (cancellationToken.IsCancellationRequested)
+ {
+ break;
+ }
+
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 = currentFrame.Metadata.GetWebpMetadata();
- bool blend = frameMetadata.BlendMethod == FrameBlendMode.Over;
+ bool blend = frameMetadata.BlendMode == FrameBlendMode.Over;
+ Color background = frameMetadata.DisposalMode == FrameDisposalMode.RestoreToBackground
+ ? this.backgroundColor ?? Color.Transparent
+ : Color.Transparent;
(bool difference, Rectangle bounds) =
AnimationUtilities.DeDuplicatePixels(
@@ -253,7 +283,7 @@ public void Encode(Image image, Stream stream, CancellationToken
currentFrame,
nextFrame,
encodingFrame,
- Color.Transparent,
+ background,
blend,
ClampingMode.Even);
@@ -273,7 +303,7 @@ public void Encode(Image image, Stream stream, CancellationToken
hasAlpha |= animatedEncoder.EncodeAnimation(encodingFrame, stream, bounds, frameMetadata);
previousFrame = currentFrame;
- previousDisposal = frameMetadata.DisposalMethod;
+ previousDisposal = frameMetadata.DisposalMode;
}
encoder.EncodeFooter(image, in vp8x, hasAlpha, stream, initialPosition);
diff --git a/src/ImageSharp/Formats/Webp/WebpFrameMetadata.cs b/src/ImageSharp/Formats/Webp/WebpFrameMetadata.cs
index 3865f9837f..3f976a6401 100644
--- a/src/ImageSharp/Formats/Webp/WebpFrameMetadata.cs
+++ b/src/ImageSharp/Formats/Webp/WebpFrameMetadata.cs
@@ -24,19 +24,21 @@ public WebpFrameMetadata()
private WebpFrameMetadata(WebpFrameMetadata other)
{
this.FrameDelay = other.FrameDelay;
- this.DisposalMethod = other.DisposalMethod;
- this.BlendMethod = other.BlendMethod;
+ this.DisposalMode = other.DisposalMode;
+ this.BlendMode = other.BlendMode;
}
///
- /// Gets or sets how transparent pixels of the current frame are to be blended with corresponding pixels of the previous canvas.
+ /// Gets or sets how transparent pixels of the current frame are to be blended with corresponding pixels
+ /// of the previous canvas.
///
- public FrameBlendMode BlendMethod { get; set; }
+ public FrameBlendMode BlendMode { 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.
+ /// 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 FrameDisposalMode DisposalMethod { get; set; }
+ public FrameDisposalMode DisposalMode { get; set; }
///
/// Gets or sets the frame duration. The time to wait before displaying the next frame,
@@ -49,8 +51,8 @@ public static WebpFrameMetadata FromFormatConnectingFrameMetadata(FormatConnecti
=> new()
{
FrameDelay = (uint)metadata.Duration.TotalMilliseconds,
- BlendMethod = metadata.BlendMode,
- DisposalMethod = GetMode(metadata.DisposalMode)
+ BlendMode = metadata.BlendMode,
+ DisposalMode = GetMode(metadata.DisposalMode)
};
///
@@ -59,8 +61,8 @@ public FormatConnectingFrameMetadata ToFormatConnectingFrameMetadata()
{
ColorTableMode = FrameColorTableMode.Global,
Duration = TimeSpan.FromMilliseconds(this.FrameDelay),
- DisposalMode = this.DisposalMethod,
- BlendMode = this.BlendMethod,
+ DisposalMode = this.DisposalMode,
+ BlendMode = this.BlendMode,
};
///
diff --git a/tests/Directory.Build.targets b/tests/Directory.Build.targets
index d6b35d003f..e20717cdd3 100644
--- a/tests/Directory.Build.targets
+++ b/tests/Directory.Build.targets
@@ -18,18 +18,23 @@
-
-
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
+
diff --git a/tests/ImageSharp.Benchmarks/Codecs/Tga/DecodeTga.cs b/tests/ImageSharp.Benchmarks/Codecs/Tga/DecodeTga.cs
index 2ebab5e001..7a3fc0b395 100644
--- a/tests/ImageSharp.Benchmarks/Codecs/Tga/DecodeTga.cs
+++ b/tests/ImageSharp.Benchmarks/Codecs/Tga/DecodeTga.cs
@@ -29,22 +29,22 @@ public void SetupData()
[Benchmark(Baseline = true, Description = "ImageMagick Tga")]
public int TgaImageMagick()
{
- var settings = new MagickReadSettings { Format = MagickFormat.Tga };
- using var image = new MagickImage(new MemoryStream(this.data), settings);
- return image.Width;
+ MagickReadSettings settings = new() { Format = MagickFormat.Tga };
+ using MagickImage image = new(new MemoryStream(this.data), settings);
+ return (int)image.Width;
}
[Benchmark(Description = "ImageSharp Tga")]
public int TgaImageSharp()
{
- using var image = Image.Load(this.data);
+ using Image image = Image.Load(this.data);
return image.Width;
}
[Benchmark(Description = "Pfim Tga")]
public int TgaPfim()
{
- using var image = Targa.Create(this.data, this.pfimConfig);
+ using Targa image = Targa.Create(this.data, this.pfimConfig);
return image.Width;
}
diff --git a/tests/ImageSharp.Benchmarks/Codecs/Webp/DecodeWebp.cs b/tests/ImageSharp.Benchmarks/Codecs/Webp/DecodeWebp.cs
index 6c71a62b50..4d6252c2b6 100644
--- a/tests/ImageSharp.Benchmarks/Codecs/Webp/DecodeWebp.cs
+++ b/tests/ImageSharp.Benchmarks/Codecs/Webp/DecodeWebp.cs
@@ -44,34 +44,35 @@ public void ReadImages()
[Benchmark(Description = "Magick Lossy Webp")]
public int WebpLossyMagick()
{
- var settings = new MagickReadSettings { Format = MagickFormat.WebP };
- using var memoryStream = new MemoryStream(this.webpLossyBytes);
- using var image = new MagickImage(memoryStream, settings);
- return image.Width;
+ MagickReadSettings settings = new() { Format = MagickFormat.WebP };
+ using MemoryStream memoryStream = new(this.webpLossyBytes);
+ using MagickImage image = new(memoryStream, settings);
+ return (int)image.Width;
}
[Benchmark(Description = "ImageSharp Lossy Webp")]
public int WebpLossy()
{
- using var memoryStream = new MemoryStream(this.webpLossyBytes);
- using var image = Image.Load(memoryStream);
+ using MemoryStream memoryStream = new(this.webpLossyBytes);
+ using Image image = Image.Load(memoryStream);
return image.Height;
}
[Benchmark(Description = "Magick Lossless Webp")]
public int WebpLosslessMagick()
{
- var settings = new MagickReadSettings { Format = MagickFormat.WebP };
- using var memoryStream = new MemoryStream(this.webpLossyBytes);
- using var image = new MagickImage(memoryStream, settings);
- return image.Width;
+ MagickReadSettings settings = new()
+ { Format = MagickFormat.WebP };
+ using MemoryStream memoryStream = new(this.webpLossyBytes);
+ using MagickImage image = new(memoryStream, settings);
+ return (int)image.Width;
}
[Benchmark(Description = "ImageSharp Lossless Webp")]
public int WebpLossless()
{
- using var memoryStream = new MemoryStream(this.webpLosslessBytes);
- using var image = Image.Load(memoryStream);
+ using MemoryStream memoryStream = new(this.webpLosslessBytes);
+ using Image image = Image.Load(memoryStream);
return image.Height;
}
diff --git a/tests/ImageSharp.Benchmarks/Codecs/Webp/EncodeWebp.cs b/tests/ImageSharp.Benchmarks/Codecs/Webp/EncodeWebp.cs
index 5be46a2220..d78640549d 100644
--- a/tests/ImageSharp.Benchmarks/Codecs/Webp/EncodeWebp.cs
+++ b/tests/ImageSharp.Benchmarks/Codecs/Webp/EncodeWebp.cs
@@ -13,7 +13,9 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs;
[MarkdownExporter]
[HtmlExporter]
[Config(typeof(Config.Short))]
+#pragma warning disable CA1001 // Types that own disposable fields should be disposable
public class EncodeWebp
+#pragma warning restore CA1001 // Types that own disposable fields should be disposable
{
private MagickImage webpMagick;
private Image webp;
@@ -52,10 +54,7 @@ public void MagickWebpLossy()
AlphaCompression = WebPAlphaCompression.None,
FilterStrength = 60,
SnsStrength = 50,
- Pass = 1,
-
- // 100 means off.
- NearLossless = 100
+ Pass = 1
};
this.webpMagick.Quality = 75;
@@ -85,9 +84,6 @@ public void MagickWebpLossless()
{
Lossless = true,
Method = 4,
-
- // 100 means off.
- NearLossless = 100
};
this.webpMagick.Quality = 75;
diff --git a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj
index a705b24b28..37a991248c 100644
--- a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj
+++ b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj
@@ -41,8 +41,8 @@
-
-
+
+
diff --git a/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs b/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs
index a06784f1b1..e7d240acd3 100644
--- a/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs
+++ b/tests/ImageSharp.Benchmarks/LoadResizeSave/LoadResizeSaveStressRunner.cs
@@ -19,6 +19,7 @@
namespace SixLabors.ImageSharp.Benchmarks.LoadResizeSave;
+[Flags]
public enum JpegKind
{
Baseline = 1,
@@ -30,7 +31,7 @@ public class LoadResizeSaveStressRunner
{
private const int Quality = 75;
- // Set the quality for ImagSharp
+ // Set the quality for ImageSharp
private readonly JpegEncoder imageSharpJpegEncoder = new() { Quality = Quality };
private readonly ImageCodecInfo systemDrawingJpegCodec =
ImageCodecInfo.GetImageEncoders().First(codec => codec.FormatID == ImageFormat.Jpeg.Guid);
@@ -126,7 +127,7 @@ public Task ForEachImageParallelAsync(Func action)
: Environment.ProcessorCount;
int partitionSize = (int)Math.Ceiling((double)this.Images.Length / maxDegreeOfParallelism);
- List tasks = new();
+ List tasks = [];
for (int i = 0; i < this.Images.Length; i += partitionSize)
{
int end = Math.Min(i + partitionSize, this.Images.Length);
@@ -176,13 +177,13 @@ private string OutputPath(string inputPath, [CallerMemberName] string postfix =
public void SystemDrawingResize(string input)
{
- using var image = SystemDrawingImage.FromFile(input, true);
+ using SystemDrawingImage image = SystemDrawingImage.FromFile(input, true);
this.LogImageProcessed(image.Width, image.Height);
- (int Width, int Height) scaled = this.ScaledSize(image.Width, image.Height, this.ThumbnailSize);
- var resized = new Bitmap(scaled.Width, scaled.Height);
- using var graphics = Graphics.FromImage(resized);
- using var attributes = new ImageAttributes();
+ (int width, int height) = this.ScaledSize(image.Width, image.Height, this.ThumbnailSize);
+ Bitmap resized = new(width, height);
+ using Graphics graphics = Graphics.FromImage(resized);
+ using ImageAttributes attributes = new();
attributes.SetWrapMode(WrapMode.TileFlipXY);
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.CompositingMode = CompositingMode.SourceCopy;
@@ -191,8 +192,8 @@ public void SystemDrawingResize(string input)
graphics.DrawImage(image, System.Drawing.Rectangle.FromLTRB(0, 0, resized.Width, resized.Height), 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, attributes);
// Save the results
- using var encoderParams = new EncoderParameters(1);
- using var qualityParam = new EncoderParameter(Encoder.Quality, (long)Quality);
+ using EncoderParameters encoderParams = new(1);
+ using EncoderParameter qualityParam = new(Encoder.Quality, (long)Quality);
encoderParams.Param[0] = qualityParam;
resized.Save(this.OutputPath(input), this.systemDrawingJpegCodec, encoderParams);
}
@@ -223,7 +224,7 @@ public void ImageSharpResize(string input)
public async Task ImageSharpResizeAsync(string input)
{
- using FileStream output = File.Open(this.OutputPath(input), FileMode.Create);
+ await using FileStream output = File.Open(this.OutputPath(input), FileMode.Create);
// Resize it to fit a 150x150 square.
DecoderOptions options = new()
@@ -246,7 +247,7 @@ public async Task ImageSharpResizeAsync(string input)
public void MagickResize(string input)
{
- using var image = new MagickImage(input);
+ using MagickImage image = new(input);
this.LogImageProcessed(image.Width, image.Height);
// Resize it to fit a 150x150 square
@@ -264,7 +265,7 @@ public void MagickResize(string input)
public void MagicScalerResize(string input)
{
- var settings = new ProcessImageSettings()
+ ProcessImageSettings settings = new()
{
Width = this.ThumbnailSize,
Height = this.ThumbnailSize,
@@ -273,19 +274,19 @@ public void MagicScalerResize(string input)
};
// TODO: Is there a way to capture input dimensions for IncreaseTotalMegapixels?
- using var output = new FileStream(this.OutputPath(input), FileMode.Create);
+ using FileStream output = new(this.OutputPath(input), FileMode.Create);
MagicImageProcessor.ProcessImage(input, output, settings);
}
public void SkiaCanvasResize(string input)
{
- using var original = SKBitmap.Decode(input);
+ using SKBitmap original = SKBitmap.Decode(input);
this.LogImageProcessed(original.Width, original.Height);
- (int Width, int Height) scaled = this.ScaledSize(original.Width, original.Height, this.ThumbnailSize);
- using var surface = SKSurface.Create(new SKImageInfo(scaled.Width, scaled.Height, original.ColorType, original.AlphaType));
- using var paint = new SKPaint() { FilterQuality = SKFilterQuality.High };
+ (int width, int height) = this.ScaledSize(original.Width, original.Height, this.ThumbnailSize);
+ using SKSurface surface = SKSurface.Create(new SKImageInfo(width, height, original.ColorType, original.AlphaType));
+ using SKPaint paint = new() { FilterQuality = SKFilterQuality.High };
SKCanvas canvas = surface.Canvas;
- canvas.Scale((float)scaled.Width / original.Width);
+ canvas.Scale((float)width / original.Width);
canvas.DrawBitmap(original, 0, 0, paint);
canvas.Flush();
@@ -297,16 +298,16 @@ public void SkiaCanvasResize(string input)
public void SkiaBitmapResize(string input)
{
- using var original = SKBitmap.Decode(input);
+ using SKBitmap original = SKBitmap.Decode(input);
this.LogImageProcessed(original.Width, original.Height);
- (int Width, int Height) scaled = this.ScaledSize(original.Width, original.Height, this.ThumbnailSize);
- using var resized = original.Resize(new SKImageInfo(scaled.Width, scaled.Height), SKFilterQuality.High);
+ (int width, int height) = this.ScaledSize(original.Width, original.Height, this.ThumbnailSize);
+ using SKBitmap resized = original.Resize(new SKImageInfo(width, height), SKFilterQuality.High);
if (resized == null)
{
return;
}
- using var image = SKImage.FromBitmap(resized);
+ using SKImage image = SKImage.FromBitmap(resized);
using FileStream output = File.OpenWrite(this.OutputPath(input));
image.Encode(SKEncodedImageFormat.Jpeg, Quality)
.SaveTo(output);
@@ -314,21 +315,21 @@ public void SkiaBitmapResize(string input)
public void SkiaBitmapDecodeToTargetSize(string input)
{
- using var codec = SKCodec.Create(input);
+ using SKCodec codec = SKCodec.Create(input);
SKImageInfo info = codec.Info;
this.LogImageProcessed(info.Width, info.Height);
- (int Width, int Height) scaled = this.ScaledSize(info.Width, info.Height, this.ThumbnailSize);
- SKSizeI supportedScale = codec.GetScaledDimensions((float)scaled.Width / info.Width);
+ (int width, int height) = this.ScaledSize(info.Width, info.Height, this.ThumbnailSize);
+ SKSizeI supportedScale = codec.GetScaledDimensions((float)width / info.Width);
- using var original = SKBitmap.Decode(codec, new SKImageInfo(supportedScale.Width, supportedScale.Height));
- using SKBitmap resized = original.Resize(new SKImageInfo(scaled.Width, scaled.Height), SKFilterQuality.High);
+ using SKBitmap original = SKBitmap.Decode(codec, new SKImageInfo(supportedScale.Width, supportedScale.Height));
+ using SKBitmap resized = original.Resize(new SKImageInfo(width, height), SKFilterQuality.High);
if (resized == null)
{
return;
}
- using var image = SKImage.FromBitmap(resized);
+ using SKImage image = SKImage.FromBitmap(resized);
using FileStream output = File.OpenWrite(this.OutputPath(input, nameof(this.SkiaBitmapDecodeToTargetSize)));
image.Encode(SKEncodedImageFormat.Jpeg, Quality)
@@ -338,7 +339,7 @@ public void SkiaBitmapDecodeToTargetSize(string input)
public void NetVipsResize(string input)
{
// Thumbnail to fit a 150x150 square
- using var thumb = NetVipsImage.Thumbnail(input, this.ThumbnailSize, this.ThumbnailSize);
+ using NetVipsImage thumb = NetVipsImage.Thumbnail(input, this.ThumbnailSize, this.ThumbnailSize);
// Save the results
thumb.Jpegsave(this.OutputPath(input), q: Quality, keep: NetVips.Enums.ForeignKeep.None);
diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs
index 77ac51e8a1..c08db84eb6 100644
--- a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs
@@ -334,7 +334,7 @@ public void Encode_AnimatedFormatTransform_FromWebp(TestImageProvider(TestImageProvider(TestImageProvider(TestImageProvider(TestImageProvider(TestImageProvider provider
Image imageFromMagick;
using (Stream stream = LoadAsStream(provider))
{
- var magickImage = new MagickImage(stream);
+ using MagickImage magickImage = new(stream);
// Apply Auto Level using the Grey (BT.709) channel.
magickImage.AutoLevel(Channels.Gray);
imageFromMagick = ConvertImageFromMagick(magickImage);
}
- using (Image image = provider.GetImage())
+ using Image image = provider.GetImage();
+ HistogramEqualizationOptions options = new()
{
- var options = new HistogramEqualizationOptions
- {
- Method = HistogramEqualizationMethod.AutoLevel,
- LuminanceLevels = 256,
- SyncChannels = true
- };
- image.Mutate(x => x.HistogramEqualization(options));
- image.DebugSave(provider);
- ExactImageComparer.Instance.CompareImages(imageFromMagick, image);
- }
+ Method = HistogramEqualizationMethod.AutoLevel,
+ LuminanceLevels = 256,
+ SyncChannels = true
+ };
+ image.Mutate(x => x.HistogramEqualization(options));
+ image.DebugSave(provider);
+ ExactImageComparer.Instance.CompareImages(imageFromMagick, image);
+
+ imageFromMagick.Dispose();
}
- private Stream LoadAsStream(TestImageProvider provider)
+ private static FileStream LoadAsStream(TestImageProvider provider)
where TPixel : unmanaged, ImageSharp.PixelFormats.IPixel
{
- string path = TestImageProvider.GetFilePathOrNull(provider);
- if (path == null)
- {
- throw new InvalidOperationException("CompareToMagick() works only with file providers!");
- }
+ string path = TestImageProvider.GetFilePathOrNull(provider)
+ ?? throw new InvalidOperationException("CompareToMagick() works only with file providers!");
- var testFile = TestFile.Create(path);
+ TestFile testFile = TestFile.Create(path);
return new FileStream(testFile.FullPath, FileMode.Open);
}
- private Image ConvertImageFromMagick(MagickImage magickImage)
+ private static Image ConvertImageFromMagick(MagickImage magickImage)
where TPixel : unmanaged, ImageSharp.PixelFormats.IPixel
{
Configuration configuration = Configuration.Default.Clone();
configuration.PreferContiguousImageBuffers = true;
- var result = new Image(configuration, magickImage.Width, magickImage.Height);
+ Image result = new(configuration, (int)magickImage.Width, (int)magickImage.Height);
Assert.True(result.DangerousTryGetSinglePixelMemory(out Memory resultPixels));
diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparingUtils.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparingUtils.cs
index f5c70b0885..05f65cfbbc 100644
--- a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparingUtils.cs
+++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparingUtils.cs
@@ -15,13 +15,10 @@ public static void CompareWithReferenceDecoder(
float compareTolerance = 0.01f)
where TPixel : unmanaged, ImageSharp.PixelFormats.IPixel
{
- string path = TestImageProvider.GetFilePathOrNull(provider);
- if (path == null)
- {
- throw new InvalidOperationException("CompareToOriginal() works only with file providers!");
- }
+ string path = TestImageProvider.GetFilePathOrNull(provider)
+ ?? throw new InvalidOperationException("CompareToOriginal() works only with file providers!");
- var testFile = TestFile.Create(path);
+ TestFile testFile = TestFile.Create(path);
using Image magickImage = DecodeWithMagick(new FileInfo(testFile.FullPath));
if (useExactComparer)
{
@@ -38,25 +35,23 @@ public static Image DecodeWithMagick(FileInfo fileInfo)
{
Configuration configuration = Configuration.Default.Clone();
configuration.PreferContiguousImageBuffers = true;
- using (var magickImage = new MagickImage(fileInfo))
- {
- magickImage.AutoOrient();
- var result = new Image(configuration, magickImage.Width, magickImage.Height);
+ using MagickImage magickImage = new(fileInfo);
+ magickImage.AutoOrient();
+ Image result = new(configuration, (int)magickImage.Width, (int)magickImage.Height);
- Assert.True(result.DangerousTryGetSinglePixelMemory(out Memory resultPixels));
+ Assert.True(result.DangerousTryGetSinglePixelMemory(out Memory resultPixels));
- using (IUnsafePixelCollection pixels = magickImage.GetPixelsUnsafe())
- {
- byte[] data = pixels.ToByteArray(PixelMapping.RGBA);
-
- PixelOperations.Instance.FromRgba32Bytes(
- configuration,
- data,
- resultPixels.Span,
- resultPixels.Length);
- }
+ using (IUnsafePixelCollection pixels = magickImage.GetPixelsUnsafe())
+ {
+ byte[] data = pixels.ToByteArray(PixelMapping.RGBA);
- return result;
+ PixelOperations.Instance.FromRgba32Bytes(
+ configuration,
+ data,
+ resultPixels.Span,
+ resultPixels.Length);
}
+
+ return result;
}
}
diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs
index f96dc19ee0..74015a4eff 100644
--- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs
+++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs
@@ -67,7 +67,7 @@ protected override Image Decode(DecoderOptions options, Stream s
List> framesList = [];
foreach (IMagickImage magicFrame in magickImageCollection)
{
- ImageFrame frame = new(configuration, magicFrame.Width, magicFrame.Height);
+ ImageFrame frame = new(configuration, (int)magicFrame.Width, (int)magicFrame.Height);
framesList.Add(frame);
MemoryGroup framePixels = frame.PixelBuffer.FastMemoryGroup;