mirror of https://github.com/SixLabors/ImageSharp
Browse Source
* temporarily disable target frameworks * drop DelegateProcessor * drop IImageProcessingContext<TPixel> * drop NamedColors<T> * drop ColorBuilder<T> * drop the *Base postfix for clean class hierarchies * adding basic skeletons * non-generic ImageFrameCollection API definition * non-generic ImageFrameCollection tests * cleanup + docs + more tests * implement ImageFrameCollection methods * tests for generic PixelOperations.To<TDest>() * experimental implementation * fix .ttinclude * generate generic From<TSourcePixel>(...) * fix RgbaVector <--> BT709 Gray pixel conversion * Gray8 and Gray16 using ConvertFromRgbaScaledVector4() by default * fixed all conversion tests * ConstructGif_FromDifferentPixelTypes * fix xmldoc and other StyelCop findings * re-enable all target frameworks * fix NonGenericAddFrame() and NonGenericInsertFrame() * fix remaining bugspull/952/head
committed by
James Jackson-South
50 changed files with 1824 additions and 876 deletions
@ -0,0 +1,91 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System; |
||||
|
|
||||
|
using SixLabors.ImageSharp.Metadata; |
||||
|
using SixLabors.ImageSharp.PixelFormats; |
||||
|
using SixLabors.Memory; |
||||
|
using SixLabors.Primitives; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Represents a pixel-agnostic image frame containing all pixel data and <see cref="ImageFrameMetadata"/>.
|
||||
|
/// In case of animated formats like gif, it contains the single frame in a animation.
|
||||
|
/// In all other cases it is the only frame of the image.
|
||||
|
/// </summary>
|
||||
|
public abstract partial class ImageFrame : IDisposable |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="ImageFrame"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="configuration">The <see cref="Configuration"/>.</param>
|
||||
|
/// <param name="width">The width.</param>
|
||||
|
/// <param name="height">The height.</param>
|
||||
|
/// <param name="metadata">The <see cref="ImageFrameMetadata"/>.</param>
|
||||
|
protected ImageFrame(Configuration configuration, int width, int height, ImageFrameMetadata metadata) |
||||
|
{ |
||||
|
Guard.NotNull(configuration, nameof(configuration)); |
||||
|
Guard.NotNull(metadata, nameof(metadata)); |
||||
|
|
||||
|
this.Configuration = configuration; |
||||
|
this.MemoryAllocator = configuration.MemoryAllocator; |
||||
|
this.Width = width; |
||||
|
this.Height = height; |
||||
|
this.Metadata = metadata; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the <see cref="MemoryAllocator" /> to use for buffer allocations.
|
||||
|
/// </summary>
|
||||
|
public MemoryAllocator MemoryAllocator { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the <see cref="Configuration"/> instance associated with this <see cref="ImageFrame{TPixel}"/>.
|
||||
|
/// </summary>
|
||||
|
internal Configuration Configuration { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the width.
|
||||
|
/// </summary>
|
||||
|
public int Width { get; private set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the height.
|
||||
|
/// </summary>
|
||||
|
public int Height { get; private set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the metadata of the frame.
|
||||
|
/// </summary>
|
||||
|
public ImageFrameMetadata Metadata { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the size of the frame.
|
||||
|
/// </summary>
|
||||
|
/// <returns>The <see cref="Size"/></returns>
|
||||
|
public Size Size() => new Size(this.Width, this.Height); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the bounds of the frame.
|
||||
|
/// </summary>
|
||||
|
/// <returns>The <see cref="Rectangle"/></returns>
|
||||
|
public Rectangle Bounds() => new Rectangle(0, 0, this.Width, this.Height); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public abstract void Dispose(); |
||||
|
|
||||
|
internal abstract void CopyPixelsTo<TDestinationPixel>(Span<TDestinationPixel> destination) |
||||
|
where TDestinationPixel : struct, IPixel<TDestinationPixel>; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Updates the size of the image frame.
|
||||
|
/// </summary>
|
||||
|
internal void UpdateSize(Size size) |
||||
|
{ |
||||
|
this.Width = size.Width; |
||||
|
this.Height = size.Height; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,358 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections; |
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
using SixLabors.ImageSharp.Advanced; |
||||
|
using SixLabors.ImageSharp.Memory; |
||||
|
using SixLabors.ImageSharp.PixelFormats; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Encapsulates a pixel-specific collection of <see cref="ImageFrame{T}"/> instances
|
||||
|
/// that make up an <see cref="Image{T}"/>.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
|
||||
|
public sealed class ImageFrameCollection<TPixel> : ImageFrameCollection, IEnumerable<ImageFrame<TPixel>> |
||||
|
where TPixel : struct, IPixel<TPixel> |
||||
|
{ |
||||
|
private readonly IList<ImageFrame<TPixel>> frames = new List<ImageFrame<TPixel>>(); |
||||
|
private readonly Image<TPixel> parent; |
||||
|
|
||||
|
internal ImageFrameCollection(Image<TPixel> parent, int width, int height, TPixel backgroundColor) |
||||
|
{ |
||||
|
this.parent = parent ?? throw new ArgumentNullException(nameof(parent)); |
||||
|
|
||||
|
// Frames are already cloned within the caller
|
||||
|
this.frames.Add(new ImageFrame<TPixel>(parent.GetConfiguration(), width, height, backgroundColor)); |
||||
|
} |
||||
|
|
||||
|
internal ImageFrameCollection(Image<TPixel> parent, int width, int height, MemorySource<TPixel> memorySource) |
||||
|
{ |
||||
|
this.parent = parent ?? throw new ArgumentNullException(nameof(parent)); |
||||
|
|
||||
|
// Frames are already cloned within the caller
|
||||
|
this.frames.Add(new ImageFrame<TPixel>(parent.GetConfiguration(), width, height, memorySource)); |
||||
|
} |
||||
|
|
||||
|
internal ImageFrameCollection(Image<TPixel> parent, IEnumerable<ImageFrame<TPixel>> frames) |
||||
|
{ |
||||
|
Guard.NotNull(parent, nameof(parent)); |
||||
|
Guard.NotNull(frames, nameof(frames)); |
||||
|
|
||||
|
this.parent = parent; |
||||
|
|
||||
|
// Frames are already cloned by the caller
|
||||
|
foreach (ImageFrame<TPixel> f in frames) |
||||
|
{ |
||||
|
this.ValidateFrame(f); |
||||
|
this.frames.Add(f); |
||||
|
} |
||||
|
|
||||
|
// Ensure at least 1 frame was added to the frames collection
|
||||
|
if (this.frames.Count == 0) |
||||
|
{ |
||||
|
throw new ArgumentException("Must not be empty.", nameof(frames)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the number of frames.
|
||||
|
/// </summary>
|
||||
|
public override int Count => this.frames.Count; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the root frame.
|
||||
|
/// </summary>
|
||||
|
public new ImageFrame<TPixel> RootFrame => this.frames.Count > 0 ? this.frames[0] : null; |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
protected override ImageFrame NonGenericRootFrame => this.RootFrame; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 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>
|
||||
|
public new ImageFrame<TPixel> this[int index] => this.frames[index]; |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public override int IndexOf(ImageFrame frame) |
||||
|
{ |
||||
|
return frame is ImageFrame<TPixel> specific ? this.IndexOf(specific) : -1; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Determines the index of a specific <paramref name="frame"/> in the <seealso cref="ImageFrameCollection{TPixel}"/>.
|
||||
|
/// </summary>
|
||||
|
/// <param name="frame">The <seealso cref="ImageFrame{TPixel}"/> to locate in the <seealso cref="ImageFrameCollection{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); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Clones and inserts the <paramref name="source"/> into the <seealso cref="ImageFrameCollection{TPixel}"/> at the specified <paramref name="index"/>.
|
||||
|
/// </summary>
|
||||
|
/// <param name="index">The zero-based index to insert the frame at.</param>
|
||||
|
/// <param name="source">The <seealso cref="ImageFrame{TPixel}"/> to clone and insert into the <seealso cref="ImageFrameCollection{TPixel}"/>.</param>
|
||||
|
/// <exception cref="ArgumentException">Frame must have the same dimensions as the image.</exception>
|
||||
|
/// <returns>The cloned <see cref="ImageFrame{TPixel}"/>.</returns>
|
||||
|
public ImageFrame<TPixel> InsertFrame(int index, ImageFrame<TPixel> source) |
||||
|
{ |
||||
|
this.ValidateFrame(source); |
||||
|
ImageFrame<TPixel> clonedFrame = source.Clone(this.parent.GetConfiguration()); |
||||
|
this.frames.Insert(index, clonedFrame); |
||||
|
return clonedFrame; |
||||
|
} |
||||
|
|
||||
|
/// <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 the <seealso cref="ImageFrame{TPixel}"/> from.</param>
|
||||
|
/// <returns>The cloned <see cref="ImageFrame{TPixel}"/>.</returns>
|
||||
|
public ImageFrame<TPixel> AddFrame(ImageFrame<TPixel> source) |
||||
|
{ |
||||
|
this.ValidateFrame(source); |
||||
|
ImageFrame<TPixel> clonedFrame = source.Clone(this.parent.GetConfiguration()); |
||||
|
this.frames.Add(clonedFrame); |
||||
|
return clonedFrame; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Creates a new frame from the pixel data with the same dimensions as the other frames and inserts the
|
||||
|
/// new frame at the end of the collection.
|
||||
|
/// </summary>
|
||||
|
/// <param name="source">The raw pixel data to generate the <seealso cref="ImageFrame{TPixel}"/> from.</param>
|
||||
|
/// <returns>The new <see cref="ImageFrame{TPixel}"/>.</returns>
|
||||
|
public ImageFrame<TPixel> AddFrame(ReadOnlySpan<TPixel> source) |
||||
|
{ |
||||
|
var frame = ImageFrame.LoadPixelData( |
||||
|
this.parent.GetConfiguration(), |
||||
|
source, |
||||
|
this.RootFrame.Width, |
||||
|
this.RootFrame.Height); |
||||
|
this.frames.Add(frame); |
||||
|
return frame; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Creates a new frame from the pixel data with the same dimensions as the other frames and inserts the
|
||||
|
/// new frame at the end of the collection.
|
||||
|
/// </summary>
|
||||
|
/// <param name="source">The raw pixel data to generate the <seealso cref="ImageFrame{TPixel}"/> from.</param>
|
||||
|
/// <returns>The new <see cref="ImageFrame{TPixel}"/>.</returns>
|
||||
|
public ImageFrame<TPixel> AddFrame(TPixel[] source) |
||||
|
{ |
||||
|
Guard.NotNull(source, nameof(source)); |
||||
|
return this.AddFrame(source.AsSpan()); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Removes the frame at the specified index and frees all freeable resources associated with it.
|
||||
|
/// </summary>
|
||||
|
/// <param name="index">The zero-based index of the frame to remove.</param>
|
||||
|
/// <exception cref="InvalidOperationException">Cannot remove last frame.</exception>
|
||||
|
public override 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(); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
public override bool Contains(ImageFrame frame) => |
||||
|
frame is ImageFrame<TPixel> specific && this.Contains(specific); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Determines whether the <seealso cref="ImageFrameCollection{TPixel}"/> contains the <paramref name="frame"/>.
|
||||
|
/// </summary>
|
||||
|
/// <param name="frame">The frame.</param>
|
||||
|
/// <returns>
|
||||
|
/// <c>true</c> if the <seealso cref="ImageFrameCollection{TPixel}"/> contains the specified frame; otherwise, <c>false</c>.
|
||||
|
/// </returns>
|
||||
|
public bool Contains(ImageFrame<TPixel> frame) => this.frames.Contains(frame); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Moves an <seealso cref="ImageFrame{TPixel}"/> from <paramref name="sourceIndex"/> to <paramref name="destinationIndex"/>.
|
||||
|
/// </summary>
|
||||
|
/// <param name="sourceIndex">The zero-based index of the frame to move.</param>
|
||||
|
/// <param name="destinationIndex">The index to move the frame to.</param>
|
||||
|
public override void MoveFrame(int sourceIndex, int destinationIndex) |
||||
|
{ |
||||
|
if (sourceIndex == destinationIndex) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
ImageFrame<TPixel> frameAtIndex = this.frames[sourceIndex]; |
||||
|
this.frames.RemoveAt(sourceIndex); |
||||
|
this.frames.Insert(destinationIndex, frameAtIndex); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Removes the frame at the specified index and creates a new image with only the removed frame
|
||||
|
/// with the same metadata as the original image.
|
||||
|
/// </summary>
|
||||
|
/// <param name="index">The zero-based index of the frame to export.</param>
|
||||
|
/// <exception cref="InvalidOperationException">Cannot remove last frame.</exception>
|
||||
|
/// <returns>The new <see cref="Image{TPixel}"/> with the specified frame.</returns>
|
||||
|
public new 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."); |
||||
|
} |
||||
|
|
||||
|
this.frames.Remove(frame); |
||||
|
|
||||
|
return new Image<TPixel>(this.parent.GetConfiguration(), this.parent.Metadata.DeepClone(), new[] { frame }); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Creates an <see cref="Image{T}"/> with only the frame at the specified index
|
||||
|
/// with the same metadata as the original image.
|
||||
|
/// </summary>
|
||||
|
/// <param name="index">The zero-based index of the frame to clone.</param>
|
||||
|
/// <returns>The new <see cref="Image{TPixel}"/> with the specified frame.</returns>
|
||||
|
public new 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.DeepClone(), new[] { clonedFrame }); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Creates a new <seealso cref="ImageFrame{TPixel}" /> and appends it to the end of the collection.
|
||||
|
/// </summary>
|
||||
|
/// <returns>
|
||||
|
/// The new <see cref="ImageFrame{TPixel}" />.
|
||||
|
/// </returns>
|
||||
|
public new ImageFrame<TPixel> CreateFrame() |
||||
|
{ |
||||
|
var frame = new ImageFrame<TPixel>( |
||||
|
this.parent.GetConfiguration(), |
||||
|
this.RootFrame.Width, |
||||
|
this.RootFrame.Height); |
||||
|
this.frames.Add(frame); |
||||
|
return frame; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
protected override IEnumerator<ImageFrame> NonGenericGetEnumerator() => this.frames.GetEnumerator(); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
protected override ImageFrame NonGenericGetFrame(int index) => this[index]; |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
protected override ImageFrame NonGenericInsertFrame(int index, ImageFrame source) |
||||
|
{ |
||||
|
Guard.NotNull(source, nameof(source)); |
||||
|
|
||||
|
if (source is ImageFrame<TPixel> compatibleSource) |
||||
|
{ |
||||
|
return this.InsertFrame(index, compatibleSource); |
||||
|
} |
||||
|
|
||||
|
ImageFrame<TPixel> result = this.CopyNonCompatibleFrame(source); |
||||
|
this.frames.Insert(index, result); |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
protected override ImageFrame NonGenericAddFrame(ImageFrame source) |
||||
|
{ |
||||
|
Guard.NotNull(source, nameof(source)); |
||||
|
|
||||
|
if (source is ImageFrame<TPixel> compatibleSource) |
||||
|
{ |
||||
|
return this.AddFrame(compatibleSource); |
||||
|
} |
||||
|
|
||||
|
ImageFrame<TPixel> result = this.CopyNonCompatibleFrame(source); |
||||
|
this.frames.Add(result); |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
protected override Image NonGenericExportFrame(int index) => this.ExportFrame(index); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
protected override Image NonGenericCloneFrame(int index) => this.CloneFrame(index); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
protected override ImageFrame NonGenericCreateFrame(Color backgroundColor) => |
||||
|
this.CreateFrame(backgroundColor.ToPixel<TPixel>()); |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
protected override ImageFrame NonGenericCreateFrame() => this.CreateFrame(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Creates a new <seealso cref="ImageFrame{TPixel}" /> and appends it to the end of the collection.
|
||||
|
/// </summary>
|
||||
|
/// <param name="backgroundColor">The background color to initialize the pixels with.</param>
|
||||
|
/// <returns>
|
||||
|
/// The new <see cref="ImageFrame{TPixel}" />.
|
||||
|
/// </returns>
|
||||
|
public ImageFrame<TPixel> CreateFrame(TPixel backgroundColor) |
||||
|
{ |
||||
|
var frame = new ImageFrame<TPixel>( |
||||
|
this.parent.GetConfiguration(), |
||||
|
this.RootFrame.Width, |
||||
|
this.RootFrame.Height, |
||||
|
backgroundColor); |
||||
|
this.frames.Add(frame); |
||||
|
return frame; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
IEnumerator<ImageFrame<TPixel>> IEnumerable<ImageFrame<TPixel>>.GetEnumerator() => this.frames.GetEnumerator(); |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)this.frames).GetEnumerator(); |
||||
|
|
||||
|
private void ValidateFrame(ImageFrame<TPixel> frame) |
||||
|
{ |
||||
|
Guard.NotNull(frame, nameof(frame)); |
||||
|
|
||||
|
if (this.Count != 0) |
||||
|
{ |
||||
|
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)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
internal void Dispose() |
||||
|
{ |
||||
|
foreach (ImageFrame<TPixel> f in this.frames) |
||||
|
{ |
||||
|
f.Dispose(); |
||||
|
} |
||||
|
|
||||
|
this.frames.Clear(); |
||||
|
} |
||||
|
|
||||
|
private ImageFrame<TPixel> CopyNonCompatibleFrame(ImageFrame source) |
||||
|
{ |
||||
|
ImageFrame<TPixel> result = new ImageFrame<TPixel>( |
||||
|
this.parent.GetConfiguration(), |
||||
|
source.Size(), |
||||
|
source.Metadata.DeepClone()); |
||||
|
source.CopyPixelsTo(result.PixelBuffer.Span); |
||||
|
return result; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,346 @@ |
|||||
|
// // Copyright (c) Six Labors and contributors.
|
||||
|
// // Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System; |
||||
|
using System.Linq; |
||||
|
|
||||
|
using SixLabors.ImageSharp.Advanced; |
||||
|
using SixLabors.ImageSharp.PixelFormats; |
||||
|
|
||||
|
using Xunit; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Tests |
||||
|
{ |
||||
|
public abstract partial class ImageFrameCollectionTests |
||||
|
{ |
||||
|
[GroupOutput("ImageFramesCollectionTests")] |
||||
|
public class Generic : ImageFrameCollectionTests |
||||
|
{ |
||||
|
[Fact] |
||||
|
public void Constructor_ShouldCreateOneFrame() |
||||
|
{ |
||||
|
Assert.Equal(1, this.Collection.Count); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void AddNewFrame_FramesMustHaveSameSize() |
||||
|
{ |
||||
|
ArgumentException ex = Assert.Throws<ArgumentException>( |
||||
|
() => |
||||
|
{ |
||||
|
this.Collection.AddFrame(new ImageFrame<Rgba32>(Configuration.Default, 1, 1)); |
||||
|
}); |
||||
|
|
||||
|
Assert.StartsWith("Frame must have the same dimensions as the image.", ex.Message); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void AddNewFrame_Frame_FramesNotBeNull() |
||||
|
{ |
||||
|
ArgumentNullException ex = Assert.Throws<ArgumentNullException>( |
||||
|
() => |
||||
|
{ |
||||
|
this.Collection.AddFrame((ImageFrame<Rgba32>)null); |
||||
|
}); |
||||
|
|
||||
|
Assert.StartsWith("Value cannot be null.", ex.Message); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void AddNewFrame_PixelBuffer_DataMustNotBeNull() |
||||
|
{ |
||||
|
Rgba32[] data = null; |
||||
|
|
||||
|
ArgumentNullException ex = Assert.Throws<ArgumentNullException>( |
||||
|
() => |
||||
|
{ |
||||
|
this.Collection.AddFrame(data); |
||||
|
}); |
||||
|
|
||||
|
Assert.StartsWith("Value cannot be null.", ex.Message); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void AddNewFrame_PixelBuffer_BufferIncorrectSize() |
||||
|
{ |
||||
|
ArgumentOutOfRangeException ex = Assert.Throws<ArgumentOutOfRangeException>( |
||||
|
() => |
||||
|
{ |
||||
|
this.Collection.AddFrame(new Rgba32[0]); |
||||
|
}); |
||||
|
|
||||
|
Assert.StartsWith("Value 0 must be greater than or equal to 100.", ex.Message); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void InsertNewFrame_FramesMustHaveSameSize() |
||||
|
{ |
||||
|
ArgumentException ex = Assert.Throws<ArgumentException>( |
||||
|
() => |
||||
|
{ |
||||
|
this.Collection.InsertFrame(1, new ImageFrame<Rgba32>(Configuration.Default, 1, 1)); |
||||
|
}); |
||||
|
|
||||
|
Assert.StartsWith("Frame must have the same dimensions as the image.", ex.Message); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void InsertNewFrame_FramesNotBeNull() |
||||
|
{ |
||||
|
ArgumentNullException ex = Assert.Throws<ArgumentNullException>( |
||||
|
() => |
||||
|
{ |
||||
|
this.Collection.InsertFrame(1, null); |
||||
|
}); |
||||
|
|
||||
|
Assert.StartsWith("Value cannot be null.", ex.Message); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Constructor_FramesMustHaveSameSize() |
||||
|
{ |
||||
|
ArgumentException ex = Assert.Throws<ArgumentException>( |
||||
|
() => |
||||
|
{ |
||||
|
var collection = new ImageFrameCollection<Rgba32>( |
||||
|
this.Image, |
||||
|
new[] |
||||
|
{ |
||||
|
new ImageFrame<Rgba32>(Configuration.Default, 10, 10), |
||||
|
new ImageFrame<Rgba32>(Configuration.Default, 1, 1) |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
Assert.StartsWith("Frame must have the same dimensions as the image.", ex.Message); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void RemoveAtFrame_ThrowIfRemovingLastFrame() |
||||
|
{ |
||||
|
var collection = new ImageFrameCollection<Rgba32>( |
||||
|
this.Image, |
||||
|
new[] { new ImageFrame<Rgba32>(Configuration.Default, 10, 10) }); |
||||
|
|
||||
|
InvalidOperationException ex = Assert.Throws<InvalidOperationException>( |
||||
|
() => |
||||
|
{ |
||||
|
collection.RemoveFrame(0); |
||||
|
}); |
||||
|
Assert.Equal("Cannot remove last frame.", ex.Message); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void RemoveAtFrame_CanRemoveFrameZeroIfMultipleFramesExist() |
||||
|
{ |
||||
|
var collection = new ImageFrameCollection<Rgba32>( |
||||
|
this.Image, |
||||
|
new[] |
||||
|
{ |
||||
|
new ImageFrame<Rgba32>(Configuration.Default, 10, 10), |
||||
|
new ImageFrame<Rgba32>(Configuration.Default, 10, 10) |
||||
|
}); |
||||
|
|
||||
|
collection.RemoveFrame(0); |
||||
|
Assert.Equal(1, collection.Count); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void RootFrameIsFrameAtIndexZero() |
||||
|
{ |
||||
|
var collection = new ImageFrameCollection<Rgba32>( |
||||
|
this.Image, |
||||
|
new[] |
||||
|
{ |
||||
|
new ImageFrame<Rgba32>(Configuration.Default, 10, 10), |
||||
|
new ImageFrame<Rgba32>(Configuration.Default, 10, 10) |
||||
|
}); |
||||
|
|
||||
|
Assert.Equal(collection.RootFrame, collection[0]); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void ConstructorPopulatesFrames() |
||||
|
{ |
||||
|
var collection = new ImageFrameCollection<Rgba32>( |
||||
|
this.Image, |
||||
|
new[] |
||||
|
{ |
||||
|
new ImageFrame<Rgba32>(Configuration.Default, 10, 10), |
||||
|
new ImageFrame<Rgba32>(Configuration.Default, 10, 10) |
||||
|
}); |
||||
|
|
||||
|
Assert.Equal(2, collection.Count); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void DisposeClearsCollection() |
||||
|
{ |
||||
|
var collection = new ImageFrameCollection<Rgba32>( |
||||
|
this.Image, |
||||
|
new[] |
||||
|
{ |
||||
|
new ImageFrame<Rgba32>(Configuration.Default, 10, 10), |
||||
|
new ImageFrame<Rgba32>(Configuration.Default, 10, 10) |
||||
|
}); |
||||
|
|
||||
|
collection.Dispose(); |
||||
|
|
||||
|
Assert.Equal(0, collection.Count); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Dispose_DisposesAllInnerFrames() |
||||
|
{ |
||||
|
var collection = new ImageFrameCollection<Rgba32>( |
||||
|
this.Image, |
||||
|
new[] |
||||
|
{ |
||||
|
new ImageFrame<Rgba32>(Configuration.Default, 10, 10), |
||||
|
new ImageFrame<Rgba32>(Configuration.Default, 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); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
[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>(Configuration.Default, 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>(Configuration.Default, 10, 10)); |
||||
|
using (Image<TPixel> cloned = img.Frames.ExportFrame(0)) |
||||
|
{ |
||||
|
Assert.Equal(1, img.Frames.Count); |
||||
|
cloned.ComparePixelBufferTo(sourcePixelData); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void CreateFrame_Default() |
||||
|
{ |
||||
|
this.Image.Frames.CreateFrame(); |
||||
|
|
||||
|
Assert.Equal(2, this.Image.Frames.Count); |
||||
|
this.Image.Frames[1].ComparePixelBufferTo(default(Rgba32)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void CreateFrame_CustomFillColor() |
||||
|
{ |
||||
|
this.Image.Frames.CreateFrame(Rgba32.HotPink); |
||||
|
|
||||
|
Assert.Equal(2, this.Image.Frames.Count); |
||||
|
this.Image.Frames[1].ComparePixelBufferTo(Rgba32.HotPink); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
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>(Configuration.Default, 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>(Configuration.Default, 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_FalseIfNonMember() |
||||
|
{ |
||||
|
for (var i = 0; i < 9; i++) |
||||
|
{ |
||||
|
this.Image.Frames.CreateFrame(); |
||||
|
} |
||||
|
|
||||
|
var frame = new ImageFrame<Rgba32>(Configuration.Default, 10, 10); |
||||
|
Assert.False(this.Image.Frames.Contains(frame)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,325 @@ |
|||||
|
// // Copyright (c) Six Labors and contributors.
|
||||
|
// // Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System; |
||||
|
using System.Drawing.Imaging; |
||||
|
using System.Linq; |
||||
|
|
||||
|
using SixLabors.ImageSharp.Advanced; |
||||
|
using SixLabors.ImageSharp.Formats.Gif; |
||||
|
using SixLabors.ImageSharp.PixelFormats; |
||||
|
|
||||
|
using Xunit; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Tests |
||||
|
{ |
||||
|
public abstract partial class ImageFrameCollectionTests |
||||
|
{ |
||||
|
[GroupOutput("ImageFramesCollectionTests")] |
||||
|
public class NonGeneric : ImageFrameCollectionTests |
||||
|
{ |
||||
|
private new Image Image => base.Image; |
||||
|
|
||||
|
private new ImageFrameCollection Collection => base.Collection; |
||||
|
|
||||
|
[Fact] |
||||
|
public void AddFrame_OfDifferentPixelType() |
||||
|
{ |
||||
|
using (Image<Bgra32> sourceImage = new Image<Bgra32>( |
||||
|
this.Image.GetConfiguration(), |
||||
|
this.Image.Width, |
||||
|
this.Image.Height, |
||||
|
(Bgra32)Color.Blue)) |
||||
|
{ |
||||
|
this.Collection.AddFrame(sourceImage.Frames.RootFrame); |
||||
|
} |
||||
|
|
||||
|
Rgba32[] expectedAllBlue = |
||||
|
Enumerable.Repeat(Rgba32.Blue, this.Image.Width * this.Image.Height).ToArray(); |
||||
|
|
||||
|
Assert.Equal(2, this.Collection.Count); |
||||
|
ImageFrame<Rgba32> actualFrame = (ImageFrame<Rgba32>)this.Collection[1]; |
||||
|
|
||||
|
actualFrame.ComparePixelBufferTo(expectedAllBlue); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void InsertFrame_OfDifferentPixelType() |
||||
|
{ |
||||
|
using (Image<Bgra32> sourceImage = new Image<Bgra32>( |
||||
|
this.Image.GetConfiguration(), |
||||
|
this.Image.Width, |
||||
|
this.Image.Height, |
||||
|
(Bgra32)Color.Blue)) |
||||
|
{ |
||||
|
this.Collection.InsertFrame(0, sourceImage.Frames.RootFrame); |
||||
|
} |
||||
|
|
||||
|
Rgba32[] expectedAllBlue = |
||||
|
Enumerable.Repeat(Rgba32.Blue, this.Image.Width * this.Image.Height).ToArray(); |
||||
|
|
||||
|
Assert.Equal(2, this.Collection.Count); |
||||
|
ImageFrame<Rgba32> actualFrame = (ImageFrame<Rgba32>)this.Collection[0]; |
||||
|
|
||||
|
actualFrame.ComparePixelBufferTo(expectedAllBlue); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Constructor_ShouldCreateOneFrame() |
||||
|
{ |
||||
|
Assert.Equal(1, this.Collection.Count); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void AddNewFrame_FramesMustHaveSameSize() |
||||
|
{ |
||||
|
ArgumentException ex = Assert.Throws<ArgumentException>( |
||||
|
() => |
||||
|
{ |
||||
|
this.Collection.AddFrame(new ImageFrame<Rgba32>(Configuration.Default, 1, 1)); |
||||
|
}); |
||||
|
|
||||
|
Assert.StartsWith("Frame must have the same dimensions as the image.", ex.Message); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void AddNewFrame_Frame_FramesNotBeNull() |
||||
|
{ |
||||
|
ArgumentNullException ex = Assert.Throws<ArgumentNullException>( |
||||
|
() => |
||||
|
{ |
||||
|
this.Collection.AddFrame((ImageFrame<Rgba32>)null); |
||||
|
}); |
||||
|
|
||||
|
Assert.StartsWith("Value cannot be null.", ex.Message); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void InsertNewFrame_FramesMustHaveSameSize() |
||||
|
{ |
||||
|
ArgumentException ex = Assert.Throws<ArgumentException>( |
||||
|
() => |
||||
|
{ |
||||
|
this.Collection.InsertFrame(1, new ImageFrame<Rgba32>(Configuration.Default, 1, 1)); |
||||
|
}); |
||||
|
|
||||
|
Assert.StartsWith("Frame must have the same dimensions as the image.", ex.Message); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void InsertNewFrame_FramesNotBeNull() |
||||
|
{ |
||||
|
ArgumentNullException ex = Assert.Throws<ArgumentNullException>( |
||||
|
() => |
||||
|
{ |
||||
|
this.Collection.InsertFrame(1, null); |
||||
|
}); |
||||
|
|
||||
|
Assert.StartsWith("Value cannot be null.", ex.Message); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
[Fact] |
||||
|
public void RemoveAtFrame_ThrowIfRemovingLastFrame() |
||||
|
{ |
||||
|
|
||||
|
InvalidOperationException ex = Assert.Throws<InvalidOperationException>( |
||||
|
() => |
||||
|
{ |
||||
|
this.Collection.RemoveFrame(0); |
||||
|
}); |
||||
|
Assert.Equal("Cannot remove last frame.", ex.Message); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void RemoveAtFrame_CanRemoveFrameZeroIfMultipleFramesExist() |
||||
|
{ |
||||
|
this.Collection.AddFrame(new ImageFrame<Rgba32>(Configuration.Default, 10, 10)); |
||||
|
|
||||
|
this.Collection.RemoveFrame(0); |
||||
|
Assert.Equal(1, this.Collection.Count); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void RootFrameIsFrameAtIndexZero() |
||||
|
{ |
||||
|
Assert.Equal(this.Collection.RootFrame, this.Collection[0]); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[WithTestPatternImages(10, 10, PixelTypes.Rgba32 | PixelTypes.Bgr24)] |
||||
|
public void CloneFrame<TPixel>(TestImageProvider<TPixel> provider) |
||||
|
where TPixel : struct, IPixel<TPixel> |
||||
|
{ |
||||
|
using (Image<TPixel> img = provider.GetImage()) |
||||
|
{ |
||||
|
ImageFrameCollection nonGenericFrameCollection = img.Frames; |
||||
|
|
||||
|
nonGenericFrameCollection.AddFrame(new ImageFrame<TPixel>(Configuration.Default, 10, 10)); // add a frame anyway
|
||||
|
using (Image cloned = nonGenericFrameCollection.CloneFrame(0)) |
||||
|
{ |
||||
|
Assert.Equal(2, img.Frames.Count); |
||||
|
|
||||
|
Image<TPixel> expectedClone = (Image<TPixel>)cloned; |
||||
|
|
||||
|
expectedClone.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(); |
||||
|
|
||||
|
ImageFrameCollection nonGenericFrameCollection = img.Frames; |
||||
|
|
||||
|
nonGenericFrameCollection.AddFrame(new ImageFrame<TPixel>(Configuration.Default, 10, 10)); |
||||
|
using (Image cloned = nonGenericFrameCollection.ExportFrame(0)) |
||||
|
{ |
||||
|
Assert.Equal(1, img.Frames.Count); |
||||
|
|
||||
|
Image<TPixel> expectedClone = (Image<TPixel>)cloned; |
||||
|
expectedClone.ComparePixelBufferTo(sourcePixelData); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void CreateFrame_Default() |
||||
|
{ |
||||
|
this.Image.Frames.CreateFrame(); |
||||
|
|
||||
|
Assert.Equal(2, this.Image.Frames.Count); |
||||
|
|
||||
|
ImageFrame<Rgba32> frame = (ImageFrame<Rgba32>)this.Image.Frames[1]; |
||||
|
|
||||
|
frame.ComparePixelBufferTo(default(Rgba32)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void CreateFrame_CustomFillColor() |
||||
|
{ |
||||
|
this.Image.Frames.CreateFrame(Rgba32.HotPink); |
||||
|
|
||||
|
Assert.Equal(2, this.Image.Frames.Count); |
||||
|
|
||||
|
ImageFrame<Rgba32> frame = (ImageFrame<Rgba32>)this.Image.Frames[1]; |
||||
|
|
||||
|
frame.ComparePixelBufferTo(Rgba32.HotPink); |
||||
|
} |
||||
|
|
||||
|
[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_FalseIfNonMember() |
||||
|
{ |
||||
|
for (var i = 0; i < 9; i++) |
||||
|
{ |
||||
|
this.Image.Frames.CreateFrame(); |
||||
|
} |
||||
|
|
||||
|
var frame = new ImageFrame<Rgba32>(Configuration.Default, 10, 10); |
||||
|
Assert.False(this.Image.Frames.Contains(frame)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Integration test for end-to end API validation.
|
||||
|
/// </summary>
|
||||
|
[Theory] |
||||
|
[WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32)] |
||||
|
public void ConstructGif_FromDifferentPixelTypes<TPixel>(TestImageProvider<TPixel> provider) |
||||
|
where TPixel : struct, IPixel<TPixel> |
||||
|
{ |
||||
|
using (Image source = provider.GetImage()) |
||||
|
using (Image<TPixel> dest = new Image<TPixel>(source.GetConfiguration(), source.Width, source.Height)) |
||||
|
{ |
||||
|
// Giphy.gif has 5 frames
|
||||
|
|
||||
|
ImportFrameAs<Bgra32>(source.Frames, dest.Frames, 0); |
||||
|
ImportFrameAs<Argb32>(source.Frames, dest.Frames, 1); |
||||
|
ImportFrameAs<Rgba64>(source.Frames, dest.Frames, 2); |
||||
|
ImportFrameAs<Rgba32>(source.Frames, dest.Frames, 3); |
||||
|
ImportFrameAs<Bgra32>(source.Frames, dest.Frames, 4); |
||||
|
|
||||
|
// Drop the original empty root frame:
|
||||
|
dest.Frames.RemoveFrame(0); |
||||
|
|
||||
|
dest.DebugSave(provider, appendSourceFileOrDescription: false, extension: "gif"); |
||||
|
dest.CompareToOriginal(provider); |
||||
|
|
||||
|
for (int i = 0; i < 5; i++) |
||||
|
{ |
||||
|
CompareGifMetadata(source.Frames[i], dest.Frames[i]); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static void ImportFrameAs<TPixel>(ImageFrameCollection source, ImageFrameCollection destination, int index) |
||||
|
where TPixel : struct, IPixel<TPixel> |
||||
|
{ |
||||
|
using (Image temp = source.CloneFrame(index)) |
||||
|
{ |
||||
|
using (Image<TPixel> temp2 = temp.CloneAs<TPixel>()) |
||||
|
{ |
||||
|
destination.AddFrame(temp2.Frames.RootFrame); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static void CompareGifMetadata(ImageFrame a, ImageFrame b) |
||||
|
{ |
||||
|
// TODO: all metadata classes should be equatable!
|
||||
|
|
||||
|
GifFrameMetadata aData = a.Metadata.GetFormatMetadata(GifFormat.Instance); |
||||
|
GifFrameMetadata bData = b.Metadata.GetFormatMetadata(GifFormat.Instance); |
||||
|
|
||||
|
Assert.Equal(aData.DisposalMethod, bData.DisposalMethod); |
||||
|
Assert.Equal(aData.FrameDelay, bData.FrameDelay); |
||||
|
Assert.Equal(aData.ColorTableLength, bData.ColorTableLength); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,29 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Text; |
||||
|
|
||||
|
using SixLabors.ImageSharp.PixelFormats; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Tests |
||||
|
{ |
||||
|
public abstract partial class ImageFrameCollectionTests : IDisposable |
||||
|
{ |
||||
|
protected Image<Rgba32> Image { get; } |
||||
|
protected ImageFrameCollection<Rgba32> Collection { get; } |
||||
|
|
||||
|
public ImageFrameCollectionTests() |
||||
|
{ |
||||
|
// Needed to get English exception messages, which are checked in several tests.
|
||||
|
System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("en-US"); |
||||
|
|
||||
|
this.Image = new Image<Rgba32>(10, 10); |
||||
|
this.Collection = new ImageFrameCollection<Rgba32>(this.Image, 10, 10, default(Rgba32)); |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
this.Image.Dispose(); |
||||
|
this.Collection.Dispose(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,331 +0,0 @@ |
|||||
using System; |
|
||||
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 : IDisposable |
|
||||
{ |
|
||||
private Image<Rgba32> image; |
|
||||
private ImageFrameCollection<Rgba32> collection; |
|
||||
|
|
||||
public ImageFramesCollectionTests() |
|
||||
{ |
|
||||
// Needed to get English exception messages, which are checked in several tests.
|
|
||||
System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("en-US"); |
|
||||
|
|
||||
this.image = new Image<Rgba32>(10, 10); |
|
||||
this.collection = new ImageFrameCollection<Rgba32>(this.image, 10, 10, default(Rgba32)); |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public void ImageFramesaLwaysHaveOneFrame() |
|
||||
{ |
|
||||
Assert.Equal(1, this.collection.Count); |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public void AddNewFrame_FramesMustHaveSameSize() |
|
||||
{ |
|
||||
ArgumentException ex = Assert.Throws<ArgumentException>(() => |
|
||||
{ |
|
||||
this.collection.AddFrame(new ImageFrame<Rgba32>(Configuration.Default, 1, 1)); |
|
||||
}); |
|
||||
|
|
||||
Assert.StartsWith("Frame must have the same dimensions as the image.", ex.Message); |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public void AddNewFrame_Frame_FramesNotBeNull() |
|
||||
{ |
|
||||
ArgumentNullException ex = Assert.Throws<ArgumentNullException>(() => |
|
||||
{ |
|
||||
this.collection.AddFrame((ImageFrame<Rgba32>)null); |
|
||||
}); |
|
||||
|
|
||||
Assert.StartsWith("Value cannot be null.", ex.Message); |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public void AddNewFrame_PixelBuffer_DataMustNotBeNull() |
|
||||
{ |
|
||||
Rgba32[] data = null; |
|
||||
|
|
||||
ArgumentNullException ex = Assert.Throws<ArgumentNullException>(() => |
|
||||
{ |
|
||||
this.collection.AddFrame(data); |
|
||||
}); |
|
||||
|
|
||||
Assert.StartsWith("Value cannot be null.", ex.Message); |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public void AddNewFrame_PixelBuffer_BufferIncorrectSize() |
|
||||
{ |
|
||||
ArgumentOutOfRangeException ex = Assert.Throws<ArgumentOutOfRangeException>(() => |
|
||||
{ |
|
||||
this.collection.AddFrame(new Rgba32[0]); |
|
||||
}); |
|
||||
|
|
||||
Assert.StartsWith("Value 0 must be greater than or equal to 100.", ex.Message); |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public void InsertNewFrame_FramesMustHaveSameSize() |
|
||||
{ |
|
||||
ArgumentException ex = Assert.Throws<ArgumentException>(() => |
|
||||
{ |
|
||||
this.collection.InsertFrame(1, new ImageFrame<Rgba32>(Configuration.Default, 1, 1)); |
|
||||
}); |
|
||||
|
|
||||
Assert.StartsWith("Frame must have the same dimensions as the image.", ex.Message); |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public void InsertNewFrame_FramesNotBeNull() |
|
||||
{ |
|
||||
ArgumentNullException ex = Assert.Throws<ArgumentNullException>(() => |
|
||||
{ |
|
||||
this.collection.InsertFrame(1, null); |
|
||||
}); |
|
||||
|
|
||||
Assert.StartsWith("Value cannot be null.", ex.Message); |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public void Constructor_FramesMustHaveSameSize() |
|
||||
{ |
|
||||
ArgumentException ex = Assert.Throws<ArgumentException>(() => |
|
||||
{ |
|
||||
var collection = new ImageFrameCollection<Rgba32>(this.image, new[] { |
|
||||
new ImageFrame<Rgba32>(Configuration.Default,10,10), |
|
||||
new ImageFrame<Rgba32>(Configuration.Default,1,1) |
|
||||
}); |
|
||||
}); |
|
||||
|
|
||||
Assert.StartsWith("Frame must have the same dimensions as the image.", ex.Message); |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public void RemoveAtFrame_ThrowIfRemovingLastFrame() |
|
||||
{ |
|
||||
var collection = new ImageFrameCollection<Rgba32>(this.image, new[] { |
|
||||
new ImageFrame<Rgba32>(Configuration.Default,10,10) |
|
||||
}); |
|
||||
|
|
||||
InvalidOperationException ex = Assert.Throws<InvalidOperationException>(() => |
|
||||
{ |
|
||||
collection.RemoveFrame(0); |
|
||||
}); |
|
||||
Assert.Equal("Cannot remove last frame.", ex.Message); |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public void RemoveAtFrame_CanRemoveFrameZeroIfMultipleFramesExist() |
|
||||
{ |
|
||||
|
|
||||
var collection = new ImageFrameCollection<Rgba32>(this.image, new[] { |
|
||||
new ImageFrame<Rgba32>(Configuration.Default,10,10), |
|
||||
new ImageFrame<Rgba32>(Configuration.Default,10,10) |
|
||||
}); |
|
||||
|
|
||||
collection.RemoveFrame(0); |
|
||||
Assert.Equal(1, collection.Count); |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public void RootFrameIsFrameAtIndexZero() |
|
||||
{ |
|
||||
var collection = new ImageFrameCollection<Rgba32>(this.image, new[] { |
|
||||
new ImageFrame<Rgba32>(Configuration.Default,10,10), |
|
||||
new ImageFrame<Rgba32>(Configuration.Default,10,10) |
|
||||
}); |
|
||||
|
|
||||
Assert.Equal(collection.RootFrame, collection[0]); |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public void ConstructorPopulatesFrames() |
|
||||
{ |
|
||||
var collection = new ImageFrameCollection<Rgba32>(this.image, new[] { |
|
||||
new ImageFrame<Rgba32>(Configuration.Default,10,10), |
|
||||
new ImageFrame<Rgba32>(Configuration.Default,10,10) |
|
||||
}); |
|
||||
|
|
||||
Assert.Equal(2, collection.Count); |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public void DisposeClearsCollection() |
|
||||
{ |
|
||||
var collection = new ImageFrameCollection<Rgba32>(this.image, new[] { |
|
||||
new ImageFrame<Rgba32>(Configuration.Default,10,10), |
|
||||
new ImageFrame<Rgba32>(Configuration.Default,10,10) |
|
||||
}); |
|
||||
|
|
||||
collection.Dispose(); |
|
||||
|
|
||||
Assert.Equal(0, collection.Count); |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public void Dispose_DisposesAllInnerFrames() |
|
||||
{ |
|
||||
var collection = new ImageFrameCollection<Rgba32>(this.image, new[] { |
|
||||
new ImageFrame<Rgba32>(Configuration.Default,10,10), |
|
||||
new ImageFrame<Rgba32>(Configuration.Default,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); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
[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>(Configuration.Default, 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>(Configuration.Default, 10, 10)); |
|
||||
using (Image<TPixel> cloned = img.Frames.ExportFrame(0)) |
|
||||
{ |
|
||||
Assert.Equal(1, img.Frames.Count); |
|
||||
cloned.ComparePixelBufferTo(sourcePixelData); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public void CreateFrame_Default() |
|
||||
{ |
|
||||
this.image.Frames.CreateFrame(); |
|
||||
|
|
||||
Assert.Equal(2, this.image.Frames.Count); |
|
||||
this.image.Frames[1].ComparePixelBufferTo(default(Rgba32)); |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public void CreateFrame_CustomFillColor() |
|
||||
{ |
|
||||
this.image.Frames.CreateFrame(Rgba32.HotPink); |
|
||||
|
|
||||
Assert.Equal(2, this.image.Frames.Count); |
|
||||
this.image.Frames[1].ComparePixelBufferTo(Rgba32.HotPink); |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
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>(Configuration.Default, 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>(Configuration.Default, 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>(Configuration.Default, 10, 10); |
|
||||
Assert.False(this.image.Frames.Contains(frame)); |
|
||||
} |
|
||||
|
|
||||
public void Dispose() |
|
||||
{ |
|
||||
this.image.Dispose(); |
|
||||
this.collection.Dispose(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,115 @@ |
|||||
|
// // Copyright (c) Six Labors and contributors.
|
||||
|
// // Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
// // Copyright (c) Six Labors and contributors.
|
||||
|
// // Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
// // Copyright (c) Six Labors and contributors.
|
||||
|
// // Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
using SixLabors.ImageSharp.PixelFormats; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Tests.PixelFormats |
||||
|
{ |
||||
|
public abstract partial class PixelConverterTests |
||||
|
{ |
||||
|
public static class ReferenceImplementations |
||||
|
{ |
||||
|
public static Rgba32 MakeRgba32(byte r, byte g, byte b, byte a) |
||||
|
{ |
||||
|
Rgba32 d = default; |
||||
|
d.R = r; |
||||
|
d.G = g; |
||||
|
d.B = b; |
||||
|
d.A = a; |
||||
|
return d; |
||||
|
} |
||||
|
|
||||
|
public static Argb32 MakeArgb32(byte r, byte g, byte b, byte a) |
||||
|
{ |
||||
|
Argb32 d = default; |
||||
|
d.R = r; |
||||
|
d.G = g; |
||||
|
d.B = b; |
||||
|
d.A = a; |
||||
|
return d; |
||||
|
} |
||||
|
|
||||
|
public static Bgra32 MakeBgra32(byte r, byte g, byte b, byte a) |
||||
|
{ |
||||
|
Bgra32 d = default; |
||||
|
d.R = r; |
||||
|
d.G = g; |
||||
|
d.B = b; |
||||
|
d.A = a; |
||||
|
return d; |
||||
|
} |
||||
|
|
||||
|
internal static void To<TSourcePixel, TDestinationPixel>( |
||||
|
Configuration configuration, |
||||
|
ReadOnlySpan<TSourcePixel> sourcePixels, |
||||
|
Span<TDestinationPixel> destinationPixels) |
||||
|
where TSourcePixel : struct, IPixel<TSourcePixel> where TDestinationPixel : struct, IPixel<TDestinationPixel> |
||||
|
{ |
||||
|
Guard.NotNull(configuration, nameof(configuration)); |
||||
|
Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels)); |
||||
|
|
||||
|
int count = sourcePixels.Length; |
||||
|
ref TSourcePixel sourceRef = ref MemoryMarshal.GetReference(sourcePixels); |
||||
|
|
||||
|
if (typeof(TSourcePixel) == typeof(TDestinationPixel)) |
||||
|
{ |
||||
|
Span<TSourcePixel> uniformDest = |
||||
|
MemoryMarshal.Cast<TDestinationPixel, TSourcePixel>(destinationPixels); |
||||
|
sourcePixels.CopyTo(uniformDest); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// Gray8 and Gray16 are special implementations of IPixel in that they do not conform to the
|
||||
|
// standard RGBA colorspace format and must be converted from RGBA using the special ITU BT709 algorithm.
|
||||
|
// One of the requirements of FromScaledVector4/ToScaledVector4 is that it unaware of this and
|
||||
|
// packs/unpacks the pixel without and conversion so we employ custom methods do do this.
|
||||
|
if (typeof(TDestinationPixel) == typeof(Gray16)) |
||||
|
{ |
||||
|
ref Gray16 gray16Ref = ref MemoryMarshal.GetReference( |
||||
|
MemoryMarshal.Cast<TDestinationPixel, Gray16>(destinationPixels)); |
||||
|
for (int i = 0; i < count; i++) |
||||
|
{ |
||||
|
ref TSourcePixel sp = ref Unsafe.Add(ref sourceRef, i); |
||||
|
ref Gray16 dp = ref Unsafe.Add(ref gray16Ref, i); |
||||
|
dp.ConvertFromRgbaScaledVector4(sp.ToScaledVector4()); |
||||
|
} |
||||
|
|
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (typeof(TDestinationPixel) == typeof(Gray8)) |
||||
|
{ |
||||
|
ref Gray8 gray8Ref = ref MemoryMarshal.GetReference( |
||||
|
MemoryMarshal.Cast<TDestinationPixel, Gray8>(destinationPixels)); |
||||
|
for (int i = 0; i < count; i++) |
||||
|
{ |
||||
|
ref TSourcePixel sp = ref Unsafe.Add(ref sourceRef, i); |
||||
|
ref Gray8 dp = ref Unsafe.Add(ref gray8Ref, i); |
||||
|
dp.ConvertFromRgbaScaledVector4(sp.ToScaledVector4()); |
||||
|
} |
||||
|
|
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// Normal conversion
|
||||
|
ref TDestinationPixel destRef = ref MemoryMarshal.GetReference(destinationPixels); |
||||
|
for (int i = 0; i < count; i++) |
||||
|
{ |
||||
|
ref TSourcePixel sp = ref Unsafe.Add(ref sourceRef, i); |
||||
|
ref TDestinationPixel dp = ref Unsafe.Add(ref destRef, i); |
||||
|
dp.FromScaledVector4(sp.ToScaledVector4()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using SixLabors.ImageSharp.PixelFormats; |
||||
|
|
||||
|
using Xunit.Abstractions; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations |
||||
|
{ |
||||
|
public partial class PixelOperationsTests |
||||
|
{ |
||||
|
public class Bgra5551OperationsTests : PixelOperationsTests<Bgra5551> |
||||
|
{ |
||||
|
public Bgra5551OperationsTests(ITestOutputHelper output) |
||||
|
: base(output) |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue