// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.Memory; namespace SixLabors.ImageSharp.Tests; public abstract partial class ImageFrameCollectionTests { [GroupOutput("ImageFramesCollectionTests")] public class Generic : ImageFrameCollectionTests { [Fact] public void Constructor_ShouldCreateOneFrame() => Assert.Equal(1, this.Collection.Count); [Fact] public void AddNewFrame_FramesMustHaveSameSize() { ArgumentException ex = Assert.Throws( () => { using ImageFrame frame = new(Configuration.Default, 1, 1); using ImageFrame addedFrame = this.Collection.AddFrame(frame); }); Assert.StartsWith("Frame must have the same dimensions as the image.", ex.Message); } [Fact] public void AddNewFrame_Frame_FramesNotBeNull() { ArgumentNullException ex = Assert.Throws( () => { using ImageFrame addedFrame = this.Collection.AddFrame((ImageFrame)null); }); Assert.StartsWith("Value cannot be null. (Parameter 'frame')", ex.Message); } [Fact] public void AddNewFrame_PixelBuffer_DataMustNotBeNull() { Rgba32[] data = null; ArgumentNullException ex = Assert.Throws( () => { using ImageFrame addedFrame = this.Collection.AddFrame(data); }); Assert.StartsWith("Value cannot be null. (Parameter 'source')", ex.Message); } [Fact] public void AddNewFrame_PixelBuffer_BufferIncorrectSize() { ArgumentOutOfRangeException ex = Assert.Throws( () => { using ImageFrame addedFrame = this.Collection.AddFrame(Array.Empty()); }); Assert.StartsWith($"Parameter \"data\" ({typeof(int)}) must be greater than or equal to {100}, was {0}", ex.Message); } [Fact] public void InsertNewFrame_FramesMustHaveSameSize() { ArgumentException ex = Assert.Throws( () => { using ImageFrame frame = new(Configuration.Default, 1, 1); using ImageFrame insertedFrame = this.Collection.InsertFrame(1, frame); }); Assert.StartsWith("Frame must have the same dimensions as the image.", ex.Message); } [Fact] public void InsertNewFrame_FramesNotBeNull() { ArgumentNullException ex = Assert.Throws( () => { using ImageFrame insertedFrame = this.Collection.InsertFrame(1, null); }); Assert.StartsWith("Value cannot be null. (Parameter 'frame')", ex.Message); } [Fact] public void Constructor_FramesMustHaveSameSize() { ArgumentException ex = Assert.Throws( () => { using ImageFrame imageFrame1 = new(Configuration.Default, 10, 10); using ImageFrame imageFrame2 = new(Configuration.Default, 1, 1); new ImageFrameCollection( this.Image, new[] { imageFrame1, imageFrame2 }); }); Assert.StartsWith("Frame must have the same dimensions as the image.", ex.Message); } [Fact] public void RemoveAtFrame_ThrowIfRemovingLastFrame() { using ImageFrame imageFrame = new(Configuration.Default, 10, 10); ImageFrameCollection collection = new( this.Image, new[] { imageFrame }); InvalidOperationException ex = Assert.Throws( () => collection.RemoveFrame(0)); Assert.Equal("Cannot remove last frame.", ex.Message); } [Fact] public void RemoveAtFrame_CanRemoveFrameZeroIfMultipleFramesExist() { using ImageFrame imageFrame1 = new(Configuration.Default, 10, 10); using ImageFrame imageFrame2 = new(Configuration.Default, 10, 10); ImageFrameCollection collection = new( this.Image, new[] { imageFrame1, imageFrame2 }); collection.RemoveFrame(0); Assert.Equal(1, collection.Count); } [Fact] public void RootFrameIsFrameAtIndexZero() { using ImageFrame imageFrame1 = new(Configuration.Default, 10, 10); using ImageFrame imageFrame2 = new(Configuration.Default, 10, 10); ImageFrameCollection collection = new( this.Image, new[] { imageFrame1, imageFrame2 }); Assert.Equal(collection.RootFrame, collection[0]); } [Fact] public void ConstructorPopulatesFrames() { using ImageFrame imageFrame1 = new(Configuration.Default, 10, 10); using ImageFrame imageFrame2 = new(Configuration.Default, 10, 10); ImageFrameCollection collection = new( this.Image, new[] { imageFrame1, imageFrame2 }); Assert.Equal(2, collection.Count); } [Fact] public void DisposeClearsCollection() { using ImageFrame imageFrame1 = new(Configuration.Default, 10, 10); using ImageFrame imageFrame2 = new(Configuration.Default, 10, 10); ImageFrameCollection collection = new( this.Image, new[] { imageFrame1, imageFrame2 }); collection.Dispose(); Assert.Equal(0, collection.Count); } [Fact] public void Dispose_DisposesAllInnerFrames() { using ImageFrame imageFrame1 = new(Configuration.Default, 10, 10); using ImageFrame imageFrame2 = new(Configuration.Default, 10, 10); ImageFrameCollection collection = new( this.Image, new[] { imageFrame1, imageFrame2 }); IPixelSource[] framesSnapShot = collection.OfType>().ToArray(); Assert.All(framesSnapShot, f => Assert.False(f.PixelBuffer.IsDisposed)); collection.Dispose(); Assert.All(framesSnapShot, f => Assert.True(f.PixelBuffer.IsDisposed)); } [Theory] [WithTestPatternImages(10, 10, PixelTypes.Rgba32)] public void CloneFrame(TestImageProvider provider) where TPixel : unmanaged, IPixel { using Image img = provider.GetImage(); using ImageFrame imageFrame = new(Configuration.Default, 10, 10); using ImageFrame addedFrame = img.Frames.AddFrame(imageFrame); // add a frame anyway using Image cloned = img.Frames.CloneFrame(0); Assert.Equal(2, img.Frames.Count); Assert.True(img.DangerousTryGetSinglePixelMemory(out Memory imgMem)); cloned.ComparePixelBufferTo(imgMem); } [Theory] [WithTestPatternImages(10, 10, PixelTypes.Rgba32)] public void ExtractFrame(TestImageProvider provider) where TPixel : unmanaged, IPixel { using Image img = provider.GetImage(); Assert.True(img.DangerousTryGetSinglePixelMemory(out Memory imgMemory)); TPixel[] sourcePixelData = imgMemory.ToArray(); using ImageFrame imageFrame = new(Configuration.Default, 10, 10); using ImageFrame addedFrame = img.Frames.AddFrame(imageFrame); using Image cloned = img.Frames.ExportFrame(0); Assert.Equal(1, img.Frames.Count); cloned.ComparePixelBufferTo(sourcePixelData.AsSpan()); } [Fact] public void CreateFrame_Default() { using (this.Image.Frames.CreateFrame()) { Assert.Equal(2, this.Image.Frames.Count); this.Image.Frames[1].ComparePixelBufferTo(default(Rgba32)); } } [Fact] public void CreateFrame_CustomFillColor() { using (this.Image.Frames.CreateFrame(Color.HotPink)) { Assert.Equal(2, this.Image.Frames.Count); this.Image.Frames[1].ComparePixelBufferTo(Color.HotPink); } } [Fact] public void AddFrameFromPixelData() { Assert.True(this.Image.Frames.RootFrame.DangerousTryGetSinglePixelMemory(out Memory imgMem)); Rgba32[] pixelData = imgMem.ToArray(); using ImageFrame addedFrame = this.Image.Frames.AddFrame(pixelData); Assert.Equal(2, this.Image.Frames.Count); } [Fact] public void AddFrame_clones_sourceFrame() { using ImageFrame otherFrame = new(Configuration.Default, 10, 10); using ImageFrame addedFrame = this.Image.Frames.AddFrame(otherFrame); Assert.True(otherFrame.DangerousTryGetSinglePixelMemory(out Memory otherFrameMem)); addedFrame.ComparePixelBufferTo(otherFrameMem.Span); Assert.NotEqual(otherFrame, addedFrame); } [Fact] public void InsertFrame_clones_sourceFrame() { using ImageFrame otherFrame = new(Configuration.Default, 10, 10); using ImageFrame addedFrame = this.Image.Frames.InsertFrame(0, otherFrame); Assert.True(otherFrame.DangerousTryGetSinglePixelMemory(out Memory otherFrameMem)); addedFrame.ComparePixelBufferTo(otherFrameMem.Span); Assert.NotEqual(otherFrame, addedFrame); } [Fact] public void MoveFrame_LeavesFrameInCorrectLocation() { for (int i = 0; i < 9; i++) { this.Image.Frames.CreateFrame(); } ImageFrame frame = this.Image.Frames[4]; this.Image.Frames.MoveFrame(4, 7); int newIndex = this.Image.Frames.IndexOf(frame); Assert.Equal(7, newIndex); } [Fact] public void IndexOf_ReturnsCorrectIndex() { for (int i = 0; i < 9; i++) { this.Image.Frames.CreateFrame(); } ImageFrame frame = this.Image.Frames[4]; int index = this.Image.Frames.IndexOf(frame); Assert.Equal(4, index); } [Fact] public void Contains_TrueIfMember() { for (int i = 0; i < 9; i++) { this.Image.Frames.CreateFrame(); } ImageFrame frame = this.Image.Frames[4]; Assert.True(this.Image.Frames.Contains(frame)); } [Fact] public void Contains_FalseIfNonMember() { for (int i = 0; i < 9; i++) { this.Image.Frames.CreateFrame(); } using ImageFrame frame = new(Configuration.Default, 10, 10); Assert.False(this.Image.Frames.Contains(frame)); } [Fact] public void PreferContiguousImageBuffers_True_AppliedToAllFrames() { Configuration configuration = Configuration.Default.Clone(); configuration.MemoryAllocator = new TestMemoryAllocator { BufferCapacityInBytes = 1000 }; configuration.PreferContiguousImageBuffers = true; using Image image = new(configuration, 100, 100); image.Frames.CreateFrame(); image.Frames.InsertFrame(0, image.Frames[0]); image.Frames.CreateFrame(Color.Red); Assert.Equal(4, image.Frames.Count); foreach (ImageFrame frame in image.Frames) { Assert.True(frame.DangerousTryGetSinglePixelMemory(out Memory _)); } } [Fact] public void DisposeCall_NoThrowIfCalledMultiple() { Image image = new(Configuration.Default, 10, 10); ImageFrameCollection frameCollection = image.Frames; image.Dispose(); // this should invalidate underlying collection as well frameCollection.Dispose(); } [Fact] public void PublicProperties_ThrowIfDisposed() { Image image = new(Configuration.Default, 10, 10); ImageFrameCollection frameCollection = image.Frames; image.Dispose(); // this should invalidate underlying collection as well Assert.Throws(() => { ImageFrame prop = frameCollection.RootFrame; }); } [Fact] public void PublicMethods_ThrowIfDisposed() { Image image = new(Configuration.Default, 10, 10); ImageFrameCollection frameCollection = image.Frames; image.Dispose(); // this should invalidate underlying collection as well Assert.Throws(() => { ImageFrame res = frameCollection.AddFrame(default(ImageFrame)); }); Assert.Throws(() => { Image res = frameCollection.CloneFrame(default); }); Assert.Throws(() => { bool res = frameCollection.Contains(default); }); Assert.Throws(() => { ImageFrame res = frameCollection.CreateFrame(); }); Assert.Throws(() => { ImageFrame res = frameCollection.CreateFrame(default); }); Assert.Throws(() => { Image res = frameCollection.ExportFrame(default); }); Assert.Throws(() => { IEnumerator> res = frameCollection.GetEnumerator(); }); Assert.Throws(() => { int prop = frameCollection.IndexOf(default); }); Assert.Throws(() => { ImageFrame prop = frameCollection.InsertFrame(default, default); }); Assert.Throws(() => frameCollection.RemoveFrame(default)); Assert.Throws(() => frameCollection.MoveFrame(default, default)); } } }