mirror of https://github.com/SixLabors/ImageSharp
committed by
GitHub
110 changed files with 1462 additions and 1037 deletions
@ -0,0 +1,16 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.Advanced |
|||
{ |
|||
/// <summary>
|
|||
/// Encapsulates the properties for configuration
|
|||
/// </summary>
|
|||
internal interface IConfigurable |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the configuration.
|
|||
/// </summary>
|
|||
Configuration Configuration { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,61 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Text; |
|||
using SixLabors.ImageSharp.Advanced; |
|||
using SixLabors.ImageSharp.Formats; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
using SixLabors.Primitives; |
|||
|
|||
namespace SixLabors.ImageSharp.Helpers |
|||
{ |
|||
/// <summary>
|
|||
/// Extension methods over Image{TPixel}
|
|||
/// </summary>
|
|||
public static partial class ImageExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the bounds of the image.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The Pixel format.</typeparam>
|
|||
/// <param name="source">The source image</param>
|
|||
/// <returns>Returns the bounds of the image</returns>
|
|||
public static Rectangle Bounds<TPixel>(this Image<TPixel> source) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
=> new Rectangle(0, 0, source.Width, source.Height); |
|||
|
|||
/// <summary>
|
|||
/// Gets the bounds of the image.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The Pixel format.</typeparam>
|
|||
/// <param name="source">The source image</param>
|
|||
/// <returns>Returns the bounds of the image</returns>
|
|||
public static Rectangle Bounds<TPixel>(this ImageFrame<TPixel> source) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
=> new Rectangle(0, 0, source.Width, source.Height); |
|||
|
|||
/// <summary>
|
|||
/// Gets the size of the image.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The Pixel format.</typeparam>
|
|||
/// <param name="source">The source image</param>
|
|||
/// <returns>Returns the bounds of the image</returns>
|
|||
public static Size Size<TPixel>(this Image<TPixel> source) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
=> new Size(source.Width, source.Height); |
|||
|
|||
/// <summary>
|
|||
/// Gets the size of the image.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The Pixel format.</typeparam>
|
|||
/// <param name="source">The source image</param>
|
|||
/// <returns>Returns the bounds of the image</returns>
|
|||
public static Size Size<TPixel>(this ImageFrame<TPixel> source) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
=> new Size(source.Width, source.Height); |
|||
} |
|||
} |
|||
@ -1,19 +0,0 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using SixLabors.ImageSharp.Formats; |
|||
using SixLabors.ImageSharp.MetaData; |
|||
|
|||
namespace SixLabors.ImageSharp |
|||
{ |
|||
/// <summary>
|
|||
/// Encapsulates the basic properties and methods required to manipulate images.
|
|||
/// </summary>
|
|||
internal interface IImage : IImageBase |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the meta data of the image.
|
|||
/// </summary>
|
|||
ImageMetaData MetaData { get; } |
|||
} |
|||
} |
|||
@ -1,28 +0,0 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using SixLabors.Primitives; |
|||
|
|||
namespace SixLabors.ImageSharp |
|||
{ |
|||
/// <summary>
|
|||
/// Encapsulates the basic properties and methods required to manipulate images.
|
|||
/// </summary>
|
|||
public interface IImageBase |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the width in pixels.
|
|||
/// </summary>
|
|||
int Width { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the height in pixels.
|
|||
/// </summary>
|
|||
int Height { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the configuration providing initialization code which allows extending the library.
|
|||
/// </summary>
|
|||
Configuration Configuration { get; } |
|||
} |
|||
} |
|||
@ -1,18 +0,0 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using SixLabors.ImageSharp.MetaData; |
|||
|
|||
namespace SixLabors.ImageSharp |
|||
{ |
|||
/// <summary>
|
|||
/// Encapsulates the basic properties and methods required to manipulate images.
|
|||
/// </summary>
|
|||
internal interface IImageFrame : IImageBase |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the meta data of the image.
|
|||
/// </summary>
|
|||
ImageFrameMetaData MetaData { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,84 @@ |
|||
// 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.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp |
|||
{ |
|||
/// <summary>
|
|||
/// Encapsulates an imaged collection of frames.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
|
|||
public interface IImageFrameCollection<TPixel> : IEnumerable<ImageFrame<TPixel>> |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the count.
|
|||
/// </summary>
|
|||
int Count { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the root frame.
|
|||
/// </summary>
|
|||
ImageFrame<TPixel> RootFrame { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the <see cref="ImageFrame{TPixel}"/> at the specified index.
|
|||
/// </summary>
|
|||
/// <value>
|
|||
/// The <see cref="ImageFrame{TPixel}"/>.
|
|||
/// </value>
|
|||
/// <param name="index">The index.</param>
|
|||
/// <returns>The <see cref="ImageFrame{TPixel}"/> at the specified index.</returns>
|
|||
ImageFrame<TPixel> this[int index] { get; set; } |
|||
|
|||
/// <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>
|
|||
/// Inserts the <paramref name="frame"/> to the <seealso cref="Image{TPixel}"/> at the specified <paramref name="index"/>.
|
|||
/// </summary>
|
|||
/// <param name="index"> The zero-based index at which item should be inserted..</param>
|
|||
/// <param name="frame">The <seealso cref="ImageFrame{TPixel}"/> to insert into the <seealso cref="Image{TPixel}"/>.</param>
|
|||
void Insert(int index, ImageFrame<TPixel> frame); |
|||
|
|||
/// <summary>
|
|||
/// Removes the <seealso cref="ImageFrame{TPixel}"/> from the <seealso cref="Image{TPixel}"/> at the specified index.
|
|||
/// </summary>
|
|||
/// <param name="index">The zero-based index of the item to remove.</param>
|
|||
/// <exception cref="InvalidOperationException">Cannot remove last frame.</exception>
|
|||
void RemoveAt(int index); |
|||
|
|||
/// <summary>
|
|||
/// Adds the specified frame.
|
|||
/// </summary>
|
|||
/// <param name="frame">The frame.</param>
|
|||
/// <exception cref="ArgumentException">Frame must have the same dimensions as the image - frame</exception>
|
|||
void Add(ImageFrame<TPixel> frame); |
|||
|
|||
/// <summary>
|
|||
/// Determines whether the <seealso cref="Image{TPixel}"/> contains the <paramref name="frame"/>.
|
|||
/// </summary>
|
|||
/// <param name="frame">The frame.</param>
|
|||
/// <returns>
|
|||
/// <c>true</c> if the <seealso cref="Image{TPixel}"/> the specified frame; otherwise, <c>false</c>.
|
|||
/// </returns>
|
|||
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); |
|||
} |
|||
} |
|||
@ -1,333 +0,0 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Diagnostics; |
|||
using System.Runtime.CompilerServices; |
|||
using SixLabors.ImageSharp.Advanced; |
|||
using SixLabors.ImageSharp.Memory; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
using SixLabors.ImageSharp.Processing; |
|||
using SixLabors.Primitives; |
|||
|
|||
namespace SixLabors.ImageSharp |
|||
{ |
|||
/// <summary>
|
|||
/// The base class of all images. Encapsulates the basic properties and methods required to manipulate
|
|||
/// images in different pixel formats.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
public abstract class ImageBase<TPixel> : IImageBase, IDisposable, IPixelSource<TPixel> |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
#pragma warning disable SA1401 // Fields must be private
|
|||
/// <summary>
|
|||
/// The image pixels. Not private as Buffer2D requires an array in its constructor.
|
|||
/// </summary>
|
|||
internal TPixel[] PixelBuffer; |
|||
#pragma warning restore SA1401 // Fields must be private
|
|||
|
|||
/// <summary>
|
|||
/// A value indicating whether this instance of the given entity has been disposed.
|
|||
/// </summary>
|
|||
/// <value><see langword="true"/> if this instance has been disposed; otherwise, <see langword="false"/>.</value>
|
|||
/// <remarks>
|
|||
/// If the entity is disposed, it must not be disposed a second time. The isDisposed field is set the first time the entity
|
|||
/// is disposed. If the isDisposed field is true, then the Dispose() method will not dispose again. This help not to prolong the entity's
|
|||
/// life in the Garbage Collector.
|
|||
/// </remarks>
|
|||
private bool isDisposed; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="ImageBase{TPixel}"/> class.
|
|||
/// </summary>
|
|||
/// <param name="configuration">
|
|||
/// The configuration providing initialization code which allows extending the library.
|
|||
/// </param>
|
|||
protected ImageBase(Configuration configuration) |
|||
{ |
|||
this.Configuration = configuration ?? Configuration.Default; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="ImageBase{TPixel}"/> class.
|
|||
/// </summary>
|
|||
/// <param name="configuration">
|
|||
/// The configuration providing initialization code which allows extending the library.
|
|||
/// </param>
|
|||
/// <param name="width">The width of the image in pixels.</param>
|
|||
/// <param name="height">The height of the image in pixels.</param>
|
|||
/// <exception cref="ArgumentOutOfRangeException">
|
|||
/// Thrown if either <paramref name="width"/> or <paramref name="height"/> are less than or equal to 0.
|
|||
/// </exception>
|
|||
protected ImageBase(Configuration configuration, int width, int height) |
|||
: this(configuration) |
|||
{ |
|||
Guard.MustBeGreaterThan(width, 0, nameof(width)); |
|||
Guard.MustBeGreaterThan(height, 0, nameof(height)); |
|||
|
|||
this.Width = width; |
|||
this.Height = height; |
|||
this.RentPixels(); |
|||
this.ClearPixels(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="ImageBase{TPixel}"/> class.
|
|||
/// </summary>
|
|||
/// <param name="other">
|
|||
/// The other <see cref="ImageBase{TPixel}"/> to create this instance from.
|
|||
/// </param>
|
|||
/// <exception cref="System.ArgumentNullException">
|
|||
/// Thrown if the given <see cref="ImageBase{TPixel}"/> is null.
|
|||
/// </exception>
|
|||
protected ImageBase(ImageBase<TPixel> other) |
|||
: this(other.Configuration) |
|||
{ |
|||
Guard.NotNull(other, nameof(other), "Other image cannot be null."); |
|||
|
|||
this.Width = other.Width; |
|||
this.Height = other.Height; |
|||
this.CopyProperties(other); |
|||
|
|||
// Rent then copy the pixels. Unsafe.CopyBlock gives us a nice speed boost here.
|
|||
this.RentPixels(); |
|||
|
|||
other.GetPixelSpan().CopyTo(this.GetPixelSpan()); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
Span<TPixel> IPixelSource<TPixel>.Span => new Span<TPixel>(this.PixelBuffer, 0, this.Width * this.Height); |
|||
|
|||
/// <inheritdoc/>
|
|||
public int Width { get; private set; } |
|||
|
|||
/// <inheritdoc/>
|
|||
public int Height { get; private set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the configuration providing initialization code which allows extending the library.
|
|||
/// </summary>
|
|||
public Configuration Configuration { get; private set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the pixel at the specified position.
|
|||
/// </summary>
|
|||
/// <param name="x">The x-coordinate of the pixel. Must be greater than or equal to zero and less than the width of the image.</param>
|
|||
/// <param name="y">The y-coordinate of the pixel. Must be greater than or equal to zero and less than the height of the image.</param>
|
|||
/// <returns>The <see typeparam="TPixel"/> at the specified position.</returns>
|
|||
public TPixel this[int x, int y] |
|||
{ |
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
get |
|||
{ |
|||
this.CheckCoordinates(x, y); |
|||
return this.PixelBuffer[(y * this.Width) + x]; |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
set |
|||
{ |
|||
this.CheckCoordinates(x, y); |
|||
this.PixelBuffer[(y * this.Width) + x] = value; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets a reference to the pixel at the specified position.
|
|||
/// </summary>
|
|||
/// <param name="x">The x-coordinate of the pixel. Must be greater than or equal to zero and less than the width of the image.</param>
|
|||
/// <param name="y">The y-coordinate of the pixel. Must be greater than or equal to zero and less than the height of the image.</param>
|
|||
/// <returns>The <see typeparam="TPixel"/> at the specified position.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
internal ref TPixel GetPixelReference(int x, int y) |
|||
{ |
|||
this.CheckCoordinates(x, y); |
|||
return ref this.PixelBuffer[(y * this.Width) + x]; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Clones the image
|
|||
/// </summary>
|
|||
/// <returns>A new items which is a clone of the original.</returns>
|
|||
public ImageBase<TPixel> Clone() |
|||
{ |
|||
return this.CloneImageBase(); |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
public void Dispose() |
|||
{ |
|||
this.Dispose(true); |
|||
|
|||
// This object will be cleaned up by the Dispose method.
|
|||
// Therefore, you should call GC.SuppressFinalize to
|
|||
// take this object off the finalization queue
|
|||
// and prevent finalization code for this object
|
|||
// from executing a second time.
|
|||
GC.SuppressFinalize(this); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Locks the image providing access to the pixels.
|
|||
/// <remarks>
|
|||
/// It is imperative that the accessor is correctly disposed off after use.
|
|||
/// </remarks>
|
|||
/// </summary>
|
|||
/// <returns>The <see cref="PixelAccessor{TPixel}"/></returns>
|
|||
internal PixelAccessor<TPixel> Lock() |
|||
{ |
|||
return new PixelAccessor<TPixel>(this); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Copies the pixels to another <see cref="PixelAccessor{TPixel}"/> of the same size.
|
|||
/// </summary>
|
|||
/// <param name="target">The target pixel buffer accessor.</param>
|
|||
internal void CopyTo(PixelAccessor<TPixel> target) |
|||
{ |
|||
SpanHelper.Copy(this.GetPixelSpan(), target.PixelBuffer.Span); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Switches the buffers used by the image and the PixelAccessor meaning that the Image will "own" the buffer from the PixelAccessor and the PixelAccessor will now own the Images buffer.
|
|||
/// </summary>
|
|||
/// <param name="pixelSource">The pixel source.</param>
|
|||
internal void SwapPixelsBuffers(PixelAccessor<TPixel> pixelSource) |
|||
{ |
|||
Guard.NotNull(pixelSource, nameof(pixelSource)); |
|||
|
|||
int newWidth = pixelSource.Width; |
|||
int newHeight = pixelSource.Height; |
|||
|
|||
// Push my memory into the accessor (which in turn unpins the old buffer ready for the images use)
|
|||
TPixel[] newPixels = pixelSource.ReturnCurrentColorsAndReplaceThemInternally(this.Width, this.Height, this.PixelBuffer); |
|||
this.Width = newWidth; |
|||
this.Height = newHeight; |
|||
this.PixelBuffer = newPixels; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Switches the buffers used by the image and the pixelSource meaning that the Image will "own" the buffer from the pixelSource and the pixelSource will now own the Images buffer.
|
|||
/// </summary>
|
|||
/// <param name="pixelSource">The pixel source.</param>
|
|||
internal void SwapPixelsData(ImageBase<TPixel> pixelSource) |
|||
{ |
|||
Guard.NotNull(pixelSource, nameof(pixelSource)); |
|||
|
|||
int newWidth = pixelSource.Width; |
|||
int newHeight = pixelSource.Height; |
|||
TPixel[] newPixels = pixelSource.PixelBuffer; |
|||
|
|||
pixelSource.PixelBuffer = this.PixelBuffer; |
|||
pixelSource.Width = this.Width; |
|||
pixelSource.Height = this.Height; |
|||
|
|||
this.Width = newWidth; |
|||
this.Height = newHeight; |
|||
this.PixelBuffer = newPixels; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Clones the image
|
|||
/// </summary>
|
|||
/// <returns>A new items which is a clone of the original.</returns>
|
|||
protected abstract ImageBase<TPixel> CloneImageBase(); |
|||
|
|||
/// <summary>
|
|||
/// Copies the properties from the other <see cref="IImageBase"/>.
|
|||
/// </summary>
|
|||
/// <param name="other">
|
|||
/// The other <see cref="IImageBase"/> to copy the properties from.
|
|||
/// </param>
|
|||
protected void CopyProperties(IImageBase other) |
|||
{ |
|||
DebugGuard.NotNull(other, nameof(other)); |
|||
|
|||
this.Configuration = other.Configuration; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Disposes the object and frees resources for the Garbage Collector.
|
|||
/// </summary>
|
|||
/// <param name="disposing">If true, the object gets disposed.</param>
|
|||
protected virtual void Dispose(bool disposing) |
|||
{ |
|||
if (this.isDisposed) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
if (disposing) |
|||
{ |
|||
this.ReturnPixels(); |
|||
} |
|||
|
|||
// Note disposing is done.
|
|||
this.isDisposed = true; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Rents the pixel array from the pool.
|
|||
/// </summary>
|
|||
private void RentPixels() |
|||
{ |
|||
this.PixelBuffer = PixelDataPool<TPixel>.Rent(this.Width * this.Height); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the rented pixel array back to the pool.
|
|||
/// </summary>
|
|||
private void ReturnPixels() |
|||
{ |
|||
PixelDataPool<TPixel>.Return(this.PixelBuffer); |
|||
this.PixelBuffer = null; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Clears the pixel array.
|
|||
/// </summary>
|
|||
private void ClearPixels() |
|||
{ |
|||
Array.Clear(this.PixelBuffer, 0, this.Width * this.Height); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Checks the coordinates to ensure they are within bounds.
|
|||
/// </summary>
|
|||
/// <param name="y">The y-coordinate of the pixel. Must be greater than zero and less than the height of the image.</param>
|
|||
/// <exception cref="ArgumentOutOfRangeException">
|
|||
/// Thrown if the coordinates are not within the bounds of the image.
|
|||
/// </exception>
|
|||
[Conditional("DEBUG")] |
|||
private void CheckCoordinates(int y) |
|||
{ |
|||
if (y < 0 || y >= this.Height) |
|||
{ |
|||
throw new ArgumentOutOfRangeException(nameof(y), y, $"{y} is outwith the image bounds."); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Checks the coordinates to ensure they are within bounds.
|
|||
/// </summary>
|
|||
/// <param name="x">The x-coordinate of the pixel. Must be greater than zero and less than the width of the image.</param>
|
|||
/// <param name="y">The y-coordinate of the pixel. Must be greater than zero and less than the height of the image.</param>
|
|||
/// <exception cref="ArgumentOutOfRangeException">
|
|||
/// Thrown if the coordinates are not within the bounds of the image.
|
|||
/// </exception>
|
|||
[Conditional("DEBUG")] |
|||
private void CheckCoordinates(int x, int y) |
|||
{ |
|||
if (x < 0 || x >= this.Width) |
|||
{ |
|||
throw new ArgumentOutOfRangeException(nameof(x), x, $"{x} is outwith the image bounds."); |
|||
} |
|||
|
|||
if (y < 0 || y >= this.Height) |
|||
{ |
|||
throw new ArgumentOutOfRangeException(nameof(y), y, $"{y} is outwith the image bounds."); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,52 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.IO; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Threading.Tasks; |
|||
using SixLabors.ImageSharp.Advanced; |
|||
using SixLabors.ImageSharp.Formats; |
|||
using SixLabors.ImageSharp.Memory; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp |
|||
{ |
|||
/// <content>
|
|||
/// Adds static methods allowing the creation of new image from raw pixel data.
|
|||
/// </content>
|
|||
public static partial class ImageFrame |
|||
{ |
|||
/// <summary>
|
|||
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given byte array in <typeparamref name="TPixel"/> format.
|
|||
/// </summary>
|
|||
/// <param name="data">The byte array containing image data.</param>
|
|||
/// <param name="width">The width of the final image.</param>
|
|||
/// <param name="height">The height of the final image.</param>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
|
|||
public static ImageFrame<TPixel> LoadPixelData<TPixel>(Span<byte> data, int width, int height) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
=> LoadPixelData(data.NonPortableCast<byte, TPixel>(), width, height); |
|||
|
|||
/// <summary>
|
|||
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the raw <typeparamref name="TPixel"/> data.
|
|||
/// </summary>
|
|||
/// <param name="data">The Span containing the image Pixel data.</param>
|
|||
/// <param name="width">The width of the final image.</param>
|
|||
/// <param name="height">The height of the final image.</param>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
|
|||
public static ImageFrame<TPixel> LoadPixelData<TPixel>(Span<TPixel> data, int width, int height) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
int count = width * height; |
|||
Guard.MustBeGreaterThanOrEqualTo(data.Length, count, nameof(data)); |
|||
|
|||
var image = new ImageFrame<TPixel>(width, height); |
|||
SpanHelper.Copy(data, image.GetPixelSpan(), count); |
|||
|
|||
return image; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,166 @@ |
|||
// 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.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp |
|||
{ |
|||
/// <summary>
|
|||
/// Encapsulates an imaged collection of frames.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
|
|||
internal sealed class ImageFrameCollection<TPixel> : IImageFrameCollection<TPixel>, IDisposable |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
private readonly IList<ImageFrame<TPixel>> frames = new List<ImageFrame<TPixel>>(); |
|||
|
|||
internal ImageFrameCollection(int width, int height) |
|||
{ |
|||
this.Add(new ImageFrame<TPixel>(width, height)); |
|||
} |
|||
|
|||
internal ImageFrameCollection(IEnumerable<ImageFrame<TPixel>> frames) |
|||
{ |
|||
Guard.NotNullOrEmpty(frames, nameof(frames)); |
|||
foreach (ImageFrame<TPixel> f in frames) |
|||
{ |
|||
this.Add(f); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the count.
|
|||
/// </summary>
|
|||
public int Count => this.frames.Count; |
|||
|
|||
/// <summary>
|
|||
/// Gets the root frame.
|
|||
/// </summary>
|
|||
public ImageFrame<TPixel> RootFrame => this.frames.Count > 0 ? this.frames[0] : null; |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the <see cref="ImageFrame{TPixel}"/> at the specified index.
|
|||
/// </summary>
|
|||
/// <value>
|
|||
/// The <see cref="ImageFrame{TPixel}"/>.
|
|||
/// </value>
|
|||
/// <param name="index">The index.</param>
|
|||
/// <returns>The <see cref="ImageFrame{TPixel}"/> at the specified index.</returns>
|
|||
public ImageFrame<TPixel> this[int index] |
|||
{ |
|||
get => this.frames[index]; |
|||
|
|||
set |
|||
{ |
|||
this.ValidateFrame(value); |
|||
this.frames[index] = value; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Determines the index of a specific <paramref name="frame"/> in the <seealso cref="Image{TPixel}"/>.
|
|||
/// </summary>
|
|||
/// <param name="frame">The <seealso cref="ImageFrame{TPixel}"/> to locate in the <seealso cref="Image{TPixel}"/>.</param>
|
|||
/// <returns>The index of item if found in the list; otherwise, -1.</returns>
|
|||
public int IndexOf(ImageFrame<TPixel> frame) => this.frames.IndexOf(frame); |
|||
|
|||
/// <summary>
|
|||
/// Inserts the <paramref name="frame"/> to the <seealso cref="Image{TPixel}"/> at the specified <paramref name="index"/>.
|
|||
/// </summary>
|
|||
/// <param name="index"> The zero-based index at which item should be inserted..</param>
|
|||
/// <param name="frame">The <seealso cref="ImageFrame{TPixel}"/> to insert into the <seealso cref="Image{TPixel}"/>.</param>
|
|||
public void Insert(int index, ImageFrame<TPixel> frame) |
|||
{ |
|||
this.ValidateFrame(frame); |
|||
this.frames.Insert(index, frame); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Removes the <seealso cref="ImageFrame{TPixel}"/> from the <seealso cref="Image{TPixel}"/> at the specified index.
|
|||
/// </summary>
|
|||
/// <param name="index">The zero-based index of the item to remove.</param>
|
|||
/// <exception cref="InvalidOperationException">Cannot remove last frame.</exception>
|
|||
public void RemoveAt(int index) |
|||
{ |
|||
if (index == 0 && this.Count == 1) |
|||
{ |
|||
throw new InvalidOperationException("Cannot remove last frame."); |
|||
} |
|||
|
|||
this.frames.RemoveAt(index); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Adds the specified frame.
|
|||
/// </summary>
|
|||
/// <param name="frame">The frame.</param>
|
|||
/// <exception cref="ArgumentException">Frame must have the same dimensions as the image - frame</exception>
|
|||
public void Add(ImageFrame<TPixel> frame) |
|||
{ |
|||
this.ValidateFrame(frame); |
|||
this.frames.Add(frame); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Determines whether the <seealso cref="Image{TPixel}"/> contains the <paramref name="frame"/>.
|
|||
/// </summary>
|
|||
/// <param name="frame">The frame.</param>
|
|||
/// <returns>
|
|||
/// <c>true</c> if the <seealso cref="Image{TPixel}"/> the specified frame; otherwise, <c>false</c>.
|
|||
/// </returns>
|
|||
public bool Contains(ImageFrame<TPixel> frame) |
|||
{ |
|||
return this.frames.Contains(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>
|
|||
public bool Remove(ImageFrame<TPixel> frame) |
|||
{ |
|||
if (this.Count == 1 && this.frames.Contains(frame)) |
|||
{ |
|||
throw new InvalidOperationException("Cannot remove last frame."); |
|||
} |
|||
|
|||
return this.frames.Remove(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)); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public void Dispose() |
|||
{ |
|||
foreach (ImageFrame<TPixel> f in this.frames) |
|||
{ |
|||
f.Dispose(); |
|||
} |
|||
|
|||
this.frames.Clear(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,54 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Diagnostics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Threading.Tasks; |
|||
using SixLabors.ImageSharp.Advanced; |
|||
using SixLabors.ImageSharp.Memory; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
using Unsafe = System.Runtime.CompilerServices.Unsafe; |
|||
|
|||
namespace SixLabors.ImageSharp |
|||
{ |
|||
/// <summary>
|
|||
/// Helper methods fro acccess pixel accessors
|
|||
/// </summary>
|
|||
internal static class PixelAccessorExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// Locks the image providing access to the pixels.
|
|||
/// <remarks>
|
|||
/// It is imperative that the accessor is correctly disposed off after use.
|
|||
/// </remarks>
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
|
|||
/// <param name="frame">The frame.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="PixelAccessor{TPixel}" />
|
|||
/// </returns>
|
|||
internal static PixelAccessor<TPixel> Lock<TPixel>(this IPixelSource<TPixel> frame) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
return new PixelAccessor<TPixel>(frame); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Locks the image providing access to the pixels.
|
|||
/// <remarks>
|
|||
/// It is imperative that the accessor is correctly disposed off after use.
|
|||
/// </remarks>
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
|
|||
/// <param name="image">The image.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="PixelAccessor{TPixel}" />
|
|||
/// </returns>
|
|||
internal static PixelAccessor<TPixel> Lock<TPixel>(this Image<TPixel> image) |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
return image.Frames.RootFrame.Lock(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,219 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using SixLabors.ImageSharp.Advanced; |
|||
using Xunit; |
|||
|
|||
namespace SixLabors.ImageSharp.Tests |
|||
{ |
|||
public class ImageFramesCollectionTests |
|||
{ |
|||
[Fact] |
|||
public void ImageFramesaLwaysHaveOneFrame() |
|||
{ |
|||
var collection = new ImageFrameCollection<Rgba32>(10, 10); |
|||
Assert.Equal(1, collection.Count); |
|||
} |
|||
|
|||
[Fact] |
|||
public void AddNewFrame_FramesMustHaveSameSize() |
|||
{ |
|||
var collection = new ImageFrameCollection<Rgba32>(10, 10); |
|||
|
|||
ArgumentException ex = Assert.Throws<ArgumentException>(() => |
|||
{ |
|||
collection.Add(new ImageFrame<Rgba32>(1, 1)); |
|||
}); |
|||
|
|||
Assert.Equal("Frame must have the same dimensions as the image.\r\nParameter name: frame", ex.Message); |
|||
} |
|||
|
|||
[Fact] |
|||
public void AddNewFrame_FramesNotBeNull() |
|||
{ |
|||
var collection = new ImageFrameCollection<Rgba32>(10, 10); |
|||
|
|||
ArgumentNullException ex = Assert.Throws<ArgumentNullException>(() => |
|||
{ |
|||
collection.Add(null); |
|||
}); |
|||
|
|||
Assert.Equal("Value cannot be null.\r\nParameter name: frame", ex.Message); |
|||
} |
|||
|
|||
[Fact] |
|||
public void InsertNewFrame_FramesMustHaveSameSize() |
|||
{ |
|||
var collection = new ImageFrameCollection<Rgba32>(10, 10); |
|||
|
|||
ArgumentException ex = Assert.Throws<ArgumentException>(() => |
|||
{ |
|||
collection.Insert(1, new ImageFrame<Rgba32>(1, 1)); |
|||
}); |
|||
|
|||
Assert.Equal("Frame must have the same dimensions as the image.\r\nParameter name: frame", ex.Message); |
|||
} |
|||
|
|||
[Fact] |
|||
public void InsertNewFrame_FramesNotBeNull() |
|||
{ |
|||
var collection = new ImageFrameCollection<Rgba32>(10, 10); |
|||
|
|||
ArgumentNullException ex = Assert.Throws<ArgumentNullException>(() => |
|||
{ |
|||
collection.Insert(1, null); |
|||
}); |
|||
|
|||
Assert.Equal("Value cannot be null.\r\nParameter name: frame", ex.Message); |
|||
} |
|||
|
|||
[Fact] |
|||
public void SetFrameAtIndex_FramesMustHaveSameSize() |
|||
{ |
|||
var collection = new ImageFrameCollection<Rgba32>(10, 10); |
|||
|
|||
ArgumentException ex = Assert.Throws<ArgumentException>(() => |
|||
{ |
|||
collection[0] = new ImageFrame<Rgba32>(1, 1); |
|||
}); |
|||
|
|||
Assert.Equal("Frame must have the same dimensions as the image.\r\nParameter name: frame", ex.Message); |
|||
} |
|||
|
|||
[Fact] |
|||
public void SetFrameAtIndex_FramesNotBeNull() |
|||
{ |
|||
var collection = new ImageFrameCollection<Rgba32>(10, 10); |
|||
|
|||
ArgumentNullException ex = Assert.Throws<ArgumentNullException>(() => |
|||
{ |
|||
collection[0] = null; |
|||
}); |
|||
|
|||
Assert.Equal("Value cannot be null.\r\nParameter name: frame", ex.Message); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Constructor_FramesMustHaveSameSize() |
|||
{ |
|||
|
|||
ArgumentException ex = Assert.Throws<ArgumentException>(() => |
|||
{ |
|||
var collection = new ImageFrameCollection<Rgba32>(new[] { |
|||
new ImageFrame<Rgba32>(10,10), |
|||
new ImageFrame<Rgba32>(1,1), |
|||
}); |
|||
}); |
|||
|
|||
Assert.Equal("Frame must have the same dimensions as the image.\r\nParameter name: frame", ex.Message); |
|||
} |
|||
|
|||
[Fact] |
|||
public void RemoveAtFrame_ThrowIfRemovingLastFrame() |
|||
{ |
|||
var collection = new ImageFrameCollection<Rgba32>(new[] { |
|||
new ImageFrame<Rgba32>(10,10) |
|||
}); |
|||
|
|||
InvalidOperationException ex = Assert.Throws<InvalidOperationException>(() => |
|||
{ |
|||
collection.RemoveAt(0); |
|||
}); |
|||
Assert.Equal("Cannot remove last frame.", ex.Message); |
|||
} |
|||
|
|||
[Fact] |
|||
public void RemoveAtFrame_CanRemoveFrameZeroIfMultipleFramesExist() |
|||
{ |
|||
|
|||
var collection = new ImageFrameCollection<Rgba32>(new[] { |
|||
new ImageFrame<Rgba32>(10,10), |
|||
new ImageFrame<Rgba32>(10,10), |
|||
}); |
|||
|
|||
collection.RemoveAt(0); |
|||
Assert.Equal(1, collection.Count); |
|||
} |
|||
|
|||
[Fact] |
|||
public void RemoveFrame_ThrowIfRemovingLastFrame() |
|||
{ |
|||
var collection = new ImageFrameCollection<Rgba32>(new[] { |
|||
new ImageFrame<Rgba32>(10,10) |
|||
}); |
|||
|
|||
InvalidOperationException ex = Assert.Throws<InvalidOperationException>(() => |
|||
{ |
|||
collection.Remove(collection[0]); |
|||
}); |
|||
Assert.Equal("Cannot remove last frame.", ex.Message); |
|||
} |
|||
|
|||
[Fact] |
|||
public void RemoveFrame_CanRemoveFrameZeroIfMultipleFramesExist() |
|||
{ |
|||
|
|||
var collection = new ImageFrameCollection<Rgba32>(new[] { |
|||
new ImageFrame<Rgba32>(10,10), |
|||
new ImageFrame<Rgba32>(10,10), |
|||
}); |
|||
|
|||
collection.Remove(collection[0]); |
|||
Assert.Equal(1, collection.Count); |
|||
} |
|||
|
|||
[Fact] |
|||
public void RootFrameIsFrameAtIndexZero() |
|||
{ |
|||
var collection = new ImageFrameCollection<Rgba32>(new[] { |
|||
new ImageFrame<Rgba32>(10,10), |
|||
new ImageFrame<Rgba32>(10,10), |
|||
}); |
|||
|
|||
Assert.Equal(collection.RootFrame, collection[0]); |
|||
} |
|||
|
|||
[Fact] |
|||
public void ConstructorPopulatesFrames() |
|||
{ |
|||
var collection = new ImageFrameCollection<Rgba32>(new[] { |
|||
new ImageFrame<Rgba32>(10,10), |
|||
new ImageFrame<Rgba32>(10,10), |
|||
}); |
|||
|
|||
Assert.Equal(2, collection.Count); |
|||
} |
|||
|
|||
[Fact] |
|||
public void DisposeClearsCollection() |
|||
{ |
|||
var collection = new ImageFrameCollection<Rgba32>(new[] { |
|||
new ImageFrame<Rgba32>(10,10), |
|||
new ImageFrame<Rgba32>(10,10), |
|||
}); |
|||
|
|||
collection.Dispose(); |
|||
|
|||
Assert.Equal(0, collection.Count); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Dispose_DisposesAllInnerFrames() |
|||
{ |
|||
var collection = new ImageFrameCollection<Rgba32>(new[] { |
|||
new ImageFrame<Rgba32>(10,10), |
|||
new ImageFrame<Rgba32>(10,10), |
|||
}); |
|||
|
|||
IPixelSource<Rgba32>[] framesSnapShot = collection.OfType<IPixelSource<Rgba32>>().ToArray(); |
|||
collection.Dispose(); |
|||
|
|||
Assert.All(framesSnapShot, f => |
|||
{ |
|||
// the pixel source of the frame is null after its been disposed.
|
|||
Assert.Null(f.PixelBuffer); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue