Browse Source

FrameCollection responsible for adding initial frame

af/merge-core
Scott Williams 9 years ago
parent
commit
80b4582c48
  1. 45
      src/ImageSharp/Image/ImageFrameCollection.cs
  2. 6
      src/ImageSharp/Image/ImageFrame{TPixel}.cs
  3. 32
      src/ImageSharp/Image/Image{TPixel}.cs
  4. 2
      src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs
  5. 219
      tests/ImageSharp.Tests/Image/ImageFramesCollectionTests.cs

45
src/ImageSharp/Image/ImageFrameCollection.cs

@ -13,11 +13,25 @@ namespace SixLabors.ImageSharp
/// Encapsulates an imaged collection of frames. /// Encapsulates an imaged collection of frames.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam> /// <typeparam name="TPixel">The type of the pixel.</typeparam>
public sealed class ImageFrameCollection<TPixel> : IEnumerable<ImageFrame<TPixel>> public sealed class ImageFrameCollection<TPixel> : IEnumerable<ImageFrame<TPixel>>, IDisposable
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
private readonly IList<ImageFrame<TPixel>> frames = new List<ImageFrame<TPixel>>(); private readonly IList<ImageFrame<TPixel>> frames = new List<ImageFrame<TPixel>>();
internal ImageFrameCollection(int width, int height)
{
this.Add(new ImageFrame<TPixel>(width, height));
}
internal ImageFrameCollection(IEnumerable<ImageFrame<TPixel>> frames)
{
Guard.NotNullOrEmpty(frames, nameof(frames));
foreach (ImageFrame<TPixel> f in frames)
{
this.Add(f);
}
}
/// <summary> /// <summary>
/// Gets the count. /// Gets the count.
/// </summary> /// </summary>
@ -42,7 +56,7 @@ namespace SixLabors.ImageSharp
set set
{ {
this.ValidateFrameSize(value); this.ValidateFrame(value);
this.frames[index] = value; this.frames[index] = value;
} }
} }
@ -61,7 +75,7 @@ namespace SixLabors.ImageSharp
/// <param name="frame">The <seealso cref="ImageFrame{TPixel}"/> to insert into the <seealso cref="Image{TPixel}"/>.</param> /// <param name="frame">The <seealso cref="ImageFrame{TPixel}"/> to insert into the <seealso cref="Image{TPixel}"/>.</param>
public void Insert(int index, ImageFrame<TPixel> frame) public void Insert(int index, ImageFrame<TPixel> frame)
{ {
this.ValidateFrameSize(frame); this.ValidateFrame(frame);
this.frames.Insert(index, frame); this.frames.Insert(index, frame);
} }
@ -72,12 +86,12 @@ namespace SixLabors.ImageSharp
/// <exception cref="InvalidOperationException">Cannot remove last frame.</exception> /// <exception cref="InvalidOperationException">Cannot remove last frame.</exception>
public void RemoveAt(int index) public void RemoveAt(int index)
{ {
if (index > 0 || this.frames.Count > 1) if (index == 0 && this.Count == 1)
{ {
this.frames.RemoveAt(index); throw new InvalidOperationException("Cannot remove last frame.");
} }
throw new InvalidOperationException("Cannot remove last frame."); this.frames.RemoveAt(index);
} }
/// <summary> /// <summary>
@ -87,7 +101,7 @@ namespace SixLabors.ImageSharp
/// <exception cref="ArgumentException">Frame must have the same dimensions as the image - frame</exception> /// <exception cref="ArgumentException">Frame must have the same dimensions as the image - frame</exception>
public void Add(ImageFrame<TPixel> frame) public void Add(ImageFrame<TPixel> frame)
{ {
this.ValidateFrameSize(frame); this.ValidateFrame(frame);
this.frames.Add(frame); this.frames.Add(frame);
} }
@ -113,7 +127,7 @@ namespace SixLabors.ImageSharp
{ {
if (this.Count == 1 && this.frames.Contains(frame)) if (this.Count == 1 && this.frames.Contains(frame))
{ {
throw new InvalidOperationException("Cannot remove last frame"); throw new InvalidOperationException("Cannot remove last frame.");
} }
return this.frames.Remove(frame); return this.frames.Remove(frame);
@ -125,7 +139,7 @@ namespace SixLabors.ImageSharp
/// <inheritdoc/> /// <inheritdoc/>
IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)this.frames).GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)this.frames).GetEnumerator();
private void ValidateFrameSize(ImageFrame<TPixel> frame) private void ValidateFrame(ImageFrame<TPixel> frame)
{ {
Guard.NotNull(frame, nameof(frame)); Guard.NotNull(frame, nameof(frame));
@ -133,9 +147,20 @@ namespace SixLabors.ImageSharp
{ {
if (this.RootFrame.Width != frame.Width || this.RootFrame.Height != frame.Height) if (this.RootFrame.Width != frame.Width || this.RootFrame.Height != frame.Height)
{ {
throw new ArgumentException("Frame must have the same dimensions as the image", nameof(frame)); throw new ArgumentException("Frame must have the same dimensions as the image.", nameof(frame));
} }
} }
} }
/// <inheritdoc/>
public void Dispose()
{
foreach (ImageFrame<TPixel> f in this.frames)
{
f.Dispose();
}
this.frames.Clear();
}
} }
} }

6
src/ImageSharp/Image/ImageFrame{TPixel}.cs

@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp
/// </summary> /// </summary>
private Buffer2D<TPixel> pixelBuffer; private Buffer2D<TPixel> pixelBuffer;
private bool isDisposed = false; private bool isDisosed = false;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ImageFrame{TPixel}" /> class. /// Initializes a new instance of the <see cref="ImageFrame{TPixel}" /> class.
@ -180,7 +180,7 @@ namespace SixLabors.ImageSharp
/// </summary> /// </summary>
public void Dispose() public void Dispose()
{ {
if (this.isDisposed) if (this.isDisosed)
{ {
return; return;
} }
@ -189,7 +189,7 @@ namespace SixLabors.ImageSharp
this.pixelBuffer = null; this.pixelBuffer = null;
// Note disposing is done. // Note disposing is done.
this.isDisposed = true; this.isDisosed = true;
} }
/// <inheritdoc/> /// <inheritdoc/>

32
src/ImageSharp/Image/Image{TPixel}.cs

@ -58,8 +58,10 @@ namespace SixLabors.ImageSharp
/// <param name="height">The height of the image in pixels.</param> /// <param name="height">The height of the image in pixels.</param>
/// <param name="metadata">The images metadata.</param> /// <param name="metadata">The images metadata.</param>
internal Image(Configuration configuration, int width, int height, ImageMetaData metadata) internal Image(Configuration configuration, int width, int height, ImageMetaData metadata)
: this(configuration, width, height, metadata, null)
{ {
this.configuration = configuration ?? Configuration.Default;
this.MetaData = metadata ?? new ImageMetaData();
this.Frames = new ImageFrameCollection<TPixel>(width, height);
} }
/// <summary> /// <summary>
@ -67,29 +69,14 @@ namespace SixLabors.ImageSharp
/// with the height and the width of the image. /// with the height and the width of the image.
/// </summary> /// </summary>
/// <param name="configuration">The configuration providing initialization code which allows extending the library.</param> /// <param name="configuration">The configuration providing initialization code which allows extending the library.</param>
/// <param name="width">The width of the image in pixels.</param>
/// <param name="height">The height of the image in pixels.</param>
/// <param name="metadata">The images metadata.</param> /// <param name="metadata">The images metadata.</param>
/// <param name="frames">The frames that will be owned by this image instance.</param> /// <param name="frames">The frames that will be owned by this image instance.</param>
internal Image(Configuration configuration, int width, int height, ImageMetaData metadata, IEnumerable<ImageFrame<TPixel>> frames) internal Image(Configuration configuration, ImageMetaData metadata, IEnumerable<ImageFrame<TPixel>> frames)
{ {
this.configuration = configuration ?? Configuration.Default; this.configuration = configuration ?? Configuration.Default;
this.MetaData = metadata ?? new ImageMetaData(); this.MetaData = metadata ?? new ImageMetaData();
this.Frames = new ImageFrameCollection<TPixel>(); this.Frames = new ImageFrameCollection<TPixel>(frames);
if (frames != null)
{
foreach (ImageFrame<TPixel> f in frames)
{
this.Frames.Add(f);
}
}
if (this.Frames.Count == 0)
{
this.Frames.Add(new ImageFrame<TPixel>(width, height));
}
} }
/// <summary> /// <summary>
@ -157,7 +144,7 @@ namespace SixLabors.ImageSharp
{ {
IEnumerable<ImageFrame<TPixel>> frames = this.Frames.Select(x => x.Clone()).ToArray(); IEnumerable<ImageFrame<TPixel>> frames = this.Frames.Select(x => x.Clone()).ToArray();
return new Image<TPixel>(this.configuration, this.Width, this.Height, this.MetaData.Clone(), frames); return new Image<TPixel>(this.configuration, this.MetaData.Clone(), frames);
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -175,7 +162,7 @@ namespace SixLabors.ImageSharp
where TPixel2 : struct, IPixel<TPixel2> where TPixel2 : struct, IPixel<TPixel2>
{ {
IEnumerable<ImageFrame<TPixel2>> frames = this.Frames.Select(x => x.CloneAs<TPixel2>()).ToArray(); IEnumerable<ImageFrame<TPixel2>> frames = this.Frames.Select(x => x.CloneAs<TPixel2>()).ToArray();
var target = new Image<TPixel2>(this.configuration, this.Width, this.Height, this.MetaData, frames); var target = new Image<TPixel2>(this.configuration, this.MetaData, frames);
return target; return target;
} }
@ -185,10 +172,7 @@ namespace SixLabors.ImageSharp
/// </summary> /// </summary>
public void Dispose() public void Dispose()
{ {
for (int i = 0; i < this.Frames.Count; i++) this.Frames.Dispose();
{
this.Frames[i].Dispose();
}
} }
/// <summary> /// <summary>

2
src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs

@ -60,7 +60,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
// For resize we know we are going to populate every pixel with fresh data and we want a different target size so // For resize we know we are going to populate every pixel with fresh data and we want a different target size so
// let's manually clone an empty set of images at the correct target and then have the base class processs them in turn. // let's manually clone an empty set of images at the correct target and then have the base class processs them in turn.
IEnumerable<ImageFrame<TPixel>> frames = source.Frames.Select(x => new ImageFrame<TPixel>(this.Width, this.Height, x.MetaData.Clone())); // this will create places holders IEnumerable<ImageFrame<TPixel>> frames = source.Frames.Select(x => new ImageFrame<TPixel>(this.Width, this.Height, x.MetaData.Clone())); // this will create places holders
var image = new Image<TPixel>(config, this.Width, this.Height, source.MetaData.Clone(), frames); // base the place holder images in to prevet a extra frame being added var image = new Image<TPixel>(config, source.MetaData.Clone(), frames); // base the place holder images in to prevet a extra frame being added
return image; return image;
} }

219
tests/ImageSharp.Tests/Image/ImageFramesCollectionTests.cs

@ -0,0 +1,219 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SixLabors.ImageSharp.Advanced;
using Xunit;
namespace SixLabors.ImageSharp.Tests
{
public class ImageFramesCollectionTests
{
[Fact]
public void ImageFramesaLwaysHaveOneFrame()
{
var collection = new ImageFrameCollection<Rgba32>(10, 10);
Assert.Equal(1, collection.Count);
}
[Fact]
public void AddNewFrame_FramesMustHaveSameSize()
{
var collection = new ImageFrameCollection<Rgba32>(10, 10);
ArgumentException ex = Assert.Throws<ArgumentException>(() =>
{
collection.Add(new ImageFrame<Rgba32>(1, 1));
});
Assert.Equal("Frame must have the same dimensions as the image.\r\nParameter name: frame", ex.Message);
}
[Fact]
public void AddNewFrame_FramesNotBeNull()
{
var collection = new ImageFrameCollection<Rgba32>(10, 10);
ArgumentNullException ex = Assert.Throws<ArgumentNullException>(() =>
{
collection.Add(null);
});
Assert.Equal("Value cannot be null.\r\nParameter name: frame", ex.Message);
}
[Fact]
public void InsertNewFrame_FramesMustHaveSameSize()
{
var collection = new ImageFrameCollection<Rgba32>(10, 10);
ArgumentException ex = Assert.Throws<ArgumentException>(() =>
{
collection.Insert(1, new ImageFrame<Rgba32>(1, 1));
});
Assert.Equal("Frame must have the same dimensions as the image.\r\nParameter name: frame", ex.Message);
}
[Fact]
public void InsertNewFrame_FramesNotBeNull()
{
var collection = new ImageFrameCollection<Rgba32>(10, 10);
ArgumentNullException ex = Assert.Throws<ArgumentNullException>(() =>
{
collection.Insert(1, null);
});
Assert.Equal("Value cannot be null.\r\nParameter name: frame", ex.Message);
}
[Fact]
public void SetFrameAtIndex_FramesMustHaveSameSize()
{
var collection = new ImageFrameCollection<Rgba32>(10, 10);
ArgumentException ex = Assert.Throws<ArgumentException>(() =>
{
collection[0] = new ImageFrame<Rgba32>(1, 1);
});
Assert.Equal("Frame must have the same dimensions as the image.\r\nParameter name: frame", ex.Message);
}
[Fact]
public void SetFrameAtIndex_FramesNotBeNull()
{
var collection = new ImageFrameCollection<Rgba32>(10, 10);
ArgumentNullException ex = Assert.Throws<ArgumentNullException>(() =>
{
collection[0] = null;
});
Assert.Equal("Value cannot be null.\r\nParameter name: frame", ex.Message);
}
[Fact]
public void Constructor_FramesMustHaveSameSize()
{
ArgumentException ex = Assert.Throws<ArgumentException>(() =>
{
var collection = new ImageFrameCollection<Rgba32>(new[] {
new ImageFrame<Rgba32>(10,10),
new ImageFrame<Rgba32>(1,1),
});
});
Assert.Equal("Frame must have the same dimensions as the image.\r\nParameter name: frame", ex.Message);
}
[Fact]
public void RemoveAtFrame_ThrowIfRemovingLastFrame()
{
var collection = new ImageFrameCollection<Rgba32>(new[] {
new ImageFrame<Rgba32>(10,10)
});
InvalidOperationException ex = Assert.Throws<InvalidOperationException>(() =>
{
collection.RemoveAt(0);
});
Assert.Equal("Cannot remove last frame.", ex.Message);
}
[Fact]
public void RemoveAtFrame_CanRemoveFrameZeroIfMultipleFramesExist()
{
var collection = new ImageFrameCollection<Rgba32>(new[] {
new ImageFrame<Rgba32>(10,10),
new ImageFrame<Rgba32>(10,10),
});
collection.RemoveAt(0);
Assert.Equal(1, collection.Count);
}
[Fact]
public void RemoveFrame_ThrowIfRemovingLastFrame()
{
var collection = new ImageFrameCollection<Rgba32>(new[] {
new ImageFrame<Rgba32>(10,10)
});
InvalidOperationException ex = Assert.Throws<InvalidOperationException>(() =>
{
collection.Remove(collection[0]);
});
Assert.Equal("Cannot remove last frame.", ex.Message);
}
[Fact]
public void RemoveFrame_CanRemoveFrameZeroIfMultipleFramesExist()
{
var collection = new ImageFrameCollection<Rgba32>(new[] {
new ImageFrame<Rgba32>(10,10),
new ImageFrame<Rgba32>(10,10),
});
collection.Remove(collection[0]);
Assert.Equal(1, collection.Count);
}
[Fact]
public void RootFrameIsFrameAtIndexZero()
{
var collection = new ImageFrameCollection<Rgba32>(new[] {
new ImageFrame<Rgba32>(10,10),
new ImageFrame<Rgba32>(10,10),
});
Assert.Equal(collection.RootFrame, collection[0]);
}
[Fact]
public void ConstructorPopulatesFrames()
{
var collection = new ImageFrameCollection<Rgba32>(new[] {
new ImageFrame<Rgba32>(10,10),
new ImageFrame<Rgba32>(10,10),
});
Assert.Equal(2, collection.Count);
}
[Fact]
public void DisposeClearsCollection()
{
var collection = new ImageFrameCollection<Rgba32>(new[] {
new ImageFrame<Rgba32>(10,10),
new ImageFrame<Rgba32>(10,10),
});
collection.Dispose();
Assert.Equal(0, collection.Count);
}
[Fact]
public void Dispose_DisposesAllInnerFrames()
{
var collection = new ImageFrameCollection<Rgba32>(new[] {
new ImageFrame<Rgba32>(10,10),
new ImageFrame<Rgba32>(10,10),
});
IPixelSource<Rgba32>[] framesSnapShot = collection.OfType<IPixelSource<Rgba32>>().ToArray();
collection.Dispose();
Assert.All(framesSnapShot, f =>
{
// the pixel source of the frame is null after its been disposed.
Assert.Null(f.PixelBuffer);
});
}
}
}
Loading…
Cancel
Save