diff --git a/.gitattributes b/.gitattributes
index adef3cc9e..f39b679c5 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -50,7 +50,7 @@
*.svg text eol=lf
*.targets text eol=lf
*.tt text eol=lf
-*.ttinclude text eol=lf
+*.ttinclude text eol=crlf
*.txt text eol=lf
*.vb text eol=lf
*.yml text eol=lf
diff --git a/src/ImageSharp/Common/Helpers/ImageMaths.cs b/src/ImageSharp/Common/Helpers/ImageMaths.cs
index 64bcb11c9..7460c9cac 100644
--- a/src/ImageSharp/Common/Helpers/ImageMaths.cs
+++ b/src/ImageSharp/Common/Helpers/ImageMaths.cs
@@ -36,6 +36,17 @@ namespace SixLabors.ImageSharp
public static ushort Get16BitBT709Luminance(ushort r, ushort g, ushort b) =>
(ushort)((r * .2126F) + (g * .7152F) + (b * .0722F));
+ ///
+ /// Gets the luminance from the rgb components using the formula as specified by ITU-R Recommendation BT.709.
+ ///
+ /// The red component.
+ /// The green component.
+ /// The blue component.
+ /// The .
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public static ushort Get16BitBT709Luminance(float r, float g, float b) =>
+ (ushort)((r * .2126F) + (g * .7152F) + (b * .0722F));
+
///
/// Scales a value from a 16 bit to it's 8 bit equivalent.
///
@@ -379,4 +390,4 @@ namespace SixLabors.ImageSharp
return GetBoundingRectangle(topLeft, bottomRight);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs
index 2c8190884..f3f2952b1 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs
@@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// (4) Packing pixels from the buffer.
/// These operations are executed in steps.
/// image rows are converted in one step,
- /// which means that size of the allocated memory is limited (does not depend on ).
+ /// which means that size of the allocated memory is limited (does not depend on ).
///
internal class JpegImagePostProcessor : IDisposable
{
@@ -177,4 +177,4 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Image.cs b/src/ImageSharp/Image.cs
index 3f3d6a62f..86fef715d 100644
--- a/src/ImageSharp/Image.cs
+++ b/src/ImageSharp/Image.cs
@@ -53,6 +53,11 @@ namespace SixLabors.ImageSharp
///
protected Configuration Configuration { get; }
+ ///
+ /// Gets the implementing the public property.
+ ///
+ protected abstract ImageFrameCollection NonGenericFrameCollection { get; }
+
///
public PixelTypeInfo PixelType { get; }
@@ -65,6 +70,11 @@ namespace SixLabors.ImageSharp
///
public ImageMetadata Metadata { get; }
+ ///
+ /// Gets the frames of the image as (non-generic) .
+ ///
+ public ImageFrameCollection Frames => this.NonGenericFrameCollection;
+
///
/// Gets the pixel buffer.
///
@@ -137,4 +147,4 @@ namespace SixLabors.ImageSharp
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/ImageFrame.LoadPixelData.cs b/src/ImageSharp/ImageFrame.LoadPixelData.cs
index 33dbe31df..9e90aeaf5 100644
--- a/src/ImageSharp/ImageFrame.LoadPixelData.cs
+++ b/src/ImageSharp/ImageFrame.LoadPixelData.cs
@@ -9,9 +9,9 @@ using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp
{
///
- /// Adds static methods allowing the creation of new image from raw pixel data.
+ /// Contains methods for loading raw pixel data.
///
- internal static class ImageFrame
+ public partial class ImageFrame
{
///
/// Create a new instance of the class from the given byte array in format.
@@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp
/// The height of the final image.
/// The pixel format.
/// A new .
- public static ImageFrame LoadPixelData(Configuration configuration, ReadOnlySpan data, int width, int height)
+ internal static ImageFrame LoadPixelData(Configuration configuration, ReadOnlySpan data, int width, int height)
where TPixel : struct, IPixel
=> LoadPixelData(configuration, MemoryMarshal.Cast(data), width, height);
@@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp
/// The height of the final image.
/// The pixel format.
/// A new .
- public static ImageFrame LoadPixelData(Configuration configuration, ReadOnlySpan data, int width, int height)
+ internal static ImageFrame LoadPixelData(Configuration configuration, ReadOnlySpan data, int width, int height)
where TPixel : struct, IPixel
{
int count = width * height;
@@ -48,4 +48,4 @@ namespace SixLabors.ImageSharp
return image;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/ImageFrame.cs b/src/ImageSharp/ImageFrame.cs
new file mode 100644
index 000000000..f3fe1ed8d
--- /dev/null
+++ b/src/ImageSharp/ImageFrame.cs
@@ -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
+{
+ ///
+ /// Represents a pixel-agnostic image frame containing all pixel data and .
+ /// 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.
+ ///
+ public abstract partial class ImageFrame : IDisposable
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The .
+ /// The width.
+ /// The height.
+ /// The .
+ 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;
+ }
+
+ ///
+ /// Gets the to use for buffer allocations.
+ ///
+ public MemoryAllocator MemoryAllocator { get; }
+
+ ///
+ /// Gets the instance associated with this .
+ ///
+ internal Configuration Configuration { get; }
+
+ ///
+ /// Gets the width.
+ ///
+ public int Width { get; private set; }
+
+ ///
+ /// Gets the height.
+ ///
+ public int Height { get; private set; }
+
+ ///
+ /// Gets the metadata of the frame.
+ ///
+ public ImageFrameMetadata Metadata { get; }
+
+ ///
+ /// Gets the size of the frame.
+ ///
+ /// The
+ public Size Size() => new Size(this.Width, this.Height);
+
+ ///
+ /// Gets the bounds of the frame.
+ ///
+ /// The
+ public Rectangle Bounds() => new Rectangle(0, 0, this.Width, this.Height);
+
+ ///
+ public abstract void Dispose();
+
+ internal abstract void CopyPixelsTo(Span destination)
+ where TDestinationPixel : struct, IPixel;
+
+ ///
+ /// Updates the size of the image frame.
+ ///
+ internal void UpdateSize(Size size)
+ {
+ this.Width = size.Width;
+ this.Height = size.Height;
+ }
+ }
+}
diff --git a/src/ImageSharp/ImageFrameCollection.cs b/src/ImageSharp/ImageFrameCollection.cs
index 7e516434c..c5bd02c79 100644
--- a/src/ImageSharp/ImageFrameCollection.cs
+++ b/src/ImageSharp/ImageFrameCollection.cs
@@ -4,149 +4,69 @@
using System;
using System.Collections;
using System.Collections.Generic;
-using SixLabors.ImageSharp.Advanced;
-using SixLabors.ImageSharp.Memory;
-using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp
{
///
- /// Encapsulates a collection of instances that make up an .
+ /// Encapsulates a pixel-agnostic collection of instances
+ /// that make up an .
///
- /// The type of the pixel.
- public sealed class ImageFrameCollection : IEnumerable>
- where TPixel : struct, IPixel
+ public abstract class ImageFrameCollection : IEnumerable
{
- private readonly IList> frames = new List>();
- private readonly Image parent;
-
- internal ImageFrameCollection(Image 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(parent.GetConfiguration(), width, height, backgroundColor));
- }
-
- internal ImageFrameCollection(Image parent, int width, int height, MemorySource memorySource)
- {
- this.parent = parent ?? throw new ArgumentNullException(nameof(parent));
-
- // Frames are already cloned within the caller
- this.frames.Add(new ImageFrame(parent.GetConfiguration(), width, height, memorySource));
- }
-
- internal ImageFrameCollection(Image parent, IEnumerable> frames)
- {
- Guard.NotNull(parent, nameof(parent));
- Guard.NotNull(frames, nameof(frames));
-
- this.parent = parent;
-
- // Frames are already cloned by the caller
- foreach (ImageFrame 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));
- }
- }
-
///
/// Gets the number of frames.
///
- public int Count => this.frames.Count;
+ public abstract int Count { get; }
///
/// Gets the root frame.
///
- public ImageFrame RootFrame => this.frames.Count > 0 ? this.frames[0] : null;
+ public ImageFrame RootFrame => this.NonGenericRootFrame;
+
+ ///
+ /// Gets the root frame. (Implements .)
+ ///
+ protected abstract ImageFrame NonGenericRootFrame { get; }
///
- /// Gets the at the specified index.
+ /// Gets the at the specified index.
///
///
- /// The .
+ /// The .
///
/// The index.
- /// The at the specified index.
- public ImageFrame this[int index] => this.frames[index];
+ /// The at the specified index.
+ public ImageFrame this[int index] => this.NonGenericGetFrame(index);
///
- /// Determines the index of a specific in the .
+ /// Determines the index of a specific in the .
///
- /// The to locate in the .
+ /// The to locate in the .
/// The index of item if found in the list; otherwise, -1.
- public int IndexOf(ImageFrame frame) => this.frames.IndexOf(frame);
+ public abstract int IndexOf(ImageFrame frame);
///
- /// Clones and inserts the into the at the specified .
+ /// Clones and inserts the into the at the specified .
///
/// The zero-based index to insert the frame at.
- /// The to clone and insert into the .
+ /// The to clone and insert into the .
/// Frame must have the same dimensions as the image.
- /// The cloned .
- public ImageFrame InsertFrame(int index, ImageFrame source)
- {
- this.ValidateFrame(source);
- ImageFrame clonedFrame = source.Clone(this.parent.GetConfiguration());
- this.frames.Insert(index, clonedFrame);
- return clonedFrame;
- }
+ /// The cloned .
+ public ImageFrame InsertFrame(int index, ImageFrame source) => this.NonGenericInsertFrame(index, source);
///
/// Clones the frame and appends the clone to the end of the collection.
///
/// The raw pixel data to generate the from.
/// The cloned .
- public ImageFrame AddFrame(ImageFrame source)
- {
- this.ValidateFrame(source);
- ImageFrame clonedFrame = source.Clone(this.parent.GetConfiguration());
- this.frames.Add(clonedFrame);
- return clonedFrame;
- }
-
- ///
- /// 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.
- ///
- /// The raw pixel data to generate the from.
- /// The new .
- public ImageFrame AddFrame(TPixel[] source)
- {
- Guard.NotNull(source, nameof(source));
-
- var frame = ImageFrame.LoadPixelData(
- this.parent.GetConfiguration(),
- new ReadOnlySpan(source),
- this.RootFrame.Width,
- this.RootFrame.Height);
- this.frames.Add(frame);
- return frame;
- }
+ public ImageFrame AddFrame(ImageFrame source) => this.NonGenericAddFrame(source);
///
/// Removes the frame at the specified index and frees all freeable resources associated with it.
///
/// The zero-based index of the frame to remove.
/// Cannot remove last frame.
- public void RemoveFrame(int index)
- {
- if (index == 0 && this.Count == 1)
- {
- throw new InvalidOperationException("Cannot remove last frame.");
- }
-
- ImageFrame frame = this.frames[index];
- this.frames.RemoveAt(index);
- frame.Dispose();
- }
+ public abstract void RemoveFrame(int index);
///
/// Determines whether the contains the .
@@ -155,24 +75,14 @@ namespace SixLabors.ImageSharp
///
/// true if the contains the specified frame; otherwise, false.
///
- public bool Contains(ImageFrame frame) => this.frames.Contains(frame);
+ public abstract bool Contains(ImageFrame frame);
///
/// Moves an from to .
///
/// The zero-based index of the frame to move.
/// The index to move the frame to.
- public void MoveFrame(int sourceIndex, int destinationIndex)
- {
- if (sourceIndex == destinationIndex)
- {
- return;
- }
-
- ImageFrame frameAtIndex = this.frames[sourceIndex];
- this.frames.RemoveAt(sourceIndex);
- this.frames.Insert(destinationIndex, frameAtIndex);
- }
+ public abstract void MoveFrame(int sourceIndex, int destinationIndex);
///
/// Removes the frame at the specified index and creates a new image with only the removed frame
@@ -181,19 +91,7 @@ namespace SixLabors.ImageSharp
/// The zero-based index of the frame to export.
/// Cannot remove last frame.
/// The new with the specified frame.
- public Image ExportFrame(int index)
- {
- ImageFrame 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(this.parent.GetConfiguration(), this.parent.Metadata.DeepClone(), new[] { frame });
- }
+ public Image ExportFrame(int index) => this.NonGenericExportFrame(index);
///
/// Creates an with only the frame at the specified index
@@ -201,12 +99,7 @@ namespace SixLabors.ImageSharp
///
/// The zero-based index of the frame to clone.
/// The new with the specified frame.
- public Image CloneFrame(int index)
- {
- ImageFrame frame = this[index];
- ImageFrame clonedFrame = frame.Clone();
- return new Image(this.parent.GetConfiguration(), this.parent.Metadata.DeepClone(), new[] { clonedFrame });
- }
+ public Image CloneFrame(int index) => this.NonGenericCloneFrame(index);
///
/// Creates a new and appends it to the end of the collection.
@@ -214,7 +107,7 @@ namespace SixLabors.ImageSharp
///
/// The new .
///
- public ImageFrame CreateFrame() => this.CreateFrame(default);
+ public ImageFrame CreateFrame() => this.NonGenericCreateFrame();
///
/// Creates a new and appends it to the end of the collection.
@@ -223,44 +116,67 @@ namespace SixLabors.ImageSharp
///
/// The new .
///
- public ImageFrame CreateFrame(TPixel backgroundColor)
- {
- var frame = new ImageFrame(
- this.parent.GetConfiguration(),
- this.RootFrame.Width,
- this.RootFrame.Height,
- backgroundColor);
- this.frames.Add(frame);
- return frame;
- }
+ public ImageFrame CreateFrame(Color backgroundColor) => this.NonGenericCreateFrame(backgroundColor);
- ///
- IEnumerator> IEnumerable>.GetEnumerator() => this.frames.GetEnumerator();
+ ///
+ public IEnumerator GetEnumerator() => this.NonGenericGetEnumerator();
///
- IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)this.frames).GetEnumerator();
+ IEnumerator IEnumerable.GetEnumerator() => this.NonGenericGetEnumerator();
- private void ValidateFrame(ImageFrame frame)
- {
- Guard.NotNull(frame, nameof(frame));
+ ///
+ /// Implements .
+ ///
+ /// The enumerator.
+ protected abstract IEnumerator NonGenericGetEnumerator();
+
+ ///
+ /// Implements the getter of the indexer.
+ ///
+ /// The index.
+ /// The frame.
+ protected abstract ImageFrame NonGenericGetFrame(int index);
+
+ ///
+ /// Implements .
+ ///
+ /// The index.
+ /// The frame.
+ /// The new frame.
+ protected abstract ImageFrame NonGenericInsertFrame(int index, ImageFrame source);
+
+ ///
+ /// Implements .
+ ///
+ /// The frame.
+ /// The new frame.
+ protected abstract ImageFrame NonGenericAddFrame(ImageFrame source);
+
+ ///
+ /// Implements .
+ ///
+ /// The index.
+ /// The new image.
+ protected abstract Image NonGenericExportFrame(int index);
- 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));
- }
- }
- }
+ ///
+ /// Implements .
+ ///
+ /// The index.
+ /// The new image.
+ protected abstract Image NonGenericCloneFrame(int index);
- internal void Dispose()
- {
- foreach (ImageFrame f in this.frames)
- {
- f.Dispose();
- }
+ ///
+ /// Implements .
+ ///
+ /// The new frame.
+ protected abstract ImageFrame NonGenericCreateFrame();
- this.frames.Clear();
- }
+ ///
+ /// Implements .
+ ///
+ /// The background color.
+ /// The new frame.
+ protected abstract ImageFrame NonGenericCreateFrame(Color backgroundColor);
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/ImageFrameCollection{TPixel}.cs b/src/ImageSharp/ImageFrameCollection{TPixel}.cs
new file mode 100644
index 000000000..ffd7225bd
--- /dev/null
+++ b/src/ImageSharp/ImageFrameCollection{TPixel}.cs
@@ -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
+{
+ ///
+ /// Encapsulates a pixel-specific collection of instances
+ /// that make up an .
+ ///
+ /// The type of the pixel.
+ public sealed class ImageFrameCollection : ImageFrameCollection, IEnumerable>
+ where TPixel : struct, IPixel
+ {
+ private readonly IList> frames = new List>();
+ private readonly Image parent;
+
+ internal ImageFrameCollection(Image 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(parent.GetConfiguration(), width, height, backgroundColor));
+ }
+
+ internal ImageFrameCollection(Image parent, int width, int height, MemorySource memorySource)
+ {
+ this.parent = parent ?? throw new ArgumentNullException(nameof(parent));
+
+ // Frames are already cloned within the caller
+ this.frames.Add(new ImageFrame(parent.GetConfiguration(), width, height, memorySource));
+ }
+
+ internal ImageFrameCollection(Image parent, IEnumerable> frames)
+ {
+ Guard.NotNull(parent, nameof(parent));
+ Guard.NotNull(frames, nameof(frames));
+
+ this.parent = parent;
+
+ // Frames are already cloned by the caller
+ foreach (ImageFrame 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));
+ }
+ }
+
+ ///
+ /// Gets the number of frames.
+ ///
+ public override int Count => this.frames.Count;
+
+ ///
+ /// Gets the root frame.
+ ///
+ public new ImageFrame RootFrame => this.frames.Count > 0 ? this.frames[0] : null;
+
+ ///
+ protected override ImageFrame NonGenericRootFrame => this.RootFrame;
+
+ ///
+ /// Gets the at the specified index.
+ ///
+ ///
+ /// The .
+ ///
+ /// The index.
+ /// The at the specified index.
+ public new ImageFrame this[int index] => this.frames[index];
+
+ ///
+ public override int IndexOf(ImageFrame frame)
+ {
+ return frame is ImageFrame specific ? this.IndexOf(specific) : -1;
+ }
+
+ ///
+ /// Determines the index of a specific in the .
+ ///
+ /// The to locate in the .
+ /// The index of item if found in the list; otherwise, -1.
+ public int IndexOf(ImageFrame frame) => this.frames.IndexOf(frame);
+
+ ///
+ /// Clones and inserts the into the at the specified .
+ ///
+ /// The zero-based index to insert the frame at.
+ /// The to clone and insert into the .
+ /// Frame must have the same dimensions as the image.
+ /// The cloned .
+ public ImageFrame InsertFrame(int index, ImageFrame source)
+ {
+ this.ValidateFrame(source);
+ ImageFrame clonedFrame = source.Clone(this.parent.GetConfiguration());
+ this.frames.Insert(index, clonedFrame);
+ return clonedFrame;
+ }
+
+ ///
+ /// Clones the frame and appends the clone to the end of the collection.
+ ///
+ /// The raw pixel data to generate the from.
+ /// The cloned .
+ public ImageFrame AddFrame(ImageFrame source)
+ {
+ this.ValidateFrame(source);
+ ImageFrame clonedFrame = source.Clone(this.parent.GetConfiguration());
+ this.frames.Add(clonedFrame);
+ return clonedFrame;
+ }
+
+ ///
+ /// 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.
+ ///
+ /// The raw pixel data to generate the from.
+ /// The new .
+ public ImageFrame AddFrame(ReadOnlySpan source)
+ {
+ var frame = ImageFrame.LoadPixelData(
+ this.parent.GetConfiguration(),
+ source,
+ this.RootFrame.Width,
+ this.RootFrame.Height);
+ this.frames.Add(frame);
+ return frame;
+ }
+
+ ///
+ /// 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.
+ ///
+ /// The raw pixel data to generate the from.
+ /// The new .
+ public ImageFrame AddFrame(TPixel[] source)
+ {
+ Guard.NotNull(source, nameof(source));
+ return this.AddFrame(source.AsSpan());
+ }
+
+ ///
+ /// Removes the frame at the specified index and frees all freeable resources associated with it.
+ ///
+ /// The zero-based index of the frame to remove.
+ /// Cannot remove last frame.
+ public override void RemoveFrame(int index)
+ {
+ if (index == 0 && this.Count == 1)
+ {
+ throw new InvalidOperationException("Cannot remove last frame.");
+ }
+
+ ImageFrame frame = this.frames[index];
+ this.frames.RemoveAt(index);
+ frame.Dispose();
+ }
+
+ ///
+ public override bool Contains(ImageFrame frame) =>
+ frame is ImageFrame specific && this.Contains(specific);
+
+ ///
+ /// Determines whether the contains the .
+ ///
+ /// The frame.
+ ///
+ /// true if the contains the specified frame; otherwise, false.
+ ///
+ public bool Contains(ImageFrame frame) => this.frames.Contains(frame);
+
+ ///
+ /// Moves an from to .
+ ///
+ /// The zero-based index of the frame to move.
+ /// The index to move the frame to.
+ public override void MoveFrame(int sourceIndex, int destinationIndex)
+ {
+ if (sourceIndex == destinationIndex)
+ {
+ return;
+ }
+
+ ImageFrame frameAtIndex = this.frames[sourceIndex];
+ this.frames.RemoveAt(sourceIndex);
+ this.frames.Insert(destinationIndex, frameAtIndex);
+ }
+
+ ///
+ /// 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.
+ ///
+ /// The zero-based index of the frame to export.
+ /// Cannot remove last frame.
+ /// The new with the specified frame.
+ public new Image ExportFrame(int index)
+ {
+ ImageFrame 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(this.parent.GetConfiguration(), this.parent.Metadata.DeepClone(), new[] { frame });
+ }
+
+ ///
+ /// Creates an with only the frame at the specified index
+ /// with the same metadata as the original image.
+ ///
+ /// The zero-based index of the frame to clone.
+ /// The new with the specified frame.
+ public new Image CloneFrame(int index)
+ {
+ ImageFrame frame = this[index];
+ ImageFrame clonedFrame = frame.Clone();
+ return new Image(this.parent.GetConfiguration(), this.parent.Metadata.DeepClone(), new[] { clonedFrame });
+ }
+
+ ///
+ /// Creates a new and appends it to the end of the collection.
+ ///
+ ///
+ /// The new .
+ ///
+ public new ImageFrame CreateFrame()
+ {
+ var frame = new ImageFrame(
+ this.parent.GetConfiguration(),
+ this.RootFrame.Width,
+ this.RootFrame.Height);
+ this.frames.Add(frame);
+ return frame;
+ }
+
+ ///
+ protected override IEnumerator NonGenericGetEnumerator() => this.frames.GetEnumerator();
+
+ ///
+ protected override ImageFrame NonGenericGetFrame(int index) => this[index];
+
+ ///
+ protected override ImageFrame NonGenericInsertFrame(int index, ImageFrame source)
+ {
+ Guard.NotNull(source, nameof(source));
+
+ if (source is ImageFrame compatibleSource)
+ {
+ return this.InsertFrame(index, compatibleSource);
+ }
+
+ ImageFrame result = this.CopyNonCompatibleFrame(source);
+ this.frames.Insert(index, result);
+ return result;
+ }
+
+ ///
+ protected override ImageFrame NonGenericAddFrame(ImageFrame source)
+ {
+ Guard.NotNull(source, nameof(source));
+
+ if (source is ImageFrame compatibleSource)
+ {
+ return this.AddFrame(compatibleSource);
+ }
+
+ ImageFrame result = this.CopyNonCompatibleFrame(source);
+ this.frames.Add(result);
+ return result;
+ }
+
+ ///
+ protected override Image NonGenericExportFrame(int index) => this.ExportFrame(index);
+
+ ///
+ protected override Image NonGenericCloneFrame(int index) => this.CloneFrame(index);
+
+ ///
+ protected override ImageFrame NonGenericCreateFrame(Color backgroundColor) =>
+ this.CreateFrame(backgroundColor.ToPixel());
+
+ ///
+ protected override ImageFrame NonGenericCreateFrame() => this.CreateFrame();
+
+ ///
+ /// Creates a new and appends it to the end of the collection.
+ ///
+ /// The background color to initialize the pixels with.
+ ///
+ /// The new .
+ ///
+ public ImageFrame CreateFrame(TPixel backgroundColor)
+ {
+ var frame = new ImageFrame(
+ this.parent.GetConfiguration(),
+ this.RootFrame.Width,
+ this.RootFrame.Height,
+ backgroundColor);
+ this.frames.Add(frame);
+ return frame;
+ }
+
+ ///
+ IEnumerator> IEnumerable>.GetEnumerator() => this.frames.GetEnumerator();
+
+ ///
+ IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)this.frames).GetEnumerator();
+
+ private void ValidateFrame(ImageFrame 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 f in this.frames)
+ {
+ f.Dispose();
+ }
+
+ this.frames.Clear();
+ }
+
+ private ImageFrame CopyNonCompatibleFrame(ImageFrame source)
+ {
+ ImageFrame result = new ImageFrame(
+ this.parent.GetConfiguration(),
+ source.Size(),
+ source.Metadata.DeepClone());
+ source.CopyPixelsTo(result.PixelBuffer.Span);
+ return result;
+ }
+ }
+}
diff --git a/src/ImageSharp/ImageFrame{TPixel}.cs b/src/ImageSharp/ImageFrame{TPixel}.cs
index 4f0be5971..982f8d8f2 100644
--- a/src/ImageSharp/ImageFrame{TPixel}.cs
+++ b/src/ImageSharp/ImageFrame{TPixel}.cs
@@ -3,6 +3,7 @@
using System;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
@@ -15,10 +16,12 @@ using SixLabors.Primitives;
namespace SixLabors.ImageSharp
{
///
- /// Represents a single frame in a animation.
+ /// Represents a pixel-specific image frame containing all pixel data and .
+ /// 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.
///
/// The pixel format.
- public sealed class ImageFrame : IPixelSource, IDisposable
+ public sealed class ImageFrame : ImageFrame, IPixelSource, IDisposable
where TPixel : struct, IPixel
{
private bool isDisposed;
@@ -53,8 +56,12 @@ namespace SixLabors.ImageSharp
/// The height of the image in pixels.
/// The metadata.
internal ImageFrame(Configuration configuration, int width, int height, ImageFrameMetadata metadata)
- : this(configuration, width, height, default(TPixel), metadata)
+ : base(configuration, width, height, metadata)
{
+ Guard.MustBeGreaterThan(width, 0, nameof(width));
+ Guard.MustBeGreaterThan(height, 0, nameof(height));
+
+ this.PixelBuffer = this.MemoryAllocator.Allocate2D(width, height, AllocationOptions.Clean);
}
///
@@ -78,15 +85,12 @@ namespace SixLabors.ImageSharp
/// The color to clear the image with.
/// The metadata.
internal ImageFrame(Configuration configuration, int width, int height, TPixel backgroundColor, ImageFrameMetadata metadata)
+ : base(configuration, width, height, metadata)
{
- Guard.NotNull(configuration, nameof(configuration));
Guard.MustBeGreaterThan(width, 0, nameof(width));
Guard.MustBeGreaterThan(height, 0, nameof(height));
- this.Configuration = configuration;
- this.MemoryAllocator = configuration.MemoryAllocator;
this.PixelBuffer = this.MemoryAllocator.Allocate2D(width, height);
- this.Metadata = metadata ?? new ImageFrameMetadata();
this.Clear(configuration.GetParallelOptions(), backgroundColor);
}
@@ -111,16 +115,12 @@ namespace SixLabors.ImageSharp
/// The memory source.
/// The metadata.
internal ImageFrame(Configuration configuration, int width, int height, MemorySource memorySource, ImageFrameMetadata metadata)
+ : base(configuration, width, height, metadata)
{
- Guard.NotNull(configuration, nameof(configuration));
Guard.MustBeGreaterThan(width, 0, nameof(width));
Guard.MustBeGreaterThan(height, 0, nameof(height));
- Guard.NotNull(metadata, nameof(metadata));
- this.Configuration = configuration;
- this.MemoryAllocator = configuration.MemoryAllocator;
this.PixelBuffer = new Buffer2D(memorySource, width, height);
- this.Metadata = metadata;
}
///
@@ -129,27 +129,15 @@ namespace SixLabors.ImageSharp
/// The configuration which allows altering default behaviour or extending the library.
/// The source.
internal ImageFrame(Configuration configuration, ImageFrame source)
+ : base(configuration, source.Width, source.Height, source.Metadata.DeepClone())
{
Guard.NotNull(configuration, nameof(configuration));
Guard.NotNull(source, nameof(source));
- this.Configuration = configuration;
- this.MemoryAllocator = configuration.MemoryAllocator;
this.PixelBuffer = this.MemoryAllocator.Allocate2D(source.PixelBuffer.Width, source.PixelBuffer.Height);
source.PixelBuffer.GetSpan().CopyTo(this.PixelBuffer.GetSpan());
- this.Metadata = source.Metadata.DeepClone();
}
- ///
- /// Gets the to use for buffer allocations.
- ///
- public MemoryAllocator MemoryAllocator { get; }
-
- ///
- /// Gets the instance associated with this .
- ///
- internal Configuration Configuration { get; }
-
///
/// Gets the image pixels. Not private as Buffer2D requires an array in its constructor.
///
@@ -158,21 +146,6 @@ namespace SixLabors.ImageSharp
///
Buffer2D IPixelSource.PixelBuffer => this.PixelBuffer;
- ///
- /// Gets the width.
- ///
- public int Width => this.PixelBuffer.Width;
-
- ///
- /// Gets the height.
- ///
- public int Height => this.PixelBuffer.Height;
-
- ///
- /// Gets the metadata of the frame.
- ///
- public ImageFrameMetadata Metadata { get; }
-
///
/// Gets or sets the pixel at the specified position.
///
@@ -188,18 +161,6 @@ namespace SixLabors.ImageSharp
set => this.PixelBuffer[x, y] = value;
}
- ///
- /// Gets the size of the frame.
- ///
- /// The
- public Size Size() => new Size(this.Width, this.Height);
-
- ///
- /// Gets the bounds of the frame.
- ///
- /// The
- public Rectangle Bounds() => new Rectangle(0, 0, this.Width, this.Height);
-
///
/// Gets a reference to the pixel at the specified position.
///
@@ -232,12 +193,13 @@ namespace SixLabors.ImageSharp
Guard.NotNull(pixelSource, nameof(pixelSource));
Buffer2D.SwapOrCopyContent(this.PixelBuffer, pixelSource.PixelBuffer);
+ this.UpdateSize(this.PixelBuffer.Size());
}
///
/// Disposes the object and frees resources for the Garbage Collector.
///
- internal void Dispose()
+ public override void Dispose()
{
if (this.isDisposed)
{
@@ -251,6 +213,17 @@ namespace SixLabors.ImageSharp
this.isDisposed = true;
}
+ internal override void CopyPixelsTo(Span destination)
+ {
+ if (typeof(TPixel) == typeof(TDestinationPixel))
+ {
+ Span dest1 = MemoryMarshal.Cast(destination);
+ this.PixelBuffer.Span.CopyTo(dest1);
+ }
+
+ PixelOperations.Instance.To(this.Configuration, this.PixelBuffer.Span, destination);
+ }
+
///
public override string ToString() => $"ImageFrame<{typeof(TPixel).Name}>({this.Width}x{this.Height})";
@@ -325,8 +298,5 @@ namespace SixLabors.ImageSharp
span.Fill(value);
}
}
-
- ///
- void IDisposable.Dispose() => this.Dispose();
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj
index a3d614a06..3b0211434 100644
--- a/src/ImageSharp/ImageSharp.csproj
+++ b/src/ImageSharp/ImageSharp.csproj
@@ -5,6 +5,16 @@
SixLabors.ImageSharp
SixLabors.ImageSharp
A cross-platform library for the processing of image files; written in C#
+ en
+
+ $(packageversion)
+ 0.0.1
+
+ netcoreapp2.1;netstandard1.3;netstandard2.0;net472
+
+ true
+ true
+ SixLabors.ImageSharp
SixLabors.ImageSharp
Image Resize Crop Gif Jpg Jpeg Bitmap Png Core
SixLabors.ImageSharp
diff --git a/src/ImageSharp/Image{TPixel}.cs b/src/ImageSharp/Image{TPixel}.cs
index 2b93b710b..5d0364dd8 100644
--- a/src/ImageSharp/Image{TPixel}.cs
+++ b/src/ImageSharp/Image{TPixel}.cs
@@ -80,7 +80,12 @@ namespace SixLabors.ImageSharp
/// The width of the image in pixels.
/// The height of the image in pixels.
/// The images metadata.
- internal Image(Configuration configuration, MemorySource memorySource, int width, int height, ImageMetadata metadata)
+ internal Image(
+ Configuration configuration,
+ MemorySource memorySource,
+ int width,
+ int height,
+ ImageMetadata metadata)
: base(configuration, PixelTypeInfo.Create(), metadata, width, height)
{
this.Frames = new ImageFrameCollection(this, width, height, memorySource);
@@ -95,7 +100,12 @@ namespace SixLabors.ImageSharp
/// The height of the image in pixels.
/// The color to initialize the pixels with.
/// The images metadata.
- internal Image(Configuration configuration, int width, int height, TPixel backgroundColor, ImageMetadata metadata)
+ internal Image(
+ Configuration configuration,
+ int width,
+ int height,
+ TPixel backgroundColor,
+ ImageMetadata metadata)
: base(configuration, PixelTypeInfo.Create(), metadata, width, height)
{
this.Frames = new ImageFrameCollection(this, width, height, backgroundColor);
@@ -114,10 +124,13 @@ namespace SixLabors.ImageSharp
this.Frames = new ImageFrameCollection(this, frames);
}
+ ///
+ protected override ImageFrameCollection NonGenericFrameCollection => this.Frames;
+
///
/// Gets the frames.
///
- public ImageFrameCollection Frames { get; }
+ public new ImageFrameCollection Frames { get; }
///
/// Gets the root frame.
@@ -149,7 +162,8 @@ namespace SixLabors.ImageSharp
/// Returns a new with all the same pixel data as the original.
public Image Clone(Configuration configuration)
{
- IEnumerable> clonedFrames = this.Frames.Select(x => x.Clone(configuration));
+ IEnumerable> clonedFrames =
+ this.Frames.Select, ImageFrame>(x => x.Clone(configuration));
return new Image(configuration, this.Metadata.DeepClone(), clonedFrames);
}
@@ -161,7 +175,8 @@ namespace SixLabors.ImageSharp
/// The .
public override Image CloneAs(Configuration configuration)
{
- IEnumerable> clonedFrames = this.Frames.Select(x => x.CloneAs(configuration));
+ IEnumerable> clonedFrames =
+ this.Frames.Select, ImageFrame>(x => x.CloneAs(configuration));
return new Image(configuration, this.Metadata.DeepClone(), clonedFrames);
}
@@ -214,4 +229,4 @@ namespace SixLabors.ImageSharp
return rootSize;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.cs
index 4cff1f8c1..8481fc62c 100644
--- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.cs
+++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.cs
@@ -11,6 +11,7 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+
namespace SixLabors.ImageSharp.PixelFormats
{
///
@@ -222,6 +223,15 @@ namespace SixLabors.ImageSharp.PixelFormats
dp.FromArgb32(sp);
}
}
+ ///
+ internal override void From(
+ Configuration configuration,
+ ReadOnlySpan sourcePixels,
+ Span destinationPixels)
+ {
+ PixelOperations.Instance.ToArgb32(configuration, sourcePixels, destinationPixels);
+ }
+
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.tt b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.tt
index 0a58504e1..3a4c7b459 100644
--- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.tt
+++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.tt
@@ -16,4 +16,4 @@ namespace SixLabors.ImageSharp.PixelFormats
<# GenerateAllDefaultConversionMethods("Argb32"); #>
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.cs
index 225e4b5e0..4b2b3a02e 100644
--- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.cs
+++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.cs
@@ -11,6 +11,7 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+
namespace SixLabors.ImageSharp.PixelFormats
{
///
@@ -196,6 +197,15 @@ namespace SixLabors.ImageSharp.PixelFormats
dp.FromBgr24(sp);
}
}
+ ///
+ internal override void From(
+ Configuration configuration,
+ ReadOnlySpan sourcePixels,
+ Span destinationPixels)
+ {
+ PixelOperations.Instance.ToBgr24(configuration, sourcePixels, destinationPixels);
+ }
+
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.tt b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.tt
index 84b89aa32..cfaefeda9 100644
--- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.tt
+++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.tt
@@ -16,4 +16,4 @@ namespace SixLabors.ImageSharp.PixelFormats
<# GenerateAllDefaultConversionMethods("Bgr24"); #>
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.cs
index ce049115e..95d85c7c8 100644
--- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.cs
+++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.cs
@@ -11,6 +11,7 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+
namespace SixLabors.ImageSharp.PixelFormats
{
///
@@ -222,6 +223,15 @@ namespace SixLabors.ImageSharp.PixelFormats
dp.FromBgra32(sp);
}
}
+ ///
+ internal override void From(
+ Configuration configuration,
+ ReadOnlySpan sourcePixels,
+ Span destinationPixels)
+ {
+ PixelOperations.Instance.ToBgra32(configuration, sourcePixels, destinationPixels);
+ }
+
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.tt b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.tt
index 004ceff51..58ecfa5a6 100644
--- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.tt
+++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.tt
@@ -16,4 +16,4 @@ namespace SixLabors.ImageSharp.PixelFormats
<# GenerateAllDefaultConversionMethods("Bgra32"); #>
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray16.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray16.PixelOperations.Generated.cs
index 288c5d92e..e6ed75d49 100644
--- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray16.PixelOperations.Generated.cs
+++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray16.PixelOperations.Generated.cs
@@ -11,6 +11,7 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+
namespace SixLabors.ImageSharp.PixelFormats
{
///
@@ -185,6 +186,15 @@ namespace SixLabors.ImageSharp.PixelFormats
dp.FromGray16(sp);
}
}
+ ///
+ internal override void From(
+ Configuration configuration,
+ ReadOnlySpan sourcePixels,
+ Span destinationPixels)
+ {
+ PixelOperations.Instance.ToGray16(configuration, sourcePixels, destinationPixels);
+ }
+
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray16.PixelOperations.Generated.tt b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray16.PixelOperations.Generated.tt
index 3cbc01e88..ac484bdbc 100644
--- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray16.PixelOperations.Generated.tt
+++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray16.PixelOperations.Generated.tt
@@ -16,4 +16,4 @@ namespace SixLabors.ImageSharp.PixelFormats
<# GenerateAllDefaultConversionMethods("Gray16"); #>
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray8.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray8.PixelOperations.Generated.cs
index f7e94788e..c9a3a06fe 100644
--- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray8.PixelOperations.Generated.cs
+++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray8.PixelOperations.Generated.cs
@@ -11,6 +11,7 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+
namespace SixLabors.ImageSharp.PixelFormats
{
///
@@ -185,6 +186,15 @@ namespace SixLabors.ImageSharp.PixelFormats
dp.FromGray8(sp);
}
}
+ ///
+ internal override void From(
+ Configuration configuration,
+ ReadOnlySpan sourcePixels,
+ Span destinationPixels)
+ {
+ PixelOperations.Instance.ToGray8(configuration, sourcePixels, destinationPixels);
+ }
+
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray8.PixelOperations.Generated.tt b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray8.PixelOperations.Generated.tt
index d35843ccd..ffa15af9d 100644
--- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray8.PixelOperations.Generated.tt
+++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray8.PixelOperations.Generated.tt
@@ -16,4 +16,4 @@ namespace SixLabors.ImageSharp.PixelFormats
<# GenerateAllDefaultConversionMethods("Gray8"); #>
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.cs
index 02a8869ec..ef224c02d 100644
--- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.cs
+++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.cs
@@ -11,6 +11,7 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+
namespace SixLabors.ImageSharp.PixelFormats
{
///
@@ -196,6 +197,15 @@ namespace SixLabors.ImageSharp.PixelFormats
dp.FromRgb24(sp);
}
}
+ ///
+ internal override void From(
+ Configuration configuration,
+ ReadOnlySpan sourcePixels,
+ Span destinationPixels)
+ {
+ PixelOperations.Instance.ToRgb24(configuration, sourcePixels, destinationPixels);
+ }
+
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.tt b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.tt
index d96c3684b..fc149b238 100644
--- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.tt
+++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.tt
@@ -16,4 +16,4 @@ namespace SixLabors.ImageSharp.PixelFormats
<# GenerateAllDefaultConversionMethods("Rgb24"); #>
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb48.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb48.PixelOperations.Generated.cs
index 30c9972bb..694388899 100644
--- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb48.PixelOperations.Generated.cs
+++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb48.PixelOperations.Generated.cs
@@ -11,6 +11,7 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+
namespace SixLabors.ImageSharp.PixelFormats
{
///
@@ -185,6 +186,15 @@ namespace SixLabors.ImageSharp.PixelFormats
dp.FromRgb48(sp);
}
}
+ ///
+ internal override void From(
+ Configuration configuration,
+ ReadOnlySpan sourcePixels,
+ Span destinationPixels)
+ {
+ PixelOperations.Instance.ToRgb48(configuration, sourcePixels, destinationPixels);
+ }
+
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb48.PixelOperations.Generated.tt b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb48.PixelOperations.Generated.tt
index 7bff33638..957c8f886 100644
--- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb48.PixelOperations.Generated.tt
+++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb48.PixelOperations.Generated.tt
@@ -16,4 +16,4 @@ namespace SixLabors.ImageSharp.PixelFormats
<# GenerateAllDefaultConversionMethods("Rgb48"); #>
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba32.PixelOperations.Generated.cs
index da2ce3770..f0c8bc884 100644
--- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba32.PixelOperations.Generated.cs
+++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba32.PixelOperations.Generated.cs
@@ -11,6 +11,7 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+
namespace SixLabors.ImageSharp.PixelFormats
{
///
@@ -211,6 +212,15 @@ namespace SixLabors.ImageSharp.PixelFormats
dp.FromRgba32(sp);
}
}
+ ///
+ internal override void From(
+ Configuration configuration,
+ ReadOnlySpan sourcePixels,
+ Span destinationPixels)
+ {
+ PixelOperations.Instance.ToRgba32(configuration, sourcePixels, destinationPixels);
+ }
+
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba32.PixelOperations.Generated.tt b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba32.PixelOperations.Generated.tt
index 6b9e2d124..905e2cc58 100644
--- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba32.PixelOperations.Generated.tt
+++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba32.PixelOperations.Generated.tt
@@ -16,4 +16,4 @@ namespace SixLabors.ImageSharp.PixelFormats
<# GenerateAllDefaultConversionMethods("Rgba32"); #>
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba64.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba64.PixelOperations.Generated.cs
index 42c40ad5d..0faa6eb44 100644
--- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba64.PixelOperations.Generated.cs
+++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba64.PixelOperations.Generated.cs
@@ -11,6 +11,7 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+
namespace SixLabors.ImageSharp.PixelFormats
{
///
@@ -185,6 +186,15 @@ namespace SixLabors.ImageSharp.PixelFormats
dp.FromRgba64(sp);
}
}
+ ///
+ internal override void From(
+ Configuration configuration,
+ ReadOnlySpan sourcePixels,
+ Span destinationPixels)
+ {
+ PixelOperations.Instance.ToRgba64(configuration, sourcePixels, destinationPixels);
+ }
+
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba64.PixelOperations.Generated.tt b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba64.PixelOperations.Generated.tt
index d15945f94..03179a392 100644
--- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba64.PixelOperations.Generated.tt
+++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba64.PixelOperations.Generated.tt
@@ -16,4 +16,4 @@ namespace SixLabors.ImageSharp.PixelFormats
<# GenerateAllDefaultConversionMethods("Rgba64"); #>
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/_Common.ttinclude b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/_Common.ttinclude
index 584615532..17e9469f3 100644
--- a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/_Common.ttinclude
+++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/_Common.ttinclude
@@ -22,6 +22,19 @@ using System.Runtime.InteropServices;
// Types with Rgba32-combatible to/from Vector4 conversion
static readonly string[] Rgba32CompatibleTypes = { "Argb32", "Bgra32", "Rgb24", "Bgr24" };
+ void GenerateGenericConverterMethods(string pixelType)
+ {
+#>
+ ///
+ internal override void From(
+ Configuration configuration,
+ ReadOnlySpan sourcePixels,
+ Span<<#=pixelType#>> destinationPixels)
+ {
+ PixelOperations.Instance.To<#=pixelType#>(configuration, sourcePixels, destinationPixels);
+ }
+<#+
+ }
void GenerateDefaultSelfConversionMethods(string pixelType)
{
@@ -117,7 +130,7 @@ using System.Runtime.InteropServices;
}
#>
///
- internal override void FromVector4(Configuration configuration, Span sourceVectors, Span<<#=pixelType#>> destPixels, PixelConversionModifiers modifiers)
+ internal override void FromVector4Destructive(Configuration configuration, Span sourceVectors, Span<<#=pixelType#>> destPixels, PixelConversionModifiers modifiers)
{
Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, modifiers.Remove(<#=removeTheseModifiers#>));
}
@@ -156,5 +169,7 @@ using System.Runtime.InteropServices;
{
GenerateDefaultConvertToMethod(pixelType, destPixelType);
}
+
+ GenerateGenericConverterMethods(pixelType);
}
#>
diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Gray16.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Gray16.cs
index 6fce1c757..195a8bedb 100644
--- a/src/ImageSharp/PixelFormats/PixelImplementations/Gray16.cs
+++ b/src/ImageSharp/PixelFormats/PixelImplementations/Gray16.cs
@@ -54,7 +54,7 @@ namespace SixLabors.ImageSharp.PixelFormats
///
[MethodImpl(InliningOptions.ShortMethod)]
- public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector);
+ public void FromScaledVector4(Vector4 vector) => this.ConvertFromRgbaScaledVector4(vector);
///
[MethodImpl(InliningOptions.ShortMethod)]
@@ -62,11 +62,7 @@ namespace SixLabors.ImageSharp.PixelFormats
///
[MethodImpl(InliningOptions.ShortMethod)]
- public void FromVector4(Vector4 vector)
- {
- vector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One) * Max * Average;
- this.PackedValue = (ushort)MathF.Round(vector.X + vector.Y + vector.Z);
- }
+ public void FromVector4(Vector4 vector) => this.ConvertFromRgbaScaledVector4(vector);
///
[MethodImpl(InliningOptions.ShortMethod)]
@@ -176,9 +172,9 @@ namespace SixLabors.ImageSharp.PixelFormats
{
vector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One) * Max;
this.PackedValue = ImageMaths.Get16BitBT709Luminance(
- (ushort)MathF.Round(vector.X),
- (ushort)MathF.Round(vector.Y),
- (ushort)MathF.Round(vector.Z));
+ vector.X,
+ vector.Y,
+ vector.Z);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Gray8.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Gray8.cs
index 1c278b434..09597a949 100644
--- a/src/ImageSharp/PixelFormats/PixelImplementations/Gray8.cs
+++ b/src/ImageSharp/PixelFormats/PixelImplementations/Gray8.cs
@@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.PixelFormats
///
[MethodImpl(InliningOptions.ShortMethod)]
- public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector);
+ public void FromScaledVector4(Vector4 vector) => this.ConvertFromRgbaScaledVector4(vector);
///
[MethodImpl(InliningOptions.ShortMethod)]
@@ -67,15 +67,7 @@ namespace SixLabors.ImageSharp.PixelFormats
///
[MethodImpl(InliningOptions.ShortMethod)]
- public void FromVector4(Vector4 vector)
- {
- vector = Vector4.Max(Min, vector);
- vector = Vector4.Min(Max, vector);
-
- float roundedSum = Vector4.Dot(vector, Accumulator);
-
- this.PackedValue = (byte)roundedSum;
- }
+ public void FromVector4(Vector4 vector) => this.ConvertFromRgbaScaledVector4(vector);
///
[MethodImpl(InliningOptions.ShortMethod)]
@@ -166,4 +158,4 @@ namespace SixLabors.ImageSharp.PixelFormats
this.PackedValue = ImageMaths.Get8BitBT709Luminance((byte)vector.X, (byte)vector.Y, (byte)vector.Z);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.PixelOperations.cs
index 3112126e7..b331dbd99 100644
--- a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.PixelOperations.cs
+++ b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.PixelOperations.cs
@@ -3,6 +3,7 @@
using System;
using System.Numerics;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.PixelFormats.Utils;
@@ -44,6 +45,38 @@ namespace SixLabors.ImageSharp.PixelFormats
MemoryMarshal.Cast(sourcePixels).CopyTo(destVectors);
Vector4Converters.ApplyForwardConversionModifiers(destVectors, modifiers);
}
+
+ internal override void ToGray8(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels)
+ {
+ Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels));
+
+ ref Vector4 sourceBaseRef = ref Unsafe.As(ref MemoryMarshal.GetReference(sourcePixels));
+ ref Gray8 destBaseRef = ref MemoryMarshal.GetReference(destPixels);
+
+ for (int i = 0; i < sourcePixels.Length; i++)
+ {
+ ref Vector4 sp = ref Unsafe.Add(ref sourceBaseRef, i);
+ ref Gray8 dp = ref Unsafe.Add(ref destBaseRef, i);
+
+ dp.ConvertFromRgbaScaledVector4(sp);
+ }
+ }
+
+ internal override void ToGray16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels)
+ {
+ Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels));
+
+ ref Vector4 sourceBaseRef = ref Unsafe.As(ref MemoryMarshal.GetReference(sourcePixels));
+ ref Gray16 destBaseRef = ref MemoryMarshal.GetReference(destPixels);
+
+ for (int i = 0; i < sourcePixels.Length; i++)
+ {
+ ref Vector4 sp = ref Unsafe.Add(ref sourceBaseRef, i);
+ ref Gray16 dp = ref Unsafe.Add(ref destBaseRef, i);
+
+ dp.ConvertFromRgbaScaledVector4(sp);
+ }
+ }
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs
index d009822e8..2b2d79c73 100644
--- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs
+++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs
@@ -7,6 +7,8 @@ using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+using SixLabors.ImageSharp.Memory;
+
namespace SixLabors.ImageSharp.PixelFormats
{
///
@@ -89,63 +91,56 @@ namespace SixLabors.ImageSharp.PixelFormats
Span destVectors) =>
this.ToVector4(configuration, sourcePixels, destVectors, PixelConversionModifiers.None);
- ///
- /// Converts 'sourceColors.Length' pixels from 'sourceColors' into 'destinationColors'.
- ///
- /// The destination pixel type.
- /// A to configure internal operations
- /// The to the source colors.
- /// The to the destination colors.
- internal virtual void To(
+ internal virtual void From(
Configuration configuration,
- ReadOnlySpan sourceColors,
- Span destinationColors)
- where TDestinationPixel : struct, IPixel
+ ReadOnlySpan sourcePixels,
+ Span destinationPixels)
+ where TSourcePixel : struct, IPixel
{
- Guard.NotNull(configuration, nameof(configuration));
- Guard.DestinationShouldNotBeTooShort(sourceColors, destinationColors, nameof(destinationColors));
-
- int count = sourceColors.Length;
- ref TPixel sourceRef = ref MemoryMarshal.GetReference(sourceColors);
-
- // 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))
+ const int SliceLength = 1024;
+ int numberOfSlices = sourcePixels.Length / SliceLength;
+ using (IMemoryOwner tempVectors = configuration.MemoryAllocator.Allocate(SliceLength))
{
- ref Gray16 gray16Ref = ref MemoryMarshal.GetReference(MemoryMarshal.Cast(destinationColors));
- for (int i = 0; i < count; i++)
+ Span vectorSpan = tempVectors.GetSpan();
+ for (int i = 0; i < numberOfSlices; i++)
{
- ref TPixel sp = ref Unsafe.Add(ref sourceRef, i);
- ref Gray16 dp = ref Unsafe.Add(ref gray16Ref, i);
- dp.ConvertFromRgbaScaledVector4(sp.ToScaledVector4());
+ int start = i * SliceLength;
+ ReadOnlySpan s = sourcePixels.Slice(start, SliceLength);
+ Span d = destinationPixels.Slice(start, SliceLength);
+ PixelOperations.Instance.ToVector4(configuration, s, vectorSpan);
+ this.FromVector4Destructive(configuration, vectorSpan, d);
}
- return;
- }
-
- if (typeof(TDestinationPixel) == typeof(Gray8))
- {
- ref Gray8 gray8Ref = ref MemoryMarshal.GetReference(MemoryMarshal.Cast(destinationColors));
- for (int i = 0; i < count; i++)
+ int endOfCompleteSlices = numberOfSlices * SliceLength;
+ int remainder = sourcePixels.Length - endOfCompleteSlices;
+ if (remainder > 0)
{
- ref TPixel sp = ref Unsafe.Add(ref sourceRef, i);
- ref Gray8 dp = ref Unsafe.Add(ref gray8Ref, i);
- dp.ConvertFromRgbaScaledVector4(sp.ToScaledVector4());
+ ReadOnlySpan s = sourcePixels.Slice(endOfCompleteSlices);
+ Span d = destinationPixels.Slice(endOfCompleteSlices);
+ vectorSpan = vectorSpan.Slice(0, remainder);
+ PixelOperations.Instance.ToVector4(configuration, s, vectorSpan);
+ this.FromVector4Destructive(configuration, vectorSpan, d);
}
-
- return;
}
+ }
- // Normal conversion
- ref TDestinationPixel destRef = ref MemoryMarshal.GetReference(destinationColors);
- for (int i = 0; i < count; i++)
- {
- ref TPixel sp = ref Unsafe.Add(ref sourceRef, i);
- ref TDestinationPixel dp = ref Unsafe.Add(ref destRef, i);
- dp.FromScaledVector4(sp.ToScaledVector4());
- }
+ ///
+ /// Converts 'sourcePixels.Length' pixels from 'sourcePixels' into 'destinationPixels'.
+ ///
+ /// The destination pixel type.
+ /// A to configure internal operations.
+ /// The to the source pixels.
+ /// The to the destination pixels.
+ internal virtual void To(
+ Configuration configuration,
+ ReadOnlySpan sourcePixels,
+ Span destinationPixels)
+ where TDestinationPixel : struct, IPixel
+ {
+ Guard.NotNull(configuration, nameof(configuration));
+ Guard.DestinationShouldNotBeTooShort(sourcePixels, destinationPixels, nameof(destinationPixels));
+
+ PixelOperations.Instance.From(configuration, sourcePixels, destinationPixels);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs
index 9625ca216..529a49bf7 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs
@@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
protected override Image CreateDestination(Image source, Rectangle sourceRectangle)
{
// We will always be creating the clone even for mutate because we may need to resize the canvas
- IEnumerable