diff --git a/src/ImageSharp.Drawing/Brushes/IBrush.cs b/src/ImageSharp.Drawing/Brushes/IBrush.cs index 8b163d7f67..bb907281b0 100644 --- a/src/ImageSharp.Drawing/Brushes/IBrush.cs +++ b/src/ImageSharp.Drawing/Brushes/IBrush.cs @@ -32,6 +32,6 @@ namespace SixLabors.ImageSharp.Drawing.Brushes /// The when being applied to things like shapes would usually be the /// bounding box of the shape not necessarily the bounds of the whole image /// - BrushApplicator CreateApplicator(ImageBase source, RectangleF region, GraphicsOptions options); + BrushApplicator CreateApplicator(ImageFrame source, RectangleF region, GraphicsOptions options); } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs index 850775ce05..4cd3585ec7 100644 --- a/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs @@ -21,19 +21,19 @@ namespace SixLabors.ImageSharp.Drawing.Brushes /// /// The image to paint. /// - private readonly ImageBase image; + private readonly ImageFrame image; /// /// Initializes a new instance of the class. /// /// The image. - public ImageBrush(ImageBase image) + public ImageBrush(ImageFrame image) { this.image = image; } /// - public BrushApplicator CreateApplicator(ImageBase source, RectangleF region, GraphicsOptions options) + public BrushApplicator CreateApplicator(ImageFrame source, RectangleF region, GraphicsOptions options) { return new ImageBrushApplicator(source, this.image, region, options); } @@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.Drawing.Brushes /// /// The source image. /// - private readonly ImageBase source; + private readonly ImageFrame source; /// /// The y-length. @@ -75,7 +75,7 @@ namespace SixLabors.ImageSharp.Drawing.Brushes /// The image. /// The region. /// The options - public ImageBrushApplicator(ImageBase target, ImageBase image, RectangleF region, GraphicsOptions options) + public ImageBrushApplicator(ImageFrame target, ImageFrame image, RectangleF region, GraphicsOptions options) : base(target, options) { this.source = image; diff --git a/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs index b1dab0ea9c..844df0e0e9 100644 --- a/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs @@ -92,7 +92,7 @@ namespace SixLabors.ImageSharp.Drawing.Brushes } /// - public BrushApplicator CreateApplicator(ImageBase source, RectangleF region, GraphicsOptions options) + public BrushApplicator CreateApplicator(ImageFrame source, RectangleF region, GraphicsOptions options) { return new PatternBrushApplicator(source, this.pattern, this.patternVector, options); } @@ -115,7 +115,7 @@ namespace SixLabors.ImageSharp.Drawing.Brushes /// The pattern. /// The patternVector. /// The options - public PatternBrushApplicator(ImageBase source, Fast2DArray pattern, Fast2DArray patternVector, GraphicsOptions options) + public PatternBrushApplicator(ImageFrame source, Fast2DArray pattern, Fast2DArray patternVector, GraphicsOptions options) : base(source, options) { this.pattern = pattern; diff --git a/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs b/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs index 258d69721c..ca6f7630d9 100644 --- a/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs +++ b/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs @@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Drawing.Brushes.Processors /// /// The target. /// The options. - internal BrushApplicator(ImageBase target, GraphicsOptions options) + internal BrushApplicator(ImageFrame target, GraphicsOptions options) { this.Target = target; @@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp.Drawing.Brushes.Processors /// /// Gets the destinaion /// - protected ImageBase Target { get; } + protected ImageFrame Target { get; } /// /// Gets the blend percentage diff --git a/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs index bbd0e4d864..ba2fca4e4b 100644 --- a/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs @@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Drawing.Brushes public TPixel TargeTPixel { get; } /// - public BrushApplicator CreateApplicator(ImageBase source, RectangleF region, GraphicsOptions options) + public BrushApplicator CreateApplicator(ImageFrame source, RectangleF region, GraphicsOptions options) { return new RecolorBrushApplicator(source, this.SourceColor, this.TargeTPixel, this.Threshold, options); } @@ -92,7 +92,7 @@ namespace SixLabors.ImageSharp.Drawing.Brushes /// Color of the target. /// The threshold . /// The options - public RecolorBrushApplicator(ImageBase source, TPixel sourceColor, TPixel targetColor, float threshold, GraphicsOptions options) + public RecolorBrushApplicator(ImageFrame source, TPixel sourceColor, TPixel targetColor, float threshold, GraphicsOptions options) : base(source, options) { this.sourceColor = sourceColor.ToVector4(); diff --git a/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs index 27bce86bfb..658164339d 100644 --- a/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs @@ -42,7 +42,7 @@ namespace SixLabors.ImageSharp.Drawing.Brushes public TPixel Color => this.color; /// - public BrushApplicator CreateApplicator(ImageBase source, RectangleF region, GraphicsOptions options) + public BrushApplicator CreateApplicator(ImageFrame source, RectangleF region, GraphicsOptions options) { return new SolidBrushApplicator(source, this.color, options); } @@ -58,7 +58,7 @@ namespace SixLabors.ImageSharp.Drawing.Brushes /// The source image. /// The color. /// The options - public SolidBrushApplicator(ImageBase source, TPixel color, GraphicsOptions options) + public SolidBrushApplicator(ImageFrame source, TPixel color, GraphicsOptions options) : base(source, options) { this.Colors = new Buffer(source.Width); diff --git a/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs b/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs index e1ce6b0133..213ab1b4a7 100644 --- a/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs @@ -4,6 +4,8 @@ using System; using System.Numerics; using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Helpers; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; @@ -58,7 +60,7 @@ namespace SixLabors.ImageSharp.Drawing.Processors public Point Location { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { Image disposableImage = null; Image targetImage = this.Image; @@ -94,7 +96,7 @@ namespace SixLabors.ImageSharp.Drawing.Processors Parallel.For( minY, maxY, - source.Configuration.ParallelOptions, + configuration.ParallelOptions, y => { Span background = sourcePixels.GetRowSpan(y).Slice(minX, width); diff --git a/src/ImageSharp.Drawing/Processors/FillProcessor.cs b/src/ImageSharp.Drawing/Processors/FillProcessor.cs index fbd9654262..679ca6a228 100644 --- a/src/ImageSharp.Drawing/Processors/FillProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/FillProcessor.cs @@ -4,6 +4,7 @@ using System; using System.Numerics; using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Drawing; using SixLabors.ImageSharp.Drawing.Brushes; using SixLabors.ImageSharp.Drawing.Brushes.Processors; @@ -39,7 +40,7 @@ namespace SixLabors.ImageSharp.Drawing.Processors } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { int startX = sourceRectangle.X; int endX = sourceRectangle.Right; @@ -65,9 +66,6 @@ namespace SixLabors.ImageSharp.Drawing.Processors int width = maxX - minX; - // We could possibly do some optimization by having knowledge about the individual brushes operate - // for example If brush is SolidBrush then we could just get the color upfront - // and skip using the IBrushApplicator?. using (var amount = new Buffer(width)) using (BrushApplicator applicator = this.brush.CreateApplicator(source, sourceRectangle, this.options)) { @@ -79,7 +77,7 @@ namespace SixLabors.ImageSharp.Drawing.Processors Parallel.For( minY, maxY, - source.Configuration.ParallelOptions, + configuration.ParallelOptions, y => { int offsetY = y - startY; diff --git a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs index b9dd9a7b10..d867008d7e 100644 --- a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs @@ -58,7 +58,7 @@ namespace SixLabors.ImageSharp.Drawing.Processors public GraphicsOptions Options { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { Region region = this.Region; Rectangle rect = region.Bounds; diff --git a/src/ImageSharp/Advanced/IConfigurable.cs b/src/ImageSharp/Advanced/IConfigurable.cs new file mode 100644 index 0000000000..fd97ae921a --- /dev/null +++ b/src/ImageSharp/Advanced/IConfigurable.cs @@ -0,0 +1,16 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Advanced +{ + /// + /// Encapsulates the properties for configuration + /// + internal interface IConfigurable + { + /// + /// Gets the configuration. + /// + Configuration Configuration { get; } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Advanced/IPixelSource{TPixel}.cs b/src/ImageSharp/Advanced/IPixelSource.cs similarity index 62% rename from src/ImageSharp/Advanced/IPixelSource{TPixel}.cs rename to src/ImageSharp/Advanced/IPixelSource.cs index 777cb76e2e..c9edf118c6 100644 --- a/src/ImageSharp/Advanced/IPixelSource{TPixel}.cs +++ b/src/ImageSharp/Advanced/IPixelSource.cs @@ -2,20 +2,23 @@ // Licensed under the Apache License, Version 2.0. using System; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.MetaData; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Advanced { /// - /// Allows access to the pixels as an area of contiguous memory in the given pixel format. + /// Encapsulates the basic properties and methods required to manipulate images. /// /// The type of the pixel. internal interface IPixelSource where TPixel : struct, IPixel { /// - /// Gets the representation of the pixels as an area of contiguous memory in the given pixel format. + /// Gets the pixel buffer. /// - Span Span { get; } + Buffer2D PixelBuffer { get; } } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Advanced/ImageExtensions.cs b/src/ImageSharp/Advanced/ImageExtensions.cs index ca2e522f59..f4043b5ade 100644 --- a/src/ImageSharp/Advanced/ImageExtensions.cs +++ b/src/ImageSharp/Advanced/ImageExtensions.cs @@ -2,19 +2,15 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Collections.Generic; -using System.IO; -using System.Text; -using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Advanced { /// /// Extension methods over Image{TPixel} /// - internal static partial class ImageExtensions + internal static class ImageExtensions { /// /// Gets the representation of the pixels as an area of contiguous memory in the given pixel format. @@ -22,29 +18,94 @@ namespace SixLabors.ImageSharp.Advanced /// The type of the pixel. /// The source. /// The - public static Span GetPixelSpan(this ImageBase source) + public static Span GetPixelSpan(this ImageFrame source) where TPixel : struct, IPixel => GetSpan(source); /// - /// Gets a representing the row 'y' beginning from the the first pixel on that row. + /// Gets the representation of the pixels as an area of contiguous memory at row 'y' beginning from the the first pixel on that row. /// /// The type of the pixel. /// The source. /// The row. /// The - public static Span GetPixelRowSpan(this ImageBase source, int row) + public static Span GetPixelRowSpan(this ImageFrame source, int row) where TPixel : struct, IPixel - => GetSpan(source).Slice(row * source.Width, source.Width); + => GetSpan(source, row); /// - /// Gets the span. + /// Gets the representation of the pixels as an area of contiguous memory in the given pixel format. + /// + /// The type of the pixel. + /// The source. + /// The + public static Span GetPixelSpan(this Image source) + where TPixel : struct, IPixel + => source.Frames.RootFrame.GetPixelSpan(); + + /// + /// Gets the representation of the pixels as an area of contiguous memory at row 'y' beginning from the the first pixel on that row. + /// + /// The type of the pixel. + /// The source. + /// The row. + /// The + public static Span GetPixelRowSpan(this Image source, int row) + where TPixel : struct, IPixel + => source.Frames.RootFrame.GetPixelRowSpan(row); + + /// + /// Gets the configuration for the image. + /// + /// The Pixel format. + /// The source image + /// Returns the configuration. + public static Configuration GetConfiguration(this Image source) + where TPixel : struct, IPixel + => GetConfiguration((IConfigurable)source); + + /// + /// Gets the span to the backing buffer. /// /// The type of the pixel. /// The source. /// The span retuned from Pixel source private static Span GetSpan(IPixelSource source) where TPixel : struct, IPixel - => source.Span; + => source.PixelBuffer.Span; + + /// + /// Gets the span to the backing buffer at the given row. + /// + /// The type of the pixel. + /// The source. + /// The row. + /// + /// The span retuned from Pixel source + /// + private static Span GetSpan(IPixelSource source, int row) + where TPixel : struct, IPixel + => GetSpan(source.PixelBuffer, row); + + /// + /// Gets the span to the backing buffer at the given row. + /// + /// The type of the pixel. + /// The source. + /// The row. + /// + /// The span retuned from Pixel source + /// + private static Span GetSpan(Buffer2D source, int row) + where TPixel : struct, IPixel + => source.Span.Slice(row * source.Width, source.Width); + + /// + /// Gets the configuration. + /// + /// The source image + /// Returns the bounds of the image + private static Configuration GetConfiguration(IConfigurable source) + => source?.Configuration ?? Configuration.Default; } } diff --git a/src/ImageSharp/ApplyProcessors.cs b/src/ImageSharp/ApplyProcessors.cs index 1788332755..58a952c406 100644 --- a/src/ImageSharp/ApplyProcessors.cs +++ b/src/ImageSharp/ApplyProcessors.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; @@ -24,7 +25,7 @@ namespace SixLabors.ImageSharp Guard.NotNull(operation, nameof(operation)); Guard.NotNull(source, nameof(source)); - IInternalImageProcessingContext operationsRunner = source.Configuration.ImageOperationsProvider.CreateImageProcessingContext(source, true); + IInternalImageProcessingContext operationsRunner = source.GetConfiguration().ImageOperationsProvider.CreateImageProcessingContext(source, true); operation(operationsRunner); operationsRunner.Apply(); } @@ -41,7 +42,7 @@ namespace SixLabors.ImageSharp Guard.NotNull(operations, nameof(operations)); Guard.NotNull(source, nameof(source)); - IInternalImageProcessingContext operationsRunner = source.Configuration.ImageOperationsProvider.CreateImageProcessingContext(source, true); + IInternalImageProcessingContext operationsRunner = source.GetConfiguration().ImageOperationsProvider.CreateImageProcessingContext(source, true); operationsRunner.ApplyProcessors(operations); operationsRunner.Apply(); } @@ -59,7 +60,7 @@ namespace SixLabors.ImageSharp Guard.NotNull(operation, nameof(operation)); Guard.NotNull(source, nameof(source)); - IInternalImageProcessingContext operationsRunner = source.Configuration.ImageOperationsProvider.CreateImageProcessingContext(source, false); + IInternalImageProcessingContext operationsRunner = source.GetConfiguration().ImageOperationsProvider.CreateImageProcessingContext(source, false); operation(operationsRunner); return operationsRunner.Apply(); } @@ -77,7 +78,7 @@ namespace SixLabors.ImageSharp Guard.NotNull(operations, nameof(operations)); Guard.NotNull(source, nameof(source)); - IInternalImageProcessingContext operationsRunner = source.Configuration.ImageOperationsProvider.CreateImageProcessingContext(source, false); + IInternalImageProcessingContext operationsRunner = source.GetConfiguration().ImageOperationsProvider.CreateImageProcessingContext(source, false); operationsRunner.ApplyProcessors(operations); return operationsRunner.Apply(); } diff --git a/src/ImageSharp/Common/Helpers/ImageMaths.cs b/src/ImageSharp/Common/Helpers/ImageMaths.cs index 8453881cc5..8717fa9876 100644 --- a/src/ImageSharp/Common/Helpers/ImageMaths.cs +++ b/src/ImageSharp/Common/Helpers/ImageMaths.cs @@ -150,7 +150,7 @@ namespace SixLabors.ImageSharp /// /// The . /// - public static Rectangle GetFilteredBoundingRectangle(ImageBase bitmap, float componentValue, RgbaComponent channel = RgbaComponent.B) + public static Rectangle GetFilteredBoundingRectangle(ImageFrame bitmap, float componentValue, RgbaComponent channel = RgbaComponent.B) where TPixel : struct, IPixel { int width = bitmap.Width; @@ -158,7 +158,7 @@ namespace SixLabors.ImageSharp var topLeft = default(Point); var bottomRight = default(Point); - Func, int, int, float, bool> delegateFunc; + Func, int, int, float, bool> delegateFunc; // Determine which channel to check against switch (channel) @@ -180,7 +180,7 @@ namespace SixLabors.ImageSharp break; } - int GetMinY(ImageBase pixels) + int GetMinY(ImageFrame pixels) { for (int y = 0; y < height; y++) { @@ -196,7 +196,7 @@ namespace SixLabors.ImageSharp return 0; } - int GetMaxY(ImageBase pixels) + int GetMaxY(ImageFrame pixels) { for (int y = height - 1; y > -1; y--) { @@ -212,7 +212,7 @@ namespace SixLabors.ImageSharp return height; } - int GetMinX(ImageBase pixels) + int GetMinX(ImageFrame pixels) { for (int x = 0; x < width; x++) { @@ -228,7 +228,7 @@ namespace SixLabors.ImageSharp return 0; } - int GetMaxX(ImageBase pixels) + int GetMaxX(ImageFrame pixels) { for (int x = width - 1; x > -1; x--) { diff --git a/src/ImageSharp/DefaultInternalImageProcessorContext.cs b/src/ImageSharp/DefaultInternalImageProcessorContext.cs index 8eb1c09fee..575525a773 100644 --- a/src/ImageSharp/DefaultInternalImageProcessorContext.cs +++ b/src/ImageSharp/DefaultInternalImageProcessorContext.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.Helpers; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.Primitives; @@ -53,8 +54,7 @@ namespace SixLabors.ImageSharp // This will only work if the first processor applied is the cloning one thus // realistically for this optermissation to work the resize must the first processor // applied any only up processors will take the douple data path. - var cloningImageProcessor = processor as ICloningImageProcessor; - if (cloningImageProcessor != null) + if (processor is ICloningImageProcessor cloningImageProcessor) { this.destination = cloningImageProcessor.CloneAndApply(this.source, rectangle); return this; diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuserBase.cs b/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuserBase.cs index 3fb86f1924..510a097eaf 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuserBase.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuserBase.cs @@ -70,7 +70,7 @@ namespace SixLabors.ImageSharp.Dithering.Base /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Dither(ImageBase pixels, TPixel source, TPixel transformed, int x, int y, int minX, int minY, int maxX, int maxY) + public void Dither(ImageFrame pixels, TPixel source, TPixel transformed, int x, int y, int minX, int minY, int maxX, int maxY) where TPixel : struct, IPixel { this.Dither(pixels, source, transformed, x, y, minX, minY, maxX, maxY, true); @@ -78,7 +78,7 @@ namespace SixLabors.ImageSharp.Dithering.Base /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Dither(ImageBase image, TPixel source, TPixel transformed, int x, int y, int minX, int minY, int maxX, int maxY, bool replacePixel) + public void Dither(ImageFrame image, TPixel source, TPixel transformed, int x, int y, int minX, int minY, int maxX, int maxY, bool replacePixel) where TPixel : struct, IPixel { if (replacePixel) diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs b/src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs index 850c978fef..c538d643c6 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs @@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Dithering /// The maximum column value. /// The maximum row value. /// The pixel format. - void Dither(ImageBase image, TPixel source, TPixel transformed, int x, int y, int minX, int minY, int maxX, int maxY) + void Dither(ImageFrame image, TPixel source, TPixel transformed, int x, int y, int minX, int minY, int maxX, int maxY) where TPixel : struct, IPixel; /// @@ -43,7 +43,7 @@ namespace SixLabors.ImageSharp.Dithering /// Generally this would be true for standard two-color dithering but when used in conjunction with color quantization this should be false. /// /// The pixel format. - void Dither(ImageBase image, TPixel source, TPixel transformed, int x, int y, int minX, int minY, int maxX, int maxY, bool replacePixel) + void Dither(ImageFrame image, TPixel source, TPixel transformed, int x, int y, int minX, int minY, int maxX, int maxY, bool replacePixel) where TPixel : struct, IPixel; } } diff --git a/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs b/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs index e3c7c5cbaf..e0e11ad9ee 100644 --- a/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs +++ b/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs @@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Dithering /// The column index. /// The row index. /// The pixel format. - void Dither(ImageBase image, TPixel source, TPixel upper, TPixel lower, byte[] bytes, int index, int x, int y) + void Dither(ImageFrame image, TPixel source, TPixel upper, TPixel lower, byte[] bytes, int index, int x, int y) where TPixel : struct, IPixel; } } \ No newline at end of file diff --git a/src/ImageSharp/Dithering/Ordered/OrderedDitherBase.cs b/src/ImageSharp/Dithering/Ordered/OrderedDitherBase.cs index 6fa406bec8..09c30eb272 100644 --- a/src/ImageSharp/Dithering/Ordered/OrderedDitherBase.cs +++ b/src/ImageSharp/Dithering/Ordered/OrderedDitherBase.cs @@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp.Dithering.Base } /// - public void Dither(ImageBase image, TPixel source, TPixel upper, TPixel lower, byte[] bytes, int index, int x, int y) + public void Dither(ImageFrame image, TPixel source, TPixel upper, TPixel lower, byte[] bytes, int index, int x, int y) where TPixel : struct, IPixel { // TODO: This doesn't really cut it for me. diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index 12dd0f91a3..d34d170847 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -33,12 +33,12 @@ namespace SixLabors.ImageSharp.Formats.Bmp } /// - /// Encodes the image to the specified stream from the . + /// Encodes the image to the specified stream from the . /// /// The pixel format. - /// The to encode from. + /// The to encode from. /// The to encode the image data to. - public void Encode(ImageBase image, Stream stream) + public void Encode(Image image, Stream stream) where TPixel : struct, IPixel { Guard.NotNull(image, nameof(image)); @@ -73,7 +73,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp WriteHeader(writer, fileHeader); this.WriteInfo(writer, infoHeader); - this.WriteImage(writer, image); + this.WriteImage(writer, image.Frames.RootFrame); writer.Flush(); } @@ -125,9 +125,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// The pixel format. /// The containing the stream to write to. /// - /// The containing pixel data. + /// The containing pixel data. /// - private void WriteImage(EndianBinaryWriter writer, ImageBase image) + private void WriteImage(EndianBinaryWriter writer, ImageFrame image) where TPixel : struct, IPixel { using (PixelAccessor pixels = image.Lock()) diff --git a/src/ImageSharp/Formats/Bmp/ImageExtensions.cs b/src/ImageSharp/Formats/Bmp/ImageExtensions.cs index 558ce69c55..935ce8f4ad 100644 --- a/src/ImageSharp/Formats/Bmp/ImageExtensions.cs +++ b/src/ImageSharp/Formats/Bmp/ImageExtensions.cs @@ -3,6 +3,7 @@ using System; using System.IO; +using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.PixelFormats; @@ -35,6 +36,6 @@ namespace SixLabors.ImageSharp /// Thrown if the stream is null. public static void SaveAsBmp(this Image source, Stream stream, BmpEncoder encoder) where TPixel : struct, IPixel - => source.Save(stream, encoder ?? source.Configuration.FindEncoder(ImageFormats.Bmp)); + => source.Save(stream, encoder ?? source.GetConfiguration().FindEncoder(ImageFormats.Bmp)); } } diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index cb124d305a..c3c395e852 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -369,7 +369,7 @@ namespace SixLabors.ImageSharp.Formats.Gif ImageFrame currentFrame = null; - ImageBase image; + ImageFrame image; if (this.previousFrame == null) { @@ -378,7 +378,7 @@ namespace SixLabors.ImageSharp.Formats.Gif this.SetFrameMetaData(this.metaData); - image = this.image; + image = this.image.Frames.RootFrame; } else { @@ -471,7 +471,7 @@ namespace SixLabors.ImageSharp.Formats.Gif return; } - this.previousFrame = currentFrame == null ? this.image.ToFrame() : currentFrame; + this.previousFrame = currentFrame == null ? this.image.Frames.RootFrame : currentFrame; if (this.graphicsControlExtension != null && this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToBackground) @@ -484,7 +484,7 @@ namespace SixLabors.ImageSharp.Formats.Gif /// Restores the current frame area to the background. /// /// The frame. - private void RestoreToBackground(ImageBase frame) + private void RestoreToBackground(ImageFrame frame) { if (this.restoreArea == null) { diff --git a/src/ImageSharp/Formats/Gif/GifEncoder.cs b/src/ImageSharp/Formats/Gif/GifEncoder.cs index 2c3bb29299..ccf46a17d6 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoder.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoder.cs @@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Formats.Gif public void Encode(Image image, Stream stream) where TPixel : struct, IPixel { - GifEncoderCore encoder = new GifEncoderCore(this); + var encoder = new GifEncoderCore(this); encoder.Encode(image, stream); } } diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs index 5022678384..d143cd5319 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs @@ -90,20 +90,20 @@ namespace SixLabors.ImageSharp.Formats.Gif var writer = new EndianBinaryWriter(Endianness.LittleEndian, stream); // Ensure that pallete size can be set but has a fallback. - int paletteSize = this.paletteSize; - paletteSize = paletteSize > 0 ? paletteSize.Clamp(1, 256) : 256; + int size = this.paletteSize; + size = size > 0 ? size.Clamp(1, 256) : 256; // Get the number of bits. - this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(paletteSize); + this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(size); - this.hasFrames = image.Frames.Any(); + this.hasFrames = image.Frames.Count > 1; // Dithering when animating gifs is a bad idea as we introduce pixel tearing across frames. var ditheredQuantizer = (IQuantizer)this.quantizer; ditheredQuantizer.Dither = !this.hasFrames; // Quantize the image returning a palette. - QuantizedImage quantized = ditheredQuantizer.Quantize(image, paletteSize); + QuantizedImage quantized = ditheredQuantizer.Quantize(image.Frames.RootFrame, size); int index = this.GetTransparentIndex(quantized); @@ -114,28 +114,27 @@ namespace SixLabors.ImageSharp.Formats.Gif this.WriteLogicalScreenDescriptor(image, writer, index); // Write the first frame. - this.WriteGraphicalControlExtension(image.MetaData, writer, index); this.WriteComments(image, writer); - this.WriteImageDescriptor(image, writer); - this.WriteColorTable(quantized, writer); - this.WriteImageData(quantized, writer); // Write additional frames. if (this.hasFrames) { this.WriteApplicationExtension(writer, image.MetaData.RepeatCount, image.Frames.Count); + } - // ReSharper disable once ForCanBeConvertedToForeach - for (int i = 0; i < image.Frames.Count; i++) + foreach (ImageFrame frame in image.Frames) + { + if (quantized == null) { - ImageFrame frame = image.Frames[i]; - QuantizedImage quantizedFrame = ditheredQuantizer.Quantize(frame, paletteSize); - - this.WriteGraphicalControlExtension(frame.MetaData, writer, this.GetTransparentIndex(quantizedFrame)); - this.WriteImageDescriptor(frame, writer); - this.WriteColorTable(quantizedFrame, writer); - this.WriteImageData(quantizedFrame, writer); + quantized = ditheredQuantizer.Quantize(frame, size); } + + this.WriteGraphicalControlExtension(frame.MetaData, writer, this.GetTransparentIndex(quantized)); + this.WriteImageDescriptor(frame, writer); + this.WriteColorTable(quantized, writer); + this.WriteImageData(quantized, writer); + + quantized = null; // so next frame can regenerate it } // TODO: Write extension etc @@ -253,7 +252,7 @@ namespace SixLabors.ImageSharp.Formats.Gif /// Writes the image comments to the stream. /// /// The pixel format. - /// The to be encoded. + /// The to be encoded. /// The stream to write to. private void WriteComments(Image image, EndianBinaryWriter writer) where TPixel : struct, IPixel @@ -321,9 +320,9 @@ namespace SixLabors.ImageSharp.Formats.Gif /// Writes the image descriptor to the stream. /// /// The pixel format. - /// The to be encoded. + /// The to be encoded. /// The stream to write to. - private void WriteImageDescriptor(ImageBase image, EndianBinaryWriter writer) + private void WriteImageDescriptor(ImageFrame image, EndianBinaryWriter writer) where TPixel : struct, IPixel { writer.Write(GifConstants.ImageDescriptorLabel); // 2c @@ -347,7 +346,7 @@ namespace SixLabors.ImageSharp.Formats.Gif /// Writes the color table to the stream. /// /// The pixel format. - /// The to encode. + /// The to encode. /// The writer to write to the stream with. private void WriteColorTable(QuantizedImage image, EndianBinaryWriter writer) where TPixel : struct, IPixel diff --git a/src/ImageSharp/Formats/Gif/ImageExtensions.cs b/src/ImageSharp/Formats/Gif/ImageExtensions.cs index b5f358f583..939eb456e1 100644 --- a/src/ImageSharp/Formats/Gif/ImageExtensions.cs +++ b/src/ImageSharp/Formats/Gif/ImageExtensions.cs @@ -3,6 +3,7 @@ using System; using System.IO; +using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.PixelFormats; @@ -35,6 +36,6 @@ namespace SixLabors.ImageSharp /// Thrown if the stream is null. public static void SaveAsGif(this Image source, Stream stream, GifEncoder encoder) where TPixel : struct, IPixel - => source.Save(stream, encoder ?? source.Configuration.FindEncoder(ImageFormats.Gif)); + => source.Save(stream, encoder ?? source.GetConfiguration().FindEncoder(ImageFormats.Gif)); } } diff --git a/src/ImageSharp/Formats/IImageDecoder.cs b/src/ImageSharp/Formats/IImageDecoder.cs index 86d5f5375b..e392cf7c61 100644 --- a/src/ImageSharp/Formats/IImageDecoder.cs +++ b/src/ImageSharp/Formats/IImageDecoder.cs @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Formats public interface IImageDecoder { /// - /// Decodes the image from the specified stream to the . + /// Decodes the image from the specified stream to the . /// /// The pixel format. /// The configuration for the image. diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs index 57b29e8ab4..84867d2766 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs @@ -2,6 +2,7 @@ using System; using System.Linq; using System.Numerics; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Helpers; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -16,7 +17,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.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 { @@ -97,7 +98,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder /// /// The pixel type /// The destination image - public void PostProcess(Image destination) + public void PostProcess(ImageFrame destination) where TPixel : struct, IPixel { this.PixelRowCounter = 0; @@ -118,7 +119,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder /// /// The pixel type /// The destination image. - public void DoPostProcessorStep(Image destination) + public void DoPostProcessorStep(ImageFrame destination) where TPixel : struct, IPixel { foreach (JpegComponentPostProcessor cpp in this.ComponentProcessors) @@ -136,7 +137,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder /// /// The pixel type /// The destination image - private void ConvertColorsInto(Image destination) + private void ConvertColorsInto(ImageFrame destination) where TPixel : struct, IPixel { int maxY = Math.Min(destination.Height, this.PixelRowCounter + PixelRowsPerStep); diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs index 33ebe72d01..8369e92366 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs @@ -815,7 +815,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort using (var postProcessor = new JpegImagePostProcessor(this)) { var image = new Image(this.configuration, this.ImageWidth, this.ImageHeight, this.MetaData); - postProcessor.PostProcess(image); + postProcessor.PostProcess(image.Frames.RootFrame); return image; } } diff --git a/src/ImageSharp/Formats/Jpeg/ImageExtensions.cs b/src/ImageSharp/Formats/Jpeg/ImageExtensions.cs index c7d7b26da6..9cd7b3a8bd 100644 --- a/src/ImageSharp/Formats/Jpeg/ImageExtensions.cs +++ b/src/ImageSharp/Formats/Jpeg/ImageExtensions.cs @@ -3,6 +3,7 @@ using System; using System.IO; +using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.PixelFormats; @@ -35,6 +36,6 @@ namespace SixLabors.ImageSharp /// Thrown if the stream is null. public static void SaveAsJpeg(this Image source, Stream stream, JpegEncoder encoder) where TPixel : struct, IPixel - => source.Save(stream, encoder ?? source.Configuration.FindEncoder(ImageFormats.Jpeg)); + => source.Save(stream, encoder ?? source.GetConfiguration().FindEncoder(ImageFormats.Jpeg)); } } diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs index c5225b5467..6c84597c99 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs @@ -159,7 +159,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort this.QuantizeAndInverseAllComponents(); var image = new Image(this.configuration, this.ImageWidth, this.ImageHeight, metadata); - this.FillPixelData(image); + this.FillPixelData(image.Frames.RootFrame); this.AssignResolution(image); return image; } @@ -326,7 +326,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort /// /// The pixel format. /// The image - private void FillPixelData(Image image) + private void FillPixelData(ImageFrame image) where TPixel : struct, IPixel { if (this.NumberOfComponents > 4) @@ -856,7 +856,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void FillGrayScaleImage(Image image) + private void FillGrayScaleImage(ImageFrame image) where TPixel : struct, IPixel { for (int y = 0; y < image.Height; y++) @@ -875,7 +875,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void FillYCbCrImage(Image image) + private void FillYCbCrImage(ImageFrame image) where TPixel : struct, IPixel { for (int y = 0; y < image.Height; y++) @@ -894,7 +894,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void FillYcckImage(Image image) + private void FillYcckImage(ImageFrame image) where TPixel : struct, IPixel { for (int y = 0; y < image.Height; y++) @@ -915,7 +915,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void FillCmykImage(Image image) + private void FillCmykImage(ImageFrame image) where TPixel : struct, IPixel { for (int y = 0; y < image.Height; y++) @@ -941,7 +941,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void FillRgbImage(Image image) + private void FillRgbImage(ImageFrame image) where TPixel : struct, IPixel { for (int y = 0; y < image.Height; y++) diff --git a/src/ImageSharp/Formats/Png/ImageExtensions.cs b/src/ImageSharp/Formats/Png/ImageExtensions.cs index 5d7539915c..10970fc16a 100644 --- a/src/ImageSharp/Formats/Png/ImageExtensions.cs +++ b/src/ImageSharp/Formats/Png/ImageExtensions.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System.IO; +using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.PixelFormats; @@ -34,6 +35,6 @@ namespace SixLabors.ImageSharp /// Thrown if the stream is null. public static void SaveAsPng(this Image source, Stream stream, PngEncoder encoder) where TPixel : struct, IPixel - => source.Save(stream, encoder ?? source.Configuration.FindEncoder(ImageFormats.Png)); + => source.Save(stream, encoder ?? source.GetConfiguration().FindEncoder(ImageFormats.Png)); } } diff --git a/src/ImageSharp/Formats/Png/PngDecoder.cs b/src/ImageSharp/Formats/Png/PngDecoder.cs index 786f3fe901..739fd6051e 100644 --- a/src/ImageSharp/Formats/Png/PngDecoder.cs +++ b/src/ImageSharp/Formats/Png/PngDecoder.cs @@ -42,7 +42,7 @@ namespace SixLabors.ImageSharp.Formats.Png public Encoding TextEncoding { get; set; } = PngConstants.DefaultEncoding; /// - /// Decodes the image from the specified stream to the . + /// Decodes the image from the specified stream to the . /// /// The pixel format. /// The configuration for the image. diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 48618c5eee..4fd57c2784 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -236,7 +236,7 @@ namespace SixLabors.ImageSharp.Formats.Png } deframeStream.AllocateNewBytes(currentChunk.Length); - this.ReadScanlines(deframeStream.CompressedStream, image); + this.ReadScanlines(deframeStream.CompressedStream, image.Frames.RootFrame); stream.Read(this.crcBuffer, 0, 4); break; case PngChunkTypes.Palette: @@ -442,7 +442,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// The pixel format. /// The containing data. /// The pixel data. - private void ReadScanlines(Stream dataStream, Image image) + private void ReadScanlines(Stream dataStream, ImageFrame image) where TPixel : struct, IPixel { if (this.header.InterlaceMethod == PngInterlaceMode.Adam7) @@ -461,7 +461,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// The pixel format. /// The compressed pixel data stream. /// The image to decode to. - private void DecodePixelData(Stream compressedStream, Image image) + private void DecodePixelData(Stream compressedStream, ImageFrame image) where TPixel : struct, IPixel { while (this.currentRow < this.header.Height) @@ -519,7 +519,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// The pixel format. /// The compressed pixel data stream. /// The current image. - private void DecodeInterlacedPixelData(Stream compressedStream, Image image) + private void DecodeInterlacedPixelData(Stream compressedStream, ImageFrame image) where TPixel : struct, IPixel { while (true) @@ -609,7 +609,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// The pixel format. /// The de-filtered scanline /// The image - private void ProcessDefilteredScanline(byte[] defilteredScanline, Image pixels) + private void ProcessDefilteredScanline(byte[] defilteredScanline, ImageFrame pixels) where TPixel : struct, IPixel { var color = default(TPixel); diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index dfc905bfa3..660c371873 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -166,7 +166,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// Encodes the image to the specified stream from the . /// /// The pixel format. - /// The to encode from. + /// The to encode from. /// The to encode the image data to. public void Encode(Image image, Stream stream) where TPixel : struct, IPixel @@ -233,12 +233,12 @@ namespace SixLabors.ImageSharp.Formats.Png // Collect the indexed pixel data if (this.pngColorType == PngColorType.Palette) { - this.CollectIndexedBytes(image, stream, header); + this.CollectIndexedBytes(image.Frames.RootFrame, stream, header); } this.WritePhysicalChunk(stream, image); this.WriteGammaChunk(stream); - this.WriteDataChunks(image, stream); + this.WriteDataChunks(image.Frames.RootFrame, stream); this.WriteEndChunk(stream); stream.Flush(); } @@ -304,7 +304,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// The image to encode. /// The containing image data. /// The . - private void CollectIndexedBytes(ImageBase image, Stream stream, PngHeader header) + private void CollectIndexedBytes(ImageFrame image, Stream stream, PngHeader header) where TPixel : struct, IPixel { // Quantize the image and get the pixels. @@ -529,7 +529,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// The . /// The image to encode. /// The - private QuantizedImage WritePaletteChunk(Stream stream, PngHeader header, ImageBase image) + private QuantizedImage WritePaletteChunk(Stream stream, PngHeader header, ImageFrame image) where TPixel : struct, IPixel { if (this.paletteSize > 256) @@ -649,7 +649,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// The pixel format. /// The image. /// The stream. - private void WriteDataChunks(Image pixels, Stream stream) + private void WriteDataChunks(ImageFrame pixels, Stream stream) where TPixel : struct, IPixel { this.bytesPerScanline = this.width * this.bytesPerPixel; diff --git a/src/ImageSharp/Helpers/ImageExtensions.cs b/src/ImageSharp/Helpers/ImageExtensions.cs new file mode 100644 index 0000000000..dbf2e34a42 --- /dev/null +++ b/src/ImageSharp/Helpers/ImageExtensions.cs @@ -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 +{ + /// + /// Extension methods over Image{TPixel} + /// + public static partial class ImageExtensions + { + /// + /// Gets the bounds of the image. + /// + /// The Pixel format. + /// The source image + /// Returns the bounds of the image + public static Rectangle Bounds(this Image source) + where TPixel : struct, IPixel + => new Rectangle(0, 0, source.Width, source.Height); + + /// + /// Gets the bounds of the image. + /// + /// The Pixel format. + /// The source image + /// Returns the bounds of the image + public static Rectangle Bounds(this ImageFrame source) + where TPixel : struct, IPixel + => new Rectangle(0, 0, source.Width, source.Height); + + /// + /// Gets the size of the image. + /// + /// The Pixel format. + /// The source image + /// Returns the bounds of the image + public static Size Size(this Image source) + where TPixel : struct, IPixel + => new Size(source.Width, source.Height); + + /// + /// Gets the size of the image. + /// + /// The Pixel format. + /// The source image + /// Returns the bounds of the image + public static Size Size(this ImageFrame source) + where TPixel : struct, IPixel + => new Size(source.Width, source.Height); + } +} diff --git a/src/ImageSharp/Image/ICloningImageProcessor.cs b/src/ImageSharp/ICloningImageProcessor.cs similarity index 96% rename from src/ImageSharp/Image/ICloningImageProcessor.cs rename to src/ImageSharp/ICloningImageProcessor.cs index 1e7d6e4f0a..aeb3c815ec 100644 --- a/src/ImageSharp/Image/ICloningImageProcessor.cs +++ b/src/ImageSharp/ICloningImageProcessor.cs @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Processing where TPixel : struct, IPixel { /// - /// Applies the process to the specified portion of the specified . + /// Applies the process to the specified portion of the specified . /// /// The source image. Cannot be null. /// diff --git a/src/ImageSharp/Image/IImageProcessor.cs b/src/ImageSharp/IImageProcessor.cs similarity index 96% rename from src/ImageSharp/Image/IImageProcessor.cs rename to src/ImageSharp/IImageProcessor.cs index b81f08e150..bd6df8d835 100644 --- a/src/ImageSharp/Image/IImageProcessor.cs +++ b/src/ImageSharp/IImageProcessor.cs @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp where TPixel : struct, IPixel { /// - /// Applies the process to the specified portion of the specified . + /// Applies the process to the specified portion of the specified . /// /// The source image. Cannot be null. /// diff --git a/src/ImageSharp/Image/IImage.cs b/src/ImageSharp/Image/IImage.cs deleted file mode 100644 index 3223e20f75..0000000000 --- a/src/ImageSharp/Image/IImage.cs +++ /dev/null @@ -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 -{ - /// - /// Encapsulates the basic properties and methods required to manipulate images. - /// - internal interface IImage : IImageBase - { - /// - /// Gets the meta data of the image. - /// - ImageMetaData MetaData { get; } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Image/IImageBase.cs b/src/ImageSharp/Image/IImageBase.cs deleted file mode 100644 index 9aea1517d6..0000000000 --- a/src/ImageSharp/Image/IImageBase.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.Primitives; - -namespace SixLabors.ImageSharp -{ - /// - /// Encapsulates the basic properties and methods required to manipulate images. - /// - public interface IImageBase - { - /// - /// Gets the width in pixels. - /// - int Width { get; } - - /// - /// Gets the height in pixels. - /// - int Height { get; } - - /// - /// Gets the configuration providing initialization code which allows extending the library. - /// - Configuration Configuration { get; } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Image/IImageFrame.cs b/src/ImageSharp/Image/IImageFrame.cs deleted file mode 100644 index 31a8165887..0000000000 --- a/src/ImageSharp/Image/IImageFrame.cs +++ /dev/null @@ -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 -{ - /// - /// Encapsulates the basic properties and methods required to manipulate images. - /// - internal interface IImageFrame : IImageBase - { - /// - /// Gets the meta data of the image. - /// - ImageFrameMetaData MetaData { get; } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Image/IImageFrameCollection.cs b/src/ImageSharp/Image/IImageFrameCollection.cs new file mode 100644 index 0000000000..ee325bc632 --- /dev/null +++ b/src/ImageSharp/Image/IImageFrameCollection.cs @@ -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 +{ + /// + /// Encapsulates an imaged collection of frames. + /// + /// The type of the pixel. + public interface IImageFrameCollection : IEnumerable> + where TPixel : struct, IPixel + { + /// + /// Gets the count. + /// + int Count { get; } + + /// + /// Gets the root frame. + /// + ImageFrame RootFrame { get; } + + /// + /// Gets or sets the at the specified index. + /// + /// + /// The . + /// + /// The index. + /// The at the specified index. + ImageFrame this[int index] { get; set; } + + /// + /// Determines the index of a specific in the . + /// + /// The to locate in the . + /// The index of item if found in the list; otherwise, -1. + int IndexOf(ImageFrame frame); + + /// + /// Inserts the to the at the specified . + /// + /// The zero-based index at which item should be inserted.. + /// The to insert into the . + void Insert(int index, ImageFrame frame); + + /// + /// Removes the from the at the specified index. + /// + /// The zero-based index of the item to remove. + /// Cannot remove last frame. + void RemoveAt(int index); + + /// + /// Adds the specified frame. + /// + /// The frame. + /// Frame must have the same dimensions as the image - frame + void Add(ImageFrame frame); + + /// + /// Determines whether the contains the . + /// + /// The frame. + /// + /// true if the the specified frame; otherwise, false. + /// + bool Contains(ImageFrame frame); + + /// + /// Removes the specified frame. + /// + /// The frame. + /// true if item is found in the ; otherwise, + /// Cannot remove last frame + bool Remove(ImageFrame frame); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Image/Image.LoadPixelData.cs b/src/ImageSharp/Image/Image.LoadPixelData.cs index d6ed4fcde2..5f1a1617f2 100644 --- a/src/ImageSharp/Image/Image.LoadPixelData.cs +++ b/src/ImageSharp/Image/Image.LoadPixelData.cs @@ -128,7 +128,7 @@ namespace SixLabors.ImageSharp Guard.MustBeGreaterThanOrEqualTo(data.Length, count, nameof(data)); var image = new Image(config, width, height); - SpanHelper.Copy(data, image.GetPixelSpan(), count); + SpanHelper.Copy(data, image.Frames.RootFrame.GetPixelSpan(), count); return image; } diff --git a/src/ImageSharp/Image/ImageBase{TPixel}.cs b/src/ImageSharp/Image/ImageBase{TPixel}.cs deleted file mode 100644 index e9987e0c76..0000000000 --- a/src/ImageSharp/Image/ImageBase{TPixel}.cs +++ /dev/null @@ -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 -{ - /// - /// The base class of all images. Encapsulates the basic properties and methods required to manipulate - /// images in different pixel formats. - /// - /// The pixel format. - public abstract class ImageBase : IImageBase, IDisposable, IPixelSource - where TPixel : struct, IPixel - { -#pragma warning disable SA1401 // Fields must be private - /// - /// The image pixels. Not private as Buffer2D requires an array in its constructor. - /// - internal TPixel[] PixelBuffer; -#pragma warning restore SA1401 // Fields must be private - - /// - /// A value indicating whether this instance of the given entity has been disposed. - /// - /// if this instance has been disposed; otherwise, . - /// - /// 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. - /// - private bool isDisposed; - - /// - /// Initializes a new instance of the class. - /// - /// - /// The configuration providing initialization code which allows extending the library. - /// - protected ImageBase(Configuration configuration) - { - this.Configuration = configuration ?? Configuration.Default; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// The configuration providing initialization code which allows extending the library. - /// - /// The width of the image in pixels. - /// The height of the image in pixels. - /// - /// Thrown if either or are less than or equal to 0. - /// - 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(); - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// The other to create this instance from. - /// - /// - /// Thrown if the given is null. - /// - protected ImageBase(ImageBase 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()); - } - - /// - Span IPixelSource.Span => new Span(this.PixelBuffer, 0, this.Width * this.Height); - - /// - public int Width { get; private set; } - - /// - public int Height { get; private set; } - - /// - /// Gets the configuration providing initialization code which allows extending the library. - /// - public Configuration Configuration { get; private set; } - - /// - /// Gets or sets the pixel at the specified position. - /// - /// The x-coordinate of the pixel. Must be greater than or equal to zero and less than the width of the image. - /// The y-coordinate of the pixel. Must be greater than or equal to zero and less than the height of the image. - /// The at the specified position. - 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; - } - } - - /// - /// Gets a reference to the pixel at the specified position. - /// - /// The x-coordinate of the pixel. Must be greater than or equal to zero and less than the width of the image. - /// The y-coordinate of the pixel. Must be greater than or equal to zero and less than the height of the image. - /// The at the specified position. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal ref TPixel GetPixelReference(int x, int y) - { - this.CheckCoordinates(x, y); - return ref this.PixelBuffer[(y * this.Width) + x]; - } - - /// - /// Clones the image - /// - /// A new items which is a clone of the original. - public ImageBase Clone() - { - return this.CloneImageBase(); - } - - /// - 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); - } - - /// - /// Locks the image providing access to the pixels. - /// - /// It is imperative that the accessor is correctly disposed off after use. - /// - /// - /// The - internal PixelAccessor Lock() - { - return new PixelAccessor(this); - } - - /// - /// Copies the pixels to another of the same size. - /// - /// The target pixel buffer accessor. - internal void CopyTo(PixelAccessor target) - { - SpanHelper.Copy(this.GetPixelSpan(), target.PixelBuffer.Span); - } - - /// - /// 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. - /// - /// The pixel source. - internal void SwapPixelsBuffers(PixelAccessor 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; - } - - /// - /// 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. - /// - /// The pixel source. - internal void SwapPixelsData(ImageBase 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; - } - - /// - /// Clones the image - /// - /// A new items which is a clone of the original. - protected abstract ImageBase CloneImageBase(); - - /// - /// Copies the properties from the other . - /// - /// - /// The other to copy the properties from. - /// - protected void CopyProperties(IImageBase other) - { - DebugGuard.NotNull(other, nameof(other)); - - this.Configuration = other.Configuration; - } - - /// - /// Disposes the object and frees resources for the Garbage Collector. - /// - /// If true, the object gets disposed. - protected virtual void Dispose(bool disposing) - { - if (this.isDisposed) - { - return; - } - - if (disposing) - { - this.ReturnPixels(); - } - - // Note disposing is done. - this.isDisposed = true; - } - - /// - /// Rents the pixel array from the pool. - /// - private void RentPixels() - { - this.PixelBuffer = PixelDataPool.Rent(this.Width * this.Height); - } - - /// - /// Returns the rented pixel array back to the pool. - /// - private void ReturnPixels() - { - PixelDataPool.Return(this.PixelBuffer); - this.PixelBuffer = null; - } - - /// - /// Clears the pixel array. - /// - private void ClearPixels() - { - Array.Clear(this.PixelBuffer, 0, this.Width * this.Height); - } - - /// - /// Checks the coordinates to ensure they are within bounds. - /// - /// The y-coordinate of the pixel. Must be greater than zero and less than the height of the image. - /// - /// Thrown if the coordinates are not within the bounds of the image. - /// - [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."); - } - } - - /// - /// Checks the coordinates to ensure they are within bounds. - /// - /// The x-coordinate of the pixel. Must be greater than zero and less than the width of the image. - /// The y-coordinate of the pixel. Must be greater than zero and less than the height of the image. - /// - /// Thrown if the coordinates are not within the bounds of the image. - /// - [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."); - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Image/ImageExtensions.cs b/src/ImageSharp/Image/ImageExtensions.cs index afd4343815..c5b3d6f31a 100644 --- a/src/ImageSharp/Image/ImageExtensions.cs +++ b/src/ImageSharp/Image/ImageExtensions.cs @@ -18,26 +18,6 @@ namespace SixLabors.ImageSharp /// public static partial class ImageExtensions { - /// - /// Gets the bounds of the image. - /// - /// The Pixel format. - /// The source image - /// Returns the bounds of the image - public static Rectangle Bounds(this ImageBase source) - where TPixel : struct, IPixel - => new Rectangle(0, 0, source.Width, source.Height); - - /// - /// Gets the size of the image. - /// - /// The Pixel format. - /// The source image - /// Returns the bounds of the image - public static Size Size(this ImageBase source) - where TPixel : struct, IPixel - => new Size(source.Width, source.Height); - #if !NETSTANDARD1_1 /// /// Saves the image to the given stream using the currently loaded image format. @@ -52,12 +32,12 @@ namespace SixLabors.ImageSharp Guard.NotNullOrEmpty(filePath, nameof(filePath)); string ext = Path.GetExtension(filePath).Trim('.'); - IImageFormat format = source.Configuration.FindFormatByFileExtension(ext); + IImageFormat format = source.GetConfiguration().FindFormatByFileExtension(ext); if (format == null) { var stringBuilder = new StringBuilder(); stringBuilder.AppendLine($"Can't find a format that is associated with the file extention '{ext}'. Registered formats with there extensions include:"); - foreach (IImageFormat fmt in source.Configuration.ImageFormats) + foreach (IImageFormat fmt in source.GetConfiguration().ImageFormats) { stringBuilder.AppendLine($" - {fmt.Name} : {string.Join(", ", fmt.FileExtensions)}"); } @@ -65,13 +45,13 @@ namespace SixLabors.ImageSharp throw new NotSupportedException(stringBuilder.ToString()); } - IImageEncoder encoder = source.Configuration.FindEncoder(format); + IImageEncoder encoder = source.GetConfiguration().FindEncoder(format); if (encoder == null) { var stringBuilder = new StringBuilder(); stringBuilder.AppendLine($"Can't find encoder for file extention '{ext}' using image format '{format.Name}'. Registered encoders include:"); - foreach (KeyValuePair enc in source.Configuration.ImageEncoders) + foreach (KeyValuePair enc in source.GetConfiguration().ImageEncoders) { stringBuilder.AppendLine($" - {enc.Key} : {enc.Value.GetType().Name}"); } @@ -94,7 +74,7 @@ namespace SixLabors.ImageSharp where TPixel : struct, IPixel { Guard.NotNull(encoder, nameof(encoder)); - using (Stream fs = source.Configuration.FileSystem.Create(filePath)) + using (Stream fs = source.GetConfiguration().FileSystem.Create(filePath)) { source.Save(fs, encoder); } @@ -113,14 +93,14 @@ namespace SixLabors.ImageSharp where TPixel : struct, IPixel { Guard.NotNull(format, nameof(format)); - IImageEncoder encoder = source.Configuration.FindEncoder(format); + IImageEncoder encoder = source.GetConfiguration().FindEncoder(format); if (encoder == null) { var stringBuilder = new StringBuilder(); stringBuilder.AppendLine("Can't find encoder for provided mime type. Available encoded:"); - foreach (KeyValuePair val in source.Configuration.ImageEncoders) + foreach (KeyValuePair val in source.GetConfiguration().ImageEncoders) { stringBuilder.AppendLine($" - {val.Key.Name} : {val.Value.GetType().Name}"); } @@ -131,6 +111,17 @@ namespace SixLabors.ImageSharp source.Save(stream, encoder); } + /// + /// Saves the raw image to the given bytes. + /// + /// The Pixel format. + /// The source image + /// A copy of the pixel data as bytes from this frame. + /// Thrown if the stream is null. + public static byte[] SavePixelData(this ImageFrame source) + where TPixel : struct, IPixel + => source.GetPixelSpan().AsBytes().ToArray(); + /// /// Saves the raw image to the given bytes. /// @@ -138,7 +129,18 @@ namespace SixLabors.ImageSharp /// The source image /// The buffer to save the raw pixel data to. /// Thrown if the stream is null. - internal static void SavePixelData(this Image source, byte[] buffer) + public static void SavePixelData(this ImageFrame source, byte[] buffer) + where TPixel : struct, IPixel + => SavePixelData(source, new Span(buffer)); + + /// + /// Saves the raw image to the given bytes. + /// + /// The Pixel format. + /// The source image + /// The buffer to save the raw pixel data to. + /// Thrown if the stream is null. + private static void SavePixelData(this ImageFrame source, Span buffer) where TPixel : struct, IPixel { Span byteBuffer = source.GetPixelSpan().AsBytes(); @@ -147,6 +149,39 @@ namespace SixLabors.ImageSharp byteBuffer.CopyTo(buffer); } + /// + /// Saves the raw image to the given bytes. + /// + /// The Pixel format. + /// The source image + /// A copy of the pixel data from the first frame as bytes. + /// Thrown if the stream is null. + public static byte[] SavePixelData(this Image source) + where TPixel : struct, IPixel + => source.Frames.RootFrame.SavePixelData(); + + /// + /// Saves the raw image to the given bytes. + /// + /// The Pixel format. + /// The source image + /// The buffer to save the raw pixel data to. + /// Thrown if the stream is null. + public static void SavePixelData(this Image source, byte[] buffer) + where TPixel : struct, IPixel + => source.Frames.RootFrame.SavePixelData(buffer); + + /// + /// Saves the raw image to the given bytes. + /// + /// The Pixel format. + /// The source image + /// The buffer to save the raw pixel data to. + /// Thrown if the stream is null. + private static void SavePixelData(this Image source, Span buffer) + where TPixel : struct, IPixel + => source.Frames.RootFrame.SavePixelData(buffer); + /// /// Returns a Base64 encoded string from the given image. /// diff --git a/src/ImageSharp/Image/ImageFrame.LoadPixelData.cs b/src/ImageSharp/Image/ImageFrame.LoadPixelData.cs new file mode 100644 index 0000000000..aecd9bba9e --- /dev/null +++ b/src/ImageSharp/Image/ImageFrame.LoadPixelData.cs @@ -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 +{ + /// + /// Adds static methods allowing the creation of new image from raw pixel data. + /// + public static partial class ImageFrame + { + /// + /// Create a new instance of the class from the given byte array in format. + /// + /// The byte array containing image data. + /// The width of the final image. + /// The height of the final image. + /// The pixel format. + /// A new . + public static ImageFrame LoadPixelData(Span data, int width, int height) + where TPixel : struct, IPixel + => LoadPixelData(data.NonPortableCast(), width, height); + + /// + /// Create a new instance of the class from the raw data. + /// + /// The Span containing the image Pixel data. + /// The width of the final image. + /// The height of the final image. + /// The pixel format. + /// A new . + public static ImageFrame LoadPixelData(Span data, int width, int height) + where TPixel : struct, IPixel + { + int count = width * height; + Guard.MustBeGreaterThanOrEqualTo(data.Length, count, nameof(data)); + + var image = new ImageFrame(width, height); + SpanHelper.Copy(data, image.GetPixelSpan(), count); + + return image; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Image/ImageFrameCollection.cs b/src/ImageSharp/Image/ImageFrameCollection.cs new file mode 100644 index 0000000000..25c0d0c449 --- /dev/null +++ b/src/ImageSharp/Image/ImageFrameCollection.cs @@ -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 +{ + /// + /// Encapsulates an imaged collection of frames. + /// + /// The type of the pixel. + internal sealed class ImageFrameCollection : IImageFrameCollection, IDisposable + where TPixel : struct, IPixel + { + private readonly IList> frames = new List>(); + + internal ImageFrameCollection(int width, int height) + { + this.Add(new ImageFrame(width, height)); + } + + internal ImageFrameCollection(IEnumerable> frames) + { + Guard.NotNullOrEmpty(frames, nameof(frames)); + foreach (ImageFrame f in frames) + { + this.Add(f); + } + } + + /// + /// Gets the count. + /// + public int Count => this.frames.Count; + + /// + /// Gets the root frame. + /// + public ImageFrame RootFrame => this.frames.Count > 0 ? this.frames[0] : null; + + /// + /// Gets or sets the at the specified index. + /// + /// + /// The . + /// + /// The index. + /// The at the specified index. + public ImageFrame this[int index] + { + get => this.frames[index]; + + set + { + this.ValidateFrame(value); + this.frames[index] = value; + } + } + + /// + /// 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); + + /// + /// Inserts the to the at the specified . + /// + /// The zero-based index at which item should be inserted.. + /// The to insert into the . + public void Insert(int index, ImageFrame frame) + { + this.ValidateFrame(frame); + this.frames.Insert(index, frame); + } + + /// + /// Removes the from the at the specified index. + /// + /// The zero-based index of the item to remove. + /// Cannot remove last frame. + public void RemoveAt(int index) + { + if (index == 0 && this.Count == 1) + { + throw new InvalidOperationException("Cannot remove last frame."); + } + + this.frames.RemoveAt(index); + } + + /// + /// Adds the specified frame. + /// + /// The frame. + /// Frame must have the same dimensions as the image - frame + public void Add(ImageFrame frame) + { + this.ValidateFrame(frame); + this.frames.Add(frame); + } + + /// + /// Determines whether the contains the . + /// + /// The frame. + /// + /// true if the the specified frame; otherwise, false. + /// + public bool Contains(ImageFrame frame) + { + return this.frames.Contains(frame); + } + + /// + /// Removes the specified frame. + /// + /// The frame. + /// true if item is found in the ; otherwise, + /// Cannot remove last frame + public bool Remove(ImageFrame frame) + { + if (this.Count == 1 && this.frames.Contains(frame)) + { + throw new InvalidOperationException("Cannot remove last frame."); + } + + return this.frames.Remove(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)); + } + } + } + + /// + public void Dispose() + { + foreach (ImageFrame f in this.frames) + { + f.Dispose(); + } + + this.frames.Clear(); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Image/ImageFrame{TPixel}.cs b/src/ImageSharp/Image/ImageFrame{TPixel}.cs index bd5e272618..73e3a80aef 100644 --- a/src/ImageSharp/Image/ImageFrame{TPixel}.cs +++ b/src/ImageSharp/Image/ImageFrame{TPixel}.cs @@ -2,8 +2,12 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Diagnostics; using System.Numerics; +using System.Runtime.CompilerServices; using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.MetaData; using SixLabors.ImageSharp.PixelFormats; @@ -13,74 +17,174 @@ namespace SixLabors.ImageSharp /// Represents a single frame in a animation. /// /// The pixel format. - public sealed class ImageFrame : ImageBase, IImageFrame + public sealed class ImageFrame : IPixelSource, IDisposable where TPixel : struct, IPixel { /// - /// Initializes a new instance of the class. + /// The image pixels. Not private as Buffer2D requires an array in its constructor. + /// + private Buffer2D pixelBuffer; + + private bool isDisposed; + + /// + /// Initializes a new instance of the class. /// - /// - /// The configuration providing initialization code which allows extending the library. - /// /// The width of the image in pixels. /// The height of the image in pixels. - public ImageFrame(Configuration configuration, int width, int height) - : base(configuration, width, height) + internal ImageFrame(int width, int height) + : this(width, height, new ImageFrameMetaData()) { - this.MetaData = new ImageFrameMetaData(); } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// - /// The configuration providing initialization code which allows extending the library. - /// /// The width of the image in pixels. /// The height of the image in pixels. - /// The metadata of the frame. - public ImageFrame(Configuration configuration, int width, int height, ImageFrameMetaData metadata) - : base(configuration, width, height) + /// The meta data. + internal ImageFrame(int width, int height, ImageFrameMetaData metaData) { - Guard.NotNull(metadata, nameof(metadata)); - this.MetaData = metadata; + Guard.MustBeGreaterThan(width, 0, nameof(width)); + Guard.MustBeGreaterThan(height, 0, nameof(height)); + Guard.NotNull(metaData, nameof(metaData)); + + this.pixelBuffer = Buffer2D.CreateClean(width, height); + this.MetaData = metaData; } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// The width of the image in pixels. - /// The height of the image in pixels. - public ImageFrame(int width, int height) - : this(null, width, height) + /// The source. + internal ImageFrame(ImageFrame source) { - this.MetaData = new ImageFrameMetaData(); + this.pixelBuffer = new Buffer2D(source.pixelBuffer.Width, source.pixelBuffer.Height); + source.pixelBuffer.Span.CopyTo(this.pixelBuffer.Span); + this.MetaData = source.MetaData.Clone(); } + /// + 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 meta data of the frame. + /// + public ImageFrameMetaData MetaData { get; private set; } + /// - /// Initializes a new instance of the class. + /// Gets or sets the pixel at the specified position. /// - /// The image to create the frame from. - internal ImageFrame(ImageBase image) - : base(image) + /// The x-coordinate of the pixel. Must be greater than or equal to zero and less than the width of the image. + /// The y-coordinate of the pixel. Must be greater than or equal to zero and less than the height of the image. + /// The at the specified position. + public TPixel this[int x, int y] { - this.MetaData = new ImageFrameMetaData(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return this.pixelBuffer[x, y]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set + { + this.pixelBuffer[x, y] = value; + } } /// - /// Initializes a new instance of the class. + /// Performs an explicit conversion from to . /// - /// The image to create the frame from. - private ImageFrame(ImageFrame image) - : base(image) + /// The image. + /// + /// The result of the conversion. + /// + public static implicit operator ImageFrame(Image image) => image.Frames[0]; + + /// + /// Gets a reference to the pixel at the specified position. + /// + /// The x-coordinate of the pixel. Must be greater than or equal to zero and less than the width of the image. + /// The y-coordinate of the pixel. Must be greater than or equal to zero and less than the height of the image. + /// The at the specified position. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal ref TPixel GetPixelReference(int x, int y) { - this.CopyProperties(image); + return ref this.pixelBuffer[x, y]; } /// - /// Gets the meta data of the frame. + /// Locks the image providing access to the pixels. + /// + /// It is imperative that the accessor is correctly disposed off after use. + /// /// - public ImageFrameMetaData MetaData { get; private set; } + /// The + internal PixelAccessor Lock() + { + return new PixelAccessor(this); + } + + /// + /// Copies the pixels to another of the same size. + /// + /// The target pixel buffer accessor. + internal void CopyTo(PixelAccessor target) + { + SpanHelper.Copy(this.GetPixelSpan(), target.PixelBuffer.Span); + } + + /// + /// 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. + /// + /// The pixel source. + internal void SwapPixelsBuffers(PixelAccessor pixelSource) + { + Guard.NotNull(pixelSource, nameof(pixelSource)); + + // Push my memory into the accessor (which in turn unpins the old buffer ready for the images use) + Buffer2D newPixels = pixelSource.SwapBufferOwnership(this.pixelBuffer); + this.pixelBuffer = newPixels; + } + + /// + /// 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. + /// + /// The pixel source. + internal void SwapPixelsBuffers(ImageFrame pixelSource) + { + Guard.NotNull(pixelSource, nameof(pixelSource)); + + ComparableExtensions.Swap(ref this.pixelBuffer, ref pixelSource.pixelBuffer); + } + + /// + /// Disposes the object and frees resources for the Garbage Collector. + /// + public void Dispose() + { + if (this.isDisposed) + { + return; + } + + this.pixelBuffer?.Dispose(); + this.pixelBuffer = null; + + // Note disposing is done. + this.isDisposed = true; + } /// public override string ToString() @@ -103,8 +207,7 @@ namespace SixLabors.ImageSharp Func scaleFunc = PackedPixelConverterHelper.ComputeScaleFunction(); - var target = new ImageFrame(this.Configuration, this.Width, this.Height); - target.CopyProperties(this); + var target = new ImageFrame(this.Width, this.Height, this.MetaData.Clone()); using (PixelAccessor pixels = this.Lock()) using (PixelAccessor targetPixels = target.Lock()) @@ -112,12 +215,12 @@ namespace SixLabors.ImageSharp Parallel.For( 0, target.Height, - this.Configuration.ParallelOptions, + Configuration.Default.ParallelOptions, y => { for (int x = 0; x < target.Width; x++) { - TPixel2 color = default(TPixel2); + var color = default(TPixel2); color.PackFromVector4(scaleFunc(pixels[x, y].ToVector4())); targetPixels[x, y] = color; } @@ -131,28 +234,9 @@ namespace SixLabors.ImageSharp /// Clones the current instance. /// /// The - public new ImageFrame Clone() + public ImageFrame Clone() { return new ImageFrame(this); } - - /// - protected override ImageBase CloneImageBase() - { - return this.Clone(); - } - - /// - /// Copies the properties from the other . - /// - /// - /// The other to copy the properties from. - /// - private void CopyProperties(IImageFrame other) - { - base.CopyProperties(other); - - this.MetaData = new ImageFrameMetaData(other.MetaData); - } } } \ No newline at end of file diff --git a/src/ImageSharp/Image/Image{TPixel}.cs b/src/ImageSharp/Image/Image{TPixel}.cs index 900045dbad..5c35d854a3 100644 --- a/src/ImageSharp/Image/Image{TPixel}.cs +++ b/src/ImageSharp/Image/Image{TPixel}.cs @@ -3,16 +3,13 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; -using System.Numerics; -using System.Text; -using System.Threading.Tasks; +using System.Linq; +using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.MetaData; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.Primitives; namespace SixLabors.ImageSharp { @@ -20,9 +17,12 @@ namespace SixLabors.ImageSharp /// Encapsulates an image, which consists of the pixel data for a graphics image and its attributes. /// /// The pixel format. - public sealed class Image : ImageBase, IImage + public sealed partial class Image : IDisposable, IConfigurable where TPixel : struct, IPixel { + private Configuration configuration; + private ImageFrameCollection frames; + /// /// Initializes a new instance of the class /// with the height and the width of the image. @@ -59,41 +59,41 @@ namespace SixLabors.ImageSharp /// The height of the image in pixels. /// The images metadata. internal Image(Configuration configuration, int width, int height, ImageMetaData metadata) - : base(configuration, width, height) { + this.configuration = configuration ?? Configuration.Default; this.MetaData = metadata ?? new ImageMetaData(); + this.frames = new ImageFrameCollection(width, height); } /// - /// Initializes a new instance of the class - /// by making a copy from another image. + /// Initializes a new instance of the class + /// with the height and the width of the image. /// - /// The other image, where the clone should be made from. - /// is null. - private Image(Image other) - : base(other) + /// The configuration providing initialization code which allows extending the library. + /// The images metadata. + /// The frames that will be owned by this image instance. + internal Image(Configuration configuration, ImageMetaData metadata, IEnumerable> frames) { - foreach (ImageFrame frame in other.Frames) - { - if (frame != null) - { - this.Frames.Add(frame.Clone()); - } - } + this.configuration = configuration ?? Configuration.Default; + this.MetaData = metadata ?? new ImageMetaData(); - this.CopyProperties(other); + this.frames = new ImageFrameCollection(frames); } /// - /// Initializes a new instance of the class - /// by making a copy from another image. + /// Gets the pixel buffer. /// - /// The other image, where the clone should be made from. - /// is null. - private Image(ImageBase other) - : base(other) - { - } + Configuration IConfigurable.Configuration => this.configuration; + + /// + /// Gets the width. + /// + public int Width => this.frames.RootFrame.Width; + + /// + /// Gets the height. + /// + public int Height => this.frames.RootFrame.Height; /// /// Gets the meta data of the image. @@ -101,10 +101,27 @@ namespace SixLabors.ImageSharp public ImageMetaData MetaData { get; private set; } = new ImageMetaData(); /// - /// Gets the other frames associated with this image. + /// Gets the frames. + /// + public IImageFrameCollection Frames => this.frames; + + /// + /// Gets the root frame. /// - /// The list of frame images. - public IList> Frames { get; } = new List>(); + private IPixelSource PixelSource => this.frames?.RootFrame ?? throw new ObjectDisposedException(nameof(Image)); + + /// + /// Gets or sets the pixel at the specified position. + /// + /// The x-coordinate of the pixel. Must be greater than or equal to zero and less than the width of the image. + /// The y-coordinate of the pixel. Must be greater than or equal to zero and less than the height of the image. + /// The at the specified position. + public TPixel this[int x, int y] + { + get => this.PixelSource.PixelBuffer[x, y]; + + set => this.PixelSource.PixelBuffer[x, y] = value; + } /// /// Saves the image to the given stream using the given image encoder. @@ -124,9 +141,11 @@ namespace SixLabors.ImageSharp /// Clones the current image /// /// Returns a new image with all the same metadata as the original. - public new Image Clone() + public Image Clone() { - return new Image(this); + IEnumerable> frames = this.frames.Select(x => x.Clone()).ToArray(); + + return new Image(this.configuration, this.MetaData.Clone(), frames); } /// @@ -143,79 +162,32 @@ namespace SixLabors.ImageSharp public Image CloneAs() where TPixel2 : struct, IPixel { - if (typeof(TPixel2) == typeof(TPixel)) - { - // short circuit when same pixel types - return this.Clone() as Image; - } - - Func scaleFunc = PackedPixelConverterHelper.ComputeScaleFunction(); - - var target = new Image(this.Configuration, this.Width, this.Height); - target.CopyProperties(this); - - using (PixelAccessor pixels = this.Lock()) - using (PixelAccessor targetPixels = target.Lock()) - { - Parallel.For( - 0, - target.Height, - this.Configuration.ParallelOptions, - y => - { - for (int x = 0; x < target.Width; x++) - { - var color = default(TPixel2); - color.PackFromVector4(scaleFunc(pixels[x, y].ToVector4())); - targetPixels[x, y] = color; - } - }); - } - - for (int i = 0; i < this.Frames.Count; i++) - { - target.Frames.Add(this.Frames[i].CloneAs()); - } + IEnumerable> frames = this.frames.Select(x => x.CloneAs()).ToArray(); + var target = new Image(this.configuration, this.MetaData, frames); return target; } /// - /// Creates a new from this instance + /// Releases managed resources. /// - /// The - internal ImageFrame ToFrame() + public void Dispose() { - return new ImageFrame(this); - } - - /// - protected override void Dispose(bool disposing) - { - // ReSharper disable once ForCanBeConvertedToForeach - for (int i = 0; i < this.Frames.Count; i++) - { - this.Frames[i].Dispose(); - } - - base.Dispose(disposing); - } - - /// - protected override ImageBase CloneImageBase() - { - return this.Clone(); + this.frames.Dispose(); } /// - /// Copies the properties from the other . + /// 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. /// - /// - /// The other to copy the properties from. - /// - private void CopyProperties(IImage other) + /// The pixel source. + internal void SwapPixelsBuffers(Image pixelSource) { - this.MetaData = new ImageMetaData(other.MetaData); + Guard.NotNull(pixelSource, nameof(pixelSource)); + + for (int i = 0; i < this.frames.Count; i++) + { + this.frames[i].SwapPixelsBuffers(pixelSource.frames[i]); + } } } } \ No newline at end of file diff --git a/src/ImageSharp/Image/PixelAccessorExtensions.cs b/src/ImageSharp/Image/PixelAccessorExtensions.cs new file mode 100644 index 0000000000..9146ef48da --- /dev/null +++ b/src/ImageSharp/Image/PixelAccessorExtensions.cs @@ -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 +{ + /// + /// Helper methods fro acccess pixel accessors + /// + internal static class PixelAccessorExtensions + { + /// + /// Locks the image providing access to the pixels. + /// + /// It is imperative that the accessor is correctly disposed off after use. + /// + /// + /// The type of the pixel. + /// The frame. + /// + /// The + /// + internal static PixelAccessor Lock(this IPixelSource frame) + where TPixel : struct, IPixel + { + return new PixelAccessor(frame); + } + + /// + /// Locks the image providing access to the pixels. + /// + /// It is imperative that the accessor is correctly disposed off after use. + /// + /// + /// The type of the pixel. + /// The image. + /// + /// The + /// + internal static PixelAccessor Lock(this Image image) + where TPixel : struct, IPixel + { + return image.Frames.RootFrame.Lock(); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Image/PixelAccessor{TPixel}.cs b/src/ImageSharp/Image/PixelAccessor{TPixel}.cs index 90908529f5..3abe28aca0 100644 --- a/src/ImageSharp/Image/PixelAccessor{TPixel}.cs +++ b/src/ImageSharp/Image/PixelAccessor{TPixel}.cs @@ -4,7 +4,7 @@ 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; @@ -23,6 +23,7 @@ namespace SixLabors.ImageSharp /// The containing the pixel data. /// internal Buffer2D PixelBuffer; + private bool ownedBuffer; #pragma warning restore SA1401 // Fields must be private /// @@ -40,14 +41,13 @@ namespace SixLabors.ImageSharp /// Initializes a new instance of the class. /// /// The image to provide pixel access for. - public PixelAccessor(ImageBase image) + public PixelAccessor(IPixelSource image) { Guard.NotNull(image, nameof(image)); - Guard.MustBeGreaterThan(image.Width, 0, "image width"); - Guard.MustBeGreaterThan(image.Height, 0, "image height"); + Guard.MustBeGreaterThan(image.PixelBuffer.Width, 0, "image width"); + Guard.MustBeGreaterThan(image.PixelBuffer.Height, 0, "image height"); - this.SetPixelBufferUnsafe(image.Width, image.Height, image.PixelBuffer); - this.ParallelOptions = image.Configuration.ParallelOptions; + this.SetPixelBufferUnsafe(image.PixelBuffer, false); } /// @@ -56,7 +56,7 @@ namespace SixLabors.ImageSharp /// The width of the image represented by the pixel buffer. /// The height of the image represented by the pixel buffer. public PixelAccessor(int width, int height) - : this(width, height, Buffer2D.CreateClean(width, height)) + : this(width, height, Buffer2D.CreateClean(width, height), true) { } @@ -66,15 +66,14 @@ namespace SixLabors.ImageSharp /// The width of the image represented by the pixel buffer. /// The height of the image represented by the pixel buffer. /// The pixel buffer. - private PixelAccessor(int width, int height, Buffer2D pixels) + /// if set to true [owned buffer]. + private PixelAccessor(int width, int height, Buffer2D pixels, bool ownedBuffer) { Guard.NotNull(pixels, nameof(pixels)); Guard.MustBeGreaterThan(width, 0, nameof(width)); Guard.MustBeGreaterThan(height, 0, nameof(height)); - this.SetPixelBufferUnsafe(width, height, pixels); - - this.ParallelOptions = Configuration.Default.ParallelOptions; + this.SetPixelBufferUnsafe(pixels, ownedBuffer); } /// @@ -100,21 +99,12 @@ namespace SixLabors.ImageSharp /// public int RowStride { get; private set; } - /// - /// Gets the width of the image. - /// + /// public int Width { get; private set; } - /// - /// Gets the height of the image. - /// + /// public int Height { get; private set; } - /// - /// Gets the global parallel options for processing tasks in parallel. - /// - public ParallelOptions ParallelOptions { get; } - /// Span IBuffer2D.Span => this.PixelBuffer; @@ -143,12 +133,10 @@ namespace SixLabors.ImageSharp } } - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// + /// public void Dispose() { - if (this.isDisposed) + if (this.isDisposed || !this.ownedBuffer) { return; } @@ -236,15 +224,13 @@ namespace SixLabors.ImageSharp /// /// Sets the pixel buffer in an unsafe manner. This should not be used unless you know what its doing!!! /// - /// The width. - /// The height. /// The pixels. /// Returns the old pixel data thats has gust been replaced. /// If is true then caller is responsible for ensuring is called. - internal TPixel[] ReturnCurrentColorsAndReplaceThemInternally(int width, int height, TPixel[] pixels) + internal Buffer2D SwapBufferOwnership(Buffer2D pixels) { - TPixel[] oldPixels = this.PixelBuffer.TakeArrayOwnership(); - this.SetPixelBufferUnsafe(width, height, pixels); + Buffer2D oldPixels = this.PixelBuffer; + this.SetPixelBufferUnsafe(pixels, this.ownedBuffer); return oldPixels; } @@ -412,23 +398,18 @@ namespace SixLabors.ImageSharp } } - private void SetPixelBufferUnsafe(int width, int height, TPixel[] pixels) - { - this.SetPixelBufferUnsafe(width, height, new Buffer2D(pixels, width, height)); - } - /// /// Sets the pixel buffer in an unsafe manor this should not be used unless you know what its doing!!! /// - /// The width. - /// The height. /// The pixel buffer - private void SetPixelBufferUnsafe(int width, int height, Buffer2D pixels) + /// if set to true then this instance ownes the buffer and thus should dispose of it afterwards. + private void SetPixelBufferUnsafe(Buffer2D pixels, bool ownedBuffer) { this.PixelBuffer = pixels; + this.ownedBuffer = ownedBuffer; - this.Width = width; - this.Height = height; + this.Width = pixels.Width; + this.Height = pixels.Height; this.PixelSize = Unsafe.SizeOf(); this.RowStride = this.Width * this.PixelSize; } diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs index c4aa98d319..d736b91ee6 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs @@ -48,13 +48,13 @@ namespace SixLabors.ImageSharp.Processing.Processors public TPixel LowerColor { get; set; } /// - protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) + protected override void BeforeApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - new GrayscaleBt709Processor().Apply(source, sourceRectangle); + new GrayscaleBt709Processor().Apply(source, sourceRectangle, configuration); } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { float threshold = this.Threshold; TPixel upper = this.UpperColor; @@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp.Processing.Processors Parallel.For( minY, maxY, - source.Configuration.ParallelOptions, + configuration.ParallelOptions, y => { Span row = source.GetPixelRowSpan(y - startY); diff --git a/src/ImageSharp/Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs index ba79d2a80e..8907770e15 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs @@ -4,6 +4,7 @@ using System; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Dithering; +using SixLabors.ImageSharp.Helpers; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -54,13 +55,13 @@ namespace SixLabors.ImageSharp.Processing.Processors public TPixel LowerColor { get; set; } /// - protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) + protected override void BeforeApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - new GrayscaleBt709Processor().Apply(source, sourceRectangle); + new GrayscaleBt709Processor().Apply(source, sourceRectangle, configuration); } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); int startY = interest.Y; diff --git a/src/ImageSharp/Processing/Processors/Binarization/OrderedDitherProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/OrderedDitherProcessor.cs index 2f76ecda57..203a64cf16 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/OrderedDitherProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/OrderedDitherProcessor.cs @@ -5,6 +5,7 @@ using System; using System.Buffers; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Dithering; +using SixLabors.ImageSharp.Helpers; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -62,13 +63,13 @@ namespace SixLabors.ImageSharp.Processing.Processors public TPixel LowerColor { get; set; } /// - protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) + protected override void BeforeApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - new GrayscaleBt709Processor().Apply(source, sourceRectangle); + new GrayscaleBt709Processor().Apply(source, sourceRectangle, configuration); } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); int startY = interest.Y; diff --git a/src/ImageSharp/Processing/Processors/CloningImageProcessor.cs b/src/ImageSharp/Processing/Processors/CloningImageProcessor.cs index a1fa3c9cba..fdee21ed6a 100644 --- a/src/ImageSharp/Processing/Processors/CloningImageProcessor.cs +++ b/src/ImageSharp/Processing/Processors/CloningImageProcessor.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; - +using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -27,21 +27,17 @@ namespace SixLabors.ImageSharp.Processing throw new ImageProcessingException($"An error occured when processing the image using {this.GetType().Name}. The processor changed the number of frames."); } + Configuration configuration = source.GetConfiguration(); this.BeforeImageApply(source, clone, sourceRectangle); - this.BeforeApply(source, clone, sourceRectangle); - this.OnApply(source, clone, sourceRectangle); - this.AfterApply(source, clone, sourceRectangle); - for (int i = 0; i < source.Frames.Count; i++) { ImageFrame sourceFrame = source.Frames[i]; ImageFrame clonedFrame = clone.Frames[i]; - this.BeforeApply(sourceFrame, clonedFrame, sourceRectangle); - - this.OnApply(sourceFrame, clonedFrame, sourceRectangle); - this.AfterApply(sourceFrame, clonedFrame, sourceRectangle); + this.BeforeApply(sourceFrame, clonedFrame, sourceRectangle, configuration); + this.OnApply(sourceFrame, clonedFrame, sourceRectangle, configuration); + this.AfterApply(sourceFrame, clonedFrame, sourceRectangle, configuration); } this.AfterImageApply(source, clone, sourceRectangle); @@ -71,11 +67,7 @@ namespace SixLabors.ImageSharp.Processing throw new ImageProcessingException($"An error occured when processing the image using {this.GetType().Name}. The processor changed the number of frames."); } - source.SwapPixelsData(cloned); - for (int i = 0; i < source.Frames.Count; i++) - { - source.Frames[i].SwapPixelsData(cloned.Frames[i]); - } + source.SwapPixelsBuffers(cloned); } } @@ -107,33 +99,30 @@ namespace SixLabors.ImageSharp.Processing /// /// The source image. Cannot be null. /// The cloned/destination image. Cannot be null. - /// - /// The structure that specifies the portion of the image object to draw. - /// - protected virtual void BeforeApply(ImageBase source, ImageBase destination, Rectangle sourceRectangle) + /// The structure that specifies the portion of the image object to draw. + /// The configuration. + protected virtual void BeforeApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) { } /// - /// Applies the process to the specified portion of the specified at the specified location + /// Applies the process to the specified portion of the specified at the specified location /// and with the specified size. /// /// The source image. Cannot be null. /// The cloned/destination image. Cannot be null. - /// - /// The structure that specifies the portion of the image object to draw. - /// - protected abstract void OnApply(ImageBase source, ImageBase destination, Rectangle sourceRectangle); + /// The structure that specifies the portion of the image object to draw. + /// The configuration. + protected abstract void OnApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration); /// /// This method is called after the process is applied to prepare the processor. /// /// The source image. Cannot be null. /// The cloned/destination image. Cannot be null. - /// - /// The structure that specifies the portion of the image object to draw. - /// - protected virtual void AfterApply(ImageBase source, ImageBase destination, Rectangle sourceRectangle) + /// The structure that specifies the portion of the image object to draw. + /// The configuration. + protected virtual void AfterApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) { } diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorMatrixProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorMatrixProcessor.cs index c4da8dee7d..4a64bfaa0d 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorMatrixProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorMatrixProcessor.cs @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Processing.Processors public virtual bool Compand { get; set; } = true; /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; @@ -54,7 +54,7 @@ namespace SixLabors.ImageSharp.Processing.Processors Parallel.For( minY, maxY, - source.Configuration.ParallelOptions, + configuration.ParallelOptions, y => { Span row = source.GetPixelRowSpan(y - startY); diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/LomographProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/LomographProcessor.cs index dcb5955c00..1ec76bf554 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/LomographProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/LomographProcessor.cs @@ -39,9 +39,9 @@ namespace SixLabors.ImageSharp.Processing.Processors }; /// - protected override void AfterApply(ImageBase source, Rectangle sourceRectangle) + protected override void AfterApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - new VignetteProcessor(VeryDarkGreen, this.options).Apply(source, sourceRectangle); + new VignetteProcessor(VeryDarkGreen, this.options).Apply(source, sourceRectangle, configuration); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/PolaroidProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/PolaroidProcessor.cs index 928a641a00..f910562e64 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/PolaroidProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/PolaroidProcessor.cs @@ -46,10 +46,10 @@ namespace SixLabors.ImageSharp.Processing.Processors }; /// - protected override void AfterApply(ImageBase source, Rectangle sourceRectangle) + protected override void AfterApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - new VignetteProcessor(VeryDarkOrange, this.options).Apply(source, sourceRectangle); - new GlowProcessor(LightOrange, source.Width / 4F, this.options).Apply(source, sourceRectangle); + new VignetteProcessor(VeryDarkOrange, this.options).Apply(source, sourceRectangle, configuration); + new GlowProcessor(LightOrange, source.Width / 4F, this.options).Apply(source, sourceRectangle, configuration); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs index 3c95d29974..8056141a09 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs @@ -50,9 +50,9 @@ namespace SixLabors.ImageSharp.Processing.Processors public Fast2DArray KernelY { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - new Convolution2PassProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle); + new Convolution2PassProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle, configuration); } /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs index d109cb10e8..b85432ac54 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs @@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Processing.Processors public Fast2DArray KernelY { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { int kernelYHeight = this.KernelY.Height; int kernelYWidth = this.KernelY.Width; @@ -63,7 +63,7 @@ namespace SixLabors.ImageSharp.Processing.Processors Parallel.For( startY, endY, - source.Configuration.ParallelOptions, + configuration.ParallelOptions, y => { Span sourceRow = source.GetPixelRowSpan(y); diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs index 08ea5a6412..362fa5c508 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs @@ -4,6 +4,8 @@ using System; using System.Numerics; using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Helpers; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -39,11 +41,11 @@ namespace SixLabors.ImageSharp.Processing.Processors public Fast2DArray KernelY { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { int width = source.Width; int height = source.Height; - ParallelOptions parallelOptions = source.Configuration.ParallelOptions; + ParallelOptions parallelOptions = configuration.ParallelOptions; using (var firstPassPixels = new PixelAccessor(width, height)) using (PixelAccessor sourcePixels = source.Lock()) @@ -54,7 +56,7 @@ namespace SixLabors.ImageSharp.Processing.Processors } /// - /// Applies the process to the specified portion of the specified at the specified location + /// Applies the process to the specified portion of the specified at the specified location /// and with the specified size. /// /// The target pixels to apply the process to. diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs index fa1f18ea96..c0d3fdcfec 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs @@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Processing.Processors public Fast2DArray KernelXY { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { int kernelLength = this.KernelXY.Height; int radius = kernelLength >> 1; @@ -52,7 +52,7 @@ namespace SixLabors.ImageSharp.Processing.Processors Parallel.For( startY, endY, - source.Configuration.ParallelOptions, + configuration.ParallelOptions, y => { Span sourceRow = source.GetPixelRowSpan(y); diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetector2DProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetector2DProcessor.cs index af7a5e276e..f93787d129 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetector2DProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetector2DProcessor.cs @@ -40,17 +40,17 @@ namespace SixLabors.ImageSharp.Processing.Processors public bool Grayscale { get; set; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - new Convolution2DProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle); + new Convolution2DProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle, configuration); } /// - protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) + protected override void BeforeApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { if (this.Grayscale) { - new GrayscaleBt709Processor().Apply(source, sourceRectangle); + new GrayscaleBt709Processor().Apply(source, sourceRectangle, configuration); } } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs index aa3c605355..32c22a8ce9 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs @@ -4,6 +4,7 @@ using System; using System.Numerics; using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -61,16 +62,16 @@ namespace SixLabors.ImageSharp.Processing.Processors public bool Grayscale { get; set; } /// - protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) + protected override void BeforeApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { if (this.Grayscale) { - new GrayscaleBt709Processor().Apply(source, sourceRectangle); + new GrayscaleBt709Processor().Apply(source, sourceRectangle, configuration); } } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { Fast2DArray[] kernels = { this.North, this.NorthWest, this.West, this.SouthWest, this.South, this.SouthEast, this.East, this.NorthEast }; @@ -86,9 +87,9 @@ namespace SixLabors.ImageSharp.Processing.Processors int maxY = Math.Min(source.Height, endY); // we need a clean copy for each pass to start from - using (ImageBase cleanCopy = source.Clone()) + using (ImageFrame cleanCopy = source.Clone()) { - new ConvolutionProcessor(kernels[0]).Apply(source, sourceRectangle); + new ConvolutionProcessor(kernels[0]).Apply(source, sourceRectangle, configuration); if (kernels.Length == 1) { @@ -113,9 +114,9 @@ namespace SixLabors.ImageSharp.Processing.Processors // ReSharper disable once ForCanBeConvertedToForeach for (int i = 1; i < kernels.Length; i++) { - using (ImageBase pass = cleanCopy.Clone()) + using (ImageFrame pass = cleanCopy.Clone()) { - new ConvolutionProcessor(kernels[i]).Apply(pass, sourceRectangle); + new ConvolutionProcessor(kernels[i]).Apply(pass, sourceRectangle, configuration); using (PixelAccessor passPixels = pass.Lock()) using (PixelAccessor targetPixels = source.Lock()) @@ -123,7 +124,7 @@ namespace SixLabors.ImageSharp.Processing.Processors Parallel.For( minY, maxY, - source.Configuration.ParallelOptions, + configuration.ParallelOptions, y => { int offsetY = y - shiftY; diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorProcessor.cs index b8e5106b7f..3b98b77fc8 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorProcessor.cs @@ -33,18 +33,18 @@ namespace SixLabors.ImageSharp.Processing.Processors public Fast2DArray KernelXY { get; } /// - protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) + protected override void BeforeApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { if (this.Grayscale) { - new GrayscaleBt709Processor().Apply(source, sourceRectangle); + new GrayscaleBt709Processor().Apply(source, sourceRectangle, configuration); } } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - new ConvolutionProcessor(this.KernelXY).Apply(source, sourceRectangle); + new ConvolutionProcessor(this.KernelXY).Apply(source, sourceRectangle, configuration); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs index 583d38bf7f..c897efeed8 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs @@ -85,9 +85,9 @@ namespace SixLabors.ImageSharp.Processing.Processors public Fast2DArray KernelY { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - new Convolution2PassProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle); + new Convolution2PassProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle, configuration); } /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs index e22904ae1c..b960e9075f 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs @@ -87,9 +87,9 @@ namespace SixLabors.ImageSharp.Processing.Processors public Fast2DArray KernelY { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - new Convolution2PassProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle); + new Convolution2PassProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle, configuration); } /// diff --git a/src/ImageSharp/Processing/Processors/DelegateProcessor.cs b/src/ImageSharp/Processing/Processors/DelegateProcessor.cs index e1bd8c5265..f17c39681e 100644 --- a/src/ImageSharp/Processing/Processors/DelegateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/DelegateProcessor.cs @@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.Processing } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { // NOP, we did all we wanted to do inside BeforeImageApply } diff --git a/src/ImageSharp/Processing/Processors/Effects/AlphaProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/AlphaProcessor.cs index 5cdf63210b..7e5bd02abc 100644 --- a/src/ImageSharp/Processing/Processors/Effects/AlphaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/AlphaProcessor.cs @@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Processing.Processors public float Value { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; @@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.Processing.Processors Parallel.For( minY, maxY, - source.Configuration.ParallelOptions, + configuration.ParallelOptions, y => { Span row = source.GetPixelRowSpan(y - startY); diff --git a/src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs index da3b78c3fb..72e9b8f555 100644 --- a/src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs @@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Processing.Processors public TPixel Value { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; @@ -80,7 +80,7 @@ namespace SixLabors.ImageSharp.Processing.Processors Parallel.For( minY, maxY, - source.Configuration.ParallelOptions, + configuration.ParallelOptions, y => { Span destination = source.GetPixelRowSpan(y - startY).Slice(minX - startX, width); diff --git a/src/ImageSharp/Processing/Processors/Effects/BrightnessProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/BrightnessProcessor.cs index 2c96ddb11e..c864330c9d 100644 --- a/src/ImageSharp/Processing/Processors/Effects/BrightnessProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/BrightnessProcessor.cs @@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Processing.Processors public int Value { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { float brightness = this.Value / 100F; @@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.Processing.Processors Parallel.For( minY, maxY, - source.Configuration.ParallelOptions, + configuration.ParallelOptions, y => { Span row = source.GetPixelRowSpan(y - startY); diff --git a/src/ImageSharp/Processing/Processors/Effects/ContrastProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/ContrastProcessor.cs index d4ec49fd11..5ab2662110 100644 --- a/src/ImageSharp/Processing/Processors/Effects/ContrastProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/ContrastProcessor.cs @@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Processing.Processors public int Value { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { float contrast = (100F + this.Value) / 100F; @@ -67,7 +67,7 @@ namespace SixLabors.ImageSharp.Processing.Processors Parallel.For( minY, maxY, - source.Configuration.ParallelOptions, + configuration.ParallelOptions, y => { Span row = source.GetPixelRowSpan(y - startY); diff --git a/src/ImageSharp/Processing/Processors/Effects/InvertProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/InvertProcessor.cs index 35443fdd51..448025f70a 100644 --- a/src/ImageSharp/Processing/Processors/Effects/InvertProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/InvertProcessor.cs @@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Processing.Processors where TPixel : struct, IPixel { /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; @@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.Processing.Processors Parallel.For( minY, maxY, - source.Configuration.ParallelOptions, + configuration.ParallelOptions, y => { Span row = source.GetPixelRowSpan(y - startY); diff --git a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs index ab55885560..b22a497987 100644 --- a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs @@ -48,7 +48,7 @@ namespace SixLabors.ImageSharp.Processing.Processors public int BrushSize { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { if (this.BrushSize <= 0 || this.BrushSize > source.Height || this.BrushSize > source.Width) { @@ -72,7 +72,7 @@ namespace SixLabors.ImageSharp.Processing.Processors Parallel.For( startY, maxY, - source.Configuration.ParallelOptions, + configuration.ParallelOptions, y => { Span sourceRow = source.GetPixelRowSpan(y); diff --git a/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs index a7513d1cbf..0ab21f65ac 100644 --- a/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs @@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.Processing.Processors public int Size { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { if (this.Size <= 0 || this.Size > source.Height || this.Size > source.Width) { @@ -73,7 +73,7 @@ namespace SixLabors.ImageSharp.Processing.Processors Parallel.ForEach( range, - source.Configuration.ParallelOptions, + configuration.ParallelOptions, y => { int offsetY = y - startY; diff --git a/src/ImageSharp/Processing/Processors/ImageProcessor.cs b/src/ImageSharp/Processing/Processors/ImageProcessor.cs index 1a4d52d3ed..cab99112c1 100644 --- a/src/ImageSharp/Processing/Processors/ImageProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ImageProcessor.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; - +using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -20,18 +20,12 @@ namespace SixLabors.ImageSharp.Processing { try { + Configuration config = source.GetConfiguration(); this.BeforeImageApply(source, sourceRectangle); - this.BeforeApply(source, sourceRectangle); - this.OnApply(source, sourceRectangle); - this.AfterApply(source, sourceRectangle); - foreach (ImageFrame sourceFrame in source.Frames) { - this.BeforeApply(sourceFrame, sourceRectangle); - - this.OnApply(sourceFrame, sourceRectangle); - this.AfterApply(sourceFrame, sourceRectangle); + this.Apply(sourceFrame, sourceRectangle, config); } this.AfterImageApply(source, sourceRectangle); @@ -53,13 +47,14 @@ namespace SixLabors.ImageSharp.Processing /// /// the source image /// the target - public void Apply(ImageBase source, Rectangle sourceRectangle) + /// The configuration. + public void Apply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { try { - this.BeforeApply(source, sourceRectangle); - this.OnApply(source, sourceRectangle); - this.AfterApply(source, sourceRectangle); + this.BeforeApply(source, sourceRectangle, configuration); + this.OnApply(source, sourceRectangle, configuration); + this.AfterApply(source, sourceRectangle, configuration); } #if DEBUG catch (Exception) @@ -77,9 +72,7 @@ namespace SixLabors.ImageSharp.Processing /// This method is called before the process is applied to prepare the processor. /// /// The source image. Cannot be null. - /// - /// The structure that specifies the portion of the image object to draw. - /// + /// The structure that specifies the portion of the image object to draw. protected virtual void BeforeImageApply(Image source, Rectangle sourceRectangle) { } @@ -88,31 +81,28 @@ namespace SixLabors.ImageSharp.Processing /// This method is called before the process is applied to prepare the processor. /// /// The source image. Cannot be null. - /// - /// The structure that specifies the portion of the image object to draw. - /// - protected virtual void BeforeApply(ImageBase source, Rectangle sourceRectangle) + /// The structure that specifies the portion of the image object to draw. + /// The configuration. + protected virtual void BeforeApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { } /// - /// Applies the process to the specified portion of the specified at the specified location + /// Applies the process to the specified portion of the specified at the specified location /// and with the specified size. /// /// The source image. Cannot be null. - /// - /// The structure that specifies the portion of the image object to draw. - /// - protected abstract void OnApply(ImageBase source, Rectangle sourceRectangle); + /// The structure that specifies the portion of the image object to draw. + /// The configuration. + protected abstract void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration); /// /// This method is called after the process is applied to prepare the processor. /// /// The source image. Cannot be null. - /// - /// The structure that specifies the portion of the image object to draw. - /// - protected virtual void AfterApply(ImageBase source, Rectangle sourceRectangle) + /// The structure that specifies the portion of the image object to draw. + /// The configuration. + protected virtual void AfterApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { } @@ -120,9 +110,7 @@ namespace SixLabors.ImageSharp.Processing /// This method is called after the process is applied to prepare the processor. /// /// The source image. Cannot be null. - /// - /// The structure that specifies the portion of the image object to draw. - /// + /// The structure that specifies the portion of the image object to draw. protected virtual void AfterImageApply(Image source, Rectangle sourceRectangle) { } diff --git a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs index 47ca3b1357..b02585d8fd 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs @@ -5,6 +5,7 @@ using System; using System.Numerics; using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Helpers; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -51,7 +52,7 @@ namespace SixLabors.ImageSharp.Processing.Processors public ValueSize Radius { get; set; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; @@ -92,7 +93,7 @@ namespace SixLabors.ImageSharp.Processing.Processors Parallel.For( minY, maxY, - source.Configuration.ParallelOptions, + configuration.ParallelOptions, y => { using (var amounts = new Buffer(width)) diff --git a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs index bad6cbf419..7b592a6a4d 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs @@ -5,6 +5,7 @@ using System; using System.Numerics; using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Helpers; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -70,7 +71,7 @@ namespace SixLabors.ImageSharp.Processing.Processors public ValueSize RadiusY { get; set; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; @@ -113,7 +114,7 @@ namespace SixLabors.ImageSharp.Processing.Processors Parallel.For( minY, maxY, - source.Configuration.ParallelOptions, + configuration.ParallelOptions, y => { using (var amounts = new Buffer(width)) diff --git a/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs index bf9f86788e..ab93e0e384 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs @@ -22,55 +22,55 @@ namespace SixLabors.ImageSharp.Processing.Processors { } - /// - protected override void OnApply(ImageBase sourceBase, Rectangle sourceRectangle) + protected override void BeforeImageApply(Image source, Rectangle sourceRectangle) { - // can only apply to the origional image - var source = sourceBase as Image; - if (source != null) + Orientation orientation = GetExifOrientation(source); + + switch (orientation) { - Orientation orientation = GetExifOrientation(source); - - switch (orientation) - { - case Orientation.TopRight: - new FlipProcessor(FlipType.Horizontal).Apply(source, sourceRectangle); - break; - - case Orientation.BottomRight: - new RotateProcessor() { Angle = (int)RotateType.Rotate180, Expand = false }.Apply(source, sourceRectangle); - break; - - case Orientation.BottomLeft: - new FlipProcessor(FlipType.Vertical).Apply(source, sourceRectangle); - break; - - case Orientation.LeftTop: - new RotateProcessor() { Angle = (int)RotateType.Rotate90, Expand = false }.Apply(source, sourceRectangle); - new FlipProcessor(FlipType.Horizontal).Apply(source, sourceRectangle); - break; - - case Orientation.RightTop: - new RotateProcessor() { Angle = (int)RotateType.Rotate90, Expand = false }.Apply(source, sourceRectangle); - break; - - case Orientation.RightBottom: - new FlipProcessor(FlipType.Vertical).Apply(source, sourceRectangle); - new RotateProcessor() { Angle = (int)RotateType.Rotate270, Expand = false }.Apply(source, sourceRectangle); - break; - - case Orientation.LeftBottom: - new RotateProcessor() { Angle = (int)RotateType.Rotate270, Expand = false }.Apply(source, sourceRectangle); - break; - - case Orientation.Unknown: - case Orientation.TopLeft: - default: - break; - } + case Orientation.TopRight: + new FlipProcessor(FlipType.Horizontal).Apply(source, sourceRectangle); + break; + + case Orientation.BottomRight: + new RotateProcessor() { Angle = (int)RotateType.Rotate180, Expand = false }.Apply(source, sourceRectangle); + break; + + case Orientation.BottomLeft: + new FlipProcessor(FlipType.Vertical).Apply(source, sourceRectangle); + break; + + case Orientation.LeftTop: + new RotateProcessor() { Angle = (int)RotateType.Rotate90, Expand = false }.Apply(source, sourceRectangle); + new FlipProcessor(FlipType.Horizontal).Apply(source, sourceRectangle); + break; + + case Orientation.RightTop: + new RotateProcessor() { Angle = (int)RotateType.Rotate90, Expand = false }.Apply(source, sourceRectangle); + break; + + case Orientation.RightBottom: + new FlipProcessor(FlipType.Vertical).Apply(source, sourceRectangle); + new RotateProcessor() { Angle = (int)RotateType.Rotate270, Expand = false }.Apply(source, sourceRectangle); + break; + + case Orientation.LeftBottom: + new RotateProcessor() { Angle = (int)RotateType.Rotate270, Expand = false }.Apply(source, sourceRectangle); + break; + + case Orientation.Unknown: + case Orientation.TopLeft: + default: + break; } } + /// + protected override void OnApply(ImageFrame sourceBase, Rectangle sourceRectangle, Configuration config) + { + // all processing happens at the image level within BeforeImageApply(); + } + /// /// Returns the current EXIF orientation /// diff --git a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs index 1d0ab6d1f1..2657daaa8a 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs @@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Processing.Processors public Rectangle CropRectangle { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { if (this.CropRectangle == sourceRectangle) { @@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Processing.Processors Parallel.For( minY, maxY, - source.Configuration.ParallelOptions, + configuration.ParallelOptions, y => { Span sourceRow = source.GetPixelRowSpan(y).Slice(minX); diff --git a/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs index 89e22a7e6b..d2a08daba6 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs @@ -34,15 +34,15 @@ namespace SixLabors.ImageSharp.Processing.Processors public float Threshold { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - using (ImageBase temp = source.Clone()) + using (ImageFrame temp = source.Clone()) { // Detect the edges. - new SobelProcessor().Apply(temp, sourceRectangle); + new SobelProcessor().Apply(temp, sourceRectangle, configuration); // Apply threshold binarization filter. - new BinaryThresholdProcessor(this.Threshold).Apply(temp, sourceRectangle); + new BinaryThresholdProcessor(this.Threshold).Apply(temp, sourceRectangle, configuration); // Search for the first white pixels Rectangle rectangle = ImageMaths.GetFilteredBoundingRectangle(temp, 0); @@ -52,7 +52,7 @@ namespace SixLabors.ImageSharp.Processing.Processors return; } - new CropProcessor(rectangle).Apply(source, sourceRectangle); + new CropProcessor(rectangle).Apply(source, sourceRectangle, configuration); } } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs index 1cb92cca90..de60177f2f 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs @@ -32,16 +32,16 @@ namespace SixLabors.ImageSharp.Processing.Processors public FlipType FlipType { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { switch (this.FlipType) { // No default needed as we have already set the pixels. case FlipType.Vertical: - this.FlipX(source); + this.FlipX(source, configuration); break; case FlipType.Horizontal: - this.FlipY(source); + this.FlipY(source, configuration); break; } } @@ -51,7 +51,8 @@ namespace SixLabors.ImageSharp.Processing.Processors /// at half the height of the image. /// /// The source image to apply the process to. - private void FlipX(ImageBase source) + /// The configuration. + private void FlipX(ImageFrame source, Configuration configuration) { int width = source.Width; int height = source.Height; @@ -62,7 +63,7 @@ namespace SixLabors.ImageSharp.Processing.Processors Parallel.For( 0, halfHeight, - source.Configuration.ParallelOptions, + configuration.ParallelOptions, y => { int newY = height - y - 1; @@ -84,7 +85,8 @@ namespace SixLabors.ImageSharp.Processing.Processors /// at half of the width of the image. /// /// The source image to apply the process to. - private void FlipY(ImageBase source) + /// The configuration. + private void FlipY(ImageFrame source, Configuration configuration) { int width = source.Width; int height = source.Height; @@ -95,7 +97,7 @@ namespace SixLabors.ImageSharp.Processing.Processors Parallel.For( 0, height, - source.Configuration.ParallelOptions, + configuration.ParallelOptions, y => { Span sourceRow = source.GetPixelRowSpan(y); diff --git a/src/ImageSharp/Processing/Processors/Transforms/Matrix3x2Processor.cs b/src/ImageSharp/Processing/Processors/Transforms/Matrix3x2Processor.cs index 54724ee782..4a15254ab2 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Matrix3x2Processor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Matrix3x2Processor.cs @@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Processing.Processors /// /// The . /// - protected Matrix3x2 GetCenteredMatrix(ImageBase source, Matrix3x2 matrix) + protected Matrix3x2 GetCenteredMatrix(ImageFrame source, Matrix3x2 matrix) { var translationToTargetCenter = Matrix3x2.CreateTranslation(-this.CanvasRectangle.Width * .5F, -this.CanvasRectangle.Height * .5F); var translateToSourceCenter = Matrix3x2.CreateTranslation(source.Width * .5F, source.Height * .5F); diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs index 8d5eeded15..781f3a01c7 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs @@ -137,7 +137,7 @@ namespace SixLabors.ImageSharp.Processing.Processors } /// - protected override void BeforeApply(ImageBase source, ImageBase destination, Rectangle sourceRectangle) + protected override void BeforeApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) { if (!(this.Sampler is NearestNeighborResampler)) { @@ -152,9 +152,9 @@ namespace SixLabors.ImageSharp.Processing.Processors } /// - protected override void AfterApply(ImageBase source, ImageBase destination, Rectangle sourceRectangle) + protected override void AfterApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) { - base.AfterApply(source, destination, sourceRectangle); + base.AfterApply(source, destination, sourceRectangle, configuration); this.HorizontalWeights?.Dispose(); this.HorizontalWeights = null; this.VerticalWeights?.Dispose(); diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs index 57b9a81f90..a4fdb1a1b4 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs @@ -2,6 +2,8 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Collections.Generic; +using System.Linq; using System.Numerics; using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; @@ -51,24 +53,20 @@ namespace SixLabors.ImageSharp.Processing.Processors /// protected override Image CreateDestination(Image source, Rectangle sourceRectangle) { + Configuration config = source.GetConfiguration(); + // We will always be creating the clone even for mutate because thats the way this base processor works // ------------ // For resize we know we are going to populate every pixel with fresh data and we want a different target size so // let's manually clone an empty set of images at the correct target and then have the base class processs them in turn. - var image = new Image(source.Configuration, this.Width, this.Height, source.MetaData.Clone()); - - // Now 'clone' the ImageFrames - foreach (ImageFrame sourceFrame in source.Frames) - { - var targetFrame = new ImageFrame(sourceFrame.Configuration, this.Width, this.Height, sourceFrame.MetaData.Clone()); - image.Frames.Add(targetFrame); - } + IEnumerable> frames = source.Frames.Select(x => new ImageFrame(this.Width, this.Height, x.MetaData.Clone())); // this will create places holders + var image = new Image(config, source.MetaData.Clone(), frames); // base the place holder images in to prevet a extra frame being added return image; } /// - protected override unsafe void OnApply(ImageBase source, ImageBase cloned, Rectangle sourceRectangle) + protected override unsafe void OnApply(ImageFrame source, ImageFrame cloned, Rectangle sourceRectangle, Configuration configuration) { // Jump out, we'll deal with that later. if (source.Width == cloned.Width && source.Height == cloned.Height && sourceRectangle == this.ResizeRectangle) @@ -101,7 +99,7 @@ namespace SixLabors.ImageSharp.Processing.Processors Parallel.For( minY, maxY, - source.Configuration.ParallelOptions, + configuration.ParallelOptions, y => { // Y coordinates of source points @@ -130,7 +128,7 @@ namespace SixLabors.ImageSharp.Processing.Processors Parallel.For( 0, sourceRectangle.Bottom, - source.Configuration.ParallelOptions, + configuration.ParallelOptions, y => { // TODO: Without Parallel.For() this buffer object could be reused: @@ -163,7 +161,7 @@ namespace SixLabors.ImageSharp.Processing.Processors Parallel.For( minY, maxY, - source.Configuration.ParallelOptions, + configuration.ParallelOptions, y => { // Ensure offsets are normalised for cropping and padding. diff --git a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs index 5b8952dc8a..a7fb400acc 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs @@ -5,6 +5,7 @@ using System; using System.Numerics; using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Helpers; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -34,9 +35,9 @@ namespace SixLabors.ImageSharp.Processing.Processors public bool Expand { get; set; } = true; /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - if (this.OptimizedApply(source)) + if (this.OptimizedApply(source, configuration)) { return; } @@ -51,7 +52,7 @@ namespace SixLabors.ImageSharp.Processing.Processors Parallel.For( 0, height, - source.Configuration.ParallelOptions, + configuration.ParallelOptions, y => { Span targetRow = targetPixels.GetRowSpan(y); @@ -72,7 +73,7 @@ namespace SixLabors.ImageSharp.Processing.Processors } /// - protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) + protected override void BeforeApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { if (MathF.Abs(this.Angle) < Constants.Epsilon || MathF.Abs(this.Angle - 90) < Constants.Epsilon || MathF.Abs(this.Angle - 180) < Constants.Epsilon || MathF.Abs(this.Angle - 270) < Constants.Epsilon) { @@ -90,8 +91,11 @@ namespace SixLabors.ImageSharp.Processing.Processors /// Rotates the images with an optimized method when the angle is 90, 180 or 270 degrees. /// /// The source image. - /// The - private bool OptimizedApply(ImageBase source) + /// The configuration. + /// + /// The + /// + private bool OptimizedApply(ImageFrame source, Configuration configuration) { if (MathF.Abs(this.Angle) < Constants.Epsilon) { @@ -101,19 +105,19 @@ namespace SixLabors.ImageSharp.Processing.Processors if (MathF.Abs(this.Angle - 90) < Constants.Epsilon) { - this.Rotate90(source); + this.Rotate90(source, configuration); return true; } if (MathF.Abs(this.Angle - 180) < Constants.Epsilon) { - this.Rotate180(source); + this.Rotate180(source, configuration); return true; } if (MathF.Abs(this.Angle - 270) < Constants.Epsilon) { - this.Rotate270(source); + this.Rotate270(source, configuration); return true; } @@ -124,7 +128,8 @@ namespace SixLabors.ImageSharp.Processing.Processors /// Rotates the image 270 degrees clockwise at the centre point. /// /// The source image. - private void Rotate270(ImageBase source) + /// The configuration. + private void Rotate270(ImageFrame source, Configuration configuration) { int width = source.Width; int height = source.Height; @@ -136,7 +141,7 @@ namespace SixLabors.ImageSharp.Processing.Processors Parallel.For( 0, height, - source.Configuration.ParallelOptions, + configuration.ParallelOptions, y => { for (int x = 0; x < width; x++) @@ -157,7 +162,8 @@ namespace SixLabors.ImageSharp.Processing.Processors /// Rotates the image 180 degrees clockwise at the centre point. /// /// The source image. - private void Rotate180(ImageBase source) + /// The configuration. + private void Rotate180(ImageFrame source, Configuration configuration) { int width = source.Width; int height = source.Height; @@ -167,7 +173,7 @@ namespace SixLabors.ImageSharp.Processing.Processors Parallel.For( 0, height, - source.Configuration.ParallelOptions, + configuration.ParallelOptions, y => { Span sourceRow = source.GetPixelRowSpan(y); @@ -187,7 +193,8 @@ namespace SixLabors.ImageSharp.Processing.Processors /// Rotates the image 90 degrees clockwise at the centre point. /// /// The source image. - private void Rotate90(ImageBase source) + /// The configuration. + private void Rotate90(ImageFrame source, Configuration configuration) { int width = source.Width; int height = source.Height; @@ -197,7 +204,7 @@ namespace SixLabors.ImageSharp.Processing.Processors Parallel.For( 0, height, - source.Configuration.ParallelOptions, + configuration.ParallelOptions, y => { Span sourceRow = source.GetPixelRowSpan(y); diff --git a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs index 85bf1b6938..316e2a2af3 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs @@ -4,6 +4,8 @@ using System; using System.Numerics; using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Helpers; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -38,7 +40,7 @@ namespace SixLabors.ImageSharp.Processing.Processors public bool Expand { get; set; } = true; /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { int height = this.CanvasRectangle.Height; int width = this.CanvasRectangle.Width; @@ -50,7 +52,7 @@ namespace SixLabors.ImageSharp.Processing.Processors Parallel.For( 0, height, - source.Configuration.ParallelOptions, + configuration.ParallelOptions, y => { Span targetRow = targetPixels.GetRowSpan(y); @@ -71,7 +73,7 @@ namespace SixLabors.ImageSharp.Processing.Processors } /// - protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) + protected override void BeforeApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { this.processMatrix = Matrix3x2Extensions.CreateSkewDegrees(-this.AngleX, -this.AngleY, new Point(0, 0)); if (this.Expand) diff --git a/src/ImageSharp/Processing/Transforms/Options/ResizeHelper.cs b/src/ImageSharp/Processing/Transforms/Options/ResizeHelper.cs index 90f50ec3dc..152b451e29 100644 --- a/src/ImageSharp/Processing/Transforms/Options/ResizeHelper.cs +++ b/src/ImageSharp/Processing/Transforms/Options/ResizeHelper.cs @@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The . /// - public static Rectangle CalculateTargetLocationAndBounds(ImageBase source, ResizeOptions options) + public static Rectangle CalculateTargetLocationAndBounds(ImageFrame source, ResizeOptions options) where TPixel : struct, IPixel { switch (options.Mode) @@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The . /// - private static Rectangle CalculateCropRectangle(ImageBase source, ResizeOptions options) + private static Rectangle CalculateCropRectangle(ImageFrame source, ResizeOptions options) where TPixel : struct, IPixel { int width = options.Size.Width; @@ -172,7 +172,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The . /// - private static Rectangle CalculatePadRectangle(ImageBase source, ResizeOptions options) + private static Rectangle CalculatePadRectangle(ImageFrame source, ResizeOptions options) where TPixel : struct, IPixel { int width = options.Size.Width; @@ -253,7 +253,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The . /// - private static Rectangle CalculateBoxPadRectangle(ImageBase source, ResizeOptions options) + private static Rectangle CalculateBoxPadRectangle(ImageFrame source, ResizeOptions options) where TPixel : struct, IPixel { int width = options.Size.Width; @@ -340,7 +340,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The . /// - private static Rectangle CalculateMaxRectangle(ImageBase source, ResizeOptions options) + private static Rectangle CalculateMaxRectangle(ImageFrame source, ResizeOptions options) where TPixel : struct, IPixel { int width = options.Size.Width; @@ -381,7 +381,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The . /// - private static Rectangle CalculateMinRectangle(ImageBase source, ResizeOptions options) + private static Rectangle CalculateMinRectangle(ImageFrame source, ResizeOptions options) where TPixel : struct, IPixel { int width = options.Size.Width; diff --git a/src/ImageSharp/Processing/Transforms/Resize.cs b/src/ImageSharp/Processing/Transforms/Resize.cs index dc23eb4fba..826741d94e 100644 --- a/src/ImageSharp/Processing/Transforms/Resize.cs +++ b/src/ImageSharp/Processing/Transforms/Resize.cs @@ -38,14 +38,14 @@ namespace SixLabors.ImageSharp options.Size = new Size(options.Size.Width, (int)MathF.Round(img.Height * options.Size.Width / (float)img.Width)); } - Rectangle targetRectangle = ResizeHelper.CalculateTargetLocationAndBounds(img, options); + Rectangle targetRectangle = ResizeHelper.CalculateTargetLocationAndBounds(img, options); img.Mutate(x => Resize(x, options.Size.Width, options.Size.Height, options.Sampler, targetRectangle, options.Compand)); }); } /// - /// Resizes an image to the given . + /// Resizes an image to the given . /// /// The pixel format. /// The image to resize. @@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp } /// - /// Resizes an image to the given . + /// Resizes an image to the given . /// /// The pixel format. /// The image to resize. diff --git a/src/ImageSharp/Quantizers/IQuantizer{TPixel}.cs b/src/ImageSharp/Quantizers/IQuantizer{TPixel}.cs index 1e992c894d..82f6146a3e 100644 --- a/src/ImageSharp/Quantizers/IQuantizer{TPixel}.cs +++ b/src/ImageSharp/Quantizers/IQuantizer{TPixel}.cs @@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Quantizers /// /// A representing a quantized version of the image pixels. /// - QuantizedImage Quantize(ImageBase image, int maxColors); + QuantizedImage Quantize(ImageFrame image, int maxColors); } /// diff --git a/src/ImageSharp/Quantizers/OctreeQuantizer{TPixel}.cs b/src/ImageSharp/Quantizers/OctreeQuantizer{TPixel}.cs index 77771c792c..49adce23b3 100644 --- a/src/ImageSharp/Quantizers/OctreeQuantizer{TPixel}.cs +++ b/src/ImageSharp/Quantizers/OctreeQuantizer{TPixel}.cs @@ -56,7 +56,7 @@ namespace SixLabors.ImageSharp.Quantizers } /// - public override QuantizedImage Quantize(ImageBase image, int maxColors) + public override QuantizedImage Quantize(ImageFrame image, int maxColors) { this.colors = (byte)maxColors.Clamp(1, 255); this.octree = new Octree(this.GetBitsNeededForColorDepth(this.colors)); @@ -66,7 +66,7 @@ namespace SixLabors.ImageSharp.Quantizers } /// - protected override void SecondPass(ImageBase source, byte[] output, int width, int height) + protected override void SecondPass(ImageFrame source, byte[] output, int width, int height) { // Load up the values for the first pixel. We can use these to speed up the second // pass of the algorithm by avoiding transforming rows of identical color. diff --git a/src/ImageSharp/Quantizers/PaletteQuantizer{TPixel}.cs b/src/ImageSharp/Quantizers/PaletteQuantizer{TPixel}.cs index 545004bc2f..ca11a042fb 100644 --- a/src/ImageSharp/Quantizers/PaletteQuantizer{TPixel}.cs +++ b/src/ImageSharp/Quantizers/PaletteQuantizer{TPixel}.cs @@ -55,14 +55,14 @@ namespace SixLabors.ImageSharp.Quantizers } /// - public override QuantizedImage Quantize(ImageBase image, int maxColors) + public override QuantizedImage Quantize(ImageFrame image, int maxColors) { Array.Resize(ref this.colors, maxColors.Clamp(1, 255)); return base.Quantize(image, maxColors); } /// - protected override void SecondPass(ImageBase source, byte[] output, int width, int height) + protected override void SecondPass(ImageFrame source, byte[] output, int width, int height) { // Load up the values for the first pixel. We can use these to speed up the second // pass of the algorithm by avoiding transforming rows of identical color. diff --git a/src/ImageSharp/Quantizers/Quantize.cs b/src/ImageSharp/Quantizers/Quantize.cs index ad166f6065..049090f432 100644 --- a/src/ImageSharp/Quantizers/Quantize.cs +++ b/src/ImageSharp/Quantizers/Quantize.cs @@ -3,6 +3,7 @@ using System; using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Quantizers; @@ -65,7 +66,7 @@ namespace SixLabors.ImageSharp Parallel.For( 0, pixels.Height, - img.Configuration.ParallelOptions, + img.GetConfiguration().ParallelOptions, y => { for (int x = 0; x < pixels.Width; x++) @@ -76,7 +77,7 @@ namespace SixLabors.ImageSharp } }); - img.SwapPixelsBuffers(pixels); + img.Frames[0].SwapPixelsBuffers(pixels); } }); } diff --git a/src/ImageSharp/Quantizers/QuantizerBase{TPixel}.cs b/src/ImageSharp/Quantizers/QuantizerBase{TPixel}.cs index 48c4df5e42..7f58ff1bf5 100644 --- a/src/ImageSharp/Quantizers/QuantizerBase{TPixel}.cs +++ b/src/ImageSharp/Quantizers/QuantizerBase{TPixel}.cs @@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.Quantizers.Base public IErrorDiffuser DitherType { get; set; } = new SierraLiteDiffuser(); /// - public virtual QuantizedImage Quantize(ImageBase image, int maxColors) + public virtual QuantizedImage Quantize(ImageFrame image, int maxColors) { Guard.NotNull(image, nameof(image)); @@ -69,7 +69,7 @@ namespace SixLabors.ImageSharp.Quantizers.Base if (this.Dither) { // We clone the image as we don't want to alter the original. - using (ImageBase clone = image.Clone()) + using (ImageFrame clone = image.Clone()) { this.SecondPass(clone, quantizedPixels, width, height); } @@ -88,7 +88,7 @@ namespace SixLabors.ImageSharp.Quantizers.Base /// The source data /// The width in pixels of the image. /// The height in pixels of the image. - protected virtual void FirstPass(ImageBase source, int width, int height) + protected virtual void FirstPass(ImageFrame source, int width, int height) { // Loop through each row for (int y = 0; y < height; y++) @@ -111,7 +111,7 @@ namespace SixLabors.ImageSharp.Quantizers.Base /// The output pixel array /// The width in pixels of the image /// The height in pixels of the image - protected abstract void SecondPass(ImageBase source, byte[] output, int width, int height); + protected abstract void SecondPass(ImageFrame source, byte[] output, int width, int height); /// /// Override this to process the pixel in the first pass of the algorithm diff --git a/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs b/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs index 5955ec9894..77c421468d 100644 --- a/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs +++ b/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs @@ -133,7 +133,7 @@ namespace SixLabors.ImageSharp.Quantizers } /// - public override QuantizedImage Quantize(ImageBase image, int maxColors) + public override QuantizedImage Quantize(ImageFrame image, int maxColors) { Guard.NotNull(image, nameof(image)); @@ -221,7 +221,7 @@ namespace SixLabors.ImageSharp.Quantizers } /// - protected override void FirstPass(ImageBase source, int width, int height) + protected override void FirstPass(ImageFrame source, int width, int height) { // Build up the 3-D color histogram // Loop through each row @@ -240,7 +240,7 @@ namespace SixLabors.ImageSharp.Quantizers } /// - protected override void SecondPass(ImageBase source, byte[] output, int width, int height) + protected override void SecondPass(ImageFrame source, byte[] output, int width, int height) { // Load up the values for the first pixel. We can use these to speed up the second // pass of the algorithm by avoiding transforming rows of identical color. diff --git a/tests/ImageSharp.Benchmarks/Image/CopyPixels.cs b/tests/ImageSharp.Benchmarks/Image/CopyPixels.cs index b3f53949d1..674aef84c1 100644 --- a/tests/ImageSharp.Benchmarks/Image/CopyPixels.cs +++ b/tests/ImageSharp.Benchmarks/Image/CopyPixels.cs @@ -103,8 +103,8 @@ namespace SixLabors.ImageSharp.Benchmarks.Image Configuration.Default.ParallelOptions, y => { - Span sourceRow = source.GetPixelRowSpan(y); - Span targetRow = target.GetPixelRowSpan(y); + Span sourceRow = source.Frames.RootFrame.GetPixelRowSpan(y); + Span targetRow = target.Frames.RootFrame.GetPixelRowSpan(y); for (int x = 0; x < source.Width; x++) { diff --git a/tests/ImageSharp.Benchmarks/Samplers/Glow.cs b/tests/ImageSharp.Benchmarks/Samplers/Glow.cs index 1530905c7e..884ae35017 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/Glow.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/Glow.cs @@ -17,6 +17,8 @@ namespace SixLabors.ImageSharp.Benchmarks using SixLabors.ImageSharp.Memory; using SixLabors.Primitives; + using SixLabors.ImageSharp.Helpers; + using SixLabors.ImageSharp.Advanced; public class Glow : BenchmarkBase { @@ -35,7 +37,7 @@ namespace SixLabors.ImageSharp.Benchmarks { using (Image image = new Image(800, 800)) { - bulk.Apply(image, image.Bounds()); + this.bulk.Apply(image, image.Bounds()); return new CoreSize(image.Width, image.Height); } } @@ -45,7 +47,7 @@ namespace SixLabors.ImageSharp.Benchmarks { using (Image image = new Image(800, 800)) { - parallel.Apply(image, image.Bounds()); + this.parallel.Apply(image, image.Bounds()); return new CoreSize(image.Width, image.Height); } } @@ -73,7 +75,7 @@ namespace SixLabors.ImageSharp.Benchmarks public float Radius { get; set; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; @@ -112,7 +114,7 @@ namespace SixLabors.ImageSharp.Benchmarks Parallel.For( minY, maxY, - source.Configuration.ParallelOptions, + configuration.ParallelOptions, y => { int offsetY = y - startY; diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegImagePostProcessorTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegImagePostProcessorTests.cs index 871321df9d..df6d1ef1bb 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegImagePostProcessorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegImagePostProcessorTests.cs @@ -56,9 +56,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg string imageFile = provider.SourceFileOrDescription; using (OrigJpegDecoderCore decoder = JpegFixture.ParseStream(imageFile)) using (var pp = new JpegImagePostProcessor(decoder)) - using (var image = new Image(decoder.ImageWidth, decoder.ImageHeight)) + using (var imageFrame = new ImageFrame(decoder.ImageWidth, decoder.ImageHeight)) { - pp.DoPostProcessorStep(image); + pp.DoPostProcessorStep(imageFrame); JpegComponentPostProcessor[] cp = pp.ComponentProcessors; @@ -80,7 +80,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg using (var pp = new JpegImagePostProcessor(decoder)) using (var image = new Image(decoder.ImageWidth, decoder.ImageHeight)) { - pp.PostProcess(image); + pp.PostProcess(image.Frames.RootFrame); image.DebugSave(provider); diff --git a/tests/ImageSharp.Tests/Image/ImageFramesCollectionTests.cs b/tests/ImageSharp.Tests/Image/ImageFramesCollectionTests.cs new file mode 100644 index 0000000000..acd1eec202 --- /dev/null +++ b/tests/ImageSharp.Tests/Image/ImageFramesCollectionTests.cs @@ -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(10, 10); + Assert.Equal(1, collection.Count); + } + + [Fact] + public void AddNewFrame_FramesMustHaveSameSize() + { + var collection = new ImageFrameCollection(10, 10); + + ArgumentException ex = Assert.Throws(() => + { + collection.Add(new ImageFrame(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(10, 10); + + ArgumentNullException ex = Assert.Throws(() => + { + collection.Add(null); + }); + + Assert.Equal("Value cannot be null.\r\nParameter name: frame", ex.Message); + } + + [Fact] + public void InsertNewFrame_FramesMustHaveSameSize() + { + var collection = new ImageFrameCollection(10, 10); + + ArgumentException ex = Assert.Throws(() => + { + collection.Insert(1, new ImageFrame(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(10, 10); + + ArgumentNullException ex = Assert.Throws(() => + { + 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(10, 10); + + ArgumentException ex = Assert.Throws(() => + { + collection[0] = new ImageFrame(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(10, 10); + + ArgumentNullException ex = Assert.Throws(() => + { + collection[0] = null; + }); + + Assert.Equal("Value cannot be null.\r\nParameter name: frame", ex.Message); + } + + [Fact] + public void Constructor_FramesMustHaveSameSize() + { + + ArgumentException ex = Assert.Throws(() => + { + var collection = new ImageFrameCollection(new[] { + new ImageFrame(10,10), + new ImageFrame(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(new[] { + new ImageFrame(10,10) + }); + + InvalidOperationException ex = Assert.Throws(() => + { + collection.RemoveAt(0); + }); + Assert.Equal("Cannot remove last frame.", ex.Message); + } + + [Fact] + public void RemoveAtFrame_CanRemoveFrameZeroIfMultipleFramesExist() + { + + var collection = new ImageFrameCollection(new[] { + new ImageFrame(10,10), + new ImageFrame(10,10), + }); + + collection.RemoveAt(0); + Assert.Equal(1, collection.Count); + } + + [Fact] + public void RemoveFrame_ThrowIfRemovingLastFrame() + { + var collection = new ImageFrameCollection(new[] { + new ImageFrame(10,10) + }); + + InvalidOperationException ex = Assert.Throws(() => + { + collection.Remove(collection[0]); + }); + Assert.Equal("Cannot remove last frame.", ex.Message); + } + + [Fact] + public void RemoveFrame_CanRemoveFrameZeroIfMultipleFramesExist() + { + + var collection = new ImageFrameCollection(new[] { + new ImageFrame(10,10), + new ImageFrame(10,10), + }); + + collection.Remove(collection[0]); + Assert.Equal(1, collection.Count); + } + + [Fact] + public void RootFrameIsFrameAtIndexZero() + { + var collection = new ImageFrameCollection(new[] { + new ImageFrame(10,10), + new ImageFrame(10,10), + }); + + Assert.Equal(collection.RootFrame, collection[0]); + } + + [Fact] + public void ConstructorPopulatesFrames() + { + var collection = new ImageFrameCollection(new[] { + new ImageFrame(10,10), + new ImageFrame(10,10), + }); + + Assert.Equal(2, collection.Count); + } + + [Fact] + public void DisposeClearsCollection() + { + var collection = new ImageFrameCollection(new[] { + new ImageFrame(10,10), + new ImageFrame(10,10), + }); + + collection.Dispose(); + + Assert.Equal(0, collection.Count); + } + + [Fact] + public void Dispose_DisposesAllInnerFrames() + { + var collection = new ImageFrameCollection(new[] { + new ImageFrame(10,10), + new ImageFrame(10,10), + }); + + IPixelSource[] framesSnapShot = collection.OfType>().ToArray(); + collection.Dispose(); + + Assert.All(framesSnapShot, f => + { + // the pixel source of the frame is null after its been disposed. + Assert.Null(f.PixelBuffer); + }); + } + } +} diff --git a/tests/ImageSharp.Tests/Image/ImageLoadTests.cs b/tests/ImageSharp.Tests/Image/ImageLoadTests.cs index cf5e484cfe..f61c73636e 100644 --- a/tests/ImageSharp.Tests/Image/ImageLoadTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageLoadTests.cs @@ -326,7 +326,7 @@ namespace SixLabors.ImageSharp.Tests using (Image img = image1Provider.GetImage()) { - Assert.Equal(166036, img.GetPixelSpan().Length); + Assert.Equal(166036, img.Frames.RootFrame.GetPixelSpan().Length); } } diff --git a/tests/ImageSharp.Tests/Image/ImageRotationTests.cs b/tests/ImageSharp.Tests/Image/ImageRotationTests.cs index 0e7dc5917e..73c2f78f40 100644 --- a/tests/ImageSharp.Tests/Image/ImageRotationTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageRotationTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.Helpers; using SixLabors.Primitives; using Xunit; @@ -48,9 +49,9 @@ namespace SixLabors.ImageSharp.Tests var file = TestFile.Create(TestImages.Bmp.Car); using (var image = Image.Load(file.FullPath)) { - Size original = image.Bounds().Size; + Size original = image.Size(); image.Mutate(x => x.Rotate(angle)); - return (original, image.Bounds().Size); + return (original, image.Size()); } } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/GrayscaleTest.cs b/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/GrayscaleTest.cs index 31ddd14f8a..fe003da32b 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/GrayscaleTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/GrayscaleTest.cs @@ -32,9 +32,10 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.ColorMatrix { image.Mutate(x => x.Grayscale(value)); byte[] data = new byte[3]; - for (int i = 0; i < image.GetPixelSpan().Length; i++) + System.Span span = image.Frames.RootFrame.GetPixelSpan(); + for (int i = 0; i < span.Length; i++) { - image.GetPixelSpan()[i].ToXyzBytes(data, 0); + span[i].ToXyzBytes(data, 0); Assert.Equal(data[0], data[1]); Assert.Equal(data[1], data[2]); } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs index 99520d887e..820ca8356e 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.Helpers; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; diff --git a/tests/ImageSharp.Tests/TestFile.cs b/tests/ImageSharp.Tests/TestFile.cs index f56802e548..b736dce207 100644 --- a/tests/ImageSharp.Tests/TestFile.cs +++ b/tests/ImageSharp.Tests/TestFile.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; +using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; @@ -145,7 +146,7 @@ namespace SixLabors.ImageSharp.Tests /// public Image CreateImage(IImageDecoder decoder) { - return ImageSharp.Image.Load(this.Image.Configuration, this.Bytes, decoder); + return ImageSharp.Image.Load(this.Image.GetConfiguration(), this.Bytes, decoder); } } } diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ExactImageComparer.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ExactImageComparer.cs index 3fe7fe007e..dbe2523661 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ExactImageComparer.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ExactImageComparer.cs @@ -3,6 +3,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison using System; using System.Collections.Generic; using SixLabors.ImageSharp.Advanced; + using SixLabors.ImageSharp.Helpers; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -11,9 +12,9 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison { public static ExactImageComparer Instance { get; } = new ExactImageComparer(); - public override ImageSimilarityReport CompareImagesOrFrames( - ImageBase expected, - ImageBase actual) + public override ImageSimilarityReport CompareImagesOrFrames( + ImageFrame expected, + ImageFrame actual) { if (expected.Size() != actual.Size()) { @@ -50,7 +51,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison } } - return new ImageSimilarityReport(expected, actual, differences); + return new ImageSimilarityReport(expected, actual, differences); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparer.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparer.cs index 4fabd60761..829bf3d10a 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparer.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparer.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; - +using SixLabors.ImageSharp.Helpers; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -22,27 +22,30 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison return new TolerantImageComparer(imageThreshold, perPixelManhattanThreshold); } - public abstract ImageSimilarityReport CompareImagesOrFrames( - ImageBase expected, - ImageBase actual) + public abstract ImageSimilarityReport CompareImagesOrFrames( + ImageFrame expected, + ImageFrame actual) where TPixelA : struct, IPixel where TPixelB : struct, IPixel; } public static class ImageComparerExtensions { - public static IEnumerable CompareImages( + public static ImageSimilarityReport CompareImagesOrFrames( this ImageComparer comparer, Image expected, Image actual) where TPixelA : struct, IPixel where TPixelB : struct, IPixel { - var result = new List(); - ImageSimilarityReport report = comparer.CompareImagesOrFrames(expected, actual); + return comparer.CompareImagesOrFrames((ImageFrame)expected, (ImageFrame)actual); + } - if (!report.IsEmpty) - { - result.Add(report); - } + public static IEnumerable> CompareImages( + this ImageComparer comparer, + Image expected, + Image actual) + where TPixelA : struct, IPixel where TPixelB : struct, IPixel + { + var result = new List>(); if (expected.Frames.Count != actual.Frames.Count) { @@ -50,12 +53,13 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison } for (int i = 0; i < expected.Frames.Count; i++) { - report = comparer.CompareImagesOrFrames(expected.Frames[i], actual.Frames[i]); + ImageSimilarityReport report = comparer.CompareImagesOrFrames(expected.Frames[i], actual.Frames[i]); if (!report.IsEmpty) { result.Add(report); } } + return result; } @@ -100,10 +104,10 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison throw new ImagesSimilarityException("Image frame count does not match!"); } - IEnumerable reports = comparer.CompareImages(expected, actual); + IEnumerable> reports = comparer.CompareImages(expected, actual); if (reports.Any()) { - List cleanedReports = new List(reports.Count()); + List> cleanedReports = new List>(reports.Count()); foreach (var r in reports) { var outsideChanges = r.Differences.Where(x => !( @@ -113,7 +117,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison x.Position.Y <= ignoredRegion.Bottom)); if (outsideChanges.Any()) { - cleanedReports.Add(new ImageSimilarityReport(r.ExpectedImage, r.ActualImage, outsideChanges, null)); + cleanedReports.Add(new ImageSimilarityReport(r.ExpectedImage, r.ActualImage, outsideChanges, null)); } } diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageSimilarityReport.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageSimilarityReport.cs index 8a992b17d3..78e390bbd2 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageSimilarityReport.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageSimilarityReport.cs @@ -4,12 +4,13 @@ using System.Collections.Generic; using System.Linq; using System.Text; + using SixLabors.ImageSharp.PixelFormats; public class ImageSimilarityReport { - public ImageSimilarityReport( - IImageBase expectedImage, - IImageBase actualImage, + protected ImageSimilarityReport( + object expectedImage, + object actualImage, IEnumerable differences, float? totalNormalizedDifference = null) { @@ -18,9 +19,9 @@ this.TotalNormalizedDifference = totalNormalizedDifference; this.Differences = differences.ToArray(); } + public object ExpectedImage { get; } - public static ImageSimilarityReport Empty => - new ImageSimilarityReport(null, null, Enumerable.Empty(), 0f); + public object ActualImage { get; } // TODO: This should not be a nullable value! public float? TotalNormalizedDifference { get; } @@ -29,10 +30,6 @@ ? $"{this.TotalNormalizedDifference.Value * 100:0.0000}%" : "?"; - public IImageBase ExpectedImage { get; } - - public IImageBase ActualImage { get; } - public PixelDifference[] Differences { get; } public bool IsEmpty => this.Differences.Length == 0; @@ -41,7 +38,7 @@ { return this.IsEmpty ? "[SimilarImages]" : this.PrintDifference(); } - + private string PrintDifference() { var sb = new StringBuilder(); @@ -66,4 +63,25 @@ return sb.ToString(); } } + + public class ImageSimilarityReport : ImageSimilarityReport + where TPixelA : struct, IPixel + where TPixelB : struct, IPixel + { + public ImageSimilarityReport( + ImageFrame expectedImage, + ImageFrame actualImage, + IEnumerable differences, + float? totalNormalizedDifference = null) + : base(expectedImage, actualImage, differences, totalNormalizedDifference) + { + } + + public static ImageSimilarityReport Empty => + new ImageSimilarityReport(null, null, Enumerable.Empty(), 0f); + + public new ImageFrame ExpectedImage => (ImageFrame)base.ExpectedImage; + + public new ImageFrame ActualImage => (ImageFrame)base.ActualImage; + } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/TolerantImageComparer.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/TolerantImageComparer.cs index ab0ab287de..d20bd72ac1 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/TolerantImageComparer.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/TolerantImageComparer.cs @@ -5,6 +5,7 @@ using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Advanced; + using SixLabors.ImageSharp.Helpers; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -45,7 +46,7 @@ /// public int PerPixelManhattanThreshold { get; } - public override ImageSimilarityReport CompareImagesOrFrames(ImageBase expected, ImageBase actual) + public override ImageSimilarityReport CompareImagesOrFrames(ImageFrame expected, ImageFrame actual) { if (expected.Size() != actual.Size()) { @@ -90,11 +91,11 @@ if (normalizedDifference > this.ImageThreshold) { - return new ImageSimilarityReport(expected, actual, differences, normalizedDifference); + return new ImageSimilarityReport(expected, actual, differences, normalizedDifference); } else { - return ImageSimilarityReport.Empty; + return ImageSimilarityReport.Empty; } } diff --git a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs index 1b3ebf016a..f4faa94565 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs @@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp.Tests /// The name of the test case (by default) /// public string TestName { get; set; } = string.Empty; - + private string GetTestOutputFileNameImpl(string extension, string details, bool appendPixelTypeToFileName) { string fn = string.Empty; @@ -141,7 +141,7 @@ namespace SixLabors.ImageSharp.Tests { string path = this.GetTestOutputFileName(extension, testOutputDetails, appendPixelTypeToFileName); encoder = encoder ?? TestEnvironment.GetReferenceEncoder(path); - + using (FileStream stream = File.OpenWrite(path)) { image.Save(stream, encoder); @@ -187,7 +187,7 @@ namespace SixLabors.ImageSharp.Tests // pngEncoder.PngColorType = PngColorType.Grayscale; // } // } - + // return encoder; //} @@ -197,8 +197,14 @@ namespace SixLabors.ImageSharp.Tests return TestEnvironment.CreateOutputDirectory(testGroupName); } - public static void ModifyPixel(ImageBase img, int x, int y, byte perChannelChange) + public static void ModifyPixel(Image img, int x, int y, byte perChannelChange) where TPixel : struct, IPixel + { + ModifyPixel((ImageFrame)img, x, y, perChannelChange); + } + + public static void ModifyPixel(ImageFrame img, int x, int y, byte perChannelChange) + where TPixel : struct, IPixel { TPixel pixel = img[x, y]; var rgbaPixel = default(Rgba32); diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs index 5fedaa124e..a201b39bd2 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs @@ -101,7 +101,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs var destPtr = (Argb32*)workBuffer.Pin(); for (int y = 0; y < h; y++) { - Span row = image.GetPixelRowSpan(y); + Span row = image.Frames.RootFrame.GetPixelRowSpan(y); byte* sourcePtr = sourcePtrBase + data.Stride * y; @@ -143,7 +143,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs var destPtr = (Rgb24*)workBuffer.Pin(); for (int y = 0; y < h; y++) { - Span row = image.GetPixelRowSpan(y); + Span row = image.Frames.RootFrame.GetPixelRowSpan(y); byte* sourcePtr = sourcePtrBase + data.Stride * y; @@ -176,7 +176,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs for (int y = 0; y < h; y++) { - Span row = image.GetPixelRowSpan(y); + Span row = image.Frames.RootFrame.GetPixelRowSpan(y); ToArgb32(row, workBuffer); byte* destPtr = destPtrBase + data.Stride * y; diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index b79165f950..b3dd763c7f 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -196,7 +196,7 @@ namespace SixLabors.ImageSharp.Tests { var image = new Image(buffer.Width, buffer.Height); - Span pixels = image.GetPixelSpan(); + Span pixels = image.Frames.RootFrame.GetPixelSpan(); for (int i = 0; i < buffer.Length; i++) {