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; 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 /> /// <inheritdoc />
public BrushApplicator<TPixel> CreateApplicator(ImageFrame<TPixel> source, RectangleF region, GraphicsOptions options) 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> /// </summary>
public static class AdvancedImageExtensions 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> /// <summary>
/// Returns a reference to the 0th element of the Pixel buffer, /// Returns a reference to the 0th element of the Pixel buffer,
/// allowing direct manipulation of pixel data through unsafe operations. /// allowing direct manipulation of pixel data through unsafe operations.
@ -78,16 +88,6 @@ namespace SixLabors.ImageSharp.Advanced
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
=> source.Frames.RootFrame.GetPixelRowSpan(row); => 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> /// <summary>
/// Gets the span to the backing buffer. /// Gets the span to the backing buffer.
/// </summary> /// </summary>

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

@ -388,15 +388,13 @@ namespace SixLabors.ImageSharp.Formats.Gif
previousFrame = this.previousFrame; 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); this.SetFrameMetaData(currentFrame.MetaData);
image = currentFrame; image = currentFrame;
this.RestoreToBackground(image); this.RestoreToBackground(image);
this.image.Frames.Add(currentFrame);
} }
int i = 0; int i = 0;

80
src/ImageSharp/Image/IImageFrameCollection.cs

@ -27,42 +27,82 @@ namespace SixLabors.ImageSharp
ImageFrame<TPixel> RootFrame { get; } ImageFrame<TPixel> RootFrame { get; }
/// <summary> /// <summary>
/// Gets or sets the <see cref="ImageFrame{TPixel}"/> at the specified index. /// Gets the <see cref="ImageFrame{TPixel}"/> at the specified index.
/// </summary> /// </summary>
/// <value> /// <value>
/// The <see cref="ImageFrame{TPixel}"/>. /// The <see cref="ImageFrame{TPixel}"/>.
/// </value> /// </value>
/// <param name="index">The index.</param> /// <param name="index">The index.</param>
/// <returns>The <see cref="ImageFrame{TPixel}"/> at the specified index.</returns> /// <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> /// <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> /// </summary>
/// <param name="frame">The <seealso cref="ImageFrame{TPixel}"/> to locate in the <seealso cref="Image{TPixel}"/>.</param> /// <param name="index"> The zero-based index at which item should be removed.</param>
/// <returns>The index of item if found in the list; otherwise, -1.</returns> /// <exception cref="InvalidOperationException">Cannot remove last frame.</exception>
int IndexOf(ImageFrame<TPixel> frame); /// <returns>The new <see cref="Image{TPixel}"/> with only the one frame on it.</returns>
Image<TPixel> CloneFrame(int index);
/// <summary> /// <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> /// </summary>
/// <param name="index"> The zero-based index at which item should be inserted..</param> /// <param name="index"> The zero-based index at which item should be removed.</param>
/// <param name="frame">The <seealso cref="ImageFrame{TPixel}"/> to insert into the <seealso cref="Image{TPixel}"/>.</param> /// <exception cref="InvalidOperationException">Cannot remove last frame.</exception>
void Insert(int index, ImageFrame<TPixel> frame); /// <returns>The new <see cref="Image{TPixel}"/> with only the one frame on it.</returns>
Image<TPixel> ExportFrame(int index);
/// <summary> /// <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> /// </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> /// <exception cref="InvalidOperationException">Cannot remove last frame.</exception>
void RemoveAt(int index); void RemoveFrame(int index);
/// <summary> /// <summary>
/// Adds the specified frame. /// Creates a new <seealso cref="ImageFrame{TPixel}"/> and appends it appends it to the end of the collection.
/// </summary> /// </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> /// <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> /// <summary>
/// Determines whether the <seealso cref="Image{TPixel}"/> contains the <paramref name="frame"/>. /// 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>. /// <c>true</c> if the <seealso cref="Image{TPixel}"/> the specified frame; otherwise, <c>false</c>.
/// </returns> /// </returns>
bool Contains(ImageFrame<TPixel> frame); 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> /// <content>
/// Adds static methods allowing the creation of new image from raw pixel data. /// Adds static methods allowing the creation of new image from raw pixel data.
/// </content> /// </content>
public static partial class ImageFrame internal static partial class ImageFrame
{ {
/// <summary> /// <summary>
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given byte array in <typeparamref name="TPixel"/> format. /// 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;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp namespace SixLabors.ImageSharp
@ -13,124 +13,134 @@ 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>
internal sealed class ImageFrameCollection<TPixel> : IImageFrameCollection<TPixel>, IDisposable internal sealed class ImageFrameCollection<TPixel> : IImageFrameCollection<TPixel>
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>>();
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)); Guard.NotNullOrEmpty(frames, nameof(frames));
this.parent = parent;
foreach (ImageFrame<TPixel> f in frames) foreach (ImageFrame<TPixel> f in frames)
{ {
this.Add(f); this.AddFrame(f);
} }
} }
/// <summary> /// <inheritdoc/>
/// Gets the count.
/// </summary>
public int Count => this.frames.Count; public int Count => this.frames.Count;
/// <summary> /// <inheritdoc/>
/// Gets the root frame.
/// </summary>
public ImageFrame<TPixel> RootFrame => this.frames.Count > 0 ? this.frames[0] : null; public ImageFrame<TPixel> RootFrame => this.frames.Count > 0 ? this.frames[0] : null;
/// <summary> /// <inheritdoc/>
/// 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>
public ImageFrame<TPixel> this[int index] public ImageFrame<TPixel> this[int index]
{ {
get => this.frames[index]; get => this.frames[index];
set
{
this.ValidateFrame(value);
this.frames[index] = value;
}
} }
/// <summary> /// <inheritdoc/>
/// 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>
public int IndexOf(ImageFrame<TPixel> frame) => this.frames.IndexOf(frame); public int IndexOf(ImageFrame<TPixel> frame) => this.frames.IndexOf(frame);
/// <summary> /// <inheritdoc/>
/// Inserts the <paramref name="frame"/> to the <seealso cref="Image{TPixel}"/> at the specified <paramref name="index"/>. public ImageFrame<TPixel> InsertFrame(int index, ImageFrame<TPixel> frame)
/// </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)
{ {
this.ValidateFrame(frame); this.ValidateFrame(frame);
this.frames.Insert(index, frame); ImageFrame<TPixel> clonedFrame = frame.Clone();
this.frames.Insert(index, clonedFrame);
return clonedFrame;
} }
/// <summary> /// <inheritdoc/>
/// Removes the <seealso cref="ImageFrame{TPixel}"/> from the <seealso cref="Image{TPixel}"/> at the specified index. public ImageFrame<TPixel> AddFrame(ImageFrame<TPixel> frame)
/// </summary> {
/// <param name="index">The zero-based index of the item to remove.</param> this.ValidateFrame(frame);
/// <exception cref="InvalidOperationException">Cannot remove last frame.</exception> ImageFrame<TPixel> clonedFrame = frame.Clone();
public void RemoveAt(int index) 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) if (index == 0 && this.Count == 1)
{ {
throw new InvalidOperationException("Cannot remove last frame."); throw new InvalidOperationException("Cannot remove last frame.");
} }
ImageFrame<TPixel> frame = this.frames[index];
this.frames.RemoveAt(index); this.frames.RemoveAt(index);
frame.Dispose();
} }
/// <summary> /// <inheritdoc/>
/// Adds the specified frame. public bool Contains(ImageFrame<TPixel> 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)
{ {
this.ValidateFrame(frame); return this.frames.Contains(frame);
this.frames.Add(frame);
} }
/// <summary> /// <inheritdoc/>
/// Determines whether the <seealso cref="Image{TPixel}"/> contains the <paramref name="frame"/>. public void MoveFrame(int sourceIndex, int destIndex)
/// </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)
{ {
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> /// <inheritdoc/>
/// Removes the specified frame. public Image<TPixel> ExportFrame(int index)
/// </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)
{ {
ImageFrame<TPixel> frame = this[index];
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); 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/> /// <inheritdoc/>
@ -152,8 +162,7 @@ namespace SixLabors.ImageSharp
} }
} }
/// <inheritdoc/> internal void Dispose()
public void Dispose()
{ {
foreach (ImageFrame<TPixel> f in this.frames) 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> /// <summary>
/// Gets a reference to the pixel at the specified position. /// Gets a reference to the pixel at the specified position.
/// </summary> /// </summary>
@ -172,7 +163,7 @@ namespace SixLabors.ImageSharp
/// <summary> /// <summary>
/// Disposes the object and frees resources for the Garbage Collector. /// Disposes the object and frees resources for the Garbage Collector.
/// </summary> /// </summary>
public void Dispose() internal void Dispose()
{ {
if (this.isDisposed) if (this.isDisposed)
{ {
@ -197,7 +188,7 @@ namespace SixLabors.ImageSharp
/// </summary> /// </summary>
/// <typeparam name="TPixel2">The pixel format.</typeparam> /// <typeparam name="TPixel2">The pixel format.</typeparam>
/// <returns>The <see cref="ImageFrame{TPixel2}"/></returns> /// <returns>The <see cref="ImageFrame{TPixel2}"/></returns>
public ImageFrame<TPixel2> CloneAs<TPixel2>() internal ImageFrame<TPixel2> CloneAs<TPixel2>()
where TPixel2 : struct, IPixel<TPixel2> where TPixel2 : struct, IPixel<TPixel2>
{ {
if (typeof(TPixel2) == typeof(TPixel)) if (typeof(TPixel2) == typeof(TPixel))
@ -234,9 +225,15 @@ namespace SixLabors.ImageSharp
/// Clones the current instance. /// Clones the current instance.
/// </summary> /// </summary>
/// <returns>The <see cref="ImageFrame{TPixel}"/></returns> /// <returns>The <see cref="ImageFrame{TPixel}"/></returns>
public ImageFrame<TPixel> Clone() internal ImageFrame<TPixel> Clone()
{ {
return new ImageFrame<TPixel>(this); 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.configuration = configuration ?? Configuration.Default;
this.MetaData = metadata ?? new ImageMetaData(); this.MetaData = metadata ?? new ImageMetaData();
this.frames = new ImageFrameCollection<TPixel>(width, height); this.frames = new ImageFrameCollection<TPixel>(this, width, height);
} }
/// <summary> /// <summary>
@ -77,7 +77,7 @@ namespace SixLabors.ImageSharp
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>(frames); this.frames = new ImageFrameCollection<TPixel>(this, frames);
} }
/// <summary> /// <summary>
@ -148,30 +148,6 @@ namespace SixLabors.ImageSharp
return new Image<TPixel>(this.configuration, this.MetaData.Clone(), frames); 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/> /// <inheritdoc/>
public override string ToString() public override string ToString()
{ {
@ -192,22 +168,6 @@ namespace SixLabors.ImageSharp
return target; 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> /// <summary>
/// Releases managed resources. /// Releases managed resources.
/// </summary> /// </summary>

2
src/ImageSharp/ImageSharp.csproj

@ -5,7 +5,7 @@
<VersionPrefix Condition="$(packageversion) != ''">$(packageversion)</VersionPrefix> <VersionPrefix Condition="$(packageversion) != ''">$(packageversion)</VersionPrefix>
<VersionPrefix Condition="$(packageversion) == ''">0.0.1</VersionPrefix> <VersionPrefix Condition="$(packageversion) == ''">0.0.1</VersionPrefix>
<Authors>Six Labors and contributors</Authors> <Authors>Six Labors and contributors</Authors>
<TargetFrameworks>netstandard1.3;netstandard1.1</TargetFrameworks> <TargetFrameworks>netstandard1.1;netstandard1.3</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<AssemblyName>SixLabors.ImageSharp</AssemblyName> <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)); 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)); 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 => return source.Apply(img =>
{ {
// TODO : move helper logic into the processor // 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; int palleteCount = quantized.Palette.Length - 1;
using (var pixels = new PixelAccessor<TPixel>(quantized.Width, quantized.Height)) 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.Linq;
using System.Text; using System.Text;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using Xunit; using Xunit;
namespace SixLabors.ImageSharp.Tests 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] [Fact]
public void ImageFramesaLwaysHaveOneFrame() public void ImageFramesaLwaysHaveOneFrame()
{ {
var collection = new ImageFrameCollection<Rgba32>(10, 10); Assert.Equal(1, this.collection.Count);
Assert.Equal(1, collection.Count);
} }
[Fact] [Fact]
public void AddNewFrame_FramesMustHaveSameSize() public void AddNewFrame_FramesMustHaveSameSize()
{ {
var collection = new ImageFrameCollection<Rgba32>(10, 10);
ArgumentException ex = Assert.Throws<ArgumentException>(() => 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); Assert.StartsWith("Frame must have the same dimensions as the image.", ex.Message);
} }
[Fact] [Fact]
public void AddNewFrame_FramesNotBeNull() public void AddNewFrame_Frame_FramesNotBeNull()
{ {
var collection = new ImageFrameCollection<Rgba32>(10, 10);
ArgumentNullException ex = Assert.Throws<ArgumentNullException>(() => ArgumentNullException ex = Assert.Throws<ArgumentNullException>(() =>
{ {
collection.Add(null); this.collection.AddFrame((ImageFrame<Rgba32>)null);
}); });
Assert.StartsWith("Value cannot be null.", ex.Message); Assert.StartsWith("Value cannot be null.", ex.Message);
} }
[Fact] [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] [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] [Fact]
public void SetFrameAtIndex_FramesMustHaveSameSize() public void InsertNewFrame_FramesMustHaveSameSize()
{ {
var collection = new ImageFrameCollection<Rgba32>(10, 10);
ArgumentException ex = Assert.Throws<ArgumentException>(() => 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); Assert.StartsWith("Frame must have the same dimensions as the image.", ex.Message);
} }
[Fact] [Fact]
public void SetFrameAtIndex_FramesNotBeNull() public void InsertNewFrame_FramesNotBeNull()
{ {
var collection = new ImageFrameCollection<Rgba32>(10, 10);
ArgumentNullException ex = Assert.Throws<ArgumentNullException>(() => ArgumentNullException ex = Assert.Throws<ArgumentNullException>(() =>
{ {
collection[0] = null; this.collection.InsertFrame(1, null);
}); });
Assert.StartsWith("Value cannot be null.", ex.Message); Assert.StartsWith("Value cannot be null.", ex.Message);
@ -100,7 +102,7 @@ namespace SixLabors.ImageSharp.Tests
ArgumentException ex = Assert.Throws<ArgumentException>(() => 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>(10,10),
new ImageFrame<Rgba32>(1,1), new ImageFrame<Rgba32>(1,1),
}); });
@ -112,13 +114,13 @@ namespace SixLabors.ImageSharp.Tests
[Fact] [Fact]
public void RemoveAtFrame_ThrowIfRemovingLastFrame() public void RemoveAtFrame_ThrowIfRemovingLastFrame()
{ {
var collection = new ImageFrameCollection<Rgba32>(new[] { var collection = new ImageFrameCollection<Rgba32>(this.image, new[] {
new ImageFrame<Rgba32>(10,10) new ImageFrame<Rgba32>(10,10)
}); });
InvalidOperationException ex = Assert.Throws<InvalidOperationException>(() => InvalidOperationException ex = Assert.Throws<InvalidOperationException>(() =>
{ {
collection.RemoveAt(0); collection.RemoveFrame(0);
}); });
Assert.Equal("Cannot remove last frame.", ex.Message); Assert.Equal("Cannot remove last frame.", ex.Message);
} }
@ -127,46 +129,19 @@ namespace SixLabors.ImageSharp.Tests
public void RemoveAtFrame_CanRemoveFrameZeroIfMultipleFramesExist() 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),
new ImageFrame<Rgba32>(10,10), new ImageFrame<Rgba32>(10,10),
}); });
collection.RemoveAt(0); collection.RemoveFrame(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); Assert.Equal(1, collection.Count);
} }
[Fact] [Fact]
public void RootFrameIsFrameAtIndexZero() 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),
new ImageFrame<Rgba32>(10,10), new ImageFrame<Rgba32>(10,10),
}); });
@ -177,7 +152,7 @@ namespace SixLabors.ImageSharp.Tests
[Fact] [Fact]
public void ConstructorPopulatesFrames() 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),
new ImageFrame<Rgba32>(10,10), new ImageFrame<Rgba32>(10,10),
}); });
@ -188,7 +163,7 @@ namespace SixLabors.ImageSharp.Tests
[Fact] [Fact]
public void DisposeClearsCollection() 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),
new ImageFrame<Rgba32>(10,10), new ImageFrame<Rgba32>(10,10),
}); });
@ -201,7 +176,7 @@ namespace SixLabors.ImageSharp.Tests
[Fact] [Fact]
public void Dispose_DisposesAllInnerFrames() 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),
new ImageFrame<Rgba32>(10,10), new ImageFrame<Rgba32>(10,10),
}); });
@ -215,5 +190,132 @@ namespace SixLabors.ImageSharp.Tests
Assert.Null(f.PixelBuffer); 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); 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) Image<TPixelB> actual)
where TPixelA : struct, IPixel<TPixelA> where TPixelB : struct, IPixel<TPixelB> 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>( 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) public static void ModifyPixel<TPixel>(Image<TPixel> img, int x, int y, byte perChannelChange)
where TPixel : struct, IPixel<TPixel> 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) 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++) 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; 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>( public static Image<TPixel> CompareToOriginal<TPixel>(
this Image<TPixel> image, this Image<TPixel> image,

Loading…
Cancel
Save