diff --git a/src/ImageSharp.Drawing/Processing/Drawing/Brushes/SolidBrush{TPixel}.cs b/src/ImageSharp.Drawing/Processing/Drawing/Brushes/SolidBrush{TPixel}.cs index 826f5f60a7..f2054ee0d7 100644 --- a/src/ImageSharp.Drawing/Processing/Drawing/Brushes/SolidBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Processing/Drawing/Brushes/SolidBrush{TPixel}.cs @@ -90,16 +90,23 @@ internal override void Apply(Span scanline, int x, int y) MemoryManager memoryManager = this.Target.MemoryManager; - using (IBuffer amountBuffer = memoryManager.Allocate(scanline.Length)) + if (this.Options.BlendPercentage == 1f) { - Span amountSpan = amountBuffer.Span; - - for (int i = 0; i < scanline.Length; i++) + this.Blender.Blend(memoryManager, destinationRow, destinationRow, this.Colors.Span, scanline); + } + else + { + using (IBuffer amountBuffer = memoryManager.Allocate(scanline.Length)) { - amountSpan[i] = scanline[i] * this.Options.BlendPercentage; - } + Span amountSpan = amountBuffer.Span; + + for (int i = 0; i < scanline.Length; i++) + { + amountSpan[i] = scanline[i] * this.Options.BlendPercentage; + } - this.Blender.Blend(memoryManager, destinationRow, destinationRow, this.Colors.Span, amountSpan); + this.Blender.Blend(memoryManager, destinationRow, destinationRow, this.Colors.Span, amountSpan); + } } } } diff --git a/src/ImageSharp.Drawing/Processing/Drawing/Processors/FillProcessor.cs b/src/ImageSharp.Drawing/Processing/Drawing/Processors/FillProcessor.cs index b9f9b46001..645ff03537 100644 --- a/src/ImageSharp.Drawing/Processing/Drawing/Processors/FillProcessor.cs +++ b/src/ImageSharp.Drawing/Processing/Drawing/Processors/FillProcessor.cs @@ -3,6 +3,7 @@ using System; using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Drawing.Brushes; @@ -49,39 +50,66 @@ protected override void OnFrameApply(ImageFrame source, Rectangle source int minY = Math.Max(0, startY); int maxY = Math.Min(source.Height, endY); - // Reset offset if necessary. - if (minX > 0) - { - startX = 0; - } - - if (minY > 0) - { - startY = 0; - } - int width = maxX - minX; - using (IBuffer amount = source.MemoryManager.Allocate(width)) - using (BrushApplicator applicator = this.brush.CreateApplicator( - source, - sourceRectangle, - this.options)) + // If there's no reason for blending, then avoid it. + if (this.IsSolidBrushWithoutBlending(out SolidBrush solidBrush)) { - amount.Span.Fill(1f); - Parallel.For( minY, maxY, configuration.ParallelOptions, y => - { - int offsetY = y - startY; - int offsetX = minX - startX; + { + source.GetPixelRowSpan(y).Slice(minX, width).Fill(solidBrush.Color); + }); + } + else + { + // Reset offset if necessary. + if (minX > 0) + { + startX = 0; + } + + if (minY > 0) + { + startY = 0; + } + + using (IBuffer amount = source.MemoryManager.Allocate(width)) + using (BrushApplicator applicator = this.brush.CreateApplicator( + source, + sourceRectangle, + this.options)) + { + amount.Span.Fill(1f); + + Parallel.For( + minY, + maxY, + configuration.ParallelOptions, + y => + { + int offsetY = y - startY; + int offsetX = minX - startX; - applicator.Apply(amount.Span, offsetX, offsetY); - }); + applicator.Apply(amount.Span, offsetX, offsetY); + }); + } } } + + private bool IsSolidBrushWithoutBlending(out SolidBrush solidBrush) + { + solidBrush = this.brush as SolidBrush; + + return solidBrush != null + && ((this.options.BlenderMode == PixelBlenderMode.Normal && this.options.BlendPercentage == 1f + && solidBrush.Color.ToVector4().W == 1f) + || (this.options.BlenderMode == PixelBlenderMode.Over && this.options.BlendPercentage == 1f + && solidBrush.Color.ToVector4().W == 1f) + || (this.options.BlenderMode == PixelBlenderMode.Src)); + } } } \ No newline at end of file diff --git a/src/ImageSharp/IImageFrameCollection.cs b/src/ImageSharp/IImageFrameCollection.cs deleted file mode 100644 index 59c64a6af6..0000000000 --- a/src/ImageSharp/IImageFrameCollection.cs +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Collections.Generic; - -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp -{ - /// - /// Encapsulates a collection of instances that make up an . - /// - /// The type of the pixel. - public interface IImageFrameCollection : IEnumerable> - where TPixel : struct, IPixel - { - /// - /// Gets the number of frames. - /// - int Count { get; } - - /// - /// Gets the root frame. - /// - ImageFrame RootFrame { get; } - - /// - /// Gets the at the specified index. - /// - /// - /// The . - /// - /// The index. - /// The at the specified index. - ImageFrame this[int index] { get; } - - /// - /// Creates an with only the frame at the specified index - /// with the same metadata as the original image. - /// - /// The zero-based index of the frame to clone. - /// The new with the specified frame. - Image CloneFrame(int index); - - /// - /// Removes the frame at the specified index and creates a new image with only the removed frame - /// with the same metadata as the original image. - /// - /// The zero-based index of the frame to export. - /// Cannot remove last frame. - /// The new with the specified frame. - Image ExportFrame(int index); - - /// - /// Removes the frame at the specified index and frees all freeable resources associated with it. - /// - /// The zero-based index of the frame to remove. - /// Cannot remove last frame. - void RemoveFrame(int index); - - /// - /// Creates a new and appends it to the end of the collection. - /// - /// The new . - ImageFrame CreateFrame(); - - /// - /// Clones the frame and appends the clone to the end of the collection. - /// - /// The raw pixel data to generate the from. - /// The cloned . - ImageFrame AddFrame(ImageFrame source); - - /// - /// Creates a new frame from the pixel data with the same dimensions as the other frames and inserts the - /// new frame at the end of the collection. - /// - /// The raw pixel data to generate the from. - /// The new . - ImageFrame AddFrame(TPixel[] source); - - /// - /// Clones and inserts the into the at the specified . - /// - /// The zero-based index to insert the frame at. - /// The to clone and insert into the . - /// Frame must have the same dimensions as the image. - /// The cloned . - ImageFrame InsertFrame(int index, ImageFrame source); - - /// - /// Moves an from to . - /// - /// The zero-based index of the frame to move. - /// The index to move the frame to. - void MoveFrame(int sourceIndex, int destinationIndex); - - /// - /// Determines the index of a specific in the . - /// - /// The to locate in the . - /// The index of item if found in the list; otherwise, -1. - int IndexOf(ImageFrame frame); - - /// - /// Determines whether the contains the . - /// - /// The frame. - /// - /// true if the contains the specified frame; otherwise, false. - /// - bool Contains(ImageFrame frame); - } -} \ No newline at end of file diff --git a/src/ImageSharp/ImageFrameCollection.cs b/src/ImageSharp/ImageFrameCollection.cs index a9225eec49..0318a7068d 100644 --- a/src/ImageSharp/ImageFrameCollection.cs +++ b/src/ImageSharp/ImageFrameCollection.cs @@ -9,21 +9,24 @@ namespace SixLabors.ImageSharp { - /// - internal sealed class ImageFrameCollection : IImageFrameCollection + /// + /// Encapsulates a collection of instances that make up an . + /// + /// The type of the pixel. + public sealed class ImageFrameCollection : IEnumerable> where TPixel : struct, IPixel { private readonly IList> frames = new List>(); private readonly Image parent; - internal ImageFrameCollection(Image parent, int width, int height) + internal ImageFrameCollection(Image parent, int width, int height, TPixel backgroundColor) { Guard.NotNull(parent, nameof(parent)); this.parent = parent; // Frames are already cloned within the caller - this.frames.Add(new ImageFrame(parent.GetConfiguration().MemoryManager, width, height)); + this.frames.Add(new ImageFrame(parent.GetConfiguration(), width, height, backgroundColor)); } internal ImageFrameCollection(Image parent, IEnumerable> frames) @@ -41,51 +44,85 @@ internal ImageFrameCollection(Image parent, IEnumerable + /// + /// Gets the number of frames. + /// public int Count => this.frames.Count; - /// + /// + /// Gets the root frame. + /// public ImageFrame RootFrame => this.frames.Count > 0 ? this.frames[0] : null; - /// + /// + /// Gets the at the specified index. + /// + /// + /// The . + /// + /// The index. + /// The at the specified index. public ImageFrame this[int index] => this.frames[index]; - /// + /// + /// Determines the index of a specific in the . + /// + /// The to locate in the . + /// The index of item if found in the list; otherwise, -1. public int IndexOf(ImageFrame frame) => this.frames.IndexOf(frame); - /// - public ImageFrame InsertFrame(int index, ImageFrame frame) + /// + /// Clones and inserts the into the at the specified . + /// + /// The zero-based index to insert the frame at. + /// The to clone and insert into the . + /// Frame must have the same dimensions as the image. + /// The cloned . + public ImageFrame InsertFrame(int index, ImageFrame source) { - this.ValidateFrame(frame); - ImageFrame clonedFrame = frame.Clone(); + this.ValidateFrame(source); + ImageFrame clonedFrame = source.Clone(); this.frames.Insert(index, clonedFrame); return clonedFrame; } - /// - public ImageFrame AddFrame(ImageFrame frame) + /// + /// Clones the frame and appends the clone to the end of the collection. + /// + /// The raw pixel data to generate the from. + /// The cloned . + public ImageFrame AddFrame(ImageFrame source) { - this.ValidateFrame(frame); - ImageFrame clonedFrame = frame.Clone(); + this.ValidateFrame(source); + ImageFrame clonedFrame = source.Clone(); this.frames.Add(clonedFrame); return clonedFrame; } - /// - public ImageFrame AddFrame(TPixel[] data) + /// + /// Creates a new frame from the pixel data with the same dimensions as the other frames and inserts the + /// new frame at the end of the collection. + /// + /// The raw pixel data to generate the from. + /// The new . + public ImageFrame AddFrame(TPixel[] source) { - Guard.NotNull(data, nameof(data)); + Guard.NotNull(source, nameof(source)); var frame = ImageFrame.LoadPixelData( this.parent.GetMemoryManager(), - new Span(data), + new Span(source), this.RootFrame.Width, this.RootFrame.Height); this.frames.Add(frame); return frame; } - /// + /// + /// Removes the frame at the specified index and frees all freeable resources associated with it. + /// + /// The zero-based index of the frame to remove. + /// Cannot remove last frame. public void RemoveFrame(int index) { if (index == 0 && this.Count == 1) @@ -98,26 +135,42 @@ public void RemoveFrame(int index) frame.Dispose(); } - /// + /// + /// Determines whether the contains the . + /// + /// The frame. + /// + /// true if the contains the specified frame; otherwise, false. + /// public bool Contains(ImageFrame frame) { return this.frames.Contains(frame); } - /// - public void MoveFrame(int sourceIndex, int destIndex) + /// + /// Moves an from to . + /// + /// The zero-based index of the frame to move. + /// The index to move the frame to. + public void MoveFrame(int sourceIndex, int destinationIndex) { - if (sourceIndex == destIndex) + if (sourceIndex == destinationIndex) { return; } ImageFrame frameAtIndex = this.frames[sourceIndex]; this.frames.RemoveAt(sourceIndex); - this.frames.Insert(destIndex, frameAtIndex); + this.frames.Insert(destinationIndex, frameAtIndex); } - /// + /// + /// Removes the frame at the specified index and creates a new image with only the removed frame + /// with the same metadata as the original image. + /// + /// The zero-based index of the frame to export. + /// Cannot remove last frame. + /// The new with the specified frame. public Image ExportFrame(int index) { ImageFrame frame = this[index]; @@ -132,7 +185,12 @@ public Image ExportFrame(int index) return new Image(this.parent.GetConfiguration(), this.parent.MetaData.Clone(), new[] { frame }); } - /// + /// + /// Creates an with only the frame at the specified index + /// with the same metadata as the original image. + /// + /// The zero-based index of the frame to clone. + /// The new with the specified frame. public Image CloneFrame(int index) { ImageFrame frame = this[index]; @@ -140,10 +198,31 @@ public Image CloneFrame(int index) return new Image(this.parent.GetConfiguration(), this.parent.MetaData.Clone(), new[] { clonedFrame }); } - /// + /// + /// Creates a new and appends it to the end of the collection. + /// + /// + /// The new . + /// public ImageFrame CreateFrame() { - var frame = new ImageFrame(this.parent.GetConfiguration().MemoryManager, this.RootFrame.Width, this.RootFrame.Height); + return this.CreateFrame(default); + } + + /// + /// Creates a new and appends it to the end of the collection. + /// + /// The background color to initialize the pixels with. + /// + /// The new . + /// + public ImageFrame CreateFrame(TPixel backgroundColor) + { + var frame = new ImageFrame( + this.parent.GetConfiguration(), + this.RootFrame.Width, + this.RootFrame.Height, + backgroundColor); this.frames.Add(frame); return frame; } diff --git a/src/ImageSharp/ImageFrame{TPixel}.cs b/src/ImageSharp/ImageFrame{TPixel}.cs index cf7a1ae4fc..c3955c1321 100644 --- a/src/ImageSharp/ImageFrame{TPixel}.cs +++ b/src/ImageSharp/ImageFrame{TPixel}.cs @@ -4,6 +4,7 @@ using System; using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; @@ -18,8 +19,7 @@ namespace SixLabors.ImageSharp /// /// The pixel format. public sealed class ImageFrame : IPixelSource, IDisposable - where TPixel : struct, IPixel - { + where TPixel : struct, IPixel { private bool isDisposed; /// @@ -29,8 +29,7 @@ public sealed class ImageFrame : IPixelSource, IDisposable /// The width of the image in pixels. /// The height of the image in pixels. internal ImageFrame(MemoryManager memoryManager, int width, int height) - : this(memoryManager, width, height, new ImageFrameMetaData()) - { + : this(memoryManager, width, height, new ImageFrameMetaData()) { } /// @@ -52,6 +51,38 @@ internal ImageFrame(MemoryManager memoryManager, int width, int height, ImageFra this.MetaData = metaData; } + /// + /// Initializes a new instance of the class. + /// + /// The to use for buffer allocation and parallel options to clear the buffer with. + /// The width of the image in pixels. + /// The height of the image in pixels. + /// The color to clear the image with. + internal ImageFrame(Configuration configuration, int width, int height, TPixel backgroundColor) + : this(configuration, width, height, backgroundColor, new ImageFrameMetaData()) { + } + + /// + /// Initializes a new instance of the class. + /// + /// The to use for buffer allocation and parallel options to clear the buffer with. + /// The width of the image in pixels. + /// The height of the image in pixels. + /// The color to clear the image with. + /// The meta data. + internal ImageFrame(Configuration configuration, int width, int height, TPixel backgroundColor, ImageFrameMetaData metaData) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.MustBeGreaterThan(width, 0, nameof(width)); + Guard.MustBeGreaterThan(height, 0, nameof(height)); + Guard.NotNull(metaData, nameof(metaData)); + + this.MemoryManager = configuration.MemoryManager; + this.PixelBuffer = this.MemoryManager.Allocate2D(width, height, false); + this.Clear(configuration.ParallelOptions, backgroundColor); + this.MetaData = metaData; + } + /// /// Initializes a new instance of the class. /// @@ -59,8 +90,7 @@ internal ImageFrame(MemoryManager memoryManager, int width, int height, ImageFra /// The of the frame. /// The meta data. internal ImageFrame(MemoryManager memoryManager, Size size, ImageFrameMetaData metaData) - : this(memoryManager, size.Width, size.Height, metaData) - { + : this(memoryManager, size.Width, size.Height, metaData) { } /// @@ -267,6 +297,23 @@ internal ImageFrame CloneAs() return target; } + /// + /// Clears the bitmap. + /// + /// The parallel options. + /// The value to initialize the bitmap with. + internal void Clear(ParallelOptions parallelOptions, TPixel value) { + Parallel.For( + 0, + this.Height, + parallelOptions, + (int y) => + { + Span targetRow = this.GetPixelRowSpan(y); + targetRow.Fill(value); + }); + } + /// /// Clones the current instance. /// diff --git a/src/ImageSharp/Image{TPixel}.cs b/src/ImageSharp/Image{TPixel}.cs index 78a091e414..596dc9bcd0 100644 --- a/src/ImageSharp/Image{TPixel}.cs +++ b/src/ImageSharp/Image{TPixel}.cs @@ -37,6 +37,21 @@ public Image(Configuration configuration, int width, int height) { } + /// + /// Initializes a new instance of the class + /// with the height and the width of the image. + /// + /// + /// The configuration providing initialization code which allows extending the library. + /// + /// The width of the image in pixels. + /// The height of the image in pixels. + /// The color to initialize the pixels with. + public Image(Configuration configuration, int width, int height, TPixel backgroundColor) + : this(configuration, width, height, backgroundColor, new ImageMetaData()) + { + } + /// /// Initializes a new instance of the class /// with the height and the width of the image. @@ -63,7 +78,25 @@ internal Image(Configuration configuration, int width, int height, ImageMetaData this.configuration = configuration ?? Configuration.Default; this.PixelType = new PixelTypeInfo(Unsafe.SizeOf() * 8); this.MetaData = metadata ?? new ImageMetaData(); - this.frames = new ImageFrameCollection(this, width, height); + this.frames = new ImageFrameCollection(this, width, height, default(TPixel)); + } + + /// + /// Initializes a new instance of the class + /// with the height and the width of the image. + /// + /// + /// The configuration providing initialization code which allows extending the library. + /// + /// The width of the image in pixels. + /// The height of the image in pixels. + /// The color to initialize the pixels with. + /// The images metadata. + internal Image(Configuration configuration, int width, int height, TPixel backgroundColor, ImageMetaData metadata) { + this.configuration = configuration ?? Configuration.Default; + this.PixelType = new PixelTypeInfo(Unsafe.SizeOf() * 8); + this.MetaData = metadata ?? new ImageMetaData(); + this.frames = new ImageFrameCollection(this, width, height, backgroundColor); } /// @@ -102,7 +135,7 @@ internal Image(Configuration configuration, ImageMetaData metadata, IEnumerable< /// /// Gets the frames. /// - public IImageFrameCollection Frames => this.frames; + public ImageFrameCollection Frames => this.frames; /// /// Gets the root frame. diff --git a/src/ImageSharp/PixelFormats/PixelBlenderMode.cs b/src/ImageSharp/PixelFormats/PixelBlenderMode.cs index 4b8f56d766..7a8ab6592a 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenderMode.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenderMode.cs @@ -54,62 +54,62 @@ public enum PixelBlenderMode HardLight, /// - /// returns the source colors + /// returns the source colors. /// Src, /// - /// returns the source over the destination + /// returns the source over the destination. /// Atop, /// - /// returns the detination over the source + /// returns the destination over the source. /// Over, /// - /// the source where the desitnation and source overlap + /// The source where the destination and source overlap. /// In, /// - /// the destination where the desitnation and source overlap + /// The destination where the destination and source overlap. /// Out, /// - /// the destination where the source does not overlap it + /// The destination where the source does not overlap it. /// Dest, /// - /// the source where they dont overlap othersie dest in overlapping parts + /// The source where they don't overlap othersie dest in overlapping parts. /// DestAtop, /// - /// the destnation over the source + /// The destination over the source. /// DestOver, /// - /// the destination where the desitnation and source overlap + /// The destination where the destination and source overlap. /// DestIn, /// - /// the source where the desitnation and source overlap + /// The source where the destination and source overlap. /// DestOut, /// - /// the clear. + /// The clear. /// Clear, /// - /// clear where they overlap + /// Clear where they overlap. /// Xor } diff --git a/tests/ImageSharp.Tests/Image/ImageFramesCollectionTests.cs b/tests/ImageSharp.Tests/Image/ImageFramesCollectionTests.cs index 4c760e6810..c2ebf83ba7 100644 --- a/tests/ImageSharp.Tests/Image/ImageFramesCollectionTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageFramesCollectionTests.cs @@ -19,7 +19,7 @@ public ImageFramesCollectionTests() System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("en-US"); this.image = new Image(10, 10); - this.collection = new ImageFrameCollection(this.image, 10, 10); + this.collection = new ImageFrameCollection(this.image, 10, 10, default); } [Fact] @@ -225,10 +225,21 @@ public void ExtractFrame(TestImageProvider provider) } [Fact] - public void CreateFrame() + public void CreateFrame_Default() { this.image.Frames.CreateFrame(); + + Assert.Equal(2, this.image.Frames.Count); + this.image.Frames[1].ComparePixelBufferTo(default(Rgba32)); + } + + [Fact] + public void CreateFrame_CustomFillColor() + { + this.image.Frames.CreateFrame(Rgba32.HotPink); + Assert.Equal(2, this.image.Frames.Count); + this.image.Frames[1].ComparePixelBufferTo(Rgba32.HotPink); } [Fact] diff --git a/tests/ImageSharp.Tests/Image/ImageTests.cs b/tests/ImageSharp.Tests/Image/ImageTests.cs index da813f4280..8234df24ef 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.cs @@ -13,10 +13,60 @@ namespace SixLabors.ImageSharp.Tests /// /// Tests the class. /// - public class ImageTests : FileTestBase + public class ImageTests { + public class Constructor + { + [Fact] + public void Width_Height() + { + using (var image = new Image(11, 23)) + { + Assert.Equal(11, image.Width); + Assert.Equal(23, image.Height); + Assert.Equal(11*23, image.GetPixelSpan().Length); + image.ComparePixelBufferTo(default(Rgba32)); + + Assert.Equal(Configuration.Default, image.GetConfiguration()); + } + } + + [Fact] + public void Configuration_Width_Height() + { + Configuration configuration = Configuration.Default.ShallowCopy(); + + using (var image = new Image(configuration, 11, 23)) + { + Assert.Equal(11, image.Width); + Assert.Equal(23, image.Height); + Assert.Equal(11 * 23, image.GetPixelSpan().Length); + image.ComparePixelBufferTo(default(Rgba32)); + + Assert.Equal(configuration, image.GetConfiguration()); + } + } + + [Fact] + public void Configuration_Width_Height_BackroundColor() + { + Configuration configuration = Configuration.Default.ShallowCopy(); + Rgba32 color = Rgba32.Aquamarine; + + using (var image = new Image(configuration, 11, 23, color)) + { + Assert.Equal(11, image.Width); + Assert.Equal(23, image.Height); + Assert.Equal(11 * 23, image.GetPixelSpan().Length); + image.ComparePixelBufferTo(color); + + Assert.Equal(configuration, image.GetConfiguration()); + } + } + } + [Fact] - public void ConstructorByteArray() + public void Load_ByteArray() { Assert.Throws(() => { @@ -32,7 +82,7 @@ public void ConstructorByteArray() } [Fact] - public void ConstructorFileSystem() + public void Load_FileSystemPath() { TestFile file = TestFile.Create(TestImages.Bmp.Car); using (Image image = Image.Load(file.FullPath)) @@ -43,7 +93,7 @@ public void ConstructorFileSystem() } [Fact] - public void ConstructorFileSystem_FileNotFound() + public void Load_FileSystemPath_FileNotFound() { System.IO.FileNotFoundException ex = Assert.Throws( () => @@ -53,7 +103,7 @@ public void ConstructorFileSystem_FileNotFound() } [Fact] - public void ConstructorFileSystem_NullPath() + public void Load_FileSystemPath_NullPath() { ArgumentNullException ex = Assert.Throws( () => diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index f37df48dce..8955eeb63c 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -361,14 +361,25 @@ public static Image ComparePixelBufferTo( public static Image ComparePixelBufferTo(this Image image, TPixel expectedPixel) where TPixel : struct, IPixel { - Span actualPixels = image.GetPixelSpan(); + foreach (ImageFrame imageFrame in image.Frames) + { + imageFrame.ComparePixelBufferTo(expectedPixel); + } + + return image; + } + + public static ImageFrame ComparePixelBufferTo(this ImageFrame imageFrame, TPixel expectedPixel) + where TPixel : struct, IPixel + { + Span actualPixels = imageFrame.GetPixelSpan(); for (int i = 0; i < actualPixels.Length; i++) { Assert.True(expectedPixel.Equals(actualPixels[i]), $"Pixels are different on position {i}!"); } - return image; + return imageFrame; } public static ImageFrame ComparePixelBufferTo(