Browse Source

refactor ImageFrameCollection for updated API

af/merge-core
Scott Williams 9 years ago
parent
commit
67cda9b4c8
  1. 6
      ImageSharp.v3.ncrunchsolution
  2. 9
      src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs
  3. 20
      src/ImageSharp/Advanced/AdvancedImageExtensions.cs
  4. 4
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  5. 80
      src/ImageSharp/Image/IImageFrameCollection.cs
  6. 2
      src/ImageSharp/Image/ImageFrame.LoadPixelData.cs
  7. 151
      src/ImageSharp/Image/ImageFrameCollection.cs
  8. 21
      src/ImageSharp/Image/ImageFrame{TPixel}.cs
  9. 44
      src/ImageSharp/Image/Image{TPixel}.cs
  10. 2
      src/ImageSharp/ImageSharp.csproj
  11. 5
      src/ImageSharp/ImageSharp.netstandard1.1.v3.ncrunchproject
  12. 2
      src/ImageSharp/Processing/Transforms/Resize.cs
  13. 2
      src/ImageSharp/Quantizers/Quantize.cs
  14. 224
      tests/ImageSharp.Tests/Image/ImageFramesCollectionTests.cs
  15. 59
      tests/ImageSharp.Tests/Image/ImageTests.cs
  16. 9
      tests/ImageSharp.Tests/ImageSharp.Tests.v3.ncrunchproject
  17. 2
      tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparer.cs
  18. 2
      tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs
  19. 20
      tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs

6
ImageSharp.v3.ncrunchsolution

@ -0,0 +1,6 @@
<SolutionConfiguration>
<Settings>
<AllowParallelTestExecution>True</AllowParallelTestExecution>
<SolutionConfigured>True</SolutionConfigured>
</Settings>
</SolutionConfiguration>

9
src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs

@ -32,6 +32,15 @@ namespace SixLabors.ImageSharp.Drawing.Brushes
this.image = image;
}
/// <summary>
/// Initializes a new instance of the <see cref="ImageBrush{TPixel}"/> class.
/// </summary>
/// <param name="image">The image.</param>
public ImageBrush(Image<TPixel> image)
: this(image.Frames.RootFrame)
{
}
/// <inheritdoc />
public BrushApplicator<TPixel> CreateApplicator(ImageFrame<TPixel> source, RectangleF region, GraphicsOptions options)
{

20
src/ImageSharp/Advanced/AdvancedImageExtensions.cs

@ -12,6 +12,16 @@ namespace SixLabors.ImageSharp.Advanced
/// </summary>
public static class AdvancedImageExtensions
{
/// <summary>
/// Gets the configuration for the image.
/// </summary>
/// <typeparam name="TPixel">The Pixel format.</typeparam>
/// <param name="source">The source image</param>
/// <returns>Returns the configuration.</returns>
public static Configuration GetConfiguration<TPixel>(this Image<TPixel> source)
where TPixel : struct, IPixel<TPixel>
=> GetConfiguration((IConfigurable)source);
/// <summary>
/// Returns a reference to the 0th element of the Pixel buffer,
/// allowing direct manipulation of pixel data through unsafe operations.
@ -78,16 +88,6 @@ namespace SixLabors.ImageSharp.Advanced
where TPixel : struct, IPixel<TPixel>
=> source.Frames.RootFrame.GetPixelRowSpan(row);
/// <summary>
/// Gets the configuration for the image.
/// </summary>
/// <typeparam name="TPixel">The Pixel format.</typeparam>
/// <param name="source">The source image</param>
/// <returns>Returns the configuration.</returns>
internal static Configuration GetConfiguration<TPixel>(this Image<TPixel> source)
where TPixel : struct, IPixel<TPixel>
=> GetConfiguration((IConfigurable)source);
/// <summary>
/// Gets the span to the backing buffer.
/// </summary>

4
src/ImageSharp/Formats/Gif/GifDecoderCore.cs

@ -388,15 +388,13 @@ namespace SixLabors.ImageSharp.Formats.Gif
previousFrame = this.previousFrame;
}
currentFrame = this.previousFrame.Clone();
currentFrame = this.image.Frames.AddFrame(this.previousFrame); // this clones the frame and adds it the collection
this.SetFrameMetaData(currentFrame.MetaData);
image = currentFrame;
this.RestoreToBackground(image);
this.image.Frames.Add(currentFrame);
}
int i = 0;

80
src/ImageSharp/Image/IImageFrameCollection.cs

@ -27,42 +27,82 @@ namespace SixLabors.ImageSharp
ImageFrame<TPixel> RootFrame { get; }
/// <summary>
/// Gets or sets the <see cref="ImageFrame{TPixel}"/> at the specified index.
/// Gets the <see cref="ImageFrame{TPixel}"/> at the specified index.
/// </summary>
/// <value>
/// The <see cref="ImageFrame{TPixel}"/>.
/// </value>
/// <param name="index">The index.</param>
/// <returns>The <see cref="ImageFrame{TPixel}"/> at the specified index.</returns>
ImageFrame<TPixel> this[int index] { get; set; }
ImageFrame<TPixel> this[int index] { get; }
/// <summary>
/// Determines the index of a specific <paramref name="frame"/> in the <seealso cref="Image{TPixel}"/>.
/// Clones the the frame at <paramref name="index"/> and generates a new images with all the same metadata from the orgional but with only the single frame on it.
/// </summary>
/// <param name="frame">The <seealso cref="ImageFrame{TPixel}"/> to locate in the <seealso cref="Image{TPixel}"/>.</param>
/// <returns>The index of item if found in the list; otherwise, -1.</returns>
int IndexOf(ImageFrame<TPixel> frame);
/// <param name="index"> The zero-based index at which item should be removed.</param>
/// <exception cref="InvalidOperationException">Cannot remove last frame.</exception>
/// <returns>The new <see cref="Image{TPixel}"/> with only the one frame on it.</returns>
Image<TPixel> CloneFrame(int index);
/// <summary>
/// Inserts the <paramref name="frame"/> to the <seealso cref="Image{TPixel}"/> at the specified <paramref name="index"/>.
/// Removed the frame at <paramref name="index"/> and generates a new images with all the same metadata from the orgional but with only the single frame on it.
/// </summary>
/// <param name="index"> The zero-based index at which item should be inserted..</param>
/// <param name="frame">The <seealso cref="ImageFrame{TPixel}"/> to insert into the <seealso cref="Image{TPixel}"/>.</param>
void Insert(int index, ImageFrame<TPixel> frame);
/// <param name="index"> The zero-based index at which item should be removed.</param>
/// <exception cref="InvalidOperationException">Cannot remove last frame.</exception>
/// <returns>The new <see cref="Image{TPixel}"/> with only the one frame on it.</returns>
Image<TPixel> ExportFrame(int index);
/// <summary>
/// Removes the <seealso cref="ImageFrame{TPixel}"/> from the <seealso cref="Image{TPixel}"/> at the specified index.
/// Remove the frame at <paramref name="index"/> and frees all freeable resources associated with it.
/// </summary>
/// <param name="index">The zero-based index of the item to remove.</param>
/// <param name="index"> The zero-based index at which item should be removed.</param>
/// <exception cref="InvalidOperationException">Cannot remove last frame.</exception>
void RemoveAt(int index);
void RemoveFrame(int index);
/// <summary>
/// Adds the specified frame.
/// Creates a new <seealso cref="ImageFrame{TPixel}"/> and appends it appends it to the end of the collection.
/// </summary>
/// <param name="frame">The frame.</param>
/// <returns>The new <see cref="ImageFrame{TPixel}"/>.</returns>
ImageFrame<TPixel> CreateFrame();
/// <summary>
/// Clones the <paramref name="source"/> frame and appends the clone to the end of the collection.
/// </summary>
/// <param name="source">The raw pixel data to generate <seealso cref="ImageFrame{TPixel}"/> from.</param>
/// <returns>The cloned <see cref="ImageFrame{TPixel}"/>.</returns>
ImageFrame<TPixel> AddFrame(ImageFrame<TPixel> source);
/// <summary>
/// Creates a new frame from the pixel data at the same dimensions at the current image and inserts the new frame
/// into the <seealso cref="Image{TPixel}"/> at the end of the collection.
/// </summary>
/// <param name="source">The raw pixel data to generate <seealso cref="ImageFrame{TPixel}"/> from.</param>
/// <returns>The new <see cref="ImageFrame{TPixel}"/>.</returns>
ImageFrame<TPixel> AddFrame(TPixel[] source);
/// <summary>
/// Clones and inserts the <paramref name="source"/> into the <seealso cref="Image{TPixel}"/> at the specified <paramref name="index"/>.
/// </summary>
/// <param name="index"> The zero-based index at which item should be inserted.</param>
/// <param name="source">The <seealso cref="ImageFrame{TPixel}"/> to clone and insert into the <seealso cref="Image{TPixel}"/>.</param>
/// <exception cref="ArgumentException">Frame must have the same dimensions as the image - frame</exception>
void Add(ImageFrame<TPixel> frame);
/// <returns>The cloned <see cref="ImageFrame{TPixel}"/>.</returns>
ImageFrame<TPixel> InsertFrame(int index, ImageFrame<TPixel> source);
/// <summary>
/// Moves a <seealso cref="ImageFrame{TPixel}"/> from the <seealso cref="Image{TPixel}"/> at the specified index to the other index.
/// </summary>
/// <param name="sourceIndex">The zero-based index of the item to move.</param>
/// <param name="destinationIndex">The zero-based index of the new index that should be inserted at.</param>
/// <exception cref="InvalidOperationException">Cannot remove last frame.</exception>
void MoveFrame(int sourceIndex, int destinationIndex);
/// <summary>
/// Determines the index of a specific <paramref name="frame"/> in the <seealso cref="Image{TPixel}"/>.
/// </summary>
/// <param name="frame">The <seealso cref="ImageFrame{TPixel}"/> to locate in the <seealso cref="Image{TPixel}"/>.</param>
/// <returns>The index of item if found in the list; otherwise, -1.</returns>
int IndexOf(ImageFrame<TPixel> frame);
/// <summary>
/// Determines whether the <seealso cref="Image{TPixel}"/> contains the <paramref name="frame"/>.
@ -72,13 +112,5 @@ namespace SixLabors.ImageSharp
/// <c>true</c> if the <seealso cref="Image{TPixel}"/> the specified frame; otherwise, <c>false</c>.
/// </returns>
bool Contains(ImageFrame<TPixel> frame);
/// <summary>
/// Removes the specified frame.
/// </summary>
/// <param name="frame">The frame.</param>
/// <returns>true if item is found in the <seealso cref="Image{TPixel}"/>; otherwise,</returns>
/// <exception cref="InvalidOperationException">Cannot remove last frame</exception>
bool Remove(ImageFrame<TPixel> frame);
}
}

2
src/ImageSharp/Image/ImageFrame.LoadPixelData.cs

@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp
/// <content>
/// Adds static methods allowing the creation of new image from raw pixel data.
/// </content>
public static partial class ImageFrame
internal static partial class ImageFrame
{
/// <summary>
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given byte array in <typeparamref name="TPixel"/> format.

151
src/ImageSharp/Image/ImageFrameCollection.cs

@ -4,7 +4,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp
@ -13,124 +13,134 @@ namespace SixLabors.ImageSharp
/// Encapsulates an imaged collection of frames.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
internal sealed class ImageFrameCollection<TPixel> : IImageFrameCollection<TPixel>, IDisposable
internal sealed class ImageFrameCollection<TPixel> : IImageFrameCollection<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private readonly IList<ImageFrame<TPixel>> frames = new List<ImageFrame<TPixel>>();
private readonly Image<TPixel> parent;
internal ImageFrameCollection(int width, int height)
internal ImageFrameCollection(Image<TPixel> parent, int width, int height)
{
this.Add(new ImageFrame<TPixel>(width, height));
Guard.NotNull(parent, nameof(parent));
this.parent = parent;
this.AddFrame(new ImageFrame<TPixel>(width, height));
}
internal ImageFrameCollection(IEnumerable<ImageFrame<TPixel>> frames)
internal ImageFrameCollection(Image<TPixel> parent, IEnumerable<ImageFrame<TPixel>> frames)
{
Guard.NotNull(parent, nameof(parent));
Guard.NotNullOrEmpty(frames, nameof(frames));
this.parent = parent;
foreach (ImageFrame<TPixel> f in frames)
{
this.Add(f);
this.AddFrame(f);
}
}
/// <summary>
/// Gets the count.
/// </summary>
/// <inheritdoc/>
public int Count => this.frames.Count;
/// <summary>
/// Gets the root frame.
/// </summary>
/// <inheritdoc/>
public ImageFrame<TPixel> RootFrame => this.frames.Count > 0 ? this.frames[0] : null;
/// <summary>
/// Gets or sets the <see cref="ImageFrame{TPixel}"/> at the specified index.
/// </summary>
/// <value>
/// The <see cref="ImageFrame{TPixel}"/>.
/// </value>
/// <param name="index">The index.</param>
/// <returns>The <see cref="ImageFrame{TPixel}"/> at the specified index.</returns>
/// <inheritdoc/>
public ImageFrame<TPixel> this[int index]
{
get => this.frames[index];
set
{
this.ValidateFrame(value);
this.frames[index] = value;
}
}
/// <summary>
/// Determines the index of a specific <paramref name="frame"/> in the <seealso cref="Image{TPixel}"/>.
/// </summary>
/// <param name="frame">The <seealso cref="ImageFrame{TPixel}"/> to locate in the <seealso cref="Image{TPixel}"/>.</param>
/// <returns>The index of item if found in the list; otherwise, -1.</returns>
/// <inheritdoc/>
public int IndexOf(ImageFrame<TPixel> frame) => this.frames.IndexOf(frame);
/// <summary>
/// Inserts the <paramref name="frame"/> to the <seealso cref="Image{TPixel}"/> at the specified <paramref name="index"/>.
/// </summary>
/// <param name="index"> The zero-based index at which item should be inserted..</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)
/// <inheritdoc/>
public ImageFrame<TPixel> InsertFrame(int index, ImageFrame<TPixel> frame)
{
this.ValidateFrame(frame);
this.frames.Insert(index, frame);
ImageFrame<TPixel> clonedFrame = frame.Clone();
this.frames.Insert(index, clonedFrame);
return clonedFrame;
}
/// <summary>
/// Removes the <seealso cref="ImageFrame{TPixel}"/> from the <seealso cref="Image{TPixel}"/> at the specified index.
/// </summary>
/// <param name="index">The zero-based index of the item to remove.</param>
/// <exception cref="InvalidOperationException">Cannot remove last frame.</exception>
public void RemoveAt(int index)
/// <inheritdoc/>
public ImageFrame<TPixel> AddFrame(ImageFrame<TPixel> frame)
{
this.ValidateFrame(frame);
ImageFrame<TPixel> clonedFrame = frame.Clone();
this.frames.Add(clonedFrame);
return clonedFrame;
}
/// <inheritdoc/>
public ImageFrame<TPixel> AddFrame(TPixel[] data)
{
var frame = ImageFrame.LoadPixelData(new Span<TPixel>(data), this.RootFrame.Width, this.RootFrame.Height);
this.frames.Add(frame);
return frame;
}
/// <inheritdoc/>
public void RemoveFrame(int index)
{
if (index == 0 && this.Count == 1)
{
throw new InvalidOperationException("Cannot remove last frame.");
}
ImageFrame<TPixel> frame = this.frames[index];
this.frames.RemoveAt(index);
frame.Dispose();
}
/// <summary>
/// Adds the specified frame.
/// </summary>
/// <param name="frame">The frame.</param>
/// <exception cref="ArgumentException">Frame must have the same dimensions as the image - frame</exception>
public void Add(ImageFrame<TPixel> frame)
/// <inheritdoc/>
public bool Contains(ImageFrame<TPixel> frame)
{
this.ValidateFrame(frame);
this.frames.Add(frame);
return this.frames.Contains(frame);
}
/// <summary>
/// Determines whether the <seealso cref="Image{TPixel}"/> contains the <paramref name="frame"/>.
/// </summary>
/// <param name="frame">The frame.</param>
/// <returns>
/// <c>true</c> if the <seealso cref="Image{TPixel}"/> the specified frame; otherwise, <c>false</c>.
/// </returns>
public bool Contains(ImageFrame<TPixel> frame)
/// <inheritdoc/>
public void MoveFrame(int sourceIndex, int destIndex)
{
return this.frames.Contains(frame);
if (sourceIndex == destIndex)
{
return;
}
ImageFrame<TPixel> frameAtIndex = this.frames[sourceIndex];
this.frames.RemoveAt(sourceIndex);
this.frames.Insert(destIndex, frameAtIndex);
}
/// <summary>
/// Removes the specified frame.
/// </summary>
/// <param name="frame">The frame.</param>
/// <returns>true if item is found in the <seealso cref="Image{TPixel}"/>; otherwise,</returns>
/// <exception cref="InvalidOperationException">Cannot remove last frame</exception>
public bool Remove(ImageFrame<TPixel> frame)
/// <inheritdoc/>
public Image<TPixel> ExportFrame(int index)
{
ImageFrame<TPixel> frame = this[index];
if (this.Count == 1 && this.frames.Contains(frame))
{
throw new InvalidOperationException("Cannot remove last frame.");
}
return this.frames.Remove(frame);
this.frames.Remove(frame);
return new Image<TPixel>(this.parent.GetConfiguration(), this.parent.MetaData.Clone(), new[] { frame });
}
/// <inheritdoc/>
public Image<TPixel> CloneFrame(int index)
{
ImageFrame<TPixel> frame = this[index];
ImageFrame<TPixel> clonedFrame = frame.Clone();
return new Image<TPixel>(this.parent.GetConfiguration(), this.parent.MetaData.Clone(), new[] { clonedFrame });
}
/// <inheritdoc/>
public ImageFrame<TPixel> CreateFrame()
{
var frame = new ImageFrame<TPixel>(this.RootFrame.Width, this.RootFrame.Height);
this.frames.Add(frame);
return frame;
}
/// <inheritdoc/>
@ -152,8 +162,7 @@ namespace SixLabors.ImageSharp
}
}
/// <inheritdoc/>
public void Dispose()
internal void Dispose()
{
foreach (ImageFrame<TPixel> f in this.frames)
{

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

@ -103,15 +103,6 @@ namespace SixLabors.ImageSharp
}
}
/// <summary>
/// Performs an explicit conversion from <see cref="Image{TPixel}"/> to <see cref="ImageFrame{TPixel}"/>.
/// </summary>
/// <param name="image">The image.</param>
/// <returns>
/// The result of the conversion.
/// </returns>
public static implicit operator ImageFrame<TPixel>(Image<TPixel> image) => image.Frames[0];
/// <summary>
/// Gets a reference to the pixel at the specified position.
/// </summary>
@ -172,7 +163,7 @@ namespace SixLabors.ImageSharp
/// <summary>
/// Disposes the object and frees resources for the Garbage Collector.
/// </summary>
public void Dispose()
internal void Dispose()
{
if (this.isDisposed)
{
@ -197,7 +188,7 @@ namespace SixLabors.ImageSharp
/// </summary>
/// <typeparam name="TPixel2">The pixel format.</typeparam>
/// <returns>The <see cref="ImageFrame{TPixel2}"/></returns>
public ImageFrame<TPixel2> CloneAs<TPixel2>()
internal ImageFrame<TPixel2> CloneAs<TPixel2>()
where TPixel2 : struct, IPixel<TPixel2>
{
if (typeof(TPixel2) == typeof(TPixel))
@ -234,9 +225,15 @@ namespace SixLabors.ImageSharp
/// Clones the current instance.
/// </summary>
/// <returns>The <see cref="ImageFrame{TPixel}"/></returns>
public ImageFrame<TPixel> Clone()
internal ImageFrame<TPixel> Clone()
{
return new ImageFrame<TPixel>(this);
}
/// <inheritdoc/>
void IDisposable.Dispose()
{
this.Dispose();
}
}
}

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

@ -62,7 +62,7 @@ namespace SixLabors.ImageSharp
{
this.configuration = configuration ?? Configuration.Default;
this.MetaData = metadata ?? new ImageMetaData();
this.frames = new ImageFrameCollection<TPixel>(width, height);
this.frames = new ImageFrameCollection<TPixel>(this, width, height);
}
/// <summary>
@ -77,7 +77,7 @@ namespace SixLabors.ImageSharp
this.configuration = configuration ?? Configuration.Default;
this.MetaData = metadata ?? new ImageMetaData();
this.frames = new ImageFrameCollection<TPixel>(frames);
this.frames = new ImageFrameCollection<TPixel>(this, frames);
}
/// <summary>
@ -148,30 +148,6 @@ namespace SixLabors.ImageSharp
return new Image<TPixel>(this.configuration, this.MetaData.Clone(), frames);
}
/// <summary>
/// Clones the current image
/// </summary>
/// <param name="frameIndex">The index of the frame to clone into the new image</param>
/// <returns>Returns a new image with all the same metadata as the original but only the single frame.</returns>
public Image<TPixel> Clone(int frameIndex)
{
ImageFrame<TPixel> frame = this.frames[frameIndex];
ImageFrame<TPixel> clonedFrame = frame.Clone();
return new Image<TPixel>(this.configuration, this.MetaData.Clone(), new[] { clonedFrame });
}
/// <summary>
/// Extracts a frame from the current image
/// </summary>
/// <param name="frameIndex">The index of the frame to cloen into the new image</param>
/// <returns>Returns a new image with all the same metadata as the original but only the single frame.</returns>
public Image<TPixel> Extract(int frameIndex)
{
ImageFrame<TPixel> frame = this.frames[frameIndex];
this.frames.Remove(frame); // try and remove frame from the current image
return new Image<TPixel>(this.configuration, this.MetaData.Clone(), new[] { frame });
}
/// <inheritdoc/>
public override string ToString()
{
@ -192,22 +168,6 @@ namespace SixLabors.ImageSharp
return target;
}
/// <summary>
/// Returns a copy of the image in the given pixel format.
/// </summary>
/// <typeparam name="TPixel2">The pixel format.</typeparam>
/// <param name="frameIndex">The index of the frame to clone into the new image</param>
/// <returns>Returns a new <see cref="Image{TPixel2}"/> with all the same metadata as the original but only the single frame.</returns>
public Image<TPixel2> CloneAs<TPixel2>(int frameIndex)
where TPixel2 : struct, IPixel<TPixel2>
{
ImageFrame<TPixel> frame = this.frames[frameIndex];
ImageFrame<TPixel2> clonedFrame = frame.CloneAs<TPixel2>();
var target = new Image<TPixel2>(this.configuration, this.MetaData, new[] { clonedFrame });
return target;
}
/// <summary>
/// Releases managed resources.
/// </summary>

2
src/ImageSharp/ImageSharp.csproj

@ -5,7 +5,7 @@
<VersionPrefix Condition="$(packageversion) != ''">$(packageversion)</VersionPrefix>
<VersionPrefix Condition="$(packageversion) == ''">0.0.1</VersionPrefix>
<Authors>Six Labors and contributors</Authors>
<TargetFrameworks>netstandard1.3;netstandard1.1</TargetFrameworks>
<TargetFrameworks>netstandard1.1;netstandard1.3</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<AssemblyName>SixLabors.ImageSharp</AssemblyName>

5
src/ImageSharp/ImageSharp.netstandard1.1.v3.ncrunchproject

@ -0,0 +1,5 @@
<ProjectConfiguration>
<Settings>
<IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely>
</Settings>
</ProjectConfiguration>

2
src/ImageSharp/Processing/Transforms/Resize.cs

@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp
options.Size = new Size(options.Size.Width, (int)MathF.Round(img.Height * options.Size.Width / (float)img.Width));
}
Rectangle targetRectangle = ResizeHelper.CalculateTargetLocationAndBounds<TPixel>(img, options);
Rectangle targetRectangle = ResizeHelper.CalculateTargetLocationAndBounds(img.Frames.RootFrame, options);
img.Mutate(x => Resize(x, options.Size.Width, options.Size.Height, options.Sampler, targetRectangle, options.Compand));
});

2
src/ImageSharp/Quantizers/Quantize.cs

@ -58,7 +58,7 @@ namespace SixLabors.ImageSharp
return source.Apply(img =>
{
// TODO : move helper logic into the processor
QuantizedImage<TPixel> quantized = quantizer.Quantize(img, maxColors);
QuantizedImage<TPixel> quantized = quantizer.Quantize(img.Frames.RootFrame, maxColors);
int palleteCount = quantized.Palette.Length - 1;
using (var pixels = new PixelAccessor<TPixel>(quantized.Width, quantized.Height))

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

@ -3,92 +3,94 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using Xunit;
namespace SixLabors.ImageSharp.Tests
{
public class ImageFramesCollectionTests
public class ImageFramesCollectionTests : IDisposable
{
private Image<Rgba32> image;
private ImageFrameCollection<Rgba32> collection;
public ImageFramesCollectionTests()
{
this.image = new Image<Rgba32>(10, 10);
this.collection = new ImageFrameCollection<Rgba32>(this.image, 10, 10);
}
[Fact]
public void ImageFramesaLwaysHaveOneFrame()
{
var collection = new ImageFrameCollection<Rgba32>(10, 10);
Assert.Equal(1, collection.Count);
Assert.Equal(1, this.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));
this.collection.AddFrame(new ImageFrame<Rgba32>(1, 1));
});
Assert.StartsWith("Frame must have the same dimensions as the image.", ex.Message);
}
[Fact]
public void AddNewFrame_FramesNotBeNull()
public void AddNewFrame_Frame_FramesNotBeNull()
{
var collection = new ImageFrameCollection<Rgba32>(10, 10);
ArgumentNullException ex = Assert.Throws<ArgumentNullException>(() =>
{
collection.Add(null);
this.collection.AddFrame((ImageFrame<Rgba32>)null);
});
Assert.StartsWith("Value cannot be null.", ex.Message);
}
[Fact]
public void InsertNewFrame_FramesMustHaveSameSize()
public void AddNewFrame_PixelBuffer_FramesNotBeNull()
{
var collection = new ImageFrameCollection<Rgba32>(10, 10);
ArgumentException ex = Assert.Throws<ArgumentException>(() =>
ArgumentNullException ex = Assert.Throws<ArgumentNullException>(() =>
{
collection.Insert(1, new ImageFrame<Rgba32>(1, 1));
this.collection.AddFrame((Rgba32[])null);
});
Assert.StartsWith("Frame must have the same dimensions as the image.", ex.Message);
Assert.StartsWith("Value cannot be null.", ex.Message);
}
[Fact]
public void InsertNewFrame_FramesNotBeNull()
public void AddNewFrame_PixelBuffer_BufferIncorrectSize()
{
var collection = new ImageFrameCollection<Rgba32>(10, 10);
ArgumentNullException ex = Assert.Throws<ArgumentNullException>(() =>
ArgumentOutOfRangeException ex = Assert.Throws<ArgumentOutOfRangeException>(() =>
{
collection.Insert(1, null);
this.collection.AddFrame(new Rgba32[0]);
});
Assert.StartsWith("Value cannot be null.", ex.Message);
Assert.StartsWith("Value must be greater than or equal to 100.", ex.Message);
}
[Fact]
public void SetFrameAtIndex_FramesMustHaveSameSize()
public void InsertNewFrame_FramesMustHaveSameSize()
{
var collection = new ImageFrameCollection<Rgba32>(10, 10);
ArgumentException ex = Assert.Throws<ArgumentException>(() =>
{
collection[0] = new ImageFrame<Rgba32>(1, 1);
this.collection.InsertFrame(1, new ImageFrame<Rgba32>(1, 1));
});
Assert.StartsWith("Frame must have the same dimensions as the image.", ex.Message);
}
[Fact]
public void SetFrameAtIndex_FramesNotBeNull()
public void InsertNewFrame_FramesNotBeNull()
{
var collection = new ImageFrameCollection<Rgba32>(10, 10);
ArgumentNullException ex = Assert.Throws<ArgumentNullException>(() =>
{
collection[0] = null;
this.collection.InsertFrame(1, null);
});
Assert.StartsWith("Value cannot be null.", ex.Message);
@ -100,7 +102,7 @@ namespace SixLabors.ImageSharp.Tests
ArgumentException ex = Assert.Throws<ArgumentException>(() =>
{
var collection = new ImageFrameCollection<Rgba32>(new[] {
var collection = new ImageFrameCollection<Rgba32>(this.image, new[] {
new ImageFrame<Rgba32>(10,10),
new ImageFrame<Rgba32>(1,1),
});
@ -112,13 +114,13 @@ namespace SixLabors.ImageSharp.Tests
[Fact]
public void RemoveAtFrame_ThrowIfRemovingLastFrame()
{
var collection = new ImageFrameCollection<Rgba32>(new[] {
var collection = new ImageFrameCollection<Rgba32>(this.image, new[] {
new ImageFrame<Rgba32>(10,10)
});
InvalidOperationException ex = Assert.Throws<InvalidOperationException>(() =>
{
collection.RemoveAt(0);
collection.RemoveFrame(0);
});
Assert.Equal("Cannot remove last frame.", ex.Message);
}
@ -127,46 +129,19 @@ namespace SixLabors.ImageSharp.Tests
public void RemoveAtFrame_CanRemoveFrameZeroIfMultipleFramesExist()
{
var collection = new ImageFrameCollection<Rgba32>(new[] {
var collection = new ImageFrameCollection<Rgba32>(this.image, 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]);
collection.RemoveFrame(0);
Assert.Equal(1, collection.Count);
}
[Fact]
public void RootFrameIsFrameAtIndexZero()
{
var collection = new ImageFrameCollection<Rgba32>(new[] {
var collection = new ImageFrameCollection<Rgba32>(this.image, new[] {
new ImageFrame<Rgba32>(10,10),
new ImageFrame<Rgba32>(10,10),
});
@ -177,7 +152,7 @@ namespace SixLabors.ImageSharp.Tests
[Fact]
public void ConstructorPopulatesFrames()
{
var collection = new ImageFrameCollection<Rgba32>(new[] {
var collection = new ImageFrameCollection<Rgba32>(this.image, new[] {
new ImageFrame<Rgba32>(10,10),
new ImageFrame<Rgba32>(10,10),
});
@ -188,7 +163,7 @@ namespace SixLabors.ImageSharp.Tests
[Fact]
public void DisposeClearsCollection()
{
var collection = new ImageFrameCollection<Rgba32>(new[] {
var collection = new ImageFrameCollection<Rgba32>(this.image, new[] {
new ImageFrame<Rgba32>(10,10),
new ImageFrame<Rgba32>(10,10),
});
@ -201,7 +176,7 @@ namespace SixLabors.ImageSharp.Tests
[Fact]
public void Dispose_DisposesAllInnerFrames()
{
var collection = new ImageFrameCollection<Rgba32>(new[] {
var collection = new ImageFrameCollection<Rgba32>(this.image, new[] {
new ImageFrame<Rgba32>(10,10),
new ImageFrame<Rgba32>(10,10),
});
@ -215,5 +190,132 @@ namespace SixLabors.ImageSharp.Tests
Assert.Null(f.PixelBuffer);
});
}
[Theory]
[WithTestPatternImages(10, 10, PixelTypes.Rgba32)]
public void CloneFrame<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> img = provider.GetImage())
{
img.Frames.AddFrame(new ImageFrame<TPixel>(10, 10));// add a frame anyway
using (Image<TPixel> cloned = img.Frames.CloneFrame(0))
{
Assert.Equal(2, img.Frames.Count);
cloned.ComparePixelBufferTo(img.GetPixelSpan());
}
}
}
[Theory]
[WithTestPatternImages(10, 10, PixelTypes.Rgba32)]
public void ExtractFrame<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> img = provider.GetImage())
{
var sourcePixelData = img.GetPixelSpan().ToArray();
img.Frames.AddFrame(new ImageFrame<TPixel>(10, 10));
using (Image<TPixel> cloned = img.Frames.ExportFrame(0))
{
Assert.Equal(1, img.Frames.Count);
cloned.ComparePixelBufferTo(sourcePixelData);
}
}
}
[Fact]
public void CreateFrame()
{
this.image.Frames.CreateFrame();
Assert.Equal(2, this.image.Frames.Count);
}
[Fact]
public void AddFrameFromPixelData()
{
var pixelData = this.image.Frames.RootFrame.GetPixelSpan().ToArray();
this.image.Frames.AddFrame(pixelData);
Assert.Equal(2, this.image.Frames.Count);
}
[Fact]
public void AddFrame_clones_sourceFrame()
{
var pixelData = this.image.Frames.RootFrame.GetPixelSpan().ToArray();
var otherFRame = new ImageFrame<Rgba32>(10, 10);
var addedFrame = this.image.Frames.AddFrame(otherFRame);
addedFrame.ComparePixelBufferTo(otherFRame.GetPixelSpan());
Assert.NotEqual(otherFRame, addedFrame);
}
[Fact]
public void InsertFrame_clones_sourceFrame()
{
var pixelData = this.image.Frames.RootFrame.GetPixelSpan().ToArray();
var otherFRame = new ImageFrame<Rgba32>(10, 10);
var addedFrame = this.image.Frames.InsertFrame(0, otherFRame);
addedFrame.ComparePixelBufferTo(otherFRame.GetPixelSpan());
Assert.NotEqual(otherFRame, addedFrame);
}
[Fact]
public void MoveFrame_LeavesFrmaeInCorrectLocation()
{
for (var i = 0; i < 9; i++)
{
this.image.Frames.CreateFrame();
}
var frame = this.image.Frames[4];
this.image.Frames.MoveFrame(4, 7);
var newIndex = this.image.Frames.IndexOf(frame);
Assert.Equal(7, newIndex);
}
[Fact]
public void IndexOf_ReturnsCorrectIndex()
{
for (var i = 0; i < 9; i++)
{
this.image.Frames.CreateFrame();
}
var frame = this.image.Frames[4];
var index = this.image.Frames.IndexOf(frame);
Assert.Equal(4, index);
}
[Fact]
public void Contains_TrueIfMember()
{
for (var i = 0; i < 9; i++)
{
this.image.Frames.CreateFrame();
}
var frame = this.image.Frames[4];
Assert.True(this.image.Frames.Contains(frame));
}
[Fact]
public void Contains_TFalseIfNoneMember()
{
for (var i = 0; i < 9; i++)
{
this.image.Frames.CreateFrame();
}
var frame = new ImageFrame<Rgba32>(10, 10);
Assert.False(this.image.Frames.Contains(frame));
}
public void Dispose()
{
this.image.Dispose();
this.collection.Dispose();
}
}
}

59
tests/ImageSharp.Tests/Image/ImageTests.cs

@ -110,64 +110,5 @@ namespace SixLabors.ImageSharp.Tests
Assert.Equal("image/png", mime.DefaultMimeType);
}
}
[Theory]
[WithTestPatternImages(10, 10, PixelTypes.Rgba32)]
public void CloneFrame<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> img = provider.GetImage())
{
img.Frames.Add(new ImageFrame<TPixel>(10, 10));// add a frame anyway
using (Image<TPixel> cloned = img.Clone(0))
{
Assert.Equal(2, img.Frames.Count);
cloned.ComparePixelBufferTo(img.GetPixelSpan());
}
}
}
[Theory]
[WithTestPatternImages(10, 10, PixelTypes.Rgba32)]
public void CloneFrameAs<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> img = provider.GetImage())
{
img.Frames.Add(new ImageFrame<TPixel>(10, 10));// add a frame anyway
using (Image<Bgra32> cloned = img.CloneAs<Bgra32>(0))
{
for (var x = 0; x < img.Width; x++)
{
for (var y = 0; y < img.Height; y++)
{
Bgra32 pixelClone = cloned[x, y];
Bgra32 pixelSource = default(Bgra32);
img[x, y].ToBgra32(ref pixelSource);
Assert.Equal(pixelSource, pixelClone);
}
}
Assert.Equal(2, img.Frames.Count);
}
}
}
[Theory]
[WithTestPatternImages(10, 10, PixelTypes.Rgba32)]
public void ExtractFrame<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> img = provider.GetImage())
{
var sourcePixelData = img.GetPixelSpan().ToArray();
img.Frames.Add(new ImageFrame<TPixel>(10, 10));
using (Image<TPixel> cloned = img.Extract(0))
{
Assert.Equal(1, img.Frames.Count);
cloned.ComparePixelBufferTo(sourcePixelData);
}
}
}
}
}

9
tests/ImageSharp.Tests/ImageSharp.Tests.v3.ncrunchproject

@ -0,0 +1,9 @@
<ProjectConfiguration>
<Settings>
<CopyReferencedAssembliesToWorkspace>False</CopyReferencedAssembliesToWorkspace>
<FrameworkUtilisationTypeForGallio>UseStaticAnalysis</FrameworkUtilisationTypeForGallio>
<ImplicitProjectDependencyPathsRelativeToSolutionDirectory />
<NUnit3Enabled>False</NUnit3Enabled>
<PreloadReferencedAssemblies>False</PreloadReferencedAssemblies>
</Settings>
</ProjectConfiguration>

2
tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparer.cs

@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison
Image<TPixelB> actual)
where TPixelA : struct, IPixel<TPixelA> where TPixelB : struct, IPixel<TPixelB>
{
return comparer.CompareImagesOrFrames((ImageFrame<TPixelA>)expected, (ImageFrame<TPixelB>)actual);
return comparer.CompareImagesOrFrames(expected.Frames.RootFrame, actual.Frames.RootFrame);
}
public static IEnumerable<ImageSimilarityReport<TPixelA, TPixelB>> CompareImages<TPixelA, TPixelB>(

2
tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs

@ -206,7 +206,7 @@ namespace SixLabors.ImageSharp.Tests
public static void ModifyPixel<TPixel>(Image<TPixel> img, int x, int y, byte perChannelChange)
where TPixel : struct, IPixel<TPixel>
{
ModifyPixel((ImageFrame<TPixel>)img, x, y, perChannelChange);
ModifyPixel(img.Frames.RootFrame, x, y, perChannelChange);
}
public static void ModifyPixel<TPixel>(ImageFrame<TPixel> img, int x, int y, byte perChannelChange)

20
tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs

@ -166,12 +166,28 @@ namespace SixLabors.ImageSharp.Tests
for (int i = 0; i < expectedPixels.Length; i++)
{
Assert.True(expectedPixels[i].Equals(actual[i]), $"Pixels are different on position {i}!" );
Assert.True(expectedPixels[i].Equals(actual[i]), $"Pixels are different on position {i}!");
}
return image;
}
public static ImageFrame<TPixel> ComparePixelBufferTo<TPixel>(
this ImageFrame<TPixel> image,
Span<TPixel> expectedPixels)
where TPixel : struct, IPixel<TPixel>
{
Span<TPixel> actual = image.GetPixelSpan();
Assert.True(expectedPixels.Length == actual.Length, "Buffer sizes are not equal!");
for (int i = 0; i < expectedPixels.Length; i++)
{
Assert.True(expectedPixels[i].Equals(actual[i]), $"Pixels are different on position {i}!");
}
return image;
}
public static Image<TPixel> CompareToOriginal<TPixel>(
this Image<TPixel> image,

Loading…
Cancel
Save