From c44c67a18a1340d0a283315ef89d0a856323e632 Mon Sep 17 00:00:00 2001 From: Lauri Kotilainen Date: Thu, 11 Jan 2018 18:58:10 +0200 Subject: [PATCH 001/151] - Make Buffer2D wrap Buffer --- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 6 +-- src/ImageSharp/Image/PixelAccessor{TPixel}.cs | 6 +-- src/ImageSharp/Memory/Buffer2D{T}.cs | 40 ++++++++++++++----- src/ImageSharp/Memory/Buffer{T}.cs | 6 +++ .../ResamplingWeightedProcessor.Weights.cs | 4 +- .../Processors/Transforms/ResizeProcessor.cs | 2 +- .../General/PixelIndexing.cs | 10 ++--- .../Formats/Jpg/SpectralJpegTests.cs | 2 +- .../ImageSharp.Tests/Memory/Buffer2DTests.cs | 16 ++++---- .../TestUtilities/TestImageExtensions.cs | 4 +- 10 files changed, 60 insertions(+), 36 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 04176e0333..4faccc58fd 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -265,7 +265,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp using (var buffer = Buffer2D.CreateClean(width, height)) { - this.UncompressRle8(width, buffer); + this.UncompressRle8(width, buffer.Span); for (int y = 0; y < height; y++) { @@ -385,7 +385,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp padding = 4 - padding; } - using (var row = Buffer.CreateClean(arrayWidth + padding)) + using (var row = MemoryManager.Current.Allocate(arrayWidth + padding, true)) { var color = default(TPixel); var rgba = new Rgba32(0, 0, 0, 255); @@ -435,7 +435,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp var color = default(TPixel); var rgba = new Rgba32(0, 0, 0, 255); - using (var buffer = new Buffer(stride)) + using (var buffer = MemoryManager.Current.Allocate(stride)) { for (int y = 0; y < height; y++) { diff --git a/src/ImageSharp/Image/PixelAccessor{TPixel}.cs b/src/ImageSharp/Image/PixelAccessor{TPixel}.cs index 8dcb1f7602..70d67954cc 100644 --- a/src/ImageSharp/Image/PixelAccessor{TPixel}.cs +++ b/src/ImageSharp/Image/PixelAccessor{TPixel}.cs @@ -86,7 +86,7 @@ namespace SixLabors.ImageSharp /// /// Gets the pixel buffer array. /// - public TPixel[] PixelArray => this.PixelBuffer.Array; + public TPixel[] PixelArray => this.PixelBuffer.Buffer.Array; /// /// Gets the size of a single pixel in the number of bytes. @@ -105,7 +105,7 @@ namespace SixLabors.ImageSharp public int Height { get; private set; } /// - Span IBuffer2D.Span => this.PixelBuffer; + Span IBuffer2D.Span => this.PixelBuffer.Span; private static PixelOperations Operations => PixelOperations.Instance; @@ -158,7 +158,7 @@ namespace SixLabors.ImageSharp /// public void Reset() { - this.PixelBuffer.Clear(); + this.PixelBuffer.Buffer.Clear(); } /// diff --git a/src/ImageSharp/Memory/Buffer2D{T}.cs b/src/ImageSharp/Memory/Buffer2D{T}.cs index 99b10cae7e..6dd4c93a5f 100644 --- a/src/ImageSharp/Memory/Buffer2D{T}.cs +++ b/src/ImageSharp/Memory/Buffer2D{T}.cs @@ -6,14 +6,15 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Memory { + using System; + /// /// Represents a buffer of value type objects /// interpreted as a 2D region of x elements. /// /// The value type. - internal class Buffer2D : Buffer, IBuffer2D - where T : struct - { + internal class Buffer2D : IBuffer2D, IDisposable + where T : struct { public Buffer2D(Size size) : this(size.Width, size.Height) { @@ -25,7 +26,7 @@ namespace SixLabors.ImageSharp.Memory /// The number of elements in a row /// The number of rows public Buffer2D(int width, int height) - : base(width * height) + : this(MemoryManager.Current.Allocate(width * height), width, height) { this.Width = width; this.Height = height; @@ -37,9 +38,20 @@ namespace SixLabors.ImageSharp.Memory /// The array to pin /// The number of elements in a row /// The number of rows - public Buffer2D(T[] array, int width, int height) - : base(array, width * height) - { + public Buffer2D(T[] array, int width, int height) { + this.Buffer = new Buffer(array, width * height); + this.Width = width; + this.Height = height; + } + + /// + /// Initializes a new instance of the class. + /// + /// The buffer to wrap + /// The number of elements in a row + /// The number of rows + public Buffer2D(Buffer wrappedBuffer, int width, int height) { + this.Buffer = wrappedBuffer; this.Width = width; this.Height = height; } @@ -50,6 +62,10 @@ namespace SixLabors.ImageSharp.Memory /// public int Height { get; } + public Span Span => this.Buffer.Span; + + public Buffer Buffer { get; } + /// /// Gets a reference to the element at the specified position. /// @@ -64,7 +80,7 @@ namespace SixLabors.ImageSharp.Memory DebugGuard.MustBeLessThan(x, this.Width, nameof(x)); DebugGuard.MustBeLessThan(y, this.Height, nameof(y)); - return ref this.Array[(this.Width * y) + x]; + return ref this.Buffer.Array[(this.Width * y) + x]; } } @@ -76,9 +92,7 @@ namespace SixLabors.ImageSharp.Memory /// The instance public static Buffer2D CreateClean(int width, int height) { - var buffer = new Buffer2D(width, height); - buffer.Clear(); - return buffer; + return new Buffer2D(MemoryManager.Current.Allocate(width*height, true), width, height); } /// @@ -87,5 +101,9 @@ namespace SixLabors.ImageSharp.Memory /// The size of the buffer /// The instance public static Buffer2D CreateClean(Size size) => CreateClean(size.Width, size.Height); + + public void Dispose() { + this.Buffer?.Dispose(); + } } } \ No newline at end of file diff --git a/src/ImageSharp/Memory/Buffer{T}.cs b/src/ImageSharp/Memory/Buffer{T}.cs index f5c9ed00a1..186a2212c0 100644 --- a/src/ImageSharp/Memory/Buffer{T}.cs +++ b/src/ImageSharp/Memory/Buffer{T}.cs @@ -71,6 +71,12 @@ namespace SixLabors.ImageSharp.Memory this.isPoolingOwner = false; } + internal Buffer(T[] array, int length, MemoryManager memoryManager) + : this(array, length) + { + this.memoryManager = memoryManager; + } + /// /// Finalizes an instance of the class. /// diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs index 22a7c90b75..9d76bf60f1 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs @@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.Processing.Processors { this.flatStartIndex = (index * buffer.Width) + left; this.Left = left; - this.buffer = buffer; + this.buffer = buffer.Buffer; this.Length = length; } @@ -69,7 +69,7 @@ namespace SixLabors.ImageSharp.Processing.Processors /// /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Span GetWindowSpan() => this.buffer.Slice(this.flatStartIndex, this.Length); + public Span GetWindowSpan() => this.buffer.Span.Slice(this.flatStartIndex, this.Length); /// /// Computes the sum of vectors in 'rowSpan' weighted by weight values, pointed by this instance. diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs index 17b42c5040..bb0845776c 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs @@ -123,7 +123,7 @@ namespace SixLabors.ImageSharp.Processing.Processors // TODO: Using a transposed variant of 'firstPassPixels' could eliminate the need for the WeightsWindow.ComputeWeightedColumnSum() method, and improve speed! using (var firstPassPixels = new Buffer2D(width, source.Height)) { - firstPassPixels.Clear(); + firstPassPixels.Buffer.Clear(); Parallel.For( 0, diff --git a/tests/ImageSharp.Benchmarks/General/PixelIndexing.cs b/tests/ImageSharp.Benchmarks/General/PixelIndexing.cs index 0e21caffbc..b0560b1631 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelIndexing.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelIndexing.cs @@ -33,9 +33,9 @@ public Data(Buffer2D buffer) { - this.pointer = (Vector4*)buffer.Pin(); - this.pinnable = Unsafe.As>(buffer.Array); - this.array = buffer.Array; + this.pointer = (Vector4*)buffer.Buffer.Pin(); + this.pinnable = Unsafe.As>(buffer.Buffer.Array); + this.array = buffer.Buffer.Array; this.width = buffer.Width; } @@ -150,8 +150,8 @@ { this.width = 2048; this.buffer = new Buffer2D(2048, 2048); - this.pointer = (Vector4*)this.buffer.Pin(); - this.array = this.buffer.Array; + this.pointer = (Vector4*)this.buffer.Buffer.Pin(); + this.array = this.buffer.Buffer.Array; this.pinnable = Unsafe.As>(this.array); this.startIndex = 2048 / 2 - (this.Count / 2); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs index 6e68c43f21..46d8328833 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs @@ -106,7 +106,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg this.Output.WriteLine($"Component{i}: {diff}"); averageDifference += diff.average; totalDifference += diff.total; - tolerance += libJpegComponent.SpectralBlocks.Length; + tolerance += libJpegComponent.SpectralBlocks.Buffer.Length; } averageDifference /= componentCount; diff --git a/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs index d662a1b3ef..7ab0ed9491 100644 --- a/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs +++ b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs @@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Tests.Memory { Assert.Equal(width, buffer.Width); Assert.Equal(height, buffer.Height); - Assert.Equal(width * height, buffer.Length); + Assert.Equal(width * height, buffer.Buffer.Length); } } @@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Tests.Memory { Assert.Equal(width, buffer.Width); Assert.Equal(height, buffer.Height); - Assert.Equal(width * height, buffer.Length); + Assert.Equal(width * height, buffer.Buffer.Length); } } @@ -61,10 +61,10 @@ namespace SixLabors.ImageSharp.Tests.Memory { using (Buffer2D buffer = Buffer2D.CreateClean(42, 42)) { - for (int j = 0; j < buffer.Length; j++) + for (int j = 0; j < buffer.Buffer.Length; j++) { - Assert.Equal(0, buffer.Array[j]); - buffer.Array[j] = 666; + Assert.Equal(0, buffer.Buffer.Array[j]); + buffer.Buffer.Array[j] = 666; } } } @@ -82,7 +82,7 @@ namespace SixLabors.ImageSharp.Tests.Memory // Assert.Equal(width * y, span.Start); Assert.Equal(width, span.Length); - Assert.SpanPointsTo(span, buffer, width * y); + Assert.SpanPointsTo(span, buffer.Buffer, width * y); } } @@ -98,7 +98,7 @@ namespace SixLabors.ImageSharp.Tests.Memory // Assert.Equal(width * y + x, span.Start); Assert.Equal(width - x, span.Length); - Assert.SpanPointsTo(span, buffer, width * y + x); + Assert.SpanPointsTo(span, buffer.Buffer, width * y + x); } } @@ -110,7 +110,7 @@ namespace SixLabors.ImageSharp.Tests.Memory { using (Buffer2D buffer = new Buffer2D(width, height)) { - TestStructs.Foo[] array = buffer.Array; + TestStructs.Foo[] array = buffer.Buffer.Array; ref TestStructs.Foo actual = ref buffer[x, y]; diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index 2b3cb1bcc3..fbada505a2 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -233,9 +233,9 @@ namespace SixLabors.ImageSharp.Tests Span pixels = image.Frames.RootFrame.GetPixelSpan(); - for (int i = 0; i < buffer.Length; i++) + for (int i = 0; i < buffer.Buffer.Length; i++) { - float value = buffer[i] * scale; + float value = buffer.Buffer[i] * scale; var v = new Vector4(value, value, value, 1f); pixels[i].PackFromVector4(v); } From 61b61709a908cb56f1d80c34415b555ce1edd0b2 Mon Sep 17 00:00:00 2001 From: Lauri Kotilainen Date: Thu, 11 Jan 2018 18:44:01 +0200 Subject: [PATCH 002/151] - Allocate Buffers from memory manager --- .../Brushes/ImageBrush{TPixel}.cs | 4 +- .../Brushes/PatternBrush{TPixel}.cs | 4 +- .../Brushes/Processors/BrushApplicator.cs | 4 +- .../Brushes/RecolorBrush{TPixel}.cs | 4 +- .../Brushes/SolidBrush{TPixel}.cs | 4 +- src/ImageSharp.Drawing/Paths/ShapeRegion.cs | 2 +- .../Processors/DrawImageProcessor.cs | 2 +- .../Processors/FillProcessor.cs | 2 +- .../Processors/FillRegionProcessor.cs | 2 +- src/ImageSharp/Configuration.cs | 1 + src/ImageSharp/Formats/Gif/GifDecoderCore.cs | 6 +-- .../Common/Decoder/JpegImagePostProcessor.cs | 2 +- .../Components/PdfJsFrameComponent.cs | 2 +- .../PdfJsPort/Components/PdfJsHuffmanTable.cs | 12 +++--- .../Components/PdfJsJpegPixelArea.cs | 4 +- .../Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs | 10 ++--- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 14 +++--- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 14 +++--- src/ImageSharp/Image/Image.Decode.cs | 2 +- src/ImageSharp/Image/PixelArea{TPixel}.cs | 2 +- .../Memory/ArrayPoolMemoryManager.cs | 28 ++++++++++++ src/ImageSharp/Memory/Buffer2D{T}.cs | 19 ++++---- src/ImageSharp/Memory/Buffer{T}.cs | 43 +++---------------- src/ImageSharp/Memory/MemoryManager.cs | 39 +++++++++++++++++ .../DefaultPixelBlenders.Generated.cs | 42 +++++++++--------- .../DefaultPixelBlenders.Generated.tt | 2 +- .../Effects/BackgroundColorProcessor.cs | 4 +- .../Processors/Overlays/GlowProcessor.cs | 4 +- .../Processors/Overlays/VignetteProcessor.cs | 4 +- .../Processors/Transforms/ResizeProcessor.cs | 2 +- .../Color/Bulk/PackFromVector4.cs | 4 +- .../Bulk/PackFromVector4ReferenceVsPointer.cs | 4 +- .../Color/Bulk/PackFromXyzw.cs | 4 +- .../Color/Bulk/ToVector4.cs | 4 +- .../ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs | 4 +- .../Color/Bulk/ToXyzw.cs | 4 +- .../General/ClearBuffer.cs | 2 +- .../General/IterateArray.cs | 2 +- .../PixelBlenders/PorterDuffBulkVsPixel.cs | 6 +-- tests/ImageSharp.Benchmarks/Samplers/Glow.cs | 2 +- tests/ImageSharp.Tests/Memory/BufferTests.cs | 22 +++++----- .../Memory/SpanUtilityTests.cs | 2 +- .../PixelFormats/PixelOperationsTests.cs | 6 +-- .../ReferenceCodecs/SystemDrawingBridge.cs | 12 +++--- 44 files changed, 200 insertions(+), 162 deletions(-) create mode 100644 src/ImageSharp/Memory/ArrayPoolMemoryManager.cs create mode 100644 src/ImageSharp/Memory/MemoryManager.cs diff --git a/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs index 2d29e23fe5..d642253851 100644 --- a/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs @@ -122,8 +122,8 @@ namespace SixLabors.ImageSharp.Drawing.Brushes internal override void Apply(Span scanline, int x, int y) { // Create a span for colors - using (var amountBuffer = new Buffer(scanline.Length)) - using (var overlay = new Buffer(scanline.Length)) + using (var amountBuffer = MemoryManager.Current.Allocate(scanline.Length)) + using (var overlay = MemoryManager.Current.Allocate(scanline.Length)) { int sourceY = (y - this.offsetY) % this.yLength; int offsetX = x - this.offsetX; diff --git a/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs index 844df0e0e9..4b26c4edce 100644 --- a/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs @@ -152,8 +152,8 @@ namespace SixLabors.ImageSharp.Drawing.Brushes internal override void Apply(Span scanline, int x, int y) { int patternY = y % this.pattern.Height; - using (var amountBuffer = new Buffer(scanline.Length)) - using (var overlay = new Buffer(scanline.Length)) + using (var amountBuffer = MemoryManager.Current.Allocate(scanline.Length)) + using (var overlay = MemoryManager.Current.Allocate(scanline.Length)) { for (int i = 0; i < scanline.Length; i++) { diff --git a/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs b/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs index ca6f7630d9..5a50e12fb8 100644 --- a/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs +++ b/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs @@ -65,8 +65,8 @@ namespace SixLabors.ImageSharp.Drawing.Brushes.Processors /// scanlineBuffer will be > scanlineWidth but provide and offset in case we want to share a larger buffer across runs. internal virtual void Apply(Span scanline, int x, int y) { - using (var amountBuffer = new Buffer(scanline.Length)) - using (var overlay = new Buffer(scanline.Length)) + using (var amountBuffer = MemoryManager.Current.Allocate(scanline.Length)) + using (var overlay = MemoryManager.Current.Allocate(scanline.Length)) { for (int i = 0; i < scanline.Length; i++) { diff --git a/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs index ba2fca4e4b..1c02884f2a 100644 --- a/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs @@ -144,8 +144,8 @@ namespace SixLabors.ImageSharp.Drawing.Brushes /// internal override void Apply(Span scanline, int x, int y) { - using (var amountBuffer = new Buffer(scanline.Length)) - using (var overlay = new Buffer(scanline.Length)) + using (var amountBuffer = MemoryManager.Current.Allocate(scanline.Length)) + using (var overlay = MemoryManager.Current.Allocate(scanline.Length)) { for (int i = 0; i < scanline.Length; i++) { diff --git a/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs index 658164339d..b935bbe363 100644 --- a/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs @@ -61,7 +61,7 @@ namespace SixLabors.ImageSharp.Drawing.Brushes public SolidBrushApplicator(ImageFrame source, TPixel color, GraphicsOptions options) : base(source, options) { - this.Colors = new Buffer(source.Width); + this.Colors = MemoryManager.Current.Allocate(source.Width); for (int i = 0; i < this.Colors.Length; i++) { this.Colors[i] = color; @@ -96,7 +96,7 @@ namespace SixLabors.ImageSharp.Drawing.Brushes { Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); - using (var amountBuffer = new Buffer(scanline.Length)) + using (var amountBuffer = MemoryManager.Current.Allocate(scanline.Length)) { for (int i = 0; i < scanline.Length; i++) { diff --git a/src/ImageSharp.Drawing/Paths/ShapeRegion.cs b/src/ImageSharp.Drawing/Paths/ShapeRegion.cs index a96b03dd04..7851bac50b 100644 --- a/src/ImageSharp.Drawing/Paths/ShapeRegion.cs +++ b/src/ImageSharp.Drawing/Paths/ShapeRegion.cs @@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.Drawing { var start = new PointF(this.Bounds.Left - 1, y); var end = new PointF(this.Bounds.Right + 1, y); - using (var innerBuffer = new Buffer(buffer.Length)) + using (var innerBuffer = MemoryManager.Current.Allocate(buffer.Length)) { PointF[] array = innerBuffer.Array; int count = this.Shape.FindIntersections(start, end, array, 0); diff --git a/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs b/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs index 47763c0aaf..2d411e0b65 100644 --- a/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs @@ -84,7 +84,7 @@ namespace SixLabors.ImageSharp.Drawing.Processors maxY = Math.Min(this.Location.Y + this.Size.Height, maxY); int width = maxX - minX; - using (var amount = new Buffer(width)) + using (var amount = MemoryManager.Current.Allocate(width)) { for (int i = 0; i < width; i++) { diff --git a/src/ImageSharp.Drawing/Processors/FillProcessor.cs b/src/ImageSharp.Drawing/Processors/FillProcessor.cs index 679ca6a228..f4763af1e3 100644 --- a/src/ImageSharp.Drawing/Processors/FillProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/FillProcessor.cs @@ -66,7 +66,7 @@ namespace SixLabors.ImageSharp.Drawing.Processors int width = maxX - minX; - using (var amount = new Buffer(width)) + using (var amount = MemoryManager.Current.Allocate(width)) using (BrushApplicator applicator = this.brush.CreateApplicator(source, sourceRectangle, this.options)) { for (int i = 0; i < width; i++) diff --git a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs index b6ef4be218..82ffea08ea 100644 --- a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs @@ -102,7 +102,7 @@ namespace SixLabors.ImageSharp.Drawing.Processors { float[] buffer = arrayPool.Rent(maxIntersections); int scanlineWidth = maxX - minX; - using (var scanline = new Buffer(scanlineWidth)) + using (var scanline = MemoryManager.Current.Allocate(scanlineWidth)) { try { diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index 7401035331..8ea676d322 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -12,6 +12,7 @@ using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.IO; +using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp { diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index ae20be7d5d..0003dbc823 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -124,7 +124,7 @@ namespace SixLabors.ImageSharp.Formats.Gif if (this.logicalScreenDescriptor.GlobalColorTableFlag) { this.globalColorTableLength = this.logicalScreenDescriptor.GlobalColorTableSize * 3; - this.globalColorTable = Buffer.CreateClean(this.globalColorTableLength); + this.globalColorTable = MemoryManager.Current.Allocate(this.globalColorTableLength, true); // Read the global color table from the stream stream.Read(this.globalColorTable.Array, 0, this.globalColorTableLength); @@ -320,11 +320,11 @@ namespace SixLabors.ImageSharp.Formats.Gif if (imageDescriptor.LocalColorTableFlag) { int length = imageDescriptor.LocalColorTableSize * 3; - localColorTable = Buffer.CreateClean(length); + localColorTable = MemoryManager.Current.Allocate(length, true); this.currentStream.Read(localColorTable.Array, 0, length); } - indices = Buffer.CreateClean(imageDescriptor.Width * imageDescriptor.Height); + indices = MemoryManager.Current.Allocate(imageDescriptor.Width * imageDescriptor.Height, true); this.ReadFrameIndices(imageDescriptor, indices); this.ReadFrameColors(indices, localColorTable ?? this.globalColorTable, imageDescriptor); diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs index 125ec52723..44deb6722b 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs @@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder this.PostProcessorBufferSize = new Size(c0.SizeInBlocks.Width * 8, PixelRowsPerStep); this.ComponentProcessors = rawJpeg.Components.Select(c => new JpegComponentPostProcessor(this, c)).ToArray(); - this.rgbaBuffer = new Buffer(rawJpeg.ImageSizeInPixels.Width); + this.rgbaBuffer = MemoryManager.Current.Allocate(rawJpeg.ImageSizeInPixels.Width); this.colorConverter = ColorConverters.JpegColorConverter.GetConverter(rawJpeg.ColorSpace); } diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrameComponent.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrameComponent.cs index f60097dc9c..5d51e2ad58 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrameComponent.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrameComponent.cs @@ -114,7 +114,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components int blocksBufferSize = 64 * this.BlocksPerColumnForMcu * (this.BlocksPerLineForMcu + 1); // Pooled. Disposed via frame disposal - this.BlockData = Buffer.CreateClean(blocksBufferSize); + this.BlockData = MemoryManager.Current.Allocate(blocksBufferSize, true); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs index 9dc8315677..38f223dcac 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs @@ -24,12 +24,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components /// The huffman values public PdfJsHuffmanTable(byte[] lengths, byte[] values) { - this.lookahead = Buffer.CreateClean(256); - this.valOffset = Buffer.CreateClean(18); - this.maxcode = Buffer.CreateClean(18); + this.lookahead = MemoryManager.Current.Allocate(256, true); + this.valOffset = MemoryManager.Current.Allocate(18, true); + this.maxcode = MemoryManager.Current.Allocate(18, true); - using (var huffsize = Buffer.CreateClean(257)) - using (var huffcode = Buffer.CreateClean(257)) + using (var huffsize = MemoryManager.Current.Allocate(257, true)) + using (var huffcode = MemoryManager.Current.Allocate(257, true)) { GenerateSizeTable(lengths, huffsize); GenerateCodeTable(huffsize, huffcode); @@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components GenerateLookaheadTables(lengths, values, this.lookahead); } - this.huffval = Buffer.CreateClean(values.Length); + this.huffval = MemoryManager.Current.Allocate(values.Length, true); Buffer.BlockCopy(values, 0, this.huffval.Array, 0, values.Length); this.MaxCode = this.maxcode.Array; diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsJpegPixelArea.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsJpegPixelArea.cs index 034986c2cb..eb16807969 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsJpegPixelArea.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsJpegPixelArea.cs @@ -69,11 +69,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components this.rowStride = width * numberOfComponents; var scale = new Vector2(this.imageWidth / (float)width, this.imageHeight / (float)height); - this.componentData = new Buffer(width * height * numberOfComponents); + this.componentData = MemoryManager.Current.Allocate(width * height * numberOfComponents); Span componentDataSpan = this.componentData; const uint Mask3Lsb = 0xFFFFFFF8; // Used to clear the 3 LSBs - using (var xScaleBlockOffset = new Buffer(width)) + using (var xScaleBlockOffset = MemoryManager.Current.Allocate(width)) { Span xScaleBlockOffsetSpan = xScaleBlockOffset; for (int i = 0; i < numberOfComponents; i++) diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs index 211c24d208..917e3c7e3f 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs @@ -673,14 +673,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort throw new ImageFormatException($"DHT has wrong length: {remaining}"); } - using (var huffmanData = Buffer.CreateClean(256)) + using (var huffmanData = MemoryManager.Current.Allocate(256, true)) { for (int i = 2; i < remaining;) { byte huffmanTableSpec = (byte)this.InputStream.ReadByte(); this.InputStream.Read(huffmanData.Array, 0, 16); - using (var codeLengths = Buffer.CreateClean(17)) + using (var codeLengths = MemoryManager.Current.Allocate(17, true)) { int codeLengthSum = 0; @@ -689,7 +689,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort codeLengthSum += codeLengths[j] = huffmanData[j - 1]; } - using (var huffmanValues = Buffer.CreateClean(256)) + using (var huffmanValues = MemoryManager.Current.Allocate(256, true)) { this.InputStream.Read(huffmanValues.Array, 0, codeLengthSum); @@ -784,8 +784,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort { int blocksPerLine = component.BlocksPerLine; int blocksPerColumn = component.BlocksPerColumn; - using (var computationBuffer = Buffer.CreateClean(64)) - using (var multiplicationBuffer = Buffer.CreateClean(64)) + using (var computationBuffer = MemoryManager.Current.Allocate(64, true)) + using (var multiplicationBuffer = MemoryManager.Current.Allocate(64, true)) { Span quantizationTable = this.quantizationTables.Tables.GetRowSpan(frameComponent.QuantizationTableIndex); Span computationBufferSpan = computationBuffer; diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 7149b74d89..ebda05a1e9 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -375,8 +375,8 @@ namespace SixLabors.ImageSharp.Formats.Png this.bytesPerSample = this.header.BitDepth / 8; } - this.previousScanline = Buffer.CreateClean(this.bytesPerScanline); - this.scanline = Buffer.CreateClean(this.bytesPerScanline); + this.previousScanline = MemoryManager.Current.Allocate(this.bytesPerScanline, true); + this.scanline = MemoryManager.Current.Allocate(this.bytesPerScanline, true); } /// @@ -669,7 +669,7 @@ namespace SixLabors.ImageSharp.Formats.Png if (this.header.BitDepth == 16) { int length = this.header.Width * 3; - using (var compressed = new Buffer(length)) + using (var compressed = MemoryManager.Current.Allocate(length)) { // TODO: Should we use pack from vector here instead? this.From16BitTo8Bit(scanlineBuffer, compressed, length); @@ -686,7 +686,7 @@ namespace SixLabors.ImageSharp.Formats.Png if (this.header.BitDepth == 16) { int length = this.header.Width * 3; - using (var compressed = new Buffer(length)) + using (var compressed = MemoryManager.Current.Allocate(length)) { // TODO: Should we use pack from vector here instead? this.From16BitTo8Bit(scanlineBuffer, compressed, length); @@ -727,7 +727,7 @@ namespace SixLabors.ImageSharp.Formats.Png if (this.header.BitDepth == 16) { int length = this.header.Width * 4; - using (var compressed = new Buffer(length)) + using (var compressed = MemoryManager.Current.Allocate(length)) { // TODO: Should we use pack from vector here instead? this.From16BitTo8Bit(scanlineBuffer, compressed, length); @@ -930,7 +930,7 @@ namespace SixLabors.ImageSharp.Formats.Png if (this.header.BitDepth == 16) { int length = this.header.Width * 3; - using (var compressed = new Buffer(length)) + using (var compressed = MemoryManager.Current.Allocate(length)) { // TODO: Should we use pack from vector here instead? this.From16BitTo8Bit(scanlineBuffer, compressed, length); @@ -998,7 +998,7 @@ namespace SixLabors.ImageSharp.Formats.Png if (this.header.BitDepth == 16) { int length = this.header.Width * 4; - using (var compressed = new Buffer(length)) + using (var compressed = MemoryManager.Current.Allocate(length)) { // TODO: Should we use pack from vector here instead? this.From16BitTo8Bit(scanlineBuffer, compressed, length); diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 0efd46ec74..9be0f5ee44 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -620,16 +620,16 @@ namespace SixLabors.ImageSharp.Formats.Png this.bytesPerScanline = this.width * this.bytesPerPixel; int resultLength = this.bytesPerScanline + 1; - this.previousScanline = Buffer.CreateClean(this.bytesPerScanline); - this.rawScanline = Buffer.CreateClean(this.bytesPerScanline); - this.result = Buffer.CreateClean(resultLength); + this.previousScanline = MemoryManager.Current.Allocate(this.bytesPerScanline, true); + this.rawScanline = MemoryManager.Current.Allocate(this.bytesPerScanline, true); + this.result = MemoryManager.Current.Allocate(resultLength, true); if (this.pngColorType != PngColorType.Palette) { - this.sub = Buffer.CreateClean(resultLength); - this.up = Buffer.CreateClean(resultLength); - this.average = Buffer.CreateClean(resultLength); - this.paeth = Buffer.CreateClean(resultLength); + this.sub = MemoryManager.Current.Allocate(resultLength, true); + this.up = MemoryManager.Current.Allocate(resultLength, true); + this.average = MemoryManager.Current.Allocate(resultLength, true); + this.paeth = MemoryManager.Current.Allocate(resultLength, true); } byte[] buffer; diff --git a/src/ImageSharp/Image/Image.Decode.cs b/src/ImageSharp/Image/Image.Decode.cs index b4ab712d05..1bce23cfcb 100644 --- a/src/ImageSharp/Image/Image.Decode.cs +++ b/src/ImageSharp/Image/Image.Decode.cs @@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp return null; } - using (var buffer = new Buffer(maxHeaderSize)) + using (var buffer = MemoryManager.Current.Allocate(maxHeaderSize)) { long startPosition = stream.Position; stream.Read(buffer.Array, 0, maxHeaderSize); diff --git a/src/ImageSharp/Image/PixelArea{TPixel}.cs b/src/ImageSharp/Image/PixelArea{TPixel}.cs index 1c7256455e..e6ae556f7d 100644 --- a/src/ImageSharp/Image/PixelArea{TPixel}.cs +++ b/src/ImageSharp/Image/PixelArea{TPixel}.cs @@ -116,7 +116,7 @@ namespace SixLabors.ImageSharp this.RowStride = (width * GetComponentCount(componentOrder)) + padding; this.Length = this.RowStride * height; - this.byteBuffer = Buffer.CreateClean(this.Length); + this.byteBuffer = MemoryManager.Current.Allocate(this.Length, true); } /// diff --git a/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs b/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs new file mode 100644 index 0000000000..c4ce7d2991 --- /dev/null +++ b/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs @@ -0,0 +1,28 @@ +using System.Buffers; + +namespace SixLabors.ImageSharp.Memory +{ + /// + /// Implements by allocating memory from . + /// + public class ArrayPoolMemoryManager : MemoryManager + { + /// + internal override Buffer Allocate(int size, bool clear = false) + { + var buffer = new Buffer(PixelDataPool.Rent(size), size, this); + if (clear) + { + buffer.Clear(); + } + + return buffer; + } + + /// + internal override void Release(Buffer buffer) + { + PixelDataPool.Return(buffer.Array); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Memory/Buffer2D{T}.cs b/src/ImageSharp/Memory/Buffer2D{T}.cs index 6dd4c93a5f..a7de343f9c 100644 --- a/src/ImageSharp/Memory/Buffer2D{T}.cs +++ b/src/ImageSharp/Memory/Buffer2D{T}.cs @@ -1,20 +1,20 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; using System.Runtime.CompilerServices; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Memory { - using System; - /// /// Represents a buffer of value type objects /// interpreted as a 2D region of x elements. /// /// The value type. internal class Buffer2D : IBuffer2D, IDisposable - where T : struct { + where T : struct + { public Buffer2D(Size size) : this(size.Width, size.Height) { @@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp.Memory /// The number of elements in a row /// The number of rows public Buffer2D(int width, int height) - : this(MemoryManager.Current.Allocate(width * height), width, height) + : this(MemoryManager.Current.Allocate(width * height), width, height) { this.Width = width; this.Height = height; @@ -38,7 +38,8 @@ namespace SixLabors.ImageSharp.Memory /// The array to pin /// The number of elements in a row /// The number of rows - public Buffer2D(T[] array, int width, int height) { + public Buffer2D(T[] array, int width, int height) + { this.Buffer = new Buffer(array, width * height); this.Width = width; this.Height = height; @@ -50,7 +51,8 @@ namespace SixLabors.ImageSharp.Memory /// The buffer to wrap /// The number of elements in a row /// The number of rows - public Buffer2D(Buffer wrappedBuffer, int width, int height) { + public Buffer2D(Buffer wrappedBuffer, int width, int height) + { this.Buffer = wrappedBuffer; this.Width = width; this.Height = height; @@ -92,7 +94,7 @@ namespace SixLabors.ImageSharp.Memory /// The instance public static Buffer2D CreateClean(int width, int height) { - return new Buffer2D(MemoryManager.Current.Allocate(width*height, true), width, height); + return new Buffer2D(MemoryManager.Current.Allocate(width * height, true), width, height); } /// @@ -102,7 +104,8 @@ namespace SixLabors.ImageSharp.Memory /// The instance public static Buffer2D CreateClean(Size size) => CreateClean(size.Width, size.Height); - public void Dispose() { + public void Dispose() + { this.Buffer?.Dispose(); } } diff --git a/src/ImageSharp/Memory/Buffer{T}.cs b/src/ImageSharp/Memory/Buffer{T}.cs index 186a2212c0..16f4e85999 100644 --- a/src/ImageSharp/Memory/Buffer{T}.cs +++ b/src/ImageSharp/Memory/Buffer{T}.cs @@ -16,6 +16,8 @@ namespace SixLabors.ImageSharp.Memory internal class Buffer : IBuffer where T : struct { + private MemoryManager memoryManager; + /// /// A pointer to the first element of when pinned. /// @@ -26,23 +28,6 @@ namespace SixLabors.ImageSharp.Memory /// private GCHandle handle; - /// - /// A value indicating wheter should be returned to - /// when disposing this instance. - /// - private bool isPoolingOwner; - - /// - /// Initializes a new instance of the class. - /// - /// The desired count of elements. (Minimum size for ) - public Buffer(int length) - { - this.Length = length; - this.Array = PixelDataPool.Rent(length); - this.isPoolingOwner = true; - } - /// /// Initializes a new instance of the class. /// @@ -51,7 +36,6 @@ namespace SixLabors.ImageSharp.Memory { this.Length = array.Length; this.Array = array; - this.isPoolingOwner = false; } /// @@ -68,7 +52,6 @@ namespace SixLabors.ImageSharp.Memory this.Length = length; this.Array = array; - this.isPoolingOwner = false; } internal Buffer(T[] array, int length, MemoryManager memoryManager) @@ -140,19 +123,6 @@ namespace SixLabors.ImageSharp.Memory return new Span(buffer.Array, 0, buffer.Length); } - /// - /// Creates a clean instance of initializing it's elements with 'default(T)'. - /// - /// The desired count of elements. (Minimum size for ) - /// The instance - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Buffer CreateClean(int count) - { - Buffer buffer = new Buffer(count); - buffer.Clear(); - return buffer; - } - /// /// Gets a to an offseted position inside the buffer. /// @@ -190,12 +160,9 @@ namespace SixLabors.ImageSharp.Memory this.IsDisposedOrLostArrayOwnership = true; this.UnPin(); - if (this.isPoolingOwner) - { - PixelDataPool.Return(this.Array); - } + this.memoryManager?.Release(this); - this.isPoolingOwner = false; + this.memoryManager = null; this.Array = null; this.Length = 0; @@ -220,7 +187,7 @@ namespace SixLabors.ImageSharp.Memory this.UnPin(); T[] array = this.Array; this.Array = null; - this.isPoolingOwner = false; + this.memoryManager = null; return array; } diff --git a/src/ImageSharp/Memory/MemoryManager.cs b/src/ImageSharp/Memory/MemoryManager.cs new file mode 100644 index 0000000000..54b727b607 --- /dev/null +++ b/src/ImageSharp/Memory/MemoryManager.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SixLabors.ImageSharp.Memory +{ + /// + /// Memory managers are used to allocate memory for image processing operations. + /// + public abstract class MemoryManager + { + /// + /// Gets or sets the that is currently in use. + /// + public static MemoryManager Current { get; set; } = new ArrayPoolMemoryManager(); + + /// + /// Allocates a of size , optionally + /// clearing the buffer before it gets returned. + /// + /// Type of the data stored in the buffer + /// Size of the buffer to allocate + /// True to clear the backing memory of the buffer + /// A buffer of values of type . + internal abstract Buffer Allocate(int size, bool clear = false) + where T : struct; + + /// + /// Releases the memory allocated for . After this, the buffer + /// is no longer usable. + /// + /// Type of the data stored in the buffer + /// The buffer to release + internal abstract void Release(Buffer buffer) + where T : struct; + } +} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs index 99a20516d2..4772816730 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs @@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = new Buffer(destination.Length * 3)) + using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -84,7 +84,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = new Buffer(destination.Length * 3)) + using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -123,7 +123,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = new Buffer(destination.Length * 3)) + using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -162,7 +162,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = new Buffer(destination.Length * 3)) + using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -201,7 +201,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = new Buffer(destination.Length * 3)) + using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -240,7 +240,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = new Buffer(destination.Length * 3)) + using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -279,7 +279,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = new Buffer(destination.Length * 3)) + using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -318,7 +318,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = new Buffer(destination.Length * 3)) + using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -357,7 +357,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = new Buffer(destination.Length * 3)) + using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -396,7 +396,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = new Buffer(destination.Length * 3)) + using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -435,7 +435,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = new Buffer(destination.Length * 3)) + using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -474,7 +474,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = new Buffer(destination.Length * 3)) + using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -513,7 +513,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = new Buffer(destination.Length * 3)) + using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -552,7 +552,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = new Buffer(destination.Length * 3)) + using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -591,7 +591,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = new Buffer(destination.Length * 3)) + using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -630,7 +630,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = new Buffer(destination.Length * 3)) + using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -669,7 +669,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = new Buffer(destination.Length * 3)) + using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -708,7 +708,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = new Buffer(destination.Length * 3)) + using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -747,7 +747,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = new Buffer(destination.Length * 3)) + using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -786,7 +786,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = new Buffer(destination.Length * 3)) + using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -825,7 +825,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = new Buffer(destination.Length * 3)) + using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt index 9d7d73db99..cb99237c45 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt @@ -87,7 +87,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = new Buffer(destination.Length * 3)) + using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); diff --git a/src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs index 72e9b8f555..5f6fd40238 100644 --- a/src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs @@ -67,8 +67,8 @@ namespace SixLabors.ImageSharp.Processing.Processors int width = maxX - minX; - using (var colors = new Buffer(width)) - using (var amount = new Buffer(width)) + using (var colors = MemoryManager.Current.Allocate(width)) + using (var amount = MemoryManager.Current.Allocate(width)) { for (int i = 0; i < width; i++) { diff --git a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs index b02585d8fd..0cee4c0b2d 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs @@ -83,7 +83,7 @@ namespace SixLabors.ImageSharp.Processing.Processors } int width = maxX - minX; - using (var rowColors = new Buffer(width)) + using (var rowColors = MemoryManager.Current.Allocate(width)) { for (int i = 0; i < width; i++) { @@ -96,7 +96,7 @@ namespace SixLabors.ImageSharp.Processing.Processors configuration.ParallelOptions, y => { - using (var amounts = new Buffer(width)) + using (var amounts = MemoryManager.Current.Allocate(width)) { int offsetY = y - startY; int offsetX = minX - startX; diff --git a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs index 7b592a6a4d..60915e6db2 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs @@ -104,7 +104,7 @@ namespace SixLabors.ImageSharp.Processing.Processors } int width = maxX - minX; - using (var rowColors = new Buffer(width)) + using (var rowColors = MemoryManager.Current.Allocate(width)) { for (int i = 0; i < width; i++) { @@ -117,7 +117,7 @@ namespace SixLabors.ImageSharp.Processing.Processors configuration.ParallelOptions, y => { - using (var amounts = new Buffer(width)) + using (var amounts = MemoryManager.Current.Allocate(width)) { int offsetY = y - startY; int offsetX = minX - startX; diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs index bb0845776c..6dcffbbc04 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs @@ -132,7 +132,7 @@ namespace SixLabors.ImageSharp.Processing.Processors y => { // TODO: Without Parallel.For() this buffer object could be reused: - using (var tempRowBuffer = new Buffer(source.Width)) + using (var tempRowBuffer = MemoryManager.Current.Allocate(source.Width)) { Span firstPassRow = firstPassPixels.GetRowSpan(y); Span sourceRow = source.GetPixelRowSpan(y); diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs index 83c2a2ee89..e00b94a2e3 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs @@ -22,8 +22,8 @@ namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk [GlobalSetup] public void Setup() { - this.destination = new Buffer(this.Count); - this.source = new Buffer(this.Count); + this.destination = MemoryManager.Current.Allocate(this.Count); + this.source = MemoryManager.Current.Allocate(this.Count); } [GlobalCleanup] diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs index b4f6ea9c06..593291dedd 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs @@ -25,8 +25,8 @@ [GlobalSetup] public void Setup() { - this.destination = new Buffer(this.Count); - this.source = new Buffer(this.Count * 4); + this.destination = MemoryManager.Current.Allocate(this.Count); + this.source = MemoryManager.Current.Allocate(this.Count * 4); this.source.Pin(); this.destination.Pin(); } diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs index 5c3648c2d8..d273124b89 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs @@ -19,8 +19,8 @@ namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk [GlobalSetup] public void Setup() { - this.destination = new Buffer(this.Count); - this.source = new Buffer(this.Count * 4); + this.destination = MemoryManager.Current.Allocate(this.Count); + this.source = MemoryManager.Current.Allocate(this.Count * 4); } [GlobalCleanup] diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs index 2bf4e0da67..7e29dfe3a8 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs @@ -21,8 +21,8 @@ namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk [GlobalSetup] public void Setup() { - this.source = new Buffer(this.Count); - this.destination = new Buffer(this.Count); + this.source = MemoryManager.Current.Allocate(this.Count); + this.destination = MemoryManager.Current.Allocate(this.Count); } [GlobalCleanup] diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs index 2d624c19f1..adc374c1fb 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs @@ -19,8 +19,8 @@ namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk [GlobalSetup] public void Setup() { - this.source = new Buffer(this.Count); - this.destination = new Buffer(this.Count * 3); + this.source = MemoryManager.Current.Allocate(this.Count); + this.destination = MemoryManager.Current.Allocate(this.Count * 3); } [GlobalCleanup] diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs index 150b55aed0..bead1384b2 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs @@ -24,8 +24,8 @@ namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk [GlobalSetup] public void Setup() { - this.source = new Buffer(this.Count); - this.destination = new Buffer(this.Count * 4); + this.source = MemoryManager.Current.Allocate(this.Count); + this.destination = MemoryManager.Current.Allocate(this.Count * 4); } [GlobalCleanup] diff --git a/tests/ImageSharp.Benchmarks/General/ClearBuffer.cs b/tests/ImageSharp.Benchmarks/General/ClearBuffer.cs index 0ac1413be0..47c8125543 100644 --- a/tests/ImageSharp.Benchmarks/General/ClearBuffer.cs +++ b/tests/ImageSharp.Benchmarks/General/ClearBuffer.cs @@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Benchmarks.General [GlobalSetup] public void Setup() { - this.buffer = new Buffer(this.Count); + this.buffer = MemoryManager.Current.Allocate(this.Count); } [GlobalCleanup] diff --git a/tests/ImageSharp.Benchmarks/General/IterateArray.cs b/tests/ImageSharp.Benchmarks/General/IterateArray.cs index 48ee266fe0..383e7080ca 100644 --- a/tests/ImageSharp.Benchmarks/General/IterateArray.cs +++ b/tests/ImageSharp.Benchmarks/General/IterateArray.cs @@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Benchmarks.General [GlobalSetup] public void Setup() { - this.buffer = new Buffer(this.Length); + this.buffer = MemoryManager.Current.Allocate(this.Length); this.buffer.Pin(); this.array = new Vector4[this.Length]; } diff --git a/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs b/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs index 5a3131f796..ef44d1e789 100644 --- a/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs +++ b/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Benchmarks Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = new Buffer(destination.Length * 3)) + using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.Benchmarks { using (Image image = new Image(800, 800)) { - Buffer amounts = new Buffer(image.Width); + Buffer amounts = MemoryManager.Current.Allocate(image.Width); for (int x = 0; x < image.Width; x++) { @@ -82,7 +82,7 @@ namespace SixLabors.ImageSharp.Benchmarks { using (Image image = new Image(800, 800)) { - Buffer amounts = new Buffer(image.Width); + Buffer amounts = MemoryManager.Current.Allocate(image.Width); for (int x = 0; x < image.Width; x++) { diff --git a/tests/ImageSharp.Benchmarks/Samplers/Glow.cs b/tests/ImageSharp.Benchmarks/Samplers/Glow.cs index 1f88c4fbfa..0494db9b9a 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/Glow.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/Glow.cs @@ -103,7 +103,7 @@ namespace SixLabors.ImageSharp.Benchmarks } int width = maxX - minX; - using (Buffer rowColors = new Buffer(width)) + using (Buffer rowColors = MemoryManager.Current.Allocate(width)) using (PixelAccessor sourcePixels = source.Lock()) { for (int i = 0; i < width; i++) diff --git a/tests/ImageSharp.Tests/Memory/BufferTests.cs b/tests/ImageSharp.Tests/Memory/BufferTests.cs index e1efeb24e8..8669f2bb05 100644 --- a/tests/ImageSharp.Tests/Memory/BufferTests.cs +++ b/tests/ImageSharp.Tests/Memory/BufferTests.cs @@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Tests.Memory [InlineData(1111)] public void ConstructWithOwnArray(int count) { - using (Buffer buffer = new Buffer(count)) + using (Buffer buffer = MemoryManager.Current.Allocate(count)) { Assert.False(buffer.IsDisposedOrLostArrayOwnership); Assert.NotNull(buffer.Array); @@ -76,7 +76,7 @@ namespace SixLabors.ImageSharp.Tests.Memory { for (int i = 0; i < 100; i++) { - using (Buffer buffer = Buffer.CreateClean(42)) + using (Buffer buffer = MemoryManager.Current.Allocate(42, true)) { for (int j = 0; j < buffer.Length; j++) { @@ -129,7 +129,7 @@ namespace SixLabors.ImageSharp.Tests.Memory [Fact] public void Dispose() { - Buffer buffer = new Buffer(42); + Buffer buffer = MemoryManager.Current.Allocate(42); buffer.Dispose(); Assert.True(buffer.IsDisposedOrLostArrayOwnership); @@ -140,7 +140,7 @@ namespace SixLabors.ImageSharp.Tests.Memory [InlineData(123)] public void CastToSpan(int bufferLength) { - using (Buffer buffer = new Buffer(bufferLength)) + using (Buffer buffer = MemoryManager.Current.Allocate(bufferLength)) { Span span = buffer; @@ -154,7 +154,7 @@ namespace SixLabors.ImageSharp.Tests.Memory [Fact] public void Span() { - using (Buffer buffer = new Buffer(42)) + using (Buffer buffer = MemoryManager.Current.Allocate(42)) { Span span = buffer.Span; @@ -173,7 +173,7 @@ namespace SixLabors.ImageSharp.Tests.Memory [InlineData(123, 17)] public void WithStartOnly(int bufferLength, int start) { - using (Buffer buffer = new Buffer(bufferLength)) + using (Buffer buffer = MemoryManager.Current.Allocate(bufferLength)) { Span span = buffer.Slice(start); @@ -187,7 +187,7 @@ namespace SixLabors.ImageSharp.Tests.Memory [InlineData(123, 17, 42)] public void WithStartAndLength(int bufferLength, int start, int spanLength) { - using (Buffer buffer = new Buffer(bufferLength)) + using (Buffer buffer = MemoryManager.Current.Allocate(bufferLength)) { Span span = buffer.Slice(start, spanLength); @@ -201,7 +201,7 @@ namespace SixLabors.ImageSharp.Tests.Memory public void UnPinAndTakeArrayOwnership() { TestStructs.Foo[] data = null; - using (Buffer buffer = new Buffer(42)) + using (Buffer buffer = MemoryManager.Current.Allocate(42)) { data = buffer.TakeArrayOwnership(); Assert.True(buffer.IsDisposedOrLostArrayOwnership); @@ -216,7 +216,7 @@ namespace SixLabors.ImageSharp.Tests.Memory [Fact] public void ReturnsPinnedPointerToTheBeginningOfArray() { - using (Buffer buffer = new Buffer(42)) + using (Buffer buffer = MemoryManager.Current.Allocate(42)) { TestStructs.Foo* actual = (TestStructs.Foo*)buffer.Pin(); fixed (TestStructs.Foo* expected = buffer.Array) @@ -229,7 +229,7 @@ namespace SixLabors.ImageSharp.Tests.Memory [Fact] public void SecondCallReturnsTheSamePointer() { - using (Buffer buffer = new Buffer(42)) + using (Buffer buffer = MemoryManager.Current.Allocate(42)) { IntPtr ptr1 = buffer.Pin(); IntPtr ptr2 = buffer.Pin(); @@ -241,7 +241,7 @@ namespace SixLabors.ImageSharp.Tests.Memory [Fact] public void WhenCalledOnDisposedBuffer_ThrowsInvalidOperationException() { - Buffer buffer = new Buffer(42); + Buffer buffer = MemoryManager.Current.Allocate(42); buffer.Dispose(); Assert.Throws(() => buffer.Pin()); diff --git a/tests/ImageSharp.Tests/Memory/SpanUtilityTests.cs b/tests/ImageSharp.Tests/Memory/SpanUtilityTests.cs index 395c325461..8b90295ce8 100644 --- a/tests/ImageSharp.Tests/Memory/SpanUtilityTests.cs +++ b/tests/ImageSharp.Tests/Memory/SpanUtilityTests.cs @@ -424,7 +424,7 @@ namespace SixLabors.ImageSharp.Tests.Memory Rgba32[] colors = { new Rgba32(0, 1, 2, 3), new Rgba32(4, 5, 6, 7), new Rgba32(8, 9, 10, 11), }; using (Buffer colorBuf = new Buffer(colors)) - using (Buffer byteBuf = new Buffer(colors.Length * 4)) + using (Buffer byteBuf = MemoryManager.Current.Allocate(colors.Length * 4)) { SpanHelper.Copy(colorBuf.Span.AsBytes(), byteBuf, colorBuf.Length * sizeof(Rgba32)); diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs index 0fde67d28e..c7227eb8ae 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs @@ -50,8 +50,8 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats int times = 200000; int count = 1024; - using (Buffer source = new Buffer(count)) - using (Buffer dest = new Buffer(count)) + using (Buffer source = MemoryManager.Current.Allocate(count)) + using (Buffer dest = MemoryManager.Current.Allocate(count)) { this.Measure( times, @@ -344,7 +344,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats { this.SourceBuffer = new Buffer(source); this.ExpectedDestBuffer = new Buffer(expectedDest); - this.ActualDestBuffer = new Buffer(expectedDest.Length); + this.ActualDestBuffer = MemoryManager.Current.Allocate(expectedDest.Length); } public void Dispose() diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs index a201b39bd2..babe148c80 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs int length = source.Length; Guard.MustBeSizedAtLeast(dest, length, nameof(dest)); - using (var rgbaBuffer = new Buffer(length)) + using (var rgbaBuffer = MemoryManager.Current.Allocate(length)) { PixelOperations.Instance.ToRgba32(source, rgbaBuffer, length); @@ -39,7 +39,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs int length = source.Length; Guard.MustBeSizedAtLeast(dest, length, nameof(dest)); - using (var rgbaBuffer = new Buffer(length)) + using (var rgbaBuffer = MemoryManager.Current.Allocate(length)) { PixelOperations.Instance.ToRgba32(source, rgbaBuffer, length); @@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs int length = source.Length; Guard.MustBeSizedAtLeast(dest, length, nameof(dest)); - using (var rgbaBuffer = new Buffer(length)) + using (var rgbaBuffer = MemoryManager.Current.Allocate(length)) { PixelOperations.Instance.ToRgb24(source, rgbaBuffer, length); @@ -96,7 +96,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs var image = new Image(w, h); - using (var workBuffer = new Buffer(w)) + using (var workBuffer = MemoryManager.Current.Allocate(w)) { var destPtr = (Argb32*)workBuffer.Pin(); for (int y = 0; y < h; y++) @@ -138,7 +138,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs var image = new Image(w, h); - using (var workBuffer = new Buffer(w)) + using (var workBuffer = MemoryManager.Current.Allocate(w)) { var destPtr = (Rgb24*)workBuffer.Pin(); for (int y = 0; y < h; y++) @@ -170,7 +170,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs long destRowByteCount = data.Stride; long sourceRowByteCount = w * sizeof(Argb32); - using (var workBuffer = new Buffer(w)) + using (var workBuffer = MemoryManager.Current.Allocate(w)) { var sourcePtr = (Argb32*)workBuffer.Pin(); From b91b2966e81eee13c491db17a974d22af28d5f00 Mon Sep 17 00:00:00 2001 From: Lauri Kotilainen Date: Thu, 11 Jan 2018 21:04:33 +0200 Subject: [PATCH 003/151] - Allocate Buffer2Ds from memory manager --- .../Decoder/JpegComponentPostProcessor.cs | 4 ++- .../Components/PdfJsQuantizationTables.cs | 2 +- src/ImageSharp/Image/ImageFrame{TPixel}.cs | 2 +- src/ImageSharp/Memory/Buffer2D{T}.cs | 30 ------------------- src/ImageSharp/Memory/MemoryManager.cs | 8 +++++ .../Processors/Transforms/ResizeProcessor.cs | 2 +- .../General/PixelIndexing.cs | 2 +- .../Image/Jpeg/YCbCrColorConversion.cs | 2 +- .../Jpg/Block8x8FTests.CopyToBufferArea.cs | 4 +-- .../Formats/Jpg/JpegColorConverterTests.cs | 2 +- .../Jpg/Utils/LibJpegTools.ComponentData.cs | 2 +- .../ImageSharp.Tests/Memory/Buffer2DTests.cs | 10 +++---- .../Memory/BufferAreaTests.cs | 4 +-- 13 files changed, 27 insertions(+), 47 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegComponentPostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegComponentPostProcessor.cs index 87c1431e02..7c91e85e74 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegComponentPostProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegComponentPostProcessor.cs @@ -26,7 +26,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder { this.Component = component; this.ImagePostProcessor = imagePostProcessor; - this.ColorBuffer = new Buffer2D(imagePostProcessor.PostProcessorBufferSize); + this.ColorBuffer = MemoryManager.Current.Allocate2D( + imagePostProcessor.PostProcessorBufferSize.Width, + imagePostProcessor.PostProcessorBufferSize.Height); this.BlockRowsPerStep = JpegImagePostProcessor.BlockRowsPerStep / this.Component.SubSamplingDivisors.Height; this.blockAreaSize = this.Component.SubSamplingDivisors * 8; diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsQuantizationTables.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsQuantizationTables.cs index 1000ce82c5..a585c5080f 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsQuantizationTables.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsQuantizationTables.cs @@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components get; set; } - = new Buffer2D(64, 4); + = MemoryManager.Current.Allocate2D(64, 4); /// public void Dispose() diff --git a/src/ImageSharp/Image/ImageFrame{TPixel}.cs b/src/ImageSharp/Image/ImageFrame{TPixel}.cs index 45ed5f053a..a0e4976cb6 100644 --- a/src/ImageSharp/Image/ImageFrame{TPixel}.cs +++ b/src/ImageSharp/Image/ImageFrame{TPixel}.cs @@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp /// The source. internal ImageFrame(ImageFrame source) { - this.pixelBuffer = new Buffer2D(source.pixelBuffer.Width, source.pixelBuffer.Height); + this.pixelBuffer = MemoryManager.Current.Allocate2D(source.pixelBuffer.Width, source.pixelBuffer.Height); source.pixelBuffer.Span.CopyTo(this.pixelBuffer.Span); this.MetaData = source.MetaData.Clone(); } diff --git a/src/ImageSharp/Memory/Buffer2D{T}.cs b/src/ImageSharp/Memory/Buffer2D{T}.cs index a7de343f9c..73444d4978 100644 --- a/src/ImageSharp/Memory/Buffer2D{T}.cs +++ b/src/ImageSharp/Memory/Buffer2D{T}.cs @@ -15,36 +15,6 @@ namespace SixLabors.ImageSharp.Memory internal class Buffer2D : IBuffer2D, IDisposable where T : struct { - public Buffer2D(Size size) - : this(size.Width, size.Height) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The number of elements in a row - /// The number of rows - public Buffer2D(int width, int height) - : this(MemoryManager.Current.Allocate(width * height), width, height) - { - this.Width = width; - this.Height = height; - } - - /// - /// Initializes a new instance of the class. - /// - /// The array to pin - /// The number of elements in a row - /// The number of rows - public Buffer2D(T[] array, int width, int height) - { - this.Buffer = new Buffer(array, width * height); - this.Width = width; - this.Height = height; - } - /// /// Initializes a new instance of the class. /// diff --git a/src/ImageSharp/Memory/MemoryManager.cs b/src/ImageSharp/Memory/MemoryManager.cs index 54b727b607..38e68e22c0 100644 --- a/src/ImageSharp/Memory/MemoryManager.cs +++ b/src/ImageSharp/Memory/MemoryManager.cs @@ -35,5 +35,13 @@ namespace SixLabors.ImageSharp.Memory /// The buffer to release internal abstract void Release(Buffer buffer) where T : struct; + + internal Buffer2D Allocate2D(int width, int height, bool clear = false) + where T : struct + { + var buffer = this.Allocate(width * height, clear); + + return new Buffer2D(buffer, width, height); + } } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs index 6dcffbbc04..dbc05876dd 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs @@ -121,7 +121,7 @@ namespace SixLabors.ImageSharp.Processing.Processors // First process the columns. Since we are not using multiple threads startY and endY // are the upper and lower bounds of the source rectangle. // TODO: Using a transposed variant of 'firstPassPixels' could eliminate the need for the WeightsWindow.ComputeWeightedColumnSum() method, and improve speed! - using (var firstPassPixels = new Buffer2D(width, source.Height)) + using (var firstPassPixels = MemoryManager.Current.Allocate2D(width, source.Height)) { firstPassPixels.Buffer.Clear(); diff --git a/tests/ImageSharp.Benchmarks/General/PixelIndexing.cs b/tests/ImageSharp.Benchmarks/General/PixelIndexing.cs index b0560b1631..58f835a076 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelIndexing.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelIndexing.cs @@ -149,7 +149,7 @@ public void Setup() { this.width = 2048; - this.buffer = new Buffer2D(2048, 2048); + this.buffer = MemoryManager.Current.Allocate2D(2048, 2048); this.pointer = (Vector4*)this.buffer.Buffer.Pin(); this.array = this.buffer.Buffer.Array; this.pinnable = Unsafe.As>(this.array); diff --git a/tests/ImageSharp.Benchmarks/Image/Jpeg/YCbCrColorConversion.cs b/tests/ImageSharp.Benchmarks/Image/Jpeg/YCbCrColorConversion.cs index 93420aacf8..f513e0d38b 100644 --- a/tests/ImageSharp.Benchmarks/Image/Jpeg/YCbCrColorConversion.cs +++ b/tests/ImageSharp.Benchmarks/Image/Jpeg/YCbCrColorConversion.cs @@ -76,7 +76,7 @@ } // no need to dispose when buffer is not array owner - buffers[i] = new Buffer2D(values, values.Length, 1); + buffers[i] = MemoryManager.Current.Allocate2D(values.Length, 1); } return buffers; diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs index 191cfec731..1dc4896eeb 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs @@ -48,7 +48,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg { Block8x8F block = CreateRandomFloatBlock(0, 100); - using (var buffer = new Buffer2D(20, 20)) + using (var buffer = MemoryManager.Current.Allocate2D(20, 20)) { BufferArea area = buffer.GetArea(5, 10, 8, 8); block.CopyTo(area); @@ -78,7 +78,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg var start = new Point(50, 50); - using (var buffer = new Buffer2D(100, 100)) + using (var buffer = MemoryManager.Current.Allocate2D(100, 100)) { BufferArea area = buffer.GetArea(start.X, start.Y, 8 * horizontalFactor, 8 * verticalFactor); block.CopyTo(area, horizontalFactor, verticalFactor); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs index 7e0dc915ce..95a62f7fe3 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs @@ -273,7 +273,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg } // no need to dispose when buffer is not array owner - buffers[i] = new Buffer2D(values, values.Length, 1); + buffers[i] = new Buffer2D(new Buffer(values, values.Length), values.Length, 1); } return new JpegColorConverter.ComponentValues(buffers, 0); } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs index 40b41b9cba..ad9ad81436 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs @@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils this.HeightInBlocks = heightInBlocks; this.WidthInBlocks = widthInBlocks; this.Index = index; - this.SpectralBlocks = new Buffer2D(this.WidthInBlocks, this.HeightInBlocks); + this.SpectralBlocks = MemoryManager.Current.Allocate2D(this.WidthInBlocks, this.HeightInBlocks); } public Size Size => new Size(this.WidthInBlocks, this.HeightInBlocks); diff --git a/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs index 7ab0ed9491..2e99616a89 100644 --- a/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs +++ b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs @@ -31,7 +31,7 @@ namespace SixLabors.ImageSharp.Tests.Memory [InlineData(1025, 17)] public void Construct(int width, int height) { - using (Buffer2D buffer = new Buffer2D(width, height)) + using (Buffer2D buffer = MemoryManager.Current.Allocate2D(width, height)) { Assert.Equal(width, buffer.Width); Assert.Equal(height, buffer.Height); @@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.Tests.Memory public void Construct_FromExternalArray(int width, int height) { TestStructs.Foo[] array = new TestStructs.Foo[width * height + 10]; - using (Buffer2D buffer = new Buffer2D(array, width, height)) + using (Buffer2D buffer = new Buffer2D(new Buffer(array, array.Length), width, height)) { Assert.Equal(width, buffer.Width); Assert.Equal(height, buffer.Height); @@ -76,7 +76,7 @@ namespace SixLabors.ImageSharp.Tests.Memory [InlineData(17, 42, 41)] public void GetRowSpanY(int width, int height, int y) { - using (Buffer2D buffer = new Buffer2D(width, height)) + using (Buffer2D buffer = MemoryManager.Current.Allocate2D(width, height)) { Span span = buffer.GetRowSpan(y); @@ -92,7 +92,7 @@ namespace SixLabors.ImageSharp.Tests.Memory [InlineData(17, 42, 0, 41)] public void GetRowSpanXY(int width, int height, int x, int y) { - using (Buffer2D buffer = new Buffer2D(width, height)) + using (Buffer2D buffer = MemoryManager.Current.Allocate2D(width, height)) { Span span = buffer.GetRowSpan(x, y); @@ -108,7 +108,7 @@ namespace SixLabors.ImageSharp.Tests.Memory [InlineData(99, 88, 98, 87)] public void Indexer(int width, int height, int x, int y) { - using (Buffer2D buffer = new Buffer2D(width, height)) + using (Buffer2D buffer = MemoryManager.Current.Allocate2D(width, height)) { TestStructs.Foo[] array = buffer.Buffer.Array; diff --git a/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs b/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs index 026b694981..74e9098b90 100644 --- a/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs +++ b/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Tests.Memory [Fact] public void Construct() { - using (var buffer = new Buffer2D(10, 20)) + using (var buffer = MemoryManager.Current.Allocate2D(10, 20)) { var rectangle = new Rectangle(3,2, 5, 6); var area = new BufferArea(buffer, rectangle); @@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Tests.Memory private static Buffer2D CreateTestBuffer(int w, int h) { - var buffer = new Buffer2D(w, h); + var buffer = MemoryManager.Current.Allocate2D(w, h); for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) From f9a52c0df76bd936212f8ca3322c7ace8dcd6678 Mon Sep 17 00:00:00 2001 From: Lauri Kotilainen Date: Thu, 11 Jan 2018 21:05:42 +0200 Subject: [PATCH 004/151] - Add a minimum size threshold for array pool usage - Add a null memory manager that doesn't do any actual memory management --- .../Memory/ArrayPoolMemoryManager.cs | 36 +++++++++++++++++++ src/ImageSharp/Memory/MemoryManager.cs | 2 +- src/ImageSharp/Memory/NullMemoryManager.cs | 19 ++++++++++ 3 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 src/ImageSharp/Memory/NullMemoryManager.cs diff --git a/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs b/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs index c4ce7d2991..7a3adfb535 100644 --- a/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs +++ b/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs @@ -1,4 +1,5 @@ using System.Buffers; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.Memory { @@ -7,9 +8,30 @@ namespace SixLabors.ImageSharp.Memory /// public class ArrayPoolMemoryManager : MemoryManager { + private readonly int minSizeBytes; + + /// + /// Initializes a new instance of the class. + /// By passing an integer greater than 0 as , a + /// minimum threshold for pooled allocations is set. Any allocation requests that + /// would require less size than the threshold will not be managed within the array pool. + /// + /// + /// Minimum size, in bytes, before an array pool is used to satisfy the request. + /// + public ArrayPoolMemoryManager(int minSizeBytes = 0) + { + this.minSizeBytes = minSizeBytes; + } + /// internal override Buffer Allocate(int size, bool clear = false) { + if (this.minSizeBytes > 0 && size < this.minSizeBytes * SizeHelper.Size) + { + return new Buffer(new T[size], size); + } + var buffer = new Buffer(PixelDataPool.Rent(size), size, this); if (clear) { @@ -24,5 +46,19 @@ namespace SixLabors.ImageSharp.Memory { PixelDataPool.Return(buffer.Array); } + + internal static class SizeHelper + { + static SizeHelper() + { + #if NETSTANDARD1_1 + Size = Marshal.SizeOf(typeof(T)); + #else + Size = Marshal.SizeOf(); + #endif + } + + public static int Size { get; } + } } } \ No newline at end of file diff --git a/src/ImageSharp/Memory/MemoryManager.cs b/src/ImageSharp/Memory/MemoryManager.cs index 38e68e22c0..b68a01feb1 100644 --- a/src/ImageSharp/Memory/MemoryManager.cs +++ b/src/ImageSharp/Memory/MemoryManager.cs @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Memory /// /// Gets or sets the that is currently in use. /// - public static MemoryManager Current { get; set; } = new ArrayPoolMemoryManager(); + public static MemoryManager Current { get; set; } = new ArrayPoolMemoryManager(1024 * 80); /// /// Allocates a of size , optionally diff --git a/src/ImageSharp/Memory/NullMemoryManager.cs b/src/ImageSharp/Memory/NullMemoryManager.cs new file mode 100644 index 0000000000..32642dae4a --- /dev/null +++ b/src/ImageSharp/Memory/NullMemoryManager.cs @@ -0,0 +1,19 @@ +namespace SixLabors.ImageSharp.Memory +{ + /// + /// Implements by allocating new buffers on every call. + /// + public class NullMemoryManager : MemoryManager + { + /// + internal override Buffer Allocate(int size, bool clear = false) + { + return new Buffer(new T[size], size); + } + + /// + internal override void Release(Buffer buffer) + { + } + } +} From 1985363ca255b3571287343d5ba2766c83edc19f Mon Sep 17 00:00:00 2001 From: Lauri Kotilainen Date: Thu, 11 Jan 2018 23:13:19 +0200 Subject: [PATCH 005/151] - Removed a test that doesn't actually test anything any more --- tests/ImageSharp.Tests/Memory/Buffer2DTests.cs | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs index 2e99616a89..f14995433d 100644 --- a/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs +++ b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs @@ -39,21 +39,6 @@ namespace SixLabors.ImageSharp.Tests.Memory } } - [Theory] - [InlineData(7, 42)] - [InlineData(1025, 17)] - public void Construct_FromExternalArray(int width, int height) - { - TestStructs.Foo[] array = new TestStructs.Foo[width * height + 10]; - using (Buffer2D buffer = new Buffer2D(new Buffer(array, array.Length), width, height)) - { - Assert.Equal(width, buffer.Width); - Assert.Equal(height, buffer.Height); - Assert.Equal(width * height, buffer.Buffer.Length); - } - } - - [Fact] public void CreateClean() { From 5d568686dbbc1e6fdb8b37e1656c70ccd9e4ceb0 Mon Sep 17 00:00:00 2001 From: Lauri Kotilainen Date: Thu, 11 Jan 2018 22:29:58 +0200 Subject: [PATCH 006/151] - Removed PixelDataPool --- .../Memory/ArrayPoolMemoryManager.cs | 35 +++++++----- src/ImageSharp/Memory/Buffer{T}.cs | 2 +- .../Memory/PixelDataPoolTests.cs | 55 ------------------- 3 files changed, 21 insertions(+), 71 deletions(-) delete mode 100644 tests/ImageSharp.Tests/Memory/PixelDataPoolTests.cs diff --git a/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs b/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs index 7a3adfb535..86f3f0015c 100644 --- a/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs +++ b/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs @@ -1,14 +1,17 @@ using System.Buffers; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.Memory { + /// /// Implements by allocating memory from . /// public class ArrayPoolMemoryManager : MemoryManager { private readonly int minSizeBytes; + private readonly ArrayPool pool; /// /// Initializes a new instance of the class. @@ -22,17 +25,21 @@ namespace SixLabors.ImageSharp.Memory public ArrayPoolMemoryManager(int minSizeBytes = 0) { this.minSizeBytes = minSizeBytes; + + this.pool = ArrayPool.Create(CalculateMaxArrayLength(), 50); } /// internal override Buffer Allocate(int size, bool clear = false) { - if (this.minSizeBytes > 0 && size < this.minSizeBytes * SizeHelper.Size) + int itemSize = Unsafe.SizeOf(); + if (this.minSizeBytes > 0 && itemSize < this.minSizeBytes * itemSize) { - return new Buffer(new T[size], size); + return new Buffer(new T[itemSize], itemSize); } - var buffer = new Buffer(PixelDataPool.Rent(size), size, this); + byte[] byteBuffer = this.pool.Rent(itemSize * itemSize); + var buffer = new Buffer(Unsafe.As(byteBuffer), itemSize, this); if (clear) { buffer.Clear(); @@ -44,21 +51,19 @@ namespace SixLabors.ImageSharp.Memory /// internal override void Release(Buffer buffer) { - PixelDataPool.Return(buffer.Array); + var byteBuffer = Unsafe.As(buffer.Array); + this.pool.Return(byteBuffer); } - internal static class SizeHelper + /// + /// Heuristically calculates a reasonable maxArrayLength value for the backing . + /// + /// The maxArrayLength value + internal static int CalculateMaxArrayLength() { - static SizeHelper() - { - #if NETSTANDARD1_1 - Size = Marshal.SizeOf(typeof(T)); - #else - Size = Marshal.SizeOf(); - #endif - } - - public static int Size { get; } + const int MaximumExpectedImageSize = 16384 * 16384; + const int MaximumBytesPerPixel = 4; + return MaximumExpectedImageSize * MaximumBytesPerPixel; } } } \ No newline at end of file diff --git a/src/ImageSharp/Memory/Buffer{T}.cs b/src/ImageSharp/Memory/Buffer{T}.cs index 16f4e85999..d25cc232ad 100644 --- a/src/ImageSharp/Memory/Buffer{T}.cs +++ b/src/ImageSharp/Memory/Buffer{T}.cs @@ -171,7 +171,7 @@ namespace SixLabors.ImageSharp.Memory /// /// Unpins and makes the object "quasi-disposed" so the array is no longer owned by this object. - /// If is rented, it's the callers responsibility to return it to it's pool. (Most likely ) + /// If is rented, it's the callers responsibility to return it to it's pool. /// /// The unpinned [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/tests/ImageSharp.Tests/Memory/PixelDataPoolTests.cs b/tests/ImageSharp.Tests/Memory/PixelDataPoolTests.cs deleted file mode 100644 index fdfd4c4b7f..0000000000 --- a/tests/ImageSharp.Tests/Memory/PixelDataPoolTests.cs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - - - -// ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Tests.Memory -{ - using SixLabors.ImageSharp.Memory; - - using Xunit; - - /// - /// Tests the class. - /// - public class PixelDataPoolTests - { - [Fact] - public void PixelDataPoolRentsMinimumSize() - { - Rgba32[] pixels = PixelDataPool.Rent(1024); - - Assert.True(pixels.Length >= 1024); - } - - [Fact] - public void PixelDataPoolDoesNotThrowWhenReturningNonPooled() - { - Rgba32[] pixels = new Rgba32[1024]; - - PixelDataPool.Return(pixels); - - Assert.True(pixels.Length >= 1024); - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void CalculateMaxArrayLength(bool isRawData) - { - int max = isRawData ? PixelDataPool.CalculateMaxArrayLength() - : PixelDataPool.CalculateMaxArrayLength(); - - Assert.Equal(max > 1024 * 1024, !isRawData); - } - - [Fact] - public void RentNonIPixelData() - { - byte[] data = PixelDataPool.Rent(16384); - - Assert.True(data.Length >= 16384); - } - } -} \ No newline at end of file From 29937e17aee6514dbbf2e8632668fd326fa18c5d Mon Sep 17 00:00:00 2001 From: Lauri Kotilainen Date: Fri, 12 Jan 2018 15:00:45 +0200 Subject: [PATCH 007/151] - Oops. Note to self: don't make changes to unsafe code just before going to sleep --- src/ImageSharp/Memory/ArrayPoolMemoryManager.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs b/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs index 86f3f0015c..80c6e00e3a 100644 --- a/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs +++ b/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs @@ -30,16 +30,18 @@ namespace SixLabors.ImageSharp.Memory } /// - internal override Buffer Allocate(int size, bool clear = false) + internal override Buffer Allocate(int itemCount, bool clear = false) { - int itemSize = Unsafe.SizeOf(); - if (this.minSizeBytes > 0 && itemSize < this.minSizeBytes * itemSize) + int itemSizeBytes = Unsafe.SizeOf(); + int bufferSizeInBytes = itemCount * itemSizeBytes; + + if (this.minSizeBytes > 0 && bufferSizeInBytes < this.minSizeBytes) { - return new Buffer(new T[itemSize], itemSize); + return new Buffer(new T[itemCount], itemCount); } - byte[] byteBuffer = this.pool.Rent(itemSize * itemSize); - var buffer = new Buffer(Unsafe.As(byteBuffer), itemSize, this); + byte[] byteBuffer = this.pool.Rent(bufferSizeInBytes); + var buffer = new Buffer(Unsafe.As(byteBuffer), itemCount, this); if (clear) { buffer.Clear(); From 8d44ae346ed1247bb71e1b789579cd00d0ab3e14 Mon Sep 17 00:00:00 2001 From: Lauri Kotilainen Date: Fri, 12 Jan 2018 13:35:35 +0200 Subject: [PATCH 008/151] - Explicitly pass MemoryManager to the places that need it (aside from a few exceptions) --- .../Brushes/ImageBrush{TPixel}.cs | 4 +- .../Brushes/PatternBrush{TPixel}.cs | 4 +- .../Brushes/Processors/BrushApplicator.cs | 4 +- .../Brushes/RecolorBrush{TPixel}.cs | 4 +- .../Brushes/SolidBrush{TPixel}.cs | 4 +- src/ImageSharp.Drawing/Paths/DrawPath.cs | 2 +- src/ImageSharp.Drawing/Paths/FillPaths.cs | 4 +- src/ImageSharp.Drawing/Paths/ShapePath.cs | 6 ++- src/ImageSharp.Drawing/Paths/ShapeRegion.cs | 7 +++- .../Processors/DrawImageProcessor.cs | 2 +- .../Processors/FillProcessor.cs | 2 +- .../Processors/FillRegionProcessor.cs | 2 +- src/ImageSharp/Configuration.cs | 5 +++ .../DefaultInternalImageProcessorContext.cs | 4 ++ src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 6 +-- src/ImageSharp/Formats/Gif/GifDecoderCore.cs | 6 +-- .../Decoder/JpegComponentPostProcessor.cs | 4 +- .../Common/Decoder/JpegImagePostProcessor.cs | 6 +-- .../Components/Decoder/OrigComponent.cs | 4 +- .../Jpeg/GolangPort/OrigJpegDecoderCore.cs | 4 +- .../Components/PdfJsFrameComponent.cs | 6 ++- .../PdfJsPort/Components/PdfJsHuffmanTable.cs | 14 +++---- .../Components/PdfJsJpegPixelArea.cs | 9 ++-- .../Components/PdfJsQuantizationTables.cs | 7 +++- .../Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs | 18 ++++---- .../Formats/Png/PngConfigurationModule.cs | 8 ++-- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 14 +++---- src/ImageSharp/Formats/Png/PngEncoder.cs | 11 ++++- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 19 +++++---- .../IImageProcessingContext{TPixel}.cs | 3 ++ src/ImageSharp/Image/Image.Decode.cs | 2 +- .../Image/ImageFrame.LoadPixelData.cs | 2 +- src/ImageSharp/Image/ImageFrameCollection.cs | 4 +- src/ImageSharp/Image/ImageFrame{TPixel}.cs | 21 ++++++---- src/ImageSharp/Image/PixelAccessor{TPixel}.cs | 2 +- src/ImageSharp/Image/PixelArea{TPixel}.cs | 2 +- src/ImageSharp/Memory/Buffer2D{T}.cs | 18 -------- src/ImageSharp/Memory/MemoryManager.cs | 5 --- .../DefaultPixelBlenders.Generated.cs | 42 +++++++++---------- .../DefaultPixelBlenders.Generated.tt | 2 +- .../Processing/ColorMatrix/Lomograph.cs | 4 +- .../Processing/ColorMatrix/Polaroid.cs | 4 +- .../Processing/Effects/BackgroundColor.cs | 4 +- src/ImageSharp/Processing/Overlays/Glow.cs | 4 +- .../Processing/Overlays/Vignette.cs | 4 +- .../ColorMatrix/LomographProcessor.cs | 11 +++-- .../ColorMatrix/PolaroidProcessor.cs | 13 ++++-- .../Effects/BackgroundColorProcessor.cs | 9 ++-- .../Processors/Overlays/GlowProcessor.cs | 9 ++-- .../Processors/Overlays/VignetteProcessor.cs | 12 ++++-- .../ResamplingWeightedProcessor.Weights.cs | 4 +- .../Transforms/ResamplingWeightedProcessor.cs | 12 ++++-- .../Processors/Transforms/ResizeProcessor.cs | 14 +++---- .../Processing/Transforms/Resize.cs | 4 +- .../FakeImageOperationsProvider.cs | 6 +++ 55 files changed, 229 insertions(+), 178 deletions(-) diff --git a/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs index d642253851..ff69d65ee6 100644 --- a/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs @@ -122,8 +122,8 @@ namespace SixLabors.ImageSharp.Drawing.Brushes internal override void Apply(Span scanline, int x, int y) { // Create a span for colors - using (var amountBuffer = MemoryManager.Current.Allocate(scanline.Length)) - using (var overlay = MemoryManager.Current.Allocate(scanline.Length)) + using (var amountBuffer = this.Target.MemoryManager.Allocate(scanline.Length)) + using (var overlay = this.Target.MemoryManager.Allocate(scanline.Length)) { int sourceY = (y - this.offsetY) % this.yLength; int offsetX = x - this.offsetX; diff --git a/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs index 4b26c4edce..2a25979873 100644 --- a/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs @@ -152,8 +152,8 @@ namespace SixLabors.ImageSharp.Drawing.Brushes internal override void Apply(Span scanline, int x, int y) { int patternY = y % this.pattern.Height; - using (var amountBuffer = MemoryManager.Current.Allocate(scanline.Length)) - using (var overlay = MemoryManager.Current.Allocate(scanline.Length)) + using (var amountBuffer = this.Target.MemoryManager.Allocate(scanline.Length)) + using (var overlay = this.Target.MemoryManager.Allocate(scanline.Length)) { for (int i = 0; i < scanline.Length; i++) { diff --git a/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs b/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs index 5a50e12fb8..08bbb571ad 100644 --- a/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs +++ b/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs @@ -65,8 +65,8 @@ namespace SixLabors.ImageSharp.Drawing.Brushes.Processors /// scanlineBuffer will be > scanlineWidth but provide and offset in case we want to share a larger buffer across runs. internal virtual void Apply(Span scanline, int x, int y) { - using (var amountBuffer = MemoryManager.Current.Allocate(scanline.Length)) - using (var overlay = MemoryManager.Current.Allocate(scanline.Length)) + using (var amountBuffer = this.Target.MemoryManager.Allocate(scanline.Length)) + using (var overlay = this.Target.MemoryManager.Allocate(scanline.Length)) { for (int i = 0; i < scanline.Length; i++) { diff --git a/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs index 1c02884f2a..d480457113 100644 --- a/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs @@ -144,8 +144,8 @@ namespace SixLabors.ImageSharp.Drawing.Brushes /// internal override void Apply(Span scanline, int x, int y) { - using (var amountBuffer = MemoryManager.Current.Allocate(scanline.Length)) - using (var overlay = MemoryManager.Current.Allocate(scanline.Length)) + using (var amountBuffer = this.Target.MemoryManager.Allocate(scanline.Length)) + using (var overlay = this.Target.MemoryManager.Allocate(scanline.Length)) { for (int i = 0; i < scanline.Length; i++) { diff --git a/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs index b935bbe363..2d460603bb 100644 --- a/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs @@ -61,7 +61,7 @@ namespace SixLabors.ImageSharp.Drawing.Brushes public SolidBrushApplicator(ImageFrame source, TPixel color, GraphicsOptions options) : base(source, options) { - this.Colors = MemoryManager.Current.Allocate(source.Width); + this.Colors = source.MemoryManager.Allocate(source.Width); for (int i = 0; i < this.Colors.Length; i++) { this.Colors[i] = color; @@ -96,7 +96,7 @@ namespace SixLabors.ImageSharp.Drawing.Brushes { Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); - using (var amountBuffer = MemoryManager.Current.Allocate(scanline.Length)) + using (var amountBuffer = this.Target.MemoryManager.Allocate(scanline.Length)) { for (int i = 0; i < scanline.Length; i++) { diff --git a/src/ImageSharp.Drawing/Paths/DrawPath.cs b/src/ImageSharp.Drawing/Paths/DrawPath.cs index b6c821a60b..a46d5751f6 100644 --- a/src/ImageSharp.Drawing/Paths/DrawPath.cs +++ b/src/ImageSharp.Drawing/Paths/DrawPath.cs @@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp public static IImageProcessingContext Draw(this IImageProcessingContext source, IPen pen, IPath path, GraphicsOptions options) where TPixel : struct, IPixel { - return source.Fill(pen.StrokeFill, new ShapePath(path, pen), options); + return source.Fill(pen.StrokeFill, new ShapePath(source.GetMemoryManager(), path, pen), options); } /// diff --git a/src/ImageSharp.Drawing/Paths/FillPaths.cs b/src/ImageSharp.Drawing/Paths/FillPaths.cs index f554ed7581..5972c52a05 100644 --- a/src/ImageSharp.Drawing/Paths/FillPaths.cs +++ b/src/ImageSharp.Drawing/Paths/FillPaths.cs @@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, IPath path, GraphicsOptions options) where TPixel : struct, IPixel { - return source.Fill(brush, new ShapeRegion(path), options); + return source.Fill(brush, new ShapeRegion(source.GetMemoryManager(), path), options); } /// @@ -39,7 +39,7 @@ namespace SixLabors.ImageSharp public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, IPath path) where TPixel : struct, IPixel { - return source.Fill(brush, new ShapeRegion(path), GraphicsOptions.Default); + return source.Fill(brush, new ShapeRegion(source.GetMemoryManager(), path), GraphicsOptions.Default); } /// diff --git a/src/ImageSharp.Drawing/Paths/ShapePath.cs b/src/ImageSharp.Drawing/Paths/ShapePath.cs index 61f1291c45..f973668e54 100644 --- a/src/ImageSharp.Drawing/Paths/ShapePath.cs +++ b/src/ImageSharp.Drawing/Paths/ShapePath.cs @@ -4,6 +4,8 @@ using System; using System.Buffers; using System.Numerics; + +using SixLabors.ImageSharp.Memory; using SixLabors.Shapes; namespace SixLabors.ImageSharp.Drawing @@ -19,8 +21,8 @@ namespace SixLabors.ImageSharp.Drawing /// The shape. /// The pen to apply to the shape. // SixLabors.shape willbe moving to a Span/ReadOnlySpan based API shortly use ToArray for now. - public ShapePath(IPath shape, Pens.IPen pen) - : base(shape.GenerateOutline(pen.StrokeWidth, pen.StrokePattern.ToArray())) + public ShapePath(MemoryManager memoryManager, IPath shape, Pens.IPen pen) + : base(memoryManager, shape.GenerateOutline(pen.StrokeWidth, pen.StrokePattern.ToArray())) { } } diff --git a/src/ImageSharp.Drawing/Paths/ShapeRegion.cs b/src/ImageSharp.Drawing/Paths/ShapeRegion.cs index 7851bac50b..489468dbed 100644 --- a/src/ImageSharp.Drawing/Paths/ShapeRegion.cs +++ b/src/ImageSharp.Drawing/Paths/ShapeRegion.cs @@ -15,12 +15,15 @@ namespace SixLabors.ImageSharp.Drawing /// internal class ShapeRegion : Region { + private readonly MemoryManager memoryManager; + /// /// Initializes a new instance of the class. /// /// The shape. - public ShapeRegion(IPath shape) + public ShapeRegion(MemoryManager memoryManager, IPath shape) { + this.memoryManager = memoryManager; this.Shape = shape.AsClosedPath(); int left = (int)MathF.Floor(shape.Bounds.Left); int top = (int)MathF.Floor(shape.Bounds.Top); @@ -46,7 +49,7 @@ namespace SixLabors.ImageSharp.Drawing { var start = new PointF(this.Bounds.Left - 1, y); var end = new PointF(this.Bounds.Right + 1, y); - using (var innerBuffer = MemoryManager.Current.Allocate(buffer.Length)) + using (var innerBuffer = this.memoryManager.Allocate(buffer.Length)) { PointF[] array = innerBuffer.Array; int count = this.Shape.FindIntersections(start, end, array, 0); diff --git a/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs b/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs index 2d411e0b65..201adfecc0 100644 --- a/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs @@ -84,7 +84,7 @@ namespace SixLabors.ImageSharp.Drawing.Processors maxY = Math.Min(this.Location.Y + this.Size.Height, maxY); int width = maxX - minX; - using (var amount = MemoryManager.Current.Allocate(width)) + using (var amount = this.Image.GetConfiguration().MemoryManager.Allocate(width)) { for (int i = 0; i < width; i++) { diff --git a/src/ImageSharp.Drawing/Processors/FillProcessor.cs b/src/ImageSharp.Drawing/Processors/FillProcessor.cs index f4763af1e3..0174a63880 100644 --- a/src/ImageSharp.Drawing/Processors/FillProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/FillProcessor.cs @@ -66,7 +66,7 @@ namespace SixLabors.ImageSharp.Drawing.Processors int width = maxX - minX; - using (var amount = MemoryManager.Current.Allocate(width)) + using (var amount = source.MemoryManager.Allocate(width)) using (BrushApplicator applicator = this.brush.CreateApplicator(source, sourceRectangle, this.options)) { for (int i = 0; i < width; i++) diff --git a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs index 82ffea08ea..f3e3d0397f 100644 --- a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs @@ -102,7 +102,7 @@ namespace SixLabors.ImageSharp.Drawing.Processors { float[] buffer = arrayPool.Rent(maxIntersections); int scanlineWidth = maxX - minX; - using (var scanline = MemoryManager.Current.Allocate(scanlineWidth)) + using (var scanline = source.MemoryManager.Allocate(scanlineWidth)) { try { diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index 8ea676d322..bc8ad1c529 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -83,6 +83,11 @@ namespace SixLabors.ImageSharp /// public IEnumerable ImageFormats => this.imageFormats; + /// + /// Gets or sets the that is currently in use. + /// + public MemoryManager MemoryManager { get; set; } = new ArrayPoolMemoryManager(1024 * 80); + /// /// Gets the maximum header size of all the formats. /// diff --git a/src/ImageSharp/DefaultInternalImageProcessorContext.cs b/src/ImageSharp/DefaultInternalImageProcessorContext.cs index 575525a773..22bcc82e16 100644 --- a/src/ImageSharp/DefaultInternalImageProcessorContext.cs +++ b/src/ImageSharp/DefaultInternalImageProcessorContext.cs @@ -1,7 +1,9 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Helpers; +using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.Primitives; @@ -72,5 +74,7 @@ namespace SixLabors.ImageSharp { return this.ApplyProcessor(processor, this.source.Bounds()); } + + public MemoryManager GetMemoryManager() => this.source.GetConfiguration().MemoryManager; } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 4faccc58fd..07b7fabb6c 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -263,7 +263,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp var color = default(TPixel); var rgba = new Rgba32(0, 0, 0, 255); - using (var buffer = Buffer2D.CreateClean(width, height)) + using (var buffer = this.configuration.MemoryManager.Allocate2D(width, height, true)) { this.UncompressRle8(width, buffer.Span); @@ -385,7 +385,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp padding = 4 - padding; } - using (var row = MemoryManager.Current.Allocate(arrayWidth + padding, true)) + using (var row = this.configuration.MemoryManager.Allocate(arrayWidth + padding, true)) { var color = default(TPixel); var rgba = new Rgba32(0, 0, 0, 255); @@ -435,7 +435,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp var color = default(TPixel); var rgba = new Rgba32(0, 0, 0, 255); - using (var buffer = MemoryManager.Current.Allocate(stride)) + using (var buffer = this.configuration.MemoryManager.Allocate(stride)) { for (int y = 0; y < height; y++) { diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index 0003dbc823..51a598bc0c 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -124,7 +124,7 @@ namespace SixLabors.ImageSharp.Formats.Gif if (this.logicalScreenDescriptor.GlobalColorTableFlag) { this.globalColorTableLength = this.logicalScreenDescriptor.GlobalColorTableSize * 3; - this.globalColorTable = MemoryManager.Current.Allocate(this.globalColorTableLength, true); + this.globalColorTable = this.configuration.MemoryManager.Allocate(this.globalColorTableLength, true); // Read the global color table from the stream stream.Read(this.globalColorTable.Array, 0, this.globalColorTableLength); @@ -320,11 +320,11 @@ namespace SixLabors.ImageSharp.Formats.Gif if (imageDescriptor.LocalColorTableFlag) { int length = imageDescriptor.LocalColorTableSize * 3; - localColorTable = MemoryManager.Current.Allocate(length, true); + localColorTable = this.configuration.MemoryManager.Allocate(length, true); this.currentStream.Read(localColorTable.Array, 0, length); } - indices = MemoryManager.Current.Allocate(imageDescriptor.Width * imageDescriptor.Height, true); + indices = this.configuration.MemoryManager.Allocate(imageDescriptor.Width * imageDescriptor.Height, true); this.ReadFrameIndices(imageDescriptor, indices); this.ReadFrameColors(indices, localColorTable ?? this.globalColorTable, imageDescriptor); diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegComponentPostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegComponentPostProcessor.cs index 7c91e85e74..ea9e52ae1b 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegComponentPostProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegComponentPostProcessor.cs @@ -22,11 +22,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder /// /// Initializes a new instance of the class. /// - public JpegComponentPostProcessor(JpegImagePostProcessor imagePostProcessor, IJpegComponent component) + public JpegComponentPostProcessor(MemoryManager memoryManager, JpegImagePostProcessor imagePostProcessor, IJpegComponent component) { this.Component = component; this.ImagePostProcessor = imagePostProcessor; - this.ColorBuffer = MemoryManager.Current.Allocate2D( + this.ColorBuffer = memoryManager.Allocate2D( imagePostProcessor.PostProcessorBufferSize.Width, imagePostProcessor.PostProcessorBufferSize.Height); diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs index 44deb6722b..44841bd676 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs @@ -45,15 +45,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder /// Initializes a new instance of the class. /// /// The representing the uncompressed spectral Jpeg data - public JpegImagePostProcessor(IRawJpegData rawJpeg) + public JpegImagePostProcessor(MemoryManager memoryManager, IRawJpegData rawJpeg) { this.RawJpeg = rawJpeg; IJpegComponent c0 = rawJpeg.Components.First(); this.NumberOfPostProcessorSteps = c0.SizeInBlocks.Height / BlockRowsPerStep; this.PostProcessorBufferSize = new Size(c0.SizeInBlocks.Width * 8, PixelRowsPerStep); - this.ComponentProcessors = rawJpeg.Components.Select(c => new JpegComponentPostProcessor(this, c)).ToArray(); - this.rgbaBuffer = MemoryManager.Current.Allocate(rawJpeg.ImageSizeInPixels.Width); + this.ComponentProcessors = rawJpeg.Components.Select(c => new JpegComponentPostProcessor(memoryManager, this, c)).ToArray(); + this.rgbaBuffer = memoryManager.Allocate(rawJpeg.ImageSizeInPixels.Width); this.colorConverter = ColorConverters.JpegColorConverter.GetConverter(rawJpeg.ColorSpace); } diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigComponent.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigComponent.cs index c87752b371..c2b4d632a1 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigComponent.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigComponent.cs @@ -55,7 +55,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder /// Initializes /// /// The instance - public void InitializeDerivedData(OrigJpegDecoderCore decoder) + public void InitializeDerivedData(MemoryManager memoryManager, OrigJpegDecoderCore decoder) { // For 4-component images (either CMYK or YCbCrK), we only support two // hv vectors: [0x11 0x11 0x11 0x11] and [0x22 0x11 0x11 0x22]. @@ -78,7 +78,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder this.SubSamplingDivisors = c0.SamplingFactors.DivideBy(this.SamplingFactors); } - this.SpectralBlocks = Buffer2D.CreateClean(this.SizeInBlocks); + this.SpectralBlocks = memoryManager.Allocate2D(this.SizeInBlocks.Width, this.SizeInBlocks.Height, true); } /// diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs index 61b18af551..053b016e64 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs @@ -659,7 +659,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort foreach (OrigComponent component in this.Components) { - component.InitializeDerivedData(this); + component.InitializeDerivedData(this.configuration.MemoryManager, this); } this.ColorSpace = this.DeduceJpegColorSpace(); @@ -767,7 +767,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort private Image PostProcessIntoImage() where TPixel : struct, IPixel { - using (var postProcessor = new JpegImagePostProcessor(this)) + using (var postProcessor = new JpegImagePostProcessor(this.configuration.MemoryManager, this)) { var image = new Image(this.configuration, this.ImageWidth, this.ImageHeight, this.MetaData); postProcessor.PostProcess(image.Frames.RootFrame); diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrameComponent.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrameComponent.cs index 5d51e2ad58..18e1773909 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrameComponent.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrameComponent.cs @@ -15,10 +15,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components /// internal class PdfJsFrameComponent : IDisposable, IJpegComponent { + private readonly MemoryManager memoryManager; #pragma warning disable SA1401 // Fields should be private - public PdfJsFrameComponent(PdfJsFrame frame, byte id, int horizontalFactor, int verticalFactor, byte quantizationTableIndex, int index) + public PdfJsFrameComponent(MemoryManager memoryManager, PdfJsFrame frame, byte id, int horizontalFactor, int verticalFactor, byte quantizationTableIndex, int index) { + this.memoryManager = memoryManager; this.Frame = frame; this.Id = id; this.HorizontalSamplingFactor = horizontalFactor; @@ -114,7 +116,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components int blocksBufferSize = 64 * this.BlocksPerColumnForMcu * (this.BlocksPerLineForMcu + 1); // Pooled. Disposed via frame disposal - this.BlockData = MemoryManager.Current.Allocate(blocksBufferSize, true); + this.BlockData = this.memoryManager.Allocate(blocksBufferSize, true); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs index 38f223dcac..4142ae354c 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs @@ -22,14 +22,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components /// /// The code lengths /// The huffman values - public PdfJsHuffmanTable(byte[] lengths, byte[] values) + public PdfJsHuffmanTable(MemoryManager memoryManager, byte[] lengths, byte[] values) { - this.lookahead = MemoryManager.Current.Allocate(256, true); - this.valOffset = MemoryManager.Current.Allocate(18, true); - this.maxcode = MemoryManager.Current.Allocate(18, true); + this.lookahead = memoryManager.Allocate(256, true); + this.valOffset = memoryManager.Allocate(18, true); + this.maxcode = memoryManager.Allocate(18, true); - using (var huffsize = MemoryManager.Current.Allocate(257, true)) - using (var huffcode = MemoryManager.Current.Allocate(257, true)) + using (var huffsize = memoryManager.Allocate(257, true)) + using (var huffcode = memoryManager.Allocate(257, true)) { GenerateSizeTable(lengths, huffsize); GenerateCodeTable(huffsize, huffcode); @@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components GenerateLookaheadTables(lengths, values, this.lookahead); } - this.huffval = MemoryManager.Current.Allocate(values.Length, true); + this.huffval = memoryManager.Allocate(values.Length, true); Buffer.BlockCopy(values, 0, this.huffval.Array, 0, values.Length); this.MaxCode = this.maxcode.Array; diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsJpegPixelArea.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsJpegPixelArea.cs index eb16807969..eebc57b862 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsJpegPixelArea.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsJpegPixelArea.cs @@ -14,6 +14,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components /// internal struct PdfJsJpegPixelArea : IDisposable { + private readonly MemoryManager memoryManager; + private readonly int imageWidth; private readonly int imageHeight; @@ -28,8 +30,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components /// The image width /// The image height /// The number of components - public PdfJsJpegPixelArea(int imageWidth, int imageHeight, int numberOfComponents) + public PdfJsJpegPixelArea(MemoryManager memoryManager, int imageWidth, int imageHeight, int numberOfComponents) { + this.memoryManager = memoryManager; this.imageWidth = imageWidth; this.imageHeight = imageHeight; this.Width = 0; @@ -69,11 +72,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components this.rowStride = width * numberOfComponents; var scale = new Vector2(this.imageWidth / (float)width, this.imageHeight / (float)height); - this.componentData = MemoryManager.Current.Allocate(width * height * numberOfComponents); + this.componentData = this.memoryManager.Allocate(width * height * numberOfComponents); Span componentDataSpan = this.componentData; const uint Mask3Lsb = 0xFFFFFFF8; // Used to clear the 3 LSBs - using (var xScaleBlockOffset = MemoryManager.Current.Allocate(width)) + using (var xScaleBlockOffset = this.memoryManager.Allocate(width)) { Span xScaleBlockOffsetSpan = xScaleBlockOffset; for (int i = 0; i < numberOfComponents; i++) diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsQuantizationTables.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsQuantizationTables.cs index a585c5080f..f7302b1564 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsQuantizationTables.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsQuantizationTables.cs @@ -40,6 +40,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components 63 }; + public PdfJsQuantizationTables(MemoryManager memoryManager) + { + this.Tables = memoryManager.Allocate2D(64, 4); + } + /// /// Gets or sets the quantization tables. /// @@ -49,8 +54,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components get; set; } - = MemoryManager.Current.Allocate2D(64, 4); - /// public void Dispose() { diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs index 917e3c7e3f..863c4380bf 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs @@ -216,7 +216,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort ushort marker = this.ReadUint16(); fileMarker = new PdfJsFileMarker(marker, (int)this.InputStream.Position - 2); - this.quantizationTables = new PdfJsQuantizationTables(); + this.quantizationTables = new PdfJsQuantizationTables(this.configuration.MemoryManager); this.dcHuffmanTables = new PdfJsHuffmanTables(); this.acHuffmanTables = new PdfJsHuffmanTables(); @@ -335,7 +335,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort throw new ImageFormatException($"Unsupported color mode. Max components 4; found {this.NumberOfComponents}"); } - this.pixelArea = new PdfJsJpegPixelArea(image.Width, image.Height, this.NumberOfComponents); + this.pixelArea = new PdfJsJpegPixelArea(this.configuration.MemoryManager, image.Width, image.Height, this.NumberOfComponents); this.pixelArea.LinearizeBlockData(this.components, image.Width, image.Height); if (this.NumberOfComponents == 1) @@ -648,7 +648,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort maxV = v; } - var component = new PdfJsFrameComponent(this.Frame, this.temp[index], h, v, this.temp[index + 2], i); + var component = new PdfJsFrameComponent(this.configuration.MemoryManager, this.Frame, this.temp[index], h, v, this.temp[index + 2], i); this.Frame.Components[i] = component; this.Frame.ComponentIds[i] = component.Id; @@ -673,14 +673,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort throw new ImageFormatException($"DHT has wrong length: {remaining}"); } - using (var huffmanData = MemoryManager.Current.Allocate(256, true)) + using (var huffmanData = this.configuration.MemoryManager.Allocate(256, true)) { for (int i = 2; i < remaining;) { byte huffmanTableSpec = (byte)this.InputStream.ReadByte(); this.InputStream.Read(huffmanData.Array, 0, 16); - using (var codeLengths = MemoryManager.Current.Allocate(17, true)) + using (var codeLengths = this.configuration.MemoryManager.Allocate(17, true)) { int codeLengthSum = 0; @@ -689,7 +689,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort codeLengthSum += codeLengths[j] = huffmanData[j - 1]; } - using (var huffmanValues = MemoryManager.Current.Allocate(256, true)) + using (var huffmanValues = this.configuration.MemoryManager.Allocate(256, true)) { this.InputStream.Read(huffmanValues.Array, 0, codeLengthSum); @@ -784,8 +784,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort { int blocksPerLine = component.BlocksPerLine; int blocksPerColumn = component.BlocksPerColumn; - using (var computationBuffer = MemoryManager.Current.Allocate(64, true)) - using (var multiplicationBuffer = MemoryManager.Current.Allocate(64, true)) + using (var computationBuffer = this.configuration.MemoryManager.Allocate(64, true)) + using (var multiplicationBuffer = this.configuration.MemoryManager.Allocate(64, true)) { Span quantizationTable = this.quantizationTables.Tables.GetRowSpan(frameComponent.QuantizationTableIndex); Span computationBufferSpan = computationBuffer; @@ -823,7 +823,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort /// The values private void BuildHuffmanTable(PdfJsHuffmanTables tables, int index, byte[] codeLengths, byte[] values) { - tables[index] = new PdfJsHuffmanTable(codeLengths, values); + tables[index] = new PdfJsHuffmanTable(this.configuration.MemoryManager, codeLengths, values); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/Formats/Png/PngConfigurationModule.cs b/src/ImageSharp/Formats/Png/PngConfigurationModule.cs index 9346f7567e..abf5bc6bb9 100644 --- a/src/ImageSharp/Formats/Png/PngConfigurationModule.cs +++ b/src/ImageSharp/Formats/Png/PngConfigurationModule.cs @@ -9,11 +9,11 @@ namespace SixLabors.ImageSharp.Formats.Png public sealed class PngConfigurationModule : IConfigurationModule { /// - public void Configure(Configuration host) + public void Configure(Configuration config) { - host.SetEncoder(ImageFormats.Png, new PngEncoder()); - host.SetDecoder(ImageFormats.Png, new PngDecoder()); - host.AddImageFormatDetector(new PngImageFormatDetector()); + config.SetEncoder(ImageFormats.Png, new PngEncoder(config.MemoryManager)); + config.SetDecoder(ImageFormats.Png, new PngDecoder()); + config.AddImageFormatDetector(new PngImageFormatDetector()); } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index ebda05a1e9..7e354b58bc 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -375,8 +375,8 @@ namespace SixLabors.ImageSharp.Formats.Png this.bytesPerSample = this.header.BitDepth / 8; } - this.previousScanline = MemoryManager.Current.Allocate(this.bytesPerScanline, true); - this.scanline = MemoryManager.Current.Allocate(this.bytesPerScanline, true); + this.previousScanline = this.configuration.MemoryManager.Allocate(this.bytesPerScanline, true); + this.scanline = this.configuration.MemoryManager.Allocate(this.bytesPerScanline, true); } /// @@ -669,7 +669,7 @@ namespace SixLabors.ImageSharp.Formats.Png if (this.header.BitDepth == 16) { int length = this.header.Width * 3; - using (var compressed = MemoryManager.Current.Allocate(length)) + using (var compressed = this.configuration.MemoryManager.Allocate(length)) { // TODO: Should we use pack from vector here instead? this.From16BitTo8Bit(scanlineBuffer, compressed, length); @@ -686,7 +686,7 @@ namespace SixLabors.ImageSharp.Formats.Png if (this.header.BitDepth == 16) { int length = this.header.Width * 3; - using (var compressed = MemoryManager.Current.Allocate(length)) + using (var compressed = this.configuration.MemoryManager.Allocate(length)) { // TODO: Should we use pack from vector here instead? this.From16BitTo8Bit(scanlineBuffer, compressed, length); @@ -727,7 +727,7 @@ namespace SixLabors.ImageSharp.Formats.Png if (this.header.BitDepth == 16) { int length = this.header.Width * 4; - using (var compressed = MemoryManager.Current.Allocate(length)) + using (var compressed = this.configuration.MemoryManager.Allocate(length)) { // TODO: Should we use pack from vector here instead? this.From16BitTo8Bit(scanlineBuffer, compressed, length); @@ -930,7 +930,7 @@ namespace SixLabors.ImageSharp.Formats.Png if (this.header.BitDepth == 16) { int length = this.header.Width * 3; - using (var compressed = MemoryManager.Current.Allocate(length)) + using (var compressed = this.configuration.MemoryManager.Allocate(length)) { // TODO: Should we use pack from vector here instead? this.From16BitTo8Bit(scanlineBuffer, compressed, length); @@ -998,7 +998,7 @@ namespace SixLabors.ImageSharp.Formats.Png if (this.header.BitDepth == 16) { int length = this.header.Width * 4; - using (var compressed = MemoryManager.Current.Allocate(length)) + using (var compressed = this.configuration.MemoryManager.Allocate(length)) { // TODO: Should we use pack from vector here instead? this.From16BitTo8Bit(scanlineBuffer, compressed, length); diff --git a/src/ImageSharp/Formats/Png/PngEncoder.cs b/src/ImageSharp/Formats/Png/PngEncoder.cs index 2fc6911f0b..f65ce59b2f 100644 --- a/src/ImageSharp/Formats/Png/PngEncoder.cs +++ b/src/ImageSharp/Formats/Png/PngEncoder.cs @@ -2,6 +2,8 @@ // Licensed under the Apache License, Version 2.0. using System.IO; + +using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Quantizers; @@ -12,6 +14,13 @@ namespace SixLabors.ImageSharp.Formats.Png /// public sealed class PngEncoder : IImageEncoder, IPngEncoderOptions { + private readonly MemoryManager memoryManager; + + public PngEncoder(MemoryManager memoryManager) + { + this.memoryManager = memoryManager; + } + /// /// Gets or sets a value indicating whether the metadata should be ignored when the image is being encoded. /// @@ -66,7 +75,7 @@ namespace SixLabors.ImageSharp.Formats.Png public void Encode(Image image, Stream stream) where TPixel : struct, IPixel { - using (var encoder = new PngEncoderCore(this)) + using (var encoder = new PngEncoderCore(this.memoryManager, this)) { encoder.Encode(image, stream); } diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 9be0f5ee44..e3e209ed43 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -20,6 +20,8 @@ namespace SixLabors.ImageSharp.Formats.Png /// internal sealed class PngEncoderCore : IDisposable { + private readonly MemoryManager memoryManager; + /// /// The maximum block size, defaults at 64k for uncompressed blocks. /// @@ -149,8 +151,9 @@ namespace SixLabors.ImageSharp.Formats.Png /// Initializes a new instance of the class. /// /// The options for influancing the encoder - public PngEncoderCore(IPngEncoderOptions options) + public PngEncoderCore(MemoryManager memoryManager, IPngEncoderOptions options) { + this.memoryManager = memoryManager; this.ignoreMetadata = options.IgnoreMetadata; this.paletteSize = options.PaletteSize > 0 ? options.PaletteSize.Clamp(1, int.MaxValue) : int.MaxValue; this.pngColorType = options.PngColorType; @@ -620,16 +623,16 @@ namespace SixLabors.ImageSharp.Formats.Png this.bytesPerScanline = this.width * this.bytesPerPixel; int resultLength = this.bytesPerScanline + 1; - this.previousScanline = MemoryManager.Current.Allocate(this.bytesPerScanline, true); - this.rawScanline = MemoryManager.Current.Allocate(this.bytesPerScanline, true); - this.result = MemoryManager.Current.Allocate(resultLength, true); + this.previousScanline = this.memoryManager.Allocate(this.bytesPerScanline, true); + this.rawScanline = this.memoryManager.Allocate(this.bytesPerScanline, true); + this.result = this.memoryManager.Allocate(resultLength, true); if (this.pngColorType != PngColorType.Palette) { - this.sub = MemoryManager.Current.Allocate(resultLength, true); - this.up = MemoryManager.Current.Allocate(resultLength, true); - this.average = MemoryManager.Current.Allocate(resultLength, true); - this.paeth = MemoryManager.Current.Allocate(resultLength, true); + this.sub = this.memoryManager.Allocate(resultLength, true); + this.up = this.memoryManager.Allocate(resultLength, true); + this.average = this.memoryManager.Allocate(resultLength, true); + this.paeth = this.memoryManager.Allocate(resultLength, true); } byte[] buffer; diff --git a/src/ImageSharp/IImageProcessingContext{TPixel}.cs b/src/ImageSharp/IImageProcessingContext{TPixel}.cs index 552e8d579d..1556987df6 100644 --- a/src/ImageSharp/IImageProcessingContext{TPixel}.cs +++ b/src/ImageSharp/IImageProcessingContext{TPixel}.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.Primitives; @@ -28,6 +29,8 @@ namespace SixLabors.ImageSharp /// The processor to apply /// The current operations class to allow chaining of operations. IImageProcessingContext ApplyProcessor(IImageProcessor processor); + + MemoryManager GetMemoryManager(); } /// diff --git a/src/ImageSharp/Image/Image.Decode.cs b/src/ImageSharp/Image/Image.Decode.cs index 1bce23cfcb..69063a8de1 100644 --- a/src/ImageSharp/Image/Image.Decode.cs +++ b/src/ImageSharp/Image/Image.Decode.cs @@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp return null; } - using (var buffer = MemoryManager.Current.Allocate(maxHeaderSize)) + using (var buffer = config.MemoryManager.Allocate(maxHeaderSize)) { long startPosition = stream.Position; stream.Read(buffer.Array, 0, maxHeaderSize); diff --git a/src/ImageSharp/Image/ImageFrame.LoadPixelData.cs b/src/ImageSharp/Image/ImageFrame.LoadPixelData.cs index e2230c4367..153a757e18 100644 --- a/src/ImageSharp/Image/ImageFrame.LoadPixelData.cs +++ b/src/ImageSharp/Image/ImageFrame.LoadPixelData.cs @@ -43,7 +43,7 @@ namespace SixLabors.ImageSharp int count = width * height; Guard.MustBeGreaterThanOrEqualTo(data.Length, count, nameof(data)); - var image = new ImageFrame(width, height); + var image = new ImageFrame(Configuration.Default.MemoryManager, width, height); SpanHelper.Copy(data, image.GetPixelSpan(), count); return image; diff --git a/src/ImageSharp/Image/ImageFrameCollection.cs b/src/ImageSharp/Image/ImageFrameCollection.cs index 3e9bb03435..bfdf1df76b 100644 --- a/src/ImageSharp/Image/ImageFrameCollection.cs +++ b/src/ImageSharp/Image/ImageFrameCollection.cs @@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp this.parent = parent; // Frames are already cloned within the caller - this.frames.Add(new ImageFrame(width, height)); + this.frames.Add(new ImageFrame(parent.GetConfiguration().MemoryManager, width, height)); } internal ImageFrameCollection(Image parent, IEnumerable> frames) @@ -143,7 +143,7 @@ namespace SixLabors.ImageSharp /// public ImageFrame CreateFrame() { - var frame = new ImageFrame(this.RootFrame.Width, this.RootFrame.Height); + var frame = new ImageFrame(this.parent.GetConfiguration().MemoryManager, this.RootFrame.Width, this.RootFrame.Height); this.frames.Add(frame); return frame; } diff --git a/src/ImageSharp/Image/ImageFrame{TPixel}.cs b/src/ImageSharp/Image/ImageFrame{TPixel}.cs index a0e4976cb6..2f2f545dbb 100644 --- a/src/ImageSharp/Image/ImageFrame{TPixel}.cs +++ b/src/ImageSharp/Image/ImageFrame{TPixel}.cs @@ -20,6 +20,8 @@ namespace SixLabors.ImageSharp public sealed class ImageFrame : IPixelSource, IDisposable where TPixel : struct, IPixel { + public MemoryManager MemoryManager { get; } + /// /// The image pixels. Not private as Buffer2D requires an array in its constructor. /// @@ -32,8 +34,8 @@ namespace SixLabors.ImageSharp /// /// The width of the image in pixels. /// The height of the image in pixels. - internal ImageFrame(int width, int height) - : this(width, height, new ImageFrameMetaData()) + internal ImageFrame(MemoryManager memoryManager, int width, int height) + : this(memoryManager, width, height, new ImageFrameMetaData()) { } @@ -43,13 +45,15 @@ namespace SixLabors.ImageSharp /// The width of the image in pixels. /// The height of the image in pixels. /// The meta data. - internal ImageFrame(int width, int height, ImageFrameMetaData metaData) + internal ImageFrame(MemoryManager memoryManager, int width, int height, ImageFrameMetaData metaData) { + Guard.NotNull(memoryManager, nameof(memoryManager)); Guard.MustBeGreaterThan(width, 0, nameof(width)); Guard.MustBeGreaterThan(height, 0, nameof(height)); Guard.NotNull(metaData, nameof(metaData)); - this.pixelBuffer = Buffer2D.CreateClean(width, height); + this.MemoryManager = memoryManager; + this.pixelBuffer = memoryManager.Allocate2D(width, height, true); this.MetaData = metaData; } @@ -57,9 +61,10 @@ namespace SixLabors.ImageSharp /// Initializes a new instance of the class. /// /// The source. - internal ImageFrame(ImageFrame source) + internal ImageFrame(MemoryManager memoryManager, ImageFrame source) { - this.pixelBuffer = MemoryManager.Current.Allocate2D(source.pixelBuffer.Width, source.pixelBuffer.Height); + this.MemoryManager = memoryManager; + this.pixelBuffer = memoryManager.Allocate2D(source.pixelBuffer.Width, source.pixelBuffer.Height); source.pixelBuffer.Span.CopyTo(this.pixelBuffer.Span); this.MetaData = source.MetaData.Clone(); } @@ -198,7 +203,7 @@ namespace SixLabors.ImageSharp Func scaleFunc = PackedPixelConverterHelper.ComputeScaleFunction(); - var target = new ImageFrame(this.Width, this.Height, this.MetaData.Clone()); + var target = new ImageFrame(this.MemoryManager, this.Width, this.Height, this.MetaData.Clone()); using (PixelAccessor pixels = this.Lock()) using (PixelAccessor targetPixels = target.Lock()) @@ -227,7 +232,7 @@ namespace SixLabors.ImageSharp /// The internal ImageFrame Clone() { - return new ImageFrame(this); + return new ImageFrame(this.MemoryManager, this); } /// diff --git a/src/ImageSharp/Image/PixelAccessor{TPixel}.cs b/src/ImageSharp/Image/PixelAccessor{TPixel}.cs index 70d67954cc..fca57b3c8e 100644 --- a/src/ImageSharp/Image/PixelAccessor{TPixel}.cs +++ b/src/ImageSharp/Image/PixelAccessor{TPixel}.cs @@ -55,7 +55,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), true) + : this(width, height, Configuration.Default.MemoryManager.Allocate2D(width, height, true), true) { } diff --git a/src/ImageSharp/Image/PixelArea{TPixel}.cs b/src/ImageSharp/Image/PixelArea{TPixel}.cs index e6ae556f7d..fa3499b6d7 100644 --- a/src/ImageSharp/Image/PixelArea{TPixel}.cs +++ b/src/ImageSharp/Image/PixelArea{TPixel}.cs @@ -116,7 +116,7 @@ namespace SixLabors.ImageSharp this.RowStride = (width * GetComponentCount(componentOrder)) + padding; this.Length = this.RowStride * height; - this.byteBuffer = MemoryManager.Current.Allocate(this.Length, true); + this.byteBuffer = Configuration.Default.MemoryManager.Allocate(this.Length, true); } /// diff --git a/src/ImageSharp/Memory/Buffer2D{T}.cs b/src/ImageSharp/Memory/Buffer2D{T}.cs index 73444d4978..82cb25f476 100644 --- a/src/ImageSharp/Memory/Buffer2D{T}.cs +++ b/src/ImageSharp/Memory/Buffer2D{T}.cs @@ -56,24 +56,6 @@ namespace SixLabors.ImageSharp.Memory } } - /// - /// Creates a clean instance of initializing it's elements with 'default(T)'. - /// - /// The number of elements in a row - /// The number of rows - /// The instance - public static Buffer2D CreateClean(int width, int height) - { - return new Buffer2D(MemoryManager.Current.Allocate(width * height, true), width, height); - } - - /// - /// Creates a clean instance of initializing it's elements with 'default(T)'. - /// - /// The size of the buffer - /// The instance - public static Buffer2D CreateClean(Size size) => CreateClean(size.Width, size.Height); - public void Dispose() { this.Buffer?.Dispose(); diff --git a/src/ImageSharp/Memory/MemoryManager.cs b/src/ImageSharp/Memory/MemoryManager.cs index b68a01feb1..67d72d2b43 100644 --- a/src/ImageSharp/Memory/MemoryManager.cs +++ b/src/ImageSharp/Memory/MemoryManager.cs @@ -11,11 +11,6 @@ namespace SixLabors.ImageSharp.Memory /// public abstract class MemoryManager { - /// - /// Gets or sets the that is currently in use. - /// - public static MemoryManager Current { get; set; } = new ArrayPoolMemoryManager(1024 * 80); - /// /// Allocates a of size , optionally /// clearing the buffer before it gets returned. diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs index 4772816730..4ca53244ad 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs @@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) + using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -84,7 +84,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) + using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -123,7 +123,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) + using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -162,7 +162,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) + using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -201,7 +201,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) + using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -240,7 +240,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) + using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -279,7 +279,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) + using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -318,7 +318,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) + using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -357,7 +357,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) + using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -396,7 +396,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) + using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -435,7 +435,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) + using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -474,7 +474,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) + using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -513,7 +513,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) + using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -552,7 +552,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) + using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -591,7 +591,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) + using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -630,7 +630,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) + using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -669,7 +669,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) + using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -708,7 +708,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) + using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -747,7 +747,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) + using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -786,7 +786,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) + using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -825,7 +825,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) + using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt index cb99237c45..5e9268dff1 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt @@ -87,7 +87,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) + using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); diff --git a/src/ImageSharp/Processing/ColorMatrix/Lomograph.cs b/src/ImageSharp/Processing/ColorMatrix/Lomograph.cs index 947e531578..96231e168b 100644 --- a/src/ImageSharp/Processing/ColorMatrix/Lomograph.cs +++ b/src/ImageSharp/Processing/ColorMatrix/Lomograph.cs @@ -51,7 +51,7 @@ namespace SixLabors.ImageSharp public static IImageProcessingContext Lomograph(this IImageProcessingContext source, GraphicsOptions options) where TPixel : struct, IPixel { - source.ApplyProcessor(new LomographProcessor(options)); + source.ApplyProcessor(new LomographProcessor(source.GetMemoryManager(), options)); return source; } @@ -68,7 +68,7 @@ namespace SixLabors.ImageSharp public static IImageProcessingContext Lomograph(this IImageProcessingContext source, Rectangle rectangle, GraphicsOptions options) where TPixel : struct, IPixel { - source.ApplyProcessor(new LomographProcessor(options), rectangle); + source.ApplyProcessor(new LomographProcessor(source.GetMemoryManager(), options), rectangle); return source; } } diff --git a/src/ImageSharp/Processing/ColorMatrix/Polaroid.cs b/src/ImageSharp/Processing/ColorMatrix/Polaroid.cs index c96087d57e..7cc0e2419f 100644 --- a/src/ImageSharp/Processing/ColorMatrix/Polaroid.cs +++ b/src/ImageSharp/Processing/ColorMatrix/Polaroid.cs @@ -51,7 +51,7 @@ namespace SixLabors.ImageSharp public static IImageProcessingContext Polaroid(this IImageProcessingContext source, GraphicsOptions options) where TPixel : struct, IPixel { - source.ApplyProcessor(new PolaroidProcessor(options)); + source.ApplyProcessor(new PolaroidProcessor(source.GetMemoryManager(), options)); return source; } @@ -68,7 +68,7 @@ namespace SixLabors.ImageSharp public static IImageProcessingContext Polaroid(this IImageProcessingContext source, Rectangle rectangle, GraphicsOptions options) where TPixel : struct, IPixel { - source.ApplyProcessor(new PolaroidProcessor(options), rectangle); + source.ApplyProcessor(new PolaroidProcessor(source.GetMemoryManager(), options), rectangle); return source; } } diff --git a/src/ImageSharp/Processing/Effects/BackgroundColor.cs b/src/ImageSharp/Processing/Effects/BackgroundColor.cs index da00b4ddd0..18caa67cca 100644 --- a/src/ImageSharp/Processing/Effects/BackgroundColor.cs +++ b/src/ImageSharp/Processing/Effects/BackgroundColor.cs @@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp /// The . public static IImageProcessingContext BackgroundColor(this IImageProcessingContext source, TPixel color, GraphicsOptions options) where TPixel : struct, IPixel - => source.ApplyProcessor(new BackgroundColorProcessor(color, options)); + => source.ApplyProcessor(new BackgroundColorProcessor(source.GetMemoryManager(), color, options)); /// /// Replaces the background color of image with the given one. @@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp /// The . public static IImageProcessingContext BackgroundColor(this IImageProcessingContext source, TPixel color, Rectangle rectangle, GraphicsOptions options) where TPixel : struct, IPixel - => source.ApplyProcessor(new BackgroundColorProcessor(color, options), rectangle); + => source.ApplyProcessor(new BackgroundColorProcessor(source.GetMemoryManager(), color, options), rectangle); /// /// Replaces the background color of image with the given one. diff --git a/src/ImageSharp/Processing/Overlays/Glow.cs b/src/ImageSharp/Processing/Overlays/Glow.cs index ee35963487..a8994b18a1 100644 --- a/src/ImageSharp/Processing/Overlays/Glow.cs +++ b/src/ImageSharp/Processing/Overlays/Glow.cs @@ -157,7 +157,7 @@ namespace SixLabors.ImageSharp /// The . private static IImageProcessingContext Glow(this IImageProcessingContext source, TPixel color, ValueSize radius, Rectangle rectangle, GraphicsOptions options) where TPixel : struct, IPixel - => source.ApplyProcessor(new GlowProcessor(color, radius, options), rectangle); + => source.ApplyProcessor(new GlowProcessor(source.GetMemoryManager(), color, radius, options), rectangle); /// /// Applies a radial glow effect to an image. @@ -170,6 +170,6 @@ namespace SixLabors.ImageSharp /// The . private static IImageProcessingContext Glow(this IImageProcessingContext source, TPixel color, ValueSize radius, GraphicsOptions options) where TPixel : struct, IPixel - => source.ApplyProcessor(new GlowProcessor(color, radius, options)); + => source.ApplyProcessor(new GlowProcessor(source.GetMemoryManager(), color, radius, options)); } } diff --git a/src/ImageSharp/Processing/Overlays/Vignette.cs b/src/ImageSharp/Processing/Overlays/Vignette.cs index cc93ccedc6..3a691ed00d 100644 --- a/src/ImageSharp/Processing/Overlays/Vignette.cs +++ b/src/ImageSharp/Processing/Overlays/Vignette.cs @@ -151,10 +151,10 @@ namespace SixLabors.ImageSharp private static IImageProcessingContext VignetteInternal(this IImageProcessingContext source, TPixel color, ValueSize radiusX, ValueSize radiusY, Rectangle rectangle, GraphicsOptions options) where TPixel : struct, IPixel - => source.ApplyProcessor(new VignetteProcessor(color, radiusX, radiusY, options), rectangle); + => source.ApplyProcessor(new VignetteProcessor(source.GetMemoryManager(), color, radiusX, radiusY, options), rectangle); private static IImageProcessingContext VignetteInternal(this IImageProcessingContext source, TPixel color, ValueSize radiusX, ValueSize radiusY, GraphicsOptions options) where TPixel : struct, IPixel - => source.ApplyProcessor(new VignetteProcessor(color, radiusX, radiusY, options)); + => source.ApplyProcessor(new VignetteProcessor(source.GetMemoryManager(), color, radiusX, radiusY, options)); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/LomographProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/LomographProcessor.cs index 1ec76bf554..a95d5fef65 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/LomographProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/LomographProcessor.cs @@ -2,6 +2,8 @@ // Licensed under the Apache License, Version 2.0. using System.Numerics; + +using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -15,14 +17,17 @@ namespace SixLabors.ImageSharp.Processing.Processors where TPixel : struct, IPixel { private static readonly TPixel VeryDarkGreen = ColorBuilder.FromRGBA(0, 10, 0, 255); + + private readonly MemoryManager memoryManager; + private readonly GraphicsOptions options; /// /// Initializes a new instance of the class. /// /// The options effecting blending and composition. - public LomographProcessor(GraphicsOptions options) - { + public LomographProcessor(MemoryManager memoryManager, GraphicsOptions options) { + this.memoryManager = memoryManager; this.options = options; } @@ -41,7 +46,7 @@ namespace SixLabors.ImageSharp.Processing.Processors /// protected override void AfterApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - new VignetteProcessor(VeryDarkGreen, this.options).Apply(source, sourceRectangle, configuration); + new VignetteProcessor(this.memoryManager, 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 f910562e64..76a616a1b0 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/PolaroidProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/PolaroidProcessor.cs @@ -2,6 +2,8 @@ // Licensed under the Apache License, Version 2.0. using System.Numerics; + +using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -16,14 +18,17 @@ namespace SixLabors.ImageSharp.Processing.Processors { private static readonly TPixel VeryDarkOrange = ColorBuilder.FromRGB(102, 34, 0); private static readonly TPixel LightOrange = ColorBuilder.FromRGBA(255, 153, 102, 178); + + private readonly MemoryManager memoryManager; + private readonly GraphicsOptions options; /// /// Initializes a new instance of the class. /// /// The options effecting blending and composition. - public PolaroidProcessor(GraphicsOptions options) - { + public PolaroidProcessor(MemoryManager memoryManager, GraphicsOptions options) { + this.memoryManager = memoryManager; this.options = options; } @@ -48,8 +53,8 @@ namespace SixLabors.ImageSharp.Processing.Processors /// protected override void AfterApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - new VignetteProcessor(VeryDarkOrange, this.options).Apply(source, sourceRectangle, configuration); - new GlowProcessor(LightOrange, source.Width / 4F, this.options).Apply(source, sourceRectangle, configuration); + new VignetteProcessor(this.memoryManager, VeryDarkOrange, this.options).Apply(source, sourceRectangle, configuration); + new GlowProcessor(this.memoryManager, LightOrange, source.Width / 4F, this.options).Apply(source, sourceRectangle, configuration); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs index 5f6fd40238..b81fe3cf31 100644 --- a/src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs @@ -17,6 +17,8 @@ namespace SixLabors.ImageSharp.Processing.Processors internal class BackgroundColorProcessor : ImageProcessor where TPixel : struct, IPixel { + private readonly MemoryManager memoryManager; + private readonly GraphicsOptions options; /// @@ -24,9 +26,10 @@ namespace SixLabors.ImageSharp.Processing.Processors /// /// The to set the background color to. /// The options defining blending algorithum and amount. - public BackgroundColorProcessor(TPixel color, GraphicsOptions options) + public BackgroundColorProcessor(MemoryManager memoryManager, TPixel color, GraphicsOptions options) { this.Value = color; + this.memoryManager = memoryManager; this.options = options; } @@ -67,8 +70,8 @@ namespace SixLabors.ImageSharp.Processing.Processors int width = maxX - minX; - using (var colors = MemoryManager.Current.Allocate(width)) - using (var amount = MemoryManager.Current.Allocate(width)) + using (var colors = this.memoryManager.Allocate(width)) + using (var amount = this.memoryManager.Allocate(width)) { for (int i = 0; i < width; i++) { diff --git a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs index 0cee4c0b2d..521da20a76 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs @@ -19,6 +19,8 @@ namespace SixLabors.ImageSharp.Processing.Processors internal class GlowProcessor : ImageProcessor where TPixel : struct, IPixel { + private readonly MemoryManager memoryManager; + private readonly GraphicsOptions options; private readonly PixelBlender blender; @@ -28,8 +30,9 @@ namespace SixLabors.ImageSharp.Processing.Processors /// The color or the glow. /// The radius of the glow. /// The options effecting blending and composition. - public GlowProcessor(TPixel color, ValueSize radius, GraphicsOptions options) + public GlowProcessor(MemoryManager memoryManager, TPixel color, ValueSize radius, GraphicsOptions options) { + this.memoryManager = memoryManager; this.options = options; this.GlowColor = color; this.Radius = radius; @@ -83,7 +86,7 @@ namespace SixLabors.ImageSharp.Processing.Processors } int width = maxX - minX; - using (var rowColors = MemoryManager.Current.Allocate(width)) + using (var rowColors = this.memoryManager.Allocate(width)) { for (int i = 0; i < width; i++) { @@ -96,7 +99,7 @@ namespace SixLabors.ImageSharp.Processing.Processors configuration.ParallelOptions, y => { - using (var amounts = MemoryManager.Current.Allocate(width)) + using (var amounts = this.memoryManager.Allocate(width)) { int offsetY = y - startY; int offsetX = minX - startX; diff --git a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs index 60915e6db2..dc60ffec91 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs @@ -19,6 +19,8 @@ namespace SixLabors.ImageSharp.Processing.Processors internal class VignetteProcessor : ImageProcessor where TPixel : struct, IPixel { + private readonly MemoryManager memoryManager; + private readonly GraphicsOptions options; private readonly PixelBlender blender; @@ -29,11 +31,12 @@ namespace SixLabors.ImageSharp.Processing.Processors /// The x-radius. /// The y-radius. /// The options effecting blending and composition. - public VignetteProcessor(TPixel color, ValueSize radiusX, ValueSize radiusY, GraphicsOptions options) + public VignetteProcessor(MemoryManager memoryManager, TPixel color, ValueSize radiusX, ValueSize radiusY, GraphicsOptions options) { this.VignetteColor = color; this.RadiusX = radiusX; this.RadiusY = radiusY; + this.memoryManager = memoryManager; this.options = options; this.blender = PixelOperations.Instance.GetPixelBlender(this.options.BlenderMode); } @@ -43,9 +46,10 @@ namespace SixLabors.ImageSharp.Processing.Processors /// /// The color of the vignette. /// The options effecting blending and composition. - public VignetteProcessor(TPixel color, GraphicsOptions options) + public VignetteProcessor(MemoryManager memoryManager, TPixel color, GraphicsOptions options) { this.VignetteColor = color; + this.memoryManager = memoryManager; this.options = options; this.blender = PixelOperations.Instance.GetPixelBlender(this.options.BlenderMode); } @@ -104,7 +108,7 @@ namespace SixLabors.ImageSharp.Processing.Processors } int width = maxX - minX; - using (var rowColors = MemoryManager.Current.Allocate(width)) + using (var rowColors = this.memoryManager.Allocate(width)) { for (int i = 0; i < width; i++) { @@ -117,7 +121,7 @@ namespace SixLabors.ImageSharp.Processing.Processors configuration.ParallelOptions, y => { - using (var amounts = MemoryManager.Current.Allocate(width)) + using (var amounts = this.memoryManager.Allocate(width)) { int offsetY = y - startY; int offsetX = minX - startX; diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs index 9d76bf60f1..c86fe89b70 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs @@ -164,9 +164,9 @@ namespace SixLabors.ImageSharp.Processing.Processors /// /// The size of the source window /// The size of the destination window - public WeightsBuffer(int sourceSize, int destinationSize) + public WeightsBuffer(MemoryManager memoryManager, int sourceSize, int destinationSize) { - this.dataBuffer = Buffer2D.CreateClean(sourceSize, destinationSize); + this.dataBuffer = memoryManager.Allocate2D(sourceSize, destinationSize, true); this.Weights = new WeightsWindow[destinationSize]; } diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs index 781f3a01c7..6b608d1021 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs @@ -3,6 +3,8 @@ using System; using System.Runtime.CompilerServices; + +using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -25,18 +27,20 @@ namespace SixLabors.ImageSharp.Processing.Processors /// /// The structure that specifies the portion of the target image object to draw to. /// - protected ResamplingWeightedProcessor(IResampler sampler, int width, int height, Rectangle resizeRectangle) + protected ResamplingWeightedProcessor(MemoryManager memoryManager, IResampler sampler, int width, int height, Rectangle resizeRectangle) { + Guard.NotNull(memoryManager, nameof(memoryManager)); Guard.NotNull(sampler, nameof(sampler)); Guard.MustBeGreaterThan(width, 0, nameof(width)); Guard.MustBeGreaterThan(height, 0, nameof(height)); + this.MemoryManager = memoryManager; this.Sampler = sampler; this.Width = width; this.Height = height; this.ResizeRectangle = resizeRectangle; } - + /// /// Gets the sampler to perform the resize operation. /// @@ -56,6 +60,8 @@ namespace SixLabors.ImageSharp.Processing.Processors /// Gets or sets the resize rectangle. /// public Rectangle ResizeRectangle { get; protected set; } + + protected MemoryManager MemoryManager { get; } /// /// Gets or sets the horizontal weights. @@ -86,7 +92,7 @@ namespace SixLabors.ImageSharp.Processing.Processors IResampler sampler = this.Sampler; float radius = MathF.Ceiling(scale * sampler.Radius); - var result = new WeightsBuffer(sourceSize, destinationSize); + var result = new WeightsBuffer(this.MemoryManager, sourceSize, destinationSize); for (int i = 0; i < destinationSize; i++) { diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs index dbc05876dd..6c4ea7e67d 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs @@ -26,8 +26,8 @@ namespace SixLabors.ImageSharp.Processing.Processors /// The sampler to perform the resize operation. /// The target width. /// The target height. - public ResizeProcessor(IResampler sampler, int width, int height) - : base(sampler, width, height, new Rectangle(0, 0, width, height)) + public ResizeProcessor(MemoryManager memoryManager, IResampler sampler, int width, int height) + : base(memoryManager, sampler, width, height, new Rectangle(0, 0, width, height)) { } @@ -40,8 +40,8 @@ namespace SixLabors.ImageSharp.Processing.Processors /// /// The structure that specifies the portion of the target image object to draw to. /// - public ResizeProcessor(IResampler sampler, int width, int height, Rectangle resizeRectangle) - : base(sampler, width, height, resizeRectangle) + public ResizeProcessor(MemoryManager memoryManager, IResampler sampler, int width, int height, Rectangle resizeRectangle) + : base(memoryManager, sampler, width, height, resizeRectangle) { } @@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.Processing.Processors // ------------ // 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 process them in turn. - IEnumerable> frames = source.Frames.Select(x => new ImageFrame(this.Width, this.Height, x.MetaData.Clone())); // this will create places holders + IEnumerable> frames = source.Frames.Select(x => new ImageFrame(source.GetConfiguration().MemoryManager, 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 prevent a extra frame being added return image; @@ -121,7 +121,7 @@ namespace SixLabors.ImageSharp.Processing.Processors // First process the columns. Since we are not using multiple threads startY and endY // are the upper and lower bounds of the source rectangle. // TODO: Using a transposed variant of 'firstPassPixels' could eliminate the need for the WeightsWindow.ComputeWeightedColumnSum() method, and improve speed! - using (var firstPassPixels = MemoryManager.Current.Allocate2D(width, source.Height)) + using (var firstPassPixels = this.MemoryManager.Allocate2D(width, source.Height)) { firstPassPixels.Buffer.Clear(); @@ -132,7 +132,7 @@ namespace SixLabors.ImageSharp.Processing.Processors y => { // TODO: Without Parallel.For() this buffer object could be reused: - using (var tempRowBuffer = MemoryManager.Current.Allocate(source.Width)) + using (var tempRowBuffer = this.MemoryManager.Allocate(source.Width)) { Span firstPassRow = firstPassPixels.GetRowSpan(y); Span sourceRow = source.GetPixelRowSpan(y); diff --git a/src/ImageSharp/Processing/Transforms/Resize.cs b/src/ImageSharp/Processing/Transforms/Resize.cs index 3c7cbca311..c54267a9f5 100644 --- a/src/ImageSharp/Processing/Transforms/Resize.cs +++ b/src/ImageSharp/Processing/Transforms/Resize.cs @@ -193,7 +193,7 @@ namespace SixLabors.ImageSharp Guard.MustBeGreaterThan(width, 0, nameof(width)); Guard.MustBeGreaterThan(height, 0, nameof(height)); - img.Mutate(x => x.ApplyProcessor(new ResizeProcessor(sampler, width, height, targetRectangle) { Compand = compand }, sourceRectangle)); + img.Mutate(x => x.ApplyProcessor(new ResizeProcessor(source.GetMemoryManager(), sampler, width, height, targetRectangle) { Compand = compand }, sourceRectangle)); }); } @@ -233,7 +233,7 @@ namespace SixLabors.ImageSharp Guard.MustBeGreaterThan(width, 0, nameof(width)); Guard.MustBeGreaterThan(height, 0, nameof(height)); - img.Mutate(x => x.ApplyProcessor(new ResizeProcessor(sampler, width, height, targetRectangle) { Compand = compand })); + img.Mutate(x => x.ApplyProcessor(new ResizeProcessor(source.GetMemoryManager(), sampler, width, height, targetRectangle) { Compand = compand })); }); } } diff --git a/tests/ImageSharp.Tests/FakeImageOperationsProvider.cs b/tests/ImageSharp.Tests/FakeImageOperationsProvider.cs index f750bfcfad..a1c199b161 100644 --- a/tests/ImageSharp.Tests/FakeImageOperationsProvider.cs +++ b/tests/ImageSharp.Tests/FakeImageOperationsProvider.cs @@ -5,6 +5,9 @@ using System; using System.Collections.Generic; using System.Linq; using System.Text; + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.Primitives; @@ -84,6 +87,9 @@ namespace SixLabors.ImageSharp.Tests }); return this; } + + public MemoryManager GetMemoryManager() => this.source.GetConfiguration().MemoryManager; + public struct AppliedOpperation { public Rectangle? Rectangle { get; set; } From a8b26a41eca7cfbb29a199c9cd639084943c1b93 Mon Sep 17 00:00:00 2001 From: Lauri Kotilainen Date: Fri, 12 Jan 2018 14:28:43 +0200 Subject: [PATCH 009/151] - Use Configuration.Default.MemoryManager in tests --- .../Color/Bulk/PackFromVector4.cs | 4 +- .../Bulk/PackFromVector4ReferenceVsPointer.cs | 4 +- .../Color/Bulk/PackFromXyzw.cs | 4 +- .../Color/Bulk/ToVector4.cs | 4 +- .../ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs | 4 +- .../Color/Bulk/ToXyzw.cs | 4 +- .../General/ClearBuffer.cs | 2 +- .../General/IterateArray.cs | 2 +- .../General/PixelIndexing.cs | 2 +- .../Image/EncodeIndexedPng.cs | 10 ++--- .../ImageSharp.Benchmarks/Image/EncodePng.cs | 2 +- .../Image/Jpeg/YCbCrColorConversion.cs | 2 +- .../PixelBlenders/PorterDuffBulkVsPixel.cs | 6 +-- tests/ImageSharp.Benchmarks/Samplers/Glow.cs | 4 +- .../Drawing/Paths/ShapeRegionTests.cs | 16 ++++---- .../Jpg/Block8x8FTests.CopyToBufferArea.cs | 4 +- .../Jpg/JpegImagePostProcessorTests.cs | 6 +-- .../Jpg/Utils/LibJpegTools.ComponentData.cs | 2 +- .../Formats/Png/PngEncoderTests.cs | 4 +- .../Formats/Png/PngSmokeTests.cs | 4 +- .../Image/ImageFramesCollectionTests.cs | 40 +++++++++---------- tests/ImageSharp.Tests/Image/ImageTests.cs | 2 +- .../ImageSharp.Tests/Memory/Buffer2DTests.cs | 10 ++--- .../Memory/BufferAreaTests.cs | 4 +- tests/ImageSharp.Tests/Memory/BufferTests.cs | 22 +++++----- .../Memory/SpanUtilityTests.cs | 2 +- .../PixelFormats/PixelOperationsTests.cs | 6 +-- .../Transforms/ResizeProfilingBenchmarks.cs | 2 +- .../ReferenceCodecs/SystemDrawingBridge.cs | 12 +++--- .../Tests/ReferenceCodecTests.cs | 2 +- 30 files changed, 96 insertions(+), 96 deletions(-) diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs index e00b94a2e3..1f660466df 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs @@ -22,8 +22,8 @@ namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk [GlobalSetup] public void Setup() { - this.destination = MemoryManager.Current.Allocate(this.Count); - this.source = MemoryManager.Current.Allocate(this.Count); + this.destination = Configuration.Default.MemoryManager.Allocate(this.Count); + this.source = Configuration.Default.MemoryManager.Allocate(this.Count); } [GlobalCleanup] diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs index 593291dedd..fd96c02cd3 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs @@ -25,8 +25,8 @@ [GlobalSetup] public void Setup() { - this.destination = MemoryManager.Current.Allocate(this.Count); - this.source = MemoryManager.Current.Allocate(this.Count * 4); + this.destination = Configuration.Default.MemoryManager.Allocate(this.Count); + this.source = Configuration.Default.MemoryManager.Allocate(this.Count * 4); this.source.Pin(); this.destination.Pin(); } diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs index d273124b89..eab65bb33a 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs @@ -19,8 +19,8 @@ namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk [GlobalSetup] public void Setup() { - this.destination = MemoryManager.Current.Allocate(this.Count); - this.source = MemoryManager.Current.Allocate(this.Count * 4); + this.destination = Configuration.Default.MemoryManager.Allocate(this.Count); + this.source = Configuration.Default.MemoryManager.Allocate(this.Count * 4); } [GlobalCleanup] diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs index 7e29dfe3a8..f9ecc9635e 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs @@ -21,8 +21,8 @@ namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk [GlobalSetup] public void Setup() { - this.source = MemoryManager.Current.Allocate(this.Count); - this.destination = MemoryManager.Current.Allocate(this.Count); + this.source = Configuration.Default.MemoryManager.Allocate(this.Count); + this.destination = Configuration.Default.MemoryManager.Allocate(this.Count); } [GlobalCleanup] diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs index adc374c1fb..8475a9e822 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs @@ -19,8 +19,8 @@ namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk [GlobalSetup] public void Setup() { - this.source = MemoryManager.Current.Allocate(this.Count); - this.destination = MemoryManager.Current.Allocate(this.Count * 3); + this.source = Configuration.Default.MemoryManager.Allocate(this.Count); + this.destination = Configuration.Default.MemoryManager.Allocate(this.Count * 3); } [GlobalCleanup] diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs index bead1384b2..b3e0eff14d 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs @@ -24,8 +24,8 @@ namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk [GlobalSetup] public void Setup() { - this.source = MemoryManager.Current.Allocate(this.Count); - this.destination = MemoryManager.Current.Allocate(this.Count * 4); + this.source = Configuration.Default.MemoryManager.Allocate(this.Count); + this.destination = Configuration.Default.MemoryManager.Allocate(this.Count * 4); } [GlobalCleanup] diff --git a/tests/ImageSharp.Benchmarks/General/ClearBuffer.cs b/tests/ImageSharp.Benchmarks/General/ClearBuffer.cs index 47c8125543..6926d92536 100644 --- a/tests/ImageSharp.Benchmarks/General/ClearBuffer.cs +++ b/tests/ImageSharp.Benchmarks/General/ClearBuffer.cs @@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Benchmarks.General [GlobalSetup] public void Setup() { - this.buffer = MemoryManager.Current.Allocate(this.Count); + this.buffer = Configuration.Default.MemoryManager.Allocate(this.Count); } [GlobalCleanup] diff --git a/tests/ImageSharp.Benchmarks/General/IterateArray.cs b/tests/ImageSharp.Benchmarks/General/IterateArray.cs index 383e7080ca..e06d71f4a0 100644 --- a/tests/ImageSharp.Benchmarks/General/IterateArray.cs +++ b/tests/ImageSharp.Benchmarks/General/IterateArray.cs @@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Benchmarks.General [GlobalSetup] public void Setup() { - this.buffer = MemoryManager.Current.Allocate(this.Length); + this.buffer = Configuration.Default.MemoryManager.Allocate(this.Length); this.buffer.Pin(); this.array = new Vector4[this.Length]; } diff --git a/tests/ImageSharp.Benchmarks/General/PixelIndexing.cs b/tests/ImageSharp.Benchmarks/General/PixelIndexing.cs index 58f835a076..50e0bd6100 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelIndexing.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelIndexing.cs @@ -149,7 +149,7 @@ public void Setup() { this.width = 2048; - this.buffer = MemoryManager.Current.Allocate2D(2048, 2048); + this.buffer = Configuration.Default.MemoryManager.Allocate2D(2048, 2048); this.pointer = (Vector4*)this.buffer.Buffer.Pin(); this.array = this.buffer.Buffer.Array; this.pinnable = Unsafe.As>(this.array); diff --git a/tests/ImageSharp.Benchmarks/Image/EncodeIndexedPng.cs b/tests/ImageSharp.Benchmarks/Image/EncodeIndexedPng.cs index 70ea164d69..b4f78bee85 100644 --- a/tests/ImageSharp.Benchmarks/Image/EncodeIndexedPng.cs +++ b/tests/ImageSharp.Benchmarks/Image/EncodeIndexedPng.cs @@ -54,7 +54,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Image { using (MemoryStream memoryStream = new MemoryStream()) { - PngEncoder encoder = new PngEncoder() { Quantizer = new OctreeQuantizer(), PaletteSize = 256 }; + PngEncoder encoder = new PngEncoder(Configuration.Default.MemoryManager) { Quantizer = new OctreeQuantizer(), PaletteSize = 256 }; this.bmpCore.SaveAsPng(memoryStream, encoder); } @@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Image { using (MemoryStream memoryStream = new MemoryStream()) { - PngEncoder options = new PngEncoder { Quantizer = new OctreeQuantizer { Dither = false }, PaletteSize = 256 }; + PngEncoder options = new PngEncoder(Configuration.Default.MemoryManager) { Quantizer = new OctreeQuantizer { Dither = false }, PaletteSize = 256 }; this.bmpCore.SaveAsPng(memoryStream, options); } @@ -76,7 +76,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Image { using (MemoryStream memoryStream = new MemoryStream()) { - PngEncoder options = new PngEncoder { Quantizer = new PaletteQuantizer(), PaletteSize = 256 }; + PngEncoder options = new PngEncoder(Configuration.Default.MemoryManager) { Quantizer = new PaletteQuantizer(), PaletteSize = 256 }; this.bmpCore.SaveAsPng(memoryStream, options); } @@ -87,7 +87,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Image { using (MemoryStream memoryStream = new MemoryStream()) { - PngEncoder options = new PngEncoder { Quantizer = new PaletteQuantizer { Dither = false }, PaletteSize = 256 }; + PngEncoder options = new PngEncoder(Configuration.Default.MemoryManager) { Quantizer = new PaletteQuantizer { Dither = false }, PaletteSize = 256 }; this.bmpCore.SaveAsPng(memoryStream, options); } @@ -98,7 +98,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Image { using (MemoryStream memoryStream = new MemoryStream()) { - PngEncoder options = new PngEncoder() { Quantizer = new WuQuantizer(), PaletteSize = 256 }; + PngEncoder options = new PngEncoder(Configuration.Default.MemoryManager) { Quantizer = new WuQuantizer(), PaletteSize = 256 }; this.bmpCore.SaveAsPng(memoryStream, options); } diff --git a/tests/ImageSharp.Benchmarks/Image/EncodePng.cs b/tests/ImageSharp.Benchmarks/Image/EncodePng.cs index b3c1e4ee95..383505e0d1 100644 --- a/tests/ImageSharp.Benchmarks/Image/EncodePng.cs +++ b/tests/ImageSharp.Benchmarks/Image/EncodePng.cs @@ -74,7 +74,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Image new OctreeQuantizer() : new PaletteQuantizer(); - var options = new PngEncoder { Quantizer = quantizer }; + var options = new PngEncoder(Configuration.Default.MemoryManager) { Quantizer = quantizer }; this.bmpCore.SaveAsPng(memoryStream, options); } } diff --git a/tests/ImageSharp.Benchmarks/Image/Jpeg/YCbCrColorConversion.cs b/tests/ImageSharp.Benchmarks/Image/Jpeg/YCbCrColorConversion.cs index f513e0d38b..c47aff9cf4 100644 --- a/tests/ImageSharp.Benchmarks/Image/Jpeg/YCbCrColorConversion.cs +++ b/tests/ImageSharp.Benchmarks/Image/Jpeg/YCbCrColorConversion.cs @@ -76,7 +76,7 @@ } // no need to dispose when buffer is not array owner - buffers[i] = MemoryManager.Current.Allocate2D(values.Length, 1); + buffers[i] = Configuration.Default.MemoryManager.Allocate2D(values.Length, 1); } return buffers; diff --git a/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs b/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs index ef44d1e789..4524d757cf 100644 --- a/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs +++ b/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Benchmarks Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = MemoryManager.Current.Allocate(destination.Length * 3)) + using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.Benchmarks { using (Image image = new Image(800, 800)) { - Buffer amounts = MemoryManager.Current.Allocate(image.Width); + Buffer amounts = Configuration.Default.MemoryManager.Allocate(image.Width); for (int x = 0; x < image.Width; x++) { @@ -82,7 +82,7 @@ namespace SixLabors.ImageSharp.Benchmarks { using (Image image = new Image(800, 800)) { - Buffer amounts = MemoryManager.Current.Allocate(image.Width); + Buffer amounts = Configuration.Default.MemoryManager.Allocate(image.Width); for (int x = 0; x < image.Width; x++) { diff --git a/tests/ImageSharp.Benchmarks/Samplers/Glow.cs b/tests/ImageSharp.Benchmarks/Samplers/Glow.cs index 0494db9b9a..2743924f24 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/Glow.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/Glow.cs @@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Benchmarks [GlobalSetup] public void Setup() { - this.bulk = new GlowProcessor(NamedColors.Beige, 800 * .5f, GraphicsOptions.Default); + this.bulk = new GlowProcessor(Configuration.Default.MemoryManager, NamedColors.Beige, 800 * .5f, GraphicsOptions.Default); this.parallel = new GlowProcessorParallel(NamedColors.Beige) { Radius = 800 * .5f, }; } @@ -103,7 +103,7 @@ namespace SixLabors.ImageSharp.Benchmarks } int width = maxX - minX; - using (Buffer rowColors = MemoryManager.Current.Allocate(width)) + using (Buffer rowColors = Configuration.Default.MemoryManager.Allocate(width)) using (PixelAccessor sourcePixels = source.Lock()) { for (int i = 0; i < width; i++) diff --git a/tests/ImageSharp.Tests/Drawing/Paths/ShapeRegionTests.cs b/tests/ImageSharp.Tests/Drawing/Paths/ShapeRegionTests.cs index 941807f542..d3fcc5322e 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/ShapeRegionTests.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/ShapeRegionTests.cs @@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths [Fact] public void ShapeRegionWithPathCallsAsShape() { - new ShapeRegion(pathMock.Object); + new ShapeRegion(Configuration.Default.MemoryManager, pathMock.Object); pathMock.Verify(x => x.AsClosedPath()); } @@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths [Fact] public void ShapeRegionWithPathRetainsShape() { - ShapeRegion region = new ShapeRegion(pathMock.Object); + ShapeRegion region = new ShapeRegion(Configuration.Default.MemoryManager, pathMock.Object); Assert.Equal(pathMock.Object, region.Shape); } @@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths [Fact] public void ShapeRegionFromPathConvertsBoundsProxyToShape() { - ShapeRegion region = new ShapeRegion(pathMock.Object); + ShapeRegion region = new ShapeRegion(Configuration.Default.MemoryManager, pathMock.Object); Assert.Equal(Math.Floor(bounds.Left), region.Bounds.Left); Assert.Equal(Math.Ceiling(bounds.Right), region.Bounds.Right); @@ -64,7 +64,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths [Fact] public void ShapeRegionFromPathMaxIntersectionsProxyToShape() { - ShapeRegion region = new ShapeRegion(pathMock.Object); + ShapeRegion region = new ShapeRegion(Configuration.Default.MemoryManager, pathMock.Object); int i = region.MaxIntersections; pathMock.Verify(x => x.MaxIntersections); @@ -74,7 +74,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths public void ShapeRegionFromPathScanYProxyToShape() { int yToScan = 10; - ShapeRegion region = new ShapeRegion(pathMock.Object); + ShapeRegion region = new ShapeRegion(Configuration.Default.MemoryManager, pathMock.Object); pathMock.Setup(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Callback((s, e, b, o) => { @@ -93,7 +93,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths public void ShapeRegionFromShapeScanYProxyToShape() { int yToScan = 10; - ShapeRegion region = new ShapeRegion(pathMock.Object); + ShapeRegion region = new ShapeRegion(Configuration.Default.MemoryManager, pathMock.Object); pathMock.Setup(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Callback((s, e, b, o) => { @@ -111,7 +111,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths [Fact] public void ShapeRegionFromShapeConvertsBoundsProxyToShape() { - ShapeRegion region = new ShapeRegion(pathMock.Object); + ShapeRegion region = new ShapeRegion(Configuration.Default.MemoryManager, pathMock.Object); Assert.Equal(Math.Floor(bounds.Left), region.Bounds.Left); Assert.Equal(Math.Ceiling(bounds.Right), region.Bounds.Right); @@ -122,7 +122,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths [Fact] public void ShapeRegionFromShapeMaxIntersectionsProxyToShape() { - ShapeRegion region = new ShapeRegion(pathMock.Object); + ShapeRegion region = new ShapeRegion(Configuration.Default.MemoryManager, pathMock.Object); int i = region.MaxIntersections; pathMock.Verify(x => x.MaxIntersections); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs index 1dc4896eeb..e50d84852a 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs @@ -48,7 +48,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg { Block8x8F block = CreateRandomFloatBlock(0, 100); - using (var buffer = MemoryManager.Current.Allocate2D(20, 20)) + using (var buffer = Configuration.Default.MemoryManager.Allocate2D(20, 20)) { BufferArea area = buffer.GetArea(5, 10, 8, 8); block.CopyTo(area); @@ -78,7 +78,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg var start = new Point(50, 50); - using (var buffer = MemoryManager.Current.Allocate2D(100, 100)) + using (var buffer = Configuration.Default.MemoryManager.Allocate2D(100, 100)) { BufferArea area = buffer.GetArea(start.X, start.Y, 8 * horizontalFactor, 8 * verticalFactor); block.CopyTo(area, horizontalFactor, verticalFactor); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegImagePostProcessorTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegImagePostProcessorTests.cs index df6d1ef1bb..423673ef93 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegImagePostProcessorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegImagePostProcessorTests.cs @@ -55,8 +55,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg { string imageFile = provider.SourceFileOrDescription; using (OrigJpegDecoderCore decoder = JpegFixture.ParseStream(imageFile)) - using (var pp = new JpegImagePostProcessor(decoder)) - using (var imageFrame = new ImageFrame(decoder.ImageWidth, decoder.ImageHeight)) + using (var pp = new JpegImagePostProcessor(Configuration.Default.MemoryManager, decoder)) + using (var imageFrame = new ImageFrame(Configuration.Default.MemoryManager, decoder.ImageWidth, decoder.ImageHeight)) { pp.DoPostProcessorStep(imageFrame); @@ -77,7 +77,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg { string imageFile = provider.SourceFileOrDescription; using (OrigJpegDecoderCore decoder = JpegFixture.ParseStream(imageFile)) - using (var pp = new JpegImagePostProcessor(decoder)) + using (var pp = new JpegImagePostProcessor(Configuration.Default.MemoryManager, decoder)) using (var image = new Image(decoder.ImageWidth, decoder.ImageHeight)) { pp.PostProcess(image.Frames.RootFrame); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs index ad9ad81436..9e287eb2d7 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs @@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils this.HeightInBlocks = heightInBlocks; this.WidthInBlocks = widthInBlocks; this.Index = index; - this.SpectralBlocks = MemoryManager.Current.Allocate2D(this.WidthInBlocks, this.HeightInBlocks); + this.SpectralBlocks = Configuration.Default.MemoryManager.Allocate2D(this.WidthInBlocks, this.HeightInBlocks); } public Size Size => new Size(this.WidthInBlocks, this.HeightInBlocks); diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index 1566ddf442..ac9a5c2209 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Tests { using (Image image = provider.GetImage()) { - var options = new PngEncoder() + var options = new PngEncoder(Configuration.Default.MemoryManager) { PngColorType = pngColorType }; @@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.Tests using (Image image = provider.GetImage()) using (var ms = new MemoryStream()) { - image.Save(ms, new PngEncoder()); + image.Save(ms, new PngEncoder(Configuration.Default.MemoryManager)); byte[] data = ms.ToArray().Take(8).ToArray(); byte[] expected = { diff --git a/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs index fc17df93d1..3a73867ba6 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs @@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png { // image.Save(provider.Utility.GetTestOutputFileName("bmp")); - image.Save(ms, new PngEncoder()); + image.Save(ms, new PngEncoder(Configuration.Default.MemoryManager)); ms.Position = 0; using (Image img2 = Image.Load(ms, new PngDecoder())) { @@ -110,7 +110,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png image.Mutate(x => x.Resize(100, 100)); // image.Save(provider.Utility.GetTestOutputFileName("png", "resize")); - image.Save(ms, new PngEncoder()); + image.Save(ms, new PngEncoder(Configuration.Default.MemoryManager)); ms.Position = 0; using (Image img2 = Image.Load(ms, new PngDecoder())) { diff --git a/tests/ImageSharp.Tests/Image/ImageFramesCollectionTests.cs b/tests/ImageSharp.Tests/Image/ImageFramesCollectionTests.cs index afae9cae8c..1f2137070f 100644 --- a/tests/ImageSharp.Tests/Image/ImageFramesCollectionTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageFramesCollectionTests.cs @@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp.Tests { ArgumentException ex = Assert.Throws(() => { - this.collection.AddFrame(new ImageFrame(1, 1)); + this.collection.AddFrame(new ImageFrame(Configuration.Default.MemoryManager, 1, 1)); }); Assert.StartsWith("Frame must have the same dimensions as the image.", ex.Message); @@ -78,7 +78,7 @@ namespace SixLabors.ImageSharp.Tests ArgumentException ex = Assert.Throws(() => { - this.collection.InsertFrame(1, new ImageFrame(1, 1)); + this.collection.InsertFrame(1, new ImageFrame(Configuration.Default.MemoryManager, 1, 1)); }); Assert.StartsWith("Frame must have the same dimensions as the image.", ex.Message); @@ -103,8 +103,8 @@ namespace SixLabors.ImageSharp.Tests ArgumentException ex = Assert.Throws(() => { var collection = new ImageFrameCollection(this.image, new[] { - new ImageFrame(10,10), - new ImageFrame(1,1), + new ImageFrame(Configuration.Default.MemoryManager,10,10), + new ImageFrame(Configuration.Default.MemoryManager,1,1), }); }); @@ -115,7 +115,7 @@ namespace SixLabors.ImageSharp.Tests public void RemoveAtFrame_ThrowIfRemovingLastFrame() { var collection = new ImageFrameCollection(this.image, new[] { - new ImageFrame(10,10) + new ImageFrame(Configuration.Default.MemoryManager,10,10) }); InvalidOperationException ex = Assert.Throws(() => @@ -130,8 +130,8 @@ namespace SixLabors.ImageSharp.Tests { var collection = new ImageFrameCollection(this.image, new[] { - new ImageFrame(10,10), - new ImageFrame(10,10), + new ImageFrame(Configuration.Default.MemoryManager,10,10), + new ImageFrame(Configuration.Default.MemoryManager,10,10), }); collection.RemoveFrame(0); @@ -142,8 +142,8 @@ namespace SixLabors.ImageSharp.Tests public void RootFrameIsFrameAtIndexZero() { var collection = new ImageFrameCollection(this.image, new[] { - new ImageFrame(10,10), - new ImageFrame(10,10), + new ImageFrame(Configuration.Default.MemoryManager,10,10), + new ImageFrame(Configuration.Default.MemoryManager,10,10), }); Assert.Equal(collection.RootFrame, collection[0]); @@ -153,8 +153,8 @@ namespace SixLabors.ImageSharp.Tests public void ConstructorPopulatesFrames() { var collection = new ImageFrameCollection(this.image, new[] { - new ImageFrame(10,10), - new ImageFrame(10,10), + new ImageFrame(Configuration.Default.MemoryManager,10,10), + new ImageFrame(Configuration.Default.MemoryManager,10,10), }); Assert.Equal(2, collection.Count); @@ -164,8 +164,8 @@ namespace SixLabors.ImageSharp.Tests public void DisposeClearsCollection() { var collection = new ImageFrameCollection(this.image, new[] { - new ImageFrame(10,10), - new ImageFrame(10,10), + new ImageFrame(Configuration.Default.MemoryManager,10,10), + new ImageFrame(Configuration.Default.MemoryManager,10,10), }); collection.Dispose(); @@ -177,8 +177,8 @@ namespace SixLabors.ImageSharp.Tests public void Dispose_DisposesAllInnerFrames() { var collection = new ImageFrameCollection(this.image, new[] { - new ImageFrame(10,10), - new ImageFrame(10,10), + new ImageFrame(Configuration.Default.MemoryManager,10,10), + new ImageFrame(Configuration.Default.MemoryManager,10,10), }); IPixelSource[] framesSnapShot = collection.OfType>().ToArray(); @@ -198,7 +198,7 @@ namespace SixLabors.ImageSharp.Tests { using (Image img = provider.GetImage()) { - img.Frames.AddFrame(new ImageFrame(10, 10));// add a frame anyway + img.Frames.AddFrame(new ImageFrame(Configuration.Default.MemoryManager,10, 10));// add a frame anyway using (Image cloned = img.Frames.CloneFrame(0)) { Assert.Equal(2, img.Frames.Count); @@ -216,7 +216,7 @@ namespace SixLabors.ImageSharp.Tests { var sourcePixelData = img.GetPixelSpan().ToArray(); - img.Frames.AddFrame(new ImageFrame(10, 10)); + img.Frames.AddFrame(new ImageFrame(Configuration.Default.MemoryManager,10, 10)); using (Image cloned = img.Frames.ExportFrame(0)) { Assert.Equal(1, img.Frames.Count); @@ -244,7 +244,7 @@ namespace SixLabors.ImageSharp.Tests public void AddFrame_clones_sourceFrame() { var pixelData = this.image.Frames.RootFrame.GetPixelSpan().ToArray(); - var otherFRame = new ImageFrame(10, 10); + var otherFRame = new ImageFrame(Configuration.Default.MemoryManager,10, 10); var addedFrame = this.image.Frames.AddFrame(otherFRame); addedFrame.ComparePixelBufferTo(otherFRame.GetPixelSpan()); Assert.NotEqual(otherFRame, addedFrame); @@ -254,7 +254,7 @@ namespace SixLabors.ImageSharp.Tests public void InsertFrame_clones_sourceFrame() { var pixelData = this.image.Frames.RootFrame.GetPixelSpan().ToArray(); - var otherFRame = new ImageFrame(10, 10); + var otherFRame = new ImageFrame(Configuration.Default.MemoryManager,10, 10); var addedFrame = this.image.Frames.InsertFrame(0, otherFRame); addedFrame.ComparePixelBufferTo(otherFRame.GetPixelSpan()); Assert.NotEqual(otherFRame, addedFrame); @@ -308,7 +308,7 @@ namespace SixLabors.ImageSharp.Tests this.image.Frames.CreateFrame(); } - var frame = new ImageFrame(10, 10); + var frame = new ImageFrame(Configuration.Default.MemoryManager,10, 10); Assert.False(this.image.Frames.Contains(frame)); } diff --git a/tests/ImageSharp.Tests/Image/ImageTests.cs b/tests/ImageSharp.Tests/Image/ImageTests.cs index da813f4280..45ecf60a06 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.cs @@ -103,7 +103,7 @@ namespace SixLabors.ImageSharp.Tests using (Image image = new Image(10, 10)) { - image.Save(file, new PngEncoder()); + image.Save(file, new PngEncoder(Configuration.Default.MemoryManager)); } using (Image img = Image.Load(file, out var mime)) { diff --git a/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs index f14995433d..7f78ef39c0 100644 --- a/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs +++ b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs @@ -31,7 +31,7 @@ namespace SixLabors.ImageSharp.Tests.Memory [InlineData(1025, 17)] public void Construct(int width, int height) { - using (Buffer2D buffer = MemoryManager.Current.Allocate2D(width, height)) + using (Buffer2D buffer = Configuration.Default.MemoryManager.Allocate2D(width, height)) { Assert.Equal(width, buffer.Width); Assert.Equal(height, buffer.Height); @@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Tests.Memory { for (int i = 0; i < 100; i++) { - using (Buffer2D buffer = Buffer2D.CreateClean(42, 42)) + using (Buffer2D buffer = Configuration.Default.MemoryManager.Allocate2D(42, 42, true)) { for (int j = 0; j < buffer.Buffer.Length; j++) { @@ -61,7 +61,7 @@ namespace SixLabors.ImageSharp.Tests.Memory [InlineData(17, 42, 41)] public void GetRowSpanY(int width, int height, int y) { - using (Buffer2D buffer = MemoryManager.Current.Allocate2D(width, height)) + using (Buffer2D buffer = Configuration.Default.MemoryManager.Allocate2D(width, height)) { Span span = buffer.GetRowSpan(y); @@ -77,7 +77,7 @@ namespace SixLabors.ImageSharp.Tests.Memory [InlineData(17, 42, 0, 41)] public void GetRowSpanXY(int width, int height, int x, int y) { - using (Buffer2D buffer = MemoryManager.Current.Allocate2D(width, height)) + using (Buffer2D buffer = Configuration.Default.MemoryManager.Allocate2D(width, height)) { Span span = buffer.GetRowSpan(x, y); @@ -93,7 +93,7 @@ namespace SixLabors.ImageSharp.Tests.Memory [InlineData(99, 88, 98, 87)] public void Indexer(int width, int height, int x, int y) { - using (Buffer2D buffer = MemoryManager.Current.Allocate2D(width, height)) + using (Buffer2D buffer = Configuration.Default.MemoryManager.Allocate2D(width, height)) { TestStructs.Foo[] array = buffer.Buffer.Array; diff --git a/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs b/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs index 74e9098b90..2ca409dc15 100644 --- a/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs +++ b/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Tests.Memory [Fact] public void Construct() { - using (var buffer = MemoryManager.Current.Allocate2D(10, 20)) + using (var buffer = Configuration.Default.MemoryManager.Allocate2D(10, 20)) { var rectangle = new Rectangle(3,2, 5, 6); var area = new BufferArea(buffer, rectangle); @@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Tests.Memory private static Buffer2D CreateTestBuffer(int w, int h) { - var buffer = MemoryManager.Current.Allocate2D(w, h); + var buffer = Configuration.Default.MemoryManager.Allocate2D(w, h); for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) diff --git a/tests/ImageSharp.Tests/Memory/BufferTests.cs b/tests/ImageSharp.Tests/Memory/BufferTests.cs index 8669f2bb05..d0a83a094d 100644 --- a/tests/ImageSharp.Tests/Memory/BufferTests.cs +++ b/tests/ImageSharp.Tests/Memory/BufferTests.cs @@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Tests.Memory [InlineData(1111)] public void ConstructWithOwnArray(int count) { - using (Buffer buffer = MemoryManager.Current.Allocate(count)) + using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(count)) { Assert.False(buffer.IsDisposedOrLostArrayOwnership); Assert.NotNull(buffer.Array); @@ -76,7 +76,7 @@ namespace SixLabors.ImageSharp.Tests.Memory { for (int i = 0; i < 100; i++) { - using (Buffer buffer = MemoryManager.Current.Allocate(42, true)) + using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(42, true)) { for (int j = 0; j < buffer.Length; j++) { @@ -129,7 +129,7 @@ namespace SixLabors.ImageSharp.Tests.Memory [Fact] public void Dispose() { - Buffer buffer = MemoryManager.Current.Allocate(42); + Buffer buffer = Configuration.Default.MemoryManager.Allocate(42); buffer.Dispose(); Assert.True(buffer.IsDisposedOrLostArrayOwnership); @@ -140,7 +140,7 @@ namespace SixLabors.ImageSharp.Tests.Memory [InlineData(123)] public void CastToSpan(int bufferLength) { - using (Buffer buffer = MemoryManager.Current.Allocate(bufferLength)) + using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(bufferLength)) { Span span = buffer; @@ -154,7 +154,7 @@ namespace SixLabors.ImageSharp.Tests.Memory [Fact] public void Span() { - using (Buffer buffer = MemoryManager.Current.Allocate(42)) + using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(42)) { Span span = buffer.Span; @@ -173,7 +173,7 @@ namespace SixLabors.ImageSharp.Tests.Memory [InlineData(123, 17)] public void WithStartOnly(int bufferLength, int start) { - using (Buffer buffer = MemoryManager.Current.Allocate(bufferLength)) + using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(bufferLength)) { Span span = buffer.Slice(start); @@ -187,7 +187,7 @@ namespace SixLabors.ImageSharp.Tests.Memory [InlineData(123, 17, 42)] public void WithStartAndLength(int bufferLength, int start, int spanLength) { - using (Buffer buffer = MemoryManager.Current.Allocate(bufferLength)) + using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(bufferLength)) { Span span = buffer.Slice(start, spanLength); @@ -201,7 +201,7 @@ namespace SixLabors.ImageSharp.Tests.Memory public void UnPinAndTakeArrayOwnership() { TestStructs.Foo[] data = null; - using (Buffer buffer = MemoryManager.Current.Allocate(42)) + using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(42)) { data = buffer.TakeArrayOwnership(); Assert.True(buffer.IsDisposedOrLostArrayOwnership); @@ -216,7 +216,7 @@ namespace SixLabors.ImageSharp.Tests.Memory [Fact] public void ReturnsPinnedPointerToTheBeginningOfArray() { - using (Buffer buffer = MemoryManager.Current.Allocate(42)) + using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(42)) { TestStructs.Foo* actual = (TestStructs.Foo*)buffer.Pin(); fixed (TestStructs.Foo* expected = buffer.Array) @@ -229,7 +229,7 @@ namespace SixLabors.ImageSharp.Tests.Memory [Fact] public void SecondCallReturnsTheSamePointer() { - using (Buffer buffer = MemoryManager.Current.Allocate(42)) + using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(42)) { IntPtr ptr1 = buffer.Pin(); IntPtr ptr2 = buffer.Pin(); @@ -241,7 +241,7 @@ namespace SixLabors.ImageSharp.Tests.Memory [Fact] public void WhenCalledOnDisposedBuffer_ThrowsInvalidOperationException() { - Buffer buffer = MemoryManager.Current.Allocate(42); + Buffer buffer = Configuration.Default.MemoryManager.Allocate(42); buffer.Dispose(); Assert.Throws(() => buffer.Pin()); diff --git a/tests/ImageSharp.Tests/Memory/SpanUtilityTests.cs b/tests/ImageSharp.Tests/Memory/SpanUtilityTests.cs index 8b90295ce8..757c8fcf9f 100644 --- a/tests/ImageSharp.Tests/Memory/SpanUtilityTests.cs +++ b/tests/ImageSharp.Tests/Memory/SpanUtilityTests.cs @@ -424,7 +424,7 @@ namespace SixLabors.ImageSharp.Tests.Memory Rgba32[] colors = { new Rgba32(0, 1, 2, 3), new Rgba32(4, 5, 6, 7), new Rgba32(8, 9, 10, 11), }; using (Buffer colorBuf = new Buffer(colors)) - using (Buffer byteBuf = MemoryManager.Current.Allocate(colors.Length * 4)) + using (Buffer byteBuf = Configuration.Default.MemoryManager.Allocate(colors.Length * 4)) { SpanHelper.Copy(colorBuf.Span.AsBytes(), byteBuf, colorBuf.Length * sizeof(Rgba32)); diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs index c7227eb8ae..4cd7ebeea3 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs @@ -50,8 +50,8 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats int times = 200000; int count = 1024; - using (Buffer source = MemoryManager.Current.Allocate(count)) - using (Buffer dest = MemoryManager.Current.Allocate(count)) + using (Buffer source = Configuration.Default.MemoryManager.Allocate(count)) + using (Buffer dest = Configuration.Default.MemoryManager.Allocate(count)) { this.Measure( times, @@ -344,7 +344,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats { this.SourceBuffer = new Buffer(source); this.ExpectedDestBuffer = new Buffer(expectedDest); - this.ActualDestBuffer = MemoryManager.Current.Allocate(expectedDest.Length); + this.ActualDestBuffer = Configuration.Default.MemoryManager.Allocate(expectedDest.Length); } public void Dispose() diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeProfilingBenchmarks.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeProfilingBenchmarks.cs index 963b849d2a..d5aed8832a 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeProfilingBenchmarks.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeProfilingBenchmarks.cs @@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms // [Fact] public void PrintWeightsData() { - var proc = new ResizeProcessor(new BicubicResampler(), 200, 200); + var proc = new ResizeProcessor(Configuration.Default.MemoryManager, new BicubicResampler(), 200, 200); ResamplingWeightedProcessor.WeightsBuffer weights = proc.PrecomputeWeights(200, 500); diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs index babe148c80..d14a0165aa 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs int length = source.Length; Guard.MustBeSizedAtLeast(dest, length, nameof(dest)); - using (var rgbaBuffer = MemoryManager.Current.Allocate(length)) + using (var rgbaBuffer = Configuration.Default.MemoryManager.Allocate(length)) { PixelOperations.Instance.ToRgba32(source, rgbaBuffer, length); @@ -39,7 +39,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs int length = source.Length; Guard.MustBeSizedAtLeast(dest, length, nameof(dest)); - using (var rgbaBuffer = MemoryManager.Current.Allocate(length)) + using (var rgbaBuffer = Configuration.Default.MemoryManager.Allocate(length)) { PixelOperations.Instance.ToRgba32(source, rgbaBuffer, length); @@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs int length = source.Length; Guard.MustBeSizedAtLeast(dest, length, nameof(dest)); - using (var rgbaBuffer = MemoryManager.Current.Allocate(length)) + using (var rgbaBuffer = Configuration.Default.MemoryManager.Allocate(length)) { PixelOperations.Instance.ToRgb24(source, rgbaBuffer, length); @@ -96,7 +96,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs var image = new Image(w, h); - using (var workBuffer = MemoryManager.Current.Allocate(w)) + using (var workBuffer = Configuration.Default.MemoryManager.Allocate(w)) { var destPtr = (Argb32*)workBuffer.Pin(); for (int y = 0; y < h; y++) @@ -138,7 +138,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs var image = new Image(w, h); - using (var workBuffer = MemoryManager.Current.Allocate(w)) + using (var workBuffer = Configuration.Default.MemoryManager.Allocate(w)) { var destPtr = (Rgb24*)workBuffer.Pin(); for (int y = 0; y < h; y++) @@ -170,7 +170,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs long destRowByteCount = data.Stride; long sourceRowByteCount = w * sizeof(Argb32); - using (var workBuffer = MemoryManager.Current.Allocate(w)) + using (var workBuffer = Configuration.Default.MemoryManager.Allocate(w)) { var sourcePtr = (Argb32*)workBuffer.Pin(); diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceCodecTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceCodecTests.cs index 0a550a3c1a..f4fce0f639 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceCodecTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceCodecTests.cs @@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.Tests sourceImage.Mutate(c => c.Alpha(1)); } - var encoder = new PngEncoder() { PngColorType = pngColorType }; + var encoder = new PngEncoder(Configuration.Default.MemoryManager) { PngColorType = pngColorType }; return provider.Utility.SaveTestOutputFile(sourceImage, "png", encoder); } } From bf8bd6832f38bd9758d0c2bf39655ee0d4923d03 Mon Sep 17 00:00:00 2001 From: Lauri Kotilainen Date: Fri, 12 Jan 2018 15:21:23 +0200 Subject: [PATCH 010/151] - Code style fixes --- src/ImageSharp.Drawing/Paths/ShapePath.cs | 1 + src/ImageSharp.Drawing/Paths/ShapeRegion.cs | 1 + .../Jpeg/Common/Decoder/JpegImagePostProcessor.cs | 1 + .../GolangPort/Components/Decoder/OrigComponent.cs | 1 + .../Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs | 1 + .../Jpeg/PdfJsPort/Components/PdfJsJpegPixelArea.cs | 1 + .../PdfJsPort/Components/PdfJsQuantizationTables.cs | 10 +++++----- src/ImageSharp/Formats/Png/PngEncoder.cs | 6 +++++- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 1 + src/ImageSharp/IImageProcessingContext{TPixel}.cs | 5 +++++ src/ImageSharp/Image/ImageFrame{TPixel}.cs | 10 ++++++++-- src/ImageSharp/Memory/ArrayPoolMemoryManager.cs | 1 - .../Processors/ColorMatrix/LomographProcessor.cs | 4 +++- .../Processors/ColorMatrix/PolaroidProcessor.cs | 4 +++- .../Processors/Effects/BackgroundColorProcessor.cs | 1 + .../Processing/Processors/Overlays/GlowProcessor.cs | 1 + .../Processors/Overlays/VignetteProcessor.cs | 2 ++ .../Transforms/ResamplingWeightedProcessor.Weights.cs | 1 + .../Transforms/ResamplingWeightedProcessor.cs | 5 +++-- .../Processors/Transforms/ResizeProcessor.cs | 2 ++ 20 files changed, 46 insertions(+), 13 deletions(-) diff --git a/src/ImageSharp.Drawing/Paths/ShapePath.cs b/src/ImageSharp.Drawing/Paths/ShapePath.cs index f973668e54..9e2b22a75b 100644 --- a/src/ImageSharp.Drawing/Paths/ShapePath.cs +++ b/src/ImageSharp.Drawing/Paths/ShapePath.cs @@ -18,6 +18,7 @@ namespace SixLabors.ImageSharp.Drawing /// /// Initializes a new instance of the class. /// + /// The to use for buffer allocations. /// The shape. /// The pen to apply to the shape. // SixLabors.shape willbe moving to a Span/ReadOnlySpan based API shortly use ToArray for now. diff --git a/src/ImageSharp.Drawing/Paths/ShapeRegion.cs b/src/ImageSharp.Drawing/Paths/ShapeRegion.cs index 489468dbed..77a3b01159 100644 --- a/src/ImageSharp.Drawing/Paths/ShapeRegion.cs +++ b/src/ImageSharp.Drawing/Paths/ShapeRegion.cs @@ -20,6 +20,7 @@ namespace SixLabors.ImageSharp.Drawing /// /// Initializes a new instance of the class. /// + /// The to use for buffer allocations. /// The shape. public ShapeRegion(MemoryManager memoryManager, IPath shape) { diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs index 44841bd676..1b83f62eb4 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs @@ -44,6 +44,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder /// /// Initializes a new instance of the class. /// + /// The to use for buffer allocations. /// The representing the uncompressed spectral Jpeg data public JpegImagePostProcessor(MemoryManager memoryManager, IRawJpegData rawJpeg) { diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigComponent.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigComponent.cs index c2b4d632a1..e83dd75a54 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigComponent.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigComponent.cs @@ -54,6 +54,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder /// /// Initializes /// + /// The to use for buffer allocations. /// The instance public void InitializeDerivedData(MemoryManager memoryManager, OrigJpegDecoderCore decoder) { diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs index 4142ae354c..f1beab114a 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs @@ -20,6 +20,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components /// /// Initializes a new instance of the struct. /// + /// The to use for buffer allocations. /// The code lengths /// The huffman values public PdfJsHuffmanTable(MemoryManager memoryManager, byte[] lengths, byte[] values) diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsJpegPixelArea.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsJpegPixelArea.cs index eebc57b862..ac26d892c1 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsJpegPixelArea.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsJpegPixelArea.cs @@ -27,6 +27,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components /// /// Initializes a new instance of the struct. /// + /// The to use for buffer allocations. /// The image width /// The image height /// The number of components diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsQuantizationTables.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsQuantizationTables.cs index f7302b1564..afe0b30072 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsQuantizationTables.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsQuantizationTables.cs @@ -12,6 +12,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components /// internal sealed class PdfJsQuantizationTables : IDisposable { + public PdfJsQuantizationTables(MemoryManager memoryManager) + { + this.Tables = memoryManager.Allocate2D(64, 4); + } + /// /// Gets the ZigZag scan table /// @@ -40,11 +45,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components 63 }; - public PdfJsQuantizationTables(MemoryManager memoryManager) - { - this.Tables = memoryManager.Allocate2D(64, 4); - } - /// /// Gets or sets the quantization tables. /// diff --git a/src/ImageSharp/Formats/Png/PngEncoder.cs b/src/ImageSharp/Formats/Png/PngEncoder.cs index f65ce59b2f..1fd70a4d36 100644 --- a/src/ImageSharp/Formats/Png/PngEncoder.cs +++ b/src/ImageSharp/Formats/Png/PngEncoder.cs @@ -16,11 +16,15 @@ namespace SixLabors.ImageSharp.Formats.Png { private readonly MemoryManager memoryManager; + /// + /// Initializes a new instance of the class. + /// + /// The to use for buffer allocations. public PngEncoder(MemoryManager memoryManager) { this.memoryManager = memoryManager; } - + /// /// Gets or sets a value indicating whether the metadata should be ignored when the image is being encoded. /// diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index e3e209ed43..327ce9fdf3 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -150,6 +150,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// Initializes a new instance of the class. /// + /// The to use for buffer allocations. /// The options for influancing the encoder public PngEncoderCore(MemoryManager memoryManager, IPngEncoderOptions options) { diff --git a/src/ImageSharp/IImageProcessingContext{TPixel}.cs b/src/ImageSharp/IImageProcessingContext{TPixel}.cs index 1556987df6..68b0a030a5 100644 --- a/src/ImageSharp/IImageProcessingContext{TPixel}.cs +++ b/src/ImageSharp/IImageProcessingContext{TPixel}.cs @@ -30,6 +30,11 @@ namespace SixLabors.ImageSharp /// The current operations class to allow chaining of operations. IImageProcessingContext ApplyProcessor(IImageProcessor processor); + /// + /// Returns a reference to the used to allocate buffers + /// for this context. + /// + /// A to use for buffer allocations. MemoryManager GetMemoryManager(); } diff --git a/src/ImageSharp/Image/ImageFrame{TPixel}.cs b/src/ImageSharp/Image/ImageFrame{TPixel}.cs index 2f2f545dbb..888630afe8 100644 --- a/src/ImageSharp/Image/ImageFrame{TPixel}.cs +++ b/src/ImageSharp/Image/ImageFrame{TPixel}.cs @@ -20,8 +20,6 @@ namespace SixLabors.ImageSharp public sealed class ImageFrame : IPixelSource, IDisposable where TPixel : struct, IPixel { - public MemoryManager MemoryManager { get; } - /// /// The image pixels. Not private as Buffer2D requires an array in its constructor. /// @@ -32,6 +30,7 @@ namespace SixLabors.ImageSharp /// /// Initializes a new instance of the class. /// + /// The to use for buffer allocations. /// The width of the image in pixels. /// The height of the image in pixels. internal ImageFrame(MemoryManager memoryManager, int width, int height) @@ -42,6 +41,7 @@ namespace SixLabors.ImageSharp /// /// Initializes a new instance of the class. /// + /// The to use for buffer allocations. /// The width of the image in pixels. /// The height of the image in pixels. /// The meta data. @@ -60,6 +60,7 @@ namespace SixLabors.ImageSharp /// /// Initializes a new instance of the class. /// + /// The to use for buffer allocations. /// The source. internal ImageFrame(MemoryManager memoryManager, ImageFrame source) { @@ -69,6 +70,11 @@ namespace SixLabors.ImageSharp this.MetaData = source.MetaData.Clone(); } + /// + /// Gets the to use for buffer allocations. + /// + public MemoryManager MemoryManager { get; } + /// Buffer2D IPixelSource.PixelBuffer => this.pixelBuffer; diff --git a/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs b/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs index 80c6e00e3a..d83e3ca3e5 100644 --- a/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs +++ b/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs @@ -4,7 +4,6 @@ using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.Memory { - /// /// Implements by allocating memory from . /// diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/LomographProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/LomographProcessor.cs index a95d5fef65..f66ce80a59 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/LomographProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/LomographProcessor.cs @@ -25,8 +25,10 @@ namespace SixLabors.ImageSharp.Processing.Processors /// /// Initializes a new instance of the class. /// + /// The to use for buffer allocations. /// The options effecting blending and composition. - public LomographProcessor(MemoryManager memoryManager, GraphicsOptions options) { + public LomographProcessor(MemoryManager memoryManager, GraphicsOptions options) + { this.memoryManager = memoryManager; this.options = options; } diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/PolaroidProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/PolaroidProcessor.cs index 76a616a1b0..a14919e863 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/PolaroidProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/PolaroidProcessor.cs @@ -26,8 +26,10 @@ namespace SixLabors.ImageSharp.Processing.Processors /// /// Initializes a new instance of the class. /// + /// The to use for buffer allocations. /// The options effecting blending and composition. - public PolaroidProcessor(MemoryManager memoryManager, GraphicsOptions options) { + public PolaroidProcessor(MemoryManager memoryManager, GraphicsOptions options) + { this.memoryManager = memoryManager; this.options = options; } diff --git a/src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs index b81fe3cf31..296ae1bb37 100644 --- a/src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs @@ -24,6 +24,7 @@ namespace SixLabors.ImageSharp.Processing.Processors /// /// Initializes a new instance of the class. /// + /// The to use for buffer allocations. /// The to set the background color to. /// The options defining blending algorithum and amount. public BackgroundColorProcessor(MemoryManager memoryManager, TPixel color, GraphicsOptions options) diff --git a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs index 521da20a76..6114c64387 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs @@ -27,6 +27,7 @@ namespace SixLabors.ImageSharp.Processing.Processors /// /// Initializes a new instance of the class. /// + /// The to use for buffer allocations. /// The color or the glow. /// The radius of the glow. /// The options effecting blending and composition. diff --git a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs index dc60ffec91..9877f4cc9d 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs @@ -27,6 +27,7 @@ namespace SixLabors.ImageSharp.Processing.Processors /// /// Initializes a new instance of the class. /// + /// The to use for buffer allocations. /// The color of the vignette. /// The x-radius. /// The y-radius. @@ -44,6 +45,7 @@ namespace SixLabors.ImageSharp.Processing.Processors /// /// Initializes a new instance of the class. /// + /// The to use for buffer allocations. /// The color of the vignette. /// The options effecting blending and composition. public VignetteProcessor(MemoryManager memoryManager, TPixel color, GraphicsOptions options) diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs index c86fe89b70..95fc5ee6d6 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs @@ -162,6 +162,7 @@ namespace SixLabors.ImageSharp.Processing.Processors /// /// Initializes a new instance of the class. /// + /// The to use for buffer allocations. /// The size of the source window /// The size of the destination window public WeightsBuffer(MemoryManager memoryManager, int sourceSize, int destinationSize) diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs index 6b608d1021..3b7ff4b642 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs @@ -21,6 +21,7 @@ namespace SixLabors.ImageSharp.Processing.Processors /// /// Initializes a new instance of the class. /// + /// The to use for buffer allocations. /// The sampler to perform the resize operation. /// The target width. /// The target height. @@ -40,7 +41,7 @@ namespace SixLabors.ImageSharp.Processing.Processors this.Height = height; this.ResizeRectangle = resizeRectangle; } - + /// /// Gets the sampler to perform the resize operation. /// @@ -60,7 +61,7 @@ namespace SixLabors.ImageSharp.Processing.Processors /// Gets or sets the resize rectangle. /// public Rectangle ResizeRectangle { get; protected set; } - + protected MemoryManager MemoryManager { get; } /// diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs index 6c4ea7e67d..2c0f3f1540 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs @@ -23,6 +23,7 @@ namespace SixLabors.ImageSharp.Processing.Processors /// /// Initializes a new instance of the class. /// + /// The to use for buffer allocations. /// The sampler to perform the resize operation. /// The target width. /// The target height. @@ -34,6 +35,7 @@ namespace SixLabors.ImageSharp.Processing.Processors /// /// Initializes a new instance of the class. /// + /// The to use for buffer allocations. /// The sampler to perform the resize operation. /// The target width. /// The target height. From 3562d9bd8f4dfef8f5cfb413f84b1a78c2b7a95e Mon Sep 17 00:00:00 2001 From: Lauri Kotilainen Date: Fri, 12 Jan 2018 18:28:50 +0200 Subject: [PATCH 011/151] - Removing more usages of ArrayPool --- ImageSharp.sln.DotSettings | 10 + .../Processors/FillRegionProcessor.cs | 127 +++++----- .../Common/Extensions/StreamExtensions.cs | 21 +- src/ImageSharp/Formats/Gif/GifDecoderCore.cs | 14 +- src/ImageSharp/Formats/Gif/GifEncoder.cs | 4 +- src/ImageSharp/Formats/Gif/GifEncoderCore.cs | 18 +- src/ImageSharp/Formats/Gif/LzwDecoder.cs | 27 +- src/ImageSharp/Formats/Gif/LzwEncoder.cs | 19 +- .../Jpeg/GolangPort/JpegEncoderCore.cs | 4 +- src/ImageSharp/Formats/Png/PngChunk.cs | 4 +- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 19 +- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 14 +- src/ImageSharp/ImageSharp.csproj | 236 +++++++++--------- .../Memory/ArrayPoolMemoryManager.cs | 8 +- src/ImageSharp/Memory/PixelDataPool{T}.cs | 59 ----- 15 files changed, 258 insertions(+), 326 deletions(-) delete mode 100644 src/ImageSharp/Memory/PixelDataPool{T}.cs diff --git a/ImageSharp.sln.DotSettings b/ImageSharp.sln.DotSettings index 1839bf2f8d..435aad73bf 100644 --- a/ImageSharp.sln.DotSettings +++ b/ImageSharp.sln.DotSettings @@ -38,10 +38,15 @@ NEXT_LINE_SHIFTED_2 1 1 + False + False False + NEVER False False + NEVER False + ALWAYS False True ON_SINGLE_LINE @@ -370,8 +375,13 @@ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> <Policy Inspect="True" Prefix="T" Suffix="" Style="AaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + True + True + True + True True True + True True True \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs index f3e3d0397f..d5bc401074 100644 --- a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs @@ -78,8 +78,6 @@ namespace SixLabors.ImageSharp.Drawing.Processors return; // no effect inside image; } - ArrayPool arrayPool = ArrayPool.Shared; - int maxIntersections = region.MaxIntersections; float subpixelCount = 4; @@ -100,101 +98,94 @@ namespace SixLabors.ImageSharp.Drawing.Processors using (BrushApplicator applicator = this.Brush.CreateApplicator(source, rect, this.Options)) { - float[] buffer = arrayPool.Rent(maxIntersections); int scanlineWidth = maxX - minX; + using (var buffer = source.MemoryManager.Allocate(maxIntersections)) using (var scanline = source.MemoryManager.Allocate(scanlineWidth)) { - try + bool scanlineDirty = true; + for (int y = minY; y < maxY; y++) { - bool scanlineDirty = true; - for (int y = minY; y < maxY; y++) + if (scanlineDirty) { - if (scanlineDirty) + // clear the buffer + for (int x = 0; x < scanlineWidth; x++) { - // clear the buffer - for (int x = 0; x < scanlineWidth; x++) - { - scanline[x] = 0; - } - - scanlineDirty = false; + scanline[x] = 0; } - float subpixelFraction = 1f / subpixelCount; - float subpixelFractionPoint = subpixelFraction / subpixelCount; - for (float subPixel = (float)y; subPixel < y + 1; subPixel += subpixelFraction) - { - int pointsFound = region.Scan(subPixel + offset, buffer, 0); - if (pointsFound == 0) - { - // nothing on this line skip - continue; - } + scanlineDirty = false; + } - QuickSort(new Span(buffer, 0, pointsFound)); + float subpixelFraction = 1f / subpixelCount; + float subpixelFractionPoint = subpixelFraction / subpixelCount; + for (float subPixel = (float)y; subPixel < y + 1; subPixel += subpixelFraction) + { + int pointsFound = region.Scan(subPixel + offset, buffer.Array, 0); + if (pointsFound == 0) + { + // nothing on this line skip + continue; + } - for (int point = 0; point < pointsFound; point += 2) - { - // points will be paired up - float scanStart = buffer[point] - minX; - float scanEnd = buffer[point + 1] - minX; - int startX = (int)MathF.Floor(scanStart + offset); - int endX = (int)MathF.Floor(scanEnd + offset); + QuickSort(new Span(buffer.Array, 0, pointsFound)); - if (startX >= 0 && startX < scanline.Length) - { - for (float x = scanStart; x < startX + 1; x += subpixelFraction) - { - scanline[startX] += subpixelFractionPoint; - scanlineDirty = true; - } - } + for (int point = 0; point < pointsFound; point += 2) + { + // points will be paired up + float scanStart = buffer[point] - minX; + float scanEnd = buffer[point + 1] - minX; + int startX = (int)MathF.Floor(scanStart + offset); + int endX = (int)MathF.Floor(scanEnd + offset); - if (endX >= 0 && endX < scanline.Length) + if (startX >= 0 && startX < scanline.Length) + { + for (float x = scanStart; x < startX + 1; x += subpixelFraction) { - for (float x = endX; x < scanEnd; x += subpixelFraction) - { - scanline[endX] += subpixelFractionPoint; - scanlineDirty = true; - } + scanline[startX] += subpixelFractionPoint; + scanlineDirty = true; } + } - int nextX = startX + 1; - endX = Math.Min(endX, scanline.Length); // reduce to end to the right edge - nextX = Math.Max(nextX, 0); - for (int x = nextX; x < endX; x++) + if (endX >= 0 && endX < scanline.Length) + { + for (float x = endX; x < scanEnd; x += subpixelFraction) { - scanline[x] += subpixelFraction; + scanline[endX] += subpixelFractionPoint; scanlineDirty = true; } } + + int nextX = startX + 1; + endX = Math.Min(endX, scanline.Length); // reduce to end to the right edge + nextX = Math.Max(nextX, 0); + for (int x = nextX; x < endX; x++) + { + scanline[x] += subpixelFraction; + scanlineDirty = true; + } } + } - if (scanlineDirty) + if (scanlineDirty) + { + if (!this.Options.Antialias) { - if (!this.Options.Antialias) + for (int x = 0; x < scanlineWidth; x++) { - for (int x = 0; x < scanlineWidth; x++) + if (scanline[x] >= 0.5) + { + scanline[x] = 1; + } + else { - if (scanline[x] >= 0.5) - { - scanline[x] = 1; - } - else - { - scanline[x] = 0; - } + scanline[x] = 0; } } - - applicator.Apply(scanline, minX, y); } + + applicator.Apply(scanline, minX, y); } } - finally - { - arrayPool.Return(buffer); - } } } } diff --git a/src/ImageSharp/Common/Extensions/StreamExtensions.cs b/src/ImageSharp/Common/Extensions/StreamExtensions.cs index b717abab1c..7a9a34ac1a 100644 --- a/src/ImageSharp/Common/Extensions/StreamExtensions.cs +++ b/src/ImageSharp/Common/Extensions/StreamExtensions.cs @@ -29,23 +29,16 @@ namespace SixLabors.ImageSharp } else { - byte[] foo = ArrayPool.Shared.Rent(count); - try + byte[] foo = new byte[count]; + while (count > 0) { - while (count > 0) + int bytesRead = stream.Read(foo, 0, count); + if (bytesRead == 0) { - int bytesRead = stream.Read(foo, 0, count); - if (bytesRead == 0) - { - break; - } - - count -= bytesRead; + break; } - } - finally - { - ArrayPool.Shared.Return(foo); + + count -= bytesRead; } } } diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index 51a598bc0c..d70d8f29c2 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -290,18 +290,12 @@ namespace SixLabors.ImageSharp.Formats.Gif continue; } - byte[] commentsBuffer = ArrayPool.Shared.Rent(length); - - try + using (Buffer commentsBuffer = this.configuration.MemoryManager.Allocate(length)) { - this.currentStream.Read(commentsBuffer, 0, length); - string comments = this.TextEncoding.GetString(commentsBuffer, 0, length); + this.currentStream.Read(commentsBuffer.Array, 0, length); + string comments = this.TextEncoding.GetString(commentsBuffer.Array, 0, length); this.metaData.Properties.Add(new ImageProperty(GifConstants.Comments, comments)); } - finally - { - ArrayPool.Shared.Return(commentsBuffer); - } } } @@ -348,7 +342,7 @@ namespace SixLabors.ImageSharp.Formats.Gif private void ReadFrameIndices(GifImageDescriptor imageDescriptor, Span indices) { int dataSize = this.currentStream.ReadByte(); - using (var lzwDecoder = new LzwDecoder(this.currentStream)) + using (var lzwDecoder = new LzwDecoder(this.configuration.MemoryManager, this.currentStream)) { lzwDecoder.DecodePixels(imageDescriptor.Width, imageDescriptor.Height, dataSize, indices); } diff --git a/src/ImageSharp/Formats/Gif/GifEncoder.cs b/src/ImageSharp/Formats/Gif/GifEncoder.cs index ccf46a17d6..b548098be3 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoder.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoder.cs @@ -5,6 +5,8 @@ using System; using System.Collections.Generic; using System.IO; using System.Text; + +using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Quantizers; @@ -44,7 +46,7 @@ namespace SixLabors.ImageSharp.Formats.Gif public void Encode(Image image, Stream stream) where TPixel : struct, IPixel { - var encoder = new GifEncoderCore(this); + var encoder = new GifEncoderCore(image.GetConfiguration().MemoryManager, this); encoder.Encode(image, stream); } } diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs index 41c8e944d8..43d48605c4 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs @@ -7,6 +7,7 @@ using System.IO; using System.Linq; using System.Text; using SixLabors.ImageSharp.IO; +using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.MetaData; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Quantizers; @@ -18,6 +19,8 @@ namespace SixLabors.ImageSharp.Formats.Gif /// internal sealed class GifEncoderCore { + private readonly MemoryManager memoryManager; + /// /// The temp buffer used to reduce allocations. /// @@ -61,9 +64,11 @@ namespace SixLabors.ImageSharp.Formats.Gif /// /// Initializes a new instance of the class. /// + /// The to use for buffer allocations. /// The options for the encoder. - public GifEncoderCore(IGifEncoderOptions options) + public GifEncoderCore(MemoryManager memoryManager, IGifEncoderOptions options) { + this.memoryManager = memoryManager; this.textEncoding = options.TextEncoding ?? GifConstants.DefaultEncoding; this.quantizer = options.Quantizer; @@ -350,9 +355,8 @@ namespace SixLabors.ImageSharp.Formats.Gif // Get max colors for bit depth. int colorTableLength = (int)Math.Pow(2, this.bitDepth) * 3; - byte[] colorTable = ArrayPool.Shared.Rent(colorTableLength); var rgb = default(Rgb24); - try + using (Buffer colorTable = this.memoryManager.Allocate(colorTableLength)) { for (int i = 0; i < pixelCount; i++) { @@ -363,11 +367,7 @@ namespace SixLabors.ImageSharp.Formats.Gif colorTable[offset + 2] = rgb.B; } - writer.Write(colorTable, 0, colorTableLength); - } - finally - { - ArrayPool.Shared.Return(colorTable); + writer.Write(colorTable.Array, 0, colorTableLength); } } @@ -380,7 +380,7 @@ namespace SixLabors.ImageSharp.Formats.Gif private void WriteImageData(QuantizedImage image, EndianBinaryWriter writer) where TPixel : struct, IPixel { - using (var encoder = new LzwEncoder(image.Pixels, (byte)this.bitDepth)) + using (var encoder = new LzwEncoder(this.memoryManager, image.Pixels, (byte)this.bitDepth)) { encoder.Encode(writer.BaseStream); } diff --git a/src/ImageSharp/Formats/Gif/LzwDecoder.cs b/src/ImageSharp/Formats/Gif/LzwDecoder.cs index 3284dad657..b28857e573 100644 --- a/src/ImageSharp/Formats/Gif/LzwDecoder.cs +++ b/src/ImageSharp/Formats/Gif/LzwDecoder.cs @@ -5,6 +5,8 @@ using System; using System.Buffers; using System.IO; +using SixLabors.ImageSharp.Memory; + namespace SixLabors.ImageSharp.Formats.Gif { /// @@ -30,17 +32,17 @@ namespace SixLabors.ImageSharp.Formats.Gif /// /// The prefix buffer. /// - private readonly int[] prefix; + private readonly Buffer prefix; /// /// The suffix buffer. /// - private readonly int[] suffix; + private readonly Buffer suffix; /// /// The pixel stack buffer. /// - private readonly int[] pixelStack; + private readonly Buffer pixelStack; /// /// A value indicating whether this instance of the given entity has been disposed. @@ -59,21 +61,18 @@ namespace SixLabors.ImageSharp.Formats.Gif /// Initializes a new instance of the class /// and sets the stream, where the compressed data should be read from. /// + /// The to use for buffer allocations. /// The stream to read from. /// is null. - public LzwDecoder(Stream stream) + public LzwDecoder(MemoryManager memoryManager, Stream stream) { Guard.NotNull(stream, nameof(stream)); this.stream = stream; - this.prefix = ArrayPool.Shared.Rent(MaxStackSize); - this.suffix = ArrayPool.Shared.Rent(MaxStackSize); - this.pixelStack = ArrayPool.Shared.Rent(MaxStackSize + 1); - - Array.Clear(this.prefix, 0, MaxStackSize); - Array.Clear(this.suffix, 0, MaxStackSize); - Array.Clear(this.pixelStack, 0, MaxStackSize + 1); + this.prefix = memoryManager.Allocate(MaxStackSize, true); + this.suffix = memoryManager.Allocate(MaxStackSize, true); + this.pixelStack = memoryManager.Allocate(MaxStackSize + 1, true); } /// @@ -262,9 +261,9 @@ namespace SixLabors.ImageSharp.Formats.Gif if (disposing) { - ArrayPool.Shared.Return(this.prefix); - ArrayPool.Shared.Return(this.suffix); - ArrayPool.Shared.Return(this.pixelStack); + this.prefix?.Dispose(); + this.suffix?.Dispose(); + this.pixelStack?.Dispose(); } this.isDisposed = true; diff --git a/src/ImageSharp/Formats/Gif/LzwEncoder.cs b/src/ImageSharp/Formats/Gif/LzwEncoder.cs index b7bfd0fd25..2ecd229b5f 100644 --- a/src/ImageSharp/Formats/Gif/LzwEncoder.cs +++ b/src/ImageSharp/Formats/Gif/LzwEncoder.cs @@ -5,6 +5,8 @@ using System; using System.Buffers; using System.IO; +using SixLabors.ImageSharp.Memory; + namespace SixLabors.ImageSharp.Formats.Gif { /// @@ -69,12 +71,12 @@ namespace SixLabors.ImageSharp.Formats.Gif /// /// The hash table. /// - private readonly int[] hashTable; + private readonly Buffer hashTable; /// /// The code table. /// - private readonly int[] codeTable; + private readonly Buffer codeTable; /// /// Define the storage for the packet accumulator. @@ -189,17 +191,16 @@ namespace SixLabors.ImageSharp.Formats.Gif /// /// Initializes a new instance of the class. /// + /// The to use for buffer allocations. /// The array of indexed pixels. /// The color depth in bits. - public LzwEncoder(byte[] indexedPixels, int colorDepth) + public LzwEncoder(MemoryManager memoryManager, byte[] indexedPixels, int colorDepth) { this.pixelArray = indexedPixels; this.initialCodeSize = Math.Max(2, colorDepth); - this.hashTable = ArrayPool.Shared.Rent(HashSize); - this.codeTable = ArrayPool.Shared.Rent(HashSize); - Array.Clear(this.hashTable, 0, HashSize); - Array.Clear(this.codeTable, 0, HashSize); + this.hashTable = memoryManager.Allocate(HashSize, true); + this.codeTable = memoryManager.Allocate(HashSize, true); } /// @@ -483,8 +484,8 @@ namespace SixLabors.ImageSharp.Formats.Gif if (disposing) { - ArrayPool.Shared.Return(this.hashTable); - ArrayPool.Shared.Return(this.codeTable); + this.hashTable?.Dispose(); + this.codeTable?.Dispose(); } this.isDisposed = true; diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs index 2912a87199..4a9ddf3536 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs @@ -8,6 +8,7 @@ using SixLabors.ImageSharp.Formats.Jpeg.Common; using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components; using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder; using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Utils; +using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.MetaData.Profiles.Exif; using SixLabors.ImageSharp.MetaData.Profiles.Icc; using SixLabors.ImageSharp.PixelFormats; @@ -657,14 +658,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort // Loop through and collect the tables as one array. // This allows us to reduce the number of writes to the stream. int dqtCount = (QuantizationTableCount * Block8x8F.Size) + QuantizationTableCount; - byte[] dqt = ArrayPool.Shared.Rent(dqtCount); + byte[] dqt = new byte[dqtCount]; int offset = 0; WriteDataToDqt(dqt, ref offset, QuantIndex.Luminance, ref this.luminanceQuantTable); WriteDataToDqt(dqt, ref offset, QuantIndex.Chrominance, ref this.chrominanceQuantTable); this.outputStream.Write(dqt, 0, dqtCount); - ArrayPool.Shared.Return(dqt); } /// diff --git a/src/ImageSharp/Formats/Png/PngChunk.cs b/src/ImageSharp/Formats/Png/PngChunk.cs index f90def5b38..7412fdfcd3 100644 --- a/src/ImageSharp/Formats/Png/PngChunk.cs +++ b/src/ImageSharp/Formats/Png/PngChunk.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.Memory; + namespace SixLabors.ImageSharp.Formats.Png { /// @@ -25,7 +27,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// Gets or sets the data bytes appropriate to the chunk type, if any. /// This field can be of zero length. /// - public byte[] Data { get; set; } + public Buffer Data { get; set; } /// /// Gets or sets a CRC (Cyclic Redundancy Check) calculated on the preceding bytes in the chunk, diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 7e354b58bc..2a5f5fabe2 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -223,11 +223,11 @@ namespace SixLabors.ImageSharp.Formats.Png switch (currentChunk.Type) { case PngChunkTypes.Header: - this.ReadHeaderChunk(currentChunk.Data); + this.ReadHeaderChunk(currentChunk.Data.Array); this.ValidateHeader(); break; case PngChunkTypes.Physical: - this.ReadPhysicalChunk(metadata, currentChunk.Data); + this.ReadPhysicalChunk(metadata, currentChunk.Data.Array); break; case PngChunkTypes.Data: if (image == null) @@ -241,17 +241,17 @@ namespace SixLabors.ImageSharp.Formats.Png break; case PngChunkTypes.Palette: byte[] pal = new byte[currentChunk.Length]; - Buffer.BlockCopy(currentChunk.Data, 0, pal, 0, currentChunk.Length); + Buffer.BlockCopy(currentChunk.Data.Array, 0, pal, 0, currentChunk.Length); this.palette = pal; break; case PngChunkTypes.PaletteAlpha: byte[] alpha = new byte[currentChunk.Length]; - Buffer.BlockCopy(currentChunk.Data, 0, alpha, 0, currentChunk.Length); + Buffer.BlockCopy(currentChunk.Data.Array, 0, alpha, 0, currentChunk.Length); this.paletteAlpha = alpha; this.AssignTransparentMarkers(alpha); break; case PngChunkTypes.Text: - this.ReadTextChunk(metadata, currentChunk.Data, currentChunk.Length); + this.ReadTextChunk(metadata, currentChunk.Data.Array, currentChunk.Length); break; case PngChunkTypes.End: this.isEndChunkReached = true; @@ -263,7 +263,8 @@ namespace SixLabors.ImageSharp.Formats.Png // Data is rented in ReadChunkData() if (currentChunk.Data != null) { - ArrayPool.Shared.Return(currentChunk.Data); + currentChunk.Data.Dispose(); + currentChunk.Data = null; } } } @@ -1173,7 +1174,7 @@ namespace SixLabors.ImageSharp.Formats.Png this.crc.Reset(); this.crc.Update(this.chunkTypeBuffer); - this.crc.Update(chunk.Data, 0, chunk.Length); + this.crc.Update(chunk.Data.Array, 0, chunk.Length); if (this.crc.Value != chunk.Crc && IsCriticalChunk(chunk)) { @@ -1188,8 +1189,8 @@ namespace SixLabors.ImageSharp.Formats.Png private void ReadChunkData(PngChunk chunk) { // We rent the buffer here to return it afterwards in Decode() - chunk.Data = ArrayPool.Shared.Rent(chunk.Length); - this.currentStream.Read(chunk.Data, 0, chunk.Length); + chunk.Data = this.configuration.MemoryManager.Allocate(chunk.Length); + this.currentStream.Read(chunk.Data.Array, 0, chunk.Length); } /// diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 327ce9fdf3..d6adaae428 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -521,11 +521,10 @@ namespace SixLabors.ImageSharp.Formats.Png // Get max colors for bit depth. int colorTableLength = (int)Math.Pow(2, header.BitDepth) * 3; - byte[] colorTable = ArrayPool.Shared.Rent(colorTableLength); - byte[] alphaTable = ArrayPool.Shared.Rent(pixelCount); var rgba = default(Rgba32); bool anyAlpha = false; - try + using (Buffer colorTable = this.memoryManager.Allocate(colorTableLength)) + using (Buffer alphaTable = this.memoryManager.Allocate(pixelCount)) { for (byte i = 0; i < pixelCount; i++) { @@ -550,19 +549,14 @@ namespace SixLabors.ImageSharp.Formats.Png } } - this.WriteChunk(stream, PngChunkTypes.Palette, colorTable, 0, colorTableLength); + this.WriteChunk(stream, PngChunkTypes.Palette, colorTable.Array, 0, colorTableLength); // Write the transparency data if (anyAlpha) { - this.WriteChunk(stream, PngChunkTypes.PaletteAlpha, alphaTable, 0, pixelCount); + this.WriteChunk(stream, PngChunkTypes.PaletteAlpha, alphaTable.Array, 0, pixelCount); } } - finally - { - ArrayPool.Shared.Return(colorTable); - ArrayPool.Shared.Return(alphaTable); - } return quantized; } diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index 8c22237cf7..1d22e59cb2 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -1,120 +1,120 @@  - - A cross-platform library for the processing of image files; written in C# - SixLabors.ImageSharp - $(packageversion) - 0.0.1 - Six Labors and contributors - netstandard1.1;netstandard1.3;netstandard2.0 - true - true - SixLabors.ImageSharp - SixLabors.ImageSharp - Image Resize Crop Gif Jpg Jpeg Bitmap Png Core - https://raw.githubusercontent.com/SixLabors/ImageSharp/master/build/icons/imagesharp-logo-128.png - https://github.com/SixLabors/ImageSharp - http://www.apache.org/licenses/LICENSE-2.0 - git - https://github.com/SixLabors/ImageSharp - false - false - false - false - false - false - false - false - false - full - portable - True - IOperation - - - - - - - - - All - - - - - - - - - - - - - ..\..\ImageSharp.ruleset - SixLabors.ImageSharp - - - true - - - - TextTemplatingFileGenerator - Block8x8F.Generated.cs - - - TextTemplatingFileGenerator - Block8x8F.Generated.cs - - - TextTemplatingFileGenerator - PixelOperations{TPixel}.Generated.cs - - - TextTemplatingFileGenerator - Rgba32.PixelOperations.Generated.cs - - - PorterDuffFunctions.Generated.cs - TextTemplatingFileGenerator - - - DefaultPixelBlenders.Generated.cs - TextTemplatingFileGenerator - - - - - - - - True - True - Block8x8F.Generated.tt - - - True - True - Block8x8F.Generated.tt - - - True - True - PixelOperations{TPixel}.Generated.tt - - - True - True - Rgba32.PixelOperations.Generated.tt - - - True - True - DefaultPixelBlenders.Generated.tt - - - True - True - PorterDuffFunctions.Generated.tt - - + + A cross-platform library for the processing of image files; written in C# + SixLabors.ImageSharp + $(packageversion) + 0.0.1 + Six Labors and contributors + netstandard1.1;netstandard1.3;netstandard2.0 + true + true + SixLabors.ImageSharp + SixLabors.ImageSharp + Image Resize Crop Gif Jpg Jpeg Bitmap Png Core + https://raw.githubusercontent.com/SixLabors/ImageSharp/master/build/icons/imagesharp-logo-128.png + https://github.com/SixLabors/ImageSharp + http://www.apache.org/licenses/LICENSE-2.0 + git + https://github.com/SixLabors/ImageSharp + false + false + false + false + false + false + false + false + false + full + portable + True + IOperation + + + + + + + + + All + + + + + + + + + + + + + ..\..\ImageSharp.ruleset + SixLabors.ImageSharp + + + true + + + + TextTemplatingFileGenerator + Block8x8F.Generated.cs + + + TextTemplatingFileGenerator + Block8x8F.Generated.cs + + + TextTemplatingFileGenerator + PixelOperations{TPixel}.Generated.cs + + + TextTemplatingFileGenerator + Rgba32.PixelOperations.Generated.cs + + + PorterDuffFunctions.Generated.cs + TextTemplatingFileGenerator + + + DefaultPixelBlenders.Generated.cs + TextTemplatingFileGenerator + + + + + + + + True + True + Block8x8F.Generated.tt + + + True + True + Block8x8F.Generated.tt + + + True + True + PixelOperations{TPixel}.Generated.tt + + + True + True + Rgba32.PixelOperations.Generated.tt + + + True + True + DefaultPixelBlenders.Generated.tt + + + True + True + PorterDuffFunctions.Generated.tt + + \ No newline at end of file diff --git a/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs b/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs index d83e3ca3e5..3e5628c75e 100644 --- a/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs +++ b/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs @@ -1,4 +1,5 @@ -using System.Buffers; +using System; +using System.Buffers; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -36,7 +37,10 @@ namespace SixLabors.ImageSharp.Memory if (this.minSizeBytes > 0 && bufferSizeInBytes < this.minSizeBytes) { - return new Buffer(new T[itemCount], itemCount); + // Minimum size set to 8 bytes to get past a misbehaving test + // (otherwise PngDecoderTests.Decode_IncorrectCRCForNonCriticalChunk_ExceptionIsThrown fails for the wrong reason) + // TODO: Remove this once the test is fixed + return new Buffer(new T[Math.Max(itemCount, 8)], itemCount); } byte[] byteBuffer = this.pool.Rent(bufferSizeInBytes); diff --git a/src/ImageSharp/Memory/PixelDataPool{T}.cs b/src/ImageSharp/Memory/PixelDataPool{T}.cs deleted file mode 100644 index 6f4cef707a..0000000000 --- a/src/ImageSharp/Memory/PixelDataPool{T}.cs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Buffers; -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp.Memory -{ - /// - /// Provides a resource pool that enables reusing instances of value type arrays for image data . - /// - /// The value type. - internal class PixelDataPool - where T : struct - { - /// - /// The which is not kept clean. - /// - private static readonly ArrayPool ArrayPool = ArrayPool.Create(CalculateMaxArrayLength(), 50); - - /// - /// Rents the pixel array from the pool. - /// - /// The minimum length of the array to return. - /// The - public static T[] Rent(int minimumLength) - { - return ArrayPool.Rent(minimumLength); - } - - /// - /// Returns the rented pixel array back to the pool. - /// - /// The array to return to the buffer pool. - public static void Return(T[] array) - { - ArrayPool.Return(array); - } - - /// - /// Heuristically calculates a reasonable maxArrayLength value for the backing . - /// - /// The maxArrayLength value - internal static int CalculateMaxArrayLength() - { - // ReSharper disable once SuspiciousTypeConversion.Global - if (default(T) is IPixel) - { - const int MaximumExpectedImageSize = 16384 * 16384; - return MaximumExpectedImageSize; - } - else - { - const int MaxArrayLength = 1024 * 1024; // Match default pool. - return MaxArrayLength; - } - } - } -} \ No newline at end of file From 4f9062ee670ea0d44f80f7349849050373c53d30 Mon Sep 17 00:00:00 2001 From: Lauri Kotilainen Date: Fri, 12 Jan 2018 20:20:02 +0200 Subject: [PATCH 012/151] - Remove ArrayPool from Huffman tree --- .../Components/Decoder/OrigHuffmanTree.cs | 46 ++++--------------- .../Jpeg/GolangPort/OrigJpegDecoderCore.cs | 5 -- 2 files changed, 10 insertions(+), 41 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigHuffmanTree.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigHuffmanTree.cs index 4c97d57415..e96421a2d6 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigHuffmanTree.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigHuffmanTree.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder /// /// Represents a Huffman tree /// - internal struct OrigHuffmanTree : IDisposable + internal struct OrigHuffmanTree { /// /// The index of the AC table row @@ -91,12 +91,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder /// public int[] Indices; - private static readonly ArrayPool IntPool256 = ArrayPool.Create(MaxNCodes, 50); - - private static readonly ArrayPool BytePool256 = ArrayPool.Create(MaxNCodes, 50); - - private static readonly ArrayPool CodesPool16 = ArrayPool.Create(MaxCodeLength, 50); - /// /// Creates and initializes an array of instances of size /// @@ -115,18 +109,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder return result; } - /// - /// Disposes the underlying buffers - /// - public void Dispose() - { - IntPool256.Return(this.Lut, true); - IntPool256.Return(this.Values, true); - CodesPool16.Return(this.MinCodes, true); - CodesPool16.Return(this.MaxCodes, true); - CodesPool16.Return(this.Indices, true); - } - /// /// Internal part of the DHT processor, whatever does it mean /// @@ -166,20 +148,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder throw new ImageFormatException("DHT has wrong length"); } - byte[] values = null; - try - { - values = BytePool256.Rent(MaxNCodes); - inputProcessor.ReadFull(values, 0, this.Length); + byte[] values = new byte[MaxNCodes]; + inputProcessor.ReadFull(values, 0, this.Length); - for (int i = 0; i < values.Length; i++) - { - this.Values[i] = values[i]; - } - } - finally + for (int i = 0; i < values.Length; i++) { - BytePool256.Return(values, true); + this.Values[i] = values[i]; } // Derive the look-up table. @@ -254,11 +228,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder /// private void Init() { - this.Lut = IntPool256.Rent(MaxNCodes); - this.Values = IntPool256.Rent(MaxNCodes); - this.MinCodes = CodesPool16.Rent(MaxCodeLength); - this.MaxCodes = CodesPool16.Rent(MaxCodeLength); - this.Indices = CodesPool16.Rent(MaxCodeLength); + this.Lut = new int[MaxNCodes]; + this.Values = new int[MaxNCodes]; + this.MinCodes = new int[MaxCodeLength]; + this.MaxCodes = new int[MaxCodeLength]; + this.Indices = new int[MaxCodeLength]; } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs index 053b016e64..0a0ddeba43 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs @@ -190,11 +190,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort /// public void Dispose() { - for (int i = 0; i < this.HuffmanTrees.Length; i++) - { - this.HuffmanTrees[i].Dispose(); - } - if (this.Components != null) { foreach (OrigComponent component in this.Components) From 15521be1c3dd52233c8bad6ae8dc61688f553abb Mon Sep 17 00:00:00 2001 From: Lauri Kotilainen Date: Fri, 12 Jan 2018 21:02:37 +0200 Subject: [PATCH 013/151] - Use fixed buffers in Huffman table --- .../Components/Decoder/InputProcessor.cs | 4 +- .../Components/Decoder/OrigHuffmanTree.cs | 154 ++++++++++-------- 2 files changed, 85 insertions(+), 73 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs index a7a5fcd986..5aa0fa3097 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs @@ -220,7 +220,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder if (this.LastErrorCode == OrigDecoderErrorCode.NoError) { int lutIndex = (this.Bits.Accumulator >> (this.Bits.UnreadBits - OrigHuffmanTree.LutSizeLog2)) & 0xFF; - int v = huffmanTree.Lut[lutIndex]; + int v = huffmanTree.ReadLut(lutIndex); if (v != 0) { @@ -259,7 +259,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder this.Bits.UnreadBits--; this.Bits.Mask >>= 1; - if (code <= huffmanTree.MaxCodes[i]) + if (code <= huffmanTree.GetMaxCode(i)) { result = huffmanTree.GetValue(code, i); return this.LastErrorCode = OrigDecoderErrorCode.NoError; diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigHuffmanTree.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigHuffmanTree.cs index e96421a2d6..f773a12dfa 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigHuffmanTree.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigHuffmanTree.cs @@ -3,13 +3,14 @@ using System; using System.Buffers; +using System.Runtime.CompilerServices; namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder { /// /// Represents a Huffman tree /// - internal struct OrigHuffmanTree + internal unsafe struct OrigHuffmanTree { /// /// The index of the AC table row @@ -67,29 +68,29 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder /// are 1 plus the code length, or 0 if the value is too large to fit in /// lutSize bits. /// - public int[] Lut; + public fixed int Lut[MaxNCodes]; /// /// Gets the the decoded values, sorted by their encoding. /// - public int[] Values; + public fixed int Values[MaxNCodes]; /// /// Gets the array of minimum codes. /// MinCodes[i] is the minimum code of length i, or -1 if there are no codes of that length. /// - public int[] MinCodes; + public fixed int MinCodes[MaxCodeLength]; /// /// Gets the array of maximum codes. /// MaxCodes[i] is the maximum code of length i, or -1 if there are no codes of that length. /// - public int[] MaxCodes; + public fixed int MaxCodes[MaxCodeLength]; /// /// Gets the array of indices. Indices[i] is the index into Values of MinCodes[i]. /// - public int[] Indices; + public fixed int Indices[MaxCodeLength]; /// /// Creates and initializes an array of instances of size @@ -97,16 +98,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder /// An array of instances representing the Huffman tables public static OrigHuffmanTree[] CreateHuffmanTrees() { - OrigHuffmanTree[] result = new OrigHuffmanTree[NumberOfTrees]; - for (int i = 0; i < MaxTc + 1; i++) - { - for (int j = 0; j < MaxTh + 1; j++) - { - result[(i * ThRowSize) + j].Init(); - } - } - - return result; + return new OrigHuffmanTree[NumberOfTrees]; } /// @@ -151,64 +143,73 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder byte[] values = new byte[MaxNCodes]; inputProcessor.ReadFull(values, 0, this.Length); - for (int i = 0; i < values.Length; i++) + fixed (int* valuesPtr = this.Values) + fixed (int* lutPtr = this.Lut) { - this.Values[i] = values[i]; - } - - // Derive the look-up table. - for (int i = 0; i < this.Lut.Length; i++) - { - this.Lut[i] = 0; - } + for (int i = 0; i < values.Length; i++) + { + valuesPtr[i] = values[i]; + } - int x = 0, code = 0; + // Derive the look-up table. + for (int i = 0; i < MaxNCodes; i++) + { + lutPtr[i] = 0; + } - for (int i = 0; i < LutSizeLog2; i++) - { - code <<= 1; + int x = 0, code = 0; - for (int j = 0; j < ncodes[i]; j++) + for (int i = 0; i < LutSizeLog2; i++) { - // The codeLength is 1+i, so shift code by 8-(1+i) to - // calculate the high bits for every 8-bit sequence - // whose codeLength's high bits matches code. - // The high 8 bits of lutValue are the encoded value. - // The low 8 bits are 1 plus the codeLength. - int base2 = code << (7 - i); - int lutValue = (this.Values[x] << 8) | (2 + i); - - for (int k = 0; k < 1 << (7 - i); k++) + code <<= 1; + + for (int j = 0; j < ncodes[i]; j++) { - this.Lut[base2 | k] = lutValue; + // The codeLength is 1+i, so shift code by 8-(1+i) to + // calculate the high bits for every 8-bit sequence + // whose codeLength's high bits matches code. + // The high 8 bits of lutValue are the encoded value. + // The low 8 bits are 1 plus the codeLength. + int base2 = code << (7 - i); + int lutValue = (valuesPtr[x] << 8) | (2 + i); + + for (int k = 0; k < 1 << (7 - i); k++) + { + lutPtr[base2 | k] = lutValue; + } + + code++; + x++; } - - code++; - x++; } } - // Derive minCodes, maxCodes, and indices. - int c = 0, index = 0; - for (int i = 0; i < ncodes.Length; i++) + fixed (int* minCodesPtr = this.MinCodes) + fixed (int* maxCodesPtr = this.MaxCodes) + fixed (int* indicesPtr = this.Indices) { - int nc = ncodes[i]; - if (nc == 0) - { - this.MinCodes[i] = -1; - this.MaxCodes[i] = -1; - this.Indices[i] = -1; - } - else + // Derive minCodes, maxCodes, and indices. + int c = 0, index = 0; + for (int i = 0; i < ncodes.Length; i++) { - this.MinCodes[i] = c; - this.MaxCodes[i] = c + nc - 1; - this.Indices[i] = index; - c += nc; - index += nc; - } + int nc = ncodes[i]; + if (nc == 0) + { + minCodesPtr[i] = -1; + maxCodesPtr[i] = -1; + indicesPtr[i] = -1; + } + else + { + minCodesPtr[i] = c; + maxCodesPtr[i] = c + nc - 1; + indicesPtr[i] = index; + c += nc; + index += nc; + } - c <<= 1; + c <<= 1; + } } } @@ -220,19 +221,30 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder /// The value public int GetValue(int code, int codeLength) { - return this.Values[this.Indices[codeLength] + code - this.MinCodes[codeLength]]; + fixed (int* valuesPtr = this.Values) + fixed (int* minCodesPtr = this.MinCodes) + fixed (int* indicesPtr = this.Indices) + { + return valuesPtr[indicesPtr[codeLength] + code - minCodesPtr[codeLength]]; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int ReadLut(int index) + { + fixed (int* lutPtr = this.Lut) + { + return lutPtr[index]; + } } - /// - /// Initializes the Huffman tree - /// - private void Init() + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int GetMaxCode(int index) { - this.Lut = new int[MaxNCodes]; - this.Values = new int[MaxNCodes]; - this.MinCodes = new int[MaxCodeLength]; - this.MaxCodes = new int[MaxCodeLength]; - this.Indices = new int[MaxCodeLength]; + fixed (int* maxCodesPtr = this.MaxCodes) + { + return maxCodesPtr[index]; + } } } } \ No newline at end of file From faa8b23d456143a15f021e3f73f6d62f58288d7a Mon Sep 17 00:00:00 2001 From: Lauri Kotilainen Date: Sat, 13 Jan 2018 13:48:05 +0200 Subject: [PATCH 014/151] - Use memory manager in Bytes --- .../GolangPort/Components/Decoder/Bytes.cs | 26 ++++++++----------- .../Components/Decoder/InputProcessor.cs | 11 +++++--- .../Jpeg/GolangPort/OrigJpegDecoderCore.cs | 2 +- 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bytes.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bytes.cs index 38507d58c8..7c1cd72061 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bytes.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bytes.cs @@ -6,6 +6,8 @@ using System.Buffers; using System.IO; using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Memory; + namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder { /// @@ -25,12 +27,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder /// buffer[i:j] are the buffered bytes read from the underlying /// stream that haven't yet been passed further on. /// - public byte[] Buffer; + public Buffer Buffer; /// /// Values of converted to -s /// - public int[] BufferAsInt; + public Buffer BufferAsInt; /// /// Start of bytes read @@ -48,20 +50,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder /// public int UnreadableBytes; - private static readonly ArrayPool BytePool = ArrayPool.Create(BufferSize, 50); - - private static readonly ArrayPool IntPool = ArrayPool.Create(BufferSize, 50); - /// /// Creates a new instance of the , and initializes it's buffer. /// + /// The to use for buffer allocations. /// The bytes created - public static Bytes Create() + public static Bytes Create(MemoryManager memoryManager) { return new Bytes { - Buffer = BytePool.Rent(BufferSize), - BufferAsInt = IntPool.Rent(BufferSize) + Buffer = memoryManager.Allocate(BufferSize), + BufferAsInt = memoryManager.Allocate(BufferSize) }; } @@ -70,11 +69,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder /// public void Dispose() { - if (this.Buffer != null) - { - BytePool.Return(this.Buffer); - IntPool.Return(this.BufferAsInt); - } + this.Buffer?.Dispose(); + this.BufferAsInt?.Dispose(); this.Buffer = null; this.BufferAsInt = null; @@ -244,7 +240,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder } // Fill in the rest of the buffer. - int n = inputStream.Read(this.Buffer, this.J, this.Buffer.Length - this.J); + int n = inputStream.Read(this.Buffer.Array, this.J, this.Buffer.Length - this.J); if (n == 0) { return OrigDecoderErrorCode.UnexpectedEndOfStream; diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs index 5aa0fa3097..f065092004 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs @@ -5,6 +5,8 @@ using System; using System.IO; using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Memory; + namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder { /// @@ -26,12 +28,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder /// /// Initializes a new instance of the struct. /// + /// The to use for buffer allocations. /// The input /// Temporal buffer, same as - public InputProcessor(Stream inputStream, byte[] temp) + public InputProcessor(MemoryManager memoryManager, Stream inputStream, byte[] temp) { this.Bits = default(Bits); - this.Bytes = Bytes.Create(); + this.Bytes = Bytes.Create(memoryManager); this.InputStream = inputStream; this.Temp = temp; this.LastErrorCode = OrigDecoderErrorCode.NoError; @@ -155,13 +158,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder { if (this.Bytes.J - this.Bytes.I >= length) { - Array.Copy(this.Bytes.Buffer, this.Bytes.I, data, offset, length); + Array.Copy(this.Bytes.Buffer.Array, this.Bytes.I, data, offset, length); this.Bytes.I += length; length -= length; } else { - Array.Copy(this.Bytes.Buffer, this.Bytes.I, data, offset, this.Bytes.J - this.Bytes.I); + Array.Copy(this.Bytes.Buffer.Array, this.Bytes.I, data, offset, this.Bytes.J - this.Bytes.I); offset += this.Bytes.J - this.Bytes.I; length -= this.Bytes.J - this.Bytes.I; this.Bytes.I += this.Bytes.J - this.Bytes.I; diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs index 0a0ddeba43..2d34aee3b1 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs @@ -210,7 +210,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort { this.MetaData = new ImageMetaData(); this.InputStream = stream; - this.InputProcessor = new InputProcessor(stream, this.Temp); + this.InputProcessor = new InputProcessor(this.configuration.MemoryManager, stream, this.Temp); // Check for the Start Of Image marker. this.InputProcessor.ReadFull(this.Temp, 0, 2); From d6a208274936475f0387dfa51c767e7d0d5c9b44 Mon Sep 17 00:00:00 2001 From: Lauri Kotilainen Date: Sat, 13 Jan 2018 14:26:20 +0200 Subject: [PATCH 015/151] - Removed ArrayPool from WuQuantizer --- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 2 +- src/ImageSharp/Quantizers/Quantize.cs | 2 +- src/ImageSharp/Quantizers/WuArrayPool.cs | 34 ---- .../Quantizers/WuQuantizer{TPixel}.cs | 177 +++++++++--------- .../Quantization/QuantizedImageTests.cs | 2 +- 5 files changed, 88 insertions(+), 129 deletions(-) delete mode 100644 src/ImageSharp/Quantizers/WuArrayPool.cs diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index d6adaae428..3250270404 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -509,7 +509,7 @@ namespace SixLabors.ImageSharp.Formats.Png if (this.quantizer == null) { - this.quantizer = new WuQuantizer(); + this.quantizer = new WuQuantizer(this.memoryManager); } // Quantize the image returning a palette. This boxing is icky. diff --git a/src/ImageSharp/Quantizers/Quantize.cs b/src/ImageSharp/Quantizers/Quantize.cs index d99d4af34c..296f4192d3 100644 --- a/src/ImageSharp/Quantizers/Quantize.cs +++ b/src/ImageSharp/Quantizers/Quantize.cs @@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp switch (mode) { case Quantization.Wu: - quantizer = new WuQuantizer(); + quantizer = new WuQuantizer(source.GetMemoryManager()); break; case Quantization.Palette: diff --git a/src/ImageSharp/Quantizers/WuArrayPool.cs b/src/ImageSharp/Quantizers/WuArrayPool.cs deleted file mode 100644 index 04a70637b6..0000000000 --- a/src/ImageSharp/Quantizers/WuArrayPool.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Buffers; - -namespace SixLabors.ImageSharp.Quantizers -{ - /// - /// Provides array pooling for the . - /// This is a separate class so that the pools can be shared accross multiple generic quantizer instaces. - /// - internal static class WuArrayPool - { - /// - /// The long array pool. - /// - public static readonly ArrayPool LongPool = ArrayPool.Create(TableLength, 25); - - /// - /// The float array pool. - /// - public static readonly ArrayPool FloatPool = ArrayPool.Create(TableLength, 5); - - /// - /// The byte array pool. - /// - public static readonly ArrayPool BytePool = ArrayPool.Create(TableLength, 5); - - /// - /// The table length. Matches the calculated value in - /// - private const int TableLength = 2471625; - } -} \ No newline at end of file diff --git a/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs b/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs index cb9eb9b0e3..bd3efe3f3d 100644 --- a/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs +++ b/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Quantizers.Base; @@ -35,6 +36,8 @@ namespace SixLabors.ImageSharp.Quantizers public class WuQuantizer : QuantizerBase where TPixel : struct, IPixel { + private readonly MemoryManager memoryManager; + /// /// The index bits. /// @@ -68,37 +71,37 @@ namespace SixLabors.ImageSharp.Quantizers /// /// Moment of P(c). /// - private long[] vwt; + private Buffer vwt; /// /// Moment of r*P(c). /// - private long[] vmr; + private Buffer vmr; /// /// Moment of g*P(c). /// - private long[] vmg; + private Buffer vmg; /// /// Moment of b*P(c). /// - private long[] vmb; + private Buffer vmb; /// /// Moment of a*P(c). /// - private long[] vma; + private Buffer vma; /// /// Moment of c^2*P(c). /// - private float[] m2; + private Buffer m2; /// /// Color space tag. /// - private byte[] tag; + private Buffer tag; /// /// Maximum allowed color depth @@ -118,13 +121,14 @@ namespace SixLabors.ImageSharp.Quantizers /// /// Initializes a new instance of the class. /// + /// The to use for buffer allocations. /// /// The Wu quantizer is a two pass algorithm. The initial pass sets up the 3-D color histogram, /// the second pass quantizes a color based on the position in the histogram. /// - public WuQuantizer() - : base(false) - { + public WuQuantizer(MemoryManager memoryManager) + : base(false) { + this.memoryManager = memoryManager; } /// @@ -138,28 +142,35 @@ namespace SixLabors.ImageSharp.Quantizers try { - this.vwt = WuArrayPool.LongPool.Rent(TableLength); - this.vmr = WuArrayPool.LongPool.Rent(TableLength); - this.vmg = WuArrayPool.LongPool.Rent(TableLength); - this.vmb = WuArrayPool.LongPool.Rent(TableLength); - this.vma = WuArrayPool.LongPool.Rent(TableLength); - this.m2 = WuArrayPool.FloatPool.Rent(TableLength); - this.tag = WuArrayPool.BytePool.Rent(TableLength); + this.vwt = this.memoryManager.Allocate(TableLength, true); + this.vmr = this.memoryManager.Allocate(TableLength, true); + this.vmg = this.memoryManager.Allocate(TableLength, true); + this.vmb = this.memoryManager.Allocate(TableLength, true); + this.vma = this.memoryManager.Allocate(TableLength, true); + this.m2 = this.memoryManager.Allocate(TableLength, true); + this.tag = this.memoryManager.Allocate(TableLength, true); return base.Quantize(image, this.colors); } finally { - WuArrayPool.LongPool.Return(this.vwt, true); - WuArrayPool.LongPool.Return(this.vmr, true); - WuArrayPool.LongPool.Return(this.vmg, true); - WuArrayPool.LongPool.Return(this.vmb, true); - WuArrayPool.LongPool.Return(this.vma, true); - WuArrayPool.FloatPool.Return(this.m2, true); - WuArrayPool.BytePool.Return(this.tag, true); + this.DisposeBuffer(ref this.vwt); + this.DisposeBuffer(ref this.vmr); + this.DisposeBuffer(ref this.vmg); + this.DisposeBuffer(ref this.vmb); + this.DisposeBuffer(ref this.vma); + this.DisposeBuffer(ref this.m2); + this.DisposeBuffer(ref this.tag); } } + private void DisposeBuffer(ref Buffer buffer) + where T : struct + { + buffer?.Dispose(); + buffer = null; + } + /// protected override TPixel[] GetPalette() { @@ -170,14 +181,14 @@ namespace SixLabors.ImageSharp.Quantizers { this.Mark(ref this.colorCube[k], (byte)k); - float weight = Volume(ref this.colorCube[k], this.vwt); + float weight = Volume(ref this.colorCube[k], this.vwt.Array); if (MathF.Abs(weight) > Constants.Epsilon) { - float r = Volume(ref this.colorCube[k], this.vmr); - float g = Volume(ref this.colorCube[k], this.vmg); - float b = Volume(ref this.colorCube[k], this.vmb); - float a = Volume(ref this.colorCube[k], this.vma); + float r = Volume(ref this.colorCube[k], this.vmr.Array); + float g = Volume(ref this.colorCube[k], this.vmg.Array); + float b = Volume(ref this.colorCube[k], this.vmb.Array); + float a = Volume(ref this.colorCube[k], this.vma.Array); ref TPixel color = ref this.palette[k]; color.PackFromVector4(new Vector4(r, g, b, a) / weight / 255F); @@ -448,39 +459,37 @@ namespace SixLabors.ImageSharp.Quantizers /// private void Get3DMoments() { - long[] volume = ArrayPool.Shared.Rent(IndexCount * IndexAlphaCount); - long[] volumeR = ArrayPool.Shared.Rent(IndexCount * IndexAlphaCount); - long[] volumeG = ArrayPool.Shared.Rent(IndexCount * IndexAlphaCount); - long[] volumeB = ArrayPool.Shared.Rent(IndexCount * IndexAlphaCount); - long[] volumeA = ArrayPool.Shared.Rent(IndexCount * IndexAlphaCount); - float[] volume2 = ArrayPool.Shared.Rent(IndexCount * IndexAlphaCount); - - long[] area = ArrayPool.Shared.Rent(IndexAlphaCount); - long[] areaR = ArrayPool.Shared.Rent(IndexAlphaCount); - long[] areaG = ArrayPool.Shared.Rent(IndexAlphaCount); - long[] areaB = ArrayPool.Shared.Rent(IndexAlphaCount); - long[] areaA = ArrayPool.Shared.Rent(IndexAlphaCount); - float[] area2 = ArrayPool.Shared.Rent(IndexAlphaCount); - - try + using (Buffer volume = this.memoryManager.Allocate(IndexCount * IndexAlphaCount)) + using (Buffer volumeR = this.memoryManager.Allocate(IndexCount * IndexAlphaCount)) + using (Buffer volumeG = this.memoryManager.Allocate(IndexCount * IndexAlphaCount)) + using (Buffer volumeB = this.memoryManager.Allocate(IndexCount * IndexAlphaCount)) + using (Buffer volumeA = this.memoryManager.Allocate(IndexCount * IndexAlphaCount)) + using (Buffer volume2 = this.memoryManager.Allocate(IndexCount * IndexAlphaCount)) + + using (Buffer area = this.memoryManager.Allocate(IndexAlphaCount)) + using (Buffer areaR = this.memoryManager.Allocate(IndexAlphaCount)) + using (Buffer areaG = this.memoryManager.Allocate(IndexAlphaCount)) + using (Buffer areaB = this.memoryManager.Allocate(IndexAlphaCount)) + using (Buffer areaA = this.memoryManager.Allocate(IndexAlphaCount)) + using (Buffer area2 = this.memoryManager.Allocate(IndexAlphaCount)) { for (int r = 1; r < IndexCount; r++) { - Array.Clear(volume, 0, IndexCount * IndexAlphaCount); - Array.Clear(volumeR, 0, IndexCount * IndexAlphaCount); - Array.Clear(volumeG, 0, IndexCount * IndexAlphaCount); - Array.Clear(volumeB, 0, IndexCount * IndexAlphaCount); - Array.Clear(volumeA, 0, IndexCount * IndexAlphaCount); - Array.Clear(volume2, 0, IndexCount * IndexAlphaCount); + volume.Clear(); + volumeR.Clear(); + volumeG.Clear(); + volumeB.Clear(); + volumeA.Clear(); + volume2.Clear(); for (int g = 1; g < IndexCount; g++) { - Array.Clear(area, 0, IndexAlphaCount); - Array.Clear(areaR, 0, IndexAlphaCount); - Array.Clear(areaG, 0, IndexAlphaCount); - Array.Clear(areaB, 0, IndexAlphaCount); - Array.Clear(areaA, 0, IndexAlphaCount); - Array.Clear(area2, 0, IndexAlphaCount); + area.Clear(); + areaR.Clear(); + areaG.Clear(); + areaB.Clear(); + areaA.Clear(); + area2.Clear(); for (int b = 1; b < IndexCount; b++) { @@ -531,22 +540,6 @@ namespace SixLabors.ImageSharp.Quantizers } } } - finally - { - ArrayPool.Shared.Return(volume); - ArrayPool.Shared.Return(volumeR); - ArrayPool.Shared.Return(volumeG); - ArrayPool.Shared.Return(volumeB); - ArrayPool.Shared.Return(volumeA); - ArrayPool.Shared.Return(volume2); - - ArrayPool.Shared.Return(area); - ArrayPool.Shared.Return(areaR); - ArrayPool.Shared.Return(areaG); - ArrayPool.Shared.Return(areaB); - ArrayPool.Shared.Return(areaA); - ArrayPool.Shared.Return(area2); - } } /// @@ -556,10 +549,10 @@ namespace SixLabors.ImageSharp.Quantizers /// The . private float Variance(ref Box cube) { - float dr = Volume(ref cube, this.vmr); - float dg = Volume(ref cube, this.vmg); - float db = Volume(ref cube, this.vmb); - float da = Volume(ref cube, this.vma); + float dr = Volume(ref cube, this.vmr.Array); + float dg = Volume(ref cube, this.vmg.Array); + float db = Volume(ref cube, this.vmb.Array); + float da = Volume(ref cube, this.vma.Array); float xx = this.m2[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A1)] @@ -580,7 +573,7 @@ namespace SixLabors.ImageSharp.Quantizers + this.m2[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A0)]; var vector = new Vector4(dr, dg, db, da); - return xx - (Vector4.Dot(vector, vector) / Volume(ref cube, this.vwt)); + return xx - (Vector4.Dot(vector, vector) / Volume(ref cube, this.vwt.Array)); } /// @@ -603,22 +596,22 @@ namespace SixLabors.ImageSharp.Quantizers /// The . private float Maximize(ref Box cube, int direction, int first, int last, out int cut, float wholeR, float wholeG, float wholeB, float wholeA, float wholeW) { - long baseR = Bottom(ref cube, direction, this.vmr); - long baseG = Bottom(ref cube, direction, this.vmg); - long baseB = Bottom(ref cube, direction, this.vmb); - long baseA = Bottom(ref cube, direction, this.vma); - long baseW = Bottom(ref cube, direction, this.vwt); + long baseR = Bottom(ref cube, direction, this.vmr.Array); + long baseG = Bottom(ref cube, direction, this.vmg.Array); + long baseB = Bottom(ref cube, direction, this.vmb.Array); + long baseA = Bottom(ref cube, direction, this.vma.Array); + long baseW = Bottom(ref cube, direction, this.vwt.Array); float max = 0F; cut = -1; for (int i = first; i < last; i++) { - float halfR = baseR + Top(ref cube, direction, i, this.vmr); - float halfG = baseG + Top(ref cube, direction, i, this.vmg); - float halfB = baseB + Top(ref cube, direction, i, this.vmb); - float halfA = baseA + Top(ref cube, direction, i, this.vma); - float halfW = baseW + Top(ref cube, direction, i, this.vwt); + float halfR = baseR + Top(ref cube, direction, i, this.vmr.Array); + float halfG = baseG + Top(ref cube, direction, i, this.vmg.Array); + float halfB = baseB + Top(ref cube, direction, i, this.vmb.Array); + float halfA = baseA + Top(ref cube, direction, i, this.vma.Array); + float halfW = baseW + Top(ref cube, direction, i, this.vwt.Array); if (MathF.Abs(halfW) < Constants.Epsilon) { @@ -662,11 +655,11 @@ namespace SixLabors.ImageSharp.Quantizers /// Returns a value indicating whether the box has been split. private bool Cut(ref Box set1, ref Box set2) { - float wholeR = Volume(ref set1, this.vmr); - float wholeG = Volume(ref set1, this.vmg); - float wholeB = Volume(ref set1, this.vmb); - float wholeA = Volume(ref set1, this.vma); - float wholeW = Volume(ref set1, this.vwt); + float wholeR = Volume(ref set1, this.vmr.Array); + float wholeG = Volume(ref set1, this.vmg.Array); + float wholeB = Volume(ref set1, this.vmb.Array); + float wholeA = Volume(ref set1, this.vma.Array); + float wholeW = Volume(ref set1, this.vwt.Array); float maxr = this.Maximize(ref set1, 3, set1.R0 + 1, set1.R1, out int cutr, wholeR, wholeG, wholeB, wholeA, wholeW); float maxg = this.Maximize(ref set1, 2, set1.G0 + 1, set1.G1, out int cutg, wholeR, wholeG, wholeB, wholeA, wholeW); diff --git a/tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs b/tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs index a0b14b09ba..3afaa26061 100644 --- a/tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs +++ b/tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs @@ -61,7 +61,7 @@ { Assert.True(image[0, 0].Equals(default(TPixel))); - IQuantizer quantizer = new WuQuantizer { Dither = dither }; + IQuantizer quantizer = new WuQuantizer(Configuration.Default.MemoryManager) { Dither = dither }; foreach (ImageFrame frame in image.Frames) { From 41f51faf579436a9bebd8b6d03b87f2acb144f90 Mon Sep 17 00:00:00 2001 From: Lauri Kotilainen Date: Sat, 13 Jan 2018 13:39:00 +0200 Subject: [PATCH 016/151] - Optional param -> second method --- src/ImageSharp/Memory/ArrayPoolMemoryManager.cs | 8 +++++++- src/ImageSharp/Memory/MemoryManager.cs | 13 ++++++++++++- src/ImageSharp/Memory/NullMemoryManager.cs | 8 +++++++- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs b/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs index 3e5628c75e..f77a1f8ac1 100644 --- a/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs +++ b/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs @@ -30,7 +30,13 @@ namespace SixLabors.ImageSharp.Memory } /// - internal override Buffer Allocate(int itemCount, bool clear = false) + internal override Buffer Allocate(int itemCount) + { + return this.Allocate(itemCount, false); + } + + /// + internal override Buffer Allocate(int itemCount, bool clear) { int itemSizeBytes = Unsafe.SizeOf(); int bufferSizeInBytes = itemCount * itemSizeBytes; diff --git a/src/ImageSharp/Memory/MemoryManager.cs b/src/ImageSharp/Memory/MemoryManager.cs index 67d72d2b43..1cefccfb20 100644 --- a/src/ImageSharp/Memory/MemoryManager.cs +++ b/src/ImageSharp/Memory/MemoryManager.cs @@ -11,6 +11,17 @@ namespace SixLabors.ImageSharp.Memory /// public abstract class MemoryManager { + /// + /// Allocates a of size . + /// Note: Depending on the implementation, the buffer may not cleared before + /// returning, so it may contain data from an earlier use. + /// + /// Type of the data stored in the buffer + /// Size of the buffer to allocate + /// A buffer of values of type . + internal abstract Buffer Allocate(int size) + where T : struct; + /// /// Allocates a of size , optionally /// clearing the buffer before it gets returned. @@ -19,7 +30,7 @@ namespace SixLabors.ImageSharp.Memory /// Size of the buffer to allocate /// True to clear the backing memory of the buffer /// A buffer of values of type . - internal abstract Buffer Allocate(int size, bool clear = false) + internal abstract Buffer Allocate(int size, bool clear) where T : struct; /// diff --git a/src/ImageSharp/Memory/NullMemoryManager.cs b/src/ImageSharp/Memory/NullMemoryManager.cs index 32642dae4a..d82f013535 100644 --- a/src/ImageSharp/Memory/NullMemoryManager.cs +++ b/src/ImageSharp/Memory/NullMemoryManager.cs @@ -6,7 +6,13 @@ public class NullMemoryManager : MemoryManager { /// - internal override Buffer Allocate(int size, bool clear = false) + internal override Buffer Allocate(int size) + { + return new Buffer(new T[size], size); + } + + /// + internal override Buffer Allocate(int size, bool clear) { return new Buffer(new T[size], size); } From b747768800de18239262bb4db8d449b502066349 Mon Sep 17 00:00:00 2001 From: Lauri Kotilainen Date: Sat, 13 Jan 2018 14:55:34 +0200 Subject: [PATCH 017/151] - Whitespace fix --- src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs b/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs index bd3efe3f3d..789dcc25c4 100644 --- a/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs +++ b/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs @@ -127,7 +127,8 @@ namespace SixLabors.ImageSharp.Quantizers /// the second pass quantizes a color based on the position in the histogram. /// public WuQuantizer(MemoryManager memoryManager) - : base(false) { + : base(false) + { this.memoryManager = memoryManager; } From 1652f46ed16936d0248488949e48cc9b887ea774 Mon Sep 17 00:00:00 2001 From: Lauri Kotilainen Date: Sat, 13 Jan 2018 16:22:15 +0200 Subject: [PATCH 018/151] - Reduced the threshold for ArrayPoolMemoryManager to 512 bytes --- src/ImageSharp/Configuration.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index bc8ad1c529..37db84627f 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -86,7 +86,7 @@ namespace SixLabors.ImageSharp /// /// Gets or sets the that is currently in use. /// - public MemoryManager MemoryManager { get; set; } = new ArrayPoolMemoryManager(1024 * 80); + public MemoryManager MemoryManager { get; set; } = new ArrayPoolMemoryManager(512); /// /// Gets the maximum header size of all the formats. From d5b7000c8710917f521207dfce05f15147bdd624 Mon Sep 17 00:00:00 2001 From: Lauri Kotilainen Date: Sat, 13 Jan 2018 22:04:40 +0200 Subject: [PATCH 019/151] - Oops, neglected to fix this test --- tests/ImageSharp.Benchmarks/Image/EncodeIndexedPng.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ImageSharp.Benchmarks/Image/EncodeIndexedPng.cs b/tests/ImageSharp.Benchmarks/Image/EncodeIndexedPng.cs index b4f78bee85..4ce5f5083a 100644 --- a/tests/ImageSharp.Benchmarks/Image/EncodeIndexedPng.cs +++ b/tests/ImageSharp.Benchmarks/Image/EncodeIndexedPng.cs @@ -98,7 +98,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Image { using (MemoryStream memoryStream = new MemoryStream()) { - PngEncoder options = new PngEncoder(Configuration.Default.MemoryManager) { Quantizer = new WuQuantizer(), PaletteSize = 256 }; + PngEncoder options = new PngEncoder(Configuration.Default.MemoryManager) { Quantizer = new WuQuantizer(Configuration.Default.MemoryManager), PaletteSize = 256 }; this.bmpCore.SaveAsPng(memoryStream, options); } From d11aaf57ba9beedd41b9725ebcb417cbf28218bb Mon Sep 17 00:00:00 2001 From: Lauri Kotilainen Date: Sat, 13 Jan 2018 22:05:06 +0200 Subject: [PATCH 020/151] - Removed Configuration.Default.MemoryManager from PixelAccessor --- src/ImageSharp/Image/PixelAccessor{TPixel}.cs | 5 +++-- .../Processors/Convolution/Convolution2DProcessor.cs | 2 +- .../Processors/Convolution/Convolution2PassProcessor.cs | 2 +- .../Processors/Convolution/ConvolutionProcessor.cs | 2 +- .../Processing/Processors/Effects/OilPaintingProcessor.cs | 2 +- .../Processing/Processors/Transforms/CropProcessor.cs | 2 +- .../Processing/Processors/Transforms/FlipProcessor.cs | 4 ++-- .../Processing/Processors/Transforms/RotateProcessor.cs | 8 ++++---- .../Processing/Processors/Transforms/SkewProcessor.cs | 2 +- src/ImageSharp/Quantizers/Quantize.cs | 2 +- 10 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/ImageSharp/Image/PixelAccessor{TPixel}.cs b/src/ImageSharp/Image/PixelAccessor{TPixel}.cs index fca57b3c8e..50e65a0829 100644 --- a/src/ImageSharp/Image/PixelAccessor{TPixel}.cs +++ b/src/ImageSharp/Image/PixelAccessor{TPixel}.cs @@ -52,10 +52,11 @@ namespace SixLabors.ImageSharp /// /// Initializes a new instance of the class. /// + /// The to use for buffer allocations. /// 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, Configuration.Default.MemoryManager.Allocate2D(width, height, true), true) + public PixelAccessor(MemoryManager memoryManager, int width, int height) + : this(width, height, memoryManager.Allocate2D(width, height, true), true) { } diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs index b85432ac54..2e2f5e3a6a 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs @@ -56,7 +56,7 @@ namespace SixLabors.ImageSharp.Processing.Processors int maxY = endY - 1; int maxX = endX - 1; - using (var targetPixels = new PixelAccessor(source.Width, source.Height)) + using (var targetPixels = new PixelAccessor(configuration.MemoryManager, source.Width, source.Height)) { source.CopyTo(targetPixels); diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs index 362fa5c508..e79a6cf27f 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs @@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp.Processing.Processors int height = source.Height; ParallelOptions parallelOptions = configuration.ParallelOptions; - using (var firstPassPixels = new PixelAccessor(width, height)) + using (var firstPassPixels = new PixelAccessor(configuration.MemoryManager, width, height)) using (PixelAccessor sourcePixels = source.Lock()) { this.ApplyConvolution(firstPassPixels, sourcePixels, source.Bounds(), this.KernelX, parallelOptions); diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs index c0d3fdcfec..da64a970e2 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs @@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.Processing.Processors int maxY = endY - 1; int maxX = endX - 1; - using (var targetPixels = new PixelAccessor(source.Width, source.Height)) + using (var targetPixels = new PixelAccessor(configuration.MemoryManager, source.Width, source.Height)) { source.CopyTo(targetPixels); diff --git a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs index b22a497987..af2c297592 100644 --- a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs @@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.Processing.Processors int radius = this.BrushSize >> 1; int levels = this.Levels; - using (var targetPixels = new PixelAccessor(source.Width, source.Height)) + using (var targetPixels = new PixelAccessor(configuration.MemoryManager, source.Width, source.Height)) { source.CopyTo(targetPixels); diff --git a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs index 2657daaa8a..5568745864 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs @@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Processing.Processors int minX = Math.Max(this.CropRectangle.X, sourceRectangle.X); int maxX = Math.Min(this.CropRectangle.Right, sourceRectangle.Right); - using (var targetPixels = new PixelAccessor(this.CropRectangle.Width, this.CropRectangle.Height)) + using (var targetPixels = new PixelAccessor(configuration.MemoryManager, this.CropRectangle.Width, this.CropRectangle.Height)) { Parallel.For( minY, diff --git a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs index de60177f2f..f52bc97c11 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs @@ -58,7 +58,7 @@ namespace SixLabors.ImageSharp.Processing.Processors int height = source.Height; int halfHeight = (int)Math.Ceiling(source.Height * .5F); - using (var targetPixels = new PixelAccessor(width, height)) + using (var targetPixels = new PixelAccessor(configuration.MemoryManager, width, height)) { Parallel.For( 0, @@ -92,7 +92,7 @@ namespace SixLabors.ImageSharp.Processing.Processors int height = source.Height; int halfWidth = (int)Math.Ceiling(width * .5F); - using (var targetPixels = new PixelAccessor(width, height)) + using (var targetPixels = new PixelAccessor(configuration.MemoryManager, width, height)) { Parallel.For( 0, diff --git a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs index 86a0c73603..ddacf219b5 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs @@ -48,7 +48,7 @@ namespace SixLabors.ImageSharp.Processing.Processors Matrix3x2 matrix = this.GetCenteredMatrix(source, this.processMatrix); Rectangle sourceBounds = source.Bounds(); - using (var targetPixels = new PixelAccessor(width, height)) + using (var targetPixels = new PixelAccessor(configuration.MemoryManager, width, height)) { Parallel.For( 0, @@ -159,7 +159,7 @@ namespace SixLabors.ImageSharp.Processing.Processors int width = source.Width; int height = source.Height; - using (var targetPixels = new PixelAccessor(height, width)) + using (var targetPixels = new PixelAccessor(configuration.MemoryManager, height, width)) { using (PixelAccessor sourcePixels = source.Lock()) { @@ -193,7 +193,7 @@ namespace SixLabors.ImageSharp.Processing.Processors int width = source.Width; int height = source.Height; - using (var targetPixels = new PixelAccessor(width, height)) + using (var targetPixels = new PixelAccessor(configuration.MemoryManager, width, height)) { Parallel.For( 0, @@ -224,7 +224,7 @@ namespace SixLabors.ImageSharp.Processing.Processors int width = source.Width; int height = source.Height; - using (var targetPixels = new PixelAccessor(height, width)) + using (var targetPixels = new PixelAccessor(configuration.MemoryManager, height, width)) { Parallel.For( 0, diff --git a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs index 316e2a2af3..9f6f1d17d2 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs @@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp.Processing.Processors Matrix3x2 matrix = this.GetCenteredMatrix(source, this.processMatrix); Rectangle sourceBounds = source.Bounds(); - using (var targetPixels = new PixelAccessor(width, height)) + using (var targetPixels = new PixelAccessor(configuration.MemoryManager, width, height)) { Parallel.For( 0, diff --git a/src/ImageSharp/Quantizers/Quantize.cs b/src/ImageSharp/Quantizers/Quantize.cs index 296f4192d3..11494a867a 100644 --- a/src/ImageSharp/Quantizers/Quantize.cs +++ b/src/ImageSharp/Quantizers/Quantize.cs @@ -61,7 +61,7 @@ namespace SixLabors.ImageSharp QuantizedImage quantized = quantizer.Quantize(img.Frames.RootFrame, maxColors); int palleteCount = quantized.Palette.Length - 1; - using (var pixels = new PixelAccessor(quantized.Width, quantized.Height)) + using (var pixels = new PixelAccessor(source.GetMemoryManager(), quantized.Width, quantized.Height)) { Parallel.For( 0, From d50e446a6f816a54ed6dc8360a617b74e183ca5c Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 14 Jan 2018 02:08:34 +0100 Subject: [PATCH 021/151] dropping minSizeBytes + fixing tests --- src/ImageSharp/Configuration.cs | 2 +- .../Memory/ArrayPoolMemoryManager.cs | 40 +++++-------------- src/ImageSharp/Memory/MemoryManager.cs | 2 +- .../Formats/Jpg/JpegDecoderTests.cs | 3 +- .../PixelFormats/PixelOperationsTests.cs | 5 ++- 5 files changed, 15 insertions(+), 37 deletions(-) diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index 37db84627f..bd6c11235f 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -86,7 +86,7 @@ namespace SixLabors.ImageSharp /// /// Gets or sets the that is currently in use. /// - public MemoryManager MemoryManager { get; set; } = new ArrayPoolMemoryManager(512); + public MemoryManager MemoryManager { get; set; } = new ArrayPoolMemoryManager(); /// /// Gets the maximum header size of all the formats. diff --git a/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs b/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs index f77a1f8ac1..9df9e794aa 100644 --- a/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs +++ b/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs @@ -10,23 +10,20 @@ namespace SixLabors.ImageSharp.Memory /// public class ArrayPoolMemoryManager : MemoryManager { - private readonly int minSizeBytes; + /// + /// Defines the default maximum size of pooled arrays. + /// Currently set to a value equivalent to 16 Megapixels of an image. + /// + public const int DefaultMaxSizeInBytes = 4096 * 4096 * 4; + private readonly ArrayPool pool; /// /// Initializes a new instance of the class. - /// By passing an integer greater than 0 as , a - /// minimum threshold for pooled allocations is set. Any allocation requests that - /// would require less size than the threshold will not be managed within the array pool. /// - /// - /// Minimum size, in bytes, before an array pool is used to satisfy the request. - /// - public ArrayPoolMemoryManager(int minSizeBytes = 0) + public ArrayPoolMemoryManager() { - this.minSizeBytes = minSizeBytes; - - this.pool = ArrayPool.Create(CalculateMaxArrayLength(), 50); + this.pool = ArrayPool.Create(DefaultMaxSizeInBytes, 50); } /// @@ -41,14 +38,6 @@ namespace SixLabors.ImageSharp.Memory int itemSizeBytes = Unsafe.SizeOf(); int bufferSizeInBytes = itemCount * itemSizeBytes; - if (this.minSizeBytes > 0 && bufferSizeInBytes < this.minSizeBytes) - { - // Minimum size set to 8 bytes to get past a misbehaving test - // (otherwise PngDecoderTests.Decode_IncorrectCRCForNonCriticalChunk_ExceptionIsThrown fails for the wrong reason) - // TODO: Remove this once the test is fixed - return new Buffer(new T[Math.Max(itemCount, 8)], itemCount); - } - byte[] byteBuffer = this.pool.Rent(bufferSizeInBytes); var buffer = new Buffer(Unsafe.As(byteBuffer), itemCount, this); if (clear) @@ -62,19 +51,8 @@ namespace SixLabors.ImageSharp.Memory /// internal override void Release(Buffer buffer) { - var byteBuffer = Unsafe.As(buffer.Array); + byte[] byteBuffer = Unsafe.As(buffer.Array); this.pool.Return(byteBuffer); } - - /// - /// Heuristically calculates a reasonable maxArrayLength value for the backing . - /// - /// The maxArrayLength value - internal static int CalculateMaxArrayLength() - { - const int MaximumExpectedImageSize = 16384 * 16384; - const int MaximumBytesPerPixel = 4; - return MaximumExpectedImageSize * MaximumBytesPerPixel; - } } } \ No newline at end of file diff --git a/src/ImageSharp/Memory/MemoryManager.cs b/src/ImageSharp/Memory/MemoryManager.cs index 1cefccfb20..df1ecbd845 100644 --- a/src/ImageSharp/Memory/MemoryManager.cs +++ b/src/ImageSharp/Memory/MemoryManager.cs @@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.Memory internal Buffer2D Allocate2D(int width, int height, bool clear = false) where T : struct { - var buffer = this.Allocate(width * height, clear); + Buffer buffer = this.Allocate(width * height, clear); return new Buffer2D(buffer, width, height); } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index d529bbaeb6..e310e1efaf 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -118,8 +118,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg } public const string DecodeBaselineJpegOutputName = "DecodeBaselineJpeg"; - - + [Theory] [WithFile(TestImages.Jpeg.Baseline.Calliphora, CommonNonDefaultPixelTypes, false)] [WithFile(TestImages.Jpeg.Baseline.Calliphora, CommonNonDefaultPixelTypes, true)] diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs index 4cd7ebeea3..41711240c9 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs @@ -362,8 +362,9 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats if (typeof(TDest) == typeof(Vector4)) { - Vector4[] expected = this.ExpectedDestBuffer.Array as Vector4[]; - Vector4[] actual = this.ActualDestBuffer.Array as Vector4[]; + + Span expected = this.ExpectedDestBuffer.Span.NonPortableCast(); + Span actual = this.ActualDestBuffer.Span.NonPortableCast(); for (int i = 0; i < count; i++) { From b63dbd3a6def2d27d342d500760193581df512ed Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 14 Jan 2018 02:26:49 +0100 Subject: [PATCH 022/151] maxPoolSizeInBytes parameter + safer indexer for Buffer --- src/ImageSharp/Memory/ArrayPoolMemoryManager.cs | 15 +++++++++++++-- src/ImageSharp/Memory/Buffer{T}.cs | 4 +++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs b/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs index 9df9e794aa..b062df7118 100644 --- a/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs +++ b/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Memory { /// /// Defines the default maximum size of pooled arrays. - /// Currently set to a value equivalent to 16 Megapixels of an image. + /// Currently set to a value equivalent to 16 MegaPixels of an image. /// public const int DefaultMaxSizeInBytes = 4096 * 4096 * 4; @@ -22,8 +22,19 @@ namespace SixLabors.ImageSharp.Memory /// Initializes a new instance of the class. /// public ArrayPoolMemoryManager() + : this(DefaultMaxSizeInBytes) { - this.pool = ArrayPool.Create(DefaultMaxSizeInBytes, 50); + } + + /// + /// Initializes a new instance of the class. + /// The maximum size of pooled arrays. Arrays over the thershold are gonna be always allocated. + /// + public ArrayPoolMemoryManager(int maxPoolSizeInBytes) + { + Guard.MustBeGreaterThan(maxPoolSizeInBytes, 0, nameof(maxPoolSizeInBytes)); + + this.pool = ArrayPool.Create(maxPoolSizeInBytes, 50); } /// diff --git a/src/ImageSharp/Memory/Buffer{T}.cs b/src/ImageSharp/Memory/Buffer{T}.cs index d25cc232ad..07a827a67d 100644 --- a/src/ImageSharp/Memory/Buffer{T}.cs +++ b/src/ImageSharp/Memory/Buffer{T}.cs @@ -99,7 +99,9 @@ namespace SixLabors.ImageSharp.Memory get { DebugGuard.MustBeLessThan(index, this.Length, nameof(index)); - return ref this.Array[index]; + + Span span = this.Span; + return ref span[index]; } } From 789e52c367fe0f2bfa257627658d6cf35564396b Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 2 Feb 2018 00:53:48 +1100 Subject: [PATCH 023/151] Refactor and fix dithering API + algorithm. --- .../ErrorDiffusion/ErrorDiffuserBase.cs | 18 +-- .../ErrorDiffusion/IErrorDiffuser.cs | 24 +--- .../Dithering/Ordered/Bayer2x2Dither.cs | 19 +++ .../Dithering/Ordered/Bayer4x4Dither.cs | 19 +++ .../Dithering/Ordered/Bayer8x8Dither.cs | 19 +++ .../Dithering/Ordered/BayerDither.cs | 60 ++++++--- .../Dithering/Ordered/IOrderedDither.cs | 7 +- .../Dithering/Ordered/OrderedDither.cs | 36 ----- .../Dithering/Ordered/OrderedDitherBase.cs | 49 ++++--- src/ImageSharp/Memory/Fast2DArray{T}.cs | 11 +- src/ImageSharp/PixelFormats/ColorConstants.cs | 18 +-- .../PixelFormats/NamedColors{TPixel}.cs | 17 +++ .../Processing/Binarization/BinaryDiffuse.cs | 86 ++++++++++++ .../Processing/Binarization/BinaryDither.cs | 82 ++++++++++++ .../Binarization/BinaryThreshold.cs | 36 ++++- .../Processing/Dithering/Diffuse.cs | 84 ++++++++++++ .../{Binarization => Dithering}/Dither.cs | 58 ++------- .../BinaryErrorDiffusionProcessor.cs | 123 ++++++++++++++++++ .../BinaryOrderedDitherProcessor.cs | 103 +++++++++++++++ .../Binarization/BinaryThresholdProcessor.cs | 73 +++++------ .../ErrorDiffusionDitherProcessor.cs | 85 ------------ .../Binarization/OrderedDitherProcessor.cs | 93 ------------- .../ErrorDiffusionPaletteProcessor.cs | 113 ++++++++++++++++ .../OrderedDitherPaletteProcessor.cs | 93 +++++++++++++ .../Dithering/PaletteDitherProcessorBase.cs | 76 +++++++++++ .../Processors/Dithering/PixelPair.cs | 49 +++++++ .../Filters/GrayscaleBt709Processor.cs | 2 +- .../Quantizers/OctreeQuantizer{TPixel}.cs | 2 +- .../Quantizers/PaletteQuantizer{TPixel}.cs | 32 ++--- .../Quantizers/WuQuantizer{TPixel}.cs | 2 +- .../Binarization/BinaryThresholdTest.cs | 28 +++- .../Processing/Binarization/DitherTests.cs | 70 +++++++--- .../Processors/Binarization/DitherTests.cs | 19 +-- 33 files changed, 1150 insertions(+), 456 deletions(-) create mode 100644 src/ImageSharp/Dithering/Ordered/Bayer2x2Dither.cs create mode 100644 src/ImageSharp/Dithering/Ordered/Bayer4x4Dither.cs create mode 100644 src/ImageSharp/Dithering/Ordered/Bayer8x8Dither.cs delete mode 100644 src/ImageSharp/Dithering/Ordered/OrderedDither.cs create mode 100644 src/ImageSharp/Processing/Binarization/BinaryDiffuse.cs create mode 100644 src/ImageSharp/Processing/Binarization/BinaryDither.cs create mode 100644 src/ImageSharp/Processing/Dithering/Diffuse.cs rename src/ImageSharp/Processing/{Binarization => Dithering}/Dither.cs (50%) create mode 100644 src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor.cs create mode 100644 src/ImageSharp/Processing/Processors/Binarization/BinaryOrderedDitherProcessor.cs delete mode 100644 src/ImageSharp/Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs delete mode 100644 src/ImageSharp/Processing/Processors/Binarization/OrderedDitherProcessor.cs create mode 100644 src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs create mode 100644 src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor.cs create mode 100644 src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessorBase.cs create mode 100644 src/ImageSharp/Processing/Processors/Dithering/PixelPair.cs diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuserBase.cs b/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuserBase.cs index 46bafcc0cf..9fde279082 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuserBase.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuserBase.cs @@ -70,22 +70,10 @@ namespace SixLabors.ImageSharp.Dithering.Base /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Dither(ImageFrame pixels, TPixel source, TPixel transformed, int x, int y, int minX, int minY, int maxX, int maxY) + public void Dither(ImageFrame image, 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); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - 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) - { - // Assign the transformed pixel to the array. - image[x, y] = transformed; - } + image[x, y] = transformed; // Calculate the error Vector4 error = source.ToVector4() - transformed.ToVector4(); @@ -117,6 +105,8 @@ namespace SixLabors.ImageSharp.Dithering.Base var offsetColor = pixel.ToVector4(); Vector4 result = ((error * coefficient) / this.divisorVector) + offsetColor; + + // result.W = offsetColor.W; pixel.PackFromVector4(result); } } diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs b/src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs index c538d643c6..dabc4e6822 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs @@ -6,7 +6,7 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Dithering { /// - /// Encapsulates properties and methods required to perfom diffused error dithering on an image. + /// Encapsulates properties and methods required to perform diffused error dithering on an image. /// public interface IErrorDiffuser { @@ -25,25 +25,5 @@ namespace SixLabors.ImageSharp.Dithering /// The pixel format. void Dither(ImageFrame image, TPixel source, TPixel transformed, int x, int y, int minX, int minY, int maxX, int maxY) where TPixel : struct, IPixel; - - /// - /// Transforms the image applying the dither matrix. This method alters the input pixels array - /// - /// The image - /// The source pixel - /// The transformed pixel - /// The column index. - /// The row index. - /// The minimum column value. - /// The minimum row value. - /// The maximum column value. - /// The maximum row value. - /// - /// Whether to replace the pixel at the given coordinates with the transformed value. - /// 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(ImageFrame image, TPixel source, TPixel transformed, int x, int y, int minX, int minY, int maxX, int maxY, bool replacePixel) - where TPixel : struct, IPixel; } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Dithering/Ordered/Bayer2x2Dither.cs b/src/ImageSharp/Dithering/Ordered/Bayer2x2Dither.cs new file mode 100644 index 0000000000..e96a9c4d34 --- /dev/null +++ b/src/ImageSharp/Dithering/Ordered/Bayer2x2Dither.cs @@ -0,0 +1,19 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Dithering +{ + /// + /// Applies order dithering using the 2x2 Bayer dithering matrix. + /// + public sealed class Bayer2x2Dither : BayerDither + { + /// + /// Initializes a new instance of the class. + /// + public Bayer2x2Dither() + : base(1) + { + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Dithering/Ordered/Bayer4x4Dither.cs b/src/ImageSharp/Dithering/Ordered/Bayer4x4Dither.cs new file mode 100644 index 0000000000..ad72c164f4 --- /dev/null +++ b/src/ImageSharp/Dithering/Ordered/Bayer4x4Dither.cs @@ -0,0 +1,19 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Dithering +{ + /// + /// Applies order dithering using the 4x4 Bayer dithering matrix. + /// + public sealed class Bayer4x4Dither : BayerDither + { + /// + /// Initializes a new instance of the class. + /// + public Bayer4x4Dither() + : base(2) + { + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Dithering/Ordered/Bayer8x8Dither.cs b/src/ImageSharp/Dithering/Ordered/Bayer8x8Dither.cs new file mode 100644 index 0000000000..9077dc2cc6 --- /dev/null +++ b/src/ImageSharp/Dithering/Ordered/Bayer8x8Dither.cs @@ -0,0 +1,19 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Dithering +{ + /// + /// Applies order dithering using the 8x8 Bayer dithering matrix. + /// + public sealed class Bayer8x8Dither : BayerDither + { + /// + /// Initializes a new instance of the class. + /// + public Bayer8x8Dither() + : base(3) + { + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Dithering/Ordered/BayerDither.cs b/src/ImageSharp/Dithering/Ordered/BayerDither.cs index 685dca5fe8..3bac601aef 100644 --- a/src/ImageSharp/Dithering/Ordered/BayerDither.cs +++ b/src/ImageSharp/Dithering/Ordered/BayerDither.cs @@ -1,36 +1,60 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Dithering.Base; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Dithering { /// - /// Applies error diffusion based dithering using the 4x4 Bayer dithering matrix. - /// + /// Applies order dithering using a Bayer dithering matrix of arbitrary length. + /// /// - public sealed class BayerDither : OrderedDitherBase + public class BayerDither : OrderedDitherBase { /// - /// The threshold matrix. - /// This is calculated by multiplying each value in the original matrix by 16 and subtracting 1 + /// Initializes a new instance of the class. /// - private static readonly Fast2DArray ThresholdMatrix = - new byte[,] + /// + /// The exponent used to raise the base value (2). + /// The value given determines the dimensions of the matrix with each dimension a power of 2. e.g 2^2 = 4, 2^3 = 8 + /// + public BayerDither(uint exponent) + : base(ComputeBayer(exponent)) + { + } + + private static Fast2DArray ComputeBayer(uint order) + { + uint dimension = (uint)(1 << (int)order); + var matrix = new Fast2DArray((int)dimension); + uint i = 0; + for (int y = 0; y < dimension; y++) { - { 15, 143, 47, 175 }, - { 207, 79, 239, 111 }, - { 63, 191, 31, 159 }, - { 255, 127, 223, 95 } - }; + for (int x = 0; x < dimension; x++) + { + matrix[y, x] = Bayer(i / dimension, i % dimension, order); + i++; + } + } - /// - /// Initializes a new instance of the class. - /// - public BayerDither() - : base(ThresholdMatrix) + return matrix; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint Bayer(uint x, uint y, uint order) { + uint res = 0; + for (uint i = 0; i < order; ++i) + { + uint xOdd_XOR_yOdd = (x & 1) ^ (y & 1); + uint xOdd = x & 1; + res = ((res << 1 | xOdd_XOR_yOdd) << 1) | xOdd; + x >>= 1; + y >>= 1; + } + + return res; } } } \ No newline at end of file diff --git a/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs b/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs index 689c9a85b4..5d05be370d 100644 --- a/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs +++ b/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs @@ -6,7 +6,7 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Dithering { /// - /// Encapsulates properties and methods required to perfom ordered dithering on an image. + /// Encapsulates properties and methods required to perform ordered dithering on an image. /// public interface IOrderedDither { @@ -17,12 +17,11 @@ namespace SixLabors.ImageSharp.Dithering /// The source pixel /// The color to apply to the pixels above the threshold. /// The color to apply to the pixels below the threshold. - /// The to pack/unpack to. - /// The component index to test the threshold against. Must range from 0 to 3. + /// The threshold to split the image. Must be between 0 and 1. /// The column index. /// The row index. /// The pixel format. - void Dither(ImageFrame image, TPixel source, TPixel upper, TPixel lower, ref Rgba32 rgba, int index, int x, int y) + void Dither(ImageFrame image, TPixel source, TPixel upper, TPixel lower, byte threshold, int x, int y) where TPixel : struct, IPixel; } } \ No newline at end of file diff --git a/src/ImageSharp/Dithering/Ordered/OrderedDither.cs b/src/ImageSharp/Dithering/Ordered/OrderedDither.cs deleted file mode 100644 index 12968914d0..0000000000 --- a/src/ImageSharp/Dithering/Ordered/OrderedDither.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.Dithering.Base; -using SixLabors.ImageSharp.Memory; - -namespace SixLabors.ImageSharp.Dithering -{ - /// - /// Applies error diffusion based dithering using the 4x4 ordered dithering matrix. - /// - /// - public sealed class OrderedDither : OrderedDitherBase - { - /// - /// The threshold matrix. - /// This is calculated by multiplying each value in the original matrix by 16 - /// - private static readonly Fast2DArray ThresholdMatrix = - new byte[,] - { - { 0, 128, 32, 160 }, - { 192, 64, 224, 96 }, - { 48, 176, 16, 144 }, - { 240, 112, 208, 80 } - }; - - /// - /// Initializes a new instance of the class. - /// - public OrderedDither() - : base(ThresholdMatrix) - { - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Dithering/Ordered/OrderedDitherBase.cs b/src/ImageSharp/Dithering/Ordered/OrderedDitherBase.cs index 818a24d5dd..cf7a142397 100644 --- a/src/ImageSharp/Dithering/Ordered/OrderedDitherBase.cs +++ b/src/ImageSharp/Dithering/Ordered/OrderedDitherBase.cs @@ -1,53 +1,48 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Dithering.Base +namespace SixLabors.ImageSharp.Dithering { /// - /// The base class for performing ordered dithering using a 4x4 matrix. + /// The base class for performing ordered dithering using a dither matrix. /// public abstract class OrderedDitherBase : IOrderedDither { - /// - /// The dithering matrix - /// - private Fast2DArray matrix; + private readonly Fast2DArray matrix; + private readonly Fast2DArray thresholdMatrix; + private readonly int modulusX; + private readonly int modulusY; /// /// Initializes a new instance of the class. /// /// The thresholding matrix. - internal OrderedDitherBase(Fast2DArray matrix) + internal OrderedDitherBase(Fast2DArray matrix) { this.matrix = matrix; + this.modulusX = matrix.Width; + this.modulusY = matrix.Height; + this.thresholdMatrix = new Fast2DArray(matrix.Width, matrix.Height); + + // Adjust the matrix range for 0-255 + int multiplier = 256 / (this.modulusX * this.modulusY); + for (int y = 0; y < matrix.Height; y++) + { + for (int x = 0; x < matrix.Width; x++) + { + this.thresholdMatrix[y, x] = (uint)((matrix[y, x] + 1) * multiplier) - 1; + } + } } /// - public void Dither(ImageFrame image, TPixel source, TPixel upper, TPixel lower, ref Rgba32 rgba, int index, int x, int y) + public void Dither(ImageFrame image, TPixel source, TPixel upper, TPixel lower, byte threshold, int x, int y) where TPixel : struct, IPixel { - source.ToRgba32(ref rgba); - switch (index) - { - case 0: - image[x, y] = this.matrix[y % 3, x % 3] >= rgba.R ? lower : upper; - return; - case 1: - image[x, y] = this.matrix[y % 3, x % 3] >= rgba.G ? lower : upper; - return; - case 2: - image[x, y] = this.matrix[y % 3, x % 3] >= rgba.B ? lower : upper; - return; - case 3: - image[x, y] = this.matrix[y % 3, x % 3] >= rgba.A ? lower : upper; - return; - } - - throw new ArgumentOutOfRangeException(nameof(index), "Index should be between 0 and 3 inclusive."); + image[x, y] = this.thresholdMatrix[y % this.modulusY, x % this.modulusX] >= threshold ? lower : upper; } } } \ No newline at end of file diff --git a/src/ImageSharp/Memory/Fast2DArray{T}.cs b/src/ImageSharp/Memory/Fast2DArray{T}.cs index 14ac58baf2..e0384d2084 100644 --- a/src/ImageSharp/Memory/Fast2DArray{T}.cs +++ b/src/ImageSharp/Memory/Fast2DArray{T}.cs @@ -28,6 +28,15 @@ namespace SixLabors.ImageSharp.Memory /// public int Height; + /// + /// Initializes a new instance of the struct. + /// + /// The length of each dimension. + public Fast2DArray(int length) + : this(length, length) + { + } + /// /// Initializes a new instance of the struct. /// @@ -96,7 +105,7 @@ namespace SixLabors.ImageSharp.Memory /// /// The source array. /// - /// The represenation on the source data. + /// The representation on the source data. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Fast2DArray(T[,] data) diff --git a/src/ImageSharp/PixelFormats/ColorConstants.cs b/src/ImageSharp/PixelFormats/ColorConstants.cs index f97d3b190e..bac05c53d2 100644 --- a/src/ImageSharp/PixelFormats/ColorConstants.cs +++ b/src/ImageSharp/PixelFormats/ColorConstants.cs @@ -1,9 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; -using System.Collections.Generic; - namespace SixLabors.ImageSharp.PixelFormats { /// @@ -11,23 +8,17 @@ namespace SixLabors.ImageSharp.PixelFormats /// public static class ColorConstants { - /// - /// Provides a lazy, one time method of returning the colors. - /// - private static readonly Lazy SafeColors = new Lazy(GetWebSafeColors); - /// /// Gets a collection of named, web safe, colors as defined in the CSS Color Module Level 4. /// - public static Rgba32[] WebSafeColors => SafeColors.Value; + public static readonly Rgba32[] WebSafeColors = GetWebSafeColors(); /// /// Returns an array of web safe colors. /// /// The private static Rgba32[] GetWebSafeColors() - { - return new List + => new Rgba32[] { Rgba32.AliceBlue, Rgba32.AntiqueWhite, @@ -171,7 +162,6 @@ namespace SixLabors.ImageSharp.PixelFormats Rgba32.WhiteSmoke, Rgba32.Yellow, Rgba32.YellowGreen - }.ToArray(); - } + }; } -} +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs b/src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs index 45050de72e..ccd532bc3c 100644 --- a/src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; + namespace SixLabors.ImageSharp.PixelFormats { /// @@ -719,5 +721,20 @@ namespace SixLabors.ImageSharp.PixelFormats /// Represents a matching the W3C definition that has an hex value of #9ACD32. /// public static readonly TPixel YellowGreen = ColorBuilder.FromRGBA(154, 205, 50, 255); + + /// + /// Represents a matching the W3C definition of web safe colors. + /// + public static readonly TPixel[] WebSafePalette = GetWebSafePalette(); + + private static TPixel[] GetWebSafePalette() + { + Rgba32[] constants = ColorConstants.WebSafeColors; + TPixel[] safe = new TPixel[constants.Length + 1]; + + Span constantsBytes = constants.AsSpan().NonPortableCast(); + PixelOperations.Instance.PackFromRgba32Bytes(constantsBytes, safe, constants.Length); + return safe; + } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Binarization/BinaryDiffuse.cs b/src/ImageSharp/Processing/Binarization/BinaryDiffuse.cs new file mode 100644 index 0000000000..eb50087570 --- /dev/null +++ b/src/ImageSharp/Processing/Binarization/BinaryDiffuse.cs @@ -0,0 +1,86 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Dithering; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp +{ + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Dithers the image reducing it to two colors using error diffusion. + /// + /// The pixel format. + /// The image this method extends. + /// The diffusion algorithm to apply. + /// The threshold to apply binarization of the image. Must be between 0 and 1. + /// The . + public static IImageProcessingContext BinaryDiffuse(this IImageProcessingContext source, IErrorDiffuser diffuser, float threshold) + where TPixel : struct, IPixel + { + source.ApplyProcessor(new BinaryErrorDiffusionProcessor(diffuser, threshold)); + return source; + } + + /// + /// Dithers the image reducing it to two colors using error diffusion. + /// + /// The pixel format. + /// The image this method extends. + /// The diffusion algorithm to apply. + /// The threshold to apply binarization of the image. Must be between 0 and 1. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The . + public static IImageProcessingContext BinaryDiffuse(this IImageProcessingContext source, IErrorDiffuser diffuser, float threshold, Rectangle rectangle) + where TPixel : struct, IPixel + { + source.ApplyProcessor(new BinaryErrorDiffusionProcessor(diffuser, threshold), rectangle); + return source; + } + + /// + /// Dithers the image reducing it to two colors using error diffusion. + /// + /// The pixel format. + /// The image this method extends. + /// The diffusion algorithm to apply. + /// The threshold to apply binarization of the image. Must be between 0 and 1. + /// The color to use for pixels that are above the threshold. + /// The color to use for pixels that are below the threshold + /// The . + public static IImageProcessingContext BinaryDiffuse(this IImageProcessingContext source, IErrorDiffuser diffuser, float threshold, TPixel upperColor, TPixel lowerColor) + where TPixel : struct, IPixel + { + source.ApplyProcessor(new BinaryErrorDiffusionProcessor(diffuser, threshold, upperColor, lowerColor)); + return source; + } + + /// + /// Dithers the image reducing it to two colors using error diffusion. + /// + /// The pixel format. + /// The image this method extends. + /// The diffusion algorithm to apply. + /// The threshold to apply binarization of the image. Must be between 0 and 1. + /// The color to use for pixels that are above the threshold. + /// The color to use for pixels that are below the threshold + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The . + public static IImageProcessingContext BinaryDiffuse(this IImageProcessingContext source, IErrorDiffuser diffuser, float threshold, TPixel upperColor, TPixel lowerColor, Rectangle rectangle) + where TPixel : struct, IPixel + { + source.ApplyProcessor(new BinaryErrorDiffusionProcessor(diffuser, threshold, upperColor, lowerColor), rectangle); + return source; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Binarization/BinaryDither.cs b/src/ImageSharp/Processing/Binarization/BinaryDither.cs new file mode 100644 index 0000000000..715dff472d --- /dev/null +++ b/src/ImageSharp/Processing/Binarization/BinaryDither.cs @@ -0,0 +1,82 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Dithering; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp +{ + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Dithers the image reducing it to two colors using ordered dithering. + /// + /// The pixel format. + /// The image this method extends. + /// The ordered ditherer. + /// The . + public static IImageProcessingContext BinaryDither(this IImageProcessingContext source, IOrderedDither dither) + where TPixel : struct, IPixel + { + source.ApplyProcessor(new BinaryOrderedDitherProcessor(dither)); + return source; + } + + /// + /// Dithers the image reducing it to two colors using ordered dithering. + /// + /// The pixel format. + /// The image this method extends. + /// The ordered ditherer. + /// The color to use for pixels that are above the threshold. + /// The color to use for pixels that are below the threshold + /// The . + public static IImageProcessingContext BinaryDither(this IImageProcessingContext source, IOrderedDither dither, TPixel upperColor, TPixel lowerColor) + where TPixel : struct, IPixel + { + source.ApplyProcessor(new BinaryOrderedDitherProcessor(dither, upperColor, lowerColor)); + return source; + } + + /// + /// Dithers the image reducing it to two colors using ordered dithering. + /// + /// The pixel format. + /// The image this method extends. + /// The ordered ditherer. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The . + public static IImageProcessingContext BinaryDither(this IImageProcessingContext source, IOrderedDither dither, Rectangle rectangle) + where TPixel : struct, IPixel + { + source.ApplyProcessor(new BinaryOrderedDitherProcessor(dither), rectangle); + return source; + } + + /// + /// Dithers the image reducing it to two colors using ordered dithering. + /// + /// The pixel format. + /// The image this method extends. + /// The ordered ditherer. + /// The color to use for pixels that are above the threshold. + /// The color to use for pixels that are below the threshold + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The . + public static IImageProcessingContext BinaryDither(this IImageProcessingContext source, IOrderedDither dither, TPixel upperColor, TPixel lowerColor, Rectangle rectangle) + where TPixel : struct, IPixel + { + source.ApplyProcessor(new BinaryOrderedDitherProcessor(dither, upperColor, lowerColor), rectangle); + return source; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Binarization/BinaryThreshold.cs b/src/ImageSharp/Processing/Binarization/BinaryThreshold.cs index 5a165659b8..3f86528f51 100644 --- a/src/ImageSharp/Processing/Binarization/BinaryThreshold.cs +++ b/src/ImageSharp/Processing/Binarization/BinaryThreshold.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors; using SixLabors.Primitives; @@ -43,5 +42,40 @@ namespace SixLabors.ImageSharp source.ApplyProcessor(new BinaryThresholdProcessor(threshold), rectangle); return source; } + + /// + /// Applies binarization to the image splitting the pixels at the given threshold. + /// + /// The pixel format. + /// The image this method extends. + /// The threshold to apply binarization of the image. Must be between 0 and 1. + /// The color to use for pixels that are above the threshold. + /// The color to use for pixels that are below the threshold + /// The . + public static IImageProcessingContext BinaryThreshold(this IImageProcessingContext source, float threshold, TPixel upperColor, TPixel lowerColor) + where TPixel : struct, IPixel + { + source.ApplyProcessor(new BinaryThresholdProcessor(threshold, upperColor, lowerColor)); + return source; + } + + /// + /// Applies binarization to the image splitting the pixels at the given threshold. + /// + /// The pixel format. + /// The image this method extends. + /// The threshold to apply binarization of the image. Must be between 0 and 1. + /// The color to use for pixels that are above the threshold. + /// The color to use for pixels that are below the threshold + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The . + public static IImageProcessingContext BinaryThreshold(this IImageProcessingContext source, float threshold, TPixel upperColor, TPixel lowerColor, Rectangle rectangle) + where TPixel : struct, IPixel + { + source.ApplyProcessor(new BinaryThresholdProcessor(threshold, upperColor, lowerColor), rectangle); + return source; + } } } diff --git a/src/ImageSharp/Processing/Dithering/Diffuse.cs b/src/ImageSharp/Processing/Dithering/Diffuse.cs new file mode 100644 index 0000000000..e6b82d3134 --- /dev/null +++ b/src/ImageSharp/Processing/Dithering/Diffuse.cs @@ -0,0 +1,84 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Dithering; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp +{ + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Dithers the image reducing it to a web-safe palette using error diffusion. + /// + /// The pixel format. + /// The image this method extends. + /// The diffusion algorithm to apply. + /// The threshold to apply binarization of the image. Must be between 0 and 1. + /// The . + public static IImageProcessingContext Diffuse(this IImageProcessingContext source, IErrorDiffuser diffuser, float threshold) + where TPixel : struct, IPixel + { + source.ApplyProcessor(new ErrorDiffusionPaletteProcessor(diffuser, threshold)); + return source; + } + + /// + /// Dithers the image reducing it to a web-safe palette using error diffusion. + /// + /// The pixel format. + /// The image this method extends. + /// The diffusion algorithm to apply. + /// The threshold to apply binarization of the image. Must be between 0 and 1. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The . + public static IImageProcessingContext Diffuse(this IImageProcessingContext source, IErrorDiffuser diffuser, float threshold, Rectangle rectangle) + where TPixel : struct, IPixel + { + source.ApplyProcessor(new ErrorDiffusionPaletteProcessor(diffuser, threshold), rectangle); + return source; + } + + /// + /// Dithers the image reducing it to the given palette using error diffusion. + /// + /// The pixel format. + /// The image this method extends. + /// The diffusion algorithm to apply. + /// The threshold to apply binarization of the image. Must be between 0 and 1. + /// The palette to select substitute colors from. + /// The . + public static IImageProcessingContext Diffuse(this IImageProcessingContext source, IErrorDiffuser diffuser, float threshold, TPixel[] palette) + where TPixel : struct, IPixel + { + source.ApplyProcessor(new ErrorDiffusionPaletteProcessor(diffuser, threshold, palette)); + return source; + } + + /// + /// Dithers the image reducing it to the given palette using error diffusion. + /// + /// The pixel format. + /// The image this method extends. + /// The diffusion algorithm to apply. + /// The threshold to apply binarization of the image. Must be between 0 and 1. + /// The palette to select substitute colors from. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The . + public static IImageProcessingContext Diffuse(this IImageProcessingContext source, IErrorDiffuser diffuser, float threshold, TPixel[] palette, Rectangle rectangle) + where TPixel : struct, IPixel + { + source.ApplyProcessor(new ErrorDiffusionPaletteProcessor(diffuser, threshold, palette), rectangle); + return source; + } + } +} diff --git a/src/ImageSharp/Processing/Binarization/Dither.cs b/src/ImageSharp/Processing/Dithering/Dither.cs similarity index 50% rename from src/ImageSharp/Processing/Binarization/Dither.cs rename to src/ImageSharp/Processing/Dithering/Dither.cs index f21ccf0bd3..85fdef24b5 100644 --- a/src/ImageSharp/Processing/Binarization/Dither.cs +++ b/src/ImageSharp/Processing/Dithering/Dither.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; using SixLabors.ImageSharp.Dithering; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors; @@ -15,7 +14,7 @@ namespace SixLabors.ImageSharp public static partial class ImageExtensions { /// - /// Dithers the image reducing it to two colors using ordered dithering. + /// Dithers the image reducing it to a web-safe palette using ordered dithering. /// /// The pixel format. /// The image this method extends. @@ -24,27 +23,27 @@ namespace SixLabors.ImageSharp public static IImageProcessingContext Dither(this IImageProcessingContext source, IOrderedDither dither) where TPixel : struct, IPixel { - source.ApplyProcessor(new OrderedDitherProcessor(dither, 0)); + source.ApplyProcessor(new OrderedDitherPaletteProcessor(dither)); return source; } /// - /// Dithers the image reducing it to two colors using ordered dithering. + /// Dithers the image reducing it to the given palette using ordered dithering. /// /// The pixel format. /// The image this method extends. /// The ordered ditherer. - /// The component index to test the threshold against. Must range from 0 to 3. + /// The palette to select substitute colors from. /// The . - public static IImageProcessingContext Dither(this IImageProcessingContext source, IOrderedDither dither, int index) + public static IImageProcessingContext Dither(this IImageProcessingContext source, IOrderedDither dither, TPixel[] palette) where TPixel : struct, IPixel { - source.ApplyProcessor(new OrderedDitherProcessor(dither, index)); + source.ApplyProcessor(new OrderedDitherPaletteProcessor(dither, palette)); return source; } /// - /// Dithers the image reducing it to two colors using ordered dithering. + /// Dithers the image reducing it to a web-safe palette using ordered dithering. /// /// The pixel format. /// The image this method extends. @@ -56,58 +55,25 @@ namespace SixLabors.ImageSharp public static IImageProcessingContext Dither(this IImageProcessingContext source, IOrderedDither dither, Rectangle rectangle) where TPixel : struct, IPixel { - source.ApplyProcessor(new OrderedDitherProcessor(dither, 0), rectangle); + source.ApplyProcessor(new OrderedDitherPaletteProcessor(dither), rectangle); return source; } /// - /// Dithers the image reducing it to two colors using ordered dithering. + /// Dithers the image reducing it to the given palette using ordered dithering. /// /// The pixel format. /// The image this method extends. /// The ordered ditherer. + /// The palette to select substitute colors from. /// /// The structure that specifies the portion of the image object to alter. /// - /// The component index to test the threshold against. Must range from 0 to 3. /// The . - public static IImageProcessingContext Dither(this IImageProcessingContext source, IOrderedDither dither, Rectangle rectangle, int index) + public static IImageProcessingContext Dither(this IImageProcessingContext source, IOrderedDither dither, TPixel[] palette, Rectangle rectangle) where TPixel : struct, IPixel { - source.ApplyProcessor(new OrderedDitherProcessor(dither, index), rectangle); - return source; - } - - /// - /// Dithers the image reducing it to two colors using error diffusion. - /// - /// The pixel format. - /// The image this method extends. - /// The diffusion algorithm to apply. - /// The threshold to apply binarization of the image. Must be between 0 and 1. - /// The . - public static IImageProcessingContext Dither(this IImageProcessingContext source, IErrorDiffuser diffuser, float threshold) - where TPixel : struct, IPixel - { - source.ApplyProcessor(new ErrorDiffusionDitherProcessor(diffuser, threshold)); - return source; - } - - /// - /// Dithers the image reducing it to two colors using error diffusion. - /// - /// The pixel format. - /// The image this method extends. - /// The diffusion algorithm to apply. - /// The threshold to apply binarization of the image. Must be between 0 and 1. - /// - /// The structure that specifies the portion of the image object to alter. - /// - /// The . - public static IImageProcessingContext Dither(this IImageProcessingContext source, IErrorDiffuser diffuser, float threshold, Rectangle rectangle) - where TPixel : struct, IPixel - { - source.ApplyProcessor(new ErrorDiffusionDitherProcessor(diffuser, threshold), rectangle); + source.ApplyProcessor(new OrderedDitherPaletteProcessor(dither, palette), rectangle); return source; } } diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor.cs new file mode 100644 index 0000000000..70d903f31e --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor.cs @@ -0,0 +1,123 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Dithering; +using SixLabors.ImageSharp.Helpers; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + /// + /// Performs binary threshold filtering against an image using error diffusion. + /// + /// The pixel format. + internal class BinaryErrorDiffusionProcessor : ImageProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + /// The error diffuser + public BinaryErrorDiffusionProcessor(IErrorDiffuser diffuser) + : this(diffuser, .5F) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The error diffuser + /// The threshold to split the image. Must be between 0 and 1. + public BinaryErrorDiffusionProcessor(IErrorDiffuser diffuser, float threshold) + : this(diffuser, threshold, NamedColors.White, NamedColors.Black) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The error diffuser + /// The threshold to split the image. Must be between 0 and 1. + /// The color to use for pixels that are above the threshold. + /// The color to use for pixels that are below the threshold. + public BinaryErrorDiffusionProcessor(IErrorDiffuser diffuser, float threshold, TPixel upperColor, TPixel lowerColor) + { + Guard.NotNull(diffuser, nameof(diffuser)); + Guard.MustBeBetweenOrEqualTo(threshold, 0, 1, nameof(threshold)); + + this.Diffuser = diffuser; + this.Threshold = threshold; + this.UpperColor = upperColor; + this.LowerColor = lowerColor; + } + + /// + /// Gets the error diffuser. + /// + public IErrorDiffuser Diffuser { get; } + + /// + /// Gets the threshold value. + /// + public float Threshold { get; } + + /// + /// Gets the color to use for pixels that are above the threshold. + /// + public TPixel UpperColor { get; } + + /// + /// Gets the color to use for pixels that fall below the threshold. + /// + public TPixel LowerColor { get; } + + /// + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + float threshold = this.Threshold * 255F; + var rgba = default(Rgba32); + bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8); + + var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); + int startY = interest.Y; + int endY = interest.Bottom; + int startX = interest.X; + int endX = interest.Right; + + // Collect the values before looping so we can reduce our calculation count for identical sibling pixels + TPixel sourcePixel = source[startX, startY]; + TPixel previousPixel = sourcePixel; + sourcePixel.ToRgba32(ref rgba); + + // Convert to grayscale using ITU-R Recommendation BT.709 if required + byte luminance = (byte)(isAlphaOnly ? rgba.A : (.2126F * rgba.R) + (.7152F * rgba.G) + (.0722F * rgba.B)).Clamp(0, 255); + + for (int y = startY; y < endY; y++) + { + Span row = source.GetPixelRowSpan(y); + + for (int x = startX; x < endX; x++) + { + sourcePixel = row[x]; + + // Check if this is the same as the last pixel. If so use that value + // rather than calculating it again. This is an inexpensive optimization. + if (!previousPixel.Equals(sourcePixel)) + { + sourcePixel.ToRgba32(ref rgba); + luminance = (byte)(isAlphaOnly ? rgba.A : (.2126F * rgba.R) + (.7152F * rgba.G) + (.0722F * rgba.B)).Clamp(0, 255); + + // Setup the previous pointer + previousPixel = sourcePixel; + } + + TPixel transformedPixel = luminance >= threshold ? this.UpperColor : this.LowerColor; + this.Diffuser.Dither(source, sourcePixel, transformedPixel, x, y, startX, startY, endX, endY); + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryOrderedDitherProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryOrderedDitherProcessor.cs new file mode 100644 index 0000000000..3cabe378a1 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryOrderedDitherProcessor.cs @@ -0,0 +1,103 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Dithering; +using SixLabors.ImageSharp.Helpers; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + /// + /// Performs binary threshold filtering against an image using ordered dithering. + /// + /// The pixel format. + internal class BinaryOrderedDitherProcessor : ImageProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + /// The ordered ditherer. + public BinaryOrderedDitherProcessor(IOrderedDither dither) + : this(dither, NamedColors.White, NamedColors.Black) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The ordered ditherer. + /// The color to use for pixels that are above the threshold. + /// The color to use for pixels that are below the threshold. + public BinaryOrderedDitherProcessor(IOrderedDither dither, TPixel upperColor, TPixel lowerColor) + { + Guard.NotNull(dither, nameof(dither)); + + this.Dither = dither; + this.UpperColor = upperColor; + this.LowerColor = lowerColor; + } + + /// + /// Gets the ditherer. + /// + public IOrderedDither Dither { get; } + + /// + /// Gets the color to use for pixels that are above the threshold. + /// + public TPixel UpperColor { get; } + + /// + /// Gets the color to use for pixels that fall below the threshold. + /// + public TPixel LowerColor { get; } + + /// + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + var rgba = default(Rgba32); + bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8); + + var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); + int startY = interest.Y; + int endY = interest.Bottom; + int startX = interest.X; + int endX = interest.Right; + + // Collect the values before looping so we can reduce our calculation count for identical sibling pixels + TPixel sourcePixel = source[startX, startY]; + TPixel previousPixel = sourcePixel; + sourcePixel.ToRgba32(ref rgba); + + // Convert to grayscale using ITU-R Recommendation BT.709 if required + byte luminance = (byte)(isAlphaOnly ? rgba.A : (.2126F * rgba.R) + (.7152F * rgba.G) + (.0722F * rgba.B)).Clamp(0, 255); + + for (int y = startY; y < endY; y++) + { + Span row = source.GetPixelRowSpan(y); + + for (int x = startX; x < endX; x++) + { + sourcePixel = row[x]; + + // Check if this is the same as the last pixel. If so use that value + // rather than calculating it again. This is an inexpensive optimization. + if (!previousPixel.Equals(sourcePixel)) + { + sourcePixel.ToRgba32(ref rgba); + luminance = (byte)(isAlphaOnly ? rgba.A : (.2126F * rgba.R) + (.7152F * rgba.G) + (.0722F * rgba.B)).Clamp(0, 255); + + // Setup the previous pointer + previousPixel = sourcePixel; + } + + this.Dither.Dither(source, sourcePixel, this.UpperColor, this.LowerColor, luminance, x, y); + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs index 434ed02698..609b090923 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs @@ -4,14 +4,14 @@ using System; using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Helpers; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors { /// - /// An to perform binary threshold filtering against an - /// . The image will be converted to grayscale before thresholding occurs. + /// Performs simple binary threshold filtering against an image. /// /// The pixel format. internal class BinaryThresholdProcessor : ImageProcessor @@ -22,14 +22,22 @@ namespace SixLabors.ImageSharp.Processing.Processors /// /// The threshold to split the image. Must be between 0 and 1. public BinaryThresholdProcessor(float threshold) + : this(threshold, NamedColors.White, NamedColors.Black) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The threshold to split the image. Must be between 0 and 1. + /// The color to use for pixels that are above the threshold. + /// The color to use for pixels that are below the threshold. + public BinaryThresholdProcessor(float threshold, TPixel upperColor, TPixel lowerColor) { - // TODO: Check thresholding limit. Colors should probably have Max/Min/Middle properties. Guard.MustBeBetweenOrEqualTo(threshold, 0, 1, nameof(threshold)); this.Threshold = threshold; - - // Default to white/black for upper/lower. - this.UpperColor = NamedColors.White; - this.LowerColor = NamedColors.Black; + this.UpperColor = upperColor; + this.LowerColor = lowerColor; } /// @@ -47,55 +55,38 @@ namespace SixLabors.ImageSharp.Processing.Processors /// public TPixel LowerColor { get; set; } - /// - protected override void BeforeApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - { - new GrayscaleBt709Processor(1F).Apply(source, sourceRectangle, configuration); - } - /// protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - float threshold = this.Threshold; + float threshold = this.Threshold * 255F; TPixel upper = this.UpperColor; TPixel lower = this.LowerColor; - int startY = sourceRectangle.Y; - int endY = sourceRectangle.Bottom; - int startX = sourceRectangle.X; - int endX = sourceRectangle.Right; - - // Align start/end positions. - int minX = Math.Max(0, startX); - int maxX = Math.Min(source.Width, endX); - int minY = Math.Max(0, startY); - int maxY = Math.Min(source.Height, endY); - - // Reset offset if necessary. - if (minX > 0) - { - startX = 0; - } + var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); + int startY = interest.Y; + int endY = interest.Bottom; + int startX = interest.X; + int endX = interest.Right; - if (minY > 0) - { - startY = 0; - } + bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8); Parallel.For( - minY, - maxY, + startY, + endY, configuration.ParallelOptions, y => { - Span row = source.GetPixelRowSpan(y - startY); + Span row = source.GetPixelRowSpan(y); + var rgba = default(Rgba32); - for (int x = minX; x < maxX; x++) + for (int x = startX; x < endX; x++) { - ref TPixel color = ref row[x - startX]; + ref TPixel color = ref row[x]; + color.ToRgba32(ref rgba); - // Any channel will do since it's Grayscale. - color = color.ToVector4().X >= threshold ? upper : lower; + // Convert to grayscale using ITU-R Recommendation BT.709 if required + float luminance = isAlphaOnly ? rgba.A : (.2126F * rgba.R) + (.7152F * rgba.G) + (.0722F * rgba.B); + color = luminance >= threshold ? upper : lower; } }); } diff --git a/src/ImageSharp/Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs deleted file mode 100644 index 01cba15c4b..0000000000 --- a/src/ImageSharp/Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Dithering; -using SixLabors.ImageSharp.Helpers; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; - -namespace SixLabors.ImageSharp.Processing.Processors -{ - /// - /// An that dithers an image using error diffusion. - /// - /// The pixel format. - internal class ErrorDiffusionDitherProcessor : ImageProcessor - where TPixel : struct, IPixel - { - /// - /// Initializes a new instance of the class. - /// - /// The error diffuser - /// The threshold to split the image. Must be between 0 and 1. - public ErrorDiffusionDitherProcessor(IErrorDiffuser diffuser, float threshold) - { - Guard.NotNull(diffuser, nameof(diffuser)); - - this.Diffuser = diffuser; - this.Threshold = threshold; - - // Default to white/black for upper/lower. - this.UpperColor = NamedColors.White; - this.LowerColor = NamedColors.Black; - } - - /// - /// Gets the error diffuser. - /// - public IErrorDiffuser Diffuser { get; } - - /// - /// Gets the threshold value. - /// - public float Threshold { get; } - - /// - /// Gets or sets the color to use for pixels that are above the threshold. - /// - public TPixel UpperColor { get; set; } - - /// - /// Gets or sets the color to use for pixels that fall below the threshold. - /// - public TPixel LowerColor { get; set; } - - /// - protected override void BeforeApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - { - new GrayscaleBt709Processor(1F).Apply(source, sourceRectangle, configuration); - } - - /// - protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - { - var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); - int startY = interest.Y; - int endY = interest.Bottom; - int startX = interest.X; - int endX = interest.Right; - - for (int y = startY; y < endY; y++) - { - Span row = source.GetPixelRowSpan(y); - - for (int x = startX; x < endX; x++) - { - TPixel sourceColor = row[x]; - TPixel transformedColor = sourceColor.ToVector4().X >= this.Threshold ? this.UpperColor : this.LowerColor; - this.Diffuser.Dither(source, sourceColor, transformedColor, x, y, startX, startY, endX, endY); - } - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Binarization/OrderedDitherProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/OrderedDitherProcessor.cs deleted file mode 100644 index a37d12f18c..0000000000 --- a/src/ImageSharp/Processing/Processors/Binarization/OrderedDitherProcessor.cs +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Buffers; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Dithering; -using SixLabors.ImageSharp.Helpers; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; - -namespace SixLabors.ImageSharp.Processing.Processors -{ - /// - /// An that dithers an image using error diffusion. - /// - /// The pixel format. - internal class OrderedDitherProcessor : ImageProcessor - where TPixel : struct, IPixel - { - /// - /// Initializes a new instance of the class. - /// - /// The ordered ditherer. - /// The component index to test the threshold against. Must range from 0 to 3. - public OrderedDitherProcessor(IOrderedDither dither, int index) - { - Guard.NotNull(dither, nameof(dither)); - Guard.MustBeBetweenOrEqualTo(index, 0, 3, nameof(index)); - - // Alpha8 only stores the pixel data in the alpha channel. - if (typeof(TPixel) == typeof(Alpha8)) - { - index = 3; - } - - this.Dither = dither; - this.Index = index; - - // Default to white/black for upper/lower. - this.UpperColor = NamedColors.White; - this.LowerColor = NamedColors.Black; - } - - /// - /// Gets the ditherer. - /// - public IOrderedDither Dither { get; } - - /// - /// Gets the component index to test the threshold against. - /// - public int Index { get; } - - /// - /// Gets or sets the color to use for pixels that are above the threshold. - /// - public TPixel UpperColor { get; set; } - - /// - /// Gets or sets the color to use for pixels that fall below the threshold. - /// - public TPixel LowerColor { get; set; } - - /// - protected override void BeforeApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - { - new GrayscaleBt709Processor(1F).Apply(source, sourceRectangle, configuration); - } - - /// - protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - { - var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); - int startY = interest.Y; - int endY = interest.Bottom; - int startX = interest.X; - int endX = interest.Right; - - var rgba = default(Rgba32); - for (int y = startY; y < endY; y++) - { - Span row = source.GetPixelRowSpan(y); - - for (int x = startX; x < endX; x++) - { - TPixel sourceColor = row[x]; - this.Dither.Dither(source, sourceColor, this.UpperColor, this.LowerColor, ref rgba, this.Index, x, y); - } - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs new file mode 100644 index 0000000000..f8ff475d16 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs @@ -0,0 +1,113 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Dithering; +using SixLabors.ImageSharp.Helpers; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + /// + /// An that dithers an image using error diffusion. + /// + /// The pixel format. + internal class ErrorDiffusionPaletteProcessor : PaletteDitherProcessorBase + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + /// The error diffuser + public ErrorDiffusionPaletteProcessor(IErrorDiffuser diffuser) + : this(diffuser, .5F) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The error diffuser + /// The threshold to split the image. Must be between 0 and 1. + public ErrorDiffusionPaletteProcessor(IErrorDiffuser diffuser, float threshold) + : this(diffuser, threshold, NamedColors.WebSafePalette) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The error diffuser + /// The threshold to split the image. Must be between 0 and 1. + /// The palette to select substitute colors from. + public ErrorDiffusionPaletteProcessor(IErrorDiffuser diffuser, float threshold, TPixel[] palette) + : base(palette) + { + Guard.NotNull(diffuser, nameof(diffuser)); + Guard.MustBeBetweenOrEqualTo(threshold, 0, 1, nameof(threshold)); + + this.Diffuser = diffuser; + this.Threshold = threshold; + } + + /// + /// Gets the error diffuser. + /// + public IErrorDiffuser Diffuser { get; } + + /// + /// Gets the threshold value. + /// + public float Threshold { get; } + + /// + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + float threshold = this.Threshold * 255F; + var rgba = default(Rgba32); + bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8); + + var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); + int startY = interest.Y; + int endY = interest.Bottom; + int startX = interest.X; + int endX = interest.Right; + + // Collect the values before looping so we can reduce our calculation count for identical sibling pixels + TPixel sourcePixel = source[startX, startY]; + TPixel previousPixel = sourcePixel; + PixelPair pair = this.GetClosestPixelPair(ref sourcePixel, this.Palette); + sourcePixel.ToRgba32(ref rgba); + + // Convert to grayscale using ITU-R Recommendation BT.709 if required + byte luminance = (byte)(isAlphaOnly ? rgba.A : (.2126F * rgba.R) + (.7152F * rgba.G) + (.0722F * rgba.B)).Clamp(0, 255); + + for (int y = startY; y < endY; y++) + { + Span row = source.GetPixelRowSpan(y); + + for (int x = startX; x < endX; x++) + { + sourcePixel = row[x]; + + // Check if this is the same as the last pixel. If so use that value + // rather than calculating it again. This is an inexpensive optimization. + if (!previousPixel.Equals(sourcePixel)) + { + pair = this.GetClosestPixelPair(ref sourcePixel, this.Palette); + sourcePixel.ToRgba32(ref rgba); + luminance = (byte)(isAlphaOnly ? rgba.A : (.2126F * rgba.R) + (.7152F * rgba.G) + (.0722F * rgba.B)).Clamp(0, 255); + + // Setup the previous pointer + previousPixel = sourcePixel; + } + + TPixel transformedPixel = luminance >= threshold ? pair.First : pair.Second; + this.Diffuser.Dither(source, sourcePixel, transformedPixel, x, y, startX, startY, endX, endY); + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor.cs b/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor.cs new file mode 100644 index 0000000000..49455928af --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor.cs @@ -0,0 +1,93 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Dithering; +using SixLabors.ImageSharp.Helpers; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + /// + /// An that dithers an image using error diffusion. + /// If no palette is given this will default to the web safe colors defined in the CSS Color Module Level 4. + /// + /// The pixel format. + internal class OrderedDitherPaletteProcessor : PaletteDitherProcessorBase + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + /// The ordered ditherer. + public OrderedDitherPaletteProcessor(IOrderedDither dither) + : this(dither, NamedColors.WebSafePalette) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The ordered ditherer. + /// The palette to select substitute colors from. + public OrderedDitherPaletteProcessor(IOrderedDither dither, TPixel[] palette) + : base(palette) + { + Guard.NotNull(dither, nameof(dither)); + this.Dither = dither; + } + + /// + /// Gets the ditherer. + /// + public IOrderedDither Dither { get; } + + /// + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + var rgba = default(Rgba32); + bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8); + + var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); + int startY = interest.Y; + int endY = interest.Bottom; + int startX = interest.X; + int endX = interest.Right; + + // Collect the values before looping so we can reduce our calculation count for identical sibling pixels + TPixel sourcePixel = source[startX, startY]; + TPixel previousPixel = sourcePixel; + PixelPair pair = this.GetClosestPixelPair(ref sourcePixel, this.Palette); + sourcePixel.ToRgba32(ref rgba); + + // Convert to grayscale using ITU-R Recommendation BT.709 if required + byte luminance = (byte)(isAlphaOnly ? rgba.A : (.2126F * rgba.R) + (.7152F * rgba.G) + (.0722F * rgba.B)).Clamp(0, 255); + + for (int y = startY; y < endY; y++) + { + Span row = source.GetPixelRowSpan(y); + + for (int x = startX; x < endX; x++) + { + sourcePixel = row[x]; + + // Check if this is the same as the last pixel. If so use that value + // rather than calculating it again. This is an inexpensive optimization. + if (!previousPixel.Equals(sourcePixel)) + { + pair = this.GetClosestPixelPair(ref sourcePixel, this.Palette); + sourcePixel.ToRgba32(ref rgba); + luminance = (byte)(isAlphaOnly ? rgba.A : (.2126F * rgba.R) + (.7152F * rgba.G) + (.0722F * rgba.B)).Clamp(0, 255); + + // Setup the previous pointer + previousPixel = sourcePixel; + } + + this.Dither.Dither(source, sourcePixel, pair.First, pair.Second, luminance, x, y); + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessorBase.cs b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessorBase.cs new file mode 100644 index 0000000000..c6b80293cf --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessorBase.cs @@ -0,0 +1,76 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Collections.Generic; +using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + /// + /// The base class for dither and diffusion processors that consume a palette. + /// + internal abstract class PaletteDitherProcessorBase : ImageProcessor + where TPixel : struct, IPixel + { + private readonly Dictionary> cache = new Dictionary>(); + + /// + /// Initializes a new instance of the class. + /// + /// The palette to select substitute colors from. + public PaletteDitherProcessorBase(TPixel[] palette) + { + Guard.NotNull(palette, nameof(palette)); + this.Palette = palette; + } + + /// + /// Gets the palette to select substitute colors from. + /// + public TPixel[] Palette { get; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected PixelPair GetClosestPixelPair(ref TPixel pixel, TPixel[] colorPalette) + { + // Check if the color is in the lookup table + if (this.cache.ContainsKey(pixel)) + { + return this.cache[pixel]; + } + + // Not found - loop through the palette and find the nearest match. + float leastDistance = int.MaxValue; + float secondLeastDistance = int.MaxValue; + var vector = pixel.ToVector4(); + + var closest = default(TPixel); + var secondClosest = default(TPixel); + for (int index = 0; index < colorPalette.Length; index++) + { + TPixel temp = colorPalette[index]; + var tempVector = temp.ToVector4(); + float distance = Vector4.Distance(vector, tempVector); + + if (distance < leastDistance) + { + leastDistance = distance; + secondClosest = closest; + closest = temp; + } + else if (distance < secondLeastDistance) + { + secondLeastDistance = distance; + secondClosest = temp; + } + } + + // Pop it into the cache for next time + var pair = new PixelPair(closest, secondClosest); + this.cache.Add(pixel, pair); + + return pair; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Dithering/PixelPair.cs b/src/ImageSharp/Processing/Processors/Dithering/PixelPair.cs new file mode 100644 index 0000000000..e3b9c11bdf --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Dithering/PixelPair.cs @@ -0,0 +1,49 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + /// + /// Represents a composite pair of pixels. Used for caching color distance lookups. + /// + /// The pixel format. + internal struct PixelPair : IEquatable> + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the struct. + /// + /// The first pixel color + /// The second pixel color + public PixelPair(TPixel first, TPixel second) + { + this.First = first; + this.Second = second; + } + + /// + /// Gets the first pixel color + /// + public TPixel First { get; } + + /// + /// Gets the second pixel color + /// + public TPixel Second { get; } + + /// + public bool Equals(PixelPair other) + => this.First.Equals(other.First) && this.Second.Equals(other.Second); + + /// + public override bool Equals(object obj) + => obj is PixelPair other && this.First.Equals(other.First) && this.Second.Equals(other.Second); + + /// + public override int GetHashCode() + => HashHelpers.Combine(this.First.GetHashCode(), this.Second.GetHashCode()); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs b/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs index 2d97f65842..fcd7b2e8f5 100644 --- a/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs @@ -6,7 +6,7 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Processing.Processors { /// - /// Applies a greyscale filter matrix using the given amount and the formula as specified by ITU-R Recommendation BT.709 + /// Applies a grayscale filter matrix using the given amount and the formula as specified by ITU-R Recommendation BT.709 /// /// The pixel format. internal class GrayscaleBt709Processor : FilterProcessor diff --git a/src/ImageSharp/Quantizers/OctreeQuantizer{TPixel}.cs b/src/ImageSharp/Quantizers/OctreeQuantizer{TPixel}.cs index d646a680ea..8b8db61777 100644 --- a/src/ImageSharp/Quantizers/OctreeQuantizer{TPixel}.cs +++ b/src/ImageSharp/Quantizers/OctreeQuantizer{TPixel}.cs @@ -107,7 +107,7 @@ namespace SixLabors.ImageSharp.Quantizers if (this.Dither) { // Apply the dithering matrix. We have to reapply the value now as the original has changed. - this.DitherType.Dither(source, sourcePixel, transformedPixel, x, y, 0, 0, width, height, false); + this.DitherType.Dither(source, sourcePixel, transformedPixel, x, y, 0, 0, width, height); } output[(y * source.Width) + x] = pixelValue; diff --git a/src/ImageSharp/Quantizers/PaletteQuantizer{TPixel}.cs b/src/ImageSharp/Quantizers/PaletteQuantizer{TPixel}.cs index 0b95c09a62..cd1b4b07b1 100644 --- a/src/ImageSharp/Quantizers/PaletteQuantizer{TPixel}.cs +++ b/src/ImageSharp/Quantizers/PaletteQuantizer{TPixel}.cs @@ -12,6 +12,7 @@ namespace SixLabors.ImageSharp.Quantizers { /// /// Encapsulates methods to create a quantized image based upon the given palette. + /// If no palette is given this will default to the web safe colors defined in the CSS Color Module Level 4. /// /// /// The pixel format. @@ -31,27 +32,20 @@ namespace SixLabors.ImageSharp.Quantizers /// /// Initializes a new instance of the class. /// - /// - /// The color palette. If none is given this will default to the web safe colors defined - /// in the CSS Color Module Level 4. - /// + public PaletteQuantizer() + : this(NamedColors.WebSafePalette) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The palette to select substitute colors from. public PaletteQuantizer(TPixel[] palette = null) : base(true) { - if (palette == null) - { - Rgba32[] constants = ColorConstants.WebSafeColors; - TPixel[] safe = new TPixel[constants.Length + 1]; - - Span constantsBytes = constants.AsSpan().NonPortableCast(); - - PixelOperations.Instance.PackFromRgba32Bytes(constantsBytes, safe, constants.Length); - this.colors = safe; - } - else - { - this.colors = palette; - } + Guard.NotNull(palette, nameof(palette)); + this.colors = palette; } /// @@ -102,7 +96,7 @@ namespace SixLabors.ImageSharp.Quantizers if (this.Dither) { // Apply the dithering matrix. We have to reapply the value now as the original has changed. - this.DitherType.Dither(source, sourcePixel, transformedPixel, x, y, 0, 0, width, height, false); + this.DitherType.Dither(source, sourcePixel, transformedPixel, x, y, 0, 0, width, height); } output[(y * source.Width) + x] = pixelValue; diff --git a/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs b/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs index f08114574e..ce2a71da48 100644 --- a/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs +++ b/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs @@ -272,7 +272,7 @@ namespace SixLabors.ImageSharp.Quantizers if (this.Dither) { // Apply the dithering matrix. We have to reapply the value now as the original has changed. - this.DitherType.Dither(source, sourcePixel, transformedPixel, x, y, 0, 0, width, height, false); + this.DitherType.Dither(source, sourcePixel, transformedPixel, x, y, 0, 0, width, height); } output[(y * source.Width) + x] = pixelValue; diff --git a/tests/ImageSharp.Tests/Processing/Binarization/BinaryThresholdTest.cs b/tests/ImageSharp.Tests/Processing/Binarization/BinaryThresholdTest.cs index 221b4a9bff..488b3d18b1 100644 --- a/tests/ImageSharp.Tests/Processing/Binarization/BinaryThresholdTest.cs +++ b/tests/ImageSharp.Tests/Processing/Binarization/BinaryThresholdTest.cs @@ -15,16 +15,40 @@ namespace SixLabors.ImageSharp.Tests.Processing.Binarization public void BinaryThreshold_CorrectProcessor() { this.operations.BinaryThreshold(.23f); - var p = this.Verify>(); + BinaryThresholdProcessor p = this.Verify>(); Assert.Equal(.23f, p.Threshold); + Assert.Equal(NamedColors.White, p.UpperColor); + Assert.Equal(NamedColors.Black, p.LowerColor); } [Fact] public void BinaryThreshold_rect_CorrectProcessor() { this.operations.BinaryThreshold(.93f, this.rect); - var p = this.Verify>(this.rect); + BinaryThresholdProcessor p = this.Verify>(this.rect); Assert.Equal(.93f, p.Threshold); + Assert.Equal(NamedColors.White, p.UpperColor); + Assert.Equal(NamedColors.Black, p.LowerColor); + } + + [Fact] + public void BinaryThreshold_CorrectProcessorWithUpperLower() + { + this.operations.BinaryThreshold(.23f, NamedColors.HotPink, NamedColors.Yellow); + BinaryThresholdProcessor p = this.Verify>(); + Assert.Equal(.23f, p.Threshold); + Assert.Equal(NamedColors.HotPink, p.UpperColor); + Assert.Equal(NamedColors.Yellow, p.LowerColor); + } + + [Fact] + public void BinaryThreshold_rect_CorrectProcessorWithUpperLower() + { + this.operations.BinaryThreshold(.93f, NamedColors.HotPink, NamedColors.Yellow, this.rect); + BinaryThresholdProcessor p = this.Verify>(this.rect); + Assert.Equal(.93f, p.Threshold); + Assert.Equal(NamedColors.HotPink, p.UpperColor); + Assert.Equal(NamedColors.Yellow, p.LowerColor); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Binarization/DitherTests.cs b/tests/ImageSharp.Tests/Processing/Binarization/DitherTests.cs index 94241d0071..ba5cb0cf33 100644 --- a/tests/ImageSharp.Tests/Processing/Binarization/DitherTests.cs +++ b/tests/ImageSharp.Tests/Processing/Binarization/DitherTests.cs @@ -5,7 +5,6 @@ using SixLabors.ImageSharp.Dithering; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors; using Moq; -using SixLabors.Primitives; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Binarization @@ -23,55 +22,84 @@ namespace SixLabors.ImageSharp.Tests.Processing.Binarization [Fact] public void Dither_CorrectProcessor() { - this.operations.Dither(orderedDither); - var p = this.Verify>(); + this.operations.BinaryDither(this.orderedDither); + BinaryOrderedDitherProcessor p = this.Verify>(); Assert.Equal(this.orderedDither, p.Dither); - Assert.Equal(0, p.Index); + Assert.Equal(NamedColors.White, p.UpperColor); + Assert.Equal(NamedColors.Black, p.LowerColor); } [Fact] public void Dither_rect_CorrectProcessor() { - this.operations.Dither(orderedDither, this.rect); - var p = this.Verify>(this.rect); + this.operations.BinaryDither(this.orderedDither, this.rect); + BinaryOrderedDitherProcessor p = this.Verify>(this.rect); Assert.Equal(this.orderedDither, p.Dither); - Assert.Equal(0, p.Index); + Assert.Equal(NamedColors.White, p.UpperColor); + Assert.Equal(NamedColors.Black, p.LowerColor); } [Fact] public void Dither_index_CorrectProcessor() { - this.operations.Dither(orderedDither, 2); - var p = this.Verify>(); + this.operations.BinaryDither(this.orderedDither, NamedColors.Yellow, NamedColors.HotPink); + BinaryOrderedDitherProcessor p = this.Verify>(); Assert.Equal(this.orderedDither, p.Dither); - Assert.Equal(2, p.Index); + Assert.Equal(NamedColors.Yellow, p.UpperColor); + Assert.Equal(NamedColors.HotPink, p.LowerColor); } [Fact] public void Dither_index_rect_CorrectProcessor() { - this.operations.Dither(orderedDither, this.rect, 2); - var p = this.Verify>(this.rect); + this.operations.BinaryDither(this.orderedDither, NamedColors.Yellow, NamedColors.HotPink, this.rect); + BinaryOrderedDitherProcessor p = this.Verify>(this.rect); Assert.Equal(this.orderedDither, p.Dither); - Assert.Equal(2, p.Index); + Assert.Equal(NamedColors.HotPink, p.LowerColor); } [Fact] - public void Dither_ErrorDifuser_CorrectProcessor() + public void Dither_ErrorDiffuser_CorrectProcessor() { - this.operations.Dither(errorDiffuser, 4); - var p = this.Verify>(); + this.operations.BinaryDiffuse(this.errorDiffuser, .4F); + BinaryErrorDiffusionProcessor p = this.Verify>(); Assert.Equal(this.errorDiffuser, p.Diffuser); - Assert.Equal(4, p.Threshold); + Assert.Equal(.4F, p.Threshold); + Assert.Equal(NamedColors.White, p.UpperColor); + Assert.Equal(NamedColors.Black, p.LowerColor); } [Fact] - public void Dither_ErrorDifuser_rect_CorrectProcessor() + public void Dither_ErrorDiffuser_rect_CorrectProcessor() { - this.operations.Dither(this.errorDiffuser, 3, this.rect); - var p = this.Verify>(this.rect); + this.operations.BinaryDiffuse(this.errorDiffuser, .3F, this.rect); + BinaryErrorDiffusionProcessor p = this.Verify>(this.rect); Assert.Equal(this.errorDiffuser, p.Diffuser); - Assert.Equal(3, p.Threshold); + Assert.Equal(.3F, p.Threshold); + Assert.Equal(NamedColors.White, p.UpperColor); + Assert.Equal(NamedColors.Black, p.LowerColor); + } + + [Fact] + public void Dither_ErrorDiffuser_CorrectProcessorWithColors() + { + this.operations.BinaryDiffuse(this.errorDiffuser, .5F, NamedColors.HotPink, NamedColors.Yellow); + BinaryErrorDiffusionProcessor p = this.Verify>(); + Assert.Equal(this.errorDiffuser, p.Diffuser); + Assert.Equal(.5F, p.Threshold); + Assert.Equal(NamedColors.HotPink, p.UpperColor); + Assert.Equal(NamedColors.Yellow, p.LowerColor); + } + + [Fact] + public void Dither_ErrorDiffuser_rect_CorrectProcessorWithColors() + { + this.operations.BinaryDiffuse(this.errorDiffuser, .5F, NamedColors.HotPink, NamedColors.Yellow, this.rect); + BinaryErrorDiffusionProcessor p = this.Verify>(this.rect); + Assert.Equal(this.errorDiffuser, p.Diffuser); + Assert.Equal(.5F, p.Threshold); + Assert.Equal(NamedColors.HotPink, p.UpperColor); + Assert.Equal(NamedColors.Yellow, p.LowerColor); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Binarization/DitherTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Binarization/DitherTests.cs index 9a6d24226b..3ddf9d0fec 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Binarization/DitherTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Binarization/DitherTests.cs @@ -22,8 +22,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization public static readonly TheoryData Ditherers = new TheoryData { - { "Ordered", new OrderedDither() }, - { "Bayer", new BayerDither() } + { "Bayer8x8", new Bayer8x8Dither() }, + { "Bayer4x4", new Bayer4x4Dither() }, + { "Bayer2x2", new Bayer2x2Dither() } }; public static readonly TheoryData ErrorDiffusers = new TheoryData @@ -39,7 +40,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization }; - private static IOrderedDither DefaultDitherer => new OrderedDither(); + private static IOrderedDither DefaultDitherer => new Bayer4x4Dither(); private static IErrorDiffuser DefaultErrorDiffuser => new AtkinsonDiffuser(); @@ -51,7 +52,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization { using (Image image = provider.GetImage()) { - image.Mutate(x => x.Dither(ditherer)); + image.Mutate(x => x.BinaryDither(ditherer)); image.DebugSave(provider, name); } } @@ -64,7 +65,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization { using (Image image = provider.GetImage()) { - image.Mutate(x => x.Dither(diffuser, .5F)); + image.Mutate(x => x.BinaryDiffuse(diffuser, .5F)); image.DebugSave(provider, name); } } @@ -76,7 +77,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization { using (Image image = provider.GetImage()) { - image.Mutate(x => x.Dither(DefaultDitherer)); + image.Mutate(x => x.BinaryDither(DefaultDitherer)); image.DebugSave(provider); } } @@ -88,7 +89,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization { using (Image image = provider.GetImage()) { - image.Mutate(x => x.Dither(DefaultErrorDiffuser, 0.5f)); + image.Mutate(x => x.BinaryDiffuse(DefaultErrorDiffuser, 0.5f)); image.DebugSave(provider); } } @@ -103,7 +104,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization { var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - image.Mutate(x => x.Dither(DefaultDitherer, bounds)); + image.Mutate(x => x.BinaryDither(DefaultDitherer, bounds)); image.DebugSave(provider); ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); @@ -120,7 +121,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization { var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - image.Mutate(x => x.Dither(DefaultErrorDiffuser, .5F, bounds)); + image.Mutate(x => x.BinaryDiffuse(DefaultErrorDiffuser, .5F, bounds)); image.DebugSave(provider); ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); From 0688343241ce991f84d37998112fb2b3fb2d3031 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 5 Feb 2018 01:54:17 +1100 Subject: [PATCH 024/151] Final refactorings + bug fixes --- .../ErrorDiffusion/ErrorDiffuserBase.cs | 2 - .../ErrorDiffusion/KnownDiffusers.cs | 56 ++++++++++ .../ErrorDiffusion/StevensonArceDiffuser.cs | 34 ++++++ .../Dithering/Ordered/BayerDither.cs | 60 ----------- .../{Bayer2x2Dither.cs => BayerDither2x2.cs} | 8 +- .../{Bayer4x4Dither.cs => BayerDither4x4.cs} | 8 +- .../{Bayer8x8Dither.cs => BayerDither8x8.cs} | 8 +- .../Dithering/Ordered/KnownDitherers.cs | 31 ++++++ .../Dithering/Ordered/OrderedDither.cs | 50 +++++++++ .../Dithering/Ordered/OrderedDither3x3.cs | 19 ++++ .../Dithering/Ordered/OrderedDitherBase.cs | 48 --------- .../Dithering/Ordered/OrderedDitherFactory.cs | 94 ++++++++++++++++ src/ImageSharp/Dithering/error_diffusion.txt | 58 ++++++++++ src/ImageSharp/Memory/Fast2DArray{T}.cs | 11 +- .../ErrorDiffusionPaletteProcessor.cs | 2 +- .../OrderedDitherPaletteProcessor.cs | 2 +- .../Dithering/PaletteDitherProcessorBase.cs | 3 +- .../Quantizers/QuantizerBase{TPixel}.cs | 2 +- .../Memory/Fast2DArrayTests.cs | 12 +-- .../Processing/Binarization/DitherTests.cs | 6 +- .../Binarization/OrderedDitherFactoryTests.cs | 102 ++++++++++++++++++ .../Processors/Binarization/DitherTests.cs | 34 +++--- 22 files changed, 495 insertions(+), 155 deletions(-) create mode 100644 src/ImageSharp/Dithering/ErrorDiffusion/KnownDiffusers.cs create mode 100644 src/ImageSharp/Dithering/ErrorDiffusion/StevensonArceDiffuser.cs delete mode 100644 src/ImageSharp/Dithering/Ordered/BayerDither.cs rename src/ImageSharp/Dithering/Ordered/{Bayer2x2Dither.cs => BayerDither2x2.cs} (63%) rename src/ImageSharp/Dithering/Ordered/{Bayer4x4Dither.cs => BayerDither4x4.cs} (63%) rename src/ImageSharp/Dithering/Ordered/{Bayer8x8Dither.cs => BayerDither8x8.cs} (63%) create mode 100644 src/ImageSharp/Dithering/Ordered/KnownDitherers.cs create mode 100644 src/ImageSharp/Dithering/Ordered/OrderedDither.cs create mode 100644 src/ImageSharp/Dithering/Ordered/OrderedDither3x3.cs delete mode 100644 src/ImageSharp/Dithering/Ordered/OrderedDitherBase.cs create mode 100644 src/ImageSharp/Dithering/Ordered/OrderedDitherFactory.cs create mode 100644 src/ImageSharp/Dithering/error_diffusion.txt create mode 100644 tests/ImageSharp.Tests/Processing/Binarization/OrderedDitherFactoryTests.cs diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuserBase.cs b/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuserBase.cs index 9fde279082..8f448198b0 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuserBase.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuserBase.cs @@ -105,8 +105,6 @@ namespace SixLabors.ImageSharp.Dithering.Base var offsetColor = pixel.ToVector4(); Vector4 result = ((error * coefficient) / this.divisorVector) + offsetColor; - - // result.W = offsetColor.W; pixel.PackFromVector4(result); } } diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/KnownDiffusers.cs b/src/ImageSharp/Dithering/ErrorDiffusion/KnownDiffusers.cs new file mode 100644 index 0000000000..c75530b8e7 --- /dev/null +++ b/src/ImageSharp/Dithering/ErrorDiffusion/KnownDiffusers.cs @@ -0,0 +1,56 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Dithering +{ + /// + /// Contains reusable static instances of known error diffusion algorithms + /// + public static class KnownDiffusers + { + /// + /// Gets the error diffuser that implements the Atkinson algorithm. + /// + public static IErrorDiffuser Atkinson { get; } = new AtkinsonDiffuser(); + + /// + /// Gets the error diffuser that implements the Burks algorithm. + /// + public static IErrorDiffuser Burks { get; } = new BurksDiffuser(); + + /// + /// Gets the error diffuser that implements the Floyd-Steinberg algorithm. + /// + public static IErrorDiffuser FloydSteinberg { get; } = new FloydSteinbergDiffuser(); + + /// + /// Gets the error diffuser that implements the Jarvis-Judice-Ninke algorithm. + /// + public static IErrorDiffuser JarvisJudiceNinke { get; } = new JarvisJudiceNinkeDiffuser(); + + /// + /// Gets the error diffuser that implements the Sierra-2 algorithm. + /// + public static IErrorDiffuser Sierra2 { get; } = new Sierra2Diffuser(); + + /// + /// Gets the error diffuser that implements the Sierra-3 algorithm. + /// + public static IErrorDiffuser Sierra3 { get; } = new Sierra3Diffuser(); + + /// + /// Gets the error diffuser that implements the Sierra-Lite algorithm. + /// + public static IErrorDiffuser SierraLite { get; } = new SierraLiteDiffuser(); + + /// + /// Gets the error diffuser that implements the Stevenson-Arce algorithm. + /// + public static IErrorDiffuser StevensonArce { get; } = new StevensonArceDiffuser(); + + /// + /// Gets the error diffuser that implements the Stucki algorithm. + /// + public static IErrorDiffuser Stucki { get; } = new StuckiDiffuser(); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/StevensonArceDiffuser.cs b/src/ImageSharp/Dithering/ErrorDiffusion/StevensonArceDiffuser.cs new file mode 100644 index 0000000000..0f0338ac72 --- /dev/null +++ b/src/ImageSharp/Dithering/ErrorDiffusion/StevensonArceDiffuser.cs @@ -0,0 +1,34 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Dithering.Base; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.Dithering +{ + /// + /// Applies error diffusion based dithering using the Stevenson-Arce image dithering algorithm. + /// + public sealed class StevensonArceDiffuser : ErrorDiffuserBase + { + /// + /// The diffusion matrix + /// + private static readonly Fast2DArray StevensonArceMatrix = + new float[,] + { + { 0, 0, 0, 0, 0, 32, 0 }, + { 12, 0, 26, 0, 30, 0, 16 }, + { 0, 12, 0, 26, 0, 12, 0 }, + { 5, 0, 12, 0, 12, 0, 5 } + }; + + /// + /// Initializes a new instance of the class. + /// + public StevensonArceDiffuser() + : base(StevensonArceMatrix, 200) + { + } + } +} diff --git a/src/ImageSharp/Dithering/Ordered/BayerDither.cs b/src/ImageSharp/Dithering/Ordered/BayerDither.cs deleted file mode 100644 index 3bac601aef..0000000000 --- a/src/ImageSharp/Dithering/Ordered/BayerDither.cs +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Memory; - -namespace SixLabors.ImageSharp.Dithering -{ - /// - /// Applies order dithering using a Bayer dithering matrix of arbitrary length. - /// - /// - public class BayerDither : OrderedDitherBase - { - /// - /// Initializes a new instance of the class. - /// - /// - /// The exponent used to raise the base value (2). - /// The value given determines the dimensions of the matrix with each dimension a power of 2. e.g 2^2 = 4, 2^3 = 8 - /// - public BayerDither(uint exponent) - : base(ComputeBayer(exponent)) - { - } - - private static Fast2DArray ComputeBayer(uint order) - { - uint dimension = (uint)(1 << (int)order); - var matrix = new Fast2DArray((int)dimension); - uint i = 0; - for (int y = 0; y < dimension; y++) - { - for (int x = 0; x < dimension; x++) - { - matrix[y, x] = Bayer(i / dimension, i % dimension, order); - i++; - } - } - - return matrix; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint Bayer(uint x, uint y, uint order) - { - uint res = 0; - for (uint i = 0; i < order; ++i) - { - uint xOdd_XOR_yOdd = (x & 1) ^ (y & 1); - uint xOdd = x & 1; - res = ((res << 1 | xOdd_XOR_yOdd) << 1) | xOdd; - x >>= 1; - y >>= 1; - } - - return res; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Dithering/Ordered/Bayer2x2Dither.cs b/src/ImageSharp/Dithering/Ordered/BayerDither2x2.cs similarity index 63% rename from src/ImageSharp/Dithering/Ordered/Bayer2x2Dither.cs rename to src/ImageSharp/Dithering/Ordered/BayerDither2x2.cs index e96a9c4d34..1d844c8a79 100644 --- a/src/ImageSharp/Dithering/Ordered/Bayer2x2Dither.cs +++ b/src/ImageSharp/Dithering/Ordered/BayerDither2x2.cs @@ -6,13 +6,13 @@ namespace SixLabors.ImageSharp.Dithering /// /// Applies order dithering using the 2x2 Bayer dithering matrix. /// - public sealed class Bayer2x2Dither : BayerDither + public sealed class BayerDither2x2 : OrderedDither { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public Bayer2x2Dither() - : base(1) + public BayerDither2x2() + : base(2) { } } diff --git a/src/ImageSharp/Dithering/Ordered/Bayer4x4Dither.cs b/src/ImageSharp/Dithering/Ordered/BayerDither4x4.cs similarity index 63% rename from src/ImageSharp/Dithering/Ordered/Bayer4x4Dither.cs rename to src/ImageSharp/Dithering/Ordered/BayerDither4x4.cs index ad72c164f4..4e9f20beb9 100644 --- a/src/ImageSharp/Dithering/Ordered/Bayer4x4Dither.cs +++ b/src/ImageSharp/Dithering/Ordered/BayerDither4x4.cs @@ -6,13 +6,13 @@ namespace SixLabors.ImageSharp.Dithering /// /// Applies order dithering using the 4x4 Bayer dithering matrix. /// - public sealed class Bayer4x4Dither : BayerDither + public sealed class BayerDither4x4 : OrderedDither { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public Bayer4x4Dither() - : base(2) + public BayerDither4x4() + : base(4) { } } diff --git a/src/ImageSharp/Dithering/Ordered/Bayer8x8Dither.cs b/src/ImageSharp/Dithering/Ordered/BayerDither8x8.cs similarity index 63% rename from src/ImageSharp/Dithering/Ordered/Bayer8x8Dither.cs rename to src/ImageSharp/Dithering/Ordered/BayerDither8x8.cs index 9077dc2cc6..3ff179a06a 100644 --- a/src/ImageSharp/Dithering/Ordered/Bayer8x8Dither.cs +++ b/src/ImageSharp/Dithering/Ordered/BayerDither8x8.cs @@ -6,13 +6,13 @@ namespace SixLabors.ImageSharp.Dithering /// /// Applies order dithering using the 8x8 Bayer dithering matrix. /// - public sealed class Bayer8x8Dither : BayerDither + public sealed class BayerDither8x8 : OrderedDither { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public Bayer8x8Dither() - : base(3) + public BayerDither8x8() + : base(8) { } } diff --git a/src/ImageSharp/Dithering/Ordered/KnownDitherers.cs b/src/ImageSharp/Dithering/Ordered/KnownDitherers.cs new file mode 100644 index 0000000000..e58cbad8ab --- /dev/null +++ b/src/ImageSharp/Dithering/Ordered/KnownDitherers.cs @@ -0,0 +1,31 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Dithering +{ + /// + /// Contains reusable static instances of known ordered dither matrices + /// + public class KnownDitherers + { + /// + /// Gets the order ditherer using the 2x2 Bayer dithering matrix + /// + public static IOrderedDither BayerDither2x2 { get; } = new BayerDither2x2(); + + /// + /// Gets the order ditherer using the 3x3 dithering matrix + /// + public static IOrderedDither OrderedDither3x3 { get; } = new OrderedDither3x3(); + + /// + /// Gets the order ditherer using the 4x4 Bayer dithering matrix + /// + public static IOrderedDither BayerDither4x4 { get; } = new BayerDither4x4(); + + /// + /// Gets the order ditherer using the 8x8 Bayer dithering matrix + /// + public static IOrderedDither BayerDither8x8 { get; } = new BayerDither8x8(); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Dithering/Ordered/OrderedDither.cs b/src/ImageSharp/Dithering/Ordered/OrderedDither.cs new file mode 100644 index 0000000000..8f8210a8b0 --- /dev/null +++ b/src/ImageSharp/Dithering/Ordered/OrderedDither.cs @@ -0,0 +1,50 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Dithering +{ + /// + /// An ordered dithering matrix with equal sides of arbitrary length + /// + public class OrderedDither : IOrderedDither + { + private readonly Fast2DArray thresholdMatrix; + private readonly int modulusX; + private readonly int modulusY; + + /// + /// Initializes a new instance of the class. + /// + /// The length of the matrix sides + public OrderedDither(uint length) + { + Fast2DArray ditherMatrix = OrderedDitherFactory.CreateDitherMatrix(length); + this.modulusX = ditherMatrix.Width; + this.modulusY = ditherMatrix.Height; + + // Adjust the matrix range for 0-255 + // It looks like it's actually possible to dither an image using it's own colors. We should investigate for V2 + // https://stackoverflow.com/questions/12422407/monochrome-dithering-in-javascript-bayer-atkinson-floyd-steinberg + int multiplier = 256 / ditherMatrix.Count; + for (int y = 0; y < ditherMatrix.Height; y++) + { + for (int x = 0; x < ditherMatrix.Width; x++) + { + ditherMatrix[y, x] = (uint)((ditherMatrix[y, x] + 1) * multiplier) - 1; + } + } + + this.thresholdMatrix = ditherMatrix; + } + + /// + public void Dither(ImageFrame image, TPixel source, TPixel upper, TPixel lower, byte threshold, int x, int y) + where TPixel : struct, IPixel + { + image[x, y] = this.thresholdMatrix[y % this.modulusY, x % this.modulusX] >= threshold ? lower : upper; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Dithering/Ordered/OrderedDither3x3.cs b/src/ImageSharp/Dithering/Ordered/OrderedDither3x3.cs new file mode 100644 index 0000000000..0436b35e9c --- /dev/null +++ b/src/ImageSharp/Dithering/Ordered/OrderedDither3x3.cs @@ -0,0 +1,19 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Dithering +{ + /// + /// Applies order dithering using the 3x3 dithering matrix. + /// + public sealed class OrderedDither3x3 : OrderedDither + { + /// + /// Initializes a new instance of the class. + /// + public OrderedDither3x3() + : base(3) + { + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Dithering/Ordered/OrderedDitherBase.cs b/src/ImageSharp/Dithering/Ordered/OrderedDitherBase.cs deleted file mode 100644 index cf7a142397..0000000000 --- a/src/ImageSharp/Dithering/Ordered/OrderedDitherBase.cs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp.Dithering -{ - /// - /// The base class for performing ordered dithering using a dither matrix. - /// - public abstract class OrderedDitherBase : IOrderedDither - { - private readonly Fast2DArray matrix; - private readonly Fast2DArray thresholdMatrix; - private readonly int modulusX; - private readonly int modulusY; - - /// - /// Initializes a new instance of the class. - /// - /// The thresholding matrix. - internal OrderedDitherBase(Fast2DArray matrix) - { - this.matrix = matrix; - this.modulusX = matrix.Width; - this.modulusY = matrix.Height; - this.thresholdMatrix = new Fast2DArray(matrix.Width, matrix.Height); - - // Adjust the matrix range for 0-255 - int multiplier = 256 / (this.modulusX * this.modulusY); - for (int y = 0; y < matrix.Height; y++) - { - for (int x = 0; x < matrix.Width; x++) - { - this.thresholdMatrix[y, x] = (uint)((matrix[y, x] + 1) * multiplier) - 1; - } - } - } - - /// - public void Dither(ImageFrame image, TPixel source, TPixel upper, TPixel lower, byte threshold, int x, int y) - where TPixel : struct, IPixel - { - image[x, y] = this.thresholdMatrix[y % this.modulusY, x % this.modulusX] >= threshold ? lower : upper; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Dithering/Ordered/OrderedDitherFactory.cs b/src/ImageSharp/Dithering/Ordered/OrderedDitherFactory.cs new file mode 100644 index 0000000000..fc9ac25517 --- /dev/null +++ b/src/ImageSharp/Dithering/Ordered/OrderedDitherFactory.cs @@ -0,0 +1,94 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.Dithering +{ + /// + /// A factory for creating ordered dither matrices. + /// + internal static class OrderedDitherFactory + { + /// + /// Creates an ordered dithering matrix with equal sides of arbitrary length. + /// + /// + /// The length of the matrix sides + /// The + public static Fast2DArray CreateDitherMatrix(uint length) + { + // Calculate the the logarithm of length to the base 2 + uint exponent = 0; + uint bayerLength = 0; + do + { + exponent++; + bayerLength = (uint)(1 << (int)exponent); + } + while (length > bayerLength); + + // Create our Bayer matrix that matches the given exponent and dimensions + var matrix = new Fast2DArray((int)length); + uint i = 0; + for (int y = 0; y < length; y++) + { + for (int x = 0; x < length; x++) + { + matrix[y, x] = Bayer(i / length, i % length, exponent); + i++; + } + } + + // If the user requested a matrix with a non-power-of-2 length e.g. 3x3 and we used 4x4 algorithm, + // we need to convert the numbers so that the resulting range is un-gapped. + // We generated: We saved: We compress the number range: + // 0 8 2 10 0 8 2 0 5 2 + // 12 4 14 6 12 4 14 7 4 8 + // 3 11 1 9 3 11 1 3 6 1 + // 15 7 13 5 + uint maxValue = bayerLength * bayerLength; + uint missing = 0; + for (uint v = 0; v < maxValue; ++v) + { + bool found = false; + for (int y = 0; y < length; ++y) + { + for (int x = 0; x < length; x++) + { + if (matrix[y, x] == v) + { + matrix[y, x] -= missing; + found = true; + break; + } + } + } + + if (!found) + { + ++missing; + } + } + + return matrix; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint Bayer(uint x, uint y, uint order) + { + uint result = 0; + for (uint i = 0; i < order; ++i) + { + uint xOdd_XOR_yOdd = (x & 1) ^ (y & 1); + uint xOdd = x & 1; + result = ((result << 1 | xOdd_XOR_yOdd) << 1) | xOdd; + x >>= 1; + y >>= 1; + } + + return result; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Dithering/error_diffusion.txt b/src/ImageSharp/Dithering/error_diffusion.txt new file mode 100644 index 0000000000..ea412f6351 --- /dev/null +++ b/src/ImageSharp/Dithering/error_diffusion.txt @@ -0,0 +1,58 @@ +List of error diffusion schemes. + +Quantization error of *current* pixel is added to the pixels +on the right and below according to the formulas below. +This works nicely for most static pictures, but causes +an avalanche of jittering artifacts if used in animation. + +Floyd-Steinberg: + + * 7 + 3 5 1 / 16 + +Jarvis-Judice-Ninke: + + * 7 5 + 3 5 7 5 3 + 1 3 5 3 1 / 48 + +Stucki: + + * 8 4 + 2 4 8 4 2 + 1 2 4 2 1 / 42 + +Burkes: + + * 8 4 + 2 4 8 4 2 / 32 + + +Sierra3: + + * 5 3 + 2 4 5 4 2 + 2 3 2 / 32 + +Sierra2: + + * 4 3 + 1 2 3 2 1 / 16 + +Sierra-2-4A: + + * 2 + 1 1 / 4 + +Stevenson-Arce: + + * . 32 + 12 . 26 . 30 . 16 + . 12 . 26 . 12 . + 5 . 12 . 12 . 5 / 200 + +Atkinson: + + * 1 1 / 8 + 1 1 1 + 1 diff --git a/src/ImageSharp/Memory/Fast2DArray{T}.cs b/src/ImageSharp/Memory/Fast2DArray{T}.cs index e0384d2084..38ccdd279d 100644 --- a/src/ImageSharp/Memory/Fast2DArray{T}.cs +++ b/src/ImageSharp/Memory/Fast2DArray{T}.cs @@ -28,6 +28,11 @@ namespace SixLabors.ImageSharp.Memory /// public int Height; + /// + /// Gets the number of items in the 2D array + /// + public int Count; + /// /// Initializes a new instance of the struct. /// @@ -50,7 +55,8 @@ namespace SixLabors.ImageSharp.Memory Guard.MustBeGreaterThan(width, 0, nameof(width)); Guard.MustBeGreaterThan(height, 0, nameof(height)); - this.Data = new T[this.Width * this.Height]; + this.Count = width * height; + this.Data = new T[this.Count]; } /// @@ -66,7 +72,8 @@ namespace SixLabors.ImageSharp.Memory Guard.MustBeGreaterThan(this.Width, 0, nameof(this.Width)); Guard.MustBeGreaterThan(this.Height, 0, nameof(this.Height)); - this.Data = new T[this.Width * this.Height]; + this.Count = this.Width * this.Height; + this.Data = new T[this.Count]; for (int y = 0; y < this.Height; y++) { diff --git a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs index f8ff475d16..1102a48e4b 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs @@ -104,7 +104,7 @@ namespace SixLabors.ImageSharp.Processing.Processors previousPixel = sourcePixel; } - TPixel transformedPixel = luminance >= threshold ? pair.First : pair.Second; + TPixel transformedPixel = luminance >= threshold ? pair.Second : pair.First; this.Diffuser.Dither(source, sourcePixel, transformedPixel, x, y, startX, startY, endX, endY); } } diff --git a/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor.cs b/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor.cs index 49455928af..0a49f99cfc 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor.cs @@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp.Processing.Processors previousPixel = sourcePixel; } - this.Dither.Dither(source, sourcePixel, pair.First, pair.Second, luminance, x, y); + this.Dither.Dither(source, sourcePixel, pair.Second, pair.First, luminance, x, y); } } } diff --git a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessorBase.cs b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessorBase.cs index c6b80293cf..b3c564edbd 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessorBase.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessorBase.cs @@ -50,8 +50,7 @@ namespace SixLabors.ImageSharp.Processing.Processors for (int index = 0; index < colorPalette.Length; index++) { TPixel temp = colorPalette[index]; - var tempVector = temp.ToVector4(); - float distance = Vector4.Distance(vector, tempVector); + float distance = Vector4.Distance(vector, temp.ToVector4()); if (distance < leastDistance) { diff --git a/src/ImageSharp/Quantizers/QuantizerBase{TPixel}.cs b/src/ImageSharp/Quantizers/QuantizerBase{TPixel}.cs index 20ba2e637e..31e424060b 100644 --- a/src/ImageSharp/Quantizers/QuantizerBase{TPixel}.cs +++ b/src/ImageSharp/Quantizers/QuantizerBase{TPixel}.cs @@ -43,7 +43,7 @@ namespace SixLabors.ImageSharp.Quantizers.Base public bool Dither { get; set; } = true; /// - public IErrorDiffuser DitherType { get; set; } = new FloydSteinbergDiffuser(); + public IErrorDiffuser DitherType { get; set; } = KnownDiffusers.FloydSteinberg; /// public virtual QuantizedImage Quantize(ImageFrame image, int maxColors) diff --git a/tests/ImageSharp.Tests/Memory/Fast2DArrayTests.cs b/tests/ImageSharp.Tests/Memory/Fast2DArrayTests.cs index 5cdbe638a6..a5364db721 100644 --- a/tests/ImageSharp.Tests/Memory/Fast2DArrayTests.cs +++ b/tests/ImageSharp.Tests/Memory/Fast2DArrayTests.cs @@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Tests.Memory { Assert.Throws(() => { - Fast2DArray fast = new Fast2DArray(null); + var fast = new Fast2DArray(null); }); } @@ -31,7 +31,7 @@ namespace SixLabors.ImageSharp.Tests.Memory { Assert.Throws(() => { - Fast2DArray fast = new Fast2DArray(0, 10); + var fast = new Fast2DArray(0, 10); }); } @@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Tests.Memory { Assert.Throws(() => { - Fast2DArray fast = new Fast2DArray(10, 0); + var fast = new Fast2DArray(10, 0); }); } @@ -49,14 +49,14 @@ namespace SixLabors.ImageSharp.Tests.Memory { Assert.Throws(() => { - Fast2DArray fast = new Fast2DArray(new float[0, 0]); + var fast = new Fast2DArray(new float[0, 0]); }); } [Fact] public void Fast2DArrayReturnsCorrectDimensions() { - Fast2DArray fast = new Fast2DArray(FloydSteinbergMatrix); + var fast = new Fast2DArray(FloydSteinbergMatrix); Assert.True(fast.Width == FloydSteinbergMatrix.GetLength(1)); Assert.True(fast.Height == FloydSteinbergMatrix.GetLength(0)); } @@ -78,7 +78,7 @@ namespace SixLabors.ImageSharp.Tests.Memory [Fact] public void Fast2DArrayGetSetReturnsCorrectResults() { - Fast2DArray fast = new Fast2DArray(4, 4); + var fast = new Fast2DArray(4, 4); const float Val = 5F; fast[3, 3] = Val; diff --git a/tests/ImageSharp.Tests/Processing/Binarization/DitherTests.cs b/tests/ImageSharp.Tests/Processing/Binarization/DitherTests.cs index ba5cb0cf33..f801b20313 100644 --- a/tests/ImageSharp.Tests/Processing/Binarization/DitherTests.cs +++ b/tests/ImageSharp.Tests/Processing/Binarization/DitherTests.cs @@ -4,7 +4,6 @@ using SixLabors.ImageSharp.Dithering; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors; -using Moq; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Binarization @@ -16,9 +15,10 @@ namespace SixLabors.ImageSharp.Tests.Processing.Binarization public DitherTest() { - this.orderedDither = new Mock().Object; - this.errorDiffuser = new Mock().Object; + this.orderedDither = KnownDitherers.BayerDither4x4; + this.errorDiffuser = KnownDiffusers.FloydSteinberg; } + [Fact] public void Dither_CorrectProcessor() { diff --git a/tests/ImageSharp.Tests/Processing/Binarization/OrderedDitherFactoryTests.cs b/tests/ImageSharp.Tests/Processing/Binarization/OrderedDitherFactoryTests.cs new file mode 100644 index 0000000000..a0ddc2c7cc --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Binarization/OrderedDitherFactoryTests.cs @@ -0,0 +1,102 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Dithering; +using SixLabors.ImageSharp.Memory; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Binarization +{ + public class OrderedDitherFactoryTests + { + private static readonly Fast2DArray Expected2x2Matrix = new Fast2DArray( + new uint[2, 2] + { + { 0, 2 }, + { 3, 1 } + }); + + private static readonly Fast2DArray Expected3x3Matrix = new Fast2DArray( + new uint[3, 3] + { + { 0, 5, 2 }, + { 7, 4, 8 }, + { 3, 6, 1 } + }); + + private static readonly Fast2DArray Expected4x4Matrix = new Fast2DArray( + new uint[4, 4] + { + { 0, 8, 2, 10 }, + { 12, 4, 14, 6 }, + { 3, 11, 1, 9 }, + { 15, 7, 13, 5 } + }); + + private static readonly Fast2DArray Expected8x8Matrix = new Fast2DArray( + new uint[8, 8] + { + { 0, 32, 8, 40, 2, 34, 10, 42 }, + { 48, 16, 56, 24, 50, 18, 58, 26 }, + { 12, 44, 4, 36, 14, 46, 6, 38 }, + { 60, 28, 52, 20, 62, 30, 54, 22 }, + { 3, 35, 11, 43, 1, 33, 9, 41 }, + { 51, 19, 59, 27, 49, 17, 57, 25 }, + { 15, 47, 7, 39, 13, 45, 5, 37 }, + { 63, 31, 55, 23, 61, 29, 53, 21 } + }); + + + [Fact] + public void OrderedDitherFactoryCreatesCorrect2x2Matrix() + { + Fast2DArray actual = OrderedDitherFactory.CreateDitherMatrix(2); + for (int y = 0; y < actual.Height; y++) + { + for (int x = 0; x < actual.Width; x++) + { + Assert.Equal(Expected2x2Matrix[y, x], actual[y, x]); + } + } + } + + [Fact] + public void OrderedDitherFactoryCreatesCorrect3x3Matrix() + { + Fast2DArray actual = OrderedDitherFactory.CreateDitherMatrix(3); + for (int y = 0; y < actual.Height; y++) + { + for (int x = 0; x < actual.Width; x++) + { + Assert.Equal(Expected3x3Matrix[y, x], actual[y, x]); + } + } + } + + [Fact] + public void OrderedDitherFactoryCreatesCorrect4x4Matrix() + { + Fast2DArray actual = OrderedDitherFactory.CreateDitherMatrix(4); + for (int y = 0; y < actual.Height; y++) + { + for (int x = 0; x < actual.Width; x++) + { + Assert.Equal(Expected4x4Matrix[y, x], actual[y, x]); + } + } + } + + [Fact] + public void OrderedDitherFactoryCreatesCorrect8x8Matrix() + { + Fast2DArray actual = OrderedDitherFactory.CreateDitherMatrix(8); + for (int y = 0; y < actual.Height; y++) + { + for (int x = 0; x < actual.Width; x++) + { + Assert.Equal(Expected8x8Matrix[y, x], actual[y, x]); + } + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Binarization/DitherTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Binarization/DitherTests.cs index 3ddf9d0fec..6db1434c68 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Binarization/DitherTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Binarization/DitherTests.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Dithering; +using SixLabors.ImageSharp.Dithering; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; @@ -11,8 +11,6 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization { - using System.Linq; - public class DitherTests : FileTestBase { public static readonly string[] CommonTestImages = @@ -22,27 +20,29 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization public static readonly TheoryData Ditherers = new TheoryData { - { "Bayer8x8", new Bayer8x8Dither() }, - { "Bayer4x4", new Bayer4x4Dither() }, - { "Bayer2x2", new Bayer2x2Dither() } + { "Bayer8x8", KnownDitherers.BayerDither8x8 }, + { "Bayer4x4", KnownDitherers.BayerDither4x4 }, + { "Ordered3x3", KnownDitherers.OrderedDither3x3 }, + { "Bayer2x2", KnownDitherers.BayerDither2x2 } }; public static readonly TheoryData ErrorDiffusers = new TheoryData { - { "Atkinson", new AtkinsonDiffuser() }, - { "Burks", new BurksDiffuser() }, - { "FloydSteinberg", new FloydSteinbergDiffuser() }, - { "JarvisJudiceNinke", new JarvisJudiceNinkeDiffuser() }, - { "Sierra2", new Sierra2Diffuser() }, - { "Sierra3", new Sierra3Diffuser() }, - { "SierraLite", new SierraLiteDiffuser() }, - { "Stucki", new StuckiDiffuser() }, + { "Atkinson", KnownDiffusers.Atkinson }, + { "Burks", KnownDiffusers.Burks }, + { "FloydSteinberg", KnownDiffusers.FloydSteinberg }, + { "JarvisJudiceNinke", KnownDiffusers.JarvisJudiceNinke }, + { "Sierra2", KnownDiffusers.Sierra2 }, + { "Sierra3", KnownDiffusers.Sierra3 }, + { "SierraLite", KnownDiffusers.SierraLite }, + { "StevensonArce", KnownDiffusers.StevensonArce }, + { "Stucki", KnownDiffusers.Stucki }, }; - private static IOrderedDither DefaultDitherer => new Bayer4x4Dither(); + private static IOrderedDither DefaultDitherer => KnownDitherers.BayerDither4x4; - private static IErrorDiffuser DefaultErrorDiffuser => new AtkinsonDiffuser(); + private static IErrorDiffuser DefaultErrorDiffuser => KnownDiffusers.Atkinson; [Theory] [WithFileCollection(nameof(CommonTestImages), nameof(Ditherers), DefaultPixelType)] @@ -81,7 +81,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization image.DebugSave(provider); } } - + [Theory] [WithFile(TestImages.Png.Bike, CommonNonDefaultPixelTypes)] public void DiffusionFilter_ShouldNotDependOnSinglePixelType(TestImageProvider provider) From 76fa8be0097eacb18f826f913bab24b4ea9a6af4 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 5 Feb 2018 02:48:39 +1100 Subject: [PATCH 025/151] Fix unit test --- tests/ImageSharp.Tests/PixelFormats/ColorDefinitionTests.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/PixelFormats/ColorDefinitionTests.cs b/tests/ImageSharp.Tests/PixelFormats/ColorDefinitionTests.cs index af4181cde2..302e56ec71 100644 --- a/tests/ImageSharp.Tests/PixelFormats/ColorDefinitionTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/ColorDefinitionTests.cs @@ -16,7 +16,8 @@ namespace SixLabors.ImageSharp.Tests get { var result = new TheoryData(); - foreach (string name in typeof(NamedColors).GetTypeInfo().GetFields().Select(x => x.Name )) + foreach (string name in typeof(NamedColors).GetTypeInfo() + .GetFields().Where(x => x.Name != nameof(NamedColors.WebSafePalette)).Select(x => x.Name)) { result.Add(name); } From a9ae5efedd1ad08b9afd4f79367ca194b63f68c3 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 5 Feb 2018 10:22:01 +1100 Subject: [PATCH 026/151] Add dither tests --- .../{DitherTests.cs => BinaryDitherTest.cs} | 20 +-- .../Processing/Dithering/DitherTest.cs | 104 ++++++++++++++ .../{DitherTests.cs => BinaryDitherTests.cs} | 6 +- .../Processors/Dithering/DitherTests.cs | 131 ++++++++++++++++++ 4 files changed, 248 insertions(+), 13 deletions(-) rename tests/ImageSharp.Tests/Processing/Binarization/{DitherTests.cs => BinaryDitherTest.cs} (86%) create mode 100644 tests/ImageSharp.Tests/Processing/Dithering/DitherTest.cs rename tests/ImageSharp.Tests/Processing/Processors/Binarization/{DitherTests.cs => BinaryDitherTests.cs} (94%) create mode 100644 tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs diff --git a/tests/ImageSharp.Tests/Processing/Binarization/DitherTests.cs b/tests/ImageSharp.Tests/Processing/Binarization/BinaryDitherTest.cs similarity index 86% rename from tests/ImageSharp.Tests/Processing/Binarization/DitherTests.cs rename to tests/ImageSharp.Tests/Processing/Binarization/BinaryDitherTest.cs index f801b20313..003f998d88 100644 --- a/tests/ImageSharp.Tests/Processing/Binarization/DitherTests.cs +++ b/tests/ImageSharp.Tests/Processing/Binarization/BinaryDitherTest.cs @@ -8,19 +8,19 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Binarization { - public class DitherTest : BaseImageOperationsExtensionTest + public class BinaryDitherTest : BaseImageOperationsExtensionTest { private readonly IOrderedDither orderedDither; private readonly IErrorDiffuser errorDiffuser; - public DitherTest() + public BinaryDitherTest() { this.orderedDither = KnownDitherers.BayerDither4x4; this.errorDiffuser = KnownDiffusers.FloydSteinberg; } [Fact] - public void Dither_CorrectProcessor() + public void BinaryDither_CorrectProcessor() { this.operations.BinaryDither(this.orderedDither); BinaryOrderedDitherProcessor p = this.Verify>(); @@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Binarization } [Fact] - public void Dither_rect_CorrectProcessor() + public void BinaryDither_rect_CorrectProcessor() { this.operations.BinaryDither(this.orderedDither, this.rect); BinaryOrderedDitherProcessor p = this.Verify>(this.rect); @@ -39,7 +39,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Binarization Assert.Equal(NamedColors.Black, p.LowerColor); } [Fact] - public void Dither_index_CorrectProcessor() + public void BinaryDither_index_CorrectProcessor() { this.operations.BinaryDither(this.orderedDither, NamedColors.Yellow, NamedColors.HotPink); BinaryOrderedDitherProcessor p = this.Verify>(); @@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Binarization } [Fact] - public void Dither_index_rect_CorrectProcessor() + public void BinaryDither_index_rect_CorrectProcessor() { this.operations.BinaryDither(this.orderedDither, NamedColors.Yellow, NamedColors.HotPink, this.rect); BinaryOrderedDitherProcessor p = this.Verify>(this.rect); @@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Binarization [Fact] - public void Dither_ErrorDiffuser_CorrectProcessor() + public void BinaryDither_ErrorDiffuser_CorrectProcessor() { this.operations.BinaryDiffuse(this.errorDiffuser, .4F); BinaryErrorDiffusionProcessor p = this.Verify>(); @@ -70,7 +70,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Binarization } [Fact] - public void Dither_ErrorDiffuser_rect_CorrectProcessor() + public void BinaryDither_ErrorDiffuser_rect_CorrectProcessor() { this.operations.BinaryDiffuse(this.errorDiffuser, .3F, this.rect); BinaryErrorDiffusionProcessor p = this.Verify>(this.rect); @@ -81,7 +81,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Binarization } [Fact] - public void Dither_ErrorDiffuser_CorrectProcessorWithColors() + public void BinaryDither_ErrorDiffuser_CorrectProcessorWithColors() { this.operations.BinaryDiffuse(this.errorDiffuser, .5F, NamedColors.HotPink, NamedColors.Yellow); BinaryErrorDiffusionProcessor p = this.Verify>(); @@ -92,7 +92,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Binarization } [Fact] - public void Dither_ErrorDiffuser_rect_CorrectProcessorWithColors() + public void BinaryDither_ErrorDiffuser_rect_CorrectProcessorWithColors() { this.operations.BinaryDiffuse(this.errorDiffuser, .5F, NamedColors.HotPink, NamedColors.Yellow, this.rect); BinaryErrorDiffusionProcessor p = this.Verify>(this.rect); diff --git a/tests/ImageSharp.Tests/Processing/Dithering/DitherTest.cs b/tests/ImageSharp.Tests/Processing/Dithering/DitherTest.cs new file mode 100644 index 0000000000..03ae17848c --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Dithering/DitherTest.cs @@ -0,0 +1,104 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Dithering; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Binarization +{ + public class DitherTest : BaseImageOperationsExtensionTest + { + private readonly IOrderedDither orderedDither; + private readonly IErrorDiffuser errorDiffuser; + private readonly Rgba32[] TestPalette = + { + Rgba32.Red, + Rgba32.Green, + Rgba32.Blue + }; + + public DitherTest() + { + this.orderedDither = KnownDitherers.BayerDither4x4; + this.errorDiffuser = KnownDiffusers.FloydSteinberg; + } + + [Fact] + public void Dither_CorrectProcessor() + { + this.operations.Dither(this.orderedDither); + OrderedDitherPaletteProcessor p = this.Verify>(); + Assert.Equal(this.orderedDither, p.Dither); + Assert.Equal(NamedColors.WebSafePalette, p.Palette); + } + + [Fact] + public void Dither_rect_CorrectProcessor() + { + this.operations.Dither(this.orderedDither, this.rect); + OrderedDitherPaletteProcessor p = this.Verify>(this.rect); + Assert.Equal(this.orderedDither, p.Dither); + Assert.Equal(NamedColors.WebSafePalette, p.Palette); + } + [Fact] + public void Dither_index_CorrectProcessor() + { + this.operations.Dither(this.orderedDither, this.TestPalette); + OrderedDitherPaletteProcessor p = this.Verify>(); + Assert.Equal(this.orderedDither, p.Dither); + Assert.Equal(this.TestPalette, p.Palette); + } + + [Fact] + public void Dither_index_rect_CorrectProcessor() + { + this.operations.Dither(this.orderedDither, this.TestPalette, this.rect); + OrderedDitherPaletteProcessor p = this.Verify>(this.rect); + Assert.Equal(this.orderedDither, p.Dither); + Assert.Equal(this.TestPalette, p.Palette); + } + + + [Fact] + public void Dither_ErrorDiffuser_CorrectProcessor() + { + this.operations.Diffuse(this.errorDiffuser, .4F); + ErrorDiffusionPaletteProcessor p = this.Verify>(); + Assert.Equal(this.errorDiffuser, p.Diffuser); + Assert.Equal(.4F, p.Threshold); + Assert.Equal(NamedColors.WebSafePalette, p.Palette); + } + + [Fact] + public void Dither_ErrorDiffuser_rect_CorrectProcessor() + { + this.operations.Diffuse(this.errorDiffuser, .3F, this.rect); + ErrorDiffusionPaletteProcessor p = this.Verify>(this.rect); + Assert.Equal(this.errorDiffuser, p.Diffuser); + Assert.Equal(.3F, p.Threshold); + Assert.Equal(NamedColors.WebSafePalette, p.Palette); + } + + [Fact] + public void Dither_ErrorDiffuser_CorrectProcessorWithColors() + { + this.operations.Diffuse(this.errorDiffuser, .5F, this.TestPalette); + ErrorDiffusionPaletteProcessor p = this.Verify>(); + Assert.Equal(this.errorDiffuser, p.Diffuser); + Assert.Equal(.5F, p.Threshold); + Assert.Equal(this.TestPalette, p.Palette); + } + + [Fact] + public void Dither_ErrorDiffuser_rect_CorrectProcessorWithColors() + { + this.operations.Diffuse(this.errorDiffuser, .5F, this.TestPalette, this.rect); + ErrorDiffusionPaletteProcessor p = this.Verify>(this.rect); + Assert.Equal(this.errorDiffuser, p.Diffuser); + Assert.Equal(.5F, p.Threshold); + Assert.Equal(this.TestPalette, p.Palette); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Binarization/DitherTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryDitherTests.cs similarity index 94% rename from tests/ImageSharp.Tests/Processing/Processors/Binarization/DitherTests.cs rename to tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryDitherTests.cs index 6db1434c68..aec201239d 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Binarization/DitherTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryDitherTests.cs @@ -11,7 +11,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization { - public class DitherTests : FileTestBase + public class BinaryDitherTests : FileTestBase { public static readonly string[] CommonTestImages = { @@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization [Theory] [WithFileCollection(nameof(CommonTestImages), nameof(Ditherers), DefaultPixelType)] [WithTestPatternImages(nameof(Ditherers), 100, 100, DefaultPixelType)] - public void DitherFilter_WorksWithAllDitherers(TestImageProvider provider, string name, IOrderedDither ditherer) + public void BinaryDitherFilter_WorksWithAllDitherers(TestImageProvider provider, string name, IOrderedDither ditherer) where TPixel : struct, IPixel { using (Image image = provider.GetImage()) @@ -72,7 +72,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization [Theory] [WithFile(TestImages.Png.Bike, CommonNonDefaultPixelTypes)] - public void DitherFilter_ShouldNotDependOnSinglePixelType(TestImageProvider provider) + public void BinaryDitherFilter_ShouldNotDependOnSinglePixelType(TestImageProvider provider) where TPixel : struct, IPixel { using (Image image = provider.GetImage()) diff --git a/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs new file mode 100644 index 0000000000..58c63d48c7 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs @@ -0,0 +1,131 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Dithering; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + +using SixLabors.Primitives; +using Xunit; +// ReSharper disable InconsistentNaming + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization +{ + public class DitherTests : FileTestBase + { + public static readonly string[] CommonTestImages = + { + TestImages.Png.CalliphoraPartial, TestImages.Png.Bike + }; + + public static readonly TheoryData Ditherers = new TheoryData + { + { "Bayer8x8", KnownDitherers.BayerDither8x8 }, + { "Bayer4x4", KnownDitherers.BayerDither4x4 }, + { "Ordered3x3", KnownDitherers.OrderedDither3x3 }, + { "Bayer2x2", KnownDitherers.BayerDither2x2 } + }; + + public static readonly TheoryData ErrorDiffusers = new TheoryData + { + { "Atkinson", KnownDiffusers.Atkinson }, + { "Burks", KnownDiffusers.Burks }, + { "FloydSteinberg", KnownDiffusers.FloydSteinberg }, + { "JarvisJudiceNinke", KnownDiffusers.JarvisJudiceNinke }, + { "Sierra2", KnownDiffusers.Sierra2 }, + { "Sierra3", KnownDiffusers.Sierra3 }, + { "SierraLite", KnownDiffusers.SierraLite }, + { "StevensonArce", KnownDiffusers.StevensonArce }, + { "Stucki", KnownDiffusers.Stucki }, + }; + + + private static IOrderedDither DefaultDitherer => KnownDitherers.BayerDither4x4; + + private static IErrorDiffuser DefaultErrorDiffuser => KnownDiffusers.Atkinson; + + [Theory] + [WithFileCollection(nameof(CommonTestImages), nameof(Ditherers), DefaultPixelType)] + [WithTestPatternImages(nameof(Ditherers), 100, 100, DefaultPixelType)] + public void DitherFilter_WorksWithAllDitherers(TestImageProvider provider, string name, IOrderedDither ditherer) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Dither(ditherer)); + image.DebugSave(provider, name); + } + } + + [Theory] + [WithFileCollection(nameof(CommonTestImages), nameof(ErrorDiffusers), DefaultPixelType)] + [WithTestPatternImages(nameof(ErrorDiffusers), 100, 100, DefaultPixelType)] + public void DiffusionFilter_WorksWithAllErrorDiffusers(TestImageProvider provider, string name, IErrorDiffuser diffuser) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Diffuse(diffuser, .5F)); + image.DebugSave(provider, name); + } + } + + [Theory] + [WithFile(TestImages.Png.Bike, CommonNonDefaultPixelTypes)] + public void DitherFilter_ShouldNotDependOnSinglePixelType(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Dither(DefaultDitherer)); + image.DebugSave(provider); + } + } + + [Theory] + [WithFile(TestImages.Png.Bike, CommonNonDefaultPixelTypes)] + public void DiffusionFilter_ShouldNotDependOnSinglePixelType(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Diffuse(DefaultErrorDiffuser, 0.5f)); + image.DebugSave(provider); + } + } + + [Theory] + [WithFile(TestImages.Png.CalliphoraPartial, DefaultPixelType)] + public void ApplyDitherFilterInBox(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (Image image = source.Clone()) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Mutate(x => x.Dither(DefaultDitherer, bounds)); + image.DebugSave(provider); + + ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); + } + } + + [Theory] + [WithFile(TestImages.Png.CalliphoraPartial, DefaultPixelType)] + public void ApplyDiffusionFilterInBox(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (Image image = source.Clone()) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Mutate(x => x.Diffuse(DefaultErrorDiffuser, .5F, bounds)); + image.DebugSave(provider); + + ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); + } + } + } +} \ No newline at end of file From b448724434e08f09a4e7cc19f7c0272b08143a1e Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 5 Feb 2018 12:24:17 +1100 Subject: [PATCH 027/151] Use float threshold for dither --- src/ImageSharp/Dithering/Ordered/IOrderedDither.cs | 2 +- src/ImageSharp/Dithering/Ordered/OrderedDither.cs | 2 +- .../Processors/Binarization/BinaryErrorDiffusionProcessor.cs | 4 ++-- .../Processors/Binarization/BinaryOrderedDitherProcessor.cs | 4 ++-- .../Processors/Dithering/ErrorDiffusionPaletteProcessor.cs | 4 ++-- .../Processors/Dithering/OrderedDitherPaletteProcessor.cs | 4 ++-- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs b/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs index 5d05be370d..339f2861d9 100644 --- a/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs +++ b/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs @@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Dithering /// The column index. /// The row index. /// The pixel format. - void Dither(ImageFrame image, TPixel source, TPixel upper, TPixel lower, byte threshold, int x, int y) + void Dither(ImageFrame image, TPixel source, TPixel upper, TPixel lower, float threshold, int x, int y) where TPixel : struct, IPixel; } } \ No newline at end of file diff --git a/src/ImageSharp/Dithering/Ordered/OrderedDither.cs b/src/ImageSharp/Dithering/Ordered/OrderedDither.cs index 8f8210a8b0..c07b185bbe 100644 --- a/src/ImageSharp/Dithering/Ordered/OrderedDither.cs +++ b/src/ImageSharp/Dithering/Ordered/OrderedDither.cs @@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Dithering } /// - public void Dither(ImageFrame image, TPixel source, TPixel upper, TPixel lower, byte threshold, int x, int y) + public void Dither(ImageFrame image, TPixel source, TPixel upper, TPixel lower, float threshold, int x, int y) where TPixel : struct, IPixel { image[x, y] = this.thresholdMatrix[y % this.modulusY, x % this.modulusX] >= threshold ? lower : upper; diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor.cs index 70d903f31e..80a4236452 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor.cs @@ -93,7 +93,7 @@ namespace SixLabors.ImageSharp.Processing.Processors sourcePixel.ToRgba32(ref rgba); // Convert to grayscale using ITU-R Recommendation BT.709 if required - byte luminance = (byte)(isAlphaOnly ? rgba.A : (.2126F * rgba.R) + (.7152F * rgba.G) + (.0722F * rgba.B)).Clamp(0, 255); + float luminance = isAlphaOnly ? rgba.A : (.2126F * rgba.R) + (.7152F * rgba.G) + (.0722F * rgba.B); for (int y = startY; y < endY; y++) { @@ -108,7 +108,7 @@ namespace SixLabors.ImageSharp.Processing.Processors if (!previousPixel.Equals(sourcePixel)) { sourcePixel.ToRgba32(ref rgba); - luminance = (byte)(isAlphaOnly ? rgba.A : (.2126F * rgba.R) + (.7152F * rgba.G) + (.0722F * rgba.B)).Clamp(0, 255); + luminance = isAlphaOnly ? rgba.A : (.2126F * rgba.R) + (.7152F * rgba.G) + (.0722F * rgba.B); // Setup the previous pointer previousPixel = sourcePixel; diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryOrderedDitherProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryOrderedDitherProcessor.cs index 3cabe378a1..baa8df8cca 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/BinaryOrderedDitherProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryOrderedDitherProcessor.cs @@ -74,7 +74,7 @@ namespace SixLabors.ImageSharp.Processing.Processors sourcePixel.ToRgba32(ref rgba); // Convert to grayscale using ITU-R Recommendation BT.709 if required - byte luminance = (byte)(isAlphaOnly ? rgba.A : (.2126F * rgba.R) + (.7152F * rgba.G) + (.0722F * rgba.B)).Clamp(0, 255); + float luminance = isAlphaOnly ? rgba.A : (.2126F * rgba.R) + (.7152F * rgba.G) + (.0722F * rgba.B); for (int y = startY; y < endY; y++) { @@ -89,7 +89,7 @@ namespace SixLabors.ImageSharp.Processing.Processors if (!previousPixel.Equals(sourcePixel)) { sourcePixel.ToRgba32(ref rgba); - luminance = (byte)(isAlphaOnly ? rgba.A : (.2126F * rgba.R) + (.7152F * rgba.G) + (.0722F * rgba.B)).Clamp(0, 255); + luminance = isAlphaOnly ? rgba.A : (.2126F * rgba.R) + (.7152F * rgba.G) + (.0722F * rgba.B); // Setup the previous pointer previousPixel = sourcePixel; diff --git a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs index 1102a48e4b..152959cb73 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs @@ -82,7 +82,7 @@ namespace SixLabors.ImageSharp.Processing.Processors sourcePixel.ToRgba32(ref rgba); // Convert to grayscale using ITU-R Recommendation BT.709 if required - byte luminance = (byte)(isAlphaOnly ? rgba.A : (.2126F * rgba.R) + (.7152F * rgba.G) + (.0722F * rgba.B)).Clamp(0, 255); + float luminance = isAlphaOnly ? rgba.A : (.2126F * rgba.R) + (.7152F * rgba.G) + (.0722F * rgba.B); for (int y = startY; y < endY; y++) { @@ -98,7 +98,7 @@ namespace SixLabors.ImageSharp.Processing.Processors { pair = this.GetClosestPixelPair(ref sourcePixel, this.Palette); sourcePixel.ToRgba32(ref rgba); - luminance = (byte)(isAlphaOnly ? rgba.A : (.2126F * rgba.R) + (.7152F * rgba.G) + (.0722F * rgba.B)).Clamp(0, 255); + luminance = isAlphaOnly ? rgba.A : (.2126F * rgba.R) + (.7152F * rgba.G) + (.0722F * rgba.B); // Setup the previous pointer previousPixel = sourcePixel; diff --git a/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor.cs b/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor.cs index 0a49f99cfc..4fc59585a9 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor.cs @@ -63,7 +63,7 @@ namespace SixLabors.ImageSharp.Processing.Processors sourcePixel.ToRgba32(ref rgba); // Convert to grayscale using ITU-R Recommendation BT.709 if required - byte luminance = (byte)(isAlphaOnly ? rgba.A : (.2126F * rgba.R) + (.7152F * rgba.G) + (.0722F * rgba.B)).Clamp(0, 255); + float luminance = isAlphaOnly ? rgba.A : (.2126F * rgba.R) + (.7152F * rgba.G) + (.0722F * rgba.B); for (int y = startY; y < endY; y++) { @@ -79,7 +79,7 @@ namespace SixLabors.ImageSharp.Processing.Processors { pair = this.GetClosestPixelPair(ref sourcePixel, this.Palette); sourcePixel.ToRgba32(ref rgba); - luminance = (byte)(isAlphaOnly ? rgba.A : (.2126F * rgba.R) + (.7152F * rgba.G) + (.0722F * rgba.B)).Clamp(0, 255); + luminance = isAlphaOnly ? rgba.A : (.2126F * rgba.R) + (.7152F * rgba.G) + (.0722F * rgba.B); // Setup the previous pointer previousPixel = sourcePixel; From 2c12f2046fc02f86fd362a9b8f7bcb34306cc4a8 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 6 Feb 2018 13:18:38 +1100 Subject: [PATCH 028/151] Replace CoreCompat.System.Drawing with System.Drawing.Common --- .../ImageSharp.Benchmarks.csproj | 13 ++++--------- tests/ImageSharp.Tests/ImageSharp.Tests.csproj | 10 +++++----- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj index 417e849be1..2e0b935155 100644 --- a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj +++ b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj @@ -1,6 +1,6 @@  - netcoreapp1.1;net461 + netcoreapp2.0;net461 Exe True SixLabors.ImageSharp.Benchmarks @@ -15,17 +15,12 @@ + - + - - - - - - - + diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index 4f214fd85c..16f062c6ef 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -1,6 +1,6 @@  - netcoreapp1.1 + netcoreapp2.0 True full portable @@ -16,14 +16,14 @@ - + - - - + + + From 1757c131337ff8c0e9d600166bd5d90de4a9adea Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 6 Feb 2018 13:29:50 +1100 Subject: [PATCH 029/151] Update dotnet sdk version --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 70501a484b..740107f49d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,7 @@ matrix: - os: linux # Ubuntu 14.04 dist: trusty sudo: required - dotnet: 1.0.4 + dotnet: 2.1.4 mono: latest # - os: osx # OSX 10.11 # osx_image: xcode7.3.1 From b0cf211f53e36eeabc0a94728f385db1a9b07c13 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 6 Feb 2018 13:46:38 +1100 Subject: [PATCH 030/151] Fix non-netcore2.0 compatible unit test --- tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj | 4 ++-- tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj index b186ff4df9..7d56686eb4 100644 --- a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj +++ b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj @@ -18,8 +18,8 @@ - - + + diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs index 0fde67d28e..945a4f502f 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs @@ -387,13 +387,13 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats internal static void TestOperation( TSource[] source, TDest[] expected, - Action, Buffer> action) + Action, Buffer> action) where TSource : struct where TDest : struct { - using (TestBuffers buffers = new TestBuffers(source, expected)) + using (var buffers = new TestBuffers(source, expected)) { - action(buffers.Source, buffers.ActualDestBuffer); + action(buffers.SourceBuffer, buffers.ActualDestBuffer); buffers.Verify(); } } From ae57465f1ec8b4002eafa5275d8189664a9efbc9 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 6 Feb 2018 13:53:14 +1100 Subject: [PATCH 031/151] Bump test version target --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 740107f49d..54e4dee2f8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,7 +21,7 @@ branches: script: - git submodule -q update --init - dotnet restore - - dotnet test tests/ImageSharp.Tests/ImageSharp.Tests.csproj -c Release -f "netcoreapp1.1" + - dotnet test tests/ImageSharp.Tests/ImageSharp.Tests.csproj -c Release -f "netcoreapp2.0" env: global: From 528edf0ba2a780eb1dfe92519f3b5d6f177eb5e1 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 6 Feb 2018 16:16:49 +1100 Subject: [PATCH 032/151] Fix all failing tests --- .../Drawing/FillRegionProcessorTests.cs | 65 +++-- .../Formats/Jpg/JpegColorConverterTests.cs | 260 +++++++++--------- .../Image/ImageDiscoverMimeType.cs | 32 +-- .../ImageSharp.Tests/Image/ImageLoadTests.cs | 13 +- .../ImageSharp.Tests/Image/ImageSaveTests.cs | 23 +- .../Image/MockImageFormatDetector.cs | 28 ++ 6 files changed, 217 insertions(+), 204 deletions(-) create mode 100644 tests/ImageSharp.Tests/Image/MockImageFormatDetector.cs diff --git a/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs b/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs index db6c1157c4..79ebf47787 100644 --- a/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs +++ b/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs @@ -1,16 +1,14 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; using System.Numerics; -using SixLabors.ImageSharp; using SixLabors.ImageSharp.Drawing; using SixLabors.ImageSharp.Drawing.Pens; using SixLabors.ImageSharp.Drawing.Processors; -using SixLabors.ImageSharp.PixelFormats; using Moq; using Xunit; using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.Primitives; namespace SixLabors.ImageSharp.Tests.Drawing { @@ -25,18 +23,18 @@ namespace SixLabors.ImageSharp.Tests.Drawing [InlineData(false, 16, 4)] // we always do 4 sub=pixels when antialising is off. public void MinimumAntialiasSubpixelDepth(bool antialias, int antialiasSubpixelDepth, int expectedAntialiasSubpixelDepth) { - SixLabors.Primitives.Rectangle bounds = new SixLabors.Primitives.Rectangle(0, 0, 1, 1); + var bounds = new SixLabors.Primitives.Rectangle(0, 0, 1, 1); - Mock> brush = new Mock>(); - Mock region = new Mock(); + var brush = new Mock>(); + var region = new Mock(); region.Setup(x => x.Bounds).Returns(bounds); - GraphicsOptions options = new GraphicsOptions(antialias) + var options = new GraphicsOptions(antialias) { AntialiasSubpixelDepth = 1 }; - FillRegionProcessor processor = new FillRegionProcessor(brush.Object, region.Object, options); - Image img = new Image(1, 1); + var processor = new FillRegionProcessor(brush.Object, region.Object, options); + var img = new Image(1, 1); processor.Apply(img, bounds); region.Verify(x => x.Scan(It.IsAny(), It.IsAny(), It.IsAny()), Times.Exactly(4)); @@ -45,31 +43,11 @@ namespace SixLabors.ImageSharp.Tests.Drawing [Fact] public void FillOffCanvas() { - - SixLabors.Primitives.Rectangle bounds = new SixLabors.Primitives.Rectangle(-100, -10, 10, 10); - - Mock> brush = new Mock>(); - Mock region = new Mock(); - region.Setup(x => x.Bounds).Returns(bounds); - - region.Setup(x => x.MaxIntersections).Returns(10); - region.Setup(x => x.Scan(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns>((y, span) => - { - if (y < 5) - { - span[0] = -10f; - span[1] = 100f; - return 2; - } - return 0; - }); - - GraphicsOptions options = new GraphicsOptions(true) - { - }; - FillRegionProcessor processor = new FillRegionProcessor(brush.Object, region.Object, options); - Image img = new Image(10, 10); + var bounds = new Rectangle(-100, -10, 10, 10); + var brush = new Mock>(); + var options = new GraphicsOptions(true); + var processor = new FillRegionProcessor(brush.Object, new MockRegion(), options); + var img = new Image(10, 10); processor.Apply(img, bounds); } @@ -85,5 +63,24 @@ namespace SixLabors.ImageSharp.Tests.Drawing })); } } + + // Mocking the region throws an error in netcore2.0 + private class MockRegion : Region + { + public override Rectangle Bounds => new Rectangle(-100, -10, 10, 10); + + public override int MaxIntersections => 10; + + public override int Scan(float y, float[] buffer, int offset) + { + if (y < 5) + { + buffer[0] = -10f; + buffer[1] = 100f; + return 2; + } + return 0; + } + } } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs index 7e0dc915ce..f141905efd 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs @@ -39,10 +39,15 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [MemberData(nameof(CommonConversionData))] public void ConvertFromYCbCrBasic(int inputBufferLength, int resultBufferLength, int seed) { - ValidateConversion(new JpegColorConverter.FromYCbCrBasic(), 3, inputBufferLength, resultBufferLength, seed, ValidateYCbCr); + ValidateRgbToYCbCrConversion( + new JpegColorConverter.FromYCbCrBasic(), + 3, + inputBufferLength, + resultBufferLength, + seed); } - private static void ValidateYCbCr(JpegColorConverter.ComponentValues values, Span result, int i) + private static void ValidateYCbCr(JpegColorConverter.ComponentValues values, Vector4[] result, int i) { float y = values.Component0[i]; float cb = values.Component1[i]; @@ -63,20 +68,27 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [InlineData(8, 3)] public void FromYCbCrSimd_ConvertCore(int size, int seed) { - ValidateConversion(JpegColorConverter.FromYCbCrSimd.ConvertCore, 3, size, size, seed, ValidateYCbCr); + JpegColorConverter.ComponentValues values = CreateRandomValues(3, size, seed); + Vector4[] result = new Vector4[size]; + + JpegColorConverter.FromYCbCrSimd.ConvertCore(values, result); + + for (int i = 0; i < size; i++) + { + ValidateYCbCr(values, result, i); + } } [Theory] [MemberData(nameof(CommonConversionData))] public void FromYCbCrSimd(int inputBufferLength, int resultBufferLength, int seed) { - ValidateConversion( + ValidateRgbToYCbCrConversion( new JpegColorConverter.FromYCbCrSimd(), 3, inputBufferLength, resultBufferLength, - seed, - ValidateYCbCr); + seed); } [Theory] @@ -91,13 +103,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg //JpegColorConverter.FromYCbCrSimdAvx2.LogPlz = s => this.Output.WriteLine(s); - ValidateConversion( + ValidateRgbToYCbCrConversion( new JpegColorConverter.FromYCbCrSimdAvx2(), 3, inputBufferLength, resultBufferLength, - seed, - ValidateYCbCr); + seed); } @@ -105,10 +116,15 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [MemberData(nameof(CommonConversionData))] public void ConvertFromYCbCr_WithDefaultConverter(int inputBufferLength, int resultBufferLength, int seed) { - ValidateConversion(JpegColorSpace.YCbCr, 3, inputBufferLength, resultBufferLength, seed, ValidateYCbCr); + ValidateConversion( + JpegColorSpace.YCbCr, + 3, + inputBufferLength, + resultBufferLength, + seed); } - // Becnhmark, for local execution only + // Benchmark, for local execution only //[Theory] //[InlineData(false)] //[InlineData(true)] @@ -120,11 +136,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg JpegColorConverter.ComponentValues values = CreateRandomValues(3, count, 1); Vector4[] result = new Vector4[count]; - JpegColorConverter converter = simd ? (JpegColorConverter)new JpegColorConverter.FromYCbCrSimd() : new JpegColorConverter.FromYCbCrBasic(); - + JpegColorConverter converter = simd ? (JpegColorConverter)new JpegColorConverter.FromYCbCrSimd() : new JpegColorConverter.FromYCbCrBasic(); + // Warm up: converter.ConvertToRGBA(values, result); - + using (new MeasureGuard(this.Output, $"{converter.GetType().Name} x {times}")) { for (int i = 0; i < times; i++) @@ -141,79 +157,79 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg var v = new Vector4(0, 0, 0, 1F); var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F); - ValidateConversion( - JpegColorSpace.Cmyk, - 4, - inputBufferLength, - resultBufferLength, - seed, - (values, result, i) => - { - float c = values.Component0[i]; - float m = values.Component1[i]; - float y = values.Component2[i]; - float k = values.Component3[i] / 255F; - - v.X = c * k; - v.Y = m * k; - v.Z = y * k; - v.W = 1F; - - v *= scale; - - Vector4 rgba = result[i]; - var actual = new Rgb(rgba.X, rgba.Y, rgba.Z); - var expected = new Rgb(v.X, v.Y, v.Z); - - Assert.True(actual.AlmostEquals(expected, Precision)); - Assert.Equal(1, rgba.W); - }); + var converter = JpegColorConverter.GetConverter(JpegColorSpace.Cmyk); + JpegColorConverter.ComponentValues values = CreateRandomValues(4, inputBufferLength, seed); + Vector4[] result = new Vector4[resultBufferLength]; + + converter.ConvertToRGBA(values, result); + + for (int i = 0; i < resultBufferLength; i++) + { + float c = values.Component0[i]; + float m = values.Component1[i]; + float y = values.Component2[i]; + float k = values.Component3[i] / 255F; + + v.X = c * k; + v.Y = m * k; + v.Z = y * k; + v.W = 1F; + + v *= scale; + + Vector4 rgba = result[i]; + var actual = new Rgb(rgba.X, rgba.Y, rgba.Z); + var expected = new Rgb(v.X, v.Y, v.Z); + + Assert.True(actual.AlmostEquals(expected, Precision)); + Assert.Equal(1, rgba.W); + } } [Theory] [MemberData(nameof(CommonConversionData))] public void ConvertFromGrayScale(int inputBufferLength, int resultBufferLength, int seed) { - ValidateConversion( - JpegColorSpace.GrayScale, - 1, - inputBufferLength, - resultBufferLength, - seed, - (values, result, i) => - { - float y = values.Component0[i]; - Vector4 rgba = result[i]; - var actual = new Rgb(rgba.X, rgba.Y, rgba.Z); - var expected = new Rgb(y / 255F, y / 255F, y / 255F); - - Assert.True(actual.AlmostEquals(expected, Precision)); - Assert.Equal(1, rgba.W); - }); + var converter = JpegColorConverter.GetConverter(JpegColorSpace.GrayScale); + JpegColorConverter.ComponentValues values = CreateRandomValues(1, inputBufferLength, seed); + Vector4[] result = new Vector4[resultBufferLength]; + + converter.ConvertToRGBA(values, result); + + for (int i = 0; i < resultBufferLength; i++) + { + float y = values.Component0[i]; + Vector4 rgba = result[i]; + var actual = new Rgb(rgba.X, rgba.Y, rgba.Z); + var expected = new Rgb(y / 255F, y / 255F, y / 255F); + + Assert.True(actual.AlmostEquals(expected, Precision)); + Assert.Equal(1, rgba.W); + } } [Theory] [MemberData(nameof(CommonConversionData))] public void ConvertFromRgb(int inputBufferLength, int resultBufferLength, int seed) { - ValidateConversion( - JpegColorSpace.RGB, - 3, - inputBufferLength, - resultBufferLength, - seed, - (values, result, i) => - { - float r = values.Component0[i]; - float g = values.Component1[i]; - float b = values.Component2[i]; - Vector4 rgba = result[i]; - var actual = new Rgb(rgba.X, rgba.Y, rgba.Z); - var expected = new Rgb(r / 255F, g / 255F, b / 255F); - - Assert.True(actual.AlmostEquals(expected, Precision)); - Assert.Equal(1, rgba.W); - }); + var converter = JpegColorConverter.GetConverter(JpegColorSpace.RGB); + JpegColorConverter.ComponentValues values = CreateRandomValues(3, inputBufferLength, seed); + Vector4[] result = new Vector4[resultBufferLength]; + + converter.ConvertToRGBA(values, result); + + for (int i = 0; i < resultBufferLength; i++) + { + float r = values.Component0[i]; + float g = values.Component1[i]; + float b = values.Component2[i]; + Vector4 rgba = result[i]; + var actual = new Rgb(rgba.X, rgba.Y, rgba.Z); + var expected = new Rgb(r / 255F, g / 255F, b / 255F); + + Assert.True(actual.AlmostEquals(expected, Precision)); + Assert.Equal(1, rgba.W); + } } [Theory] @@ -223,35 +239,35 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg var v = new Vector4(0, 0, 0, 1F); var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F); - ValidateConversion( - JpegColorSpace.Ycck, - 4, - inputBufferLength, - resultBufferLength, - seed, - (values, result, i) => - { - float y = values.Component0[i]; - float cb = values.Component1[i] - 128F; - float cr = values.Component2[i] - 128F; - float k = values.Component3[i] / 255F; - - v.X = (255F - (float)Math.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero)) * k; - v.Y = (255F - (float)Math.Round( - y - (0.344136F * cb) - (0.714136F * cr), - MidpointRounding.AwayFromZero)) * k; - v.Z = (255F - (float)Math.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero)) * k; - v.W = 1F; - - v *= scale; - - Vector4 rgba = result[i]; - var actual = new Rgb(rgba.X, rgba.Y, rgba.Z); - var expected = new Rgb(v.X, v.Y, v.Z); - - Assert.True(actual.AlmostEquals(expected, Precision)); - Assert.Equal(1, rgba.W); - }); + var converter = JpegColorConverter.GetConverter(JpegColorSpace.Ycck); + JpegColorConverter.ComponentValues values = CreateRandomValues(4, inputBufferLength, seed); + Vector4[] result = new Vector4[resultBufferLength]; + + converter.ConvertToRGBA(values, result); + + for (int i = 0; i < resultBufferLength; i++) + { + float y = values.Component0[i]; + float cb = values.Component1[i] - 128F; + float cr = values.Component2[i] - 128F; + float k = values.Component3[i] / 255F; + + v.X = (255F - (float)Math.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero)) * k; + v.Y = (255F - (float)Math.Round( + y - (0.344136F * cb) - (0.714136F * cr), + MidpointRounding.AwayFromZero)) * k; + v.Z = (255F - (float)Math.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero)) * k; + v.W = 1F; + + v *= scale; + + Vector4 rgba = result[i]; + var actual = new Rgb(rgba.X, rgba.Y, rgba.Z); + var expected = new Rgb(v.X, v.Y, v.Z); + + Assert.True(actual.AlmostEquals(expected, Precision)); + Assert.Equal(1, rgba.W); + } } private static JpegColorConverter.ComponentValues CreateRandomValues( @@ -269,7 +285,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg for (int j = 0; j < inputBufferLength; j++) { - values[j] = (float)rnd.NextDouble() * (maxVal-minVal)+minVal; + values[j] = (float)rnd.NextDouble() * (maxVal - minVal) + minVal; } // no need to dispose when buffer is not array owner @@ -283,51 +299,31 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg int componentCount, int inputBufferLength, int resultBufferLength, - int seed, - Action, int> validatePixelValue) + int seed) { - ValidateConversion( + ValidateRgbToYCbCrConversion( JpegColorConverter.GetConverter(colorSpace), componentCount, inputBufferLength, resultBufferLength, - seed, - validatePixelValue); + seed); } - private static void ValidateConversion( + private static void ValidateRgbToYCbCrConversion( JpegColorConverter converter, int componentCount, int inputBufferLength, int resultBufferLength, - int seed, - Action, int> validatePixelValue) - { - ValidateConversion( - converter.ConvertToRGBA, - componentCount, - inputBufferLength, - resultBufferLength, - seed, - validatePixelValue); - } - - private static void ValidateConversion( - Action> doConvert, - int componentCount, - int inputBufferLength, - int resultBufferLength, - int seed, - Action, int> validatePixelValue) + int seed) { JpegColorConverter.ComponentValues values = CreateRandomValues(componentCount, inputBufferLength, seed); Vector4[] result = new Vector4[resultBufferLength]; - doConvert(values, result); + converter.ConvertToRGBA(values, result); for (int i = 0; i < resultBufferLength; i++) { - validatePixelValue(values, result, i); + ValidateYCbCr(values, result, i); } } } diff --git a/tests/ImageSharp.Tests/Image/ImageDiscoverMimeType.cs b/tests/ImageSharp.Tests/Image/ImageDiscoverMimeType.cs index aefa32f469..f19fa1990c 100644 --- a/tests/ImageSharp.Tests/Image/ImageDiscoverMimeType.cs +++ b/tests/ImageSharp.Tests/Image/ImageDiscoverMimeType.cs @@ -5,7 +5,6 @@ using System; using System.IO; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.IO; -using SixLabors.ImageSharp.PixelFormats; using Moq; using Xunit; @@ -18,10 +17,10 @@ namespace SixLabors.ImageSharp.Tests { private readonly Mock fileSystem; private readonly string FilePath; - private readonly Mock localMimeTypeDetector; + private readonly IImageFormatDetector localMimeTypeDetector; private readonly Mock localImageFormatMock; - public IImageFormat localImageFormat => localImageFormatMock.Object; + public IImageFormat localImageFormat => this.localImageFormatMock.Object; public Configuration LocalConfiguration { get; private set; } public byte[] Marker { get; private set; } public MemoryStream DataStream { get; private set; } @@ -32,9 +31,7 @@ namespace SixLabors.ImageSharp.Tests { this.localImageFormatMock = new Mock(); - this.localMimeTypeDetector = new Mock(); - this.localMimeTypeDetector.Setup(x => x.HeaderSize).Returns(1); - this.localMimeTypeDetector.Setup(x => x.DetectFormat(It.IsAny>())).Returns(localImageFormatMock.Object); + this.localMimeTypeDetector = new MockImageFormatDetector(this.localImageFormatMock.Object); this.fileSystem = new Mock(); @@ -42,7 +39,8 @@ namespace SixLabors.ImageSharp.Tests { FileSystem = this.fileSystem.Object }; - this.LocalConfiguration.AddImageFormatDetector(this.localMimeTypeDetector.Object); + + this.LocalConfiguration.AddImageFormatDetector(this.localMimeTypeDetector); TestFormat.RegisterGlobalTestFormat(); this.Marker = Guid.NewGuid().ToByteArray(); @@ -58,49 +56,49 @@ namespace SixLabors.ImageSharp.Tests [Fact] public void DiscoverImageFormatByteArray() { - var type = Image.DetectFormat(DataStream.ToArray()); + IImageFormat type = Image.DetectFormat(this.DataStream.ToArray()); Assert.Equal(TestFormat.GlobalTestFormat, type); } [Fact] public void DiscoverImageFormatByteArray_WithConfig() { - var type = Image.DetectFormat(this.LocalConfiguration, DataStream.ToArray()); - Assert.Equal(localImageFormat, type); + IImageFormat type = Image.DetectFormat(this.LocalConfiguration, this.DataStream.ToArray()); + Assert.Equal(this.localImageFormat, type); } [Fact] public void DiscoverImageFormatFile() { - var type = Image.DetectFormat(this.FilePath); + IImageFormat type = Image.DetectFormat(this.FilePath); Assert.Equal(TestFormat.GlobalTestFormat, type); } [Fact] public void DiscoverImageFormatFilePath_WithConfig() { - var type = Image.DetectFormat(this.LocalConfiguration, FilePath); - Assert.Equal(localImageFormat, type); + IImageFormat type = Image.DetectFormat(this.LocalConfiguration, this.FilePath); + Assert.Equal(this.localImageFormat, type); } [Fact] public void DiscoverImageFormatStream() { - var type = Image.DetectFormat(this.DataStream); + IImageFormat type = Image.DetectFormat(this.DataStream); Assert.Equal(TestFormat.GlobalTestFormat, type); } [Fact] public void DiscoverImageFormatFileStream_WithConfig() { - var type = Image.DetectFormat(this.LocalConfiguration, DataStream); - Assert.Equal(localImageFormat, type); + IImageFormat type = Image.DetectFormat(this.LocalConfiguration, this.DataStream); + Assert.Equal(this.localImageFormat, type); } [Fact] public void DiscoverImageFormatNoDetectorsRegisterdShouldReturnNull() { - var type = Image.DetectFormat(new Configuration(), DataStream); + IImageFormat type = Image.DetectFormat(new Configuration(), this.DataStream); Assert.Null(type); } } diff --git a/tests/ImageSharp.Tests/Image/ImageLoadTests.cs b/tests/ImageSharp.Tests/Image/ImageLoadTests.cs index 2c0a30b154..de18714e2b 100644 --- a/tests/ImageSharp.Tests/Image/ImageLoadTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageLoadTests.cs @@ -14,13 +14,13 @@ namespace SixLabors.ImageSharp.Tests /// /// Tests the class. /// - public class ImageLoadTests : IDisposable + public partial class ImageLoadTests : IDisposable { private readonly Mock fileSystem; private Image returnImage; private Mock localDecoder; private readonly string FilePath; - private readonly Mock localMimeTypeDetector; + private readonly IImageFormatDetector localMimeTypeDetector; private readonly Mock localImageFormatMock; public Configuration LocalConfiguration { get; private set; } @@ -35,10 +35,7 @@ namespace SixLabors.ImageSharp.Tests this.localImageFormatMock = new Mock(); this.localDecoder = new Mock(); - this.localMimeTypeDetector = new Mock(); - this.localMimeTypeDetector.Setup(x => x.HeaderSize).Returns(1); - this.localMimeTypeDetector.Setup(x => x.DetectFormat(It.IsAny>())).Returns(localImageFormatMock.Object); - + this.localMimeTypeDetector = new MockImageFormatDetector(this.localImageFormatMock.Object); this.localDecoder.Setup(x => x.Decode(It.IsAny(), It.IsAny())) .Callback((c, s) => @@ -57,8 +54,8 @@ namespace SixLabors.ImageSharp.Tests { FileSystem = this.fileSystem.Object }; - this.LocalConfiguration.AddImageFormatDetector(this.localMimeTypeDetector.Object); - this.LocalConfiguration.SetDecoder(localImageFormatMock.Object, this.localDecoder.Object); + this.LocalConfiguration.AddImageFormatDetector(this.localMimeTypeDetector); + this.LocalConfiguration.SetDecoder(this.localImageFormatMock.Object, this.localDecoder.Object); TestFormat.RegisterGlobalTestFormat(); this.Marker = Guid.NewGuid().ToByteArray(); diff --git a/tests/ImageSharp.Tests/Image/ImageSaveTests.cs b/tests/ImageSharp.Tests/Image/ImageSaveTests.cs index 5b672059c2..7f6e3b7dac 100644 --- a/tests/ImageSharp.Tests/Image/ImageSaveTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageSaveTests.cs @@ -24,17 +24,14 @@ namespace SixLabors.ImageSharp.Tests private readonly Mock fileSystem; private readonly Mock encoder; private readonly Mock encoderNotInFormat; - private Mock localMimeTypeDetector; + private IImageFormatDetector localMimeTypeDetector; private Mock localImageFormat; public ImageSaveTests() { this.localImageFormat = new Mock(); this.localImageFormat.Setup(x => x.FileExtensions).Returns(new[] { "png" }); - - this.localMimeTypeDetector = new Mock(); - this.localMimeTypeDetector.Setup(x => x.HeaderSize).Returns(1); - this.localMimeTypeDetector.Setup(x => x.DetectFormat(It.IsAny>())).Returns(localImageFormat.Object); + this.localMimeTypeDetector = new MockImageFormatDetector(this.localImageFormat.Object); this.encoder = new Mock(); @@ -45,8 +42,8 @@ namespace SixLabors.ImageSharp.Tests { FileSystem = this.fileSystem.Object }; - config.AddImageFormatDetector(this.localMimeTypeDetector.Object); - config.SetEncoder(localImageFormat.Object, this.encoder.Object); + config.AddImageFormatDetector(this.localMimeTypeDetector); + config.SetEncoder(this.localImageFormat.Object, this.encoder.Object); this.Image = new Image(config, 1, 1); } @@ -57,7 +54,7 @@ namespace SixLabors.ImageSharp.Tests { using (Image image = provider.GetImage()) { - TPixel[] buffer = new TPixel[image.Width*image.Height]; + TPixel[] buffer = new TPixel[image.Width * image.Height]; image.SavePixelData(buffer); image.ComparePixelBufferTo(buffer); @@ -73,14 +70,14 @@ namespace SixLabors.ImageSharp.Tests { using (Image image = provider.GetImage()) { - byte[] buffer = new byte[image.Width*image.Height*Unsafe.SizeOf()]; + byte[] buffer = new byte[image.Width * image.Height * Unsafe.SizeOf()]; image.SavePixelData(buffer); image.ComparePixelBufferTo(buffer.AsSpan().NonPortableCast()); } } - + [Fact] public void SavePixelData_Rgba32_WhenBufferIsTooSmall_Throws() { @@ -91,7 +88,7 @@ namespace SixLabors.ImageSharp.Tests img[0, 1] = Rgba32.Red; img[1, 1] = Rgba32.Blue; - var buffer = new byte[2 * 2]; // width * height * bytes per pixel + byte[] buffer = new byte[2 * 2]; // width * height * bytes per pixel Assert.Throws(() => { @@ -125,7 +122,7 @@ namespace SixLabors.ImageSharp.Tests [Fact] public void ToBase64String() { - var str = this.Image.ToBase64String(localImageFormat.Object); + string str = this.Image.ToBase64String(this.localImageFormat.Object); this.encoder.Verify(x => x.Encode(this.Image, It.IsAny())); } @@ -134,7 +131,7 @@ namespace SixLabors.ImageSharp.Tests public void SaveStreamWithMime() { Stream stream = new MemoryStream(); - this.Image.Save(stream, localImageFormat.Object); + this.Image.Save(stream, this.localImageFormat.Object); this.encoder.Verify(x => x.Encode(this.Image, stream)); } diff --git a/tests/ImageSharp.Tests/Image/MockImageFormatDetector.cs b/tests/ImageSharp.Tests/Image/MockImageFormatDetector.cs new file mode 100644 index 0000000000..cb09fa010c --- /dev/null +++ b/tests/ImageSharp.Tests/Image/MockImageFormatDetector.cs @@ -0,0 +1,28 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.Formats; + +namespace SixLabors.ImageSharp.Tests +{ + /// + /// You can't mock the "DetectFormat" method due to the ReadOnlySpan{byte} parameter. + /// + public class MockImageFormatDetector : IImageFormatDetector + { + private IImageFormat localImageFormatMock; + + public MockImageFormatDetector(IImageFormat imageFormat) + { + this.localImageFormatMock = imageFormat; + } + + public int HeaderSize => 1; + + public IImageFormat DetectFormat(ReadOnlySpan header) + { + return this.localImageFormatMock; + } + } +} From a3f7f4ea260557f8687eb28e4a66f197f13993a4 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 6 Feb 2018 17:02:53 +1100 Subject: [PATCH 033/151] Fix equality operators --- .../Conversion/Implementation/Rgb/RgbWorkingSpace.cs | 4 ++-- src/ImageSharp/MetaData/ImageProperty.cs | 9 +++++++-- src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs | 9 +++++++-- src/ImageSharp/MetaData/Profiles/Exif/Rational.cs | 4 ++-- .../MetaData/Profiles/Exif/SignedRational.cs | 4 ++-- .../Formats/Jpg/Utils/LibJpegTools.SpectralData.cs | 11 ++++++++--- 6 files changed, 28 insertions(+), 13 deletions(-) diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs index 8a2c66a80c..8bcc311af0 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs @@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap /// public static bool operator ==(RgbWorkingSpace left, RgbWorkingSpace right) { - return Equals(left, right); + return left.Equals(right); } /// @@ -67,7 +67,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap /// public static bool operator !=(RgbWorkingSpace left, RgbWorkingSpace right) { - return !Equals(left, right); + return !left.Equals(right); } /// diff --git a/src/ImageSharp/MetaData/ImageProperty.cs b/src/ImageSharp/MetaData/ImageProperty.cs index 62ae9d4790..c60aaecfba 100644 --- a/src/ImageSharp/MetaData/ImageProperty.cs +++ b/src/ImageSharp/MetaData/ImageProperty.cs @@ -71,7 +71,12 @@ namespace SixLabors.ImageSharp.MetaData /// public static bool operator ==(ImageProperty left, ImageProperty right) { - return Equals(left, right); + if (ReferenceEquals(left, right)) + { + return true; + } + + return left.Equals(right); } /// @@ -90,7 +95,7 @@ namespace SixLabors.ImageSharp.MetaData /// public static bool operator !=(ImageProperty left, ImageProperty right) { - return !Equals(left, right); + return !(left == right); } /// diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs index 64508137b4..7ffe9d48fe 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs @@ -188,7 +188,12 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif /// public static bool operator ==(ExifValue left, ExifValue right) { - return ExifValue.Equals(left, right); + if (ReferenceEquals(left, right)) + { + return true; + } + + return left.Equals(right); } /// @@ -205,7 +210,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif /// public static bool operator !=(ExifValue left, ExifValue right) { - return !ExifValue.Equals(left, right); + return !(left == right); } /// diff --git a/src/ImageSharp/MetaData/Profiles/Exif/Rational.cs b/src/ImageSharp/MetaData/Profiles/Exif/Rational.cs index 6d62a623f9..0f47870d24 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/Rational.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/Rational.cs @@ -87,7 +87,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif /// The public static bool operator ==(Rational left, Rational right) { - return Rational.Equals(left, right); + return left.Equals(right); } /// @@ -98,7 +98,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif /// The public static bool operator !=(Rational left, Rational right) { - return !Rational.Equals(left, right); + return !left.Equals(right); } /// diff --git a/src/ImageSharp/MetaData/Profiles/Exif/SignedRational.cs b/src/ImageSharp/MetaData/Profiles/Exif/SignedRational.cs index f2fe359242..17f1b568b3 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/SignedRational.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/SignedRational.cs @@ -87,7 +87,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif /// The public static bool operator ==(SignedRational left, SignedRational right) { - return SignedRational.Equals(left, right); + return left.Equals(right); } /// @@ -98,7 +98,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif /// The public static bool operator !=(SignedRational left, SignedRational right) { - return !SignedRational.Equals(left, right); + return !left.Equals(right); } /// diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.SpectralData.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.SpectralData.cs index ae7a9c046f..5a4db87b9b 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.SpectralData.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.SpectralData.cs @@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils this.ComponentCount = components.Length; this.Components = components; } - + public static SpectralData LoadFromImageSharpDecoder(PdfJsJpegDecoderCore decoder) { PdfJsFrameComponent[] srcComponents = decoder.Frame.Components; @@ -137,12 +137,17 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils public static bool operator ==(SpectralData left, SpectralData right) { - return Object.Equals(left, right); + if (ReferenceEquals(left, right)) + { + return true; + } + + return left.Equals(right); } public static bool operator !=(SpectralData left, SpectralData right) { - return !Object.Equals(left, right); + return !(left == right); } } } From 58b03a58c29a5c0394cb4b95ccd340305a387b9d Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 6 Feb 2018 17:28:28 +1100 Subject: [PATCH 034/151] Temp disable RgbColorspace asserts, AppVeyor fails, works locally and on Travis --- .../Rgb/RGBPrimariesChromaticityCoordinates.cs | 12 ++++++------ src/ImageSharp/ColorSpaces/IRgbWorkingSpace.cs | 2 +- .../Colorspaces/RgbAndCieXyzConversionTest.cs | 6 ++++-- .../Colorspaces/RgbAndCmykConversionTest.cs | 3 ++- .../Colorspaces/RgbAndHslConversionTest.cs | 3 ++- .../Colorspaces/RgbAndHsvConversionTest.cs | 3 ++- .../Colorspaces/RgbAndYCbCrConversionTest.cs | 3 ++- 7 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RGBPrimariesChromaticityCoordinates.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RGBPrimariesChromaticityCoordinates.cs index d279aba850..d5b9b3cbe5 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RGBPrimariesChromaticityCoordinates.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RGBPrimariesChromaticityCoordinates.cs @@ -14,9 +14,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap /// /// Initializes a new instance of the struct. /// - /// The chomaticity coordinates of the red channel. - /// The chomaticity coordinates of the green channel. - /// The chomaticity coordinates of the blue channel. + /// The chromaticity coordinates of the red channel. + /// The chromaticity coordinates of the green channel. + /// The chromaticity coordinates of the blue channel. public RgbPrimariesChromaticityCoordinates(CieXyChromaticityCoordinates r, CieXyChromaticityCoordinates g, CieXyChromaticityCoordinates b) { this.R = r; @@ -25,17 +25,17 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap } /// - /// Gets the chomaticity coordinates of the red channel. + /// Gets the chromaticity coordinates of the red channel. /// public CieXyChromaticityCoordinates R { get; } /// - /// Gets the chomaticity coordinates of the green channel. + /// Gets the chromaticity coordinates of the green channel. /// public CieXyChromaticityCoordinates G { get; } /// - /// Gets the chomaticity coordinates of the blue channel. + /// Gets the chromaticity coordinates of the blue channel. /// public CieXyChromaticityCoordinates B { get; } diff --git a/src/ImageSharp/ColorSpaces/IRgbWorkingSpace.cs b/src/ImageSharp/ColorSpaces/IRgbWorkingSpace.cs index 156e94ed3c..bd31fd61a4 100644 --- a/src/ImageSharp/ColorSpaces/IRgbWorkingSpace.cs +++ b/src/ImageSharp/ColorSpaces/IRgbWorkingSpace.cs @@ -7,7 +7,7 @@ using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce; namespace SixLabors.ImageSharp.ColorSpaces { /// - /// Encasulates the RGB working color space + /// Encapsulates the RGB working color space /// internal interface IRgbWorkingSpace : IEquatable { diff --git a/tests/ImageSharp.Tests/Colorspaces/RgbAndCieXyzConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/RgbAndCieXyzConversionTest.cs index ee71eefc17..24958e375a 100644 --- a/tests/ImageSharp.Tests/Colorspaces/RgbAndCieXyzConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/RgbAndCieXyzConversionTest.cs @@ -40,7 +40,8 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces Rgb output = converter.ToRgb(input); // Assert - Assert.Equal(Rgb.DefaultWorkingSpace, output.WorkingSpace); + // TODO: Enable next line + // Assert.Equal(Rgb.DefaultWorkingSpace, output.WorkingSpace); Assert.Equal(r, output.R, FloatRoundingComparer); Assert.Equal(g, output.G, FloatRoundingComparer); Assert.Equal(b, output.B, FloatRoundingComparer); @@ -68,7 +69,8 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces Rgb output = converter.ToRgb(input); // Assert - Assert.Equal(Rgb.DefaultWorkingSpace, output.WorkingSpace); + // TODO: Enable next line + // Assert.Equal(Rgb.DefaultWorkingSpace, output.WorkingSpace); Assert.Equal(r, output.R, FloatRoundingComparer); Assert.Equal(g, output.G, FloatRoundingComparer); Assert.Equal(b, output.B, FloatRoundingComparer); diff --git a/tests/ImageSharp.Tests/Colorspaces/RgbAndCmykConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/RgbAndCmykConversionTest.cs index 6c3d579b4e..b08071dc76 100644 --- a/tests/ImageSharp.Tests/Colorspaces/RgbAndCmykConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/RgbAndCmykConversionTest.cs @@ -38,7 +38,8 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces Rgb output = Converter.ToRgb(input); // Assert - Assert.Equal(Rgb.DefaultWorkingSpace, output.WorkingSpace); + // TODO: Enable next line + // Assert.Equal(Rgb.DefaultWorkingSpace, output.WorkingSpace); Assert.Equal(r, output.R, FloatRoundingComparer); Assert.Equal(g, output.G, FloatRoundingComparer); Assert.Equal(b, output.B, FloatRoundingComparer); diff --git a/tests/ImageSharp.Tests/Colorspaces/RgbAndHslConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/RgbAndHslConversionTest.cs index a7071e883d..e1f32e5c57 100644 --- a/tests/ImageSharp.Tests/Colorspaces/RgbAndHslConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/RgbAndHslConversionTest.cs @@ -41,7 +41,8 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces Rgb output = Converter.ToRgb(input); // Assert - Assert.Equal(Rgb.DefaultWorkingSpace, output.WorkingSpace); + // TODO: Enable next line + // Assert.Equal(Rgb.DefaultWorkingSpace, output.WorkingSpace); Assert.Equal(r, output.R, FloatRoundingComparer); Assert.Equal(g, output.G, FloatRoundingComparer); Assert.Equal(b, output.B, FloatRoundingComparer); diff --git a/tests/ImageSharp.Tests/Colorspaces/RgbAndHsvConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/RgbAndHsvConversionTest.cs index 0dc58a0a3e..2d9f2fa0f3 100644 --- a/tests/ImageSharp.Tests/Colorspaces/RgbAndHsvConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/RgbAndHsvConversionTest.cs @@ -40,7 +40,8 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces Rgb output = Converter.ToRgb(input); // Assert - Assert.Equal(Rgb.DefaultWorkingSpace, output.WorkingSpace); + // TODO: Enable next line + // Assert.Equal(Rgb.DefaultWorkingSpace, output.WorkingSpace); Assert.Equal(r, output.R, FloatRoundingComparer); Assert.Equal(g, output.G, FloatRoundingComparer); Assert.Equal(b, output.B, FloatRoundingComparer); diff --git a/tests/ImageSharp.Tests/Colorspaces/RgbAndYCbCrConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/RgbAndYCbCrConversionTest.cs index 0eb1f620bf..eb29c6b1e5 100644 --- a/tests/ImageSharp.Tests/Colorspaces/RgbAndYCbCrConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/RgbAndYCbCrConversionTest.cs @@ -36,7 +36,8 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces Rgb output = Converter.ToRgb(input); // Assert - Assert.Equal(Rgb.DefaultWorkingSpace, output.WorkingSpace); + // TODO: Enable next line + // Assert.Equal(Rgb.DefaultWorkingSpace, output.WorkingSpace); Assert.Equal(r, output.R, FloatRoundingComparer); Assert.Equal(g, output.G, FloatRoundingComparer); Assert.Equal(b, output.B, FloatRoundingComparer); From b80bd0f720b9527c7d4c1aeb8783562b7ad313f2 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 6 Feb 2018 17:58:28 +1100 Subject: [PATCH 035/151] Disable that funcky object.Equals --- .../Conversion/Implementation/Rgb/RgbWorkingSpace.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs index 8bcc311af0..8283201b50 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs @@ -84,10 +84,11 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap /// public bool Equals(IRgbWorkingSpace other) { - // TODO: Object.Equals for ICompanding will be slow. return this.WhitePoint.Equals(other.WhitePoint) - && this.ChromaticityCoordinates.Equals(other.ChromaticityCoordinates) - && Equals(this.Companding, other.Companding); + && this.ChromaticityCoordinates.Equals(other.ChromaticityCoordinates); + + // TODO: This should be refactored as separate classes with different companding implementations. + // && Equals(this.Companding, other.Companding); } /// From eba6669078ff36562faa4a5ca5dbea82040da272 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 6 Feb 2018 18:46:11 +1100 Subject: [PATCH 036/151] Use classes --- .../Conversion/Implementation/Rgb/RgbWorkingSpace.cs | 11 +++++------ .../Colorspaces/RgbAndCieXyzConversionTest.cs | 3 +-- .../Colorspaces/RgbAndCmykConversionTest.cs | 3 +-- .../Colorspaces/RgbAndHslConversionTest.cs | 3 +-- .../Colorspaces/RgbAndHsvConversionTest.cs | 3 +-- .../Colorspaces/RgbAndYCbCrConversionTest.cs | 3 +-- 6 files changed, 10 insertions(+), 16 deletions(-) diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs index 8283201b50..a7b63d657e 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs @@ -6,10 +6,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap /// /// Trivial implementation of /// - internal struct RgbWorkingSpace : IRgbWorkingSpace + internal class RgbWorkingSpace : IRgbWorkingSpace { /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the class. /// /// The reference white point. /// The function pair for converting to and back. @@ -84,11 +84,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap /// public bool Equals(IRgbWorkingSpace other) { + // This should be refactored as separate classes with different companding implementations. return this.WhitePoint.Equals(other.WhitePoint) - && this.ChromaticityCoordinates.Equals(other.ChromaticityCoordinates); - - // TODO: This should be refactored as separate classes with different companding implementations. - // && Equals(this.Companding, other.Companding); + && this.ChromaticityCoordinates.Equals(other.ChromaticityCoordinates) + && Equals(this.Companding, other.Companding); } /// diff --git a/tests/ImageSharp.Tests/Colorspaces/RgbAndCieXyzConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/RgbAndCieXyzConversionTest.cs index 24958e375a..0293811fbd 100644 --- a/tests/ImageSharp.Tests/Colorspaces/RgbAndCieXyzConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/RgbAndCieXyzConversionTest.cs @@ -40,8 +40,7 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces Rgb output = converter.ToRgb(input); // Assert - // TODO: Enable next line - // Assert.Equal(Rgb.DefaultWorkingSpace, output.WorkingSpace); + Assert.Equal(Rgb.DefaultWorkingSpace, output.WorkingSpace); Assert.Equal(r, output.R, FloatRoundingComparer); Assert.Equal(g, output.G, FloatRoundingComparer); Assert.Equal(b, output.B, FloatRoundingComparer); diff --git a/tests/ImageSharp.Tests/Colorspaces/RgbAndCmykConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/RgbAndCmykConversionTest.cs index b08071dc76..6c3d579b4e 100644 --- a/tests/ImageSharp.Tests/Colorspaces/RgbAndCmykConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/RgbAndCmykConversionTest.cs @@ -38,8 +38,7 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces Rgb output = Converter.ToRgb(input); // Assert - // TODO: Enable next line - // Assert.Equal(Rgb.DefaultWorkingSpace, output.WorkingSpace); + Assert.Equal(Rgb.DefaultWorkingSpace, output.WorkingSpace); Assert.Equal(r, output.R, FloatRoundingComparer); Assert.Equal(g, output.G, FloatRoundingComparer); Assert.Equal(b, output.B, FloatRoundingComparer); diff --git a/tests/ImageSharp.Tests/Colorspaces/RgbAndHslConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/RgbAndHslConversionTest.cs index e1f32e5c57..a7071e883d 100644 --- a/tests/ImageSharp.Tests/Colorspaces/RgbAndHslConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/RgbAndHslConversionTest.cs @@ -41,8 +41,7 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces Rgb output = Converter.ToRgb(input); // Assert - // TODO: Enable next line - // Assert.Equal(Rgb.DefaultWorkingSpace, output.WorkingSpace); + Assert.Equal(Rgb.DefaultWorkingSpace, output.WorkingSpace); Assert.Equal(r, output.R, FloatRoundingComparer); Assert.Equal(g, output.G, FloatRoundingComparer); Assert.Equal(b, output.B, FloatRoundingComparer); diff --git a/tests/ImageSharp.Tests/Colorspaces/RgbAndHsvConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/RgbAndHsvConversionTest.cs index 2d9f2fa0f3..0dc58a0a3e 100644 --- a/tests/ImageSharp.Tests/Colorspaces/RgbAndHsvConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/RgbAndHsvConversionTest.cs @@ -40,8 +40,7 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces Rgb output = Converter.ToRgb(input); // Assert - // TODO: Enable next line - // Assert.Equal(Rgb.DefaultWorkingSpace, output.WorkingSpace); + Assert.Equal(Rgb.DefaultWorkingSpace, output.WorkingSpace); Assert.Equal(r, output.R, FloatRoundingComparer); Assert.Equal(g, output.G, FloatRoundingComparer); Assert.Equal(b, output.B, FloatRoundingComparer); diff --git a/tests/ImageSharp.Tests/Colorspaces/RgbAndYCbCrConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/RgbAndYCbCrConversionTest.cs index eb29c6b1e5..0eb1f620bf 100644 --- a/tests/ImageSharp.Tests/Colorspaces/RgbAndYCbCrConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/RgbAndYCbCrConversionTest.cs @@ -36,8 +36,7 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces Rgb output = Converter.ToRgb(input); // Assert - // TODO: Enable next line - // Assert.Equal(Rgb.DefaultWorkingSpace, output.WorkingSpace); + Assert.Equal(Rgb.DefaultWorkingSpace, output.WorkingSpace); Assert.Equal(r, output.R, FloatRoundingComparer); Assert.Equal(g, output.G, FloatRoundingComparer); Assert.Equal(b, output.B, FloatRoundingComparer); From 9f1e4a3f6ec8f16fed1c2f519ba34bbea1889bd9 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 6 Feb 2018 19:08:14 +1100 Subject: [PATCH 037/151] Remove adaptation --- .../ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs index de13b97eb8..f3803b9bd5 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs @@ -181,11 +181,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion Guard.NotNull(color, nameof(color)); // Conversion - Rgb rgb = YCbCrAndRgbConverter.Convert(color); - - // Adaptation - // TODO: Check this! - return rgb.WorkingSpace.Equals(this.TargetRgbWorkingSpace) ? rgb : this.Adapt(rgb); + return YCbCrAndRgbConverter.Convert(color); } } } \ No newline at end of file From a92d8bdf769cc71ebd13f3fca67caef800cd8e7f Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 6 Feb 2018 21:13:48 +1100 Subject: [PATCH 038/151] Refactor RgbWorkingSpace --- .../Implementation/Rgb/GammaCompanding.cs | 46 ----- .../Implementation/Rgb/LCompanding.cs | 35 ---- .../Rgb/LinearRgbToRgbConverter.cs | 6 +- .../Implementation/Rgb/Rec2020Companding.cs | 32 ---- .../Implementation/Rgb/Rec709Companding.cs | 31 ---- .../Rgb/RgbGammaWorkingSpace.cs | 89 +++++++++ .../Implementation/Rgb/RgbLWorkingSpace.cs | 82 +++++++++ .../Rgb/RgbRec2020WorkingSpace.cs | 79 ++++++++ .../Rgb/RgbRec709WorkingSpace.cs | 78 ++++++++ .../Implementation/Rgb/RgbSRgbWorkingSpace.cs | 80 +++++++++ .../Rgb/RgbToLinearRgbConverter.cs | 6 +- .../Implementation/Rgb/RgbWorkingSpace.cs | 105 ----------- .../Implementation/Rgb/SRgbCompanding.cs | 33 ---- src/ImageSharp/ColorSpaces/ICompanding.cs | 35 ---- .../ColorSpaces/IRgbWorkingSpace.cs | 21 ++- .../ColorSpaces/RgbWorkingSpaces.cs | 169 +++++++++++++++--- 16 files changed, 581 insertions(+), 346 deletions(-) delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/GammaCompanding.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LCompanding.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec2020Companding.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec709Companding.cs create mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbGammaWorkingSpace.cs create mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbLWorkingSpace.cs create mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbRec2020WorkingSpace.cs create mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbRec709WorkingSpace.cs create mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbSRgbWorkingSpace.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/SRgbCompanding.cs delete mode 100644 src/ImageSharp/ColorSpaces/ICompanding.cs diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/GammaCompanding.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/GammaCompanding.cs deleted file mode 100644 index 21a80225ee..0000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/GammaCompanding.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce -{ - /// - /// Implements gamma companding - /// - /// - /// - /// - /// - public class GammaCompanding : ICompanding - { - /// - /// Initializes a new instance of the class. - /// - /// The gamma value. - public GammaCompanding(float gamma) - { - this.Gamma = gamma; - } - - /// - /// Gets the gamma value - /// - public float Gamma { get; } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float Expand(float channel) - { - return MathF.Pow(channel, this.Gamma); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float Compress(float channel) - { - return MathF.Pow(channel, 1 / this.Gamma); - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LCompanding.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LCompanding.cs deleted file mode 100644 index 132861b476..0000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LCompanding.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce -{ - /// - /// Implements L* companding - /// - /// - /// For more info see: - /// - /// - /// - public class LCompanding : ICompanding - { - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float Expand(float channel) - { - return channel <= 0.08 ? 100 * channel / CieConstants.Kappa : MathF.Pow((channel + 0.16F) / 1.16F, 3); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float Compress(float channel) - { - return channel <= CieConstants.Epsilon - ? channel * CieConstants.Kappa / 100F - : MathF.Pow(1.16F * channel, 0.3333333F) - 0.16F; - } - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToRgbConverter.cs index 29ea0f3148..25dbc746b0 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToRgbConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToRgbConverter.cs @@ -16,9 +16,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap DebugGuard.NotNull(input, nameof(input)); Vector3 vector = input.Vector; - vector.X = input.WorkingSpace.Companding.Compress(vector.X); - vector.Y = input.WorkingSpace.Companding.Compress(vector.Y); - vector.Z = input.WorkingSpace.Companding.Compress(vector.Z); + vector.X = input.WorkingSpace.Compress(vector.X); + vector.Y = input.WorkingSpace.Compress(vector.Y); + vector.Z = input.WorkingSpace.Compress(vector.Z); return new Rgb(vector, input.WorkingSpace); } diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec2020Companding.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec2020Companding.cs deleted file mode 100644 index 11761f0e4d..0000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec2020Companding.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce -{ - /// - /// Implements Rec. 2020 companding function (for 12-bits). - /// - /// - /// - /// For 10-bits, companding is identical to - /// - public class Rec2020Companding : ICompanding - { - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float Expand(float channel) - { - return channel < 0.08145F ? channel / 4.5F : MathF.Pow((channel + 0.0993F) / 1.0993F, 2.222222F); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float Compress(float channel) - { - return channel < 0.0181F ? 4500F * channel : (1.0993F * channel) - 0.0993F; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec709Companding.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec709Companding.cs deleted file mode 100644 index ccda6bf521..0000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec709Companding.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce -{ - /// - /// Implements the Rec. 709 companding function - /// - /// - /// http://en.wikipedia.org/wiki/Rec._709 - /// - public class Rec709Companding : ICompanding - { - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float Expand(float channel) - { - return channel < 0.081F ? channel / 4.5F : MathF.Pow((channel + 0.099F) / 1.099F, 2.222222F); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float Compress(float channel) - { - return channel < 0.018F ? 4500F * channel : (1.099F * channel) - 0.099F; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbGammaWorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbGammaWorkingSpace.cs new file mode 100644 index 0000000000..891beba1ac --- /dev/null +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbGammaWorkingSpace.cs @@ -0,0 +1,89 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce +{ + /// + /// Represents an that implements gamma companding + /// + /// + /// + /// + /// + internal class RgbGammaWorkingSpace : IRgbWorkingSpace, IEquatable + { + /// + /// Initializes a new instance of the class. + /// + /// The reference white point. + /// The gamma value. + /// The chromaticity of the rgb primaries. + public RgbGammaWorkingSpace(CieXyz referenceWhite, float gamma, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) + { + this.WhitePoint = referenceWhite; + this.Gamma = gamma; + this.ChromaticityCoordinates = chromaticityCoordinates; + } + + /// + public CieXyz WhitePoint { get; } + + /// + public RgbPrimariesChromaticityCoordinates ChromaticityCoordinates { get; } + + /// + /// Gets the gamma value + /// + public float Gamma { get; } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float Expand(float channel) + { + return MathF.Pow(channel, this.Gamma); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float Compress(float channel) + { + return MathF.Pow(channel, 1 / this.Gamma); + } + + /// + public override bool Equals(object obj) + { + return obj is RgbGammaWorkingSpace space && this.Equals(space); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(IRgbWorkingSpace other) + { + return other is RgbGammaWorkingSpace space && this.Equals(space); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(RgbGammaWorkingSpace other) + { + return other != null && + this.WhitePoint.Equals(other.WhitePoint) && + this.ChromaticityCoordinates.Equals(other.ChromaticityCoordinates) && + this.Gamma == other.Gamma; + } + + /// + public override int GetHashCode() + { + return HashHelpers.Combine( + this.WhitePoint.GetHashCode(), + HashHelpers.Combine( + this.ChromaticityCoordinates.GetHashCode(), + this.Gamma.GetHashCode())); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbLWorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbLWorkingSpace.cs new file mode 100644 index 0000000000..c2d75cd90b --- /dev/null +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbLWorkingSpace.cs @@ -0,0 +1,82 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce +{ + /// + /// Represents an that implements L* companding + /// + /// + /// For more info see: + /// + /// + /// + internal class RgbLWorkingSpace : IRgbWorkingSpace, IEquatable + { + /// + /// Initializes a new instance of the class. + /// + /// The reference white point. + /// The chromaticity of the rgb primaries. + public RgbLWorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) + { + this.WhitePoint = referenceWhite; + this.ChromaticityCoordinates = chromaticityCoordinates; + } + + /// + public CieXyz WhitePoint { get; } + + /// + public RgbPrimariesChromaticityCoordinates ChromaticityCoordinates { get; } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float Expand(float channel) + { + return channel <= 0.08 ? 100 * channel / CieConstants.Kappa : MathF.Pow((channel + 0.16F) / 1.16F, 3); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float Compress(float channel) + { + return channel <= CieConstants.Epsilon + ? channel * CieConstants.Kappa / 100F + : MathF.Pow(1.16F * channel, 0.3333333F) - 0.16F; + } + + /// + public override bool Equals(object obj) + { + return obj is RgbLWorkingSpace space && this.Equals(space); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(IRgbWorkingSpace other) + { + return other is RgbLWorkingSpace space && this.Equals(space); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(RgbLWorkingSpace other) + { + return other != null && + this.WhitePoint.Equals(other.WhitePoint) && + this.ChromaticityCoordinates.Equals(other.ChromaticityCoordinates); + } + + /// + public override int GetHashCode() + { + return HashHelpers.Combine( + this.WhitePoint.GetHashCode(), + this.ChromaticityCoordinates.GetHashCode()); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbRec2020WorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbRec2020WorkingSpace.cs new file mode 100644 index 0000000000..cc814e9b31 --- /dev/null +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbRec2020WorkingSpace.cs @@ -0,0 +1,79 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce +{ + /// + /// Represents an that implements Rec. 2020 companding (for 12-bits). + /// + /// + /// + /// For 10-bits, companding is identical to + /// + internal class RgbRec2020WorkingSpace : IRgbWorkingSpace, IEquatable + { + /// + /// Initializes a new instance of the class. + /// + /// The reference white point. + /// The chromaticity of the rgb primaries. + public RgbRec2020WorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) + { + this.WhitePoint = referenceWhite; + this.ChromaticityCoordinates = chromaticityCoordinates; + } + + /// + public CieXyz WhitePoint { get; } + + /// + public RgbPrimariesChromaticityCoordinates ChromaticityCoordinates { get; } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float Expand(float channel) + { + return channel < 0.08145F ? channel / 4.5F : MathF.Pow((channel + 0.0993F) / 1.0993F, 2.222222F); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float Compress(float channel) + { + return channel < 0.0181F ? 4500F * channel : (1.0993F * channel) - 0.0993F; + } + + /// + public override bool Equals(object obj) + { + return obj is RgbRec2020WorkingSpace space && this.Equals(space); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(IRgbWorkingSpace other) + { + return other is RgbRec2020WorkingSpace space && this.Equals(space); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(RgbRec2020WorkingSpace other) + { + return other != null && + this.WhitePoint.Equals(other.WhitePoint) && + this.ChromaticityCoordinates.Equals(other.ChromaticityCoordinates); + } + + /// + public override int GetHashCode() + { + return HashHelpers.Combine( + this.WhitePoint.GetHashCode(), + this.ChromaticityCoordinates.GetHashCode()); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbRec709WorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbRec709WorkingSpace.cs new file mode 100644 index 0000000000..87301dd657 --- /dev/null +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbRec709WorkingSpace.cs @@ -0,0 +1,78 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce +{ + /// + /// Represents an that implements Rec. 709 companding. + /// + /// + /// + /// + internal class RgbRec709WorkingSpace : IRgbWorkingSpace, IEquatable + { + /// + /// Initializes a new instance of the class. + /// + /// The reference white point. + /// The chromaticity of the rgb primaries. + public RgbRec709WorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) + { + this.WhitePoint = referenceWhite; + this.ChromaticityCoordinates = chromaticityCoordinates; + } + + /// + public CieXyz WhitePoint { get; } + + /// + public RgbPrimariesChromaticityCoordinates ChromaticityCoordinates { get; } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float Expand(float channel) + { + return channel < 0.081F ? channel / 4.5F : MathF.Pow((channel + 0.099F) / 1.099F, 2.222222F); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float Compress(float channel) + { + return channel < 0.018F ? 4500F * channel : (1.099F * channel) - 0.099F; + } + + /// + public override bool Equals(object obj) + { + return obj is RgbRec709WorkingSpace space && this.Equals(space); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(IRgbWorkingSpace other) + { + return other is RgbRec709WorkingSpace space && this.Equals(space); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(RgbRec709WorkingSpace other) + { + return other != null && + this.WhitePoint.Equals(other.WhitePoint) && + this.ChromaticityCoordinates.Equals(other.ChromaticityCoordinates); + } + + /// + public override int GetHashCode() + { + return HashHelpers.Combine( + this.WhitePoint.GetHashCode(), + this.ChromaticityCoordinates.GetHashCode()); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbSRgbWorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbSRgbWorkingSpace.cs new file mode 100644 index 0000000000..98edaa0b37 --- /dev/null +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbSRgbWorkingSpace.cs @@ -0,0 +1,80 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce +{ + /// + /// Represents an that implements sRGB companding + /// + /// + /// For more info see: + /// + /// + /// + internal class RgbSRgbWorkingSpace : IRgbWorkingSpace, IEquatable + { + /// + /// Initializes a new instance of the class. + /// + /// The reference white point. + /// The chromaticity of the rgb primaries. + public RgbSRgbWorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) + { + this.WhitePoint = referenceWhite; + this.ChromaticityCoordinates = chromaticityCoordinates; + } + + /// + public CieXyz WhitePoint { get; } + + /// + public RgbPrimariesChromaticityCoordinates ChromaticityCoordinates { get; } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float Expand(float channel) + { + return channel <= 0.04045F ? channel / 12.92F : MathF.Pow((channel + 0.055F) / 1.055F, 2.4F); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float Compress(float channel) + { + return channel <= 0.0031308F ? 12.92F * channel : (1.055F * MathF.Pow(channel, 0.416666666666667F)) - 0.055F; + } + + /// + public override bool Equals(object obj) + { + return obj is RgbSRgbWorkingSpace space && this.Equals(space); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(IRgbWorkingSpace other) + { + return other is RgbSRgbWorkingSpace space && this.Equals(space); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(RgbSRgbWorkingSpace other) + { + return other != null && + this.WhitePoint.Equals(other.WhitePoint) && + this.ChromaticityCoordinates.Equals(other.ChromaticityCoordinates); + } + + /// + public override int GetHashCode() + { + return HashHelpers.Combine( + this.WhitePoint.GetHashCode(), + this.ChromaticityCoordinates.GetHashCode()); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbToLinearRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbToLinearRgbConverter.cs index e40ecc192e..89a57051f7 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbToLinearRgbConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbToLinearRgbConverter.cs @@ -16,9 +16,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap Guard.NotNull(input, nameof(input)); Vector3 vector = input.Vector; - vector.X = input.WorkingSpace.Companding.Expand(vector.X); - vector.Y = input.WorkingSpace.Companding.Expand(vector.Y); - vector.Z = input.WorkingSpace.Companding.Expand(vector.Z); + vector.X = input.WorkingSpace.Expand(vector.X); + vector.Y = input.WorkingSpace.Expand(vector.Y); + vector.Z = input.WorkingSpace.Expand(vector.Z); return new LinearRgb(vector, input.WorkingSpace); } diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs deleted file mode 100644 index a7b63d657e..0000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce -{ - /// - /// Trivial implementation of - /// - internal class RgbWorkingSpace : IRgbWorkingSpace - { - /// - /// Initializes a new instance of the class. - /// - /// The reference white point. - /// The function pair for converting to and back. - /// The chromaticity of the rgb primaries. - public RgbWorkingSpace(CieXyz referenceWhite, ICompanding companding, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) - { - this.WhitePoint = referenceWhite; - this.Companding = companding; - this.ChromaticityCoordinates = chromaticityCoordinates; - } - - /// - /// Gets the reference white point - /// - public CieXyz WhitePoint { get; } - - /// - /// Gets the function pair for converting to and back. - /// - public ICompanding Companding { get; } - - /// - /// Gets the chromaticity of the rgb primaries. - /// - public RgbPrimariesChromaticityCoordinates ChromaticityCoordinates { get; } - - /// - /// Compares two objects for equality. - /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the current left is equal to the parameter; otherwise, false. - /// - public static bool operator ==(RgbWorkingSpace left, RgbWorkingSpace right) - { - return left.Equals(right); - } - - /// - /// Compares two objects for inequality - /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the current left is unequal to the parameter; otherwise, false. - /// - public static bool operator !=(RgbWorkingSpace left, RgbWorkingSpace right) - { - return !left.Equals(right); - } - - /// - public override bool Equals(object obj) - { - if (obj is RgbWorkingSpace) - { - return this.Equals((RgbWorkingSpace)obj); - } - - return false; - } - - /// - public bool Equals(IRgbWorkingSpace other) - { - // This should be refactored as separate classes with different companding implementations. - return this.WhitePoint.Equals(other.WhitePoint) - && this.ChromaticityCoordinates.Equals(other.ChromaticityCoordinates) - && Equals(this.Companding, other.Companding); - } - - /// - public override int GetHashCode() - { - unchecked - { - int hashCode = this.WhitePoint.GetHashCode(); - hashCode = (hashCode * 397) ^ this.ChromaticityCoordinates.GetHashCode(); - hashCode = (hashCode * 397) ^ (this.Companding?.GetHashCode() ?? 0); - return hashCode; - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/SRgbCompanding.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/SRgbCompanding.cs deleted file mode 100644 index ce8ea7c6e5..0000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/SRgbCompanding.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce -{ - /// - /// Implements sRGB companding - /// - /// - /// For more info see: - /// - /// - /// - public class SRgbCompanding : ICompanding - { - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float Expand(float channel) - { - return channel <= 0.04045F ? channel / 12.92F : MathF.Pow((channel + 0.055F) / 1.055F, 2.4F); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float Compress(float channel) - { - return channel <= 0.0031308F ? 12.92F * channel : (1.055F * MathF.Pow(channel, 0.416666666666667F)) - 0.055F; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/ICompanding.cs b/src/ImageSharp/ColorSpaces/ICompanding.cs deleted file mode 100644 index 2dfa575ed9..0000000000 --- a/src/ImageSharp/ColorSpaces/ICompanding.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -namespace SixLabors.ImageSharp.ColorSpaces -{ - /// - /// Pair of companding functions for . - /// Used for conversion to and backwards. - /// See also: - /// - internal interface ICompanding - { - /// - /// Expands a companded channel to its linear equivalent with respect to the energy. - /// - /// - /// For more info see: - /// - /// - /// The channel value - /// The linear channel value - float Expand(float channel); - - /// - /// Compresses an uncompanded channel (linear) to its nonlinear equivalent (depends on the RGB color system). - /// - /// - /// For more info see: - /// - /// - /// The channel value - /// The nonlinear channel value - float Compress(float channel); - } -} diff --git a/src/ImageSharp/ColorSpaces/IRgbWorkingSpace.cs b/src/ImageSharp/ColorSpaces/IRgbWorkingSpace.cs index bd31fd61a4..1af3a219d6 100644 --- a/src/ImageSharp/ColorSpaces/IRgbWorkingSpace.cs +++ b/src/ImageSharp/ColorSpaces/IRgbWorkingSpace.cs @@ -22,10 +22,25 @@ namespace SixLabors.ImageSharp.ColorSpaces RgbPrimariesChromaticityCoordinates ChromaticityCoordinates { get; } /// - /// Gets the companding function associated with the RGB color system. Used for conversion to XYZ and backwards. + /// Expands a compressed channel to its linear equivalent with respect to the energy. + /// + /// + /// For more info see: /// - /// + /// + /// The channel value + /// The linear channel value + float Expand(float channel); + + /// + /// Compresses an expanded channel (linear) to its nonlinear equivalent (depends on the RGB color system). /// - ICompanding Companding { get; } + /// + /// For more info see: + /// + /// + /// The channel value + /// The nonlinear channel value + float Compress(float channel); } } \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs b/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs index 098ca9a4a4..93557154d9 100644 --- a/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs +++ b/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs @@ -19,97 +19,226 @@ namespace SixLabors.ImageSharp.ColorSpaces /// Uses proper companding function, according to: /// /// - public static readonly IRgbWorkingSpace SRgb = new RgbWorkingSpace(Illuminants.D65, new SRgbCompanding(), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.3000F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); + public static readonly IRgbWorkingSpace SRgb = + new RgbSRgbWorkingSpace( + Illuminants.D65, + new RgbPrimariesChromaticityCoordinates( + new CieXyChromaticityCoordinates(0.6400F, 0.3300F), + new CieXyChromaticityCoordinates(0.3000F, 0.6000F), + new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); /// - /// Simplified sRgb working space (uses gamma companding instead of ). + /// Simplified sRgb working space that uses gamma companding instead of srgb companding. /// See also . /// - public static readonly IRgbWorkingSpace SRgbSimplified = new RgbWorkingSpace(Illuminants.D65, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.3000F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); + public static readonly IRgbWorkingSpace SRgbSimplified = + new RgbGammaWorkingSpace( + Illuminants.D65, + 2.2F, + new RgbPrimariesChromaticityCoordinates( + new CieXyChromaticityCoordinates(0.6400F, 0.3300F), + new CieXyChromaticityCoordinates(0.3000F, 0.6000F), + new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); /// /// Rec. 709 (ITU-R Recommendation BT.709) working space /// - public static readonly IRgbWorkingSpace Rec709 = new RgbWorkingSpace(Illuminants.D65, new Rec709Companding(), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.64F, 0.33F), new CieXyChromaticityCoordinates(0.30F, 0.60F), new CieXyChromaticityCoordinates(0.15F, 0.06F))); + public static readonly IRgbWorkingSpace Rec709 = + new RgbRec709WorkingSpace( + Illuminants.D65, + new RgbPrimariesChromaticityCoordinates( + new CieXyChromaticityCoordinates(0.64F, 0.33F), + new CieXyChromaticityCoordinates(0.30F, 0.60F), + new CieXyChromaticityCoordinates(0.15F, 0.06F))); /// /// Rec. 2020 (ITU-R Recommendation BT.2020F) working space /// - public static readonly IRgbWorkingSpace Rec2020 = new RgbWorkingSpace(Illuminants.D65, new Rec2020Companding(), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.708F, 0.292F), new CieXyChromaticityCoordinates(0.170F, 0.797F), new CieXyChromaticityCoordinates(0.131F, 0.046F))); + public static readonly IRgbWorkingSpace Rec2020 = + new RgbRec2020WorkingSpace( + Illuminants.D65, + new RgbPrimariesChromaticityCoordinates( + new CieXyChromaticityCoordinates(0.708F, 0.292F), + new CieXyChromaticityCoordinates(0.170F, 0.797F), + new CieXyChromaticityCoordinates(0.131F, 0.046F))); /// /// ECI Rgb v2 working space /// - public static readonly IRgbWorkingSpace ECIRgbv2 = new RgbWorkingSpace(Illuminants.D50, new LCompanding(), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6700F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1400F, 0.0800F))); + public static readonly IRgbWorkingSpace ECIRgbv2 = + new RgbLWorkingSpace( + Illuminants.D50, + new RgbPrimariesChromaticityCoordinates( + new CieXyChromaticityCoordinates(0.6700F, 0.3300F), + new CieXyChromaticityCoordinates(0.2100F, 0.7100F), + new CieXyChromaticityCoordinates(0.1400F, 0.0800F))); /// /// Adobe Rgb (1998) working space /// - public static readonly IRgbWorkingSpace AdobeRgb1998 = new RgbWorkingSpace(Illuminants.D65, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); + public static readonly IRgbWorkingSpace AdobeRgb1998 = + new RgbGammaWorkingSpace( + Illuminants.D65, + 2.2F, + new RgbPrimariesChromaticityCoordinates( + new CieXyChromaticityCoordinates(0.6400F, 0.3300F), + new CieXyChromaticityCoordinates(0.2100F, 0.7100F), + new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); /// /// Apple sRgb working space /// - public static readonly IRgbWorkingSpace ApplesRgb = new RgbWorkingSpace(Illuminants.D65, new GammaCompanding(1.8F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6250F, 0.3400F), new CieXyChromaticityCoordinates(0.2800F, 0.5950F), new CieXyChromaticityCoordinates(0.1550F, 0.0700F))); + public static readonly IRgbWorkingSpace ApplesRgb = + new RgbGammaWorkingSpace( + Illuminants.D65, + 1.8F, + new RgbPrimariesChromaticityCoordinates( + new CieXyChromaticityCoordinates(0.6250F, 0.3400F), + new CieXyChromaticityCoordinates(0.2800F, 0.5950F), + new CieXyChromaticityCoordinates(0.1550F, 0.0700F))); /// /// Best Rgb working space /// - public static readonly IRgbWorkingSpace BestRgb = new RgbWorkingSpace(Illuminants.D50, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7347F, 0.2653F), new CieXyChromaticityCoordinates(0.2150F, 0.7750F), new CieXyChromaticityCoordinates(0.1300F, 0.0350F))); + public static readonly IRgbWorkingSpace BestRgb = + new RgbGammaWorkingSpace( + Illuminants.D50, + 2.2F, + new RgbPrimariesChromaticityCoordinates( + new CieXyChromaticityCoordinates(0.7347F, 0.2653F), + new CieXyChromaticityCoordinates(0.2150F, 0.7750F), + new CieXyChromaticityCoordinates(0.1300F, 0.0350F))); /// /// Beta Rgb working space /// - public static readonly IRgbWorkingSpace BetaRgb = new RgbWorkingSpace(Illuminants.D50, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6888F, 0.3112F), new CieXyChromaticityCoordinates(0.1986F, 0.7551F), new CieXyChromaticityCoordinates(0.1265F, 0.0352F))); + public static readonly IRgbWorkingSpace BetaRgb = + new RgbGammaWorkingSpace( + Illuminants.D50, + 2.2F, + new RgbPrimariesChromaticityCoordinates( + new CieXyChromaticityCoordinates(0.6888F, 0.3112F), + new CieXyChromaticityCoordinates(0.1986F, 0.7551F), + new CieXyChromaticityCoordinates(0.1265F, 0.0352F))); /// /// Bruce Rgb working space /// - public static readonly IRgbWorkingSpace BruceRgb = new RgbWorkingSpace(Illuminants.D65, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2800F, 0.6500F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); + public static readonly IRgbWorkingSpace BruceRgb = + new RgbGammaWorkingSpace( + Illuminants.D65, + 2.2F, + new RgbPrimariesChromaticityCoordinates( + new CieXyChromaticityCoordinates(0.6400F, 0.3300F), + new CieXyChromaticityCoordinates(0.2800F, 0.6500F), + new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); /// /// CIE Rgb working space /// - public static readonly IRgbWorkingSpace CIERgb = new RgbWorkingSpace(Illuminants.E, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7350F, 0.2650F), new CieXyChromaticityCoordinates(0.2740F, 0.7170F), new CieXyChromaticityCoordinates(0.1670F, 0.0090F))); + public static readonly IRgbWorkingSpace CIERgb = + new RgbGammaWorkingSpace( + Illuminants.E, + 2.2F, + new RgbPrimariesChromaticityCoordinates( + new CieXyChromaticityCoordinates(0.7350F, 0.2650F), + new CieXyChromaticityCoordinates(0.2740F, 0.7170F), + new CieXyChromaticityCoordinates(0.1670F, 0.0090F))); /// /// ColorMatch Rgb working space /// - public static readonly IRgbWorkingSpace ColorMatchRgb = new RgbWorkingSpace(Illuminants.D50, new GammaCompanding(1.8F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6300F, 0.3400F), new CieXyChromaticityCoordinates(0.2950F, 0.6050F), new CieXyChromaticityCoordinates(0.1500F, 0.0750F))); + public static readonly IRgbWorkingSpace ColorMatchRgb = + new RgbGammaWorkingSpace( + Illuminants.D50, + 1.8F, + new RgbPrimariesChromaticityCoordinates( + new CieXyChromaticityCoordinates(0.6300F, 0.3400F), + new CieXyChromaticityCoordinates(0.2950F, 0.6050F), + new CieXyChromaticityCoordinates(0.1500F, 0.0750F))); /// /// Don Rgb 4 working space /// - public static readonly IRgbWorkingSpace DonRgb4 = new RgbWorkingSpace(Illuminants.D50, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6960F, 0.3000F), new CieXyChromaticityCoordinates(0.2150F, 0.7650F), new CieXyChromaticityCoordinates(0.1300F, 0.0350F))); + public static readonly IRgbWorkingSpace DonRgb4 = + new RgbGammaWorkingSpace( + Illuminants.D50, + 2.2F, + new RgbPrimariesChromaticityCoordinates( + new CieXyChromaticityCoordinates(0.6960F, 0.3000F), + new CieXyChromaticityCoordinates(0.2150F, 0.7650F), + new CieXyChromaticityCoordinates(0.1300F, 0.0350F))); /// /// Ekta Space PS5 working space /// - public static readonly IRgbWorkingSpace EktaSpacePS5 = new RgbWorkingSpace(Illuminants.D50, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6950F, 0.3050F), new CieXyChromaticityCoordinates(0.2600F, 0.7000F), new CieXyChromaticityCoordinates(0.1100F, 0.0050F))); + public static readonly IRgbWorkingSpace EktaSpacePS5 = + new RgbGammaWorkingSpace( + Illuminants.D50, + 2.2F, + new RgbPrimariesChromaticityCoordinates( + new CieXyChromaticityCoordinates(0.6950F, 0.3050F), + new CieXyChromaticityCoordinates(0.2600F, 0.7000F), + new CieXyChromaticityCoordinates(0.1100F, 0.0050F))); /// /// NTSC Rgb working space /// - public static readonly IRgbWorkingSpace NTSCRgb = new RgbWorkingSpace(Illuminants.C, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6700F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1400F, 0.0800F))); + public static readonly IRgbWorkingSpace NTSCRgb = + new RgbGammaWorkingSpace( + Illuminants.C, + 2.2F, + new RgbPrimariesChromaticityCoordinates( + new CieXyChromaticityCoordinates(0.6700F, 0.3300F), + new CieXyChromaticityCoordinates(0.2100F, 0.7100F), + new CieXyChromaticityCoordinates(0.1400F, 0.0800F))); /// /// PAL/SECAM Rgb working space /// - public static readonly IRgbWorkingSpace PALSECAMRgb = new RgbWorkingSpace(Illuminants.D65, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2900F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); + public static readonly IRgbWorkingSpace PALSECAMRgb = + new RgbGammaWorkingSpace( + Illuminants.D65, + 2.2F, + new RgbPrimariesChromaticityCoordinates( + new CieXyChromaticityCoordinates(0.6400F, 0.3300F), + new CieXyChromaticityCoordinates(0.2900F, 0.6000F), + new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); /// /// ProPhoto Rgb working space /// - public static readonly IRgbWorkingSpace ProPhotoRgb = new RgbWorkingSpace(Illuminants.D50, new GammaCompanding(1.8F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7347F, 0.2653F), new CieXyChromaticityCoordinates(0.1596F, 0.8404F), new CieXyChromaticityCoordinates(0.0366F, 0.0001F))); + public static readonly IRgbWorkingSpace ProPhotoRgb = + new RgbGammaWorkingSpace( + Illuminants.D50, + 1.8F, + new RgbPrimariesChromaticityCoordinates( + new CieXyChromaticityCoordinates(0.7347F, 0.2653F), + new CieXyChromaticityCoordinates(0.1596F, 0.8404F), + new CieXyChromaticityCoordinates(0.0366F, 0.0001F))); /// /// SMPTE-C Rgb working space /// - public static readonly IRgbWorkingSpace SMPTECRgb = new RgbWorkingSpace(Illuminants.D65, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6300F, 0.3400F), new CieXyChromaticityCoordinates(0.3100F, 0.5950F), new CieXyChromaticityCoordinates(0.1550F, 0.0700F))); + public static readonly IRgbWorkingSpace SMPTECRgb = + new RgbGammaWorkingSpace( + Illuminants.D65, + 2.2F, + new RgbPrimariesChromaticityCoordinates( + new CieXyChromaticityCoordinates(0.6300F, 0.3400F), + new CieXyChromaticityCoordinates(0.3100F, 0.5950F), + new CieXyChromaticityCoordinates(0.1550F, 0.0700F))); /// /// Wide Gamut Rgb working space /// - public static readonly IRgbWorkingSpace WideGamutRgb = new RgbWorkingSpace(Illuminants.D50, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7350F, 0.2650F), new CieXyChromaticityCoordinates(0.1150F, 0.8260F), new CieXyChromaticityCoordinates(0.1570F, 0.0180F))); + public static readonly IRgbWorkingSpace WideGamutRgb = + new RgbGammaWorkingSpace( + Illuminants.D50, + 2.2F, + new RgbPrimariesChromaticityCoordinates( + new CieXyChromaticityCoordinates(0.7350F, 0.2650F), + new CieXyChromaticityCoordinates(0.1150F, 0.8260F), + new CieXyChromaticityCoordinates(0.1570F, 0.0180F))); } } \ No newline at end of file From 5959bfbf3e04da954186fa9f7ceca837a616b41b Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 6 Feb 2018 22:06:33 +1100 Subject: [PATCH 039/151] Update ToString so we can see full output. --- .../ColorSpaces/CieXyChromaticityCoordinates.cs | 9 ++------- .../Rgb/RGBPrimariesChromaticityCoordinates.cs | 13 +++++++------ 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs b/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs index d9767d45ea..94ba6de782 100644 --- a/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs +++ b/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs @@ -124,19 +124,14 @@ namespace SixLabors.ImageSharp.ColorSpaces return "CieXyChromaticityCoordinates [Empty]"; } - return $"CieXyChromaticityCoordinates [ X={this.X:#0.##}, Y={this.Y:#0.##}]"; + return $"CieXyChromaticityCoordinates [ X={this.X}, Y={this.Y}]"; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals(object obj) { - if (obj is CieXyChromaticityCoordinates) - { - return this.Equals((CieXyChromaticityCoordinates)obj); - } - - return false; + return obj is CieXyChromaticityCoordinates coordinates && this.Equals(coordinates); } /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RGBPrimariesChromaticityCoordinates.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RGBPrimariesChromaticityCoordinates.cs index d5b9b3cbe5..062398f94e 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RGBPrimariesChromaticityCoordinates.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RGBPrimariesChromaticityCoordinates.cs @@ -76,12 +76,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap /// public override bool Equals(object obj) { - if (obj is RgbPrimariesChromaticityCoordinates) - { - return this.Equals((RgbPrimariesChromaticityCoordinates)obj); - } - - return false; + return obj is RgbPrimariesChromaticityCoordinates coordinates && this.Equals(coordinates); } /// @@ -90,6 +85,12 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap return this.R.Equals(other.R) && this.G.Equals(other.G) && this.B.Equals(other.B); } + /// + public override string ToString() + { + return $"RgbPrimariesChromaticityCoordinates [ R={this.R}, G={this.G}, B={this.B}]"; + } + /// public override int GetHashCode() { From 93f7e0e585cbd64e5ad2cf19b98753f1bdbbb7b9 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 6 Feb 2018 23:11:57 +1100 Subject: [PATCH 040/151] Use custom comparer --- .../Colorspaces/RgbAndCieXyzConversionTest.cs | 7 ++- .../Colorspaces/RgbAndHslConversionTest.cs | 4 +- .../Colorspaces/RgbAndHsvConversionTest.cs | 4 +- .../Colorspaces/RgbAndYCbCrConversionTest.cs | 4 +- .../TestUtilities/ApproximateFloatComparer.cs | 57 ++++++++++++++++++- 5 files changed, 69 insertions(+), 7 deletions(-) diff --git a/tests/ImageSharp.Tests/Colorspaces/RgbAndCieXyzConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/RgbAndCieXyzConversionTest.cs index 0293811fbd..48c91dd6d0 100644 --- a/tests/ImageSharp.Tests/Colorspaces/RgbAndCieXyzConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/RgbAndCieXyzConversionTest.cs @@ -19,6 +19,8 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces { private static readonly IEqualityComparer FloatRoundingComparer = new FloatRoundingComparer(6); + private static readonly ApproximateFloatComparer ApproximateComparer = new ApproximateFloatComparer(0.0001F); + /// /// Tests conversion from () /// to (default sRGB working space). @@ -40,7 +42,7 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces Rgb output = converter.ToRgb(input); // Assert - Assert.Equal(Rgb.DefaultWorkingSpace, output.WorkingSpace); + Assert.Equal(Rgb.DefaultWorkingSpace, output.WorkingSpace, ApproximateComparer); Assert.Equal(r, output.R, FloatRoundingComparer); Assert.Equal(g, output.G, FloatRoundingComparer); Assert.Equal(b, output.B, FloatRoundingComparer); @@ -68,8 +70,7 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces Rgb output = converter.ToRgb(input); // Assert - // TODO: Enable next line - // Assert.Equal(Rgb.DefaultWorkingSpace, output.WorkingSpace); + Assert.Equal(Rgb.DefaultWorkingSpace, output.WorkingSpace, ApproximateComparer); Assert.Equal(r, output.R, FloatRoundingComparer); Assert.Equal(g, output.G, FloatRoundingComparer); Assert.Equal(b, output.B, FloatRoundingComparer); diff --git a/tests/ImageSharp.Tests/Colorspaces/RgbAndHslConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/RgbAndHslConversionTest.cs index a7071e883d..f658ddaae5 100644 --- a/tests/ImageSharp.Tests/Colorspaces/RgbAndHslConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/RgbAndHslConversionTest.cs @@ -22,6 +22,8 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + private static readonly ApproximateFloatComparer ApproximateComparer = new ApproximateFloatComparer(0.0001F); + /// /// Tests conversion from to . /// @@ -41,7 +43,7 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces Rgb output = Converter.ToRgb(input); // Assert - Assert.Equal(Rgb.DefaultWorkingSpace, output.WorkingSpace); + Assert.Equal(Rgb.DefaultWorkingSpace, output.WorkingSpace, ApproximateComparer); Assert.Equal(r, output.R, FloatRoundingComparer); Assert.Equal(g, output.G, FloatRoundingComparer); Assert.Equal(b, output.B, FloatRoundingComparer); diff --git a/tests/ImageSharp.Tests/Colorspaces/RgbAndHsvConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/RgbAndHsvConversionTest.cs index 0dc58a0a3e..63b3d9b749 100644 --- a/tests/ImageSharp.Tests/Colorspaces/RgbAndHsvConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/RgbAndHsvConversionTest.cs @@ -21,6 +21,8 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + private static readonly ApproximateFloatComparer ApproximateComparer = new ApproximateFloatComparer(0.0001F); + /// /// Tests conversion from to . /// @@ -40,7 +42,7 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces Rgb output = Converter.ToRgb(input); // Assert - Assert.Equal(Rgb.DefaultWorkingSpace, output.WorkingSpace); + Assert.Equal(Rgb.DefaultWorkingSpace, output.WorkingSpace, ApproximateComparer); Assert.Equal(r, output.R, FloatRoundingComparer); Assert.Equal(g, output.G, FloatRoundingComparer); Assert.Equal(b, output.B, FloatRoundingComparer); diff --git a/tests/ImageSharp.Tests/Colorspaces/RgbAndYCbCrConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/RgbAndYCbCrConversionTest.cs index 0eb1f620bf..96c302e25b 100644 --- a/tests/ImageSharp.Tests/Colorspaces/RgbAndYCbCrConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/RgbAndYCbCrConversionTest.cs @@ -20,6 +20,8 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + private static readonly ApproximateFloatComparer ApproximateComparer = new ApproximateFloatComparer(0.0001F); + /// /// Tests conversion from to . /// @@ -36,7 +38,7 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces Rgb output = Converter.ToRgb(input); // Assert - Assert.Equal(Rgb.DefaultWorkingSpace, output.WorkingSpace); + Assert.Equal(Rgb.DefaultWorkingSpace, output.WorkingSpace, ApproximateComparer); Assert.Equal(r, output.R, FloatRoundingComparer); Assert.Equal(g, output.G, FloatRoundingComparer); Assert.Equal(b, output.B, FloatRoundingComparer); diff --git a/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs b/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs index 70d4df273e..1bd80073e5 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs @@ -4,10 +4,18 @@ using System; using System.Collections.Generic; using System.Numerics; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce; namespace SixLabors.ImageSharp.Tests { - internal struct ApproximateFloatComparer : IEqualityComparer, IEqualityComparer + internal struct ApproximateFloatComparer : + IEqualityComparer, + IEqualityComparer, + IEqualityComparer, + IEqualityComparer, + IEqualityComparer, + IEqualityComparer { private readonly float Eps; @@ -37,5 +45,52 @@ namespace SixLabors.ImageSharp.Tests { throw new InvalidOperationException(); } + + public bool Equals(CieXyChromaticityCoordinates x, CieXyChromaticityCoordinates y) + { + return this.Equals(x.X, y.X) && this.Equals(x.Y, y.Y); + } + + public int GetHashCode(CieXyChromaticityCoordinates obj) + { + throw new NotImplementedException(); + } + + public bool Equals(RgbPrimariesChromaticityCoordinates x, RgbPrimariesChromaticityCoordinates y) + { + return this.Equals(x.R, y.R) && this.Equals(x.G, y.G) && this.Equals(x.B, y.B); + } + + public int GetHashCode(RgbPrimariesChromaticityCoordinates obj) + { + throw new NotImplementedException(); + } + + public bool Equals(CieXyz x, CieXyz y) + { + return this.Equals(x.X, y.X) && this.Equals(x.Y, y.Y) && this.Equals(x.Z, y.Z); + } + + public int GetHashCode(CieXyz obj) + { + throw new NotImplementedException(); + } + + public bool Equals(IRgbWorkingSpace x, IRgbWorkingSpace y) + { + if (x is RgbGammaWorkingSpace g1 && y is RgbGammaWorkingSpace g2) + { + return this.Equals(g1.WhitePoint, g2.WhitePoint) + && this.Equals(g1.ChromaticityCoordinates, g2.ChromaticityCoordinates); + } + + return this.Equals(x.WhitePoint, y.WhitePoint) + && this.Equals(x.ChromaticityCoordinates, y.ChromaticityCoordinates); + } + + public int GetHashCode(IRgbWorkingSpace obj) + { + throw new NotImplementedException(); + } } } \ No newline at end of file From 46161c652589d2dc01767cc139123f0e625f279f Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 6 Feb 2018 23:38:42 +1100 Subject: [PATCH 041/151] DebugGuard + missing ApproximateComparer --- .../Implementation/Rgb/LinearRgbAndCieXyzConverterBase.cs | 4 ++-- .../Implementation/Rgb/LinearRgbToCieXyzConverter.cs | 2 +- .../ImageSharp.Tests/Colorspaces/RgbAndCmykConversionTest.cs | 4 +++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbAndCieXyzConverterBase.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbAndCieXyzConverterBase.cs index 2ec79b353b..b40a02af76 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbAndCieXyzConverterBase.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbAndCieXyzConverterBase.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap internal abstract class LinearRgbAndCieXyzConverterBase { /// - /// Geturns the correct matrix to convert between the Rgb and CieXyz color space. + /// Returns the correct matrix to convert between the Rgb and CieXyz color space. /// /// The Rgb working space. /// The based on the chromaticity and working space. @@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap Vector3 vector = Vector3.Transform(workingSpace.WhitePoint.Vector, inverseXyzMatrix); - // Use transposed Rows/Coloumns + // Use transposed Rows/Columns // TODO: Is there a built in method for this multiplication? return new Matrix4x4 { diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToCieXyzConverter.cs index 19d4130373..bf36e252a2 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToCieXyzConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToCieXyzConverter.cs @@ -39,7 +39,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap public CieXyz Convert(LinearRgb input) { DebugGuard.NotNull(input, nameof(input)); - Guard.IsTrue(input.WorkingSpace.Equals(this.SourceWorkingSpace), nameof(input.WorkingSpace), "Input and source working spaces must be equal."); + DebugGuard.IsTrue(input.WorkingSpace.Equals(this.SourceWorkingSpace), nameof(input.WorkingSpace), "Input and source working spaces must be equal."); Vector3 vector = Vector3.Transform(input.Vector, this.conversionMatrix); return new CieXyz(vector); diff --git a/tests/ImageSharp.Tests/Colorspaces/RgbAndCmykConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/RgbAndCmykConversionTest.cs index 6c3d579b4e..aa1f9c5743 100644 --- a/tests/ImageSharp.Tests/Colorspaces/RgbAndCmykConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/RgbAndCmykConversionTest.cs @@ -22,6 +22,8 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + private static readonly ApproximateFloatComparer ApproximateComparer = new ApproximateFloatComparer(0.0001F); + /// /// Tests conversion from to . /// @@ -38,7 +40,7 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces Rgb output = Converter.ToRgb(input); // Assert - Assert.Equal(Rgb.DefaultWorkingSpace, output.WorkingSpace); + Assert.Equal(Rgb.DefaultWorkingSpace, output.WorkingSpace, ApproximateComparer); Assert.Equal(r, output.R, FloatRoundingComparer); Assert.Equal(g, output.G, FloatRoundingComparer); Assert.Equal(b, output.B, FloatRoundingComparer); From 4269320a228ea99cad87a902ccadf927e3355727 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 7 Feb 2018 00:00:34 +1100 Subject: [PATCH 042/151] Should be last missing ApproximateComparer --- .../ImageSharp.Tests/Colorspaces/ColorConverterAdaptTest.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/ImageSharp.Tests/Colorspaces/ColorConverterAdaptTest.cs b/tests/ImageSharp.Tests/Colorspaces/ColorConverterAdaptTest.cs index 87dc59907b..6cb32be47b 100644 --- a/tests/ImageSharp.Tests/Colorspaces/ColorConverterAdaptTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/ColorConverterAdaptTest.cs @@ -19,6 +19,8 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces { private static readonly IEqualityComparer FloatRoundingComparer = new FloatRoundingComparer(3); + private static readonly ApproximateFloatComparer ApproximateComparer = new ApproximateFloatComparer(0.0001F); + [Theory] [InlineData(0, 0, 0, 0, 0, 0)] [InlineData(1, 1, 1, 1, 1, 1)] @@ -34,7 +36,7 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces Rgb output = converter.Adapt(input); // Assert - Assert.Equal(expectedOutput.WorkingSpace, output.WorkingSpace); + Assert.Equal(expectedOutput.WorkingSpace, output.WorkingSpace, ApproximateComparer); Assert.Equal(expectedOutput.R, output.R, FloatRoundingComparer); Assert.Equal(expectedOutput.G, output.G, FloatRoundingComparer); Assert.Equal(expectedOutput.B, output.B, FloatRoundingComparer); @@ -55,7 +57,7 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces Rgb output = converter.Adapt(input); // Assert - Assert.Equal(expectedOutput.WorkingSpace, output.WorkingSpace); + Assert.Equal(expectedOutput.WorkingSpace, output.WorkingSpace, ApproximateComparer); Assert.Equal(expectedOutput.R, output.R, FloatRoundingComparer); Assert.Equal(expectedOutput.G, output.G, FloatRoundingComparer); Assert.Equal(expectedOutput.B, output.B, FloatRoundingComparer); From d75005f47314c582b5199a1a99e451aff236d0c7 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 15 Feb 2018 23:54:02 +1100 Subject: [PATCH 043/151] Revert most colorspace changes. --- .../CieXyChromaticityCoordinates.cs | 9 +- .../Conversion/ColorSpaceConverter.Rgb.cs | 5 +- .../Implementation/Rgb/GammaCompanding.cs | 46 +++++ .../Implementation/Rgb/LCompanding.cs | 35 ++++ .../Rgb/LinearRgbToRgbConverter.cs | 6 +- .../RGBPrimariesChromaticityCoordinates.cs | 25 ++- .../Implementation/Rgb/Rec2020Companding.cs | 32 ++++ .../Implementation/Rgb/Rec709Companding.cs | 31 ++++ .../Rgb/RgbGammaWorkingSpace.cs | 89 --------- .../Implementation/Rgb/RgbLWorkingSpace.cs | 82 --------- .../Rgb/RgbRec2020WorkingSpace.cs | 79 -------- .../Rgb/RgbRec709WorkingSpace.cs | 78 -------- .../Implementation/Rgb/RgbSRgbWorkingSpace.cs | 80 --------- .../Rgb/RgbToLinearRgbConverter.cs | 6 +- .../Implementation/Rgb/RgbWorkingSpace.cs | 105 +++++++++++ .../Implementation/Rgb/SRgbCompanding.cs | 33 ++++ src/ImageSharp/ColorSpaces/ICompanding.cs | 35 ++++ .../ColorSpaces/IRgbWorkingSpace.cs | 23 +-- .../ColorSpaces/RgbWorkingSpaces.cs | 169 +++--------------- .../TestUtilities/ApproximateFloatComparer.cs | 2 +- 20 files changed, 371 insertions(+), 599 deletions(-) create mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/GammaCompanding.cs create mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LCompanding.cs create mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec2020Companding.cs create mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec709Companding.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbGammaWorkingSpace.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbLWorkingSpace.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbRec2020WorkingSpace.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbRec709WorkingSpace.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbSRgbWorkingSpace.cs create mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs create mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/SRgbCompanding.cs create mode 100644 src/ImageSharp/ColorSpaces/ICompanding.cs diff --git a/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs b/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs index 94ba6de782..d9767d45ea 100644 --- a/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs +++ b/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs @@ -124,14 +124,19 @@ namespace SixLabors.ImageSharp.ColorSpaces return "CieXyChromaticityCoordinates [Empty]"; } - return $"CieXyChromaticityCoordinates [ X={this.X}, Y={this.Y}]"; + return $"CieXyChromaticityCoordinates [ X={this.X:#0.##}, Y={this.Y:#0.##}]"; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals(object obj) { - return obj is CieXyChromaticityCoordinates coordinates && this.Equals(coordinates); + if (obj is CieXyChromaticityCoordinates) + { + return this.Equals((CieXyChromaticityCoordinates)obj); + } + + return false; } /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs index f3803b9bd5..6844e3a3ca 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs @@ -181,7 +181,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion Guard.NotNull(color, nameof(color)); // Conversion - return YCbCrAndRgbConverter.Convert(color); + Rgb rgb = YCbCrAndRgbConverter.Convert(color); + + // Adaptation + return this.Adapt(rgb); } } } \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/GammaCompanding.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/GammaCompanding.cs new file mode 100644 index 0000000000..21a80225ee --- /dev/null +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/GammaCompanding.cs @@ -0,0 +1,46 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce +{ + /// + /// Implements gamma companding + /// + /// + /// + /// + /// + public class GammaCompanding : ICompanding + { + /// + /// Initializes a new instance of the class. + /// + /// The gamma value. + public GammaCompanding(float gamma) + { + this.Gamma = gamma; + } + + /// + /// Gets the gamma value + /// + public float Gamma { get; } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float Expand(float channel) + { + return MathF.Pow(channel, this.Gamma); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float Compress(float channel) + { + return MathF.Pow(channel, 1 / this.Gamma); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LCompanding.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LCompanding.cs new file mode 100644 index 0000000000..132861b476 --- /dev/null +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LCompanding.cs @@ -0,0 +1,35 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce +{ + /// + /// Implements L* companding + /// + /// + /// For more info see: + /// + /// + /// + public class LCompanding : ICompanding + { + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float Expand(float channel) + { + return channel <= 0.08 ? 100 * channel / CieConstants.Kappa : MathF.Pow((channel + 0.16F) / 1.16F, 3); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float Compress(float channel) + { + return channel <= CieConstants.Epsilon + ? channel * CieConstants.Kappa / 100F + : MathF.Pow(1.16F * channel, 0.3333333F) - 0.16F; + } + } +} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToRgbConverter.cs index 25dbc746b0..29ea0f3148 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToRgbConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToRgbConverter.cs @@ -16,9 +16,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap DebugGuard.NotNull(input, nameof(input)); Vector3 vector = input.Vector; - vector.X = input.WorkingSpace.Compress(vector.X); - vector.Y = input.WorkingSpace.Compress(vector.Y); - vector.Z = input.WorkingSpace.Compress(vector.Z); + vector.X = input.WorkingSpace.Companding.Compress(vector.X); + vector.Y = input.WorkingSpace.Companding.Compress(vector.Y); + vector.Z = input.WorkingSpace.Companding.Compress(vector.Z); return new Rgb(vector, input.WorkingSpace); } diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RGBPrimariesChromaticityCoordinates.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RGBPrimariesChromaticityCoordinates.cs index 062398f94e..d279aba850 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RGBPrimariesChromaticityCoordinates.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RGBPrimariesChromaticityCoordinates.cs @@ -14,9 +14,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap /// /// Initializes a new instance of the struct. /// - /// The chromaticity coordinates of the red channel. - /// The chromaticity coordinates of the green channel. - /// The chromaticity coordinates of the blue channel. + /// The chomaticity coordinates of the red channel. + /// The chomaticity coordinates of the green channel. + /// The chomaticity coordinates of the blue channel. public RgbPrimariesChromaticityCoordinates(CieXyChromaticityCoordinates r, CieXyChromaticityCoordinates g, CieXyChromaticityCoordinates b) { this.R = r; @@ -25,17 +25,17 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap } /// - /// Gets the chromaticity coordinates of the red channel. + /// Gets the chomaticity coordinates of the red channel. /// public CieXyChromaticityCoordinates R { get; } /// - /// Gets the chromaticity coordinates of the green channel. + /// Gets the chomaticity coordinates of the green channel. /// public CieXyChromaticityCoordinates G { get; } /// - /// Gets the chromaticity coordinates of the blue channel. + /// Gets the chomaticity coordinates of the blue channel. /// public CieXyChromaticityCoordinates B { get; } @@ -76,7 +76,12 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap /// public override bool Equals(object obj) { - return obj is RgbPrimariesChromaticityCoordinates coordinates && this.Equals(coordinates); + if (obj is RgbPrimariesChromaticityCoordinates) + { + return this.Equals((RgbPrimariesChromaticityCoordinates)obj); + } + + return false; } /// @@ -85,12 +90,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap return this.R.Equals(other.R) && this.G.Equals(other.G) && this.B.Equals(other.B); } - /// - public override string ToString() - { - return $"RgbPrimariesChromaticityCoordinates [ R={this.R}, G={this.G}, B={this.B}]"; - } - /// public override int GetHashCode() { diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec2020Companding.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec2020Companding.cs new file mode 100644 index 0000000000..11761f0e4d --- /dev/null +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec2020Companding.cs @@ -0,0 +1,32 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce +{ + /// + /// Implements Rec. 2020 companding function (for 12-bits). + /// + /// + /// + /// For 10-bits, companding is identical to + /// + public class Rec2020Companding : ICompanding + { + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float Expand(float channel) + { + return channel < 0.08145F ? channel / 4.5F : MathF.Pow((channel + 0.0993F) / 1.0993F, 2.222222F); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float Compress(float channel) + { + return channel < 0.0181F ? 4500F * channel : (1.0993F * channel) - 0.0993F; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec709Companding.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec709Companding.cs new file mode 100644 index 0000000000..ccda6bf521 --- /dev/null +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec709Companding.cs @@ -0,0 +1,31 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce +{ + /// + /// Implements the Rec. 709 companding function + /// + /// + /// http://en.wikipedia.org/wiki/Rec._709 + /// + public class Rec709Companding : ICompanding + { + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float Expand(float channel) + { + return channel < 0.081F ? channel / 4.5F : MathF.Pow((channel + 0.099F) / 1.099F, 2.222222F); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float Compress(float channel) + { + return channel < 0.018F ? 4500F * channel : (1.099F * channel) - 0.099F; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbGammaWorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbGammaWorkingSpace.cs deleted file mode 100644 index 891beba1ac..0000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbGammaWorkingSpace.cs +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce -{ - /// - /// Represents an that implements gamma companding - /// - /// - /// - /// - /// - internal class RgbGammaWorkingSpace : IRgbWorkingSpace, IEquatable - { - /// - /// Initializes a new instance of the class. - /// - /// The reference white point. - /// The gamma value. - /// The chromaticity of the rgb primaries. - public RgbGammaWorkingSpace(CieXyz referenceWhite, float gamma, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) - { - this.WhitePoint = referenceWhite; - this.Gamma = gamma; - this.ChromaticityCoordinates = chromaticityCoordinates; - } - - /// - public CieXyz WhitePoint { get; } - - /// - public RgbPrimariesChromaticityCoordinates ChromaticityCoordinates { get; } - - /// - /// Gets the gamma value - /// - public float Gamma { get; } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float Expand(float channel) - { - return MathF.Pow(channel, this.Gamma); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float Compress(float channel) - { - return MathF.Pow(channel, 1 / this.Gamma); - } - - /// - public override bool Equals(object obj) - { - return obj is RgbGammaWorkingSpace space && this.Equals(space); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(IRgbWorkingSpace other) - { - return other is RgbGammaWorkingSpace space && this.Equals(space); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(RgbGammaWorkingSpace other) - { - return other != null && - this.WhitePoint.Equals(other.WhitePoint) && - this.ChromaticityCoordinates.Equals(other.ChromaticityCoordinates) && - this.Gamma == other.Gamma; - } - - /// - public override int GetHashCode() - { - return HashHelpers.Combine( - this.WhitePoint.GetHashCode(), - HashHelpers.Combine( - this.ChromaticityCoordinates.GetHashCode(), - this.Gamma.GetHashCode())); - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbLWorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbLWorkingSpace.cs deleted file mode 100644 index c2d75cd90b..0000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbLWorkingSpace.cs +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce -{ - /// - /// Represents an that implements L* companding - /// - /// - /// For more info see: - /// - /// - /// - internal class RgbLWorkingSpace : IRgbWorkingSpace, IEquatable - { - /// - /// Initializes a new instance of the class. - /// - /// The reference white point. - /// The chromaticity of the rgb primaries. - public RgbLWorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) - { - this.WhitePoint = referenceWhite; - this.ChromaticityCoordinates = chromaticityCoordinates; - } - - /// - public CieXyz WhitePoint { get; } - - /// - public RgbPrimariesChromaticityCoordinates ChromaticityCoordinates { get; } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float Expand(float channel) - { - return channel <= 0.08 ? 100 * channel / CieConstants.Kappa : MathF.Pow((channel + 0.16F) / 1.16F, 3); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float Compress(float channel) - { - return channel <= CieConstants.Epsilon - ? channel * CieConstants.Kappa / 100F - : MathF.Pow(1.16F * channel, 0.3333333F) - 0.16F; - } - - /// - public override bool Equals(object obj) - { - return obj is RgbLWorkingSpace space && this.Equals(space); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(IRgbWorkingSpace other) - { - return other is RgbLWorkingSpace space && this.Equals(space); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(RgbLWorkingSpace other) - { - return other != null && - this.WhitePoint.Equals(other.WhitePoint) && - this.ChromaticityCoordinates.Equals(other.ChromaticityCoordinates); - } - - /// - public override int GetHashCode() - { - return HashHelpers.Combine( - this.WhitePoint.GetHashCode(), - this.ChromaticityCoordinates.GetHashCode()); - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbRec2020WorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbRec2020WorkingSpace.cs deleted file mode 100644 index cc814e9b31..0000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbRec2020WorkingSpace.cs +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce -{ - /// - /// Represents an that implements Rec. 2020 companding (for 12-bits). - /// - /// - /// - /// For 10-bits, companding is identical to - /// - internal class RgbRec2020WorkingSpace : IRgbWorkingSpace, IEquatable - { - /// - /// Initializes a new instance of the class. - /// - /// The reference white point. - /// The chromaticity of the rgb primaries. - public RgbRec2020WorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) - { - this.WhitePoint = referenceWhite; - this.ChromaticityCoordinates = chromaticityCoordinates; - } - - /// - public CieXyz WhitePoint { get; } - - /// - public RgbPrimariesChromaticityCoordinates ChromaticityCoordinates { get; } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float Expand(float channel) - { - return channel < 0.08145F ? channel / 4.5F : MathF.Pow((channel + 0.0993F) / 1.0993F, 2.222222F); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float Compress(float channel) - { - return channel < 0.0181F ? 4500F * channel : (1.0993F * channel) - 0.0993F; - } - - /// - public override bool Equals(object obj) - { - return obj is RgbRec2020WorkingSpace space && this.Equals(space); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(IRgbWorkingSpace other) - { - return other is RgbRec2020WorkingSpace space && this.Equals(space); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(RgbRec2020WorkingSpace other) - { - return other != null && - this.WhitePoint.Equals(other.WhitePoint) && - this.ChromaticityCoordinates.Equals(other.ChromaticityCoordinates); - } - - /// - public override int GetHashCode() - { - return HashHelpers.Combine( - this.WhitePoint.GetHashCode(), - this.ChromaticityCoordinates.GetHashCode()); - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbRec709WorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbRec709WorkingSpace.cs deleted file mode 100644 index 87301dd657..0000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbRec709WorkingSpace.cs +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce -{ - /// - /// Represents an that implements Rec. 709 companding. - /// - /// - /// - /// - internal class RgbRec709WorkingSpace : IRgbWorkingSpace, IEquatable - { - /// - /// Initializes a new instance of the class. - /// - /// The reference white point. - /// The chromaticity of the rgb primaries. - public RgbRec709WorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) - { - this.WhitePoint = referenceWhite; - this.ChromaticityCoordinates = chromaticityCoordinates; - } - - /// - public CieXyz WhitePoint { get; } - - /// - public RgbPrimariesChromaticityCoordinates ChromaticityCoordinates { get; } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float Expand(float channel) - { - return channel < 0.081F ? channel / 4.5F : MathF.Pow((channel + 0.099F) / 1.099F, 2.222222F); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float Compress(float channel) - { - return channel < 0.018F ? 4500F * channel : (1.099F * channel) - 0.099F; - } - - /// - public override bool Equals(object obj) - { - return obj is RgbRec709WorkingSpace space && this.Equals(space); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(IRgbWorkingSpace other) - { - return other is RgbRec709WorkingSpace space && this.Equals(space); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(RgbRec709WorkingSpace other) - { - return other != null && - this.WhitePoint.Equals(other.WhitePoint) && - this.ChromaticityCoordinates.Equals(other.ChromaticityCoordinates); - } - - /// - public override int GetHashCode() - { - return HashHelpers.Combine( - this.WhitePoint.GetHashCode(), - this.ChromaticityCoordinates.GetHashCode()); - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbSRgbWorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbSRgbWorkingSpace.cs deleted file mode 100644 index 98edaa0b37..0000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbSRgbWorkingSpace.cs +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce -{ - /// - /// Represents an that implements sRGB companding - /// - /// - /// For more info see: - /// - /// - /// - internal class RgbSRgbWorkingSpace : IRgbWorkingSpace, IEquatable - { - /// - /// Initializes a new instance of the class. - /// - /// The reference white point. - /// The chromaticity of the rgb primaries. - public RgbSRgbWorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) - { - this.WhitePoint = referenceWhite; - this.ChromaticityCoordinates = chromaticityCoordinates; - } - - /// - public CieXyz WhitePoint { get; } - - /// - public RgbPrimariesChromaticityCoordinates ChromaticityCoordinates { get; } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float Expand(float channel) - { - return channel <= 0.04045F ? channel / 12.92F : MathF.Pow((channel + 0.055F) / 1.055F, 2.4F); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float Compress(float channel) - { - return channel <= 0.0031308F ? 12.92F * channel : (1.055F * MathF.Pow(channel, 0.416666666666667F)) - 0.055F; - } - - /// - public override bool Equals(object obj) - { - return obj is RgbSRgbWorkingSpace space && this.Equals(space); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(IRgbWorkingSpace other) - { - return other is RgbSRgbWorkingSpace space && this.Equals(space); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(RgbSRgbWorkingSpace other) - { - return other != null && - this.WhitePoint.Equals(other.WhitePoint) && - this.ChromaticityCoordinates.Equals(other.ChromaticityCoordinates); - } - - /// - public override int GetHashCode() - { - return HashHelpers.Combine( - this.WhitePoint.GetHashCode(), - this.ChromaticityCoordinates.GetHashCode()); - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbToLinearRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbToLinearRgbConverter.cs index 89a57051f7..e40ecc192e 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbToLinearRgbConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbToLinearRgbConverter.cs @@ -16,9 +16,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap Guard.NotNull(input, nameof(input)); Vector3 vector = input.Vector; - vector.X = input.WorkingSpace.Expand(vector.X); - vector.Y = input.WorkingSpace.Expand(vector.Y); - vector.Z = input.WorkingSpace.Expand(vector.Z); + vector.X = input.WorkingSpace.Companding.Expand(vector.X); + vector.Y = input.WorkingSpace.Companding.Expand(vector.Y); + vector.Z = input.WorkingSpace.Companding.Expand(vector.Z); return new LinearRgb(vector, input.WorkingSpace); } diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs new file mode 100644 index 0000000000..8a2c66a80c --- /dev/null +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs @@ -0,0 +1,105 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce +{ + /// + /// Trivial implementation of + /// + internal struct RgbWorkingSpace : IRgbWorkingSpace + { + /// + /// Initializes a new instance of the struct. + /// + /// The reference white point. + /// The function pair for converting to and back. + /// The chromaticity of the rgb primaries. + public RgbWorkingSpace(CieXyz referenceWhite, ICompanding companding, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) + { + this.WhitePoint = referenceWhite; + this.Companding = companding; + this.ChromaticityCoordinates = chromaticityCoordinates; + } + + /// + /// Gets the reference white point + /// + public CieXyz WhitePoint { get; } + + /// + /// Gets the function pair for converting to and back. + /// + public ICompanding Companding { get; } + + /// + /// Gets the chromaticity of the rgb primaries. + /// + public RgbPrimariesChromaticityCoordinates ChromaticityCoordinates { get; } + + /// + /// Compares two objects for equality. + /// + /// + /// The on the left side of the operand. + /// + /// + /// The on the right side of the operand. + /// + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + public static bool operator ==(RgbWorkingSpace left, RgbWorkingSpace right) + { + return Equals(left, right); + } + + /// + /// Compares two objects for inequality + /// + /// + /// The on the left side of the operand. + /// + /// + /// The on the right side of the operand. + /// + /// + /// True if the current left is unequal to the parameter; otherwise, false. + /// + public static bool operator !=(RgbWorkingSpace left, RgbWorkingSpace right) + { + return !Equals(left, right); + } + + /// + public override bool Equals(object obj) + { + if (obj is RgbWorkingSpace) + { + return this.Equals((RgbWorkingSpace)obj); + } + + return false; + } + + /// + public bool Equals(IRgbWorkingSpace other) + { + // TODO: Object.Equals for ICompanding will be slow. + return this.WhitePoint.Equals(other.WhitePoint) + && this.ChromaticityCoordinates.Equals(other.ChromaticityCoordinates) + && Equals(this.Companding, other.Companding); + } + + /// + public override int GetHashCode() + { + unchecked + { + int hashCode = this.WhitePoint.GetHashCode(); + hashCode = (hashCode * 397) ^ this.ChromaticityCoordinates.GetHashCode(); + hashCode = (hashCode * 397) ^ (this.Companding?.GetHashCode() ?? 0); + return hashCode; + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/SRgbCompanding.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/SRgbCompanding.cs new file mode 100644 index 0000000000..ce8ea7c6e5 --- /dev/null +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/SRgbCompanding.cs @@ -0,0 +1,33 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce +{ + /// + /// Implements sRGB companding + /// + /// + /// For more info see: + /// + /// + /// + public class SRgbCompanding : ICompanding + { + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float Expand(float channel) + { + return channel <= 0.04045F ? channel / 12.92F : MathF.Pow((channel + 0.055F) / 1.055F, 2.4F); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float Compress(float channel) + { + return channel <= 0.0031308F ? 12.92F * channel : (1.055F * MathF.Pow(channel, 0.416666666666667F)) - 0.055F; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/ICompanding.cs b/src/ImageSharp/ColorSpaces/ICompanding.cs new file mode 100644 index 0000000000..2dfa575ed9 --- /dev/null +++ b/src/ImageSharp/ColorSpaces/ICompanding.cs @@ -0,0 +1,35 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.ColorSpaces +{ + /// + /// Pair of companding functions for . + /// Used for conversion to and backwards. + /// See also: + /// + internal interface ICompanding + { + /// + /// Expands a companded channel to its linear equivalent with respect to the energy. + /// + /// + /// For more info see: + /// + /// + /// The channel value + /// The linear channel value + float Expand(float channel); + + /// + /// Compresses an uncompanded channel (linear) to its nonlinear equivalent (depends on the RGB color system). + /// + /// + /// For more info see: + /// + /// + /// The channel value + /// The nonlinear channel value + float Compress(float channel); + } +} diff --git a/src/ImageSharp/ColorSpaces/IRgbWorkingSpace.cs b/src/ImageSharp/ColorSpaces/IRgbWorkingSpace.cs index 1af3a219d6..156e94ed3c 100644 --- a/src/ImageSharp/ColorSpaces/IRgbWorkingSpace.cs +++ b/src/ImageSharp/ColorSpaces/IRgbWorkingSpace.cs @@ -7,7 +7,7 @@ using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce; namespace SixLabors.ImageSharp.ColorSpaces { /// - /// Encapsulates the RGB working color space + /// Encasulates the RGB working color space /// internal interface IRgbWorkingSpace : IEquatable { @@ -22,25 +22,10 @@ namespace SixLabors.ImageSharp.ColorSpaces RgbPrimariesChromaticityCoordinates ChromaticityCoordinates { get; } /// - /// Expands a compressed channel to its linear equivalent with respect to the energy. - /// - /// - /// For more info see: + /// Gets the companding function associated with the RGB color system. Used for conversion to XYZ and backwards. /// - /// - /// The channel value - /// The linear channel value - float Expand(float channel); - - /// - /// Compresses an expanded channel (linear) to its nonlinear equivalent (depends on the RGB color system). - /// - /// - /// For more info see: /// - /// - /// The channel value - /// The nonlinear channel value - float Compress(float channel); + /// + ICompanding Companding { get; } } } \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs b/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs index 93557154d9..098ca9a4a4 100644 --- a/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs +++ b/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs @@ -19,226 +19,97 @@ namespace SixLabors.ImageSharp.ColorSpaces /// Uses proper companding function, according to: /// /// - public static readonly IRgbWorkingSpace SRgb = - new RgbSRgbWorkingSpace( - Illuminants.D65, - new RgbPrimariesChromaticityCoordinates( - new CieXyChromaticityCoordinates(0.6400F, 0.3300F), - new CieXyChromaticityCoordinates(0.3000F, 0.6000F), - new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); + public static readonly IRgbWorkingSpace SRgb = new RgbWorkingSpace(Illuminants.D65, new SRgbCompanding(), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.3000F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); /// - /// Simplified sRgb working space that uses gamma companding instead of srgb companding. + /// Simplified sRgb working space (uses gamma companding instead of ). /// See also . /// - public static readonly IRgbWorkingSpace SRgbSimplified = - new RgbGammaWorkingSpace( - Illuminants.D65, - 2.2F, - new RgbPrimariesChromaticityCoordinates( - new CieXyChromaticityCoordinates(0.6400F, 0.3300F), - new CieXyChromaticityCoordinates(0.3000F, 0.6000F), - new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); + public static readonly IRgbWorkingSpace SRgbSimplified = new RgbWorkingSpace(Illuminants.D65, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.3000F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); /// /// Rec. 709 (ITU-R Recommendation BT.709) working space /// - public static readonly IRgbWorkingSpace Rec709 = - new RgbRec709WorkingSpace( - Illuminants.D65, - new RgbPrimariesChromaticityCoordinates( - new CieXyChromaticityCoordinates(0.64F, 0.33F), - new CieXyChromaticityCoordinates(0.30F, 0.60F), - new CieXyChromaticityCoordinates(0.15F, 0.06F))); + public static readonly IRgbWorkingSpace Rec709 = new RgbWorkingSpace(Illuminants.D65, new Rec709Companding(), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.64F, 0.33F), new CieXyChromaticityCoordinates(0.30F, 0.60F), new CieXyChromaticityCoordinates(0.15F, 0.06F))); /// /// Rec. 2020 (ITU-R Recommendation BT.2020F) working space /// - public static readonly IRgbWorkingSpace Rec2020 = - new RgbRec2020WorkingSpace( - Illuminants.D65, - new RgbPrimariesChromaticityCoordinates( - new CieXyChromaticityCoordinates(0.708F, 0.292F), - new CieXyChromaticityCoordinates(0.170F, 0.797F), - new CieXyChromaticityCoordinates(0.131F, 0.046F))); + public static readonly IRgbWorkingSpace Rec2020 = new RgbWorkingSpace(Illuminants.D65, new Rec2020Companding(), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.708F, 0.292F), new CieXyChromaticityCoordinates(0.170F, 0.797F), new CieXyChromaticityCoordinates(0.131F, 0.046F))); /// /// ECI Rgb v2 working space /// - public static readonly IRgbWorkingSpace ECIRgbv2 = - new RgbLWorkingSpace( - Illuminants.D50, - new RgbPrimariesChromaticityCoordinates( - new CieXyChromaticityCoordinates(0.6700F, 0.3300F), - new CieXyChromaticityCoordinates(0.2100F, 0.7100F), - new CieXyChromaticityCoordinates(0.1400F, 0.0800F))); + public static readonly IRgbWorkingSpace ECIRgbv2 = new RgbWorkingSpace(Illuminants.D50, new LCompanding(), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6700F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1400F, 0.0800F))); /// /// Adobe Rgb (1998) working space /// - public static readonly IRgbWorkingSpace AdobeRgb1998 = - new RgbGammaWorkingSpace( - Illuminants.D65, - 2.2F, - new RgbPrimariesChromaticityCoordinates( - new CieXyChromaticityCoordinates(0.6400F, 0.3300F), - new CieXyChromaticityCoordinates(0.2100F, 0.7100F), - new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); + public static readonly IRgbWorkingSpace AdobeRgb1998 = new RgbWorkingSpace(Illuminants.D65, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); /// /// Apple sRgb working space /// - public static readonly IRgbWorkingSpace ApplesRgb = - new RgbGammaWorkingSpace( - Illuminants.D65, - 1.8F, - new RgbPrimariesChromaticityCoordinates( - new CieXyChromaticityCoordinates(0.6250F, 0.3400F), - new CieXyChromaticityCoordinates(0.2800F, 0.5950F), - new CieXyChromaticityCoordinates(0.1550F, 0.0700F))); + public static readonly IRgbWorkingSpace ApplesRgb = new RgbWorkingSpace(Illuminants.D65, new GammaCompanding(1.8F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6250F, 0.3400F), new CieXyChromaticityCoordinates(0.2800F, 0.5950F), new CieXyChromaticityCoordinates(0.1550F, 0.0700F))); /// /// Best Rgb working space /// - public static readonly IRgbWorkingSpace BestRgb = - new RgbGammaWorkingSpace( - Illuminants.D50, - 2.2F, - new RgbPrimariesChromaticityCoordinates( - new CieXyChromaticityCoordinates(0.7347F, 0.2653F), - new CieXyChromaticityCoordinates(0.2150F, 0.7750F), - new CieXyChromaticityCoordinates(0.1300F, 0.0350F))); + public static readonly IRgbWorkingSpace BestRgb = new RgbWorkingSpace(Illuminants.D50, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7347F, 0.2653F), new CieXyChromaticityCoordinates(0.2150F, 0.7750F), new CieXyChromaticityCoordinates(0.1300F, 0.0350F))); /// /// Beta Rgb working space /// - public static readonly IRgbWorkingSpace BetaRgb = - new RgbGammaWorkingSpace( - Illuminants.D50, - 2.2F, - new RgbPrimariesChromaticityCoordinates( - new CieXyChromaticityCoordinates(0.6888F, 0.3112F), - new CieXyChromaticityCoordinates(0.1986F, 0.7551F), - new CieXyChromaticityCoordinates(0.1265F, 0.0352F))); + public static readonly IRgbWorkingSpace BetaRgb = new RgbWorkingSpace(Illuminants.D50, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6888F, 0.3112F), new CieXyChromaticityCoordinates(0.1986F, 0.7551F), new CieXyChromaticityCoordinates(0.1265F, 0.0352F))); /// /// Bruce Rgb working space /// - public static readonly IRgbWorkingSpace BruceRgb = - new RgbGammaWorkingSpace( - Illuminants.D65, - 2.2F, - new RgbPrimariesChromaticityCoordinates( - new CieXyChromaticityCoordinates(0.6400F, 0.3300F), - new CieXyChromaticityCoordinates(0.2800F, 0.6500F), - new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); + public static readonly IRgbWorkingSpace BruceRgb = new RgbWorkingSpace(Illuminants.D65, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2800F, 0.6500F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); /// /// CIE Rgb working space /// - public static readonly IRgbWorkingSpace CIERgb = - new RgbGammaWorkingSpace( - Illuminants.E, - 2.2F, - new RgbPrimariesChromaticityCoordinates( - new CieXyChromaticityCoordinates(0.7350F, 0.2650F), - new CieXyChromaticityCoordinates(0.2740F, 0.7170F), - new CieXyChromaticityCoordinates(0.1670F, 0.0090F))); + public static readonly IRgbWorkingSpace CIERgb = new RgbWorkingSpace(Illuminants.E, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7350F, 0.2650F), new CieXyChromaticityCoordinates(0.2740F, 0.7170F), new CieXyChromaticityCoordinates(0.1670F, 0.0090F))); /// /// ColorMatch Rgb working space /// - public static readonly IRgbWorkingSpace ColorMatchRgb = - new RgbGammaWorkingSpace( - Illuminants.D50, - 1.8F, - new RgbPrimariesChromaticityCoordinates( - new CieXyChromaticityCoordinates(0.6300F, 0.3400F), - new CieXyChromaticityCoordinates(0.2950F, 0.6050F), - new CieXyChromaticityCoordinates(0.1500F, 0.0750F))); + public static readonly IRgbWorkingSpace ColorMatchRgb = new RgbWorkingSpace(Illuminants.D50, new GammaCompanding(1.8F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6300F, 0.3400F), new CieXyChromaticityCoordinates(0.2950F, 0.6050F), new CieXyChromaticityCoordinates(0.1500F, 0.0750F))); /// /// Don Rgb 4 working space /// - public static readonly IRgbWorkingSpace DonRgb4 = - new RgbGammaWorkingSpace( - Illuminants.D50, - 2.2F, - new RgbPrimariesChromaticityCoordinates( - new CieXyChromaticityCoordinates(0.6960F, 0.3000F), - new CieXyChromaticityCoordinates(0.2150F, 0.7650F), - new CieXyChromaticityCoordinates(0.1300F, 0.0350F))); + public static readonly IRgbWorkingSpace DonRgb4 = new RgbWorkingSpace(Illuminants.D50, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6960F, 0.3000F), new CieXyChromaticityCoordinates(0.2150F, 0.7650F), new CieXyChromaticityCoordinates(0.1300F, 0.0350F))); /// /// Ekta Space PS5 working space /// - public static readonly IRgbWorkingSpace EktaSpacePS5 = - new RgbGammaWorkingSpace( - Illuminants.D50, - 2.2F, - new RgbPrimariesChromaticityCoordinates( - new CieXyChromaticityCoordinates(0.6950F, 0.3050F), - new CieXyChromaticityCoordinates(0.2600F, 0.7000F), - new CieXyChromaticityCoordinates(0.1100F, 0.0050F))); + public static readonly IRgbWorkingSpace EktaSpacePS5 = new RgbWorkingSpace(Illuminants.D50, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6950F, 0.3050F), new CieXyChromaticityCoordinates(0.2600F, 0.7000F), new CieXyChromaticityCoordinates(0.1100F, 0.0050F))); /// /// NTSC Rgb working space /// - public static readonly IRgbWorkingSpace NTSCRgb = - new RgbGammaWorkingSpace( - Illuminants.C, - 2.2F, - new RgbPrimariesChromaticityCoordinates( - new CieXyChromaticityCoordinates(0.6700F, 0.3300F), - new CieXyChromaticityCoordinates(0.2100F, 0.7100F), - new CieXyChromaticityCoordinates(0.1400F, 0.0800F))); + public static readonly IRgbWorkingSpace NTSCRgb = new RgbWorkingSpace(Illuminants.C, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6700F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1400F, 0.0800F))); /// /// PAL/SECAM Rgb working space /// - public static readonly IRgbWorkingSpace PALSECAMRgb = - new RgbGammaWorkingSpace( - Illuminants.D65, - 2.2F, - new RgbPrimariesChromaticityCoordinates( - new CieXyChromaticityCoordinates(0.6400F, 0.3300F), - new CieXyChromaticityCoordinates(0.2900F, 0.6000F), - new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); + public static readonly IRgbWorkingSpace PALSECAMRgb = new RgbWorkingSpace(Illuminants.D65, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2900F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); /// /// ProPhoto Rgb working space /// - public static readonly IRgbWorkingSpace ProPhotoRgb = - new RgbGammaWorkingSpace( - Illuminants.D50, - 1.8F, - new RgbPrimariesChromaticityCoordinates( - new CieXyChromaticityCoordinates(0.7347F, 0.2653F), - new CieXyChromaticityCoordinates(0.1596F, 0.8404F), - new CieXyChromaticityCoordinates(0.0366F, 0.0001F))); + public static readonly IRgbWorkingSpace ProPhotoRgb = new RgbWorkingSpace(Illuminants.D50, new GammaCompanding(1.8F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7347F, 0.2653F), new CieXyChromaticityCoordinates(0.1596F, 0.8404F), new CieXyChromaticityCoordinates(0.0366F, 0.0001F))); /// /// SMPTE-C Rgb working space /// - public static readonly IRgbWorkingSpace SMPTECRgb = - new RgbGammaWorkingSpace( - Illuminants.D65, - 2.2F, - new RgbPrimariesChromaticityCoordinates( - new CieXyChromaticityCoordinates(0.6300F, 0.3400F), - new CieXyChromaticityCoordinates(0.3100F, 0.5950F), - new CieXyChromaticityCoordinates(0.1550F, 0.0700F))); + public static readonly IRgbWorkingSpace SMPTECRgb = new RgbWorkingSpace(Illuminants.D65, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6300F, 0.3400F), new CieXyChromaticityCoordinates(0.3100F, 0.5950F), new CieXyChromaticityCoordinates(0.1550F, 0.0700F))); /// /// Wide Gamut Rgb working space /// - public static readonly IRgbWorkingSpace WideGamutRgb = - new RgbGammaWorkingSpace( - Illuminants.D50, - 2.2F, - new RgbPrimariesChromaticityCoordinates( - new CieXyChromaticityCoordinates(0.7350F, 0.2650F), - new CieXyChromaticityCoordinates(0.1150F, 0.8260F), - new CieXyChromaticityCoordinates(0.1570F, 0.0180F))); + public static readonly IRgbWorkingSpace WideGamutRgb = new RgbWorkingSpace(Illuminants.D50, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7350F, 0.2650F), new CieXyChromaticityCoordinates(0.1150F, 0.8260F), new CieXyChromaticityCoordinates(0.1570F, 0.0180F))); } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs b/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs index 1bd80073e5..24363173ae 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs @@ -78,7 +78,7 @@ namespace SixLabors.ImageSharp.Tests public bool Equals(IRgbWorkingSpace x, IRgbWorkingSpace y) { - if (x is RgbGammaWorkingSpace g1 && y is RgbGammaWorkingSpace g2) + if (x is IRgbWorkingSpace g1 && y is IRgbWorkingSpace g2) { return this.Equals(g1.WhitePoint, g2.WhitePoint) && this.Equals(g1.ChromaticityCoordinates, g2.ChromaticityCoordinates); From 63876e1e1f2236b726b3e918e7922dbe2a37e106 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 16 Feb 2018 23:01:53 +0100 Subject: [PATCH 044/151] fix benchmark.sh --- tests/ImageSharp.Benchmarks/benchmark.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ImageSharp.Benchmarks/benchmark.sh b/tests/ImageSharp.Benchmarks/benchmark.sh index 1966475bca..f51a9833aa 100755 --- a/tests/ImageSharp.Benchmarks/benchmark.sh +++ b/tests/ImageSharp.Benchmarks/benchmark.sh @@ -1,7 +1,7 @@ #!/bin/bash # Build in release mode -dotnet build -c Release -f netcoreapp1.1 +dotnet build -c Release -f netcoreapp2.0 # Run benchmarks -dotnet bin/Release/netcoreapp1.1/ImageSharp.Benchmarks.dll \ No newline at end of file +dotnet bin/Release/netcoreapp2.0/ImageSharp.Benchmarks.dll \ No newline at end of file From 0f6134970e4fde6bd3ccdb8f0ba721b3f68d0456 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 16 Feb 2018 23:17:01 +0100 Subject: [PATCH 045/151] removing samples --- ImageSharp.sln | 32 ----- .../AvatarWithRoundedCorner.csproj | 12 -- samples/AvatarWithRoundedCorner/Program.cs | 111 ------------------ samples/AvatarWithRoundedCorner/fb.jpg | 3 - .../ChangeDefaultEncoderOptions.csproj | 12 -- .../ChangeDefaultEncoderOptions/Program.cs | 23 ---- 6 files changed, 193 deletions(-) delete mode 100644 samples/AvatarWithRoundedCorner/AvatarWithRoundedCorner.csproj delete mode 100644 samples/AvatarWithRoundedCorner/Program.cs delete mode 100644 samples/AvatarWithRoundedCorner/fb.jpg delete mode 100644 samples/ChangeDefaultEncoderOptions/ChangeDefaultEncoderOptions.csproj delete mode 100644 samples/ChangeDefaultEncoderOptions/Program.cs diff --git a/ImageSharp.sln b/ImageSharp.sln index 4ea89dd450..3ff5b09d41 100644 --- a/ImageSharp.sln +++ b/ImageSharp.sln @@ -43,12 +43,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Tests", "tests\I EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Benchmarks", "tests\ImageSharp.Benchmarks\ImageSharp.Benchmarks.csproj", "{2BF743D8-2A06-412D-96D7-F448F00C5EA5}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{7CC6D57E-B916-43B8-B315-A0BB92F260A2}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AvatarWithRoundedCorner", "samples\AvatarWithRoundedCorner\AvatarWithRoundedCorner.csproj", "{844FC582-4E78-4371-847D-EFD4D1103578}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ChangeDefaultEncoderOptions", "samples\ChangeDefaultEncoderOptions\ChangeDefaultEncoderOptions.csproj", "{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Sandbox46", "tests\ImageSharp.Sandbox46\ImageSharp.Sandbox46.csproj", "{561B880A-D9EE-44EF-90F5-817C54A9D9AB}" EndProject Global @@ -112,30 +106,6 @@ Global {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release|x64.Build.0 = Release|Any CPU {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release|x86.ActiveCfg = Release|Any CPU {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release|x86.Build.0 = Release|Any CPU - {844FC582-4E78-4371-847D-EFD4D1103578}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {844FC582-4E78-4371-847D-EFD4D1103578}.Debug|Any CPU.Build.0 = Debug|Any CPU - {844FC582-4E78-4371-847D-EFD4D1103578}.Debug|x64.ActiveCfg = Debug|Any CPU - {844FC582-4E78-4371-847D-EFD4D1103578}.Debug|x64.Build.0 = Debug|Any CPU - {844FC582-4E78-4371-847D-EFD4D1103578}.Debug|x86.ActiveCfg = Debug|Any CPU - {844FC582-4E78-4371-847D-EFD4D1103578}.Debug|x86.Build.0 = Debug|Any CPU - {844FC582-4E78-4371-847D-EFD4D1103578}.Release|Any CPU.ActiveCfg = Release|Any CPU - {844FC582-4E78-4371-847D-EFD4D1103578}.Release|Any CPU.Build.0 = Release|Any CPU - {844FC582-4E78-4371-847D-EFD4D1103578}.Release|x64.ActiveCfg = Release|Any CPU - {844FC582-4E78-4371-847D-EFD4D1103578}.Release|x64.Build.0 = Release|Any CPU - {844FC582-4E78-4371-847D-EFD4D1103578}.Release|x86.ActiveCfg = Release|Any CPU - {844FC582-4E78-4371-847D-EFD4D1103578}.Release|x86.Build.0 = Release|Any CPU - {07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Debug|x64.ActiveCfg = Debug|Any CPU - {07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Debug|x64.Build.0 = Debug|Any CPU - {07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Debug|x86.ActiveCfg = Debug|Any CPU - {07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Debug|x86.Build.0 = Debug|Any CPU - {07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Release|Any CPU.Build.0 = Release|Any CPU - {07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Release|x64.ActiveCfg = Release|Any CPU - {07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Release|x64.Build.0 = Release|Any CPU - {07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Release|x86.ActiveCfg = Release|Any CPU - {07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Release|x86.Build.0 = Release|Any CPU {561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Debug|Any CPU.Build.0 = Debug|Any CPU {561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -158,8 +128,6 @@ Global {2E33181E-6E28-4662-A801-E2E7DC206029} = {815C0625-CD3D-440F-9F80-2D83856AB7AE} {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} {2BF743D8-2A06-412D-96D7-F448F00C5EA5} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} - {844FC582-4E78-4371-847D-EFD4D1103578} = {7CC6D57E-B916-43B8-B315-A0BB92F260A2} - {07EE511D-4BAB-4323-BAFC-3AF2BF9366F0} = {7CC6D57E-B916-43B8-B315-A0BB92F260A2} {561B880A-D9EE-44EF-90F5-817C54A9D9AB} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution diff --git a/samples/AvatarWithRoundedCorner/AvatarWithRoundedCorner.csproj b/samples/AvatarWithRoundedCorner/AvatarWithRoundedCorner.csproj deleted file mode 100644 index e000aacf10..0000000000 --- a/samples/AvatarWithRoundedCorner/AvatarWithRoundedCorner.csproj +++ /dev/null @@ -1,12 +0,0 @@ - - - - Exe - netcoreapp1.1 - - - - - - - \ No newline at end of file diff --git a/samples/AvatarWithRoundedCorner/Program.cs b/samples/AvatarWithRoundedCorner/Program.cs deleted file mode 100644 index 087bbc29d5..0000000000 --- a/samples/AvatarWithRoundedCorner/Program.cs +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Numerics; -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.Primitives; -using SixLabors.Shapes; - -namespace AvatarWithRoundedCorner -{ - static class Program - { - static void Main(string[] args) - { - System.IO.Directory.CreateDirectory("output"); - using (var img = Image.Load("fb.jpg")) - { - // as generate returns a new IImage make sure we dispose of it - using (Image destRound = img.Clone(x => x.ConvertToAvatar(new Size(200, 200), 20))) - { - destRound.Save("output/fb.png"); - } - - using (Image destRound = img.Clone(x => x.ConvertToAvatar(new Size(200, 200), 100))) - { - destRound.Save("output/fb-round.png"); - } - - using (Image destRound = img.Clone(x => x.ConvertToAvatar(new Size(200, 200), 150))) - { - destRound.Save("output/fb-rounder.png"); - } - - using (Image destRound = img.CloneAndConvertToAvatarWithoutApply(new Size(200, 200), 150)) - { - destRound.Save("output/fb-rounder-without-apply.png"); - } - - // the original `img` object has not been altered at all. - } - } - - // 1. The short way: - // Implements a full image mutating pipeline operating on IImageProcessingContext - // We need the dimensions of the resized image to deduce 'IPathCollection' needed to build the corners, - // so we implement an "inline" image processor by utilizing 'ImageExtensions.Apply()' - private static IImageProcessingContext ConvertToAvatar(this IImageProcessingContext processingContext, Size size, float cornerRadius) - { - return processingContext.Resize(new ResizeOptions - { - Size = size, - Mode = ResizeMode.Crop - }).Apply(i => ApplyRoundedCorners(i, cornerRadius)); - } - - // 2. A more verbose way, avoiding 'Apply()': - // First we create a resized clone of the image, then we draw the corners on that instance with Mutate(). - private static Image CloneAndConvertToAvatarWithoutApply(this Image image, Size size, float cornerRadius) - { - Image result = image.Clone( - ctx => ctx.Resize( - new ResizeOptions - { - Size = size, - Mode = ResizeMode.Crop - })); - - ApplyRoundedCorners(result, cornerRadius); - return result; - } - - // This method can be seen as an inline implementation of an `IImageProcessor`: - // (The combination of `IImageOperations.Apply()` + this could be replaced with an `IImageProcessor`) - public static void ApplyRoundedCorners(Image img, float cornerRadius) - { - IPathCollection corners = BuildCorners(img.Width, img.Height, cornerRadius); - - // mutating in here as we already have a cloned original - img.Mutate(x => x.Fill(Rgba32.Transparent, corners, new GraphicsOptions(true) - { - BlenderMode = PixelBlenderMode.Src // enforces that any part of this shape that has color is punched out of the background - })); - } - - public static IPathCollection BuildCorners(int imageWidth, int imageHeight, float cornerRadius) - { - // first create a square - var rect = new RectangularePolygon(-0.5f, -0.5f, cornerRadius, cornerRadius); - - // then cut out of the square a circle so we are left with a corner - IPath cornerToptLeft = rect.Clip(new EllipsePolygon(cornerRadius - 0.5f, cornerRadius - 0.5f, cornerRadius)); - - // corner is now a corner shape positions top left - //lets make 3 more positioned correctly, we can do that by translating the orgional artound the center of the image - var center = new Vector2(imageWidth / 2F, imageHeight / 2F); - - float rightPos = imageWidth - cornerToptLeft.Bounds.Width + 1; - float bottomPos = imageHeight - cornerToptLeft.Bounds.Height + 1; - - // move it across the widthof the image - the width of the shape - IPath cornerTopRight = cornerToptLeft.RotateDegree(90).Translate(rightPos, 0); - IPath cornerBottomLeft = cornerToptLeft.RotateDegree(-90).Translate(0, bottomPos); - IPath cornerBottomRight = cornerToptLeft.RotateDegree(180).Translate(rightPos, bottomPos); - - return new PathCollection(cornerToptLeft, cornerBottomLeft, cornerTopRight, cornerBottomRight); - } - } -} \ No newline at end of file diff --git a/samples/AvatarWithRoundedCorner/fb.jpg b/samples/AvatarWithRoundedCorner/fb.jpg deleted file mode 100644 index 7241890e2e..0000000000 --- a/samples/AvatarWithRoundedCorner/fb.jpg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:93bb4d6281dc1e845db57e836e0dca30b7a4062e81044efb27ad4d8b1a33130c -size 15787 diff --git a/samples/ChangeDefaultEncoderOptions/ChangeDefaultEncoderOptions.csproj b/samples/ChangeDefaultEncoderOptions/ChangeDefaultEncoderOptions.csproj deleted file mode 100644 index 5797be0f56..0000000000 --- a/samples/ChangeDefaultEncoderOptions/ChangeDefaultEncoderOptions.csproj +++ /dev/null @@ -1,12 +0,0 @@ - - - - Exe - netcoreapp1.1 - - - - - - - \ No newline at end of file diff --git a/samples/ChangeDefaultEncoderOptions/Program.cs b/samples/ChangeDefaultEncoderOptions/Program.cs deleted file mode 100644 index a8fbd75993..0000000000 --- a/samples/ChangeDefaultEncoderOptions/Program.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.Formats.Jpeg; - -namespace ChangeDefaultEncoderOptions -{ - class Program - { - static void Main(string[] args) - { - // lets switch out the default encoder for jpeg to one - // that saves at 90 quality and ignores the matadata - Configuration.Default.SetEncoder(ImageFormats.Jpeg, new JpegEncoder() - { - Quality = 90, - IgnoreMetadata = true - }); - } - } -} \ No newline at end of file From a9a577c953937cef945398f1c6203bd7a36390c5 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 17 Feb 2018 02:23:01 +0100 Subject: [PATCH 046/151] NamedColors.WebSafePalette is now a property backed by thread safe Lazy --- src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs b/src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs index ccd532bc3c..6a2902f9b1 100644 --- a/src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs @@ -12,6 +12,11 @@ namespace SixLabors.ImageSharp.PixelFormats public static class NamedColors where TPixel : struct, IPixel { + /// + /// Thread-safe backing field for . + /// + private static readonly Lazy WebSafePaletteLazy = new Lazy(GetWebSafePalette, true); + /// /// Represents a matching the W3C definition that has an hex value of #F0F8FF. /// @@ -723,9 +728,9 @@ namespace SixLabors.ImageSharp.PixelFormats public static readonly TPixel YellowGreen = ColorBuilder.FromRGBA(154, 205, 50, 255); /// - /// Represents a matching the W3C definition of web safe colors. + /// Gets a matching the W3C definition of web safe colors. /// - public static readonly TPixel[] WebSafePalette = GetWebSafePalette(); + public static TPixel[] WebSafePalette => WebSafePaletteLazy.Value; private static TPixel[] GetWebSafePalette() { From c08b155d2cc6916e11f9ddeeca60f7a39fe9e328 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 17 Feb 2018 19:32:17 +0100 Subject: [PATCH 047/151] fix build after merge --- src/ImageSharp/Advanced/AdvancedImageExtensions.cs | 8 ++++++++ src/ImageSharp/Formats/Gif/GifDecoderCore.cs | 10 +++++++--- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 13 ++++++++----- src/ImageSharp/Image/ImageFrame{TPixel}.cs | 6 ++++-- .../Transforms/AffineTransformProcessor.cs | 8 +++++--- .../Transforms/ProjectiveTransformProcessor.cs | 13 +++++++++---- .../Processors/Transforms/WeightsBuffer.cs | 5 +++-- .../Processors/Transforms/WeightsWindow.cs | 2 +- .../ImageSharp.Tests/Formats/GeneralFormatTests.cs | 9 +++++---- .../Transforms/ResizeProfilingBenchmarks.cs | 3 ++- .../Quantization/QuantizedImageTests.cs | 2 +- 11 files changed, 53 insertions(+), 26 deletions(-) diff --git a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs index 0acb846c50..612ced5d8d 100644 --- a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs +++ b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs @@ -88,6 +88,14 @@ namespace SixLabors.ImageSharp.Advanced where TPixel : struct, IPixel => source.Frames.RootFrame.GetPixelRowSpan(row); + /// + /// Gets the assigned to 'source'. + /// + /// The source image + /// Returns the configuration. + internal static MemoryManager GetMemoryManager(this IConfigurable source) + => GetConfiguration(source).MemoryManager; + /// /// Gets the span to the backing buffer. /// diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index 0c1d8b21ac..c120c9e113 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -92,6 +92,8 @@ namespace SixLabors.ImageSharp.Formats.Gif /// public FrameDecodingMode DecodingMode { get; } + private MemoryManager MemoryManager => this.configuration.MemoryManager; + /// /// Decodes the stream to the image. /// @@ -148,7 +150,8 @@ namespace SixLabors.ImageSharp.Formats.Gif { break; } - this.globalColorTable = this.configuration.MemoryManager.Allocate(this.globalColorTableLength, true); + + this.globalColorTable = this.MemoryManager.Allocate(this.globalColorTableLength, true); nextFlag = stream.ReadByte(); if (nextFlag == -1) @@ -334,7 +337,7 @@ namespace SixLabors.ImageSharp.Formats.Gif continue; } - using (Buffer commentsBuffer = this.configuration.MemoryManager.Allocate(length)) + using (Buffer commentsBuffer = this.MemoryManager.Allocate(length)) { this.currentStream.Read(commentsBuffer.Array, 0, length); string comments = this.TextEncoding.GetString(commentsBuffer.Array, 0, length); @@ -601,7 +604,8 @@ namespace SixLabors.ImageSharp.Formats.Gif if (this.logicalScreenDescriptor.GlobalColorTableFlag) { this.globalColorTableLength = this.logicalScreenDescriptor.GlobalColorTableSize * 3; - this.globalColorTable = Buffer.CreateClean(this.globalColorTableLength); + + this.globalColorTable = this.MemoryManager.Allocate(this.globalColorTableLength, true); // Read the global color table from the stream stream.Read(this.globalColorTable.Array, 0, this.globalColorTableLength); diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 6a04c77b96..0546c5ee3d 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -190,6 +190,8 @@ namespace SixLabors.ImageSharp.Formats.Png this.textEncoding = options.TextEncoding ?? PngConstants.DefaultEncoding; this.ignoreMetadata = options.IgnoreMetadata; } + + private MemoryManager MemoryManager => this.configuration.MemoryManager; /// /// Decodes the stream to the image. @@ -297,17 +299,17 @@ namespace SixLabors.ImageSharp.Formats.Png switch (currentChunk.Type) { case PngChunkTypes.Header: - this.ReadHeaderChunk(currentChunk.Data); + this.ReadHeaderChunk(currentChunk.Data.Array); this.ValidateHeader(); break; case PngChunkTypes.Physical: - this.ReadPhysicalChunk(metadata, currentChunk.Data); + this.ReadPhysicalChunk(metadata, currentChunk.Data.Array); break; case PngChunkTypes.Data: this.SkipChunkDataAndCrc(currentChunk); break; case PngChunkTypes.Text: - this.ReadTextChunk(metadata, currentChunk.Data, currentChunk.Length); + this.ReadTextChunk(metadata, currentChunk.Data.Array, currentChunk.Length); break; case PngChunkTypes.End: this.isEndChunkReached = true; @@ -319,7 +321,7 @@ namespace SixLabors.ImageSharp.Formats.Png // Data is rented in ReadChunkData() if (currentChunk.Data != null) { - ArrayPool.Shared.Return(currentChunk.Data); + ArrayPool.Shared.Return(currentChunk.Data.Array); } } } @@ -435,10 +437,11 @@ namespace SixLabors.ImageSharp.Formats.Png this.bytesPerSample = this.header.BitDepth / 8; } - this.previousScanline = this.configuration.MemoryManager.Allocate(this.bytesPerScanline, true); + this.previousScanline = this.MemoryManager.Allocate(this.bytesPerScanline, true); this.scanline = this.configuration.MemoryManager.Allocate(this.bytesPerScanline, true); } + /// /// Calculates the correct number of bits per pixel for the given color type. /// diff --git a/src/ImageSharp/Image/ImageFrame{TPixel}.cs b/src/ImageSharp/Image/ImageFrame{TPixel}.cs index e96275a9fb..eb34c26737 100644 --- a/src/ImageSharp/Image/ImageFrame{TPixel}.cs +++ b/src/ImageSharp/Image/ImageFrame{TPixel}.cs @@ -61,16 +61,18 @@ namespace SixLabors.ImageSharp /// /// Initializes a new instance of the class. /// + /// The to use for buffer allocations. /// The of the frame. /// The meta data. - internal ImageFrame(Size size, ImageFrameMetaData metaData) - : this(size.Width, size.Height, metaData) + internal ImageFrame(MemoryManager memoryManager, Size size, ImageFrameMetaData metaData) + : this(memoryManager, size.Width, size.Height, metaData) { } /// /// Initializes a new instance of the class. /// + /// The to use for buffer allocations. /// The source. internal ImageFrame(MemoryManager memoryManager, ImageFrame source) { diff --git a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs index 8595e86922..9a5b000deb 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs @@ -73,7 +73,7 @@ namespace SixLabors.ImageSharp.Processing.Processors // We will always be creating the clone even for mutate because we may need to resize the canvas IEnumerable> frames = - source.Frames.Select(x => new ImageFrame(this.targetDimensions, x.MetaData.Clone())); + source.Frames.Select(x => new ImageFrame(source.GetMemoryManager(), this.targetDimensions, x.MetaData.Clone())); // Use the overload to prevent an extra frame being added return new Image(source.GetConfiguration(), source.MetaData.Clone(), frames); @@ -130,8 +130,10 @@ namespace SixLabors.ImageSharp.Processing.Processors int xLength = (int)MathF.Ceiling((radius.X * 2) + 2); int yLength = (int)MathF.Ceiling((radius.Y * 2) + 2); - using (var yBuffer = new Buffer2D(yLength, height)) - using (var xBuffer = new Buffer2D(xLength, height)) + MemoryManager memoryManager = configuration.MemoryManager; + + using (Buffer2D yBuffer = memoryManager.Allocate2D(yLength, height)) + using (Buffer2D xBuffer = memoryManager.Allocate2D(xLength, height)) { Parallel.For( 0, diff --git a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs index 7e547727e6..3c04c2722e 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs @@ -73,8 +73,11 @@ namespace SixLabors.ImageSharp.Processing.Processors } // We will always be creating the clone even for mutate because we may need to resize the canvas - IEnumerable> frames = - source.Frames.Select(x => new ImageFrame(this.targetRectangle.Width, this.targetRectangle.Height, x.MetaData.Clone())); + IEnumerable> frames = source.Frames.Select( + x => new ImageFrame( + source.GetMemoryManager(), + this.targetRectangle.Size, + x.MetaData.Clone())); // Use the overload to prevent an extra frame being added return new Image(source.GetConfiguration(), source.MetaData.Clone(), frames); @@ -125,8 +128,10 @@ namespace SixLabors.ImageSharp.Processing.Processors int xLength = (int)MathF.Ceiling((radius.X * 2) + 2); int yLength = (int)MathF.Ceiling((radius.Y * 2) + 2); - using (var yBuffer = new Buffer2D(yLength, height)) - using (var xBuffer = new Buffer2D(xLength, height)) + MemoryManager memoryManager = configuration.MemoryManager; + + using (Buffer2D yBuffer = memoryManager.Allocate2D(yLength, height)) + using (Buffer2D xBuffer = memoryManager.Allocate2D(xLength, height)) { Parallel.For( 0, diff --git a/src/ImageSharp/Processing/Processors/Transforms/WeightsBuffer.cs b/src/ImageSharp/Processing/Processors/Transforms/WeightsBuffer.cs index 0e91087f90..d633a3869f 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/WeightsBuffer.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/WeightsBuffer.cs @@ -17,11 +17,12 @@ namespace SixLabors.ImageSharp.Processing.Processors /// /// Initializes a new instance of the class. /// + /// The MemoryManager to use for allocations. /// The size of the source window /// The size of the destination window - public WeightsBuffer(int sourceSize, int destinationSize) + public WeightsBuffer(MemoryManager memoryManager, int sourceSize, int destinationSize) { - this.dataBuffer = Buffer2D.CreateClean(sourceSize, destinationSize); + this.dataBuffer = memoryManager.Allocate2D(sourceSize, destinationSize, true); this.Weights = new WeightsWindow[destinationSize]; } diff --git a/src/ImageSharp/Processing/Processors/Transforms/WeightsWindow.cs b/src/ImageSharp/Processing/Processors/Transforms/WeightsWindow.cs index b0a530514e..1ee61a9a60 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/WeightsWindow.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/WeightsWindow.cs @@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.Processing.Processors { this.flatStartIndex = (index * buffer.Width) + left; this.Left = left; - this.buffer = buffer; + this.buffer = buffer.Buffer; this.Length = length; } diff --git a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs index 97128e2c93..a6a883d421 100644 --- a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs +++ b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs @@ -14,7 +14,8 @@ namespace SixLabors.ImageSharp.Tests { using System; - + using SixLabors.ImageSharp.Advanced; + using SixLabors.ImageSharp.Memory; public class GeneralFormatTests : FileTestBase { @@ -178,7 +179,7 @@ namespace SixLabors.ImageSharp.Tests { using (var memoryStream = new MemoryStream()) { - image.Save(memoryStream, GetEncoder(format)); + image.Save(memoryStream, GetEncoder(image.GetMemoryManager(), format)); memoryStream.Position = 0; var imageInfo = Image.Identify(memoryStream); @@ -189,12 +190,12 @@ namespace SixLabors.ImageSharp.Tests } } - private static IImageEncoder GetEncoder(string format) + private static IImageEncoder GetEncoder(MemoryManager memoryManager, string format) { switch (format) { case "png": - return new PngEncoder(); + return new PngEncoder(memoryManager); case "gif": return new GifEncoder(); case "bmp": diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeProfilingBenchmarks.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeProfilingBenchmarks.cs index c87dcb6485..54a3b7418f 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeProfilingBenchmarks.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeProfilingBenchmarks.cs @@ -11,6 +11,8 @@ using Xunit.Abstractions; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms { + using SixLabors.ImageSharp.Memory; + public class ResizeProfilingBenchmarks : MeasureFixture { public ResizeProfilingBenchmarks(ITestOutputHelper output) @@ -38,7 +40,6 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms // [Fact] public void PrintWeightsData() { - var proc = new ResizeProcessor(KnownResamplers.Bicubic, 200, 200); var proc = new ResizeProcessor(Configuration.Default.MemoryManager, new BicubicResampler(), 200, 200); WeightsBuffer weights = proc.PrecomputeWeights(200, 500); diff --git a/tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs b/tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs index a9e5b440d2..5d569aa8fb 100644 --- a/tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs +++ b/tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs @@ -12,7 +12,7 @@ { var palette = new PaletteQuantizer(); var octree = new OctreeQuantizer(); - var wu = new WuQuantizer(); + var wu = new WuQuantizer(Configuration.Default.MemoryManager); Assert.True(palette.Dither); Assert.True(octree.Dither); From efc5d6f3067e03f09fd287e69d356efbbc6472f6 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 17 Feb 2018 19:56:40 +0100 Subject: [PATCH 048/151] moving common MemoryManager logic into extension methods --- src/ImageSharp/Image/ImageFrame{TPixel}.cs | 2 +- .../Memory/ArrayPoolMemoryManager.cs | 8 +--- src/ImageSharp/Memory/MemoryManager.cs | 19 -------- .../Memory/MemoryManagerExtensions.cs | 45 +++++++++++++++++++ ...nager.cs => SimpleManagedMemoryManager.cs} | 8 +--- .../Processors/Transforms/ResizeProcessor.cs | 2 +- 6 files changed, 49 insertions(+), 35 deletions(-) create mode 100644 src/ImageSharp/Memory/MemoryManagerExtensions.cs rename src/ImageSharp/Memory/{NullMemoryManager.cs => SimpleManagedMemoryManager.cs} (68%) diff --git a/src/ImageSharp/Image/ImageFrame{TPixel}.cs b/src/ImageSharp/Image/ImageFrame{TPixel}.cs index eb34c26737..5170522de3 100644 --- a/src/ImageSharp/Image/ImageFrame{TPixel}.cs +++ b/src/ImageSharp/Image/ImageFrame{TPixel}.cs @@ -54,7 +54,7 @@ namespace SixLabors.ImageSharp Guard.NotNull(metaData, nameof(metaData)); this.MemoryManager = memoryManager; - this.pixelBuffer = memoryManager.Allocate2D(width, height, true); + this.pixelBuffer = memoryManager.AllocateClean2D(width, height); this.MetaData = metaData; } diff --git a/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs b/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs index b062df7118..86776fd358 100644 --- a/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs +++ b/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs @@ -28,8 +28,8 @@ namespace SixLabors.ImageSharp.Memory /// /// Initializes a new instance of the class. - /// The maximum size of pooled arrays. Arrays over the thershold are gonna be always allocated. /// + /// The maximum size of pooled arrays. Arrays over the thershold are gonna be always allocated. public ArrayPoolMemoryManager(int maxPoolSizeInBytes) { Guard.MustBeGreaterThan(maxPoolSizeInBytes, 0, nameof(maxPoolSizeInBytes)); @@ -37,12 +37,6 @@ namespace SixLabors.ImageSharp.Memory this.pool = ArrayPool.Create(maxPoolSizeInBytes, 50); } - /// - internal override Buffer Allocate(int itemCount) - { - return this.Allocate(itemCount, false); - } - /// internal override Buffer Allocate(int itemCount, bool clear) { diff --git a/src/ImageSharp/Memory/MemoryManager.cs b/src/ImageSharp/Memory/MemoryManager.cs index df1ecbd845..6be7012e6a 100644 --- a/src/ImageSharp/Memory/MemoryManager.cs +++ b/src/ImageSharp/Memory/MemoryManager.cs @@ -11,17 +11,6 @@ namespace SixLabors.ImageSharp.Memory /// public abstract class MemoryManager { - /// - /// Allocates a of size . - /// Note: Depending on the implementation, the buffer may not cleared before - /// returning, so it may contain data from an earlier use. - /// - /// Type of the data stored in the buffer - /// Size of the buffer to allocate - /// A buffer of values of type . - internal abstract Buffer Allocate(int size) - where T : struct; - /// /// Allocates a of size , optionally /// clearing the buffer before it gets returned. @@ -41,13 +30,5 @@ namespace SixLabors.ImageSharp.Memory /// The buffer to release internal abstract void Release(Buffer buffer) where T : struct; - - internal Buffer2D Allocate2D(int width, int height, bool clear = false) - where T : struct - { - Buffer buffer = this.Allocate(width * height, clear); - - return new Buffer2D(buffer, width, height); - } } } diff --git a/src/ImageSharp/Memory/MemoryManagerExtensions.cs b/src/ImageSharp/Memory/MemoryManagerExtensions.cs new file mode 100644 index 0000000000..8e1aa9850f --- /dev/null +++ b/src/ImageSharp/Memory/MemoryManagerExtensions.cs @@ -0,0 +1,45 @@ +namespace SixLabors.ImageSharp.Memory +{ + /// + /// Extension methods for . + /// + internal static class MemoryManagerExtensions + { + /// + /// Allocates a of size . + /// Note: Depending on the implementation, the buffer may not cleared before + /// returning, so it may contain data from an earlier use. + /// + /// Type of the data stored in the buffer + /// The + /// Size of the buffer to allocate + /// A buffer of values of type . + public static Buffer Allocate(this MemoryManager memoryManager, int size) + where T : struct + { + return memoryManager.Allocate(size, false); + } + + public static Buffer AllocateClean(this MemoryManager memoryManager, int size) + where T : struct + { + return memoryManager.Allocate(size, true); + } + + public static Buffer2D Allocate2D(this MemoryManager memoryManager, int width, int height, bool clear) + where T : struct + { + Buffer buffer = memoryManager.Allocate(width * height, clear); + + return new Buffer2D(buffer, width, height); + } + + public static Buffer2D Allocate2D(this MemoryManager memoryManager, int width, int height) + where T : struct => + Allocate2D(memoryManager, width, height, false); + + public static Buffer2D AllocateClean2D(this MemoryManager memoryManager, int width, int height) + where T : struct => + Allocate2D(memoryManager, width, height, true); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Memory/NullMemoryManager.cs b/src/ImageSharp/Memory/SimpleManagedMemoryManager.cs similarity index 68% rename from src/ImageSharp/Memory/NullMemoryManager.cs rename to src/ImageSharp/Memory/SimpleManagedMemoryManager.cs index d82f013535..2aefe898fd 100644 --- a/src/ImageSharp/Memory/NullMemoryManager.cs +++ b/src/ImageSharp/Memory/SimpleManagedMemoryManager.cs @@ -3,14 +3,8 @@ /// /// Implements by allocating new buffers on every call. /// - public class NullMemoryManager : MemoryManager + public class SimpleManagedMemoryManager : MemoryManager { - /// - internal override Buffer Allocate(int size) - { - return new Buffer(new T[size], size); - } - /// internal override Buffer Allocate(int size, bool clear) { diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs index e8463a2667..1fa388da48 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs @@ -124,7 +124,7 @@ namespace SixLabors.ImageSharp.Processing.Processors // First process the columns. Since we are not using multiple threads startY and endY // are the upper and lower bounds of the source rectangle. // TODO: Using a transposed variant of 'firstPassPixels' could eliminate the need for the WeightsWindow.ComputeWeightedColumnSum() method, and improve speed! - using (var firstPassPixels = this.MemoryManager.Allocate2D(width, source.Height)) + using (Buffer2D firstPassPixels = this.MemoryManager.Allocate2D(width, source.Height)) { firstPassPixels.Buffer.Clear(); From 4b2777793f97924165593843a1a2eb77ef99aa5f Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 18 Feb 2018 18:04:12 +1100 Subject: [PATCH 049/151] Update .vscode files --- .vscode/launch.json | 2 +- .vscode/tasks.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index c9c7453f64..c772e647ce 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -10,7 +10,7 @@ "request": "launch", "preLaunchTask": "build", // If you have changed target frameworks, make sure to update the program path. - "program": "${workspaceRoot}/samples/AvatarWithRoundedCorner/bin/Debug/netcoreapp1.1/AvatarWithRoundedCorner.dll", + "program": "${workspaceRoot}/tests/ImageSharp.Benchmarks/bin/Debug/netcoreapp2.0/ImageSharp.Benchmarks.dll", "args": [], "cwd": "${workspaceRoot}/samples/AvatarWithRoundedCorner", // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 4a7b35ac2c..82aaa2f8d0 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -16,13 +16,13 @@ { "taskName": "build benchmark", "suppressTaskName": true, - "args": [ "build", "tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj", "-f", "netcoreapp1.1", "-c", "Release" ], + "args": [ "build", "tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj", "-f", "netcoreapp2.0", "-c", "Release" ], "showOutput": "always", "problemMatcher": "$msCompile" }, { "taskName": "test", - "args": ["tests/ImageSharp.Tests/ImageSharp.Tests.csproj", "-c", "release", "-f", "netcoreapp1.1"], + "args": ["tests/ImageSharp.Tests/ImageSharp.Tests.csproj", "-c", "release", "-f", "netcoreapp2.0"], "isTestCommand": true, "showOutput": "always", "problemMatcher": "$msCompile" From 631d173b72107fbc0fb06558eb8e3b8f3232259f Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 18 Feb 2018 19:31:13 +1100 Subject: [PATCH 050/151] use DistanceSquared --- .../Processors/Dithering/PaletteDitherProcessorBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessorBase.cs b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessorBase.cs index b3c564edbd..4e6b7bec0e 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessorBase.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessorBase.cs @@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.Processing.Processors for (int index = 0; index < colorPalette.Length; index++) { TPixel temp = colorPalette[index]; - float distance = Vector4.Distance(vector, temp.ToVector4()); + float distance = Vector4.DistanceSquared(vector, temp.ToVector4()); if (distance < leastDistance) { From dab4d0cae421d1b6ad603eef307dbfac013db38a Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 18 Feb 2018 16:57:09 +0100 Subject: [PATCH 051/151] IImage : IDisposable --- src/ImageSharp/Image/IImage.cs | 4 +++- src/ImageSharp/Image/Image{TPixel}.cs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/Image/IImage.cs b/src/ImageSharp/Image/IImage.cs index 7355dc1fec..b9e2cee616 100644 --- a/src/ImageSharp/Image/IImage.cs +++ b/src/ImageSharp/Image/IImage.cs @@ -1,12 +1,14 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; + namespace SixLabors.ImageSharp { /// /// Encapsulates the properties and methods that describe an image. /// - public interface IImage : IImageInfo + public interface IImage : IImageInfo, IDisposable { } } \ No newline at end of file diff --git a/src/ImageSharp/Image/Image{TPixel}.cs b/src/ImageSharp/Image/Image{TPixel}.cs index be38b41f24..f264d8a59d 100644 --- a/src/ImageSharp/Image/Image{TPixel}.cs +++ b/src/ImageSharp/Image/Image{TPixel}.cs @@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp /// Encapsulates an image, which consists of the pixel data for a graphics image and its attributes. /// /// The pixel format. - public sealed partial class Image : IImage, IDisposable, IConfigurable + public sealed partial class Image : IImage, IConfigurable where TPixel : struct, IPixel { private Configuration configuration; From 5ae591a53a8784f7cb8e4536b1fb429ed3c2e065 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 18 Feb 2018 19:31:21 +0100 Subject: [PATCH 052/151] better BmpEncoderTests --- .../Formats/Bmp/BmpEncoderTests.cs | 61 ++++++++++++++----- .../ImageProviders/TestPatternProvider.cs | 3 +- 2 files changed, 48 insertions(+), 16 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs index d96d3def5e..ff7e34733a 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs @@ -8,28 +8,59 @@ using Xunit; namespace SixLabors.ImageSharp.Tests { + using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + + using Xunit.Abstractions; + public class BmpEncoderTests : FileTestBase { - public static readonly TheoryData BitsPerPixel - = new TheoryData + private const PixelTypes PixelTypesToTest = PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.Rgb24; + + public static readonly TheoryData BitsPerPixel = + new TheoryData + { + BmpBitsPerPixel.Pixel24, + BmpBitsPerPixel.Pixel32 + }; + + public BmpEncoderTests(ITestOutputHelper output) { - BmpBitsPerPixel.Pixel24, - BmpBitsPerPixel.Pixel32 - }; + this.Output = output; + } + + private ITestOutputHelper Output { get; } [Theory] - [MemberData(nameof(BitsPerPixel))] - public void BitmapCanEncodeDifferentBitRates(BmpBitsPerPixel bitsPerPixel) + [WithTestPatternImages(nameof(BitsPerPixel), 48, 24, PixelTypesToTest)] + [WithTestPatternImages(nameof(BitsPerPixel), 47, 8, PixelTypesToTest)] + [WithTestPatternImages(nameof(BitsPerPixel), 49, 7, PixelTypesToTest)] + [WithSolidFilledImages(nameof(BitsPerPixel), 1, 1, 255, 100, 50, 255, PixelTypesToTest)] + [WithTestPatternImages(nameof(BitsPerPixel), 7, 5, PixelTypesToTest)] + public void Encode(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) + where TPixel : struct, IPixel { - string path = TestEnvironment.CreateOutputDirectory("Bmp"); - - foreach (TestFile file in Files) + + using (Image image = provider.GetImage()) { - string filename = file.GetFileNameWithoutExtension(bitsPerPixel); - using (Image image = file.CreateImage()) - { - image.Save($"{path}/{filename}.bmp", new BmpEncoder { BitsPerPixel = bitsPerPixel }); - } + // there is no alpha in bmp! + image.Mutate( + c => c.Opacity(1) + ); + + var encoder = new BmpEncoder { BitsPerPixel = bitsPerPixel }; + string path = provider.Utility.SaveTestOutputFile(image, "bmp", encoder, testOutputDetails:bitsPerPixel); + + this.Output.WriteLine(path); + + IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(path); + string referenceOutputFile = provider.Utility.GetReferenceOutputFileName("bmp", bitsPerPixel, true); + + this.Output.WriteLine(referenceOutputFile); + + //using (var encodedImage = Image.Load(referenceOutputFile, referenceDecoder)) + //{ + // ImageComparer.Exact.CompareImagesOrFrames(image, encodedImage); + //} } } } diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs index 0b48170879..7fc9e58d4e 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs @@ -86,9 +86,10 @@ namespace SixLabors.ImageSharp.Tests NamedColors.HotPink, NamedColors.Blue }; - int p = 0; + for (int y = top; y < bottom; y++) { + int p = 0; for (int x = left; x < right; x++) { if (x % stride == 0) From d21064927b7a1e552db3d2d8a6ca82d559f9bdac Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 18 Feb 2018 19:50:02 +0100 Subject: [PATCH 053/151] BmpEncoderTests using reference output --- .../Formats/Bmp/BmpEncoderTests.cs | 48 ++++++++++--------- .../TestUtilities/TestImageExtensions.cs | 28 +++++++++++ tests/Images/External | 2 +- 3 files changed, 54 insertions(+), 24 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs index ff7e34733a..b1eea79d80 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs @@ -5,6 +5,7 @@ using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.PixelFormats; using Xunit; +// ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests { @@ -31,36 +32,37 @@ namespace SixLabors.ImageSharp.Tests private ITestOutputHelper Output { get; } [Theory] - [WithTestPatternImages(nameof(BitsPerPixel), 48, 24, PixelTypesToTest)] - [WithTestPatternImages(nameof(BitsPerPixel), 47, 8, PixelTypesToTest)] - [WithTestPatternImages(nameof(BitsPerPixel), 49, 7, PixelTypesToTest)] - [WithSolidFilledImages(nameof(BitsPerPixel), 1, 1, 255, 100, 50, 255, PixelTypesToTest)] - [WithTestPatternImages(nameof(BitsPerPixel), 7, 5, PixelTypesToTest)] - public void Encode(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) + [WithTestPatternImages(nameof(BitsPerPixel), 24, 24, PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.Rgb24)] + public void Encode_IsNotBoundToSinglePixelType(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) + where TPixel : struct, IPixel + { + TestBmpEncoderCore(provider, bitsPerPixel); + } + + [Theory] + [WithTestPatternImages(nameof(BitsPerPixel), 48, 24, PixelTypes.Rgba32)] + [WithTestPatternImages(nameof(BitsPerPixel), 47, 8, PixelTypes.Rgba32)] + [WithTestPatternImages(nameof(BitsPerPixel), 49, 7, PixelTypes.Rgba32)] + [WithSolidFilledImages(nameof(BitsPerPixel), 1, 1, 255, 100, 50, 255, PixelTypes.Rgba32)] + [WithTestPatternImages(nameof(BitsPerPixel), 7, 5, PixelTypes.Rgba32)] + public void Encode_WorksWithDifferentSizes(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) + where TPixel : struct, IPixel + { + TestBmpEncoderCore(provider, bitsPerPixel); + } + + private static void TestBmpEncoderCore(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) where TPixel : struct, IPixel { - using (Image image = provider.GetImage()) { // there is no alpha in bmp! - image.Mutate( - c => c.Opacity(1) - ); - - var encoder = new BmpEncoder { BitsPerPixel = bitsPerPixel }; - string path = provider.Utility.SaveTestOutputFile(image, "bmp", encoder, testOutputDetails:bitsPerPixel); + image.Mutate(c => c.Opacity(1)); - this.Output.WriteLine(path); - - IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(path); - string referenceOutputFile = provider.Utility.GetReferenceOutputFileName("bmp", bitsPerPixel, true); - - this.Output.WriteLine(referenceOutputFile); + var encoder = new BmpEncoder { BitsPerPixel = bitsPerPixel }; - //using (var encodedImage = Image.Load(referenceOutputFile, referenceDecoder)) - //{ - // ImageComparer.Exact.CompareImagesOrFrames(image, encodedImage); - //} + // Does DebugSave & load reference CompareToReferenceInput(): + image.VerifyEncoder(provider, "bmp", bitsPerPixel, encoder); } } } diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index 2b3cb1bcc3..5d43816a55 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -227,6 +227,33 @@ namespace SixLabors.ImageSharp.Tests return image; } + /// + /// Loads the expected image with a reference decoder + compares it to . + /// Also performs a debug save using . + /// + internal static void VerifyEncoder(this Image image, + ITestImageProvider provider, + string extension, + object testOutputDetails, + IImageEncoder encoder, + ImageComparer customComparer = null, + bool appendPixelTypeToFileName = true + ) + where TPixel : struct, IPixel + { + + string path = provider.Utility.SaveTestOutputFile(image, extension, encoder, testOutputDetails, appendPixelTypeToFileName); + + IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(path); + string referenceOutputFile = provider.Utility.GetReferenceOutputFileName(extension, testOutputDetails, appendPixelTypeToFileName); + + using (var encodedImage = Image.Load(referenceOutputFile, referenceDecoder)) + { + ImageComparer comparer = customComparer ?? ImageComparer.Exact; + comparer.CompareImagesOrFrames(image, encodedImage); + } + } + internal static Image ToGrayscaleImage(this Buffer2D buffer, float scale) { var image = new Image(buffer.Width, buffer.Height); @@ -242,5 +269,6 @@ namespace SixLabors.ImageSharp.Tests return image; } + } } \ No newline at end of file diff --git a/tests/Images/External b/tests/Images/External index 376605e05b..6508e097ad 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 376605e05bb704d425b2d17bf5b310f5376da22e +Subproject commit 6508e097adf1ef6c38a8df17465c6868ed133256 From 109bd1c8274d7b9ab4884ccc8335835047d9d44e Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 18 Feb 2018 19:55:29 +0100 Subject: [PATCH 054/151] improve information locality for BmpDecoderTests --- .../Formats/Bmp/BmpDecoderTests.cs | 35 ++++++++++++------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index 2c0121803b..2209f223b2 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -1,19 +1,28 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Formats; +using System.IO; +using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.PixelFormats; using Xunit; + // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests { - using System.IO; - - using SixLabors.ImageSharp.Formats.Bmp; + + using static TestImages.Bmp; - public class BmpDecoderTests : FileTestBase + public class BmpDecoderTests { + public const PixelTypes CommonNonDefaultPixelTypes = + PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.RgbaVector; + + public static readonly string[] AllBmpFiles = + { + Car, F, NegHeight, CoreHeader, V5Header, RLE, RLEInverted, Bit8, Bit8Inverted, Bit16, Bit16Inverted + }; + [Theory] [WithFileCollection(nameof(AllBmpFiles), PixelTypes.Rgba32)] public void DecodeBmp(TestImageProvider provider) @@ -27,7 +36,7 @@ namespace SixLabors.ImageSharp.Tests } [Theory] - [WithFile(TestImages.Bmp.F, CommonNonDefaultPixelTypes)] + [WithFile(F, CommonNonDefaultPixelTypes)] public void BmpDecoder_IsNotBoundToSinglePixelType(TestImageProvider provider) where TPixel : struct, IPixel { @@ -39,18 +48,18 @@ namespace SixLabors.ImageSharp.Tests } [Theory] - [InlineData(TestImages.Bmp.Car, 24)] - [InlineData(TestImages.Bmp.F, 24)] - [InlineData(TestImages.Bmp.NegHeight, 24)] - [InlineData(TestImages.Bmp.Bit8, 8)] - [InlineData(TestImages.Bmp.Bit8Inverted, 8)] + [InlineData(Car, 24)] + [InlineData(F, 24)] + [InlineData(NegHeight, 24)] + [InlineData(Bit8, 8)] + [InlineData(Bit8Inverted, 8)] public void DetectPixelSize(string imagePath, int expectedPixelSize) { - TestFile testFile = TestFile.Create(imagePath); + var testFile = TestFile.Create(imagePath); using (var stream = new MemoryStream(testFile.Bytes, false)) { Assert.Equal(expectedPixelSize, Image.Identify(stream)?.PixelType?.BitsPerPixel); } } } -} +} \ No newline at end of file From 9af8fb4e2abb74950884907357d72cc69b3aaf2b Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 18 Feb 2018 21:21:01 +0100 Subject: [PATCH 055/151] DebugSaveMultiFrame() works --- .../Formats/Bmp/BmpDecoderTests.cs | 1 - .../Formats/Gif/GifDecoderTests.cs | 43 +++++++-- .../TestUtilities/ImagingTestCaseUtility.cs | 65 +++++++++++++- .../TestUtilities/TestImageExtensions.cs | 87 +++++++++++++++++++ .../Tests/TestImageProviderTests.cs | 24 ++++- 5 files changed, 208 insertions(+), 12 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index 2209f223b2..d958278f6e 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -10,7 +10,6 @@ using Xunit; namespace SixLabors.ImageSharp.Tests { - using static TestImages.Bmp; public class BmpDecoderTests diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs index 9a095548a7..f92d5da81f 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs @@ -6,23 +6,52 @@ using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; using Xunit; +using System.IO; +using SixLabors.ImageSharp.Advanced; // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests { - using System.IO; - using SixLabors.ImageSharp.Advanced; - public class GifDecoderTests { - private const PixelTypes PixelTypes = Tests.PixelTypes.Rgba32 | Tests.PixelTypes.RgbaVector | Tests.PixelTypes.Argb32; + private const PixelTypes TestPixelTypes = PixelTypes.Rgba32 | PixelTypes.RgbaVector | PixelTypes.Argb32; + + public static readonly string[] TestFiles = + { + TestImages.Gif.Giphy, TestImages.Gif.Rings, TestImages.Gif.Trans, TestImages.Gif.Kumin + }; - public static readonly string[] TestFiles = { TestImages.Gif.Giphy, TestImages.Gif.Rings, TestImages.Gif.Trans }; + public static readonly string[] MultiFrameTestFiles = + { + TestImages.Gif.Giphy, TestImages.Gif.Kumin + }; public static readonly string[] BadAppExtFiles = { TestImages.Gif.Issues.BadAppExtLength, TestImages.Gif.Issues.BadAppExtLength_2 }; [Theory] - [WithFileCollection(nameof(TestFiles), PixelTypes)] + [WithFileCollection(nameof(MultiFrameTestFiles), PixelTypes.Rgba32)] + public void AllFramesDecoded(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.DebugSaveMultiFrame(provider); + } + } + + [Theory] + [WithFile(TestImages.Gif.Trans, TestPixelTypes)] + public void IsNotBoundToSinglePixelType(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.DebugSave(provider); + } + } + + [Theory] + [WithFileCollection(nameof(TestFiles), TestPixelTypes)] public void DecodeAndReSave(TestImageProvider imageProvider) where TPixel : struct, IPixel { @@ -34,7 +63,7 @@ namespace SixLabors.ImageSharp.Tests } [Theory] - [WithFileCollection(nameof(TestFiles), PixelTypes)] + [WithFileCollection(nameof(TestFiles), TestPixelTypes)] public void DecodeResizeAndSave(TestImageProvider imageProvider) where TPixel : struct, IPixel { diff --git a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs index e7dfe54881..cde8ec9e47 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs @@ -157,16 +157,77 @@ namespace SixLabors.ImageSharp.Tests return path; } + public IEnumerable GetTestOutputFileNamesMultiFrame( + int frameCount, + string extension = null, + object testOutputDetails = null, + bool appendPixelTypeToFileName = true) + { + string baseDir = this.GetTestOutputFileName("", testOutputDetails, appendPixelTypeToFileName); + + if (!Directory.Exists(baseDir)) + { + Directory.CreateDirectory(baseDir); + } + + for (int i = 0; i < frameCount; i++) + { + string filePath = $"{baseDir}/{i:D2}.{extension}"; + yield return filePath; + } + } + + public string[] SaveTestOutputFileMultiFrame( + Image image, + string extension = "png", + IImageEncoder encoder = null, + object testOutputDetails = null, + bool appendPixelTypeToFileName = true) + where TPixel : struct, IPixel + { + encoder = encoder ?? TestEnvironment.GetReferenceEncoder($"foo.{extension}"); + + string[] files = this.GetTestOutputFileNamesMultiFrame( + image.Frames.Count, + extension, + testOutputDetails, + appendPixelTypeToFileName).ToArray(); + + for (int i = 0; i < image.Frames.Count; i++) + { + using (Image frameImage = image.Frames.CloneFrame(i)) + { + string filePath = files[i]; + using (FileStream stream = File.OpenWrite(filePath)) + { + frameImage.Save(stream, encoder); + } + } + } + + return files; + } + internal string GetReferenceOutputFileName( string extension, - object settings, + object testOutputDetails, bool appendPixelTypeToFileName) { return TestEnvironment.GetReferenceOutputFileName( - this.GetTestOutputFileName(extension, settings, appendPixelTypeToFileName) + this.GetTestOutputFileName(extension, testOutputDetails, appendPixelTypeToFileName) ); } + public string[] GetReferenceOutputFileNamesMultiFrame( + int frameCount, + string extension, + object testOutputDetails, + bool appendPixelTypeToFileName = true) + { + return this.GetTestOutputFileNamesMultiFrame(frameCount, extension, testOutputDetails) + .Select(TestEnvironment.GetReferenceOutputFileName).ToArray(); + } + internal void Init(string typeName, string methodName, string outputSubfolderName) { this.TestGroupName = typeName; diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index 5d43816a55..c95ebc4249 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -16,6 +16,7 @@ namespace SixLabors.ImageSharp.Tests using System.Numerics; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; + using SixLabors.ImageSharp.MetaData; using Xunit; @@ -52,6 +53,28 @@ namespace SixLabors.ImageSharp.Tests return image; } + public static Image DebugSaveMultiFrame( + this Image image, + ITestImageProvider provider, + object testOutputDetails = null, + string extension = "png", + bool appendPixelTypeToFileName = true) + where TPixel : struct, IPixel + { + if (TestEnvironment.RunsOnCI) + { + return image; + } + + // We are running locally then we want to save it out + provider.Utility.SaveTestOutputFileMultiFrame( + image, + extension, + testOutputDetails: testOutputDetails, + appendPixelTypeToFileName: appendPixelTypeToFileName); + return image; + } + /// /// Compares the image against the expected Reference output, throws an exception if the images are not similar enough. /// The output file should be named identically to the output produced by . @@ -118,6 +141,29 @@ namespace SixLabors.ImageSharp.Tests return image; } + public static Image CompareToReferenceOutputMultiFrame( + this Image image, + ITestImageProvider provider, + ImageComparer comparer, + object testOutputDetails = null, + string extension = "png", + bool grayscale = false, + bool appendPixelTypeToFileName = true) + where TPixel : struct, IPixel + { + using (Image referenceImage = GetReferenceOutputImageMultiFrame( + provider, + image.Frames.Count, + testOutputDetails, + extension, + appendPixelTypeToFileName)) + { + comparer.VerifySimilarity(referenceImage, image); + } + + return image; + } + public static Image GetReferenceOutputImage(this ITestImageProvider provider, object testOutputDetails = null, string extension = "png", @@ -136,6 +182,47 @@ namespace SixLabors.ImageSharp.Tests return Image.Load(referenceOutputFile, decoder); } + public static Image GetReferenceOutputImageMultiFrame(this ITestImageProvider provider, + int frameCount, + object testOutputDetails = null, + string extension = "png", + bool appendPixelTypeToFileName = true) + where TPixel : struct, IPixel + { + string[] frameFiles = provider.Utility.GetReferenceOutputFileNamesMultiFrame( + frameCount, + extension, + testOutputDetails, + appendPixelTypeToFileName); + + var temporalFrameImages = new List>(); + + IImageDecoder decoder = TestEnvironment.GetReferenceDecoder(frameFiles[0]); + + foreach (string path in frameFiles) + { + if (!File.Exists(path)) + { + throw new Exception("Reference output file missing: " + path); + } + + var tempImage = Image.Load(path, decoder); + temporalFrameImages.Add(tempImage); + } + + var result = new Image( + Configuration.Default, + new ImageMetaData(), + temporalFrameImages.Select(fi => fi.Frames.RootFrame)); + + foreach (Image fi in temporalFrameImages) + { + fi.Dispose(); + } + + return result; + } + public static IEnumerable GetReferenceOutputSimilarityReports( this Image image, ITestImageProvider provider, diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs index f0adeb7534..2f306e9494 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs @@ -239,8 +239,28 @@ namespace SixLabors.ImageSharp.Tests where TPixel : struct, IPixel { Assert.NotNull(provider.Utility.SourceFileOrDescription); - Image image = provider.GetImage(); - provider.Utility.SaveTestOutputFile(image, "png"); + using (Image image = provider.GetImage()) + { + provider.Utility.SaveTestOutputFile(image, "png"); + } + } + + [Theory] + [WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32)] + public void SaveTestOutputFileMultiFrame(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + string[] files = provider.Utility.SaveTestOutputFileMultiFrame(image); + + Assert.True(files.Length > 2); + foreach (string path in files) + { + this.Output.WriteLine(path); + Assert.True(File.Exists(path)); + } + } } [Theory] From 52ad911330d9883cb1deac32cecdc698ff767eb4 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 18 Feb 2018 22:30:12 +0100 Subject: [PATCH 056/151] GifDecoderTests using reference images --- .../Formats/Gif/GifDecoderTests.cs | 78 +++++++++---------- .../TestUtilities/TestImageExtensions.cs | 36 ++++++++- tests/Images/External | 2 +- 3 files changed, 70 insertions(+), 46 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs index f92d5da81f..3802a2c279 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs @@ -12,6 +12,10 @@ using SixLabors.ImageSharp.Advanced; // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests { + using System.Collections.Generic; + + using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + public class GifDecoderTests { private const PixelTypes TestPixelTypes = PixelTypes.Rgba32 | PixelTypes.RgbaVector | PixelTypes.Argb32; @@ -26,56 +30,70 @@ namespace SixLabors.ImageSharp.Tests TestImages.Gif.Giphy, TestImages.Gif.Kumin }; + public static readonly string[] BasicVerificationFiles = + { + TestImages.Gif.Cheers, + TestImages.Gif.Rings, + + // previously DecodeBadApplicationExtensionLength: + TestImages.Gif.Issues.BadAppExtLength, + TestImages.Gif.Issues.BadAppExtLength_2, + + // previously DecodeBadDescriptorDimensionsLength: + TestImages.Gif.Issues.BadDescriptorWidth + }; + + private static readonly Dictionary BasicVerificationFrameCount = + new Dictionary + { + [TestImages.Gif.Cheers] = 93, + [TestImages.Gif.Issues.BadDescriptorWidth] = 36, + }; + public static readonly string[] BadAppExtFiles = { TestImages.Gif.Issues.BadAppExtLength, TestImages.Gif.Issues.BadAppExtLength_2 }; [Theory] [WithFileCollection(nameof(MultiFrameTestFiles), PixelTypes.Rgba32)] - public void AllFramesDecoded(TestImageProvider provider) + public void Decode_VerifyAllFrames(TestImageProvider provider) where TPixel : struct, IPixel { using (Image image = provider.GetImage()) { image.DebugSaveMultiFrame(provider); + image.CompareToReferenceOutputMultiFrame(provider, ImageComparer.Exact); } } [Theory] [WithFile(TestImages.Gif.Trans, TestPixelTypes)] - public void IsNotBoundToSinglePixelType(TestImageProvider provider) + public void GifDecoder_IsNotBoundToSinglePixelType(TestImageProvider provider) where TPixel : struct, IPixel { using (Image image = provider.GetImage()) { image.DebugSave(provider); + image.CompareFirstFrameToReferenceOutput(provider, ImageComparer.Exact); } } [Theory] - [WithFileCollection(nameof(TestFiles), TestPixelTypes)] - public void DecodeAndReSave(TestImageProvider imageProvider) + [WithFileCollection(nameof(BasicVerificationFiles), PixelTypes.Rgba32)] + public void Decode_VerifyRootFrameAndFrameCount(TestImageProvider provider) where TPixel : struct, IPixel { - using (Image image = imageProvider.GetImage()) + if (!BasicVerificationFrameCount.TryGetValue(provider.SourceFileOrDescription, out int expectedFrameCount)) { - imageProvider.Utility.SaveTestOutputFile(image, "bmp"); - imageProvider.Utility.SaveTestOutputFile(image, "gif"); + expectedFrameCount = 1; } - } - [Theory] - [WithFileCollection(nameof(TestFiles), TestPixelTypes)] - public void DecodeResizeAndSave(TestImageProvider imageProvider) - where TPixel : struct, IPixel - { - using (Image image = imageProvider.GetImage()) + using (Image image = provider.GetImage()) { - image.Mutate(x => x.Resize(new Size(image.Width / 2, image.Height / 2))); - - imageProvider.Utility.SaveTestOutputFile(image, "bmp"); - imageProvider.Utility.SaveTestOutputFile(image, "gif"); + Assert.Equal(expectedFrameCount, image.Frames.Count); + image.DebugSave(provider); + image.CompareFirstFrameToReferenceOutput(provider, ImageComparer.Exact); } } - + [Fact] public void Decode_IgnoreMetadataIsFalse_CommentsAreRead() { @@ -178,27 +196,5 @@ namespace SixLabors.ImageSharp.Tests } } } - - [Theory] - [WithFileCollection(nameof(BadAppExtFiles), PixelTypes.Rgba32)] - public void DecodeBadApplicationExtensionLength(TestImageProvider imageProvider) - where TPixel : struct, IPixel - { - using (Image image = imageProvider.GetImage()) - { - imageProvider.Utility.SaveTestOutputFile(image, "bmp"); - } - } - - [Theory] - [WithFile(TestImages.Gif.Issues.BadDescriptorWidth, PixelTypes.Rgba32)] - public void DecodeBadDescriptorDimensionsLength(TestImageProvider provider) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - provider.Utility.SaveTestOutputFile(image, "bmp"); - } - } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index c95ebc4249..fcc28bf4eb 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -141,6 +141,32 @@ namespace SixLabors.ImageSharp.Tests return image; } + public static Image CompareFirstFrameToReferenceOutput( + this Image image, + ITestImageProvider provider, + ImageComparer comparer, + object testOutputDetails = null, + string extension = "png", + bool grayscale = false, + bool appendPixelTypeToFileName = true) + where TPixel : struct, IPixel + { + using (Image firstFrameOnlyImage = new Image(image.Width, image.Height)) + using (Image referenceImage = GetReferenceOutputImage( + provider, + testOutputDetails, + extension, + appendPixelTypeToFileName)) + { + firstFrameOnlyImage.Frames.AddFrame(image.Frames.RootFrame); + firstFrameOnlyImage.Frames.RemoveFrame(0); + + comparer.VerifySimilarity(referenceImage, firstFrameOnlyImage); + } + + return image; + } + public static Image CompareToReferenceOutputMultiFrame( this Image image, ITestImageProvider provider, @@ -209,17 +235,19 @@ namespace SixLabors.ImageSharp.Tests var tempImage = Image.Load(path, decoder); temporalFrameImages.Add(tempImage); } + + Image firstTemp = temporalFrameImages[0]; - var result = new Image( - Configuration.Default, - new ImageMetaData(), - temporalFrameImages.Select(fi => fi.Frames.RootFrame)); + var result = new Image(firstTemp.Width, firstTemp.Height); foreach (Image fi in temporalFrameImages) { + result.Frames.AddFrame(fi.Frames.RootFrame); fi.Dispose(); } + // remove the initial empty frame: + result.Frames.RemoveFrame(0); return result; } diff --git a/tests/Images/External b/tests/Images/External index 6508e097ad..65db1d045e 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 6508e097adf1ef6c38a8df17465c6868ed133256 +Subproject commit 65db1d045e74e7702ec33b44f88f97b4bf86f1ea From a470f61e071a06bf4e73ad89631cb690ab5bcca0 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 18 Feb 2018 23:50:15 +0100 Subject: [PATCH 057/151] PngEncoderTests using reference images --- .../Formats/Gif/GifEncoderTests.cs | 7 +- .../Formats/Png/PngEncoderTests.cs | 119 +++++++++++++++--- tests/Images/External | 2 +- 3 files changed, 110 insertions(+), 18 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs index a06e36e2a6..a2f4806f37 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs @@ -7,15 +7,16 @@ using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.MetaData; using SixLabors.ImageSharp.PixelFormats; using Xunit; +// ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests { public class GifEncoderTests { - private const PixelTypes PixelTypes = Tests.PixelTypes.Rgba32 | Tests.PixelTypes.RgbaVector | Tests.PixelTypes.Argb32; + private const PixelTypes TestPixelTypes = PixelTypes.Rgba32 | PixelTypes.RgbaVector | PixelTypes.Argb32; [Theory] - [WithTestPatternImages(100, 100, PixelTypes)] + [WithTestPatternImages(100, 100, TestPixelTypes)] public void EncodeGeneratedPatterns(TestImageProvider provider) where TPixel : struct, IPixel { @@ -78,7 +79,7 @@ namespace SixLabors.ImageSharp.Tests } [Fact] - public void Encode_CommentIsToLong_CommentIsTrimmed() + public void Encode_WhenCommentIsTooLong_CommentIsTrimmed() { using (Image input = new Image(1, 1)) { diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index 1566ddf442..ef261aac90 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -10,36 +10,127 @@ using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.PixelFormats; using Xunit; +// ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests { + using SixLabors.ImageSharp.Quantizers; + using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + public class PngEncoderTests : FileTestBase { - private const PixelTypes PixelTypes = Tests.PixelTypes.Rgba32 | Tests.PixelTypes.RgbaVector | Tests.PixelTypes.Argb32; + /// + /// All types except Palette + /// + public static readonly TheoryData PngColorTypes = new TheoryData() + { + PngColorType.RgbWithAlpha, + PngColorType.Rgb, + PngColorType.Grayscale, + PngColorType.GrayscaleWithAlpha, + }; + + /// + /// All types except Palette + /// + public static readonly TheoryData CompressionLevels = new TheoryData() + { + 1, 2, 3, 4, 5, 6, 7, 8, 9 + }; + + public static readonly TheoryData PaletteSizes = new TheoryData() + { + 30, 55, 100, 201, 255 + }; + + [Theory] + [WithTestPatternImages(nameof(PngColorTypes), 48, 24, PixelTypes.Rgba32)] + [WithTestPatternImages(nameof(PngColorTypes), 47, 8, PixelTypes.Rgba32)] + [WithTestPatternImages(nameof(PngColorTypes), 49, 7, PixelTypes.Rgba32)] + [WithSolidFilledImages(nameof(PngColorTypes), 1, 1, 255, 100, 50, 255, PixelTypes.Rgba32)] + [WithTestPatternImages(nameof(PngColorTypes), 7, 5, PixelTypes.Rgba32)] + public void WorksWithDifferentSizes(TestImageProvider provider, PngColorType pngColorType) + where TPixel : struct, IPixel + { + TestPngEncoderCore(provider, pngColorType, appendPngColorType: true); + } + + [Theory] + [WithTestPatternImages(nameof(PngColorTypes), 24, 24, PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.Rgb24)] + public void IsNotBoundToSinglePixelType(TestImageProvider provider, PngColorType pngColorType) + where TPixel : struct, IPixel + { + TestPngEncoderCore(provider, pngColorType, appendPixelType: true); + } + + [Theory] + [WithTestPatternImages(nameof(CompressionLevels), 24, 24, PixelTypes.Rgba32)] + public void WorksWithAllCompressionLevels(TestImageProvider provider, int compressionLevel) + where TPixel : struct, IPixel + { + TestPngEncoderCore(provider, PngColorType.RgbWithAlpha, compressionLevel, appendCompressionLevel: true); + } [Theory] - [WithTestPatternImages(100, 100, PixelTypes, PngColorType.RgbWithAlpha)] - [WithTestPatternImages(100, 100, PixelTypes, PngColorType.Rgb)] - [WithTestPatternImages(100, 100, PixelTypes, PngColorType.Palette)] - [WithTestPatternImages(100, 100, PixelTypes, PngColorType.Grayscale)] - [WithTestPatternImages(100, 100, PixelTypes, PngColorType.GrayscaleWithAlpha)] - public void EncodeGeneratedPatterns(TestImageProvider provider, PngColorType pngColorType) + [WithTestPatternImages(nameof(PaletteSizes), 72, 72, PixelTypes.Rgba32)] + public void PaletteColorType_WuQuantizer(TestImageProvider provider, int paletteSize) where TPixel : struct, IPixel { using (Image image = provider.GetImage()) { - var options = new PngEncoder() - { - PngColorType = pngColorType - }; - provider.Utility.TestName += "_" + pngColorType; + var encoder = new PngEncoder + { + PngColorType = PngColorType.Palette, + PaletteSize = paletteSize, + Quantizer = new WuQuantizer() + }; - provider.Utility.SaveTestOutputFile(image, "png", options); + image.VerifyEncoder(provider, "png", $"PaletteSize-{paletteSize}", encoder, appendPixelTypeToFileName: false); } } + private static bool HasAlpha(PngColorType pngColorType) => + pngColorType == PngColorType.GrayscaleWithAlpha || pngColorType == PngColorType.RgbWithAlpha; + + private static void TestPngEncoderCore( + TestImageProvider provider, + PngColorType pngColorType, + int compressionLevel = 6, + bool appendPngColorType = false, + bool appendPixelType = false, + bool appendCompressionLevel = false) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + if (!HasAlpha(pngColorType)) + { + image.Mutate(c => c.Opacity(1)); + } + + var encoder = new PngEncoder { PngColorType = pngColorType, CompressionLevel = compressionLevel}; + + string pngColorTypeInfo = appendPixelType ? pngColorType.ToString() : ""; + string compressionLevelInfo = appendCompressionLevel ? $"_C{compressionLevel}" : ""; + string debugInfo = $"{pngColorTypeInfo}{compressionLevelInfo}"; + string referenceInfo = $"{pngColorTypeInfo}"; + + // Does DebugSave & load reference CompareToReferenceInput(): + string path = ((ITestImageProvider)provider).Utility.SaveTestOutputFile(image, "png", encoder, debugInfo, appendPixelType); + + IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(path); + string referenceOutputFile = ((ITestImageProvider)provider).Utility.GetReferenceOutputFileName("png", referenceInfo, appendPixelType); + + using (var encodedImage = Image.Load(referenceOutputFile, referenceDecoder)) + { + ImageComparer comparer = null ?? ImageComparer.Exact; + comparer.CompareImagesOrFrames(image, encodedImage); + } + } + } + [Theory] - [WithBlankImages(1, 1, PixelTypes.All)] + [WithBlankImages(1, 1, PixelTypes.Rgba32)] public void WritesFileMarker(TestImageProvider provider) where TPixel : struct, IPixel { diff --git a/tests/Images/External b/tests/Images/External index 65db1d045e..06809c1f24 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 65db1d045e74e7702ec33b44f88f97b4bf86f1ea +Subproject commit 06809c1f2462332731f2a88bd866d5222f533aa5 From 28c4e2c93e9099f66831be0bc168cd2c9dc9f39c Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 19 Feb 2018 01:00:44 +0100 Subject: [PATCH 058/151] use ImageComparer.Tolerant() when testing Palette PNG encoding --- tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index ef261aac90..7e24f41df1 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -19,6 +19,8 @@ namespace SixLabors.ImageSharp.Tests public class PngEncoderTests : FileTestBase { + private const float ToleranceThresholdForPaletteEncoder = 0.01f / 100; + /// /// All types except Palette /// @@ -123,7 +125,7 @@ namespace SixLabors.ImageSharp.Tests using (var encodedImage = Image.Load(referenceOutputFile, referenceDecoder)) { - ImageComparer comparer = null ?? ImageComparer.Exact; + ImageComparer comparer = pngColorType== PngColorType.Palette ? ImageComparer.Tolerant(ToleranceThresholdForPaletteEncoder) : ImageComparer.Exact; comparer.CompareImagesOrFrames(image, encodedImage); } } From 57b3c967f939f3571f1c441fad694c8ee22278e2 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 19 Feb 2018 02:15:48 +0100 Subject: [PATCH 059/151] JpegEncoderTests using reference images --- .../ComplexIntegrationTests.cs | 35 ++++ .../Formats/Jpg/JpegEncoderTests.cs | 155 +++++++++--------- .../TestUtilities/TestImageExtensions.cs | 13 +- tests/Images/External | 2 +- 4 files changed, 126 insertions(+), 79 deletions(-) create mode 100644 tests/ImageSharp.Tests/ComplexIntegrationTests.cs diff --git a/tests/ImageSharp.Tests/ComplexIntegrationTests.cs b/tests/ImageSharp.Tests/ComplexIntegrationTests.cs new file mode 100644 index 0000000000..ad4676872f --- /dev/null +++ b/tests/ImageSharp.Tests/ComplexIntegrationTests.cs @@ -0,0 +1,35 @@ +namespace SixLabors.ImageSharp.Tests +{ + using SixLabors.ImageSharp.Formats.Jpeg; + using SixLabors.ImageSharp.PixelFormats; + using SixLabors.ImageSharp.Processing; + using SixLabors.Primitives; + + using Xunit; + + /// + /// Might be useful to catch complex bugs + /// + public class ComplexIntegrationTests + { + [Theory] + [WithFile(TestImages.Jpeg.Baseline.Snake, PixelTypes.Rgba32, 75, JpegSubsample.Ratio420)] + [WithFile(TestImages.Jpeg.Baseline.Lake, PixelTypes.Rgba32, 75, JpegSubsample.Ratio420)] + [WithFile(TestImages.Jpeg.Baseline.Snake, PixelTypes.Rgba32, 75, JpegSubsample.Ratio444)] + [WithFile(TestImages.Jpeg.Baseline.Lake, PixelTypes.Rgba32, 75, JpegSubsample.Ratio444)] + public void LoadResizeSave(TestImageProvider provider, int quality, JpegSubsample subsample) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(x => x.Resize(new ResizeOptions { Size = new Size(150, 100), Mode = ResizeMode.Max }))) + { + + image.MetaData.ExifProfile = null; // Reduce the size of the file + JpegEncoder options = new JpegEncoder { Subsample = subsample, Quality = quality }; + + provider.Utility.TestName += $"{subsample}_Q{quality}"; + provider.Utility.SaveTestOutputFile(image, "png"); + provider.Utility.SaveTestOutputFile(image, "jpg", options); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs index c8d416beaf..8610356b56 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs @@ -10,117 +10,126 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg using System.Collections.Generic; using System.IO; + using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.PixelFormats; - using SixLabors.ImageSharp.Processing; - using SixLabors.Primitives; + using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; using Xunit; using Xunit.Abstractions; - public class JpegEncoderTests : MeasureFixture + public class JpegEncoderTests { - public static IEnumerable AllBmpFiles => TestImages.Bmp.All; + public static readonly TheoryData BitsPerPixel_Quality = + new TheoryData + { + { JpegSubsample.Ratio420, 40 }, + { JpegSubsample.Ratio420, 60 }, + { JpegSubsample.Ratio420, 100 }, + + { JpegSubsample.Ratio444, 40 }, + { JpegSubsample.Ratio444, 60 }, + { JpegSubsample.Ratio444, 100 }, + }; - public JpegEncoderTests(ITestOutputHelper output) - : base(output) + [Theory] + [WithFile(TestImages.Png.CalliphoraPartial, nameof(BitsPerPixel_Quality), PixelTypes.Rgba32)] + [WithTestPatternImages(nameof(BitsPerPixel_Quality), 73, 71, PixelTypes.Rgba32)] + [WithTestPatternImages(nameof(BitsPerPixel_Quality), 48, 24, PixelTypes.Rgba32)] + [WithTestPatternImages(nameof(BitsPerPixel_Quality), 46, 8, PixelTypes.Rgba32)] + [WithTestPatternImages(nameof(BitsPerPixel_Quality), 51, 7, PixelTypes.Rgba32)] + [WithSolidFilledImages(nameof(BitsPerPixel_Quality), 1, 1, 255, 100, 50, 255, PixelTypes.Rgba32)] + [WithTestPatternImages(nameof(BitsPerPixel_Quality), 7, 5, PixelTypes.Rgba32)] + public void EncodeBaseline_WorksWithDifferentSizes(TestImageProvider provider, JpegSubsample subsample, int quality) + where TPixel : struct, IPixel { + TestJpegEncoderCore(provider, subsample, quality); } [Theory] - [WithFile(TestImages.Jpeg.Baseline.Snake, PixelTypes.Rgba32, 75, JpegSubsample.Ratio420)] - [WithFile(TestImages.Jpeg.Baseline.Lake, PixelTypes.Rgba32, 75, JpegSubsample.Ratio420)] - [WithFile(TestImages.Jpeg.Baseline.Snake, PixelTypes.Rgba32, 75, JpegSubsample.Ratio444)] - [WithFile(TestImages.Jpeg.Baseline.Lake, PixelTypes.Rgba32, 75, JpegSubsample.Ratio444)] - public void LoadResizeSave(TestImageProvider provider, int quality, JpegSubsample subsample) + [WithTestPatternImages(nameof(BitsPerPixel_Quality), 48, 48, PixelTypes.Rgba32 | PixelTypes.Bgra32)] + public void EncodeBaseline_IsNotBoundToSinglePixelType(TestImageProvider provider, JpegSubsample subsample, int quality) where TPixel : struct, IPixel { - using (Image image = provider.GetImage(x => x.Resize(new ResizeOptions { Size = new Size(150, 100), Mode = ResizeMode.Max }))) - { - - image.MetaData.ExifProfile = null; // Reduce the size of the file - JpegEncoder options = new JpegEncoder { Subsample = subsample, Quality = quality }; - - provider.Utility.TestName += $"{subsample}_Q{quality}"; - provider.Utility.SaveTestOutputFile(image, "png"); - provider.Utility.SaveTestOutputFile(image, "jpg", options); - } + TestJpegEncoderCore(provider, subsample, quality); } - [Theory] - [WithFileCollection(nameof(AllBmpFiles), PixelTypes.Rgba32 | PixelTypes.Rgba32 | PixelTypes.Argb32, JpegSubsample.Ratio420, 75)] - [WithFileCollection(nameof(AllBmpFiles), PixelTypes.Rgba32 | PixelTypes.Rgba32 | PixelTypes.Argb32, JpegSubsample.Ratio444, 75)] - public void OpenBmp_SaveJpeg(TestImageProvider provider, JpegSubsample subSample, int quality) - where TPixel : struct, IPixel + private static ImageComparer GetComparer(int quality) { - using (Image image = provider.GetImage()) + if (quality > 90) { - ImagingTestCaseUtility utility = provider.Utility; - utility.TestName += "_" + subSample + "_Q" + quality; - - using (FileStream outputStream = File.OpenWrite(utility.GetTestOutputFileName("jpg"))) - { - image.Save(outputStream, new JpegEncoder() - { - Subsample = subSample, - Quality = quality - }); - } + return ImageComparer.Tolerant(0.0005f / 100); + } + else if (quality > 50) + { + return ImageComparer.Tolerant(0.005f / 100); + } + else + { + return ImageComparer.Tolerant(0.01f / 100); } } - [Fact] - public void Encode_IgnoreMetadataIsFalse_ExifProfileIsWritten() + private static void TestJpegEncoderCore( + TestImageProvider provider, + JpegSubsample subsample, + int quality = 100) + where TPixel : struct, IPixel { - JpegEncoder options = new JpegEncoder() - { - IgnoreMetadata = false - }; - - TestFile testFile = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan); - - using (Image input = testFile.CreateImage()) + using (Image image = provider.GetImage()) { - using (MemoryStream memStream = new MemoryStream()) - { - input.Save(memStream, options); - - memStream.Position = 0; - using (Image output = Image.Load(memStream)) - { - Assert.NotNull(output.MetaData.ExifProfile); - } - } + // There is no alpha in Jpeg! + image.Mutate(c => c.Opacity(1)); + + var encoder = new JpegEncoder() + { + Subsample = subsample, + Quality = quality + }; + string info = $"{subsample}-Q{quality}"; + ImageComparer comparer = GetComparer(quality); + + // Does DebugSave & load reference CompareToReferenceInput(): + image.VerifyEncoder(provider, "jpeg", info, encoder, comparer, referenceImageExtension: "png"); } } + - [Fact] - public void Encode_IgnoreMetadataIsTrue_ExifProfileIgnored() + [Theory] + [InlineData(false)] + [InlineData(true)] + public void IgnoreMetadata_ControlsIfExifProfileIsWritten(bool ignoreMetaData) { - JpegEncoder options = new JpegEncoder() + var encoder = new JpegEncoder() { - IgnoreMetadata = true + IgnoreMetadata = ignoreMetaData }; - - TestFile testFile = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan); - - using (Image input = testFile.CreateImage()) + + using (Image input = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateImage()) { - using (MemoryStream memStream = new MemoryStream()) + using (var memStream = new MemoryStream()) { - input.SaveAsJpeg(memStream, options); + input.Save(memStream, encoder); memStream.Position = 0; - using (Image output = Image.Load(memStream)) + using (var output = Image.Load(memStream)) { - Assert.Null(output.MetaData.ExifProfile); + if (ignoreMetaData) + { + Assert.Null(output.MetaData.ExifProfile); + } + else + { + Assert.NotNull(output.MetaData.ExifProfile); + } } } } } - + [Fact] - public void Encode_Quality_0_And_1_Are_Identical() + public void Quality_0_And_1_Are_Identical() { var options = new JpegEncoder { @@ -143,7 +152,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg } [Fact] - public void Encode_Quality_0_And_100_Are_Not_Identical() + public void Quality_0_And_100_Are_Not_Identical() { var options = new JpegEncoder { diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index fcc28bf4eb..33dbc911e4 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -352,16 +352,19 @@ namespace SixLabors.ImageSharp.Tests object testOutputDetails, IImageEncoder encoder, ImageComparer customComparer = null, - bool appendPixelTypeToFileName = true + bool appendPixelTypeToFileName = true, + string referenceImageExtension = null ) where TPixel : struct, IPixel { - string path = provider.Utility.SaveTestOutputFile(image, extension, encoder, testOutputDetails, appendPixelTypeToFileName); - - IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(path); - string referenceOutputFile = provider.Utility.GetReferenceOutputFileName(extension, testOutputDetails, appendPixelTypeToFileName); + provider.Utility.SaveTestOutputFile(image, extension, encoder, testOutputDetails, appendPixelTypeToFileName); + referenceImageExtension = referenceImageExtension ?? extension; + string referenceOutputFile = provider.Utility.GetReferenceOutputFileName(referenceImageExtension, testOutputDetails, appendPixelTypeToFileName); + + IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(referenceOutputFile); + using (var encodedImage = Image.Load(referenceOutputFile, referenceDecoder)) { ImageComparer comparer = customComparer ?? ImageComparer.Exact; diff --git a/tests/Images/External b/tests/Images/External index 06809c1f24..550a157d8a 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 06809c1f2462332731f2a88bd866d5222f533aa5 +Subproject commit 550a157d8af7a6883646a010c609f9c7c5c015ac From 70ed6c7b821397ab022d6fcd30c8a031208b5ccc Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 19 Feb 2018 02:45:42 +0100 Subject: [PATCH 060/151] adding a few more cases to PngEncoderTests --- .../Formats/Png/PngEncoderTests.cs | 18 +++++++++++++++++- tests/Images/External | 2 +- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index 7e24f41df1..b1c4199906 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -45,7 +45,13 @@ namespace SixLabors.ImageSharp.Tests 30, 55, 100, 201, 255 }; + public static readonly TheoryData PaletteLargeOnly = new TheoryData() + { + 80, 100, 120, 230 + }; + [Theory] + [WithFile(TestImages.Png.Palette8Bpp, nameof(PngColorTypes), PixelTypes.Rgba32)] [WithTestPatternImages(nameof(PngColorTypes), 48, 24, PixelTypes.Rgba32)] [WithTestPatternImages(nameof(PngColorTypes), 47, 8, PixelTypes.Rgba32)] [WithTestPatternImages(nameof(PngColorTypes), 49, 7, PixelTypes.Rgba32)] @@ -56,7 +62,7 @@ namespace SixLabors.ImageSharp.Tests { TestPngEncoderCore(provider, pngColorType, appendPngColorType: true); } - + [Theory] [WithTestPatternImages(nameof(PngColorTypes), 24, 24, PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.Rgb24)] public void IsNotBoundToSinglePixelType(TestImageProvider provider, PngColorType pngColorType) @@ -73,6 +79,16 @@ namespace SixLabors.ImageSharp.Tests TestPngEncoderCore(provider, PngColorType.RgbWithAlpha, compressionLevel, appendCompressionLevel: true); } + [Theory] + [WithFile(TestImages.Png.Palette8Bpp, nameof(PaletteLargeOnly), PixelTypes.Rgba32)] + public void PaletteColorType_WuQuantizer_File( + TestImageProvider provider, + int paletteSize) + where TPixel : struct, IPixel + { + this.PaletteColorType_WuQuantizer(provider, paletteSize); + } + [Theory] [WithTestPatternImages(nameof(PaletteSizes), 72, 72, PixelTypes.Rgba32)] public void PaletteColorType_WuQuantizer(TestImageProvider provider, int paletteSize) diff --git a/tests/Images/External b/tests/Images/External index 550a157d8a..b3be1178d4 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 550a157d8af7a6883646a010c609f9c7c5c015ac +Subproject commit b3be1178d4e970efc624181480094e50b0d57a90 From 4f04027bdcdca50bb619892cd8dfd0bc62240687 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 19 Feb 2018 03:09:19 +0100 Subject: [PATCH 061/151] removing unused stuff --- tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs | 5 ----- tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs index 3802a2c279..9cdb9f8b1a 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs @@ -20,11 +20,6 @@ namespace SixLabors.ImageSharp.Tests { private const PixelTypes TestPixelTypes = PixelTypes.Rgba32 | PixelTypes.RgbaVector | PixelTypes.Argb32; - public static readonly string[] TestFiles = - { - TestImages.Gif.Giphy, TestImages.Gif.Rings, TestImages.Gif.Trans, TestImages.Gif.Kumin - }; - public static readonly string[] MultiFrameTestFiles = { TestImages.Gif.Giphy, TestImages.Gif.Kumin diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index b1c4199906..28f156279c 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Tests using SixLabors.ImageSharp.Quantizers; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; - public class PngEncoderTests : FileTestBase + public class PngEncoderTests { private const float ToleranceThresholdForPaletteEncoder = 0.01f / 100; From 27dade8d33b96398595bb4555080a1a60911166b Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 19 Feb 2018 17:37:05 +0100 Subject: [PATCH 062/151] dropping MemoryManager ctr. argument: PngEncoder, WuQuantizer, ShapeRegion, ShapePath --- src/ImageSharp.Drawing/Paths/DrawPath.cs | 2 +- src/ImageSharp.Drawing/Paths/FillPaths.cs | 4 +- src/ImageSharp.Drawing/Paths/ShapePath.cs | 5 +- src/ImageSharp.Drawing/Paths/ShapeRegion.cs | 23 ++-- .../Formats/Png/PngConfigurationModule.cs | 2 +- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 3 +- src/ImageSharp/Formats/Png/PngEncoder.cs | 15 +-- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 2 +- .../Processors/Filters/PolaroidProcessor.cs | 1 - src/ImageSharp/Quantizers/Quantize.cs | 2 +- .../Quantizers/WuQuantizer{TPixel}.cs | 52 ++++--- .../Image/EncodeIndexedPng.cs | 10 +- .../ImageSharp.Benchmarks/Image/EncodePng.cs | 2 +- .../Drawing/Paths/ShapeRegionTests.cs | 127 ++++++++++-------- .../Formats/GeneralFormatTests.cs | 9 +- .../Formats/Png/PngEncoderTests.cs | 2 +- .../Formats/Png/PngSmokeTests.cs | 4 +- tests/ImageSharp.Tests/Image/ImageTests.cs | 2 +- .../Quantization/QuantizedImageTests.cs | 4 +- .../Tests/ReferenceCodecTests.cs | 2 +- 20 files changed, 131 insertions(+), 142 deletions(-) diff --git a/src/ImageSharp.Drawing/Paths/DrawPath.cs b/src/ImageSharp.Drawing/Paths/DrawPath.cs index a46d5751f6..b6c821a60b 100644 --- a/src/ImageSharp.Drawing/Paths/DrawPath.cs +++ b/src/ImageSharp.Drawing/Paths/DrawPath.cs @@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp public static IImageProcessingContext Draw(this IImageProcessingContext source, IPen pen, IPath path, GraphicsOptions options) where TPixel : struct, IPixel { - return source.Fill(pen.StrokeFill, new ShapePath(source.GetMemoryManager(), path, pen), options); + return source.Fill(pen.StrokeFill, new ShapePath(path, pen), options); } /// diff --git a/src/ImageSharp.Drawing/Paths/FillPaths.cs b/src/ImageSharp.Drawing/Paths/FillPaths.cs index 5972c52a05..f554ed7581 100644 --- a/src/ImageSharp.Drawing/Paths/FillPaths.cs +++ b/src/ImageSharp.Drawing/Paths/FillPaths.cs @@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, IPath path, GraphicsOptions options) where TPixel : struct, IPixel { - return source.Fill(brush, new ShapeRegion(source.GetMemoryManager(), path), options); + return source.Fill(brush, new ShapeRegion(path), options); } /// @@ -39,7 +39,7 @@ namespace SixLabors.ImageSharp public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, IPath path) where TPixel : struct, IPixel { - return source.Fill(brush, new ShapeRegion(source.GetMemoryManager(), path), GraphicsOptions.Default); + return source.Fill(brush, new ShapeRegion(path), GraphicsOptions.Default); } /// diff --git a/src/ImageSharp.Drawing/Paths/ShapePath.cs b/src/ImageSharp.Drawing/Paths/ShapePath.cs index 9e2b22a75b..4c22787195 100644 --- a/src/ImageSharp.Drawing/Paths/ShapePath.cs +++ b/src/ImageSharp.Drawing/Paths/ShapePath.cs @@ -18,12 +18,11 @@ namespace SixLabors.ImageSharp.Drawing /// /// Initializes a new instance of the class. /// - /// The to use for buffer allocations. /// The shape. /// The pen to apply to the shape. // SixLabors.shape willbe moving to a Span/ReadOnlySpan based API shortly use ToArray for now. - public ShapePath(MemoryManager memoryManager, IPath shape, Pens.IPen pen) - : base(memoryManager, shape.GenerateOutline(pen.StrokeWidth, pen.StrokePattern.ToArray())) + public ShapePath(IPath shape, Pens.IPen pen) + : base(shape.GenerateOutline(pen.StrokeWidth, pen.StrokePattern.ToArray())) { } } diff --git a/src/ImageSharp.Drawing/Paths/ShapeRegion.cs b/src/ImageSharp.Drawing/Paths/ShapeRegion.cs index 77a3b01159..072a38cf86 100644 --- a/src/ImageSharp.Drawing/Paths/ShapeRegion.cs +++ b/src/ImageSharp.Drawing/Paths/ShapeRegion.cs @@ -15,16 +15,12 @@ namespace SixLabors.ImageSharp.Drawing /// internal class ShapeRegion : Region { - private readonly MemoryManager memoryManager; - /// /// Initializes a new instance of the class. /// - /// The to use for buffer allocations. /// The shape. - public ShapeRegion(MemoryManager memoryManager, IPath shape) + public ShapeRegion(IPath shape) { - this.memoryManager = memoryManager; this.Shape = shape.AsClosedPath(); int left = (int)MathF.Floor(shape.Bounds.Left); int top = (int)MathF.Floor(shape.Bounds.Top); @@ -50,18 +46,17 @@ namespace SixLabors.ImageSharp.Drawing { var start = new PointF(this.Bounds.Left - 1, y); var end = new PointF(this.Bounds.Right + 1, y); - using (var innerBuffer = this.memoryManager.Allocate(buffer.Length)) - { - PointF[] array = innerBuffer.Array; - int count = this.Shape.FindIntersections(start, end, array, 0); - for (int i = 0; i < count; i++) - { - buffer[i + offset] = array[i].X; - } + // TODO: This is a temporal workaround because of the lack of Span API-s on IPath. We should use MemoryManager.Allocate() here! + PointF[] innerBuffer = new PointF[buffer.Length]; + int count = this.Shape.FindIntersections(start, end, innerBuffer, 0); - return count; + for (int i = 0; i < count; i++) + { + buffer[i + offset] = innerBuffer[i].X; } + + return count; } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Png/PngConfigurationModule.cs b/src/ImageSharp/Formats/Png/PngConfigurationModule.cs index abf5bc6bb9..ab6f31d49a 100644 --- a/src/ImageSharp/Formats/Png/PngConfigurationModule.cs +++ b/src/ImageSharp/Formats/Png/PngConfigurationModule.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// public void Configure(Configuration config) { - config.SetEncoder(ImageFormats.Png, new PngEncoder(config.MemoryManager)); + config.SetEncoder(ImageFormats.Png, new PngEncoder()); config.SetDecoder(ImageFormats.Png, new PngDecoder()); config.AddImageFormatDetector(new PngImageFormatDetector()); } diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 0546c5ee3d..45d6fa3a28 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -190,7 +190,7 @@ namespace SixLabors.ImageSharp.Formats.Png this.textEncoding = options.TextEncoding ?? PngConstants.DefaultEncoding; this.ignoreMetadata = options.IgnoreMetadata; } - + private MemoryManager MemoryManager => this.configuration.MemoryManager; /// @@ -441,7 +441,6 @@ namespace SixLabors.ImageSharp.Formats.Png this.scanline = this.configuration.MemoryManager.Allocate(this.bytesPerScanline, true); } - /// /// Calculates the correct number of bits per pixel for the given color type. /// diff --git a/src/ImageSharp/Formats/Png/PngEncoder.cs b/src/ImageSharp/Formats/Png/PngEncoder.cs index ee5651f2dd..0c40ccf2a0 100644 --- a/src/ImageSharp/Formats/Png/PngEncoder.cs +++ b/src/ImageSharp/Formats/Png/PngEncoder.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System.IO; - +using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Quantizers; @@ -14,17 +14,6 @@ namespace SixLabors.ImageSharp.Formats.Png /// public sealed class PngEncoder : IImageEncoder, IPngEncoderOptions { - private readonly MemoryManager memoryManager; - - /// - /// Initializes a new instance of the class. - /// - /// The to use for buffer allocations. - public PngEncoder(MemoryManager memoryManager) - { - this.memoryManager = memoryManager; - } - /// /// Gets or sets a value indicating whether the metadata should be ignored when the image is being encoded. /// @@ -79,7 +68,7 @@ namespace SixLabors.ImageSharp.Formats.Png public void Encode(Image image, Stream stream) where TPixel : struct, IPixel { - using (var encoder = new PngEncoderCore(this.memoryManager, this)) + using (var encoder = new PngEncoderCore(image.GetMemoryManager(), this)) { encoder.Encode(image, stream); } diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index e8e1726e9e..d531250898 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -508,7 +508,7 @@ namespace SixLabors.ImageSharp.Formats.Png if (this.quantizer == null) { - this.quantizer = new WuQuantizer(this.memoryManager); + this.quantizer = new WuQuantizer(); } // Quantize the image returning a palette. This boxing is icky. diff --git a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs index f5b4b71920..152d586afe 100644 --- a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. - using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; diff --git a/src/ImageSharp/Quantizers/Quantize.cs b/src/ImageSharp/Quantizers/Quantize.cs index 11494a867a..0e3d806dab 100644 --- a/src/ImageSharp/Quantizers/Quantize.cs +++ b/src/ImageSharp/Quantizers/Quantize.cs @@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp switch (mode) { case Quantization.Wu: - quantizer = new WuQuantizer(source.GetMemoryManager()); + quantizer = new WuQuantizer(); break; case Quantization.Palette: diff --git a/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs b/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs index 966ec60340..8f89c49611 100644 --- a/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs +++ b/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs @@ -36,8 +36,6 @@ namespace SixLabors.ImageSharp.Quantizers public class WuQuantizer : QuantizerBase where TPixel : struct, IPixel { - private readonly MemoryManager memoryManager; - /// /// The index bits. /// @@ -121,15 +119,13 @@ namespace SixLabors.ImageSharp.Quantizers /// /// Initializes a new instance of the class. /// - /// The to use for buffer allocations. /// /// The Wu quantizer is a two pass algorithm. The initial pass sets up the 3-D color histogram, /// the second pass quantizes a color based on the position in the histogram. /// - public WuQuantizer(MemoryManager memoryManager) + public WuQuantizer() : base(false) { - this.memoryManager = memoryManager; } /// @@ -141,15 +137,17 @@ namespace SixLabors.ImageSharp.Quantizers this.palette = null; this.colorMap.Clear(); + MemoryManager memoryManager = image.MemoryManager; + try { - this.vwt = this.memoryManager.Allocate(TableLength, true); - this.vmr = this.memoryManager.Allocate(TableLength, true); - this.vmg = this.memoryManager.Allocate(TableLength, true); - this.vmb = this.memoryManager.Allocate(TableLength, true); - this.vma = this.memoryManager.Allocate(TableLength, true); - this.m2 = this.memoryManager.Allocate(TableLength, true); - this.tag = this.memoryManager.Allocate(TableLength, true); + this.vwt = memoryManager.AllocateClean(TableLength); + this.vmr = memoryManager.AllocateClean(TableLength); + this.vmg = memoryManager.AllocateClean(TableLength); + this.vmb = memoryManager.AllocateClean(TableLength); + this.vma = memoryManager.AllocateClean(TableLength); + this.m2 = memoryManager.AllocateClean(TableLength); + this.tag = memoryManager.AllocateClean(TableLength); return base.Quantize(image, this.colors); } @@ -240,7 +238,7 @@ namespace SixLabors.ImageSharp.Quantizers } } - this.Get3DMoments(); + this.Get3DMoments(source.MemoryManager); this.BuildCube(); } @@ -458,21 +456,21 @@ namespace SixLabors.ImageSharp.Quantizers /// /// Converts the histogram into moments so that we can rapidly calculate the sums of the above quantities over any desired box. /// - private void Get3DMoments() + private void Get3DMoments(MemoryManager memoryManager) { - using (Buffer volume = this.memoryManager.Allocate(IndexCount * IndexAlphaCount)) - using (Buffer volumeR = this.memoryManager.Allocate(IndexCount * IndexAlphaCount)) - using (Buffer volumeG = this.memoryManager.Allocate(IndexCount * IndexAlphaCount)) - using (Buffer volumeB = this.memoryManager.Allocate(IndexCount * IndexAlphaCount)) - using (Buffer volumeA = this.memoryManager.Allocate(IndexCount * IndexAlphaCount)) - using (Buffer volume2 = this.memoryManager.Allocate(IndexCount * IndexAlphaCount)) - - using (Buffer area = this.memoryManager.Allocate(IndexAlphaCount)) - using (Buffer areaR = this.memoryManager.Allocate(IndexAlphaCount)) - using (Buffer areaG = this.memoryManager.Allocate(IndexAlphaCount)) - using (Buffer areaB = this.memoryManager.Allocate(IndexAlphaCount)) - using (Buffer areaA = this.memoryManager.Allocate(IndexAlphaCount)) - using (Buffer area2 = this.memoryManager.Allocate(IndexAlphaCount)) + using (Buffer volume = memoryManager.Allocate(IndexCount * IndexAlphaCount)) + using (Buffer volumeR = memoryManager.Allocate(IndexCount * IndexAlphaCount)) + using (Buffer volumeG = memoryManager.Allocate(IndexCount * IndexAlphaCount)) + using (Buffer volumeB = memoryManager.Allocate(IndexCount * IndexAlphaCount)) + using (Buffer volumeA = memoryManager.Allocate(IndexCount * IndexAlphaCount)) + using (Buffer volume2 = memoryManager.Allocate(IndexCount * IndexAlphaCount)) + + using (Buffer area = memoryManager.Allocate(IndexAlphaCount)) + using (Buffer areaR = memoryManager.Allocate(IndexAlphaCount)) + using (Buffer areaG = memoryManager.Allocate(IndexAlphaCount)) + using (Buffer areaB = memoryManager.Allocate(IndexAlphaCount)) + using (Buffer areaA = memoryManager.Allocate(IndexAlphaCount)) + using (Buffer area2 = memoryManager.Allocate(IndexAlphaCount)) { for (int r = 1; r < IndexCount; r++) { diff --git a/tests/ImageSharp.Benchmarks/Image/EncodeIndexedPng.cs b/tests/ImageSharp.Benchmarks/Image/EncodeIndexedPng.cs index 4ce5f5083a..e5eb295449 100644 --- a/tests/ImageSharp.Benchmarks/Image/EncodeIndexedPng.cs +++ b/tests/ImageSharp.Benchmarks/Image/EncodeIndexedPng.cs @@ -54,7 +54,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Image { using (MemoryStream memoryStream = new MemoryStream()) { - PngEncoder encoder = new PngEncoder(Configuration.Default.MemoryManager) { Quantizer = new OctreeQuantizer(), PaletteSize = 256 }; + PngEncoder encoder = new PngEncoder() { Quantizer = new OctreeQuantizer(), PaletteSize = 256 }; this.bmpCore.SaveAsPng(memoryStream, encoder); } @@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Image { using (MemoryStream memoryStream = new MemoryStream()) { - PngEncoder options = new PngEncoder(Configuration.Default.MemoryManager) { Quantizer = new OctreeQuantizer { Dither = false }, PaletteSize = 256 }; + PngEncoder options = new PngEncoder() { Quantizer = new OctreeQuantizer { Dither = false }, PaletteSize = 256 }; this.bmpCore.SaveAsPng(memoryStream, options); } @@ -76,7 +76,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Image { using (MemoryStream memoryStream = new MemoryStream()) { - PngEncoder options = new PngEncoder(Configuration.Default.MemoryManager) { Quantizer = new PaletteQuantizer(), PaletteSize = 256 }; + PngEncoder options = new PngEncoder() { Quantizer = new PaletteQuantizer(), PaletteSize = 256 }; this.bmpCore.SaveAsPng(memoryStream, options); } @@ -87,7 +87,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Image { using (MemoryStream memoryStream = new MemoryStream()) { - PngEncoder options = new PngEncoder(Configuration.Default.MemoryManager) { Quantizer = new PaletteQuantizer { Dither = false }, PaletteSize = 256 }; + PngEncoder options = new PngEncoder() { Quantizer = new PaletteQuantizer { Dither = false }, PaletteSize = 256 }; this.bmpCore.SaveAsPng(memoryStream, options); } @@ -98,7 +98,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Image { using (MemoryStream memoryStream = new MemoryStream()) { - PngEncoder options = new PngEncoder(Configuration.Default.MemoryManager) { Quantizer = new WuQuantizer(Configuration.Default.MemoryManager), PaletteSize = 256 }; + PngEncoder options = new PngEncoder() { Quantizer = new WuQuantizer(), PaletteSize = 256 }; this.bmpCore.SaveAsPng(memoryStream, options); } diff --git a/tests/ImageSharp.Benchmarks/Image/EncodePng.cs b/tests/ImageSharp.Benchmarks/Image/EncodePng.cs index 383505e0d1..53522a51f3 100644 --- a/tests/ImageSharp.Benchmarks/Image/EncodePng.cs +++ b/tests/ImageSharp.Benchmarks/Image/EncodePng.cs @@ -74,7 +74,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Image new OctreeQuantizer() : new PaletteQuantizer(); - var options = new PngEncoder(Configuration.Default.MemoryManager) { Quantizer = quantizer }; + var options = new PngEncoder() { Quantizer = quantizer }; this.bmpCore.SaveAsPng(memoryStream, options); } } diff --git a/tests/ImageSharp.Tests/Drawing/Paths/ShapeRegionTests.cs b/tests/ImageSharp.Tests/Drawing/Paths/ShapeRegionTests.cs index d3fcc5322e..5ca09ad1d2 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/ShapeRegionTests.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/ShapeRegionTests.cs @@ -1,131 +1,144 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.IO; -using System.Numerics; -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.Drawing; -using SixLabors.ImageSharp.Drawing.Brushes; -using SixLabors.ImageSharp.Drawing.Pens; -using SixLabors.ImageSharp.Drawing.Processors; -using SixLabors.ImageSharp.Processing; -using Moq; -using SixLabors.Primitives; -using SixLabors.Shapes; -using Xunit; - namespace SixLabors.ImageSharp.Tests.Drawing.Paths { - public class ShapeRegionTests + using System; + + using Moq; + + using SixLabors.ImageSharp.Drawing; + using SixLabors.Primitives; + using SixLabors.Shapes; + + using Xunit; + + public class ShapeRegionTests { private readonly Mock pathMock; - private readonly SixLabors.Primitives.RectangleF bounds; + + private readonly RectangleF bounds; public ShapeRegionTests() { this.pathMock = new Mock(); this.bounds = new RectangleF(10.5f, 10, 10, 10); - pathMock.Setup(x => x.Bounds).Returns(this.bounds); + this.pathMock.Setup(x => x.Bounds).Returns(this.bounds); // wire up the 2 mocks to reference eachother - pathMock.Setup(x => x.AsClosedPath()).Returns(() => pathMock.Object); + this.pathMock.Setup(x => x.AsClosedPath()).Returns(() => this.pathMock.Object); } [Fact] public void ShapeRegionWithPathCallsAsShape() { - new ShapeRegion(Configuration.Default.MemoryManager, pathMock.Object); + new ShapeRegion(this.pathMock.Object); - pathMock.Verify(x => x.AsClosedPath()); + this.pathMock.Verify(x => x.AsClosedPath()); } [Fact] public void ShapeRegionWithPathRetainsShape() { - ShapeRegion region = new ShapeRegion(Configuration.Default.MemoryManager, pathMock.Object); + ShapeRegion region = new ShapeRegion(this.pathMock.Object); - Assert.Equal(pathMock.Object, region.Shape); + Assert.Equal(this.pathMock.Object, region.Shape); } [Fact] public void ShapeRegionFromPathConvertsBoundsProxyToShape() { - ShapeRegion region = new ShapeRegion(Configuration.Default.MemoryManager, pathMock.Object); + ShapeRegion region = new ShapeRegion(this.pathMock.Object); - Assert.Equal(Math.Floor(bounds.Left), region.Bounds.Left); - Assert.Equal(Math.Ceiling(bounds.Right), region.Bounds.Right); + Assert.Equal(Math.Floor(this.bounds.Left), region.Bounds.Left); + Assert.Equal(Math.Ceiling(this.bounds.Right), region.Bounds.Right); - pathMock.Verify(x => x.Bounds); + this.pathMock.Verify(x => x.Bounds); } [Fact] public void ShapeRegionFromPathMaxIntersectionsProxyToShape() { - ShapeRegion region = new ShapeRegion(Configuration.Default.MemoryManager, pathMock.Object); + ShapeRegion region = new ShapeRegion(this.pathMock.Object); int i = region.MaxIntersections; - pathMock.Verify(x => x.MaxIntersections); + this.pathMock.Verify(x => x.MaxIntersections); } [Fact] public void ShapeRegionFromPathScanYProxyToShape() { int yToScan = 10; - ShapeRegion region = new ShapeRegion(Configuration.Default.MemoryManager, pathMock.Object); - - pathMock.Setup(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Callback((s, e, b, o) => { - Assert.Equal(yToScan, s.Y); - Assert.Equal(yToScan, e.Y); - Assert.True(s.X < bounds.Left); - Assert.True(e.X > bounds.Right); - }).Returns(0); + ShapeRegion region = new ShapeRegion(this.pathMock.Object); + + this.pathMock + .Setup( + x => x.FindIntersections( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())).Callback( + (s, e, b, o) => + { + Assert.Equal(yToScan, s.Y); + Assert.Equal(yToScan, e.Y); + Assert.True(s.X < this.bounds.Left); + Assert.True(e.X > this.bounds.Right); + }).Returns(0); int i = region.Scan(yToScan, new float[0], 0); - pathMock.Verify(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); + this.pathMock.Verify( + x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), + Times.Once); } [Fact] public void ShapeRegionFromShapeScanYProxyToShape() { int yToScan = 10; - ShapeRegion region = new ShapeRegion(Configuration.Default.MemoryManager, pathMock.Object); - - pathMock.Setup(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Callback((s, e, b, o) => { - Assert.Equal(yToScan, s.Y); - Assert.Equal(yToScan, e.Y); - Assert.True(s.X < bounds.Left); - Assert.True(e.X > bounds.Right); - }).Returns(0); + ShapeRegion region = new ShapeRegion(this.pathMock.Object); + + this.pathMock + .Setup( + x => x.FindIntersections( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())).Callback( + (s, e, b, o) => + { + Assert.Equal(yToScan, s.Y); + Assert.Equal(yToScan, e.Y); + Assert.True(s.X < this.bounds.Left); + Assert.True(e.X > this.bounds.Right); + }).Returns(0); int i = region.Scan(yToScan, new float[0], 0); - pathMock.Verify(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); + this.pathMock.Verify( + x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), + Times.Once); } [Fact] public void ShapeRegionFromShapeConvertsBoundsProxyToShape() { - ShapeRegion region = new ShapeRegion(Configuration.Default.MemoryManager, pathMock.Object); + ShapeRegion region = new ShapeRegion(this.pathMock.Object); - Assert.Equal(Math.Floor(bounds.Left), region.Bounds.Left); - Assert.Equal(Math.Ceiling(bounds.Right), region.Bounds.Right); + Assert.Equal(Math.Floor(this.bounds.Left), region.Bounds.Left); + Assert.Equal(Math.Ceiling(this.bounds.Right), region.Bounds.Right); - pathMock.Verify(x => x.Bounds); + this.pathMock.Verify(x => x.Bounds); } [Fact] public void ShapeRegionFromShapeMaxIntersectionsProxyToShape() { - ShapeRegion region = new ShapeRegion(Configuration.Default.MemoryManager, pathMock.Object); + ShapeRegion region = new ShapeRegion(this.pathMock.Object); int i = region.MaxIntersections; - pathMock.Verify(x => x.MaxIntersections); + this.pathMock.Verify(x => x.MaxIntersections); } } -} +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs index a6a883d421..22a811feee 100644 --- a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs +++ b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs @@ -14,9 +14,6 @@ namespace SixLabors.ImageSharp.Tests { using System; - using SixLabors.ImageSharp.Advanced; - using SixLabors.ImageSharp.Memory; - public class GeneralFormatTests : FileTestBase { [Theory] @@ -179,7 +176,7 @@ namespace SixLabors.ImageSharp.Tests { using (var memoryStream = new MemoryStream()) { - image.Save(memoryStream, GetEncoder(image.GetMemoryManager(), format)); + image.Save(memoryStream, GetEncoder(format)); memoryStream.Position = 0; var imageInfo = Image.Identify(memoryStream); @@ -190,12 +187,12 @@ namespace SixLabors.ImageSharp.Tests } } - private static IImageEncoder GetEncoder(MemoryManager memoryManager, string format) + private static IImageEncoder GetEncoder(string format) { switch (format) { case "png": - return new PngEncoder(memoryManager); + return new PngEncoder(); case "gif": return new GifEncoder(); case "bmp": diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index ba6c19e461..28f156279c 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -155,7 +155,7 @@ namespace SixLabors.ImageSharp.Tests using (Image image = provider.GetImage()) using (var ms = new MemoryStream()) { - image.Save(ms, new PngEncoder(Configuration.Default.MemoryManager)); + image.Save(ms, new PngEncoder()); byte[] data = ms.ToArray().Take(8).ToArray(); byte[] expected = { diff --git a/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs index 3a73867ba6..fc17df93d1 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs @@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png { // image.Save(provider.Utility.GetTestOutputFileName("bmp")); - image.Save(ms, new PngEncoder(Configuration.Default.MemoryManager)); + image.Save(ms, new PngEncoder()); ms.Position = 0; using (Image img2 = Image.Load(ms, new PngDecoder())) { @@ -110,7 +110,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png image.Mutate(x => x.Resize(100, 100)); // image.Save(provider.Utility.GetTestOutputFileName("png", "resize")); - image.Save(ms, new PngEncoder(Configuration.Default.MemoryManager)); + image.Save(ms, new PngEncoder()); ms.Position = 0; using (Image img2 = Image.Load(ms, new PngDecoder())) { diff --git a/tests/ImageSharp.Tests/Image/ImageTests.cs b/tests/ImageSharp.Tests/Image/ImageTests.cs index 45ecf60a06..da813f4280 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.cs @@ -103,7 +103,7 @@ namespace SixLabors.ImageSharp.Tests using (Image image = new Image(10, 10)) { - image.Save(file, new PngEncoder(Configuration.Default.MemoryManager)); + image.Save(file, new PngEncoder()); } using (Image img = Image.Load(file, out var mime)) { diff --git a/tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs b/tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs index 5d569aa8fb..18fd29237c 100644 --- a/tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs +++ b/tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs @@ -12,7 +12,7 @@ { var palette = new PaletteQuantizer(); var octree = new OctreeQuantizer(); - var wu = new WuQuantizer(Configuration.Default.MemoryManager); + var wu = new WuQuantizer(); Assert.True(palette.Dither); Assert.True(octree.Dither); @@ -73,7 +73,7 @@ { Assert.True(image[0, 0].Equals(default(TPixel))); - IQuantizer quantizer = new WuQuantizer(Configuration.Default.MemoryManager) { Dither = dither }; + IQuantizer quantizer = new WuQuantizer() { Dither = dither }; foreach (ImageFrame frame in image.Frames) { diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceCodecTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceCodecTests.cs index b454f16085..dde34fcc43 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceCodecTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceCodecTests.cs @@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.Tests sourceImage.Mutate(c => c.Opacity(1)); } - var encoder = new PngEncoder(Configuration.Default.MemoryManager) { PngColorType = pngColorType }; + var encoder = new PngEncoder() { PngColorType = pngColorType }; return provider.Utility.SaveTestOutputFile(sourceImage, "png", encoder); } } From 6a2bd3d617a70d4c30d8846e46be9549efa08c4d Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 19 Feb 2018 18:03:35 +0100 Subject: [PATCH 063/151] introducing FakeBuffer workaround --- .../Processors/FillRegionProcessor.cs | 4 +- .../Memory/ArrayPoolMemoryManager.cs | 6 +- src/ImageSharp/Memory/FakeBuffer.cs | 64 +++++++++++++++++++ src/ImageSharp/Memory/MemoryManager.cs | 16 ++++- .../Memory/SimpleManagedMemoryManager.cs | 4 +- 5 files changed, 84 insertions(+), 10 deletions(-) create mode 100644 src/ImageSharp/Memory/FakeBuffer.cs diff --git a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs index d5bc401074..fc3f289abf 100644 --- a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs @@ -99,8 +99,8 @@ namespace SixLabors.ImageSharp.Drawing.Processors using (BrushApplicator applicator = this.Brush.CreateApplicator(source, rect, this.Options)) { int scanlineWidth = maxX - minX; - using (var buffer = source.MemoryManager.Allocate(maxIntersections)) - using (var scanline = source.MemoryManager.Allocate(scanlineWidth)) + using (FakeBuffer buffer = source.MemoryManager.AllocateFake(maxIntersections)) + using (FakeBuffer scanline = source.MemoryManager.AllocateFake(scanlineWidth)) { bool scanlineDirty = true; for (int y = minY; y < maxY; y++) diff --git a/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs b/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs index 86776fd358..0cb1e38f8a 100644 --- a/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs +++ b/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs @@ -38,13 +38,13 @@ namespace SixLabors.ImageSharp.Memory } /// - internal override Buffer Allocate(int itemCount, bool clear) + internal override Buffer Allocate(int length, bool clear) { int itemSizeBytes = Unsafe.SizeOf(); - int bufferSizeInBytes = itemCount * itemSizeBytes; + int bufferSizeInBytes = length * itemSizeBytes; byte[] byteBuffer = this.pool.Rent(bufferSizeInBytes); - var buffer = new Buffer(Unsafe.As(byteBuffer), itemCount, this); + var buffer = new Buffer(Unsafe.As(byteBuffer), length, this); if (clear) { buffer.Clear(); diff --git a/src/ImageSharp/Memory/FakeBuffer.cs b/src/ImageSharp/Memory/FakeBuffer.cs new file mode 100644 index 0000000000..e4bc4e463c --- /dev/null +++ b/src/ImageSharp/Memory/FakeBuffer.cs @@ -0,0 +1,64 @@ +using System; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.Memory +{ + /// + /// Temporal workaround providing a "Buffer" based on a generic array without the 'Unsafe.As()' hackery. + /// + internal class FakeBuffer : IBuffer + where T : struct + { + public FakeBuffer(T[] array) + { + this.Array = array; + } + + public T[] Array { get; } + + public Span Span => this.Array; + + public int Length => this.Array.Length; + + /// + /// Returns a reference to specified element of the buffer. + /// + /// The index + /// The reference to the specified element + public ref T this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + DebugGuard.MustBeLessThan(index, this.Length, nameof(index)); + + Span span = this.Span; + return ref span[index]; + } + } + + /// + /// Converts to an . + /// + /// The to convert. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlySpan(FakeBuffer buffer) + { + return new ReadOnlySpan(buffer.Array, 0, buffer.Length); + } + + /// + /// Converts to an . + /// + /// The to convert. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Span(FakeBuffer buffer) + { + return new Span(buffer.Array, 0, buffer.Length); + } + + public void Dispose() + { + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Memory/MemoryManager.cs b/src/ImageSharp/Memory/MemoryManager.cs index 6be7012e6a..6bad01cea3 100644 --- a/src/ImageSharp/Memory/MemoryManager.cs +++ b/src/ImageSharp/Memory/MemoryManager.cs @@ -12,14 +12,14 @@ namespace SixLabors.ImageSharp.Memory public abstract class MemoryManager { /// - /// Allocates a of size , optionally + /// Allocates a of size , optionally /// clearing the buffer before it gets returned. /// /// Type of the data stored in the buffer - /// Size of the buffer to allocate + /// Size of the buffer to allocate /// True to clear the backing memory of the buffer /// A buffer of values of type . - internal abstract Buffer Allocate(int size, bool clear) + internal abstract Buffer Allocate(int length, bool clear) where T : struct; /// @@ -30,5 +30,15 @@ namespace SixLabors.ImageSharp.Memory /// The buffer to release internal abstract void Release(Buffer buffer) where T : struct; + + /// + /// Temporal workaround. A method providing a "Buffer" based on a generic array without the 'Unsafe.As()' hackery. + /// Should be replaced with 'Allocate()' as soon as SixLabors.Shapes has Span-based API-s! + /// + internal FakeBuffer AllocateFake(int length) + where T : struct + { + return new FakeBuffer(new T[length]); + } } } diff --git a/src/ImageSharp/Memory/SimpleManagedMemoryManager.cs b/src/ImageSharp/Memory/SimpleManagedMemoryManager.cs index 2aefe898fd..7a92d6c9fb 100644 --- a/src/ImageSharp/Memory/SimpleManagedMemoryManager.cs +++ b/src/ImageSharp/Memory/SimpleManagedMemoryManager.cs @@ -6,9 +6,9 @@ public class SimpleManagedMemoryManager : MemoryManager { /// - internal override Buffer Allocate(int size, bool clear) + internal override Buffer Allocate(int length, bool clear) { - return new Buffer(new T[size], size); + return new Buffer(new T[length], length); } /// From d8ebe2f03d16895dd23c4489f3c60beaef8654d8 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 19 Feb 2018 19:00:34 +0100 Subject: [PATCH 064/151] IManagedByteBuffer --- src/ImageSharp/Memory/ArrayPoolMemoryManager.cs | 12 ++++++++++++ src/ImageSharp/Memory/IManagedByteBuffer.cs | 13 +++++++++++++ src/ImageSharp/Memory/ManagedByteBuffer.cs | 10 ++++++++++ src/ImageSharp/Memory/MemoryManager.cs | 2 ++ .../Memory/MemoryManagerExtensions.cs | 17 +++++++++++------ .../Memory/SimpleManagedMemoryManager.cs | 5 +++++ 6 files changed, 53 insertions(+), 6 deletions(-) create mode 100644 src/ImageSharp/Memory/IManagedByteBuffer.cs create mode 100644 src/ImageSharp/Memory/ManagedByteBuffer.cs diff --git a/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs b/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs index 0cb1e38f8a..4034643345 100644 --- a/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs +++ b/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs @@ -53,6 +53,18 @@ namespace SixLabors.ImageSharp.Memory return buffer; } + internal override IManagedByteBuffer AllocateManagedByteBuffer(int length, bool clear) + { + byte[] array = this.pool.Rent(length); + var buffer = new ManagedByteBuffer(array, length, this); + if (clear) + { + buffer.Clear(); + } + + return buffer; + } + /// internal override void Release(Buffer buffer) { diff --git a/src/ImageSharp/Memory/IManagedByteBuffer.cs b/src/ImageSharp/Memory/IManagedByteBuffer.cs new file mode 100644 index 0000000000..541957f422 --- /dev/null +++ b/src/ImageSharp/Memory/IManagedByteBuffer.cs @@ -0,0 +1,13 @@ +namespace SixLabors.ImageSharp.Memory +{ + /// + /// Represents a byte buffer backed by a managed array. + /// + internal interface IManagedByteBuffer : IBuffer + { + /// + /// Gets the managed array backing this buffer instance. + /// + byte[] Array { get; } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Memory/ManagedByteBuffer.cs b/src/ImageSharp/Memory/ManagedByteBuffer.cs new file mode 100644 index 0000000000..17fe945d61 --- /dev/null +++ b/src/ImageSharp/Memory/ManagedByteBuffer.cs @@ -0,0 +1,10 @@ +namespace SixLabors.ImageSharp.Memory +{ + internal class ManagedByteBuffer : Buffer, IManagedByteBuffer + { + internal ManagedByteBuffer(byte[] array, int length, MemoryManager memoryManager) + : base(array, length, memoryManager) + { + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Memory/MemoryManager.cs b/src/ImageSharp/Memory/MemoryManager.cs index 6bad01cea3..cac9b785b8 100644 --- a/src/ImageSharp/Memory/MemoryManager.cs +++ b/src/ImageSharp/Memory/MemoryManager.cs @@ -22,6 +22,8 @@ namespace SixLabors.ImageSharp.Memory internal abstract Buffer Allocate(int length, bool clear) where T : struct; + internal abstract IManagedByteBuffer AllocateManagedByteBuffer(int length, bool clear); + /// /// Releases the memory allocated for . After this, the buffer /// is no longer usable. diff --git a/src/ImageSharp/Memory/MemoryManagerExtensions.cs b/src/ImageSharp/Memory/MemoryManagerExtensions.cs index 8e1aa9850f..8772307885 100644 --- a/src/ImageSharp/Memory/MemoryManagerExtensions.cs +++ b/src/ImageSharp/Memory/MemoryManagerExtensions.cs @@ -6,24 +6,29 @@ internal static class MemoryManagerExtensions { /// - /// Allocates a of size . + /// Allocates a of size . /// Note: Depending on the implementation, the buffer may not cleared before /// returning, so it may contain data from an earlier use. /// /// Type of the data stored in the buffer /// The - /// Size of the buffer to allocate + /// Size of the buffer to allocate /// A buffer of values of type . - public static Buffer Allocate(this MemoryManager memoryManager, int size) + public static Buffer Allocate(this MemoryManager memoryManager, int length) where T : struct { - return memoryManager.Allocate(size, false); + return memoryManager.Allocate(length, false); } - public static Buffer AllocateClean(this MemoryManager memoryManager, int size) + public static Buffer AllocateClean(this MemoryManager memoryManager, int length) where T : struct { - return memoryManager.Allocate(size, true); + return memoryManager.Allocate(length, true); + } + + public static IManagedByteBuffer AllocateCleanManagedByteBuffer(this MemoryManager memoryManager, int length) + { + return memoryManager.AllocateManagedByteBuffer(length, true); } public static Buffer2D Allocate2D(this MemoryManager memoryManager, int width, int height, bool clear) diff --git a/src/ImageSharp/Memory/SimpleManagedMemoryManager.cs b/src/ImageSharp/Memory/SimpleManagedMemoryManager.cs index 7a92d6c9fb..12d8582c79 100644 --- a/src/ImageSharp/Memory/SimpleManagedMemoryManager.cs +++ b/src/ImageSharp/Memory/SimpleManagedMemoryManager.cs @@ -11,6 +11,11 @@ return new Buffer(new T[length], length); } + internal override IManagedByteBuffer AllocateManagedByteBuffer(int length, bool clear) + { + return new ManagedByteBuffer(new byte[length], length, this); + } + /// internal override void Release(Buffer buffer) { From 17018555c530a567fa80be15847284f00f3d5d82 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 19 Feb 2018 21:19:12 +0100 Subject: [PATCH 065/151] hide Buffer.Array, use IManagedByteBuffer when necessary --- .../CieXyChromaticityCoordinates.cs | 4 +- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 10 +- src/ImageSharp/Formats/Gif/GifDecoderCore.cs | 21 +- src/ImageSharp/Formats/Gif/GifEncoderCore.cs | 10 +- .../GolangPort/Components/Decoder/Bytes.cs | 19 +- .../PdfJsPort/Components/PdfJsHuffmanTable.cs | 21 +- .../Jpeg/PdfJsPort/Components/PdfJsIDCT.cs | 2 + .../Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs | 14 +- src/ImageSharp/Formats/Png/PngChunk.cs | 2 +- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 71 ++-- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 71 ++-- src/ImageSharp/Image/Image.Decode.cs | 4 +- src/ImageSharp/Image/PixelAccessor{TPixel}.cs | 12 +- src/ImageSharp/Image/PixelArea{TPixel}.cs | 41 +- .../Memory/ArrayPoolMemoryManager.cs | 2 +- src/ImageSharp/Memory/Buffer2DExtensions.cs | 1 + src/ImageSharp/Memory/Buffer2D{T}.cs | 4 +- src/ImageSharp/Memory/BufferExtensions.cs | 54 +++ src/ImageSharp/Memory/Buffer{T}.cs | 87 ++--- src/ImageSharp/Memory/ManagedByteBuffer.cs | 2 + src/ImageSharp/Memory/MemoryManager.cs | 2 +- .../Memory/MemoryManagerExtensions.cs | 5 + .../Quantizers/WuQuantizer{TPixel}.cs | 56 +-- .../Color/Bulk/PackFromVector4.cs | 7 +- .../Bulk/PackFromVector4ReferenceVsPointer.cs | 4 +- .../Color/Bulk/PackFromXyzw.cs | 8 +- .../Color/Bulk/ToVector4.cs | 5 +- .../ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs | 8 +- .../Color/Bulk/ToXyzw.cs | 5 +- .../General/ClearBuffer.cs | 42 -- .../General/PixelIndexing.cs | 362 ------------------ .../ImageSharp.Tests/Memory/Buffer2DTests.cs | 7 +- tests/ImageSharp.Tests/Memory/BufferTests.cs | 251 ------------ .../Memory/SpanUtilityTests.cs | 248 +----------- .../PixelFormats/PixelOperationsTests.cs | 10 +- 35 files changed, 322 insertions(+), 1150 deletions(-) create mode 100644 src/ImageSharp/Memory/BufferExtensions.cs delete mode 100644 tests/ImageSharp.Benchmarks/General/ClearBuffer.cs delete mode 100644 tests/ImageSharp.Benchmarks/General/PixelIndexing.cs delete mode 100644 tests/ImageSharp.Tests/Memory/BufferTests.cs diff --git a/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs b/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs index d9767d45ea..92687a5630 100644 --- a/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs +++ b/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs @@ -5,6 +5,7 @@ using System; using System.ComponentModel; using System.Numerics; using System.Runtime.CompilerServices; +// ReSharper disable CompareOfFloatsByEqualityOperator namespace SixLabors.ImageSharp.ColorSpaces { @@ -143,7 +144,8 @@ namespace SixLabors.ImageSharp.ColorSpaces [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(CieXyChromaticityCoordinates other) { - return this.backingVector.Equals(other.backingVector); + // The memberwise comparison here is a workaround for https://github.com/dotnet/coreclr/issues/16443 + return this.X == other.X && this.Y == other.Y; } /// diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index b4db7527d0..201c041a24 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -343,15 +343,17 @@ namespace SixLabors.ImageSharp.Formats.Bmp padding = 4 - padding; } - using (var row = this.configuration.MemoryManager.Allocate(arrayWidth + padding, true)) + using (IManagedByteBuffer row = this.configuration.MemoryManager.AllocateManagedByteBuffer(arrayWidth + padding, true)) { var color = default(TPixel); var rgba = new Rgba32(0, 0, 0, 255); + Span rowSpan = row.Span; + for (int y = 0; y < height; y++) { int newY = Invert(y, height, inverted); - this.currentStream.Read(row.Array, 0, row.Length); + this.currentStream.Read(row.Array, 0, row.Length()); int offset = 0; Span pixelRow = pixels.GetRowSpan(newY); @@ -362,7 +364,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp for (int shift = 0; shift < ppb && (x + shift) < width; shift++) { - int colorIndex = ((row[offset] >> (8 - bits - (shift * bits))) & mask) * 4; + int colorIndex = ((rowSpan[offset] >> (8 - bits - (shift * bits))) & mask) * 4; int newX = colOffset + shift; // Stored in b-> g-> r order. @@ -393,7 +395,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp var color = default(TPixel); var rgba = new Rgba32(0, 0, 0, 255); - using (var buffer = this.configuration.MemoryManager.Allocate(stride)) + using (var buffer = this.configuration.MemoryManager.AllocateManagedByteBuffer(stride)) { for (int y = 0; y < height; y++) { diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index c120c9e113..c35d506dfe 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.Formats.Gif /// /// The global color table. /// - private Buffer globalColorTable; + private IManagedByteBuffer globalColorTable; /// /// The global color table length @@ -151,7 +151,7 @@ namespace SixLabors.ImageSharp.Formats.Gif break; } - this.globalColorTable = this.MemoryManager.Allocate(this.globalColorTableLength, true); + this.globalColorTable = this.MemoryManager.AllocateManagedByteBuffer(this.globalColorTableLength, true); nextFlag = stream.ReadByte(); if (nextFlag == -1) @@ -337,7 +337,7 @@ namespace SixLabors.ImageSharp.Formats.Gif continue; } - using (Buffer commentsBuffer = this.MemoryManager.Allocate(length)) + using (IManagedByteBuffer commentsBuffer = this.MemoryManager.AllocateManagedByteBuffer(length)) { this.currentStream.Read(commentsBuffer.Array, 0, length); string comments = this.TextEncoding.GetString(commentsBuffer.Array, 0, length); @@ -357,22 +357,23 @@ namespace SixLabors.ImageSharp.Formats.Gif { GifImageDescriptor imageDescriptor = this.ReadImageDescriptor(); - Buffer localColorTable = null; - Buffer indices = null; + IManagedByteBuffer localColorTable = null; + IManagedByteBuffer indices = null; try { // Determine the color table for this frame. If there is a local one, use it otherwise use the global color table. if (imageDescriptor.LocalColorTableFlag) { int length = imageDescriptor.LocalColorTableSize * 3; - localColorTable = this.configuration.MemoryManager.Allocate(length, true); + localColorTable = this.configuration.MemoryManager.AllocateManagedByteBuffer(length, true); this.currentStream.Read(localColorTable.Array, 0, length); } - indices = this.configuration.MemoryManager.Allocate(imageDescriptor.Width * imageDescriptor.Height, true); + indices = this.configuration.MemoryManager.AllocateManagedByteBuffer(imageDescriptor.Width * imageDescriptor.Height, true); - this.ReadFrameIndices(imageDescriptor, indices); - this.ReadFrameColors(ref image, ref previousFrame, indices, localColorTable ?? this.globalColorTable, imageDescriptor); + this.ReadFrameIndices(imageDescriptor, indices.Span); + IManagedByteBuffer colorTable = localColorTable ?? this.globalColorTable; + this.ReadFrameColors(ref image, ref previousFrame, indices.Span, colorTable.Span, imageDescriptor); // Skip any remaining blocks this.Skip(0); @@ -605,7 +606,7 @@ namespace SixLabors.ImageSharp.Formats.Gif { this.globalColorTableLength = this.logicalScreenDescriptor.GlobalColorTableSize * 3; - this.globalColorTable = this.MemoryManager.Allocate(this.globalColorTableLength, true); + this.globalColorTable = this.MemoryManager.AllocateManagedByteBuffer(this.globalColorTableLength, true); // Read the global color table from the stream stream.Read(this.globalColorTable.Array, 0, this.globalColorTableLength); diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs index 43d48605c4..13ca5f2c61 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs @@ -356,15 +356,17 @@ namespace SixLabors.ImageSharp.Formats.Gif // Get max colors for bit depth. int colorTableLength = (int)Math.Pow(2, this.bitDepth) * 3; var rgb = default(Rgb24); - using (Buffer colorTable = this.memoryManager.Allocate(colorTableLength)) + using (IManagedByteBuffer colorTable = this.memoryManager.AllocateManagedByteBuffer(colorTableLength)) { + Span colorTableSpan = colorTable.Span; + for (int i = 0; i < pixelCount; i++) { int offset = i * 3; image.Palette[i].ToRgb24(ref rgb); - colorTable[offset] = rgb.R; - colorTable[offset + 1] = rgb.G; - colorTable[offset + 2] = rgb.B; + colorTableSpan[offset] = rgb.R; + colorTableSpan[offset + 1] = rgb.G; + colorTableSpan[offset + 2] = rgb.B; } writer.Write(colorTable.Array, 0, colorTableLength); diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bytes.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bytes.cs index 7c1cd72061..56a85bc9df 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bytes.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bytes.cs @@ -26,8 +26,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder /// Gets or sets the buffer. /// buffer[i:j] are the buffered bytes read from the underlying /// stream that haven't yet been passed further on. + /// TODO: Do we really need buffer here? Might be an optimiziation opportunity. /// - public Buffer Buffer; + public IManagedByteBuffer Buffer; /// /// Values of converted to -s @@ -59,7 +60,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder { return new Bytes { - Buffer = memoryManager.Allocate(BufferSize), + Buffer = memoryManager.AllocateManagedByteBuffer(BufferSize), BufferAsInt = memoryManager.Allocate(BufferSize) }; } @@ -169,7 +170,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder } } - result = this.Buffer[this.I]; + result = this.Buffer.Span[this.I]; this.I++; this.UnreadableBytes = 0; return errorCode; @@ -229,18 +230,20 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder DecoderThrowHelper.ThrowImageFormatException.FillCalledWhenUnreadBytesExist(); } + Span bufferSpan = this.Buffer.Span; + // Move the last 2 bytes to the start of the buffer, in case we need // to call UnreadByteStuffedByte. if (this.J > 2) { - this.Buffer[0] = this.Buffer[this.J - 2]; - this.Buffer[1] = this.Buffer[this.J - 1]; + bufferSpan[0] = bufferSpan[this.J - 2]; + bufferSpan[1] = bufferSpan[this.J - 1]; this.I = 2; this.J = 2; } // Fill in the rest of the buffer. - int n = inputStream.Read(this.Buffer.Array, this.J, this.Buffer.Length - this.J); + int n = inputStream.Read(this.Buffer.Array, this.J, bufferSpan.Length - this.J); if (n == 0) { return OrigDecoderErrorCode.UnexpectedEndOfStream; @@ -248,9 +251,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder this.J += n; - for (int i = 0; i < this.Buffer.Length; i++) + for (int i = 0; i < bufferSpan.Length; i++) { - this.BufferAsInt[i] = this.Buffer[i]; + this.BufferAsInt[i] = bufferSpan[i]; } return OrigDecoderErrorCode.NoError; diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs index f1beab114a..95631a7e66 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs @@ -12,10 +12,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components /// internal struct PdfJsHuffmanTable : IDisposable { - private Buffer lookahead; - private Buffer valOffset; - private Buffer maxcode; - private Buffer huffval; + private FakeBuffer lookahead; + private FakeBuffer valOffset; + private FakeBuffer maxcode; + private IManagedByteBuffer huffval; /// /// Initializes a new instance of the struct. @@ -25,12 +25,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components /// The huffman values public PdfJsHuffmanTable(MemoryManager memoryManager, byte[] lengths, byte[] values) { - this.lookahead = memoryManager.Allocate(256, true); - this.valOffset = memoryManager.Allocate(18, true); - this.maxcode = memoryManager.Allocate(18, true); + // TODO: Replace FakeBuffer usages with standard or array orfixed-sized arrays + this.lookahead = memoryManager.AllocateFake(256); + this.valOffset = memoryManager.AllocateFake(18); + this.maxcode = memoryManager.AllocateFake(18); - using (var huffsize = memoryManager.Allocate(257, true)) - using (var huffcode = memoryManager.Allocate(257, true)) + using (FakeBuffer huffsize = memoryManager.AllocateFake(257)) + using (FakeBuffer huffcode = memoryManager.AllocateFake(257)) { GenerateSizeTable(lengths, huffsize); GenerateCodeTable(huffsize, huffcode); @@ -38,7 +39,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components GenerateLookaheadTables(lengths, values, this.lookahead); } - this.huffval = memoryManager.Allocate(values.Length, true); + this.huffval = memoryManager.AllocateManagedByteBuffer(values.Length, true); Buffer.BlockCopy(values, 0, this.huffval.Array, 0, values.Length); this.MaxCode = this.maxcode.Array; diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsIDCT.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsIDCT.cs index 49bdc2423e..f2e269f6c2 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsIDCT.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsIDCT.cs @@ -6,6 +6,8 @@ using System.Runtime.CompilerServices; namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components { + using SixLabors.ImageSharp.Memory; + /// /// Performs the inverse Descrete Cosine Transform on each frame component. /// diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs index 863c4380bf..f05a8a136d 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs @@ -673,23 +673,25 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort throw new ImageFormatException($"DHT has wrong length: {remaining}"); } - using (var huffmanData = this.configuration.MemoryManager.Allocate(256, true)) + using (IManagedByteBuffer huffmanData = this.configuration.MemoryManager.AllocateCleanManagedByteBuffer(256)) { + Span huffmanSpan = huffmanData.Span; for (int i = 2; i < remaining;) { byte huffmanTableSpec = (byte)this.InputStream.ReadByte(); this.InputStream.Read(huffmanData.Array, 0, 16); - using (var codeLengths = this.configuration.MemoryManager.Allocate(17, true)) + using (IManagedByteBuffer codeLengths = this.configuration.MemoryManager.AllocateCleanManagedByteBuffer(17)) { + Span codeLengthsSpan = codeLengths.Span; int codeLengthSum = 0; for (int j = 1; j < 17; j++) { - codeLengthSum += codeLengths[j] = huffmanData[j - 1]; + codeLengthSum += codeLengthsSpan[j] = huffmanSpan[j - 1]; } - using (var huffmanValues = this.configuration.MemoryManager.Allocate(256, true)) + using (IManagedByteBuffer huffmanValues = this.configuration.MemoryManager.AllocateCleanManagedByteBuffer(256)) { this.InputStream.Read(huffmanValues.Array, 0, codeLengthSum); @@ -784,8 +786,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort { int blocksPerLine = component.BlocksPerLine; int blocksPerColumn = component.BlocksPerColumn; - using (var computationBuffer = this.configuration.MemoryManager.Allocate(64, true)) - using (var multiplicationBuffer = this.configuration.MemoryManager.Allocate(64, true)) + using (Buffer computationBuffer = this.configuration.MemoryManager.Allocate(64, true)) + using (Buffer multiplicationBuffer = this.configuration.MemoryManager.Allocate(64, true)) { Span quantizationTable = this.quantizationTables.Tables.GetRowSpan(frameComponent.QuantizationTableIndex); Span computationBufferSpan = computationBuffer; diff --git a/src/ImageSharp/Formats/Png/PngChunk.cs b/src/ImageSharp/Formats/Png/PngChunk.cs index 7412fdfcd3..2483a3ad9d 100644 --- a/src/ImageSharp/Formats/Png/PngChunk.cs +++ b/src/ImageSharp/Formats/Png/PngChunk.cs @@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// Gets or sets the data bytes appropriate to the chunk type, if any. /// This field can be of zero length. /// - public Buffer Data { get; set; } + public IManagedByteBuffer Data { get; set; } /// /// Gets or sets a CRC (Cyclic Redundancy Check) calculated on the preceding bytes in the chunk, diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 45d6fa3a28..fbff0ae1d9 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -137,12 +137,12 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// Previous scanline processed /// - private Buffer previousScanline; + private IManagedByteBuffer previousScanline; /// /// The current scanline that is being processed /// - private Buffer scanline; + private IManagedByteBuffer scanline; /// /// The index of the current scanline being processed @@ -437,8 +437,8 @@ namespace SixLabors.ImageSharp.Formats.Png this.bytesPerSample = this.header.BitDepth / 8; } - this.previousScanline = this.MemoryManager.Allocate(this.bytesPerScanline, true); - this.scanline = this.configuration.MemoryManager.Allocate(this.bytesPerScanline, true); + this.previousScanline = this.MemoryManager.AllocateCleanManagedByteBuffer(this.bytesPerScanline); + this.scanline = this.configuration.MemoryManager.AllocateCleanManagedByteBuffer(this.bytesPerScanline); } /// @@ -558,7 +558,8 @@ namespace SixLabors.ImageSharp.Formats.Png } this.currentRowBytesRead = 0; - var filterType = (FilterType)this.scanline[0]; + Span scanlineSpan = this.scanline.Span; + var filterType = (FilterType)scanlineSpan[0]; switch (filterType) { @@ -567,22 +568,22 @@ namespace SixLabors.ImageSharp.Formats.Png case FilterType.Sub: - SubFilter.Decode(this.scanline, this.bytesPerPixel); + SubFilter.Decode(scanlineSpan, this.bytesPerPixel); break; case FilterType.Up: - UpFilter.Decode(this.scanline, this.previousScanline); + UpFilter.Decode(scanlineSpan, this.previousScanline.Span); break; case FilterType.Average: - AverageFilter.Decode(this.scanline, this.previousScanline, this.bytesPerPixel); + AverageFilter.Decode(scanlineSpan, this.previousScanline.Span, this.bytesPerPixel); break; case FilterType.Paeth: - PaethFilter.Decode(this.scanline, this.previousScanline, this.bytesPerPixel); + PaethFilter.Decode(scanlineSpan, this.previousScanline.Span, this.bytesPerPixel); break; default: @@ -753,11 +754,11 @@ namespace SixLabors.ImageSharp.Formats.Png if (this.header.BitDepth == 16) { int length = this.header.Width * 3; - using (var compressed = this.configuration.MemoryManager.Allocate(length)) + using (IBuffer compressed = this.configuration.MemoryManager.Allocate(length)) { // TODO: Should we use pack from vector here instead? - this.From16BitTo8Bit(scanlineBuffer, compressed, length); - PixelOperations.Instance.PackFromRgb24Bytes(compressed, rowSpan, this.header.Width); + this.From16BitTo8Bit(scanlineBuffer, compressed.Span, length); + PixelOperations.Instance.PackFromRgb24Bytes(compressed.Span, rowSpan, this.header.Width); } } else @@ -770,10 +771,10 @@ namespace SixLabors.ImageSharp.Formats.Png if (this.header.BitDepth == 16) { int length = this.header.Width * 3; - using (var compressed = this.configuration.MemoryManager.Allocate(length)) + using (IBuffer compressed = this.configuration.MemoryManager.Allocate(length)) { // TODO: Should we use pack from vector here instead? - this.From16BitTo8Bit(scanlineBuffer, compressed, length); + this.From16BitTo8Bit(scanlineBuffer, compressed.Span, length); Span rgb24Span = compressed.Span.NonPortableCast(); for (int x = 0; x < this.header.Width; x++) @@ -811,11 +812,11 @@ namespace SixLabors.ImageSharp.Formats.Png if (this.header.BitDepth == 16) { int length = this.header.Width * 4; - using (var compressed = this.configuration.MemoryManager.Allocate(length)) + using (IBuffer compressed = this.configuration.MemoryManager.Allocate(length)) { // TODO: Should we use pack from vector here instead? - this.From16BitTo8Bit(scanlineBuffer, compressed, length); - PixelOperations.Instance.PackFromRgba32Bytes(compressed, rowSpan, this.header.Width); + this.From16BitTo8Bit(scanlineBuffer, compressed.Span, length); + PixelOperations.Instance.PackFromRgba32Bytes(compressed.Span, rowSpan, this.header.Width); } } else @@ -1014,18 +1015,20 @@ namespace SixLabors.ImageSharp.Formats.Png if (this.header.BitDepth == 16) { int length = this.header.Width * 3; - using (var compressed = this.configuration.MemoryManager.Allocate(length)) + using (IBuffer compressed = this.configuration.MemoryManager.Allocate(length)) { + Span compressedSpan = compressed.Span; + // TODO: Should we use pack from vector here instead? - this.From16BitTo8Bit(scanlineBuffer, compressed, length); + this.From16BitTo8Bit(scanlineBuffer, compressedSpan, length); if (this.hasTrans) { for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += 3) { - rgba.R = compressed[o]; - rgba.G = compressed[o + 1]; - rgba.B = compressed[o + 2]; + rgba.R = compressedSpan[o]; + rgba.G = compressedSpan[o + 1]; + rgba.B = compressedSpan[o + 2]; rgba.A = (byte)(this.rgb24Trans.Equals(rgba.Rgb) ? 0 : 255); color.PackFromRgba32(rgba); @@ -1036,9 +1039,9 @@ namespace SixLabors.ImageSharp.Formats.Png { for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += 3) { - rgba.R = compressed[o]; - rgba.G = compressed[o + 1]; - rgba.B = compressed[o + 2]; + rgba.R = compressedSpan[o]; + rgba.G = compressedSpan[o + 1]; + rgba.B = compressedSpan[o + 2]; color.PackFromRgba32(rgba); rowSpan[x] = color; @@ -1082,16 +1085,18 @@ namespace SixLabors.ImageSharp.Formats.Png if (this.header.BitDepth == 16) { int length = this.header.Width * 4; - using (var compressed = this.configuration.MemoryManager.Allocate(length)) + using (IBuffer compressed = this.configuration.MemoryManager.Allocate(length)) { + Span compressedSpan = compressed.Span; + // TODO: Should we use pack from vector here instead? - this.From16BitTo8Bit(scanlineBuffer, compressed, length); + this.From16BitTo8Bit(scanlineBuffer, compressedSpan, length); for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += 4) { - rgba.R = compressed[o]; - rgba.G = compressed[o + 1]; - rgba.B = compressed[o + 2]; - rgba.A = compressed[o + 3]; + rgba.R = compressedSpan[o]; + rgba.G = compressedSpan[o + 1]; + rgba.B = compressedSpan[o + 2]; + rgba.A = compressedSpan[o + 3]; color.PackFromRgba32(rgba); rowSpan[x] = color; @@ -1281,7 +1286,7 @@ namespace SixLabors.ImageSharp.Formats.Png private void ReadChunkData(PngChunk chunk) { // We rent the buffer here to return it afterwards in Decode() - chunk.Data = this.configuration.MemoryManager.Allocate(chunk.Length); + chunk.Data = this.configuration.MemoryManager.AllocateCleanManagedByteBuffer(chunk.Length); this.currentStream.Read(chunk.Data.Array, 0, chunk.Length); } @@ -1353,7 +1358,7 @@ namespace SixLabors.ImageSharp.Formats.Png private void SwapBuffers() { - Buffer temp = this.previousScanline; + IManagedByteBuffer temp = this.previousScanline; this.previousScanline = this.scanline; this.scanline = temp; } diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index d531250898..1ab7a83ce0 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -74,37 +74,37 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// The previous scanline. /// - private Buffer previousScanline; + private IManagedByteBuffer previousScanline; /// /// The raw scanline. /// - private Buffer rawScanline; + private IManagedByteBuffer rawScanline; /// /// The filtered scanline result. /// - private Buffer result; + private IManagedByteBuffer result; /// /// The buffer for the sub filter /// - private Buffer sub; + private IManagedByteBuffer sub; /// /// The buffer for the up filter /// - private Buffer up; + private IManagedByteBuffer up; /// /// The buffer for the average filter /// - private Buffer average; + private IManagedByteBuffer average; /// /// The buffer for the paeth filter /// - private Buffer paeth; + private IManagedByteBuffer paeth; /// /// The png color type. @@ -357,11 +357,11 @@ namespace SixLabors.ImageSharp.Formats.Png { if (this.bytesPerPixel == 4) { - PixelOperations.Instance.ToRgba32Bytes(rowSpan, this.rawScanline, this.width); + PixelOperations.Instance.ToRgba32Bytes(rowSpan, this.rawScanline.Span, this.width); } else { - PixelOperations.Instance.ToRgb24Bytes(rowSpan, this.rawScanline, this.width); + PixelOperations.Instance.ToRgb24Bytes(rowSpan, this.rawScanline.Span, this.width); } } @@ -373,13 +373,14 @@ namespace SixLabors.ImageSharp.Formats.Png /// The row span. /// The row. /// The - private Buffer EncodePixelRow(Span rowSpan, int row) + private IManagedByteBuffer EncodePixelRow(Span rowSpan, int row) where TPixel : struct, IPixel { switch (this.pngColorType) { case PngColorType.Palette: - Buffer.BlockCopy(this.palettePixelData, row * this.rawScanline.Length, this.rawScanline.Array, 0, this.rawScanline.Length); + // TODO: Use Span copy! + Buffer.BlockCopy(this.palettePixelData, row * this.rawScanline.Length(), this.rawScanline.Array, 0, this.rawScanline.Length()); break; case PngColorType.Grayscale: case PngColorType.GrayscaleWithAlpha: @@ -398,7 +399,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// to be most compressible, using lowest total variation as proxy for compressibility. /// /// The - private Buffer GetOptimalFilteredScanline() + private IManagedByteBuffer GetOptimalFilteredScanline() { Span scanSpan = this.rawScanline.Span; Span prevSpan = this.previousScanline.Span; @@ -406,18 +407,18 @@ namespace SixLabors.ImageSharp.Formats.Png // Palette images don't compress well with adaptive filtering. if (this.pngColorType == PngColorType.Palette || this.bitDepth < 8) { - NoneFilter.Encode(this.rawScanline, this.result); + NoneFilter.Encode(this.rawScanline.Span, this.result.Span); return this.result; } // This order, while different to the enumerated order is more likely to produce a smaller sum // early on which shaves a couple of milliseconds off the processing time. - UpFilter.Encode(scanSpan, prevSpan, this.up, out int currentSum); + UpFilter.Encode(scanSpan, prevSpan, this.up.Span, out int currentSum); int lowestSum = currentSum; - Buffer actualResult = this.up; + IManagedByteBuffer actualResult = this.up; - PaethFilter.Encode(scanSpan, prevSpan, this.paeth, this.bytesPerPixel, out currentSum); + PaethFilter.Encode(scanSpan, prevSpan, this.paeth.Span, this.bytesPerPixel, out currentSum); if (currentSum < lowestSum) { @@ -425,7 +426,7 @@ namespace SixLabors.ImageSharp.Formats.Png actualResult = this.paeth; } - SubFilter.Encode(scanSpan, this.sub, this.bytesPerPixel, out currentSum); + SubFilter.Encode(scanSpan, this.sub.Span, this.bytesPerPixel, out currentSum); if (currentSum < lowestSum) { @@ -433,7 +434,7 @@ namespace SixLabors.ImageSharp.Formats.Png actualResult = this.sub; } - AverageFilter.Encode(scanSpan, prevSpan, this.average, this.bytesPerPixel, out currentSum); + AverageFilter.Encode(scanSpan, prevSpan, this.average.Span, this.bytesPerPixel, out currentSum); if (currentSum < lowestSum) { @@ -522,9 +523,13 @@ namespace SixLabors.ImageSharp.Formats.Png int colorTableLength = (int)Math.Pow(2, header.BitDepth) * 3; var rgba = default(Rgba32); bool anyAlpha = false; - using (Buffer colorTable = this.memoryManager.Allocate(colorTableLength)) - using (Buffer alphaTable = this.memoryManager.Allocate(pixelCount)) + + using (IManagedByteBuffer colorTable = this.memoryManager.AllocateManagedByteBuffer(colorTableLength)) + using (IManagedByteBuffer alphaTable = this.memoryManager.AllocateManagedByteBuffer(pixelCount)) { + Span colorTableSpan = colorTable.Span; + Span alphaTableSpan = alphaTable.Span; + for (byte i = 0; i < pixelCount; i++) { if (quantized.Pixels.Contains(i)) @@ -534,9 +539,9 @@ namespace SixLabors.ImageSharp.Formats.Png byte alpha = rgba.A; - colorTable[offset] = rgba.R; - colorTable[offset + 1] = rgba.G; - colorTable[offset + 2] = rgba.B; + colorTableSpan[offset] = rgba.R; + colorTableSpan[offset + 1] = rgba.G; + colorTableSpan[offset + 2] = rgba.B; if (alpha > this.threshold) { @@ -544,7 +549,7 @@ namespace SixLabors.ImageSharp.Formats.Png } anyAlpha = anyAlpha || alpha < 255; - alphaTable[i] = alpha; + alphaTableSpan[i] = alpha; } } @@ -617,16 +622,16 @@ namespace SixLabors.ImageSharp.Formats.Png this.bytesPerScanline = this.width * this.bytesPerPixel; int resultLength = this.bytesPerScanline + 1; - this.previousScanline = this.memoryManager.Allocate(this.bytesPerScanline, true); - this.rawScanline = this.memoryManager.Allocate(this.bytesPerScanline, true); - this.result = this.memoryManager.Allocate(resultLength, true); + this.previousScanline = this.memoryManager.AllocateCleanManagedByteBuffer(this.bytesPerScanline); + this.rawScanline = this.memoryManager.AllocateCleanManagedByteBuffer(this.bytesPerScanline); + this.result = this.memoryManager.AllocateCleanManagedByteBuffer(resultLength); if (this.pngColorType != PngColorType.Palette) { - this.sub = this.memoryManager.Allocate(resultLength, true); - this.up = this.memoryManager.Allocate(resultLength, true); - this.average = this.memoryManager.Allocate(resultLength, true); - this.paeth = this.memoryManager.Allocate(resultLength, true); + this.sub = this.memoryManager.AllocateCleanManagedByteBuffer(resultLength); + this.up = this.memoryManager.AllocateCleanManagedByteBuffer(resultLength); + this.average = this.memoryManager.AllocateCleanManagedByteBuffer(resultLength); + this.paeth = this.memoryManager.AllocateCleanManagedByteBuffer(resultLength); } byte[] buffer; @@ -639,10 +644,10 @@ namespace SixLabors.ImageSharp.Formats.Png { for (int y = 0; y < this.height; y++) { - Buffer r = this.EncodePixelRow(pixels.GetPixelRowSpan(y), y); + IManagedByteBuffer r = this.EncodePixelRow(pixels.GetPixelRowSpan(y), y); deflateStream.Write(r.Array, 0, resultLength); - Buffer temp = this.rawScanline; + IManagedByteBuffer temp = this.rawScanline; this.rawScanline = this.previousScanline; this.previousScanline = temp; } diff --git a/src/ImageSharp/Image/Image.Decode.cs b/src/ImageSharp/Image/Image.Decode.cs index a2eacd3733..72492a494b 100644 --- a/src/ImageSharp/Image/Image.Decode.cs +++ b/src/ImageSharp/Image/Image.Decode.cs @@ -29,12 +29,12 @@ namespace SixLabors.ImageSharp return null; } - using (var buffer = config.MemoryManager.Allocate(maxHeaderSize)) + using (IManagedByteBuffer buffer = config.MemoryManager.AllocateManagedByteBuffer(maxHeaderSize)) { long startPosition = stream.Position; stream.Read(buffer.Array, 0, maxHeaderSize); stream.Position = startPosition; - return config.FormatDetectors.Select(x => x.DetectFormat(buffer)).LastOrDefault(x => x != null); + return config.FormatDetectors.Select(x => x.DetectFormat(buffer.Span)).LastOrDefault(x => x != null); } } diff --git a/src/ImageSharp/Image/PixelAccessor{TPixel}.cs b/src/ImageSharp/Image/PixelAccessor{TPixel}.cs index 50e65a0829..80c0ce4e66 100644 --- a/src/ImageSharp/Image/PixelAccessor{TPixel}.cs +++ b/src/ImageSharp/Image/PixelAccessor{TPixel}.cs @@ -84,11 +84,6 @@ namespace SixLabors.ImageSharp this.Dispose(); } - /// - /// Gets the pixel buffer array. - /// - public TPixel[] PixelArray => this.PixelBuffer.Buffer.Array; - /// /// Gets the size of a single pixel in the number of bytes. /// @@ -106,7 +101,7 @@ namespace SixLabors.ImageSharp public int Height { get; private set; } /// - Span IBuffer2D.Span => this.PixelBuffer.Span; + public Span Span => this.PixelBuffer.Span; private static PixelOperations Operations => PixelOperations.Instance; @@ -122,14 +117,15 @@ namespace SixLabors.ImageSharp get { this.CheckCoordinates(x, y); - return this.PixelArray[(y * this.Width) + x]; + return this.Span[(y * this.Width) + x]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] set { this.CheckCoordinates(x, y); - this.PixelArray[(y * this.Width) + x] = value; + Span span = this.Span; + span[(y * this.Width) + x] = value; } } diff --git a/src/ImageSharp/Image/PixelArea{TPixel}.cs b/src/ImageSharp/Image/PixelArea{TPixel}.cs index fa3499b6d7..7648017222 100644 --- a/src/ImageSharp/Image/PixelArea{TPixel}.cs +++ b/src/ImageSharp/Image/PixelArea{TPixel}.cs @@ -30,44 +30,7 @@ namespace SixLabors.ImageSharp /// /// The underlying buffer containing the raw pixel data. /// - private readonly Buffer byteBuffer; - - /// - /// Initializes a new instance of the class. - /// - /// The width. - /// The bytes. - /// The component order. - /// - /// Thrown if is the incorrect length. - /// - public PixelArea(int width, byte[] bytes, ComponentOrder componentOrder) - : this(width, 1, bytes, componentOrder) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The width. - /// The height. - /// The bytes. - /// The component order. - /// - /// Thrown if is the incorrect length. - /// - public PixelArea(int width, int height, byte[] bytes, ComponentOrder componentOrder) - { - this.CheckBytesLength(width, height, bytes, componentOrder); - - this.Width = width; - this.Height = height; - this.ComponentOrder = componentOrder; - this.RowStride = width * GetComponentCount(componentOrder); - this.Length = bytes.Length; // TODO: Is this the right value for Length? - - this.byteBuffer = new Buffer(bytes); - } + private readonly IManagedByteBuffer byteBuffer; /// /// Initializes a new instance of the class. @@ -116,7 +79,7 @@ namespace SixLabors.ImageSharp this.RowStride = (width * GetComponentCount(componentOrder)) + padding; this.Length = this.RowStride * height; - this.byteBuffer = Configuration.Default.MemoryManager.Allocate(this.Length, true); + this.byteBuffer = Configuration.Default.MemoryManager.AllocateCleanManagedByteBuffer(this.Length); } /// diff --git a/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs b/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs index 4034643345..e14ba443f9 100644 --- a/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs +++ b/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs @@ -68,7 +68,7 @@ namespace SixLabors.ImageSharp.Memory /// internal override void Release(Buffer buffer) { - byte[] byteBuffer = Unsafe.As(buffer.Array); + byte[] byteBuffer = Unsafe.As(buffer.GetArray()); this.pool.Return(byteBuffer); } } diff --git a/src/ImageSharp/Memory/Buffer2DExtensions.cs b/src/ImageSharp/Memory/Buffer2DExtensions.cs index ac5ab09dbd..0cbffde775 100644 --- a/src/ImageSharp/Memory/Buffer2DExtensions.cs +++ b/src/ImageSharp/Memory/Buffer2DExtensions.cs @@ -12,6 +12,7 @@ namespace SixLabors.ImageSharp.Memory /// internal static class Buffer2DExtensions { + /// /// Gets a to the row 'y' beginning from the pixel at 'x'. /// diff --git a/src/ImageSharp/Memory/Buffer2D{T}.cs b/src/ImageSharp/Memory/Buffer2D{T}.cs index 82cb25f476..e527c90c07 100644 --- a/src/ImageSharp/Memory/Buffer2D{T}.cs +++ b/src/ImageSharp/Memory/Buffer2D{T}.cs @@ -51,8 +51,8 @@ namespace SixLabors.ImageSharp.Memory { DebugGuard.MustBeLessThan(x, this.Width, nameof(x)); DebugGuard.MustBeLessThan(y, this.Height, nameof(y)); - - return ref this.Buffer.Array[(this.Width * y) + x]; + Span span = this.Buffer.Span; + return ref span[(this.Width * y) + x]; } } diff --git a/src/ImageSharp/Memory/BufferExtensions.cs b/src/ImageSharp/Memory/BufferExtensions.cs new file mode 100644 index 0000000000..8975d3b45d --- /dev/null +++ b/src/ImageSharp/Memory/BufferExtensions.cs @@ -0,0 +1,54 @@ +using System; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.Memory +{ + internal static class BufferExtensions + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Length(this IBuffer buffer) + where T : struct => buffer.Span.Length; + + /// + /// Gets a to an offseted position inside the buffer. + /// + /// The buffer + /// The start + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span Slice(this IBuffer buffer, int start) + where T : struct + { + return buffer.Span.Slice(start); + } + + /// + /// Gets a to an offsetted position inside the buffer. + /// + /// The buffer + /// The start + /// The length of the slice + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span Slice(this IBuffer buffer, int start, int length) + where T : struct + { + return buffer.Span.Slice(start, length); + } + + /// + /// Clears the contents of this buffer. + /// + /// The buffer + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Clear(this IBuffer buffer) + where T : struct + { + buffer.Span.Clear(); + } + + public static ref T DangerousGetPinnableReference(this IBuffer buffer) + where T : struct => + ref buffer.Span.DangerousGetPinnableReference(); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Memory/Buffer{T}.cs b/src/ImageSharp/Memory/Buffer{T}.cs index 07a827a67d..1ee1571c84 100644 --- a/src/ImageSharp/Memory/Buffer{T}.cs +++ b/src/ImageSharp/Memory/Buffer{T}.cs @@ -19,15 +19,23 @@ namespace SixLabors.ImageSharp.Memory private MemoryManager memoryManager; /// - /// A pointer to the first element of when pinned. + /// A pointer to the first element of when pinned. /// private IntPtr pointer; /// - /// A handle that allows to access the managed as an unmanaged memory by pinning. + /// A handle that allows to access the managed as an unmanaged memory by pinning. /// private GCHandle handle; + // why is there such a rule? :S Protected should be fine for a field! +#pragma warning disable SA1401 // Fields should be private + /// + /// The backing array. + /// + protected T[] array; +#pragma warning restore SA1401 // Fields should be private + /// /// Initializes a new instance of the class. /// @@ -35,7 +43,7 @@ namespace SixLabors.ImageSharp.Memory public Buffer(T[] array) { this.Length = array.Length; - this.Array = array; + this.array = array; } /// @@ -51,7 +59,7 @@ namespace SixLabors.ImageSharp.Memory } this.Length = length; - this.Array = array; + this.array = array; } internal Buffer(T[] array, int length, MemoryManager memoryManager) @@ -69,20 +77,15 @@ namespace SixLabors.ImageSharp.Memory } /// - /// Gets a value indicating whether this instance is disposed, or has lost ownership of . + /// Gets a value indicating whether this instance is disposed, or has lost ownership of . /// public bool IsDisposedOrLostArrayOwnership { get; private set; } /// - /// Gets the count of "relevant" elements. It's usually smaller than 'Array.Length' when is pooled. + /// Gets the count of "relevant" elements. It's usually smaller than 'Array.Length' when is pooled. /// public int Length { get; private set; } - /// - /// Gets the backing pinned array. - /// - public T[] Array { get; private set; } - /// /// Gets a to the backing buffer. /// @@ -112,7 +115,7 @@ namespace SixLabors.ImageSharp.Memory [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator ReadOnlySpan(Buffer buffer) { - return new ReadOnlySpan(buffer.Array, 0, buffer.Length); + return new ReadOnlySpan(buffer.array, 0, buffer.Length); } /// @@ -122,30 +125,7 @@ namespace SixLabors.ImageSharp.Memory [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Span(Buffer buffer) { - return new Span(buffer.Array, 0, buffer.Length); - } - - /// - /// Gets a to an offseted position inside the buffer. - /// - /// The start - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Span Slice(int start) - { - return new Span(this.Array, start, this.Length - start); - } - - /// - /// Gets a to an offsetted position inside the buffer. - /// - /// The start - /// The length of the slice - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Span Slice(int start, int length) - { - return new Span(this.Array, start, length); + return new Span(buffer.array, 0, buffer.Length); } /// @@ -165,17 +145,17 @@ namespace SixLabors.ImageSharp.Memory this.memoryManager?.Release(this); this.memoryManager = null; - this.Array = null; + this.array = null; this.Length = 0; GC.SuppressFinalize(this); } /// - /// Unpins and makes the object "quasi-disposed" so the array is no longer owned by this object. - /// If is rented, it's the callers responsibility to return it to it's pool. + /// Unpins and makes the object "quasi-disposed" so the array is no longer owned by this object. + /// If is rented, it's the callers responsibility to return it to it's pool. /// - /// The unpinned + /// The unpinned [MethodImpl(MethodImplOptions.AggressiveInlining)] public T[] TakeArrayOwnership() { @@ -187,23 +167,14 @@ namespace SixLabors.ImageSharp.Memory this.IsDisposedOrLostArrayOwnership = true; this.UnPin(); - T[] array = this.Array; - this.Array = null; + T[] array = this.array; + this.array = null; this.memoryManager = null; return array; } /// - /// Clears the contents of this buffer. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Clear() - { - this.Span.Clear(); - } - - /// - /// Pins . + /// Pins . /// /// The pinned pointer [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -217,7 +188,7 @@ namespace SixLabors.ImageSharp.Memory if (this.pointer == IntPtr.Zero) { - this.handle = GCHandle.Alloc(this.Array, GCHandleType.Pinned); + this.handle = GCHandle.Alloc(this.array, GCHandleType.Pinned); this.pointer = this.handle.AddrOfPinnedObject(); } @@ -225,7 +196,15 @@ namespace SixLabors.ImageSharp.Memory } /// - /// Unpins . + /// TODO: Refactor this + /// + internal T[] GetArray() + { + return this.array; + } + + /// + /// Unpins . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] private void UnPin() diff --git a/src/ImageSharp/Memory/ManagedByteBuffer.cs b/src/ImageSharp/Memory/ManagedByteBuffer.cs index 17fe945d61..94d08e2aa7 100644 --- a/src/ImageSharp/Memory/ManagedByteBuffer.cs +++ b/src/ImageSharp/Memory/ManagedByteBuffer.cs @@ -6,5 +6,7 @@ namespace SixLabors.ImageSharp.Memory : base(array, length, memoryManager) { } + + public byte[] Array => this.array; } } \ No newline at end of file diff --git a/src/ImageSharp/Memory/MemoryManager.cs b/src/ImageSharp/Memory/MemoryManager.cs index cac9b785b8..58f2458193 100644 --- a/src/ImageSharp/Memory/MemoryManager.cs +++ b/src/ImageSharp/Memory/MemoryManager.cs @@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.Memory /// Temporal workaround. A method providing a "Buffer" based on a generic array without the 'Unsafe.As()' hackery. /// Should be replaced with 'Allocate()' as soon as SixLabors.Shapes has Span-based API-s! /// - internal FakeBuffer AllocateFake(int length) + internal FakeBuffer AllocateFake(int length, bool dummy = false) where T : struct { return new FakeBuffer(new T[length]); diff --git a/src/ImageSharp/Memory/MemoryManagerExtensions.cs b/src/ImageSharp/Memory/MemoryManagerExtensions.cs index 8772307885..f157767217 100644 --- a/src/ImageSharp/Memory/MemoryManagerExtensions.cs +++ b/src/ImageSharp/Memory/MemoryManagerExtensions.cs @@ -26,6 +26,11 @@ return memoryManager.Allocate(length, true); } + public static IManagedByteBuffer AllocateManagedByteBuffer(this MemoryManager memoryManager, int length) + { + return memoryManager.AllocateManagedByteBuffer(length, false); + } + public static IManagedByteBuffer AllocateCleanManagedByteBuffer(this MemoryManager memoryManager, int length) { return memoryManager.AllocateManagedByteBuffer(length, true); diff --git a/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs b/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs index 8f89c49611..b5d31014b2 100644 --- a/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs +++ b/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs @@ -180,14 +180,14 @@ namespace SixLabors.ImageSharp.Quantizers { this.Mark(ref this.colorCube[k], (byte)k); - float weight = Volume(ref this.colorCube[k], this.vwt.Array); + float weight = Volume(ref this.colorCube[k], this.vwt.Span); if (MathF.Abs(weight) > Constants.Epsilon) { - float r = Volume(ref this.colorCube[k], this.vmr.Array); - float g = Volume(ref this.colorCube[k], this.vmg.Array); - float b = Volume(ref this.colorCube[k], this.vmb.Array); - float a = Volume(ref this.colorCube[k], this.vma.Array); + float r = Volume(ref this.colorCube[k], this.vmr.Span); + float g = Volume(ref this.colorCube[k], this.vmg.Span); + float b = Volume(ref this.colorCube[k], this.vmb.Span); + float a = Volume(ref this.colorCube[k], this.vma.Span); ref TPixel color = ref this.palette[k]; color.PackFromVector4(new Vector4(r, g, b, a) / weight / 255F); @@ -312,7 +312,7 @@ namespace SixLabors.ImageSharp.Quantizers /// The cube. /// The moment. /// The result. - private static float Volume(ref Box cube, long[] moment) + private static float Volume(ref Box cube, Span moment) { return moment[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A1)] - moment[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A0)] @@ -339,7 +339,7 @@ namespace SixLabors.ImageSharp.Quantizers /// The direction. /// The moment. /// The result. - private static long Bottom(ref Box cube, int direction, long[] moment) + private static long Bottom(ref Box cube, int direction, Span moment) { switch (direction) { @@ -400,7 +400,7 @@ namespace SixLabors.ImageSharp.Quantizers /// The position. /// The moment. /// The result. - private static long Top(ref Box cube, int direction, int position, long[] moment) + private static long Top(ref Box cube, int direction, int position, Span moment) { switch (direction) { @@ -548,10 +548,10 @@ namespace SixLabors.ImageSharp.Quantizers /// The . private float Variance(ref Box cube) { - float dr = Volume(ref cube, this.vmr.Array); - float dg = Volume(ref cube, this.vmg.Array); - float db = Volume(ref cube, this.vmb.Array); - float da = Volume(ref cube, this.vma.Array); + float dr = Volume(ref cube, this.vmr.Span); + float dg = Volume(ref cube, this.vmg.Span); + float db = Volume(ref cube, this.vmb.Span); + float da = Volume(ref cube, this.vma.Span); float xx = this.m2[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A1)] @@ -572,7 +572,7 @@ namespace SixLabors.ImageSharp.Quantizers + this.m2[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A0)]; var vector = new Vector4(dr, dg, db, da); - return xx - (Vector4.Dot(vector, vector) / Volume(ref cube, this.vwt.Array)); + return xx - (Vector4.Dot(vector, vector) / Volume(ref cube, this.vwt.Span)); } /// @@ -595,22 +595,22 @@ namespace SixLabors.ImageSharp.Quantizers /// The . private float Maximize(ref Box cube, int direction, int first, int last, out int cut, float wholeR, float wholeG, float wholeB, float wholeA, float wholeW) { - long baseR = Bottom(ref cube, direction, this.vmr.Array); - long baseG = Bottom(ref cube, direction, this.vmg.Array); - long baseB = Bottom(ref cube, direction, this.vmb.Array); - long baseA = Bottom(ref cube, direction, this.vma.Array); - long baseW = Bottom(ref cube, direction, this.vwt.Array); + long baseR = Bottom(ref cube, direction, this.vmr.Span); + long baseG = Bottom(ref cube, direction, this.vmg.Span); + long baseB = Bottom(ref cube, direction, this.vmb.Span); + long baseA = Bottom(ref cube, direction, this.vma.Span); + long baseW = Bottom(ref cube, direction, this.vwt.Span); float max = 0F; cut = -1; for (int i = first; i < last; i++) { - float halfR = baseR + Top(ref cube, direction, i, this.vmr.Array); - float halfG = baseG + Top(ref cube, direction, i, this.vmg.Array); - float halfB = baseB + Top(ref cube, direction, i, this.vmb.Array); - float halfA = baseA + Top(ref cube, direction, i, this.vma.Array); - float halfW = baseW + Top(ref cube, direction, i, this.vwt.Array); + float halfR = baseR + Top(ref cube, direction, i, this.vmr.Span); + float halfG = baseG + Top(ref cube, direction, i, this.vmg.Span); + float halfB = baseB + Top(ref cube, direction, i, this.vmb.Span); + float halfA = baseA + Top(ref cube, direction, i, this.vma.Span); + float halfW = baseW + Top(ref cube, direction, i, this.vwt.Span); if (MathF.Abs(halfW) < Constants.Epsilon) { @@ -654,11 +654,11 @@ namespace SixLabors.ImageSharp.Quantizers /// Returns a value indicating whether the box has been split. private bool Cut(ref Box set1, ref Box set2) { - float wholeR = Volume(ref set1, this.vmr.Array); - float wholeG = Volume(ref set1, this.vmg.Array); - float wholeB = Volume(ref set1, this.vmb.Array); - float wholeA = Volume(ref set1, this.vma.Array); - float wholeW = Volume(ref set1, this.vwt.Array); + float wholeR = Volume(ref set1, this.vmr.Span); + float wholeG = Volume(ref set1, this.vmg.Span); + float wholeB = Volume(ref set1, this.vmb.Span); + float wholeA = Volume(ref set1, this.vma.Span); + float wholeW = Volume(ref set1, this.vwt.Span); float maxr = this.Maximize(ref set1, 3, set1.R0 + 1, set1.R1, out int cutr, wholeR, wholeG, wholeB, wholeA, wholeW); float maxg = this.Maximize(ref set1, 2, set1.G0 + 1, set1.G1, out int cutg, wholeR, wholeG, wholeB, wholeA, wholeW); diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs index 1f660466df..53a55e06e3 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs @@ -2,6 +2,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk { using System.Numerics; + using System.Runtime.CompilerServices; using BenchmarkDotNet.Attributes; @@ -36,12 +37,12 @@ namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk [Benchmark(Baseline = true)] public void PerElement() { - Vector4[] s = this.source.Array; - TPixel[] d = this.destination.Array; + ref Vector4 s = ref this.source.Span.DangerousGetPinnableReference(); + ref TPixel d = ref this.destination.Span.DangerousGetPinnableReference(); for (int i = 0; i < this.Count; i++) { - d[i].PackFromVector4(s[i]); + Unsafe.Add(ref d, i).PackFromVector4(Unsafe.Add(ref s, i)); } } diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs index fd96c02cd3..8925fe9038 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs @@ -61,8 +61,8 @@ [Benchmark] public void PackUsingReferences() { - ref Vector4 sp = ref this.source.Array[0]; - ref Rgba32 dp = ref this.destination.Array[0]; + ref Vector4 sp = ref this.source.DangerousGetPinnableReference(); + ref Rgba32 dp = ref this.destination.DangerousGetPinnableReference(); int count = this.Count; for (int i = 0; i < count; i++) diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs index eab65bb33a..fb2f03d743 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs @@ -1,6 +1,8 @@ // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk { + using System; + using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Memory; @@ -33,13 +35,13 @@ namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk [Benchmark(Baseline = true)] public void PerElement() { - byte[] s = this.source.Array; - TPixel[] d = this.destination.Array; + Span s = this.source.Span; + Span d = this.destination.Span; for (int i = 0; i < this.Count; i++) { int i4 = i * 4; - TPixel c = default(TPixel); + var c = default(TPixel); c.PackFromRgba32(new Rgba32(s[i4], s[i4 + 1], s[i4 + 2], s[i4 + 3])); d[i] = c; } diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs index f9ecc9635e..cddf0f9a86 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs @@ -1,6 +1,7 @@ // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk { + using System; using System.Numerics; using BenchmarkDotNet.Attributes; @@ -35,8 +36,8 @@ namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk [Benchmark(Baseline = true)] public void PerElement() { - TPixel[] s = this.source.Array; - Vector4[] d = this.destination.Array; + Span s = this.source.Span; + Span d = this.destination.Span; for (int i = 0; i < this.Count; i++) { diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs index 8475a9e822..6593a28ae3 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs @@ -1,6 +1,9 @@ // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk { + using System; + using System.Numerics; + using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Memory; @@ -33,8 +36,9 @@ namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk [Benchmark(Baseline = true)] public void PerElement() { - TPixel[] s = this.source.Array; - byte[] d = this.destination.Array; + Span s = this.source.Span; + Span d = this.destination.Span; + var rgb = default(Rgb24); for (int i = 0; i < this.Count; i++) diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs index b3e0eff14d..58b80d5504 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs @@ -38,8 +38,9 @@ namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk [Benchmark(Baseline = true)] public void PerElement() { - TPixel[] s = this.source.Array; - byte[] d = this.destination.Array; + Span s = this.source.Span; + Span d = this.destination.Span; + var rgba = default(Rgba32); for (int i = 0; i < this.Count; i++) diff --git a/tests/ImageSharp.Benchmarks/General/ClearBuffer.cs b/tests/ImageSharp.Benchmarks/General/ClearBuffer.cs deleted file mode 100644 index 6926d92536..0000000000 --- a/tests/ImageSharp.Benchmarks/General/ClearBuffer.cs +++ /dev/null @@ -1,42 +0,0 @@ -// ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Benchmarks.General -{ - using System; - using System.Runtime.CompilerServices; - - using BenchmarkDotNet.Attributes; - - using SixLabors.ImageSharp.Memory; - - public unsafe class ClearBuffer - { - private Buffer buffer; - - [Params(32, 128, 512)] - public int Count { get; set; } - - [GlobalSetup] - public void Setup() - { - this.buffer = Configuration.Default.MemoryManager.Allocate(this.Count); - } - - [GlobalCleanup] - public void Cleanup() - { - this.buffer.Dispose(); - } - - [Benchmark(Baseline = true)] - public void Array_Clear() - { - Array.Clear(this.buffer.Array, 0, this.Count); - } - - [Benchmark] - public void Unsafe_InitBlock() - { - Unsafe.InitBlock((void*)this.buffer.Pin(), default(byte), (uint)this.Count * sizeof(uint)); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/PixelIndexing.cs b/tests/ImageSharp.Benchmarks/General/PixelIndexing.cs deleted file mode 100644 index 50e0bd6100..0000000000 --- a/tests/ImageSharp.Benchmarks/General/PixelIndexing.cs +++ /dev/null @@ -1,362 +0,0 @@ -namespace SixLabors.ImageSharp.Benchmarks.General -{ - using System.Numerics; - using System.Runtime.CompilerServices; - - using BenchmarkDotNet.Attributes; - - using SixLabors.ImageSharp.Memory; - - // Pixel indexing benchmarks compare different methods for getting/setting all pixel values in a subsegment of a single pixel row. - public abstract unsafe class PixelIndexing - { - /// - /// https://github.com/dotnet/corefx/blob/master/src/System.Memory/src/System/Pinnable.cs - /// - protected class Pinnable - { - public T Data; - } - - /// - /// The indexer methods are encapsulated into a struct to make sure everything is inlined. - /// - internal struct Data - { - private Vector4* pointer; - - private Pinnable pinnable; - - private Vector4[] array; - - private int width; - - public Data(Buffer2D buffer) - { - this.pointer = (Vector4*)buffer.Buffer.Pin(); - this.pinnable = Unsafe.As>(buffer.Buffer.Array); - this.array = buffer.Buffer.Array; - this.width = buffer.Width; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 GetPointersBasicImpl(int x, int y) - { - return this.pointer[y * this.width + x]; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 GetPointersSrcsUnsafeImpl(int x, int y) - { - // This is the original solution in PixelAccessor: - return Unsafe.Read((byte*)this.pointer + (((y * this.width) + x) * Unsafe.SizeOf())); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 GetReferencesImpl(int x, int y) - { - int elementOffset = (y * this.width) + x; - return Unsafe.Add(ref this.pinnable.Data, elementOffset); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref Vector4 GetReferencesRefReturnsImpl(int x, int y) - { - int elementOffset = (y * this.width) + x; - return ref Unsafe.Add(ref this.pinnable.Data, elementOffset); - } - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void IndexWithPointersBasicImpl(int x, int y, Vector4 v) - { - this.pointer[y * this.width + x] = v; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void IndexWithPointersSrcsUnsafeImpl(int x, int y, Vector4 v) - { - Unsafe.Write((byte*)this.pointer + (((y * this.width) + x) * Unsafe.SizeOf()), v); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void IndexWithReferencesOnPinnableIncorrectImpl(int x, int y, Vector4 v) - { - int elementOffset = (y * this.width) + x; - // Incorrect, because also should add a runtime-specific byte offset here. See https://github.com/dotnet/corefx/blob/master/src/System.Memory/src/System/Span.cs#L68 - Unsafe.Add(ref this.pinnable.Data, elementOffset) = v; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref Vector4 IndexWithReferencesOnPinnableIncorrectRefReturnImpl(int x, int y) - { - int elementOffset = (y * this.width) + x; - // Incorrect, because also should add a runtime-specific byte offset here. See https://github.com/dotnet/corefx/blob/master/src/System.Memory/src/System/Span.cs#L68 - return ref Unsafe.Add(ref this.pinnable.Data, elementOffset); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void IndexWithUnsafeReferenceArithmeticsOnArray0Impl(int x, int y, Vector4 v) - { - int elementOffset = (y * this.width) + x; - Unsafe.Add(ref this.array[0], elementOffset) = v; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref Vector4 IndexWithUnsafeReferenceArithmeticsOnArray0RefReturnImpl(int x, int y) - { - int elementOffset = (y * this.width) + x; - return ref Unsafe.Add(ref this.array[0], elementOffset); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void IndexSetArrayStraightforward(int x, int y, Vector4 v) - { - // No magic. - // We just index right into the array as normal people do. - // And it looks like this is the fastest way! - this.array[(y * this.width) + x] = v; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref Vector4 IndexWithReferencesOnArrayStraightforwardRefReturnImpl(int x, int y) - { - // No magic. - // We just index right into the array as normal people do. - // And it looks like this is the fastest way! - return ref this.array[(y * this.width) + x]; - } - } - - internal Buffer2D buffer; - - protected int width; - - protected int startIndex; - - protected int endIndex; - - protected Vector4* pointer; - - protected Vector4[] array; - - protected Pinnable pinnable; - - // [Params(1024)] - public int Count { get; set; } = 1024; - - [GlobalSetup] - public void Setup() - { - this.width = 2048; - this.buffer = Configuration.Default.MemoryManager.Allocate2D(2048, 2048); - this.pointer = (Vector4*)this.buffer.Buffer.Pin(); - this.array = this.buffer.Buffer.Array; - this.pinnable = Unsafe.As>(this.array); - - this.startIndex = 2048 / 2 - (this.Count / 2); - this.endIndex = 2048 / 2 + (this.Count / 2); - } - - [GlobalCleanup] - public void Cleanup() - { - this.buffer.Dispose(); - } - - } - - public class PixelIndexingGetter : PixelIndexing - { - [Benchmark(Description = "Index.Get: Pointers+arithmetics", Baseline = true)] - public Vector4 IndexWithPointersBasic() - { - Vector4 sum = Vector4.Zero; - Data data = new Data(this.buffer); - int y = this.startIndex; - for (int x = this.startIndex; x < this.endIndex; x++) - { - sum += data.GetPointersBasicImpl(x, y); - } - - return sum; - } - - [Benchmark(Description = "Index.Get: Pointers+SRCS.Unsafe")] - public Vector4 IndexWithPointersSrcsUnsafe() - { - Vector4 sum = Vector4.Zero; - Data data = new Data(this.buffer); - - int y = this.startIndex; - for (int x = this.startIndex; x < this.endIndex; x++) - { - sum += data.GetPointersSrcsUnsafeImpl(x, y); - } - - return sum; - } - - [Benchmark(Description = "Index.Get: References")] - public Vector4 IndexWithReferences() - { - Vector4 sum = Vector4.Zero; - Data data = new Data(this.buffer); - - int y = this.startIndex; - for (int x = this.startIndex; x < this.endIndex; x++) - { - sum += data.GetReferencesImpl(x, y); - } - - return sum; - } - - [Benchmark(Description = "Index.Get: References|refreturns")] - public Vector4 IndexWithReferencesRefReturns() - { - Vector4 sum = Vector4.Zero; - Data data = new Data(this.buffer); - - int y = this.startIndex; - for (int x = this.startIndex; x < this.endIndex; x++) - { - sum += data.GetReferencesRefReturnsImpl(x, y); - } - - return sum; - } - } - - public class PixelIndexingSetter : PixelIndexing - { - [Benchmark(Description = "!!! Index.Set: Pointers|arithmetics", Baseline = true)] - public void IndexWithPointersBasic() - { - Vector4 v = new Vector4(1, 2, 3, 4); - Data data = new Data(this.buffer); - - int y = this.startIndex; - for (int x = this.startIndex; x < this.endIndex; x++) - { - data.IndexWithPointersBasicImpl(x, y, v); - } - } - - [Benchmark(Description = "Index.Set: Pointers|SRCS.Unsafe")] - public void IndexWithPointersSrcsUnsafe() - { - Vector4 v = new Vector4(1, 2, 3, 4); - Data data = new Data(this.buffer); - - int y = this.startIndex; - for (int x = this.startIndex; x < this.endIndex; x++) - { - data.IndexWithPointersSrcsUnsafeImpl(x, y, v); - } - } - - [Benchmark(Description = "Index.Set: References|IncorrectPinnable")] - public void IndexWithReferencesPinnableBasic() - { - Vector4 v = new Vector4(1, 2, 3, 4); - Data data = new Data(this.buffer); - - int y = this.startIndex; - for (int x = this.startIndex; x < this.endIndex; x++) - { - data.IndexWithReferencesOnPinnableIncorrectImpl(x, y, v); - } - } - - [Benchmark(Description = "Index.Set: References|IncorrectPinnable|refreturn")] - public void IndexWithReferencesPinnableRefReturn() - { - Vector4 v = new Vector4(1, 2, 3, 4); - Data data = new Data(this.buffer); - - int y = this.startIndex; - for (int x = this.startIndex; x < this.endIndex; x++) - { - data.IndexWithReferencesOnPinnableIncorrectRefReturnImpl(x, y) = v; - } - } - - [Benchmark(Description = "Index.Set: References|Array[0]Unsafe")] - public void IndexWithReferencesArrayBasic() - { - Vector4 v = new Vector4(1, 2, 3, 4); - Data data = new Data(this.buffer); - - int y = this.startIndex; - for (int x = this.startIndex; x < this.endIndex; x++) - { - data.IndexWithUnsafeReferenceArithmeticsOnArray0Impl(x, y, v); - } - } - - [Benchmark(Description = "Index.Set: References|Array[0]Unsafe|refreturn")] - public void IndexWithReferencesArrayRefReturn() - { - Vector4 v = new Vector4(1, 2, 3, 4); - Data data = new Data(this.buffer); - - int y = this.startIndex; - for (int x = this.startIndex; x < this.endIndex; x++) - { - data.IndexWithUnsafeReferenceArithmeticsOnArray0RefReturnImpl(x, y) = v; - } - } - - [Benchmark(Description = "!!! Index.Set: References|Array+Straight")] - public void IndexWithReferencesArrayStraightforward() - { - Vector4 v = new Vector4(1, 2, 3, 4); - Data data = new Data(this.buffer); - - int y = this.startIndex; - for (int x = this.startIndex; x < this.endIndex; x++) - { - // No magic. - // We just index right into the array as normal people do. - // And it looks like this is the fastest way! - data.IndexSetArrayStraightforward(x, y, v); - } - } - - - [Benchmark(Description = "!!! Index.Set: References|Array+Straight|refreturn")] - public void IndexWithReferencesArrayStraightforwardRefReturn() - { - Vector4 v = new Vector4(1, 2, 3, 4); - Data data = new Data(this.buffer); - - int y = this.startIndex; - for (int x = this.startIndex; x < this.endIndex; x++) - { - // No magic. - // We just index right into the array as normal people do. - // And it looks like this is the fastest way! - data.IndexWithReferencesOnArrayStraightforwardRefReturnImpl(x, y) = v; - } - } - - [Benchmark(Description = "!!! Index.Set: SmartUnsafe")] - public void SmartUnsafe() - { - Vector4 v = new Vector4(1, 2, 3, 4); - Data data = new Data(this.buffer); - - // This method is basically an unsafe variant of .GetRowSpan(y) + indexing individual pixels in the row. - // If a user seriously needs by-pixel manipulation to be performant, we should provide this option. - - ref Vector4 rowStart = ref data.IndexWithReferencesOnArrayStraightforwardRefReturnImpl(this.startIndex, this.startIndex); - - for (int i = 0; i < this.Count; i++) - { - // We don't have to add 'Width * y' here! - Unsafe.Add(ref rowStart, i) = v; - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs index 7f78ef39c0..50c3ff0050 100644 --- a/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs +++ b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs @@ -46,10 +46,11 @@ namespace SixLabors.ImageSharp.Tests.Memory { using (Buffer2D buffer = Configuration.Default.MemoryManager.Allocate2D(42, 42, true)) { + Span span = buffer.Span; for (int j = 0; j < buffer.Buffer.Length; j++) { - Assert.Equal(0, buffer.Buffer.Array[j]); - buffer.Buffer.Array[j] = 666; + Assert.Equal(0, span[j]); + span[j] = 666; } } } @@ -95,7 +96,7 @@ namespace SixLabors.ImageSharp.Tests.Memory { using (Buffer2D buffer = Configuration.Default.MemoryManager.Allocate2D(width, height)) { - TestStructs.Foo[] array = buffer.Buffer.Array; + Span array = buffer.Buffer.Span; ref TestStructs.Foo actual = ref buffer[x, y]; diff --git a/tests/ImageSharp.Tests/Memory/BufferTests.cs b/tests/ImageSharp.Tests/Memory/BufferTests.cs deleted file mode 100644 index d0a83a094d..0000000000 --- a/tests/ImageSharp.Tests/Memory/BufferTests.cs +++ /dev/null @@ -1,251 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -namespace SixLabors.ImageSharp.Tests.Memory -{ - using System; - using System.Runtime.CompilerServices; - - using SixLabors.ImageSharp.Memory; - - using Xunit; - - public unsafe class BufferTests - { - // ReSharper disable once ClassNeverInstantiated.Local - private class Assert : Xunit.Assert - { - public static void SpanPointsTo(Span span, Buffer buffer, int bufferOffset = 0) - where T : struct - { - ref T actual = ref span.DangerousGetPinnableReference(); - ref T expected = ref Unsafe.Add(ref buffer[0], bufferOffset); - - Assert.True(Unsafe.AreSame(ref expected, ref actual), "span does not point to the expected position"); - } - - public static void Equal(void* expected, void* actual) - { - Assert.Equal((IntPtr)expected, (IntPtr)actual); - } - } - - [Theory] - [InlineData(42)] - [InlineData(1111)] - public void ConstructWithOwnArray(int count) - { - using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(count)) - { - Assert.False(buffer.IsDisposedOrLostArrayOwnership); - Assert.NotNull(buffer.Array); - Assert.Equal(count, buffer.Length); - Assert.True(buffer.Array.Length >= count); - } - } - - [Theory] - [InlineData(42)] - [InlineData(1111)] - public void ConstructWithExistingArray(int count) - { - TestStructs.Foo[] array = new TestStructs.Foo[count]; - using (Buffer buffer = new Buffer(array)) - { - Assert.False(buffer.IsDisposedOrLostArrayOwnership); - Assert.Equal(array, buffer.Array); - Assert.Equal(count, buffer.Length); - } - } - - [Fact] - public void Clear() - { - TestStructs.Foo[] a = { new TestStructs.Foo() { A = 1, B = 2 }, new TestStructs.Foo() { A = 3, B = 4 } }; - using (Buffer buffer = new Buffer(a)) - { - buffer.Clear(); - - Assert.Equal(default(TestStructs.Foo), a[0]); - Assert.Equal(default(TestStructs.Foo), a[1]); - } - } - - [Fact] - public void CreateClean() - { - for (int i = 0; i < 100; i++) - { - using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(42, true)) - { - for (int j = 0; j < buffer.Length; j++) - { - Assert.Equal(0, buffer.Array[j]); - buffer.Array[j] = 666; - } - } - } - } - - public class Indexer - { - public static readonly TheoryData IndexerData = - new TheoryData() - { - { 10, 0 }, - { 16, 3 }, - { 10, 9 } - }; - - [Theory] - [MemberData(nameof(IndexerData))] - public void Read(int length, int index) - { - TestStructs.Foo[] a = TestStructs.Foo.CreateArray(length); - - using (Buffer buffer = new Buffer(a)) - { - TestStructs.Foo element = buffer[index]; - - Assert.Equal(a[index], element); - } - } - - [Theory] - [MemberData(nameof(IndexerData))] - public void Write(int length, int index) - { - TestStructs.Foo[] a = TestStructs.Foo.CreateArray(length); - - using (Buffer buffer = new Buffer(a)) - { - buffer[index] = new TestStructs.Foo(666, 666); - - Assert.Equal(new TestStructs.Foo(666, 666), a[index]); - } - } - } - - [Fact] - public void Dispose() - { - Buffer buffer = Configuration.Default.MemoryManager.Allocate(42); - buffer.Dispose(); - - Assert.True(buffer.IsDisposedOrLostArrayOwnership); - } - - [Theory] - [InlineData(7)] - [InlineData(123)] - public void CastToSpan(int bufferLength) - { - using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(bufferLength)) - { - Span span = buffer; - - //Assert.Equal(buffer.Array, span.ToArray()); - //Assert.Equal(0, span.Start); - Assert.SpanPointsTo(span, buffer); - Assert.Equal(span.Length, bufferLength); - } - } - - [Fact] - public void Span() - { - using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(42)) - { - Span span = buffer.Span; - - // Assert.Equal(buffer.Array, span.ToArray()); - // Assert.Equal(0, span.Start); - Assert.SpanPointsTo(span, buffer); - Assert.Equal(42, span.Length); - } - } - - public class Slice - { - - [Theory] - [InlineData(7, 2)] - [InlineData(123, 17)] - public void WithStartOnly(int bufferLength, int start) - { - using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(bufferLength)) - { - Span span = buffer.Slice(start); - - Assert.SpanPointsTo(span, buffer, start); - Assert.Equal(span.Length, bufferLength - start); - } - } - - [Theory] - [InlineData(7, 2, 5)] - [InlineData(123, 17, 42)] - public void WithStartAndLength(int bufferLength, int start, int spanLength) - { - using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(bufferLength)) - { - Span span = buffer.Slice(start, spanLength); - - Assert.SpanPointsTo(span, buffer, start); - Assert.Equal(span.Length, spanLength); - } - } - } - - [Fact] - public void UnPinAndTakeArrayOwnership() - { - TestStructs.Foo[] data = null; - using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(42)) - { - data = buffer.TakeArrayOwnership(); - Assert.True(buffer.IsDisposedOrLostArrayOwnership); - } - - Assert.NotNull(data); - Assert.True(data.Length >= 42); - } - - public class Pin - { - [Fact] - public void ReturnsPinnedPointerToTheBeginningOfArray() - { - using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(42)) - { - TestStructs.Foo* actual = (TestStructs.Foo*)buffer.Pin(); - fixed (TestStructs.Foo* expected = buffer.Array) - { - Assert.Equal(expected, actual); - } - } - } - - [Fact] - public void SecondCallReturnsTheSamePointer() - { - using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(42)) - { - IntPtr ptr1 = buffer.Pin(); - IntPtr ptr2 = buffer.Pin(); - - Assert.Equal(ptr1, ptr2); - } - } - - [Fact] - public void WhenCalledOnDisposedBuffer_ThrowsInvalidOperationException() - { - Buffer buffer = Configuration.Default.MemoryManager.Allocate(42); - buffer.Dispose(); - - Assert.Throws(() => buffer.Pin()); - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Memory/SpanUtilityTests.cs b/tests/ImageSharp.Tests/Memory/SpanUtilityTests.cs index 757c8fcf9f..049c4c6ba9 100644 --- a/tests/ImageSharp.Tests/Memory/SpanUtilityTests.cs +++ b/tests/ImageSharp.Tests/Memory/SpanUtilityTests.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +// ReSharper disable InconsistentNaming +// ReSharper disable AccessToStaticMemberViaDerivedType namespace SixLabors.ImageSharp.Tests.Memory { using System; @@ -30,7 +32,7 @@ namespace SixLabors.ImageSharp.Tests.Memory { float[] stuff = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; - Span span = new Span(stuff); + var span = new Span(stuff); ref Vector v = ref span.FetchVector(); @@ -39,199 +41,8 @@ namespace SixLabors.ImageSharp.Tests.Memory Assert.Equal(2, v[2]); Assert.Equal(3, v[3]); } - - [Fact] - public void AsBytes() - { - TestStructs.Foo[] fooz = { new TestStructs.Foo(1, 2), new TestStructs.Foo(3, 4), new TestStructs.Foo(5, 6) }; - - using (Buffer colorBuf = new Buffer(fooz)) - { - Span orig = colorBuf.Slice(1); - Span asBytes = orig.AsBytes(); - - // Assert.Equal(asBytes.Start, sizeof(Foo)); - Assert.Equal(orig.Length * Unsafe.SizeOf(), asBytes.Length); - Assert.SameRefs(ref orig.DangerousGetPinnableReference(), ref asBytes.DangerousGetPinnableReference()); - } - } - - public class Construct - { - [Fact] - public void Basic() - { - TestStructs.Foo[] array = TestStructs.Foo.CreateArray(3); - - // Act: - Span span = new Span(array); - - // Assert: - Assert.Equal(array, span.ToArray()); - Assert.Equal(3, span.Length); - Assert.SameRefs(ref array[0], ref span.DangerousGetPinnableReference()); - } - - [Fact] - public void WithStart() - { - TestStructs.Foo[] array = TestStructs.Foo.CreateArray(4); - int start = 2; - - // Act: - Span span = new Span(array, start); - - // Assert: - Assert.SameRefs(ref array[start], ref span.DangerousGetPinnableReference()); - Assert.Equal(array.Length - start, span.Length); - } - - [Fact] - public void WithStartAndLength() - { - TestStructs.Foo[] array = TestStructs.Foo.CreateArray(10); - int start = 2; - int length = 3; - // Act: - Span span = new Span(array, start, length); - - // Assert: - Assert.SameRefs(ref array[start], ref span.DangerousGetPinnableReference()); - Assert.Equal(length, span.Length); - } - } - - public class Slice - { - [Fact] - public void StartOnly() - { - TestStructs.Foo[] array = TestStructs.Foo.CreateArray(5); - int start0 = 2; - int start1 = 2; - int totalOffset = start0 + start1; - - Span span = new Span(array, start0); - - // Act: - span = span.Slice(start1); - - // Assert: - Assert.SameRefs(ref array[totalOffset], ref span.DangerousGetPinnableReference()); - Assert.Equal(array.Length - totalOffset, span.Length); - } - - [Fact] - public void StartAndLength() - { - TestStructs.Foo[] array = TestStructs.Foo.CreateArray(10); - int start0 = 2; - int start1 = 2; - int totalOffset = start0 + start1; - int sliceLength = 3; - - Span span = new Span(array, start0); - - // Act: - span = span.Slice(start1, sliceLength); - - // Assert: - Assert.SameRefs(ref array[totalOffset], ref span.DangerousGetPinnableReference()); - Assert.Equal(sliceLength, span.Length); - } - } - - //[Theory] - //[InlineData(4)] - //[InlineData(1500)] - //public void Clear(int count) - //{ - // Foo[] array = Foo.CreateArray(count + 42); - - // int offset = 2; - // Span ap = new Span(array, offset); - - // // Act: - // ap.Clear(count); - - // Assert.NotEqual(default(Foo), array[offset - 1]); - // Assert.Equal(default(Foo), array[offset]); - // Assert.Equal(default(Foo), array[offset + count - 1]); - // Assert.NotEqual(default(Foo), array[offset + count]); - //} - - public class Indexer - { - public static readonly TheoryData IndexerData = - new TheoryData() - { - { 10, 0, 0 }, - { 10, 2, 0 }, - { 16, 0, 3 }, - { 16, 2, 3 }, - { 10, 0, 9 }, - { 10, 1, 8 } - }; - - [Theory] - [MemberData(nameof(IndexerData))] - public void Read(int length, int start, int index) - { - TestStructs.Foo[] a = TestStructs.Foo.CreateArray(length); - Span span = new Span(a, start); - - TestStructs.Foo element = span[index]; - - Assert.Equal(a[start + index], element); - } - - [Theory] - [MemberData(nameof(IndexerData))] - public void Write(int length, int start, int index) - { - TestStructs.Foo[] a = TestStructs.Foo.CreateArray(length); - Span span = new Span(a, start); - - span[index] = new TestStructs.Foo(666, 666); - - Assert.Equal(new TestStructs.Foo(666, 666), a[start + index]); - } - - [Theory] - [InlineData(10, 0, 0, 5)] - [InlineData(10, 1, 1, 5)] - [InlineData(10, 1, 1, 6)] - [InlineData(10, 1, 1, 7)] - public void AsBytes_Read(int length, int start, int index, int byteOffset) - { - TestStructs.Foo[] a = TestStructs.Foo.CreateArray(length); - Span span = new Span(a, start); - - Span bytes = span.AsBytes(); - - byte actual = bytes[index * Unsafe.SizeOf() + byteOffset]; - - ref byte baseRef = ref Unsafe.As(ref a[0]); - byte expected = Unsafe.Add(ref baseRef, (start + index) * Unsafe.SizeOf() + byteOffset); - - Assert.Equal(expected, actual); - } - } - - [Theory] - [InlineData(0, 4)] - [InlineData(2, 4)] - [InlineData(3, 4)] - public void DangerousGetPinnableReference(int start, int length) - { - TestStructs.Foo[] a = TestStructs.Foo.CreateArray(length); - Span span = new Span(a, start); - ref TestStructs.Foo r = ref span.DangerousGetPinnableReference(); - - Assert.True(Unsafe.AreSame(ref a[start], ref r)); - } - - public class Copy + + public class SpanHelper_Copy { private static void AssertNotDefault(T[] data, int idx) where T : struct @@ -267,8 +78,8 @@ namespace SixLabors.ImageSharp.Tests.Memory TestStructs.Foo[] source = TestStructs.Foo.CreateArray(count + 2); TestStructs.Foo[] dest = new TestStructs.Foo[count + 5]; - Span apSource = new Span(source, 1); - Span apDest = new Span(dest, 1); + var apSource = new Span(source, 1); + var apDest = new Span(dest, 1); SpanHelper.Copy(apSource, apDest, count - 1); @@ -290,8 +101,8 @@ namespace SixLabors.ImageSharp.Tests.Memory TestStructs.AlignedFoo[] source = TestStructs.AlignedFoo.CreateArray(count + 2); TestStructs.AlignedFoo[] dest = new TestStructs.AlignedFoo[count + 5]; - Span apSource = new Span(source, 1); - Span apDest = new Span(dest, 1); + var apSource = new Span(source, 1); + var apDest = new Span(dest, 1); SpanHelper.Copy(apSource, apDest, count - 1); @@ -313,8 +124,8 @@ namespace SixLabors.ImageSharp.Tests.Memory int[] source = CreateTestInts(count + 2); int[] dest = new int[count + 5]; - Span apSource = new Span(source, 1); - Span apDest = new Span(dest, 1); + var apSource = new Span(source, 1); + var apDest = new Span(dest, 1); SpanHelper.Copy(apSource, apDest, count - 1); @@ -337,8 +148,8 @@ namespace SixLabors.ImageSharp.Tests.Memory TestStructs.Foo[] source = TestStructs.Foo.CreateArray(count + 2); byte[] dest = new byte[destCount + sizeof(TestStructs.Foo) * 2]; - Span apSource = new Span(source, 1); - Span apDest = new Span(dest, sizeof(TestStructs.Foo)); + var apSource = new Span(source, 1); + var apDest = new Span(dest, sizeof(TestStructs.Foo)); SpanHelper.Copy(apSource.AsBytes(), apDest, (count - 1) * sizeof(TestStructs.Foo)); @@ -360,8 +171,8 @@ namespace SixLabors.ImageSharp.Tests.Memory TestStructs.AlignedFoo[] source = TestStructs.AlignedFoo.CreateArray(count + 2); byte[] dest = new byte[destCount + sizeof(TestStructs.AlignedFoo) * 2]; - Span apSource = new Span(source, 1); - Span apDest = new Span(dest, sizeof(TestStructs.AlignedFoo)); + var apSource = new Span(source, 1); + var apDest = new Span(dest, sizeof(TestStructs.AlignedFoo)); SpanHelper.Copy(apSource.AsBytes(), apDest, (count - 1) * sizeof(TestStructs.AlignedFoo)); @@ -383,8 +194,8 @@ namespace SixLabors.ImageSharp.Tests.Memory int[] source = CreateTestInts(count + 2); byte[] dest = new byte[destCount + sizeof(int) + 1]; - Span apSource = new Span(source); - Span apDest = new Span(dest); + var apSource = new Span(source); + var apDest = new Span(dest); SpanHelper.Copy(apSource.AsBytes(), apDest, count * sizeof(int)); @@ -404,8 +215,8 @@ namespace SixLabors.ImageSharp.Tests.Memory byte[] source = CreateTestBytes(srcCount); TestStructs.Foo[] dest = new TestStructs.Foo[count + 2]; - Span apSource = new Span(source); - Span apDest = new Span(dest); + var apSource = new Span(source); + var apDest = new Span(dest); SpanHelper.Copy(apSource, apDest.AsBytes(), count * sizeof(TestStructs.Foo)); @@ -417,26 +228,7 @@ namespace SixLabors.ImageSharp.Tests.Memory Assert.True((bool)ElementsAreEqual(dest, source, count - 1)); Assert.False((bool)ElementsAreEqual(dest, source, count)); } - - [Fact] - public void Color32ToBytes() - { - Rgba32[] colors = { new Rgba32(0, 1, 2, 3), new Rgba32(4, 5, 6, 7), new Rgba32(8, 9, 10, 11), }; - - using (Buffer colorBuf = new Buffer(colors)) - using (Buffer byteBuf = Configuration.Default.MemoryManager.Allocate(colors.Length * 4)) - { - SpanHelper.Copy(colorBuf.Span.AsBytes(), byteBuf, colorBuf.Length * sizeof(Rgba32)); - - byte[] a = byteBuf.Array; - - for (int i = 0; i < byteBuf.Length; i++) - { - Assert.Equal((byte)i, a[i]); - } - } - } - + internal static bool ElementsAreEqual(TestStructs.Foo[] array, byte[] rawArray, int index) { fixed (TestStructs.Foo* pArray = array) diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs index 39cc0f35ef..8787fba556 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs @@ -375,8 +375,8 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats } else { - TDest[] expected = this.ExpectedDestBuffer.Array; - TDest[] actual = this.ActualDestBuffer.Array; + Span expected = this.ExpectedDestBuffer.Span; + Span actual = this.ActualDestBuffer.Span; for (int i = 0; i < count; i++) { Assert.Equal(expected[i], actual[i]); @@ -402,7 +402,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats internal static Vector4[] CreateVector4TestData(int length) { Vector4[] result = new Vector4[length]; - Random rnd = new Random(42); // Deterministic random values + var rnd = new Random(42); // Deterministic random values for (int i = 0; i < result.Length; i++) { @@ -415,7 +415,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats { TPixel[] result = new TPixel[length]; - Random rnd = new Random(42); // Deterministic random values + var rnd = new Random(42); // Deterministic random values for (int i = 0; i < result.Length; i++) { @@ -429,7 +429,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats internal static byte[] CreateByteTestData(int length) { byte[] result = new byte[length]; - Random rnd = new Random(42); // Deterministic random values + var rnd = new Random(42); // Deterministic random values for (int i = 0; i < result.Length; i++) { From 1e70705621cf37b1c7cc7228cdb5b80eedef061c Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 19 Feb 2018 21:53:23 +0100 Subject: [PATCH 066/151] fix regression in GifDecoderCore --- src/ImageSharp/Formats/Gif/GifDecoderCore.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index c35d506dfe..da92665be5 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -151,8 +151,6 @@ namespace SixLabors.ImageSharp.Formats.Gif break; } - this.globalColorTable = this.MemoryManager.AllocateManagedByteBuffer(this.globalColorTableLength, true); - nextFlag = stream.ReadByte(); if (nextFlag == -1) { From b61574fdd047b6cce929fa65daf4761be32e7826 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 19 Feb 2018 22:23:20 +0100 Subject: [PATCH 067/151] clean up Buffer API --- src/ImageSharp/Memory/Buffer2D{T}.cs | 4 +- src/ImageSharp/Memory/Buffer{T}.cs | 83 +------------------ .../Memory/SimpleManagedMemoryManager.cs | 2 +- .../Processors/Transforms/WeightsWindow.cs | 5 +- .../Bulk/PackFromVector4ReferenceVsPointer.cs | 77 ----------------- .../General/IterateArray.cs | 73 ---------------- .../Formats/Jpg/JpegColorConverterTests.cs | 2 +- .../Formats/Jpg/SpectralJpegTests.cs | 2 +- .../ImageSharp.Tests/Memory/Buffer2DTests.cs | 8 +- .../PixelFormats/PixelOperationsTests.cs | 16 ++-- .../ReferenceCodecs/SystemDrawingBridge.cs | 50 ++++++----- .../TestUtilities/TestImageExtensions.cs | 8 +- 12 files changed, 55 insertions(+), 275 deletions(-) delete mode 100644 tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs delete mode 100644 tests/ImageSharp.Benchmarks/General/IterateArray.cs diff --git a/src/ImageSharp/Memory/Buffer2D{T}.cs b/src/ImageSharp/Memory/Buffer2D{T}.cs index e527c90c07..6b18aaa8fc 100644 --- a/src/ImageSharp/Memory/Buffer2D{T}.cs +++ b/src/ImageSharp/Memory/Buffer2D{T}.cs @@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Memory /// The buffer to wrap /// The number of elements in a row /// The number of rows - public Buffer2D(Buffer wrappedBuffer, int width, int height) + public Buffer2D(IBuffer wrappedBuffer, int width, int height) { this.Buffer = wrappedBuffer; this.Width = width; @@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Memory public Span Span => this.Buffer.Span; - public Buffer Buffer { get; } + public IBuffer Buffer { get; } /// /// Gets a reference to the element at the specified position. diff --git a/src/ImageSharp/Memory/Buffer{T}.cs b/src/ImageSharp/Memory/Buffer{T}.cs index 1ee1571c84..55eb44820a 100644 --- a/src/ImageSharp/Memory/Buffer{T}.cs +++ b/src/ImageSharp/Memory/Buffer{T}.cs @@ -18,16 +18,6 @@ namespace SixLabors.ImageSharp.Memory { private MemoryManager memoryManager; - /// - /// A pointer to the first element of when pinned. - /// - private IntPtr pointer; - - /// - /// A handle that allows to access the managed as an unmanaged memory by pinning. - /// - private GCHandle handle; - // why is there such a rule? :S Protected should be fine for a field! #pragma warning disable SA1401 // Fields should be private /// @@ -36,22 +26,7 @@ namespace SixLabors.ImageSharp.Memory protected T[] array; #pragma warning restore SA1401 // Fields should be private - /// - /// Initializes a new instance of the class. - /// - /// The array to pin. - public Buffer(T[] array) - { - this.Length = array.Length; - this.array = array; - } - - /// - /// Initializes a new instance of the class. - /// - /// The array to pin. - /// The count of "relevant" elements in 'array'. - public Buffer(T[] array, int length) + internal Buffer(T[] array, int length, MemoryManager memoryManager) { if (array.Length < length) { @@ -60,22 +35,9 @@ namespace SixLabors.ImageSharp.Memory this.Length = length; this.array = array; - } - - internal Buffer(T[] array, int length, MemoryManager memoryManager) - : this(array, length) - { this.memoryManager = memoryManager; } - /// - /// Finalizes an instance of the class. - /// - ~Buffer() - { - this.UnPin(); - } - /// /// Gets a value indicating whether this instance is disposed, or has lost ownership of . /// @@ -140,7 +102,6 @@ namespace SixLabors.ImageSharp.Memory } this.IsDisposedOrLostArrayOwnership = true; - this.UnPin(); this.memoryManager?.Release(this); @@ -166,33 +127,10 @@ namespace SixLabors.ImageSharp.Memory } this.IsDisposedOrLostArrayOwnership = true; - this.UnPin(); - T[] array = this.array; + T[] a = this.array; this.array = null; this.memoryManager = null; - return array; - } - - /// - /// Pins . - /// - /// The pinned pointer - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public IntPtr Pin() - { - if (this.IsDisposedOrLostArrayOwnership) - { - throw new InvalidOperationException( - "Pin() is invalid on a buffer with IsDisposedOrLostArrayOwnership == true!"); - } - - if (this.pointer == IntPtr.Zero) - { - this.handle = GCHandle.Alloc(this.array, GCHandleType.Pinned); - this.pointer = this.handle.AddrOfPinnedObject(); - } - - return this.pointer; + return a; } /// @@ -202,20 +140,5 @@ namespace SixLabors.ImageSharp.Memory { return this.array; } - - /// - /// Unpins . - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void UnPin() - { - if (this.pointer == IntPtr.Zero || !this.handle.IsAllocated) - { - return; - } - - this.handle.Free(); - this.pointer = IntPtr.Zero; - } } } \ No newline at end of file diff --git a/src/ImageSharp/Memory/SimpleManagedMemoryManager.cs b/src/ImageSharp/Memory/SimpleManagedMemoryManager.cs index 12d8582c79..ac4098c71d 100644 --- a/src/ImageSharp/Memory/SimpleManagedMemoryManager.cs +++ b/src/ImageSharp/Memory/SimpleManagedMemoryManager.cs @@ -8,7 +8,7 @@ /// internal override Buffer Allocate(int length, bool clear) { - return new Buffer(new T[length], length); + return new Buffer(new T[length], length, this); } internal override IManagedByteBuffer AllocateManagedByteBuffer(int length, bool clear) diff --git a/src/ImageSharp/Processing/Processors/Transforms/WeightsWindow.cs b/src/ImageSharp/Processing/Processors/Transforms/WeightsWindow.cs index 1ee61a9a60..399b3db842 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/WeightsWindow.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/WeightsWindow.cs @@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Processing.Processors /// /// The buffer containing the weights values. /// - private readonly Buffer buffer; + private readonly IBuffer buffer; /// /// Initializes a new instance of the struct. @@ -57,7 +57,8 @@ namespace SixLabors.ImageSharp.Processing.Processors [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref float GetStartReference() { - return ref this.buffer[this.flatStartIndex]; + Span span = this.buffer.Span; + return ref span[this.flatStartIndex]; } /// diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs deleted file mode 100644 index 8925fe9038..0000000000 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs +++ /dev/null @@ -1,77 +0,0 @@ -namespace SixLabors.ImageSharp.Benchmarks -{ - using System.Numerics; - using System.Runtime.CompilerServices; - - using BenchmarkDotNet.Attributes; - - using SixLabors.ImageSharp; - using SixLabors.ImageSharp.Memory; - - /// - /// Compares two implementation candidates for general BulkPixelOperations.ToVector4(): - /// - One iterating with pointers - /// - One iterating with ref locals - /// - public unsafe class PackFromVector4ReferenceVsPointer - { - private Buffer destination; - - private Buffer source; - - [Params(16, 128, 1024)] - public int Count { get; set; } - - [GlobalSetup] - public void Setup() - { - this.destination = Configuration.Default.MemoryManager.Allocate(this.Count); - this.source = Configuration.Default.MemoryManager.Allocate(this.Count * 4); - this.source.Pin(); - this.destination.Pin(); - } - - [GlobalCleanup] - public void Cleanup() - { - this.source.Dispose(); - this.destination.Dispose(); - } - - [Benchmark(Baseline = true)] - public void PackUsingPointers() - { - Vector4* sp = (Vector4*)this.source.Pin(); - byte* dp = (byte*)this.destination.Pin(); - int count = this.Count; - int size = sizeof(Rgba32); - - for (int i = 0; i < count; i++) - { - Vector4 v = Unsafe.Read(sp); - Rgba32 c = default(Rgba32); - c.PackFromVector4(v); - Unsafe.Write(dp, c); - - sp++; - dp += size; - } - } - - [Benchmark] - public void PackUsingReferences() - { - ref Vector4 sp = ref this.source.DangerousGetPinnableReference(); - ref Rgba32 dp = ref this.destination.DangerousGetPinnableReference(); - int count = this.Count; - - for (int i = 0; i < count; i++) - { - dp.PackFromVector4(sp); - - sp = Unsafe.Add(ref sp, 1); - dp = Unsafe.Add(ref dp, 1); - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/IterateArray.cs b/tests/ImageSharp.Benchmarks/General/IterateArray.cs deleted file mode 100644 index e06d71f4a0..0000000000 --- a/tests/ImageSharp.Benchmarks/General/IterateArray.cs +++ /dev/null @@ -1,73 +0,0 @@ -namespace SixLabors.ImageSharp.Benchmarks.General -{ - using System.Numerics; - using System.Runtime.CompilerServices; - - using BenchmarkDotNet.Attributes; - - using SixLabors.ImageSharp.Memory; - - public class IterateArray - { - // Usual pinned stuff - private Buffer buffer; - - // An array that's not pinned by intent! - private Vector4[] array; - - [Params(64, 1024)] - public int Length { get; set; } - - [GlobalSetup] - public void Setup() - { - this.buffer = Configuration.Default.MemoryManager.Allocate(this.Length); - this.buffer.Pin(); - this.array = new Vector4[this.Length]; - } - - [Benchmark(Baseline = true)] - public Vector4 IterateIndexed() - { - Vector4 sum = new Vector4(); - Vector4[] a = this.array; - - for (int i = 0; i < a.Length; i++) - { - sum += a[i]; - } - return sum; - } - - [Benchmark] - public unsafe Vector4 IterateUsingPointers() - { - Vector4 sum = new Vector4(); - - Vector4* ptr = (Vector4*) this.buffer.Pin(); - Vector4* end = ptr + this.Length; - - for (; ptr < end; ptr++) - { - sum += *ptr; - } - - return sum; - } - - [Benchmark] - public Vector4 IterateUsingReferences() - { - Vector4 sum = new Vector4(); - - ref Vector4 start = ref this.array[0]; - - for (int i = 0; i < this.Length; i++) - { - sum += Unsafe.Add(ref start, i); - } - - return sum; - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs index ffac1eafa8..b5d4aaebe4 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs @@ -289,7 +289,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg } // no need to dispose when buffer is not array owner - buffers[i] = new Buffer2D(new Buffer(values, values.Length), values.Length, 1); + buffers[i] = new Buffer2D(new FakeBuffer(values), values.Length, 1); } return new JpegColorConverter.ComponentValues(buffers, 0); } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs index 46d8328833..4508c6863a 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs @@ -106,7 +106,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg this.Output.WriteLine($"Component{i}: {diff}"); averageDifference += diff.average; totalDifference += diff.total; - tolerance += libJpegComponent.SpectralBlocks.Buffer.Length; + tolerance += libJpegComponent.SpectralBlocks.Buffer.Span.Length; } averageDifference /= componentCount; diff --git a/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs index 50c3ff0050..6afce94fd3 100644 --- a/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs +++ b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs @@ -16,11 +16,11 @@ namespace SixLabors.ImageSharp.Tests.Memory // ReSharper disable once ClassNeverInstantiated.Local private class Assert : Xunit.Assert { - public static void SpanPointsTo(Span span, Buffer buffer, int bufferOffset = 0) + public static void SpanPointsTo(Span span, IBuffer buffer, int bufferOffset = 0) where T : struct { ref T actual = ref span.DangerousGetPinnableReference(); - ref T expected = ref Unsafe.Add(ref buffer[0], bufferOffset); + ref T expected = ref Unsafe.Add(ref buffer.DangerousGetPinnableReference(), bufferOffset); Assert.True(Unsafe.AreSame(ref expected, ref actual), "span does not point to the expected position"); } @@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Tests.Memory { Assert.Equal(width, buffer.Width); Assert.Equal(height, buffer.Height); - Assert.Equal(width * height, buffer.Buffer.Length); + Assert.Equal(width * height, buffer.Buffer.Length()); } } @@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp.Tests.Memory using (Buffer2D buffer = Configuration.Default.MemoryManager.Allocate2D(42, 42, true)) { Span span = buffer.Span; - for (int j = 0; j < buffer.Buffer.Length; j++) + for (int j = 0; j < span.Length; j++) { Assert.Equal(0, span[j]); span[j] = 666; diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs index 8787fba556..2ea06d7248 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs @@ -333,25 +333,23 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats where TSource : struct where TDest : struct { - public Buffer SourceBuffer { get; } + public TSource[] SourceBuffer { get; } public Buffer ActualDestBuffer { get; } - public Buffer ExpectedDestBuffer { get; } + public TDest[] ExpectedDestBuffer { get; } public Span Source => this.SourceBuffer; public Span ActualDest => this.ActualDestBuffer; public TestBuffers(TSource[] source, TDest[] expectedDest) { - this.SourceBuffer = new Buffer(source); - this.ExpectedDestBuffer = new Buffer(expectedDest); + this.SourceBuffer = source; + this.ExpectedDestBuffer = expectedDest; this.ActualDestBuffer = Configuration.Default.MemoryManager.Allocate(expectedDest.Length); } public void Dispose() { - this.SourceBuffer.Dispose(); this.ActualDestBuffer.Dispose(); - this.ExpectedDestBuffer.Dispose(); } private const float Tolerance = 0.0001f; @@ -363,7 +361,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats if (typeof(TDest) == typeof(Vector4)) { - Span expected = this.ExpectedDestBuffer.Span.NonPortableCast(); + Span expected = this.ExpectedDestBuffer.AsSpan().NonPortableCast(); Span actual = this.ActualDestBuffer.Span.NonPortableCast(); for (int i = 0; i < count; i++) @@ -375,7 +373,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats } else { - Span expected = this.ExpectedDestBuffer.Span; + Span expected = this.ExpectedDestBuffer.AsSpan(); Span actual = this.ActualDestBuffer.Span; for (int i = 0; i < count; i++) { @@ -388,7 +386,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats internal static void TestOperation( TSource[] source, TDest[] expected, - Action, Buffer> action) + Action> action) where TSource : struct where TDest : struct { diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs index d14a0165aa..b8907e81e3 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs @@ -96,18 +96,20 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs var image = new Image(w, h); - using (var workBuffer = Configuration.Default.MemoryManager.Allocate(w)) + using (IBuffer workBuffer = Configuration.Default.MemoryManager.Allocate(w)) { - var destPtr = (Argb32*)workBuffer.Pin(); - for (int y = 0; y < h; y++) + fixed (Argb32* destPtr = &workBuffer.DangerousGetPinnableReference()) { - Span row = image.Frames.RootFrame.GetPixelRowSpan(y); + for (int y = 0; y < h; y++) + { + Span row = image.Frames.RootFrame.GetPixelRowSpan(y); - byte* sourcePtr = sourcePtrBase + data.Stride * y; + byte* sourcePtr = sourcePtrBase + data.Stride * y; - Buffer.MemoryCopy(sourcePtr, destPtr, destRowByteCount, sourceRowByteCount); + Buffer.MemoryCopy(sourcePtr, destPtr, destRowByteCount, sourceRowByteCount); - FromArgb32(workBuffer, row); + FromArgb32(workBuffer.Span, row); + } } } @@ -138,18 +140,20 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs var image = new Image(w, h); - using (var workBuffer = Configuration.Default.MemoryManager.Allocate(w)) + using (IBuffer workBuffer = Configuration.Default.MemoryManager.Allocate(w)) { - var destPtr = (Rgb24*)workBuffer.Pin(); - for (int y = 0; y < h; y++) + fixed (Rgb24* destPtr = &workBuffer.DangerousGetPinnableReference()) { - Span row = image.Frames.RootFrame.GetPixelRowSpan(y); + for (int y = 0; y < h; y++) + { + Span row = image.Frames.RootFrame.GetPixelRowSpan(y); - byte* sourcePtr = sourcePtrBase + data.Stride * y; + byte* sourcePtr = sourcePtrBase + data.Stride * y; - Buffer.MemoryCopy(sourcePtr, destPtr, destRowByteCount, sourceRowByteCount); + Buffer.MemoryCopy(sourcePtr, destPtr, destRowByteCount, sourceRowByteCount); - FromRgb24(workBuffer, row); + FromRgb24(workBuffer.Span, row); + } } } @@ -170,17 +174,19 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs long destRowByteCount = data.Stride; long sourceRowByteCount = w * sizeof(Argb32); - using (var workBuffer = Configuration.Default.MemoryManager.Allocate(w)) + using (IBuffer workBuffer = image.GetConfiguration().MemoryManager.Allocate(w)) { - var sourcePtr = (Argb32*)workBuffer.Pin(); - - for (int y = 0; y < h; y++) + fixed (Argb32* sourcePtr = &workBuffer.DangerousGetPinnableReference()) { - Span row = image.Frames.RootFrame.GetPixelRowSpan(y); - ToArgb32(row, workBuffer); - byte* destPtr = destPtrBase + data.Stride * y; - Buffer.MemoryCopy(sourcePtr, destPtr, destRowByteCount, sourceRowByteCount); + for (int y = 0; y < h; y++) + { + Span row = image.Frames.RootFrame.GetPixelRowSpan(y); + ToArgb32(row, workBuffer.Span); + byte* destPtr = destPtrBase + data.Stride * y; + + Buffer.MemoryCopy(sourcePtr, destPtr, destRowByteCount, sourceRowByteCount); + } } } diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index 54e55f7e56..b830acdefc 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -151,7 +151,7 @@ namespace SixLabors.ImageSharp.Tests bool appendPixelTypeToFileName = true) where TPixel : struct, IPixel { - using (Image firstFrameOnlyImage = new Image(image.Width, image.Height)) + using (var firstFrameOnlyImage = new Image(image.Width, image.Height)) using (Image referenceImage = GetReferenceOutputImage( provider, testOutputDetails, @@ -378,9 +378,11 @@ namespace SixLabors.ImageSharp.Tests Span pixels = image.Frames.RootFrame.GetPixelSpan(); - for (int i = 0; i < buffer.Buffer.Length; i++) + Span bufferSpan = buffer.Span; + + for (int i = 0; i < bufferSpan.Length; i++) { - float value = buffer.Buffer[i] * scale; + float value = bufferSpan[i] * scale; var v = new Vector4(value, value, value, 1f); pixels[i].PackFromVector4(v); } From 7881fdd341ad1da9e7a10812b690593e820507d6 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 20 Feb 2018 02:07:27 +0100 Subject: [PATCH 068/151] Hide Buffer indexer + !! WuQuantizer review in comments !! --- .../Brushes/ImageBrush{TPixel}.cs | 13 +- .../Brushes/PatternBrush{TPixel}.cs | 13 +- .../Brushes/Processors/BrushApplicator.cs | 13 +- .../Brushes/RecolorBrush{TPixel}.cs | 13 +- .../Brushes/SolidBrush{TPixel}.cs | 15 +- .../Processors/DrawImageProcessor.cs | 9 +- .../Processors/FillProcessor.cs | 24 +-- .../Processors/FillRegionProcessor.cs | 4 +- src/ImageSharp/Formats/Gif/LzwDecoder.cs | 26 +-- src/ImageSharp/Formats/Gif/LzwEncoder.cs | 39 ++-- .../Common/Decoder/JpegImagePostProcessor.cs | 4 +- .../GolangPort/Components/Decoder/Bytes.cs | 22 ++- .../PdfJsPort/Components/PdfJsHuffmanTable.cs | 10 +- .../Components/PdfJsJpegPixelArea.cs | 8 +- .../PdfJsPort/Components/PdfJsScanDecoder.cs | 14 +- .../Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs | 4 +- .../{FakeBuffer.cs => BasicArrayBuffer.cs} | 10 +- src/ImageSharp/Memory/Buffer{T}.cs | 69 +------ src/ImageSharp/Memory/MemoryManager.cs | 4 +- .../Effects/BackgroundColorProcessor.cs | 14 +- .../Processors/Overlays/GlowProcessor.cs | 16 +- .../Processors/Overlays/VignetteProcessor.cs | 18 +- .../Processors/Transforms/ResizeProcessor.cs | 8 +- .../Quantizers/QuantizerBase{TPixel}.cs | 1 + .../Quantizers/WuQuantizer{TPixel}.cs | 173 +++++++++++------- .../Color/Bulk/PackFromVector4.cs | 4 +- .../Color/Bulk/PackFromXyzw.cs | 4 +- .../Color/Bulk/ToVector4.cs | 4 +- .../ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs | 4 +- .../Color/Bulk/ToXyzw.cs | 4 +- .../PixelBlenders/PorterDuffBulkVsPixel.cs | 42 ++--- tests/ImageSharp.Benchmarks/Samplers/Glow.cs | 5 +- .../Formats/Jpg/JpegColorConverterTests.cs | 2 +- .../PixelFormats/PixelOperationsTests.cs | 31 ++-- .../ReferenceCodecs/SystemDrawingBridge.cs | 21 ++- 35 files changed, 334 insertions(+), 331 deletions(-) rename src/ImageSharp/Memory/{FakeBuffer.cs => BasicArrayBuffer.cs} (82%) diff --git a/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs index ff69d65ee6..5866d9feae 100644 --- a/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs @@ -122,24 +122,27 @@ namespace SixLabors.ImageSharp.Drawing.Brushes internal override void Apply(Span scanline, int x, int y) { // Create a span for colors - using (var amountBuffer = this.Target.MemoryManager.Allocate(scanline.Length)) - using (var overlay = this.Target.MemoryManager.Allocate(scanline.Length)) + using (Buffer amountBuffer = this.Target.MemoryManager.Allocate(scanline.Length)) + using (Buffer overlay = this.Target.MemoryManager.Allocate(scanline.Length)) { + Span amountSpan = amountBuffer.Span; + Span overlaySpan = overlay.Span; + int sourceY = (y - this.offsetY) % this.yLength; int offsetX = x - this.offsetX; Span sourceRow = this.source.GetPixelRowSpan(sourceY); for (int i = 0; i < scanline.Length; i++) { - amountBuffer[i] = scanline[i] * this.Options.BlendPercentage; + amountSpan[i] = scanline[i] * this.Options.BlendPercentage; int sourceX = (i + offsetX) % this.xLength; TPixel pixel = sourceRow[sourceX]; - overlay[i] = pixel; + overlaySpan[i] = pixel; } Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); - this.Blender.Blend(destinationRow, destinationRow, overlay, amountBuffer); + this.Blender.Blend(destinationRow, destinationRow, overlaySpan, amountSpan); } } } diff --git a/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs index 2a25979873..ac8ffa7941 100644 --- a/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs @@ -152,19 +152,22 @@ namespace SixLabors.ImageSharp.Drawing.Brushes internal override void Apply(Span scanline, int x, int y) { int patternY = y % this.pattern.Height; - using (var amountBuffer = this.Target.MemoryManager.Allocate(scanline.Length)) - using (var overlay = this.Target.MemoryManager.Allocate(scanline.Length)) + using (Buffer amountBuffer = this.Target.MemoryManager.Allocate(scanline.Length)) + using (Buffer overlay = this.Target.MemoryManager.Allocate(scanline.Length)) { + Span amountSpan = amountBuffer.Span; + Span overlaySpan = overlay.Span; + for (int i = 0; i < scanline.Length; i++) { - amountBuffer[i] = (scanline[i] * this.Options.BlendPercentage).Clamp(0, 1); + amountSpan[i] = (scanline[i] * this.Options.BlendPercentage).Clamp(0, 1); int patternX = (x + i) % this.pattern.Width; - overlay[i] = this.pattern[patternY, patternX]; + overlaySpan[i] = this.pattern[patternY, patternX]; } Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); - this.Blender.Blend(destinationRow, destinationRow, overlay, amountBuffer); + this.Blender.Blend(destinationRow, destinationRow, overlaySpan, amountSpan); } } } diff --git a/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs b/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs index 08bbb571ad..dadd546e93 100644 --- a/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs +++ b/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs @@ -65,21 +65,24 @@ namespace SixLabors.ImageSharp.Drawing.Brushes.Processors /// scanlineBuffer will be > scanlineWidth but provide and offset in case we want to share a larger buffer across runs. internal virtual void Apply(Span scanline, int x, int y) { - using (var amountBuffer = this.Target.MemoryManager.Allocate(scanline.Length)) - using (var overlay = this.Target.MemoryManager.Allocate(scanline.Length)) + using (Buffer amountBuffer = this.Target.MemoryManager.Allocate(scanline.Length)) + using (Buffer overlay = this.Target.MemoryManager.Allocate(scanline.Length)) { + Span amountSpan = amountBuffer.Span; + Span overlaySpan = overlay.Span; + for (int i = 0; i < scanline.Length; i++) { if (this.Options.BlendPercentage < 1) { - amountBuffer[i] = scanline[i] * this.Options.BlendPercentage; + amountSpan[i] = scanline[i] * this.Options.BlendPercentage; } - overlay[i] = this[x + i, y]; + overlaySpan[i] = this[x + i, y]; } Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); - this.Blender.Blend(destinationRow, destinationRow, overlay, amountBuffer); + this.Blender.Blend(destinationRow, destinationRow, overlaySpan, amountSpan); } } } diff --git a/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs index d480457113..d1fda7ebe6 100644 --- a/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs @@ -144,22 +144,25 @@ namespace SixLabors.ImageSharp.Drawing.Brushes /// internal override void Apply(Span scanline, int x, int y) { - using (var amountBuffer = this.Target.MemoryManager.Allocate(scanline.Length)) - using (var overlay = this.Target.MemoryManager.Allocate(scanline.Length)) + using (Buffer amountBuffer = this.Target.MemoryManager.Allocate(scanline.Length)) + using (Buffer overlay = this.Target.MemoryManager.Allocate(scanline.Length)) { + Span amountSpan = amountBuffer.Span; + Span overlaySpan = overlay.Span; + for (int i = 0; i < scanline.Length; i++) { - amountBuffer[i] = scanline[i] * this.Options.BlendPercentage; + amountSpan[i] = scanline[i] * this.Options.BlendPercentage; int offsetX = x + i; // no doubt this one can be optermised further but I can't imagine its // actually being used and can probably be removed/interalised for now - overlay[i] = this[offsetX, y]; + overlaySpan[i] = this[offsetX, y]; } Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); - this.Blender.Blend(destinationRow, destinationRow, overlay, amountBuffer); + this.Blender.Blend(destinationRow, destinationRow, overlaySpan, amountSpan); } } } diff --git a/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs index 2d460603bb..510299f263 100644 --- a/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs @@ -62,10 +62,7 @@ namespace SixLabors.ImageSharp.Drawing.Brushes : base(source, options) { this.Colors = source.MemoryManager.Allocate(source.Width); - for (int i = 0; i < this.Colors.Length; i++) - { - this.Colors[i] = color; - } + this.Colors.Span.Fill(color); } /// @@ -81,7 +78,7 @@ namespace SixLabors.ImageSharp.Drawing.Brushes /// /// The color /// - internal override TPixel this[int x, int y] => this.Colors[x]; + internal override TPixel this[int x, int y] => this.Colors.Span[x]; /// public override void Dispose() @@ -96,14 +93,16 @@ namespace SixLabors.ImageSharp.Drawing.Brushes { Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); - using (var amountBuffer = this.Target.MemoryManager.Allocate(scanline.Length)) + using (Buffer amountBuffer = this.Target.MemoryManager.Allocate(scanline.Length)) { + Span amountSpan = amountBuffer.Span; + for (int i = 0; i < scanline.Length; i++) { - amountBuffer[i] = scanline[i] * this.Options.BlendPercentage; + amountSpan[i] = scanline[i] * this.Options.BlendPercentage; } - this.Blender.Blend(destinationRow, destinationRow, this.Colors, amountBuffer); + this.Blender.Blend(destinationRow, destinationRow, this.Colors.Span, amountSpan); } } catch (Exception) diff --git a/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs b/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs index 201adfecc0..eb3949b007 100644 --- a/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs @@ -84,12 +84,9 @@ namespace SixLabors.ImageSharp.Drawing.Processors maxY = Math.Min(this.Location.Y + this.Size.Height, maxY); int width = maxX - minX; - using (var amount = this.Image.GetConfiguration().MemoryManager.Allocate(width)) + using (Buffer amount = this.Image.GetConfiguration().MemoryManager.Allocate(width)) { - for (int i = 0; i < width; i++) - { - amount[i] = this.Alpha; - } + amount.Span.Fill(this.Alpha); Parallel.For( minY, @@ -99,7 +96,7 @@ namespace SixLabors.ImageSharp.Drawing.Processors { Span background = source.GetPixelRowSpan(y).Slice(minX, width); Span foreground = targetImage.GetPixelRowSpan(y - this.Location.Y).Slice(targetX, width); - this.blender.Blend(background, background, foreground, amount); + this.blender.Blend(background, background, foreground, amount.Span); }); } } diff --git a/src/ImageSharp.Drawing/Processors/FillProcessor.cs b/src/ImageSharp.Drawing/Processors/FillProcessor.cs index 0174a63880..75ea1f2033 100644 --- a/src/ImageSharp.Drawing/Processors/FillProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/FillProcessor.cs @@ -66,25 +66,25 @@ namespace SixLabors.ImageSharp.Drawing.Processors int width = maxX - minX; - using (var amount = source.MemoryManager.Allocate(width)) - using (BrushApplicator applicator = this.brush.CreateApplicator(source, sourceRectangle, this.options)) + using (Buffer amount = source.MemoryManager.Allocate(width)) + using (BrushApplicator applicator = this.brush.CreateApplicator( + source, + sourceRectangle, + this.options)) { - for (int i = 0; i < width; i++) - { - amount[i] = this.options.BlendPercentage; - } + amount.Span.Fill(this.options.BlendPercentage); - Parallel.For( + Parallel.For( minY, maxY, configuration.ParallelOptions, y => - { - int offsetY = y - startY; - int offsetX = minX - startX; + { + int offsetY = y - startY; + int offsetX = minX - startX; - applicator.Apply(amount, offsetX, offsetY); - }); + applicator.Apply(amount.Span, offsetX, offsetY); + }); } } } diff --git a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs index fc3f289abf..ae5ee2d9f6 100644 --- a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs @@ -99,8 +99,8 @@ namespace SixLabors.ImageSharp.Drawing.Processors using (BrushApplicator applicator = this.Brush.CreateApplicator(source, rect, this.Options)) { int scanlineWidth = maxX - minX; - using (FakeBuffer buffer = source.MemoryManager.AllocateFake(maxIntersections)) - using (FakeBuffer scanline = source.MemoryManager.AllocateFake(scanlineWidth)) + using (BasicArrayBuffer buffer = source.MemoryManager.AllocateFake(maxIntersections)) + using (BasicArrayBuffer scanline = source.MemoryManager.AllocateFake(scanlineWidth)) { bool scanlineDirty = true; for (int y = minY; y < maxY; y++) diff --git a/src/ImageSharp/Formats/Gif/LzwDecoder.cs b/src/ImageSharp/Formats/Gif/LzwDecoder.cs index b28857e573..27ca275db1 100644 --- a/src/ImageSharp/Formats/Gif/LzwDecoder.cs +++ b/src/ImageSharp/Formats/Gif/LzwDecoder.cs @@ -115,10 +115,14 @@ namespace SixLabors.ImageSharp.Formats.Gif int data = 0; int first = 0; + Span prefixSpan = this.prefix.Span; + Span suffixSpan = this.suffix.Span; + Span pixelStackSpan = this.pixelStack.Span; + for (code = 0; code < clearCode; code++) { - this.prefix[code] = 0; - this.suffix[code] = (byte)code; + prefixSpan[code] = 0; + suffixSpan[code] = (byte)code; } byte[] buffer = new byte[255]; @@ -172,7 +176,7 @@ namespace SixLabors.ImageSharp.Formats.Gif if (oldCode == NullCode) { - this.pixelStack[top++] = this.suffix[code]; + pixelStackSpan[top++] = suffixSpan[code]; oldCode = code; first = code; continue; @@ -181,27 +185,27 @@ namespace SixLabors.ImageSharp.Formats.Gif int inCode = code; if (code == availableCode) { - this.pixelStack[top++] = (byte)first; + pixelStackSpan[top++] = (byte)first; code = oldCode; } while (code > clearCode) { - this.pixelStack[top++] = this.suffix[code]; - code = this.prefix[code]; + pixelStackSpan[top++] = suffixSpan[code]; + code = prefixSpan[code]; } - first = this.suffix[code]; + first = suffixSpan[code]; - this.pixelStack[top++] = this.suffix[code]; + pixelStackSpan[top++] = suffixSpan[code]; // Fix for Gifs that have "deferred clear code" as per here : // https://bugzilla.mozilla.org/show_bug.cgi?id=55918 if (availableCode < MaxStackSize) { - this.prefix[availableCode] = oldCode; - this.suffix[availableCode] = first; + prefixSpan[availableCode] = oldCode; + suffixSpan[availableCode] = first; availableCode++; if (availableCode == codeMask + 1 && availableCode < MaxStackSize) { @@ -217,7 +221,7 @@ namespace SixLabors.ImageSharp.Formats.Gif top--; // Clear missing pixels - pixels[xyz++] = (byte)this.pixelStack[top]; + pixels[xyz++] = (byte)pixelStackSpan[top]; } } diff --git a/src/ImageSharp/Formats/Gif/LzwEncoder.cs b/src/ImageSharp/Formats/Gif/LzwEncoder.cs index 2ecd229b5f..115ecf6fbe 100644 --- a/src/ImageSharp/Formats/Gif/LzwEncoder.cs +++ b/src/ImageSharp/Formats/Gif/LzwEncoder.cs @@ -259,7 +259,7 @@ namespace SixLabors.ImageSharp.Formats.Gif /// The output stream. private void ClearBlock(Stream stream) { - this.ResetCodeTable(this.hsize); + this.ResetCodeTable(); this.freeEntry = this.clearCode + 2; this.clearFlag = true; @@ -269,13 +269,15 @@ namespace SixLabors.ImageSharp.Formats.Gif /// /// Reset the code table. /// - /// The hash size. - private void ResetCodeTable(int size) + private void ResetCodeTable() { - for (int i = 0; i < size; ++i) - { - this.hashTable[i] = -1; - } + this.hashTable.Span.Fill(-1); + + // Original code: + // for (int i = 0; i < size; ++i) + // { + // this.hashTable[i] = -1; + // } } /// @@ -317,23 +319,26 @@ namespace SixLabors.ImageSharp.Formats.Gif hsizeReg = this.hsize; - this.ResetCodeTable(hsizeReg); // clear hash table + this.ResetCodeTable(); // clear hash table this.Output(this.clearCode, stream); + Span hashTableSpan = this.hashTable.Span; + Span codeTableSpan = this.codeTable.Span; + while ((c = this.NextPixel()) != Eof) { fcode = (c << this.maxbits) + ent; int i = (c << hshift) ^ ent /* = 0 */; - if (this.hashTable[i] == fcode) + if (hashTableSpan[i] == fcode) { - ent = this.codeTable[i]; + ent = codeTableSpan[i]; continue; } // Non-empty slot - if (this.hashTable[i] >= 0) + if (hashTableSpan[i] >= 0) { int disp = hsizeReg - i; if (i == 0) @@ -348,15 +353,15 @@ namespace SixLabors.ImageSharp.Formats.Gif i += hsizeReg; } - if (this.hashTable[i] == fcode) + if (hashTableSpan[i] == fcode) { - ent = this.codeTable[i]; + ent = codeTableSpan[i]; break; } } - while (this.hashTable[i] >= 0); + while (hashTableSpan[i] >= 0); - if (this.hashTable[i] == fcode) + if (hashTableSpan[i] == fcode) { continue; } @@ -366,8 +371,8 @@ namespace SixLabors.ImageSharp.Formats.Gif ent = c; if (this.freeEntry < this.maxmaxcode) { - this.codeTable[i] = this.freeEntry++; // code -> hashtable - this.hashTable[i] = fcode; + codeTableSpan[i] = this.freeEntry++; // code -> hashtable + hashTableSpan[i] = fcode; } else { diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs index 1b83f62eb4..3258fd32cc 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs @@ -150,11 +150,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder int y = yy - this.PixelRowCounter; var values = new ColorConverters.JpegColorConverter.ComponentValues(buffers, y); - this.colorConverter.ConvertToRGBA(values, this.rgbaBuffer); + this.colorConverter.ConvertToRGBA(values, this.rgbaBuffer.Span); Span destRow = destination.GetPixelRowSpan(yy); - PixelOperations.Instance.PackFromVector4(this.rgbaBuffer, destRow, destination.Width); + PixelOperations.Instance.PackFromVector4(this.rgbaBuffer.Span, destRow, destination.Width); } } } diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bytes.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bytes.cs index 56a85bc9df..7a22b043b8 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bytes.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bytes.cs @@ -14,6 +14,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder /// Bytes is a byte buffer, similar to a stream, except that it /// has to be able to unread more than 1 byte, due to byte stuffing. /// Byte stuffing is specified in section F.1.2.3. + /// TODO: Optimize buffer management inside this class! /// internal struct Bytes : IDisposable { @@ -26,7 +27,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder /// Gets or sets the buffer. /// buffer[i:j] are the buffered bytes read from the underlying /// stream that haven't yet been passed further on. - /// TODO: Do we really need buffer here? Might be an optimiziation opportunity. /// public IManagedByteBuffer Buffer; @@ -88,7 +88,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder // Take the fast path if bytes.buf contains at least two bytes. if (this.I + 2 <= this.J) { - x = this.BufferAsInt[this.I]; + Span bufferSpan = this.BufferAsInt.Span; + x = bufferSpan[this.I]; this.I++; this.UnreadableBytes = 1; if (x != OrigJpegConstants.Markers.XFFInt) @@ -96,7 +97,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder return OrigDecoderErrorCode.NoError; } - if (this.BufferAsInt[this.I] != 0x00) + if (bufferSpan[this.I] != 0x00) { return OrigDecoderErrorCode.MissingFF00; } @@ -196,7 +197,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder } } - result = this.BufferAsInt[this.I]; + result = this.BufferAsInt.Span[this.I]; this.I++; this.UnreadableBytes = 0; return errorCode; @@ -230,20 +231,20 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder DecoderThrowHelper.ThrowImageFormatException.FillCalledWhenUnreadBytesExist(); } - Span bufferSpan = this.Buffer.Span; + Span byteSpan = this.Buffer.Span; // Move the last 2 bytes to the start of the buffer, in case we need // to call UnreadByteStuffedByte. if (this.J > 2) { - bufferSpan[0] = bufferSpan[this.J - 2]; - bufferSpan[1] = bufferSpan[this.J - 1]; + byteSpan[0] = byteSpan[this.J - 2]; + byteSpan[1] = byteSpan[this.J - 1]; this.I = 2; this.J = 2; } // Fill in the rest of the buffer. - int n = inputStream.Read(this.Buffer.Array, this.J, bufferSpan.Length - this.J); + int n = inputStream.Read(this.Buffer.Array, this.J, byteSpan.Length - this.J); if (n == 0) { return OrigDecoderErrorCode.UnexpectedEndOfStream; @@ -251,9 +252,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder this.J += n; - for (int i = 0; i < bufferSpan.Length; i++) + Span intSpan = this.BufferAsInt.Span; + for (int i = 0; i < byteSpan.Length; i++) { - this.BufferAsInt[i] = bufferSpan[i]; + intSpan[i] = byteSpan[i]; } return OrigDecoderErrorCode.NoError; diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs index 95631a7e66..b8694c538e 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs @@ -12,9 +12,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components /// internal struct PdfJsHuffmanTable : IDisposable { - private FakeBuffer lookahead; - private FakeBuffer valOffset; - private FakeBuffer maxcode; + private BasicArrayBuffer lookahead; + private BasicArrayBuffer valOffset; + private BasicArrayBuffer maxcode; private IManagedByteBuffer huffval; /// @@ -30,8 +30,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components this.valOffset = memoryManager.AllocateFake(18); this.maxcode = memoryManager.AllocateFake(18); - using (FakeBuffer huffsize = memoryManager.AllocateFake(257)) - using (FakeBuffer huffcode = memoryManager.AllocateFake(257)) + using (BasicArrayBuffer huffsize = memoryManager.AllocateFake(257)) + using (BasicArrayBuffer huffcode = memoryManager.AllocateFake(257)) { GenerateSizeTable(lengths, huffsize); GenerateCodeTable(huffsize, huffcode); diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsJpegPixelArea.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsJpegPixelArea.cs index ac26d892c1..a6f8780177 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsJpegPixelArea.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsJpegPixelArea.cs @@ -74,18 +74,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components var scale = new Vector2(this.imageWidth / (float)width, this.imageHeight / (float)height); this.componentData = this.memoryManager.Allocate(width * height * numberOfComponents); - Span componentDataSpan = this.componentData; + Span componentDataSpan = this.componentData.Span; const uint Mask3Lsb = 0xFFFFFFF8; // Used to clear the 3 LSBs - using (var xScaleBlockOffset = this.memoryManager.Allocate(width)) + using (IBuffer xScaleBlockOffset = this.memoryManager.Allocate(width)) { - Span xScaleBlockOffsetSpan = xScaleBlockOffset; + Span xScaleBlockOffsetSpan = xScaleBlockOffset.Span; for (int i = 0; i < numberOfComponents; i++) { ref PdfJsComponent component = ref components.Components[i]; Vector2 componentScale = component.Scale * scale; int offset = i; - Span output = component.Output; + Span output = component.Output.Span; int blocksPerScanline = (component.BlocksPerLine + 1) << 3; // Precalculate the xScaleBlockOffset diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsScanDecoder.cs index e2e5d985e6..9e245ea2c6 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsScanDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsScanDecoder.cs @@ -707,6 +707,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components [MethodImpl(MethodImplOptions.AggressiveInlining)] private void DecodeBaseline(PdfJsFrameComponent component, int offset, ref PdfJsHuffmanTable dcHuffmanTable, ref PdfJsHuffmanTable acHuffmanTable, Stream stream) { + Span blockDataSpan = component.BlockData.Span; + int t = this.DecodeHuffman(ref dcHuffmanTable, stream); if (this.endOfStreamReached || this.unexpectedMarkerReached) { @@ -714,7 +716,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components } int diff = t == 0 ? 0 : this.ReceiveAndExtend(t, stream); - component.BlockData[offset] = (short)(component.Pred += diff); + blockDataSpan[offset] = (short)(component.Pred += diff); int k = 1; while (k < 64) @@ -748,7 +750,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components byte z = PdfJsQuantizationTables.DctZigZag[k]; short re = (short)this.ReceiveAndExtend(s, stream); - component.BlockData[offset + z] = re; + blockDataSpan[offset + z] = re; k++; } } @@ -756,6 +758,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components [MethodImpl(MethodImplOptions.AggressiveInlining)] private void DecodeDCFirst(PdfJsFrameComponent component, int offset, ref PdfJsHuffmanTable dcHuffmanTable, Stream stream) { + Span blockDataSpan = component.BlockData.Span; + int t = this.DecodeHuffman(ref dcHuffmanTable, stream); if (this.endOfStreamReached || this.unexpectedMarkerReached) { @@ -763,19 +767,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components } int diff = t == 0 ? 0 : this.ReceiveAndExtend(t, stream) << this.successiveState; - component.BlockData[offset] = (short)(component.Pred += diff); + blockDataSpan[offset] = (short)(component.Pred += diff); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void DecodeDCSuccessive(PdfJsFrameComponent component, int offset, Stream stream) { + Span blockDataSpan = component.BlockData.Span; + int bit = this.ReadBit(stream); if (this.endOfStreamReached || this.unexpectedMarkerReached) { return; } - component.BlockData[offset] |= (short)(bit << this.successiveState); + blockDataSpan[offset] |= (short)(bit << this.successiveState); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs index f05a8a136d..6bcde2f487 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs @@ -790,14 +790,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort using (Buffer multiplicationBuffer = this.configuration.MemoryManager.Allocate(64, true)) { Span quantizationTable = this.quantizationTables.Tables.GetRowSpan(frameComponent.QuantizationTableIndex); - Span computationBufferSpan = computationBuffer; + Span computationBufferSpan = computationBuffer.Span; // For AA&N IDCT method, multiplier are equal to quantization // coefficients scaled by scalefactor[row]*scalefactor[col], where // scalefactor[0] = 1 // scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 // For integer operation, the multiplier table is to be scaled by 12. - Span multiplierSpan = multiplicationBuffer; + Span multiplierSpan = multiplicationBuffer.Span; // for (int i = 0; i < 64; i++) // { diff --git a/src/ImageSharp/Memory/FakeBuffer.cs b/src/ImageSharp/Memory/BasicArrayBuffer.cs similarity index 82% rename from src/ImageSharp/Memory/FakeBuffer.cs rename to src/ImageSharp/Memory/BasicArrayBuffer.cs index e4bc4e463c..d9eb5a19a1 100644 --- a/src/ImageSharp/Memory/FakeBuffer.cs +++ b/src/ImageSharp/Memory/BasicArrayBuffer.cs @@ -4,12 +4,12 @@ using System.Runtime.CompilerServices; namespace SixLabors.ImageSharp.Memory { /// - /// Temporal workaround providing a "Buffer" based on a generic array without the 'Unsafe.As()' hackery. + /// Exposes an array through the interface. /// - internal class FakeBuffer : IBuffer + internal class BasicArrayBuffer : IBuffer where T : struct { - public FakeBuffer(T[] array) + public BasicArrayBuffer(T[] array) { this.Array = array; } @@ -42,7 +42,7 @@ namespace SixLabors.ImageSharp.Memory /// /// The to convert. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator ReadOnlySpan(FakeBuffer buffer) + public static implicit operator ReadOnlySpan(BasicArrayBuffer buffer) { return new ReadOnlySpan(buffer.Array, 0, buffer.Length); } @@ -52,7 +52,7 @@ namespace SixLabors.ImageSharp.Memory /// /// The to convert. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator Span(FakeBuffer buffer) + public static implicit operator Span(BasicArrayBuffer buffer) { return new Span(buffer.Array, 0, buffer.Length); } diff --git a/src/ImageSharp/Memory/Buffer{T}.cs b/src/ImageSharp/Memory/Buffer{T}.cs index 55eb44820a..309cca1f41 100644 --- a/src/ImageSharp/Memory/Buffer{T}.cs +++ b/src/ImageSharp/Memory/Buffer{T}.cs @@ -38,11 +38,6 @@ namespace SixLabors.ImageSharp.Memory this.memoryManager = memoryManager; } - /// - /// Gets a value indicating whether this instance is disposed, or has lost ownership of . - /// - public bool IsDisposedOrLostArrayOwnership { get; private set; } - /// /// Gets the count of "relevant" elements. It's usually smaller than 'Array.Length' when is pooled. /// @@ -51,44 +46,7 @@ namespace SixLabors.ImageSharp.Memory /// /// Gets a to the backing buffer. /// - public Span Span => this; - - /// - /// Returns a reference to specified element of the buffer. - /// - /// The index - /// The reference to the specified element - public ref T this[int index] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - DebugGuard.MustBeLessThan(index, this.Length, nameof(index)); - - Span span = this.Span; - return ref span[index]; - } - } - - /// - /// Converts to an . - /// - /// The to convert. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator ReadOnlySpan(Buffer buffer) - { - return new ReadOnlySpan(buffer.array, 0, buffer.Length); - } - - /// - /// Converts to an . - /// - /// The to convert. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator Span(Buffer buffer) - { - return new Span(buffer.array, 0, buffer.Length); - } + public Span Span => new Span(this.array, 0, this.Length); /// /// Disposes the instance by unpinning the array, and returning the pooled buffer when necessary. @@ -96,13 +54,11 @@ namespace SixLabors.ImageSharp.Memory [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Dispose() { - if (this.IsDisposedOrLostArrayOwnership) + if (this.array == null) { return; } - this.IsDisposedOrLostArrayOwnership = true; - this.memoryManager?.Release(this); this.memoryManager = null; @@ -112,27 +68,6 @@ namespace SixLabors.ImageSharp.Memory GC.SuppressFinalize(this); } - /// - /// Unpins and makes the object "quasi-disposed" so the array is no longer owned by this object. - /// If is rented, it's the callers responsibility to return it to it's pool. - /// - /// The unpinned - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public T[] TakeArrayOwnership() - { - if (this.IsDisposedOrLostArrayOwnership) - { - throw new InvalidOperationException( - "TakeArrayOwnership() is invalid: either Buffer is disposed or TakeArrayOwnership() has been called multiple times!"); - } - - this.IsDisposedOrLostArrayOwnership = true; - T[] a = this.array; - this.array = null; - this.memoryManager = null; - return a; - } - /// /// TODO: Refactor this /// diff --git a/src/ImageSharp/Memory/MemoryManager.cs b/src/ImageSharp/Memory/MemoryManager.cs index 58f2458193..540c045de3 100644 --- a/src/ImageSharp/Memory/MemoryManager.cs +++ b/src/ImageSharp/Memory/MemoryManager.cs @@ -37,10 +37,10 @@ namespace SixLabors.ImageSharp.Memory /// Temporal workaround. A method providing a "Buffer" based on a generic array without the 'Unsafe.As()' hackery. /// Should be replaced with 'Allocate()' as soon as SixLabors.Shapes has Span-based API-s! /// - internal FakeBuffer AllocateFake(int length, bool dummy = false) + internal BasicArrayBuffer AllocateFake(int length, bool dummy = false) where T : struct { - return new FakeBuffer(new T[length]); + return new BasicArrayBuffer(new T[length]); } } } diff --git a/src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs index 296ae1bb37..a42a2056c4 100644 --- a/src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs @@ -71,13 +71,17 @@ namespace SixLabors.ImageSharp.Processing.Processors int width = maxX - minX; - using (var colors = this.memoryManager.Allocate(width)) - using (var amount = this.memoryManager.Allocate(width)) + using (Buffer colors = this.memoryManager.Allocate(width)) + using (Buffer amount = this.memoryManager.Allocate(width)) { + // Be careful! Do not capture colorSpan & amountSpan in the lambda below! + Span colorSpan = colors.Span; + Span amountSpan = amount.Span; + for (int i = 0; i < width; i++) { - colors[i] = this.Value; - amount[i] = this.options.BlendPercentage; + colorSpan[i] = this.Value; + amountSpan[i] = this.options.BlendPercentage; } PixelBlender blender = PixelOperations.Instance.GetPixelBlender(this.options.BlenderMode); @@ -90,7 +94,7 @@ namespace SixLabors.ImageSharp.Processing.Processors Span destination = source.GetPixelRowSpan(y - startY).Slice(minX - startX, width); // This switched color & destination in the 2nd and 3rd places because we are applying the target colour under the current one - blender.Blend(destination, colors, destination, amount); + blender.Blend(destination, colors.Span, destination, amount.Span); }); } } diff --git a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs index 6114c64387..85c592ceaa 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs @@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.Processing.Processors TPixel glowColor = this.GlowColor; Vector2 centre = Rectangle.Center(sourceRectangle); - var finalRadius = this.Radius.Calculate(source.Size()); + float finalRadius = this.Radius.Calculate(source.Size()); float maxDistance = finalRadius > 0 ? MathF.Min(finalRadius, sourceRectangle.Width * .5F) : sourceRectangle.Width * .5F; @@ -87,11 +87,14 @@ namespace SixLabors.ImageSharp.Processing.Processors } int width = maxX - minX; - using (var rowColors = this.memoryManager.Allocate(width)) + using (IBuffer rowColors = this.memoryManager.Allocate(width)) { + // Be careful! Do not capture rowColorsSpan in the lambda below! + Span rowColorsSpan = rowColors.Span; + for (int i = 0; i < width; i++) { - rowColors[i] = glowColor; + rowColorsSpan[i] = glowColor; } Parallel.For( @@ -100,19 +103,20 @@ namespace SixLabors.ImageSharp.Processing.Processors configuration.ParallelOptions, y => { - using (var amounts = this.memoryManager.Allocate(width)) + using (IBuffer amounts = this.memoryManager.Allocate(width)) { + Span amountsSpan = amounts.Span; int offsetY = y - startY; int offsetX = minX - startX; for (int i = 0; i < width; i++) { float distance = Vector2.Distance(centre, new Vector2(i + offsetX, offsetY)); - amounts[i] = (this.options.BlendPercentage * (1 - (.95F * (distance / maxDistance)))).Clamp(0, 1); + amountsSpan[i] = (this.options.BlendPercentage * (1 - (.95F * (distance / maxDistance)))).Clamp(0, 1); } Span destination = source.GetPixelRowSpan(offsetY).Slice(offsetX, width); - this.blender.Blend(destination, destination, rowColors, amounts); + this.blender.Blend(destination, destination, rowColors.Span, amountsSpan); } }); } diff --git a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs index 9877f4cc9d..d0943b27bb 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs @@ -86,8 +86,8 @@ namespace SixLabors.ImageSharp.Processing.Processors TPixel vignetteColor = this.VignetteColor; Vector2 centre = Rectangle.Center(sourceRectangle); - var finalradiusX = this.RadiusX.Calculate(source.Size()); - var finalradiusY = this.RadiusY.Calculate(source.Size()); + float finalradiusX = this.RadiusX.Calculate(source.Size()); + float finalradiusY = this.RadiusY.Calculate(source.Size()); float rX = finalradiusX > 0 ? MathF.Min(finalradiusX, sourceRectangle.Width * .5F) : sourceRectangle.Width * .5F; float rY = finalradiusY > 0 ? MathF.Min(finalradiusY, sourceRectangle.Height * .5F) : sourceRectangle.Height * .5F; float maxDistance = MathF.Sqrt((rX * rX) + (rY * rY)); @@ -110,11 +110,14 @@ namespace SixLabors.ImageSharp.Processing.Processors } int width = maxX - minX; - using (var rowColors = this.memoryManager.Allocate(width)) + using (IBuffer rowColors = this.memoryManager.Allocate(width)) { + // Be careful! Do not capture rowColorsSpan in the lambda below! + Span rowColorsSpan = rowColors.Span; + for (int i = 0; i < width; i++) { - rowColors[i] = vignetteColor; + rowColorsSpan[i] = vignetteColor; } Parallel.For( @@ -123,19 +126,20 @@ namespace SixLabors.ImageSharp.Processing.Processors configuration.ParallelOptions, y => { - using (var amounts = this.memoryManager.Allocate(width)) + using (IBuffer amounts = this.memoryManager.Allocate(width)) { + Span amountsSpan = amounts.Span; int offsetY = y - startY; int offsetX = minX - startX; for (int i = 0; i < width; i++) { float distance = Vector2.Distance(centre, new Vector2(i + offsetX, offsetY)); - amounts[i] = (this.options.BlendPercentage * (.9F * (distance / maxDistance))).Clamp(0, 1); + amountsSpan[i] = (this.options.BlendPercentage * (.9F * (distance / maxDistance))).Clamp(0, 1); } Span destination = source.GetPixelRowSpan(offsetY).Slice(offsetX, width); - this.blender.Blend(destination, destination, rowColors, amounts); + this.blender.Blend(destination, destination, rowColors.Span, amountsSpan); } }); } diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs index 1fa388da48..1e76422508 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs @@ -135,18 +135,18 @@ namespace SixLabors.ImageSharp.Processing.Processors y => { // TODO: Without Parallel.For() this buffer object could be reused: - using (var tempRowBuffer = this.MemoryManager.Allocate(source.Width)) + using (IBuffer tempRowBuffer = this.MemoryManager.Allocate(source.Width)) { Span firstPassRow = firstPassPixels.GetRowSpan(y); Span sourceRow = source.GetPixelRowSpan(y); - PixelOperations.Instance.ToVector4(sourceRow, tempRowBuffer, sourceRow.Length); + PixelOperations.Instance.ToVector4(sourceRow, tempRowBuffer.Span, sourceRow.Length); if (this.Compand) { for (int x = minX; x < maxX; x++) { WeightsWindow window = this.HorizontalWeights.Weights[x - startX]; - firstPassRow[x] = window.ComputeExpandedWeightedRowSum(tempRowBuffer, sourceX); + firstPassRow[x] = window.ComputeExpandedWeightedRowSum(tempRowBuffer.Span, sourceX); } } else @@ -154,7 +154,7 @@ namespace SixLabors.ImageSharp.Processing.Processors for (int x = minX; x < maxX; x++) { WeightsWindow window = this.HorizontalWeights.Weights[x - startX]; - firstPassRow[x] = window.ComputeWeightedRowSum(tempRowBuffer, sourceX); + firstPassRow[x] = window.ComputeWeightedRowSum(tempRowBuffer.Span, sourceX); } } } diff --git a/src/ImageSharp/Quantizers/QuantizerBase{TPixel}.cs b/src/ImageSharp/Quantizers/QuantizerBase{TPixel}.cs index 31e424060b..0b41edf98d 100644 --- a/src/ImageSharp/Quantizers/QuantizerBase{TPixel}.cs +++ b/src/ImageSharp/Quantizers/QuantizerBase{TPixel}.cs @@ -115,6 +115,7 @@ namespace SixLabors.ImageSharp.Quantizers.Base /// /// Override this to process the pixel in the first pass of the algorithm + /// TODO: We really should do this on a per-row basis! Shouldn't we internalize this method? /// /// The pixel to quantize /// diff --git a/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs b/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs index b5d31014b2..d9f188bf30 100644 --- a/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs +++ b/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs @@ -36,6 +36,14 @@ namespace SixLabors.ImageSharp.Quantizers public class WuQuantizer : QuantizerBase where TPixel : struct, IPixel { + // TODO: The WuQuantizer code is rising several questions: + // - Do we really need to ALWAYS allocate the whole table of size TableLength? (~ 2471625 * sizeof(long) * 5 bytes ) + // - Isn't an AOS ("array of structures") layout more efficient & more readable than SOA ("structure of arrays") for this particular use case? + // (T, R, G, B, A, M2) could be grouped together! + // - There are per-pixel virtual calls in InitialQuantizePixel, why not do it on a per-row basis? + // - It's a frequently used class, we need tests! (So we can optimize safely.) There are tests in the original!!! We should just adopt them! + // https://github.com/JeremyAnsel/JeremyAnsel.ColorQuant/blob/master/JeremyAnsel.ColorQuant/JeremyAnsel.ColorQuant.Tests/WuColorQuantizerTests.cs + /// /// The index bits. /// @@ -69,37 +77,37 @@ namespace SixLabors.ImageSharp.Quantizers /// /// Moment of P(c). /// - private Buffer vwt; + private IBuffer vwt; /// /// Moment of r*P(c). /// - private Buffer vmr; + private IBuffer vmr; /// /// Moment of g*P(c). /// - private Buffer vmg; + private IBuffer vmg; /// /// Moment of b*P(c). /// - private Buffer vmb; + private IBuffer vmb; /// /// Moment of a*P(c). /// - private Buffer vma; + private IBuffer vma; /// /// Moment of c^2*P(c). /// - private Buffer m2; + private IBuffer m2; /// /// Color space tag. /// - private Buffer tag; + private IBuffer tag; /// /// Maximum allowed color depth @@ -153,23 +161,16 @@ namespace SixLabors.ImageSharp.Quantizers } finally { - this.DisposeBuffer(ref this.vwt); - this.DisposeBuffer(ref this.vmr); - this.DisposeBuffer(ref this.vmg); - this.DisposeBuffer(ref this.vmb); - this.DisposeBuffer(ref this.vma); - this.DisposeBuffer(ref this.m2); - this.DisposeBuffer(ref this.tag); + this.vwt.Dispose(); + this.vmr.Dispose(); + this.vmg.Dispose(); + this.vmb.Dispose(); + this.vma.Dispose(); + this.m2.Dispose(); + this.tag.Dispose(); } } - private void DisposeBuffer(ref Buffer buffer) - where T : struct - { - buffer?.Dispose(); - buffer = null; - } - /// protected override TPixel[] GetPalette() { @@ -213,14 +214,21 @@ namespace SixLabors.ImageSharp.Quantizers int index = GetPaletteIndex(r + 1, g + 1, b + 1, a + 1); - this.vwt[index]++; - this.vmr[index] += rgba.R; - this.vmg[index] += rgba.G; - this.vmb[index] += rgba.B; - this.vma[index] += rgba.A; + Span vwtSpan = this.vwt.Span; + Span vmrSpan = this.vmr.Span; + Span vmgSpan = this.vmg.Span; + Span vmbSpan = this.vmb.Span; + Span vmaSpan = this.vma.Span; + Span m2Span = this.m2.Span; + + vwtSpan[index]++; + vmrSpan[index] += rgba.R; + vmgSpan[index] += rgba.G; + vmbSpan[index] += rgba.B; + vmaSpan[index] += rgba.A; var vector = new Vector4(rgba.R, rgba.G, rgba.B, rgba.A); - this.m2[index] += Vector4.Dot(vector, vector); + m2Span[index] += Vector4.Dot(vector, vector); } /// @@ -458,6 +466,13 @@ namespace SixLabors.ImageSharp.Quantizers /// private void Get3DMoments(MemoryManager memoryManager) { + Span vwtSpan = this.vwt.Span; + Span vmrSpan = this.vmr.Span; + Span vmgSpan = this.vmg.Span; + Span vmbSpan = this.vmb.Span; + Span vmaSpan = this.vma.Span; + Span m2Span = this.m2.Span; + using (Buffer volume = memoryManager.Allocate(IndexCount * IndexAlphaCount)) using (Buffer volumeR = memoryManager.Allocate(IndexCount * IndexAlphaCount)) using (Buffer volumeG = memoryManager.Allocate(IndexCount * IndexAlphaCount)) @@ -472,6 +487,20 @@ namespace SixLabors.ImageSharp.Quantizers using (Buffer areaA = memoryManager.Allocate(IndexAlphaCount)) using (Buffer area2 = memoryManager.Allocate(IndexAlphaCount)) { + Span volumeSpan = volume.Span; + Span volumeRSpan = volumeR.Span; + Span volumeGSpan = volumeG.Span; + Span volumeBSpan = volumeB.Span; + Span volumeASpan = volumeA.Span; + Span volume2Span = volume2.Span; + + Span areaSpan = area.Span; + Span areaRSpan = areaR.Span; + Span areaGSpan = areaG.Span; + Span areaBSpan = areaB.Span; + Span areaASpan = areaA.Span; + Span area2Span = area2.Span; + for (int r = 1; r < IndexCount; r++) { volume.Clear(); @@ -503,37 +532,37 @@ namespace SixLabors.ImageSharp.Quantizers { int ind1 = GetPaletteIndex(r, g, b, a); - line += this.vwt[ind1]; - lineR += this.vmr[ind1]; - lineG += this.vmg[ind1]; - lineB += this.vmb[ind1]; - lineA += this.vma[ind1]; - line2 += this.m2[ind1]; + line += vwtSpan[ind1]; + lineR += vmrSpan[ind1]; + lineG += vmgSpan[ind1]; + lineB += vmbSpan[ind1]; + lineA += vmaSpan[ind1]; + line2 += m2Span[ind1]; - area[a] += line; - areaR[a] += lineR; - areaG[a] += lineG; - areaB[a] += lineB; - areaA[a] += lineA; - area2[a] += line2; + areaSpan[a] += line; + areaRSpan[a] += lineR; + areaGSpan[a] += lineG; + areaBSpan[a] += lineB; + areaASpan[a] += lineA; + area2Span[a] += line2; int inv = (b * IndexAlphaCount) + a; - volume[inv] += area[a]; - volumeR[inv] += areaR[a]; - volumeG[inv] += areaG[a]; - volumeB[inv] += areaB[a]; - volumeA[inv] += areaA[a]; - volume2[inv] += area2[a]; + volumeSpan[inv] += areaSpan[a]; + volumeRSpan[inv] += areaRSpan[a]; + volumeGSpan[inv] += areaGSpan[a]; + volumeBSpan[inv] += areaBSpan[a]; + volumeASpan[inv] += areaASpan[a]; + volume2Span[inv] += area2Span[a]; int ind2 = ind1 - GetPaletteIndex(1, 0, 0, 0); - this.vwt[ind1] = this.vwt[ind2] + volume[inv]; - this.vmr[ind1] = this.vmr[ind2] + volumeR[inv]; - this.vmg[ind1] = this.vmg[ind2] + volumeG[inv]; - this.vmb[ind1] = this.vmb[ind2] + volumeB[inv]; - this.vma[ind1] = this.vma[ind2] + volumeA[inv]; - this.m2[ind1] = this.m2[ind2] + volume2[inv]; + vwtSpan[ind1] = vwtSpan[ind2] + volumeSpan[inv]; + vmrSpan[ind1] = vmrSpan[ind2] + volumeRSpan[inv]; + vmgSpan[ind1] = vmgSpan[ind2] + volumeGSpan[inv]; + vmbSpan[ind1] = vmbSpan[ind2] + volumeBSpan[inv]; + vmaSpan[ind1] = vmaSpan[ind2] + volumeASpan[inv]; + m2Span[ind1] = m2Span[ind2] + volume2Span[inv]; } } } @@ -553,23 +582,25 @@ namespace SixLabors.ImageSharp.Quantizers float db = Volume(ref cube, this.vmb.Span); float da = Volume(ref cube, this.vma.Span); + Span m2Span = this.m2.Span; + float xx = - this.m2[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A1)] - - this.m2[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A0)] - - this.m2[GetPaletteIndex(cube.R1, cube.G1, cube.B0, cube.A1)] - + this.m2[GetPaletteIndex(cube.R1, cube.G1, cube.B0, cube.A0)] - - this.m2[GetPaletteIndex(cube.R1, cube.G0, cube.B1, cube.A1)] - + this.m2[GetPaletteIndex(cube.R1, cube.G0, cube.B1, cube.A0)] - + this.m2[GetPaletteIndex(cube.R1, cube.G0, cube.B0, cube.A1)] - - this.m2[GetPaletteIndex(cube.R1, cube.G0, cube.B0, cube.A0)] - - this.m2[GetPaletteIndex(cube.R0, cube.G1, cube.B1, cube.A1)] - + this.m2[GetPaletteIndex(cube.R0, cube.G1, cube.B1, cube.A0)] - + this.m2[GetPaletteIndex(cube.R0, cube.G1, cube.B0, cube.A1)] - - this.m2[GetPaletteIndex(cube.R0, cube.G1, cube.B0, cube.A0)] - + this.m2[GetPaletteIndex(cube.R0, cube.G0, cube.B1, cube.A1)] - - this.m2[GetPaletteIndex(cube.R0, cube.G0, cube.B1, cube.A0)] - - this.m2[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A1)] - + this.m2[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A0)]; + m2Span[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A1)] + - m2Span[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A0)] + - m2Span[GetPaletteIndex(cube.R1, cube.G1, cube.B0, cube.A1)] + + m2Span[GetPaletteIndex(cube.R1, cube.G1, cube.B0, cube.A0)] + - m2Span[GetPaletteIndex(cube.R1, cube.G0, cube.B1, cube.A1)] + + m2Span[GetPaletteIndex(cube.R1, cube.G0, cube.B1, cube.A0)] + + m2Span[GetPaletteIndex(cube.R1, cube.G0, cube.B0, cube.A1)] + - m2Span[GetPaletteIndex(cube.R1, cube.G0, cube.B0, cube.A0)] + - m2Span[GetPaletteIndex(cube.R0, cube.G1, cube.B1, cube.A1)] + + m2Span[GetPaletteIndex(cube.R0, cube.G1, cube.B1, cube.A0)] + + m2Span[GetPaletteIndex(cube.R0, cube.G1, cube.B0, cube.A1)] + - m2Span[GetPaletteIndex(cube.R0, cube.G1, cube.B0, cube.A0)] + + m2Span[GetPaletteIndex(cube.R0, cube.G0, cube.B1, cube.A1)] + - m2Span[GetPaletteIndex(cube.R0, cube.G0, cube.B1, cube.A0)] + - m2Span[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A1)] + + m2Span[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A0)]; var vector = new Vector4(dr, dg, db, da); return xx - (Vector4.Dot(vector, vector) / Volume(ref cube, this.vwt.Span)); @@ -742,6 +773,8 @@ namespace SixLabors.ImageSharp.Quantizers /// A label. private void Mark(ref Box cube, byte label) { + Span tagSpan = this.tag.Span; + for (int r = cube.R0 + 1; r <= cube.R1; r++) { for (int g = cube.G0 + 1; g <= cube.G1; g++) @@ -750,7 +783,7 @@ namespace SixLabors.ImageSharp.Quantizers { for (int a = cube.A0 + 1; a <= cube.A1; a++) { - this.tag[GetPaletteIndex(r, g, b, a)] = label; + tagSpan[GetPaletteIndex(r, g, b, a)] = label; } } } @@ -833,7 +866,9 @@ namespace SixLabors.ImageSharp.Quantizers int b = rgba.B >> (8 - IndexBits); int a = rgba.A >> (8 - IndexAlphaBits); - return this.tag[GetPaletteIndex(r + 1, g + 1, b + 1, a + 1)]; + Span tagSpan = this.tag.Span; + + return tagSpan[GetPaletteIndex(r + 1, g + 1, b + 1, a + 1)]; } } } \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs index 53a55e06e3..1b49c48ec5 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs @@ -49,13 +49,13 @@ namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk [Benchmark] public void CommonBulk() { - new PixelOperations().PackFromVector4(this.source, this.destination, this.Count); + new PixelOperations().PackFromVector4(this.source.Span, this.destination.Span, this.Count); } [Benchmark] public void OptimizedBulk() { - PixelOperations.Instance.PackFromVector4(this.source, this.destination, this.Count); + PixelOperations.Instance.PackFromVector4(this.source.Span, this.destination.Span, this.Count); } } diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs index fb2f03d743..33dbcd24ca 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs @@ -50,13 +50,13 @@ namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk [Benchmark] public void CommonBulk() { - new PixelOperations().PackFromRgba32Bytes(this.source, this.destination, this.Count); + new PixelOperations().PackFromRgba32Bytes(this.source.Span, this.destination.Span, this.Count); } [Benchmark] public void OptimizedBulk() { - PixelOperations.Instance.PackFromRgba32Bytes(this.source, this.destination, this.Count); + PixelOperations.Instance.PackFromRgba32Bytes(this.source.Span, this.destination.Span, this.Count); } } diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs index cddf0f9a86..8a7fc52afd 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs @@ -49,13 +49,13 @@ namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk [Benchmark] public void CommonBulk() { - new PixelOperations().ToVector4(this.source, this.destination, this.Count); + new PixelOperations().ToVector4(this.source.Span, this.destination.Span, this.Count); } [Benchmark] public void OptimizedBulk() { - PixelOperations.Instance.ToVector4(this.source, this.destination, this.Count); + PixelOperations.Instance.ToVector4(this.source.Span, this.destination.Span, this.Count); } } diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs index 6593a28ae3..45ccaa8299 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs @@ -55,13 +55,13 @@ namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk [Benchmark] public void CommonBulk() { - new PixelOperations().ToRgb24Bytes(this.source, this.destination, this.Count); + new PixelOperations().ToRgb24Bytes(this.source.Span, this.destination.Span, this.Count); } [Benchmark] public void OptimizedBulk() { - PixelOperations.Instance.ToRgb24Bytes(this.source, this.destination, this.Count); + PixelOperations.Instance.ToRgb24Bytes(this.source.Span, this.destination.Span, this.Count); } } diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs index 58b80d5504..9912e987cf 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs @@ -58,13 +58,13 @@ namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk [Benchmark] public void CommonBulk() { - new PixelOperations().ToRgba32Bytes(this.source, this.destination, this.Count); + new PixelOperations().ToRgba32Bytes(this.source.Span, this.destination.Span, this.Count); } [Benchmark] public void OptimizedBulk() { - PixelOperations.Instance.ToRgba32Bytes(this.source, this.destination, this.Count); + PixelOperations.Instance.ToRgba32Bytes(this.source.Span, this.destination.Span, this.Count); } } diff --git a/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs b/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs index 4524d757cf..07734c6f55 100644 --- a/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs +++ b/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs @@ -59,21 +59,20 @@ namespace SixLabors.ImageSharp.Benchmarks { using (Image image = new Image(800, 800)) { - Buffer amounts = Configuration.Default.MemoryManager.Allocate(image.Width); - - for (int x = 0; x < image.Width; x++) - { - amounts[x] = 1; - } - using (PixelAccessor pixels = image.Lock()) + using (Buffer amounts = Configuration.Default.MemoryManager.Allocate(image.Width)) { - for (int y = 0; y < image.Height; y++) + amounts.Span.Fill(1); + + using (PixelAccessor pixels = image.Lock()) { - Span span = pixels.GetRowSpan(y); - BulkVectorConvert(span, span, span, amounts); + for (int y = 0; y < image.Height; y++) + { + Span span = pixels.GetRowSpan(y); + this.BulkVectorConvert(span, span, span, amounts.Span); + } } + return new CoreSize(image.Width, image.Height); } - return new CoreSize(image.Width, image.Height); } } @@ -82,21 +81,20 @@ namespace SixLabors.ImageSharp.Benchmarks { using (Image image = new Image(800, 800)) { - Buffer amounts = Configuration.Default.MemoryManager.Allocate(image.Width); - - for (int x = 0; x < image.Width; x++) + using (Buffer amounts = Configuration.Default.MemoryManager.Allocate(image.Width)) { - amounts[x] = 1; - } - using (PixelAccessor pixels = image.Lock()) - { - for (int y = 0; y < image.Height; y++) + amounts.Span.Fill(1); + using (PixelAccessor pixels = image.Lock()) { - Span span = pixels.GetRowSpan(y); - BulkPixelConvert(span, span, span, amounts); + for (int y = 0; y < image.Height; y++) + { + Span span = pixels.GetRowSpan(y); + this.BulkPixelConvert(span, span, span, amounts.Span); + } } + + return new CoreSize(image.Width, image.Height); } - return new CoreSize(image.Width, image.Height); } } } diff --git a/tests/ImageSharp.Benchmarks/Samplers/Glow.cs b/tests/ImageSharp.Benchmarks/Samplers/Glow.cs index 2743924f24..b36b4841f2 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/Glow.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/Glow.cs @@ -106,10 +106,7 @@ namespace SixLabors.ImageSharp.Benchmarks using (Buffer rowColors = Configuration.Default.MemoryManager.Allocate(width)) using (PixelAccessor sourcePixels = source.Lock()) { - for (int i = 0; i < width; i++) - { - rowColors[i] = glowColor; - } + rowColors.Span.Fill(glowColor); Parallel.For( minY, diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs index b5d4aaebe4..bf6b1f4ab2 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs @@ -289,7 +289,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg } // no need to dispose when buffer is not array owner - buffers[i] = new Buffer2D(new FakeBuffer(values), values.Length, 1); + buffers[i] = new Buffer2D(new BasicArrayBuffer(values), values.Length, 1); } return new JpegColorConverter.ComponentValues(buffers, 0); } diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs index 2ea06d7248..6e01bf182d 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs @@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats TestOperation( source, expected, - (s, d) => ImageSharp.Rgba32.PixelOperations.ToVector4SimdAligned(s, d, 64) + (s, d) => ImageSharp.Rgba32.PixelOperations.ToVector4SimdAligned(s, d.Span, 64) ); } @@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats times, () => { - PixelOperations.Instance.ToVector4(source, dest, count); + PixelOperations.Instance.ToVector4(source.Span, dest.Span, count); }); } } @@ -116,7 +116,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats TestOperation( source, expected, - (s, d) => Operations.PackFromVector4(s, d, count) + (s, d) => Operations.PackFromVector4(s, d.Span, count) ); } @@ -141,7 +141,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats TestOperation( source, expected, - (s, d) => Operations.ToVector4(s, d, count) + (s, d) => Operations.ToVector4(s, d.Span, count) ); } @@ -163,7 +163,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats TestOperation( source, expected, - (s, d) => Operations.PackFromRgb24Bytes(s, d, count) + (s, d) => Operations.PackFromRgb24Bytes(s, d.Span, count) ); } @@ -187,7 +187,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats TestOperation( source, expected, - (s, d) => Operations.ToRgb24Bytes(s, d, count) + (s, d) => Operations.ToRgb24Bytes(s, d.Span, count) ); } @@ -208,7 +208,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats TestOperation( source, expected, - (s, d) => Operations.PackFromRgba32Bytes(s, d, count) + (s, d) => Operations.PackFromRgba32Bytes(s, d.Span, count) ); } @@ -233,7 +233,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats TestOperation( source, expected, - (s, d) => Operations.ToRgba32Bytes(s, d, count) + (s, d) => Operations.ToRgba32Bytes(s, d.Span, count) ); } @@ -254,7 +254,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats TestOperation( source, expected, - (s, d) => Operations.PackFromBgr24Bytes(s, d, count) + (s, d) => Operations.PackFromBgr24Bytes(s, d.Span, count) ); } @@ -278,7 +278,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats TestOperation( source, expected, - (s, d) => Operations.ToBgr24Bytes(s, d, count) + (s, d) => Operations.ToBgr24Bytes(s, d.Span, count) ); } @@ -299,7 +299,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats TestOperation( source, expected, - (s, d) => Operations.PackFromBgra32Bytes(s, d, count) + (s, d) => Operations.PackFromBgra32Bytes(s, d.Span, count) ); } @@ -324,7 +324,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats TestOperation( source, expected, - (s, d) => Operations.ToBgra32Bytes(s, d, count) + (s, d) => Operations.ToBgra32Bytes(s, d.Span, count) ); } @@ -336,10 +336,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats public TSource[] SourceBuffer { get; } public Buffer ActualDestBuffer { get; } public TDest[] ExpectedDestBuffer { get; } - - public Span Source => this.SourceBuffer; - public Span ActualDest => this.ActualDestBuffer; - + public TestBuffers(TSource[] source, TDest[] expectedDest) { this.SourceBuffer = source; @@ -386,7 +383,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats internal static void TestOperation( TSource[] source, TDest[] expected, - Action> action) + Action> action) where TSource : struct where TDest : struct { diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs index b8907e81e3..9dbfeaca91 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs @@ -19,13 +19,14 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs int length = source.Length; Guard.MustBeSizedAtLeast(dest, length, nameof(dest)); - using (var rgbaBuffer = Configuration.Default.MemoryManager.Allocate(length)) + using (Buffer rgbaBuffer = Configuration.Default.MemoryManager.Allocate(length)) { - PixelOperations.Instance.ToRgba32(source, rgbaBuffer, length); + Span rgbaSpan = rgbaBuffer.Span; + PixelOperations.Instance.ToRgba32(source, rgbaSpan, length); for (int i = 0; i < length; i++) { - ref Rgba32 s = ref rgbaBuffer[i]; + ref Rgba32 s = ref rgbaSpan[i]; ref Argb32 d = ref dest[i]; d.PackFromRgba32(s); @@ -39,13 +40,14 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs int length = source.Length; Guard.MustBeSizedAtLeast(dest, length, nameof(dest)); - using (var rgbaBuffer = Configuration.Default.MemoryManager.Allocate(length)) + using (Buffer rgbaBuffer = Configuration.Default.MemoryManager.Allocate(length)) { - PixelOperations.Instance.ToRgba32(source, rgbaBuffer, length); + Span rgbaSpan = rgbaBuffer.Span; + PixelOperations.Instance.ToRgba32(source, rgbaSpan, length); for (int i = 0; i < length; i++) { - ref Rgba32 s = ref rgbaBuffer[i]; + ref Rgba32 s = ref rgbaSpan[i]; ref TPixel d = ref dest[i]; d.PackFromRgba32(s); @@ -59,13 +61,14 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs int length = source.Length; Guard.MustBeSizedAtLeast(dest, length, nameof(dest)); - using (var rgbaBuffer = Configuration.Default.MemoryManager.Allocate(length)) + using (Buffer rgbBuffer = Configuration.Default.MemoryManager.Allocate(length)) { - PixelOperations.Instance.ToRgb24(source, rgbaBuffer, length); + Span rgbSpan = rgbBuffer.Span; + PixelOperations.Instance.ToRgb24(source, rgbSpan, length); for (int i = 0; i < length; i++) { - ref Rgb24 s = ref rgbaBuffer[i]; + ref Rgb24 s = ref rgbSpan[i]; ref TPixel d = ref dest[i]; var rgba = default(Rgba32); s.ToRgba32(ref rgba); From 5e8b8bbffec7cfc5ea3b70b122c480d51a3a4335 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 20 Feb 2018 02:33:31 +0100 Subject: [PATCH 069/151] 2 drawing regression test cases for safety --- .../Drawing/SolidBezierTests.cs | 63 +++++++------------ tests/Images/External | 2 +- 2 files changed, 25 insertions(+), 40 deletions(-) diff --git a/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs b/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs index 07e75acf43..8bc4645ff2 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs @@ -9,40 +9,39 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Drawing { - public class SolidBezierTests : FileTestBase + [GroupOutput("Drawing")] + public class SolidBezierTests { - [Fact] - public void ImageShouldBeOverlayedByFilledPolygon() + [Theory] + [WithBlankImages(500, 500, PixelTypes.Rgba32)] + public void FilledBezier(TestImageProvider provider) + where TPixel : struct, IPixel { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledBezier"); - SixLabors.Primitives.PointF[] simplePath = new SixLabors.Primitives.PointF[] { + Primitives.PointF[] simplePath = { new Vector2(10, 400), new Vector2(30, 10), new Vector2(240, 30), new Vector2(300, 400) }; - using (Image image = new Image(500, 500)) - { - image.Mutate(x => x - .BackgroundColor(Rgba32.Blue) - .Fill(Rgba32.HotPink, new Polygon(new CubicBezierLineSegment(simplePath)))); - image.Save($"{path}/Simple.png"); - - using (PixelAccessor sourcePixels = image.Lock()) - { - Assert.Equal(Rgba32.HotPink, sourcePixels[150, 300]); - //curve points should not be never be set - Assert.Equal(Rgba32.Blue, sourcePixels[240, 30]); + TPixel blue = NamedColors.Blue; + TPixel hotPink = NamedColors.HotPink; - // inside shape should not be empty - Assert.Equal(Rgba32.HotPink, sourcePixels[200, 250]); - } + using (Image image = provider.GetImage()) + { + + image.Mutate(x => x + .BackgroundColor(blue) + .Fill(hotPink, new Polygon(new CubicBezierLineSegment(simplePath)))); + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); } } - [Fact] - public void ImageShouldBeOverlayedByFilledPolygonOpacity() + [Theory] + [WithBlankImages(500, 500, PixelTypes.Rgba32)] + public void OverlayByFilledPolygonOpacity(TestImageProvider provider) + where TPixel : struct, IPixel { string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledBezier"); SixLabors.Primitives.PointF[] simplePath = new SixLabors.Primitives.PointF[] { @@ -53,27 +52,13 @@ namespace SixLabors.ImageSharp.Tests.Drawing }; Rgba32 color = new Rgba32(Rgba32.HotPink.R, Rgba32.HotPink.G, Rgba32.HotPink.B, 150); - using (Image image = new Image(500, 500)) + using (var image = provider.GetImage() as Image) { image.Mutate(x => x .BackgroundColor(Rgba32.Blue) .Fill(color, new Polygon(new CubicBezierLineSegment(simplePath)))); - image.Save($"{path}/Opacity.png"); - - //shift background color towards forground color by the opacity amount - Rgba32 mergedColor = new Rgba32(Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f)); - - using (PixelAccessor sourcePixels = image.Lock()) - { - //top of curve - Assert.Equal(mergedColor, sourcePixels[138, 116]); - - //curve points should not be never be set - Assert.Equal(Rgba32.Blue, sourcePixels[240, 30]); - - // inside shape should not be empty - Assert.Equal(mergedColor, sourcePixels[200, 250]); - } + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); } } } diff --git a/tests/Images/External b/tests/Images/External index b3be1178d4..0e1b5fd398 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit b3be1178d4e970efc624181480094e50b0d57a90 +Subproject commit 0e1b5fd3987081c8fbee9d5cf6d74142b942df60 From caf1ad05a21c681042b7dbeb58525abd73758044 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 20 Feb 2018 03:00:25 +0100 Subject: [PATCH 070/151] MemoryManager returns IBuffer now --- .../Brushes/ImageBrush{TPixel}.cs | 4 +- .../Brushes/PatternBrush{TPixel}.cs | 4 +- .../Brushes/Processors/BrushApplicator.cs | 4 +- .../Brushes/RecolorBrush{TPixel}.cs | 4 +- .../Brushes/SolidBrush{TPixel}.cs | 4 +- .../Processors/DrawImageProcessor.cs | 2 +- .../Processors/FillProcessor.cs | 2 +- src/ImageSharp/Formats/Gif/LzwDecoder.cs | 6 +-- src/ImageSharp/Formats/Gif/LzwEncoder.cs | 4 +- .../Common/Decoder/JpegImagePostProcessor.cs | 2 +- .../GolangPort/Components/Decoder/Bytes.cs | 2 +- .../PdfJsPort/Components/PdfJsComponent.cs | 2 +- .../Components/PdfJsFrameComponent.cs | 2 +- .../Components/PdfJsJpegPixelArea.cs | 2 +- .../Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs | 4 +- .../Memory/ArrayPoolMemoryManager.cs | 11 ++++- src/ImageSharp/Memory/Buffer{T}.cs | 4 +- src/ImageSharp/Memory/IGetArray.cs | 14 +++++++ src/ImageSharp/Memory/MemoryManager.cs | 2 +- .../Memory/MemoryManagerExtensions.cs | 6 +-- .../Memory/SimpleManagedMemoryManager.cs | 2 +- .../DefaultPixelBlenders.Generated.cs | 42 +++++++++---------- .../DefaultPixelBlenders.Generated.tt | 2 +- .../Effects/BackgroundColorProcessor.cs | 4 +- .../Quantizers/WuQuantizer{TPixel}.cs | 26 ++++++------ .../Color/Bulk/PackFromVector4.cs | 4 +- .../Color/Bulk/PackFromXyzw.cs | 4 +- .../Color/Bulk/ToVector4.cs | 4 +- .../ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs | 4 +- .../Color/Bulk/ToXyzw.cs | 4 +- .../PixelBlenders/PorterDuffBulkVsPixel.cs | 6 +-- tests/ImageSharp.Benchmarks/Samplers/Glow.cs | 2 +- .../Drawing/SolidBezierTests.cs | 4 +- .../PixelFormats/PixelOperationsTests.cs | 6 +-- .../ReferenceCodecs/SystemDrawingBridge.cs | 6 +-- 35 files changed, 113 insertions(+), 92 deletions(-) create mode 100644 src/ImageSharp/Memory/IGetArray.cs diff --git a/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs index 5866d9feae..6b3ce36fe1 100644 --- a/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs @@ -122,8 +122,8 @@ namespace SixLabors.ImageSharp.Drawing.Brushes internal override void Apply(Span scanline, int x, int y) { // Create a span for colors - using (Buffer amountBuffer = this.Target.MemoryManager.Allocate(scanline.Length)) - using (Buffer overlay = this.Target.MemoryManager.Allocate(scanline.Length)) + using (IBuffer amountBuffer = this.Target.MemoryManager.Allocate(scanline.Length)) + using (IBuffer overlay = this.Target.MemoryManager.Allocate(scanline.Length)) { Span amountSpan = amountBuffer.Span; Span overlaySpan = overlay.Span; diff --git a/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs index ac8ffa7941..449a23da9f 100644 --- a/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs @@ -152,8 +152,8 @@ namespace SixLabors.ImageSharp.Drawing.Brushes internal override void Apply(Span scanline, int x, int y) { int patternY = y % this.pattern.Height; - using (Buffer amountBuffer = this.Target.MemoryManager.Allocate(scanline.Length)) - using (Buffer overlay = this.Target.MemoryManager.Allocate(scanline.Length)) + using (IBuffer amountBuffer = this.Target.MemoryManager.Allocate(scanline.Length)) + using (IBuffer overlay = this.Target.MemoryManager.Allocate(scanline.Length)) { Span amountSpan = amountBuffer.Span; Span overlaySpan = overlay.Span; diff --git a/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs b/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs index dadd546e93..68bc13ecb5 100644 --- a/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs +++ b/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs @@ -65,8 +65,8 @@ namespace SixLabors.ImageSharp.Drawing.Brushes.Processors /// scanlineBuffer will be > scanlineWidth but provide and offset in case we want to share a larger buffer across runs. internal virtual void Apply(Span scanline, int x, int y) { - using (Buffer amountBuffer = this.Target.MemoryManager.Allocate(scanline.Length)) - using (Buffer overlay = this.Target.MemoryManager.Allocate(scanline.Length)) + using (IBuffer amountBuffer = this.Target.MemoryManager.Allocate(scanline.Length)) + using (IBuffer overlay = this.Target.MemoryManager.Allocate(scanline.Length)) { Span amountSpan = amountBuffer.Span; Span overlaySpan = overlay.Span; diff --git a/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs index d1fda7ebe6..37cba42c9e 100644 --- a/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs @@ -144,8 +144,8 @@ namespace SixLabors.ImageSharp.Drawing.Brushes /// internal override void Apply(Span scanline, int x, int y) { - using (Buffer amountBuffer = this.Target.MemoryManager.Allocate(scanline.Length)) - using (Buffer overlay = this.Target.MemoryManager.Allocate(scanline.Length)) + using (IBuffer amountBuffer = this.Target.MemoryManager.Allocate(scanline.Length)) + using (IBuffer overlay = this.Target.MemoryManager.Allocate(scanline.Length)) { Span amountSpan = amountBuffer.Span; Span overlaySpan = overlay.Span; diff --git a/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs index 510299f263..90286cb6c9 100644 --- a/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs @@ -68,7 +68,7 @@ namespace SixLabors.ImageSharp.Drawing.Brushes /// /// Gets the colors. /// - protected Buffer Colors { get; } + protected IBuffer Colors { get; } /// /// Gets the color for a single pixel. @@ -93,7 +93,7 @@ namespace SixLabors.ImageSharp.Drawing.Brushes { Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); - using (Buffer amountBuffer = this.Target.MemoryManager.Allocate(scanline.Length)) + using (IBuffer amountBuffer = this.Target.MemoryManager.Allocate(scanline.Length)) { Span amountSpan = amountBuffer.Span; diff --git a/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs b/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs index eb3949b007..21e13d47af 100644 --- a/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs @@ -84,7 +84,7 @@ namespace SixLabors.ImageSharp.Drawing.Processors maxY = Math.Min(this.Location.Y + this.Size.Height, maxY); int width = maxX - minX; - using (Buffer amount = this.Image.GetConfiguration().MemoryManager.Allocate(width)) + using (IBuffer amount = this.Image.GetConfiguration().MemoryManager.Allocate(width)) { amount.Span.Fill(this.Alpha); diff --git a/src/ImageSharp.Drawing/Processors/FillProcessor.cs b/src/ImageSharp.Drawing/Processors/FillProcessor.cs index 75ea1f2033..3bf18a37ba 100644 --- a/src/ImageSharp.Drawing/Processors/FillProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/FillProcessor.cs @@ -66,7 +66,7 @@ namespace SixLabors.ImageSharp.Drawing.Processors int width = maxX - minX; - using (Buffer amount = source.MemoryManager.Allocate(width)) + using (IBuffer amount = source.MemoryManager.Allocate(width)) using (BrushApplicator applicator = this.brush.CreateApplicator( source, sourceRectangle, diff --git a/src/ImageSharp/Formats/Gif/LzwDecoder.cs b/src/ImageSharp/Formats/Gif/LzwDecoder.cs index 27ca275db1..0c73efea46 100644 --- a/src/ImageSharp/Formats/Gif/LzwDecoder.cs +++ b/src/ImageSharp/Formats/Gif/LzwDecoder.cs @@ -32,17 +32,17 @@ namespace SixLabors.ImageSharp.Formats.Gif /// /// The prefix buffer. /// - private readonly Buffer prefix; + private readonly IBuffer prefix; /// /// The suffix buffer. /// - private readonly Buffer suffix; + private readonly IBuffer suffix; /// /// The pixel stack buffer. /// - private readonly Buffer pixelStack; + private readonly IBuffer pixelStack; /// /// A value indicating whether this instance of the given entity has been disposed. diff --git a/src/ImageSharp/Formats/Gif/LzwEncoder.cs b/src/ImageSharp/Formats/Gif/LzwEncoder.cs index 115ecf6fbe..35c4148964 100644 --- a/src/ImageSharp/Formats/Gif/LzwEncoder.cs +++ b/src/ImageSharp/Formats/Gif/LzwEncoder.cs @@ -71,12 +71,12 @@ namespace SixLabors.ImageSharp.Formats.Gif /// /// The hash table. /// - private readonly Buffer hashTable; + private readonly IBuffer hashTable; /// /// The code table. /// - private readonly Buffer codeTable; + private readonly IBuffer codeTable; /// /// Define the storage for the packet accumulator. diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs index 3258fd32cc..aa1c216a75 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs @@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder /// /// Temporal buffer to store a row of colors. /// - private readonly Buffer rgbaBuffer; + private readonly IBuffer rgbaBuffer; /// /// The corresponding to the current determined by . diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bytes.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bytes.cs index 7a22b043b8..c10771b462 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bytes.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bytes.cs @@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder /// /// Values of converted to -s /// - public Buffer BufferAsInt; + public IBuffer BufferAsInt; /// /// Start of bytes read diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsComponent.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsComponent.cs index 3c35e311f1..0742293c78 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsComponent.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsComponent.cs @@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components /// /// Gets or sets the output /// - public Buffer Output; + public IBuffer Output; /// /// Gets or sets the scaling factors diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrameComponent.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrameComponent.cs index 18e1773909..2442c39981 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrameComponent.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrameComponent.cs @@ -60,7 +60,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components /// /// Gets the block data /// - public Buffer BlockData { get; private set; } + public IBuffer BlockData { get; private set; } /// public int Index { get; } diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsJpegPixelArea.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsJpegPixelArea.cs index a6f8780177..f16fb9a2c2 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsJpegPixelArea.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsJpegPixelArea.cs @@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components private readonly int imageHeight; - private Buffer componentData; + private IBuffer componentData; private int rowStride; diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs index 6bcde2f487..4fa0bc281d 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs @@ -786,8 +786,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort { int blocksPerLine = component.BlocksPerLine; int blocksPerColumn = component.BlocksPerColumn; - using (Buffer computationBuffer = this.configuration.MemoryManager.Allocate(64, true)) - using (Buffer multiplicationBuffer = this.configuration.MemoryManager.Allocate(64, true)) + using (IBuffer computationBuffer = this.configuration.MemoryManager.Allocate(64, true)) + using (IBuffer multiplicationBuffer = this.configuration.MemoryManager.Allocate(64, true)) { Span quantizationTable = this.quantizationTables.Tables.GetRowSpan(frameComponent.QuantizationTableIndex); Span computationBufferSpan = computationBuffer.Span; diff --git a/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs b/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs index e14ba443f9..de26b0ba5e 100644 --- a/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs +++ b/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs @@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp.Memory } /// - internal override Buffer Allocate(int length, bool clear) + internal override IBuffer Allocate(int length, bool clear) { int itemSizeBytes = Unsafe.SizeOf(); int bufferSizeInBytes = length * itemSizeBytes; @@ -68,7 +68,14 @@ namespace SixLabors.ImageSharp.Memory /// internal override void Release(Buffer buffer) { - byte[] byteBuffer = Unsafe.As(buffer.GetArray()); + T[] array = (buffer as IGetArray)?.GetArray(); + if (array == null) + { + return; + } + + // TODO: OMG Do not do this! + byte[] byteBuffer = Unsafe.As(array); this.pool.Return(byteBuffer); } } diff --git a/src/ImageSharp/Memory/Buffer{T}.cs b/src/ImageSharp/Memory/Buffer{T}.cs index 309cca1f41..b1449f9b61 100644 --- a/src/ImageSharp/Memory/Buffer{T}.cs +++ b/src/ImageSharp/Memory/Buffer{T}.cs @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Memory /// The backing array is either pooled or comes from the outside. /// /// The value type. - internal class Buffer : IBuffer + internal class Buffer : IBuffer, IGetArray where T : struct { private MemoryManager memoryManager; @@ -71,7 +71,7 @@ namespace SixLabors.ImageSharp.Memory /// /// TODO: Refactor this /// - internal T[] GetArray() + T[] IGetArray.GetArray() { return this.array; } diff --git a/src/ImageSharp/Memory/IGetArray.cs b/src/ImageSharp/Memory/IGetArray.cs new file mode 100644 index 0000000000..9b46058d19 --- /dev/null +++ b/src/ImageSharp/Memory/IGetArray.cs @@ -0,0 +1,14 @@ +namespace SixLabors.ImageSharp.Memory +{ + /// + /// Absolutely temporal. + /// + internal interface IGetArray + where T : struct + { + /// + /// Absolutely temporal. + /// + T[] GetArray(); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Memory/MemoryManager.cs b/src/ImageSharp/Memory/MemoryManager.cs index 540c045de3..fe6bb7823f 100644 --- a/src/ImageSharp/Memory/MemoryManager.cs +++ b/src/ImageSharp/Memory/MemoryManager.cs @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Memory /// Size of the buffer to allocate /// True to clear the backing memory of the buffer /// A buffer of values of type . - internal abstract Buffer Allocate(int length, bool clear) + internal abstract IBuffer Allocate(int length, bool clear) where T : struct; internal abstract IManagedByteBuffer AllocateManagedByteBuffer(int length, bool clear); diff --git a/src/ImageSharp/Memory/MemoryManagerExtensions.cs b/src/ImageSharp/Memory/MemoryManagerExtensions.cs index f157767217..21c8f71248 100644 --- a/src/ImageSharp/Memory/MemoryManagerExtensions.cs +++ b/src/ImageSharp/Memory/MemoryManagerExtensions.cs @@ -14,13 +14,13 @@ /// The /// Size of the buffer to allocate /// A buffer of values of type . - public static Buffer Allocate(this MemoryManager memoryManager, int length) + public static IBuffer Allocate(this MemoryManager memoryManager, int length) where T : struct { return memoryManager.Allocate(length, false); } - public static Buffer AllocateClean(this MemoryManager memoryManager, int length) + public static IBuffer AllocateClean(this MemoryManager memoryManager, int length) where T : struct { return memoryManager.Allocate(length, true); @@ -39,7 +39,7 @@ public static Buffer2D Allocate2D(this MemoryManager memoryManager, int width, int height, bool clear) where T : struct { - Buffer buffer = memoryManager.Allocate(width * height, clear); + IBuffer buffer = memoryManager.Allocate(width * height, clear); return new Buffer2D(buffer, width, height); } diff --git a/src/ImageSharp/Memory/SimpleManagedMemoryManager.cs b/src/ImageSharp/Memory/SimpleManagedMemoryManager.cs index ac4098c71d..804a468fd6 100644 --- a/src/ImageSharp/Memory/SimpleManagedMemoryManager.cs +++ b/src/ImageSharp/Memory/SimpleManagedMemoryManager.cs @@ -6,7 +6,7 @@ public class SimpleManagedMemoryManager : MemoryManager { /// - internal override Buffer Allocate(int length, bool clear) + internal override IBuffer Allocate(int length, bool clear) { return new Buffer(new T[length], length, this); } diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs index 4ca53244ad..17f75898db 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs @@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) + using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3, false)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -84,7 +84,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) + using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3, false)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -123,7 +123,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) + using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3, false)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -162,7 +162,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) + using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3, false)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -201,7 +201,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) + using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3, false)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -240,7 +240,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) + using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3, false)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -279,7 +279,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) + using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3, false)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -318,7 +318,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) + using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3, false)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -357,7 +357,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) + using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3, false)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -396,7 +396,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) + using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3, false)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -435,7 +435,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) + using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3, false)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -474,7 +474,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) + using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3, false)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -513,7 +513,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) + using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3, false)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -552,7 +552,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) + using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3, false)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -591,7 +591,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) + using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3, false)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -630,7 +630,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) + using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3, false)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -669,7 +669,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) + using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3, false)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -708,7 +708,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) + using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3, false)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -747,7 +747,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) + using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3, false)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -786,7 +786,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) + using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3, false)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -825,7 +825,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) + using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3, false)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt index 5e9268dff1..75c6b2d81c 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt @@ -87,7 +87,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) + using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3, false)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); diff --git a/src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs index a42a2056c4..e97495a3db 100644 --- a/src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs @@ -71,8 +71,8 @@ namespace SixLabors.ImageSharp.Processing.Processors int width = maxX - minX; - using (Buffer colors = this.memoryManager.Allocate(width)) - using (Buffer amount = this.memoryManager.Allocate(width)) + using (IBuffer colors = this.memoryManager.Allocate(width)) + using (IBuffer amount = this.memoryManager.Allocate(width)) { // Be careful! Do not capture colorSpan & amountSpan in the lambda below! Span colorSpan = colors.Span; diff --git a/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs b/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs index d9f188bf30..0aadf49732 100644 --- a/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs +++ b/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs @@ -473,19 +473,19 @@ namespace SixLabors.ImageSharp.Quantizers Span vmaSpan = this.vma.Span; Span m2Span = this.m2.Span; - using (Buffer volume = memoryManager.Allocate(IndexCount * IndexAlphaCount)) - using (Buffer volumeR = memoryManager.Allocate(IndexCount * IndexAlphaCount)) - using (Buffer volumeG = memoryManager.Allocate(IndexCount * IndexAlphaCount)) - using (Buffer volumeB = memoryManager.Allocate(IndexCount * IndexAlphaCount)) - using (Buffer volumeA = memoryManager.Allocate(IndexCount * IndexAlphaCount)) - using (Buffer volume2 = memoryManager.Allocate(IndexCount * IndexAlphaCount)) - - using (Buffer area = memoryManager.Allocate(IndexAlphaCount)) - using (Buffer areaR = memoryManager.Allocate(IndexAlphaCount)) - using (Buffer areaG = memoryManager.Allocate(IndexAlphaCount)) - using (Buffer areaB = memoryManager.Allocate(IndexAlphaCount)) - using (Buffer areaA = memoryManager.Allocate(IndexAlphaCount)) - using (Buffer area2 = memoryManager.Allocate(IndexAlphaCount)) + using (IBuffer volume = memoryManager.Allocate(IndexCount * IndexAlphaCount)) + using (IBuffer volumeR = memoryManager.Allocate(IndexCount * IndexAlphaCount)) + using (IBuffer volumeG = memoryManager.Allocate(IndexCount * IndexAlphaCount)) + using (IBuffer volumeB = memoryManager.Allocate(IndexCount * IndexAlphaCount)) + using (IBuffer volumeA = memoryManager.Allocate(IndexCount * IndexAlphaCount)) + using (IBuffer volume2 = memoryManager.Allocate(IndexCount * IndexAlphaCount)) + + using (IBuffer area = memoryManager.Allocate(IndexAlphaCount)) + using (IBuffer areaR = memoryManager.Allocate(IndexAlphaCount)) + using (IBuffer areaG = memoryManager.Allocate(IndexAlphaCount)) + using (IBuffer areaB = memoryManager.Allocate(IndexAlphaCount)) + using (IBuffer areaA = memoryManager.Allocate(IndexAlphaCount)) + using (IBuffer area2 = memoryManager.Allocate(IndexAlphaCount)) { Span volumeSpan = volume.Span; Span volumeRSpan = volumeR.Span; diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs index 1b49c48ec5..7d8519875b 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs @@ -13,9 +13,9 @@ namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk public abstract class PackFromVector4 where TPixel : struct, IPixel { - private Buffer source; + private IBuffer source; - private Buffer destination; + private IBuffer destination; [Params(16, 128, 512)] public int Count { get; set; } diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs index 33dbcd24ca..882d77dd12 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs @@ -11,9 +11,9 @@ namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk public abstract class PackFromXyzw where TPixel : struct, IPixel { - private Buffer destination; + private IBuffer destination; - private Buffer source; + private IBuffer source; [Params(16, 128, 1024)] public int Count { get; set; } diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs index 8a7fc52afd..6537141501 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs @@ -12,9 +12,9 @@ namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk public abstract class ToVector4 where TPixel : struct, IPixel { - private Buffer source; + private IBuffer source; - private Buffer destination; + private IBuffer destination; [Params(64, 300, 1024)] public int Count { get; set; } diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs index 45ccaa8299..b2def64ace 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs @@ -12,9 +12,9 @@ namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk public abstract class ToXyz where TPixel : struct, IPixel { - private Buffer source; + private IBuffer source; - private Buffer destination; + private IBuffer destination; [Params(16, 128, 1024)] public int Count { get; set; } diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs index 9912e987cf..dd9a628c25 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs @@ -14,9 +14,9 @@ namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk public abstract class ToXyzw where TPixel : struct, IPixel { - private Buffer source; + private IBuffer source; - private Buffer destination; + private IBuffer destination; [Params(16, 128, 1024)] public int Count { get; set; } diff --git a/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs b/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs index 07734c6f55..c088e8eed4 100644 --- a/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs +++ b/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Benchmarks Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (Buffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) + using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.Benchmarks { using (Image image = new Image(800, 800)) { - using (Buffer amounts = Configuration.Default.MemoryManager.Allocate(image.Width)) + using (IBuffer amounts = Configuration.Default.MemoryManager.Allocate(image.Width)) { amounts.Span.Fill(1); @@ -81,7 +81,7 @@ namespace SixLabors.ImageSharp.Benchmarks { using (Image image = new Image(800, 800)) { - using (Buffer amounts = Configuration.Default.MemoryManager.Allocate(image.Width)) + using (IBuffer amounts = Configuration.Default.MemoryManager.Allocate(image.Width)) { amounts.Span.Fill(1); using (PixelAccessor pixels = image.Lock()) diff --git a/tests/ImageSharp.Benchmarks/Samplers/Glow.cs b/tests/ImageSharp.Benchmarks/Samplers/Glow.cs index b36b4841f2..5f4e2d75b4 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/Glow.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/Glow.cs @@ -103,7 +103,7 @@ namespace SixLabors.ImageSharp.Benchmarks } int width = maxX - minX; - using (Buffer rowColors = Configuration.Default.MemoryManager.Allocate(width)) + using (IBuffer rowColors = Configuration.Default.MemoryManager.Allocate(width)) using (PixelAccessor sourcePixels = source.Lock()) { rowColors.Span.Fill(glowColor); diff --git a/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs b/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs index 8bc4645ff2..70badd34c5 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs @@ -43,13 +43,13 @@ namespace SixLabors.ImageSharp.Tests.Drawing public void OverlayByFilledPolygonOpacity(TestImageProvider provider) where TPixel : struct, IPixel { - string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledBezier"); - SixLabors.Primitives.PointF[] simplePath = new SixLabors.Primitives.PointF[] { + Primitives.PointF[] simplePath = { new Vector2(10, 400), new Vector2(30, 10), new Vector2(240, 30), new Vector2(300, 400) }; + Rgba32 color = new Rgba32(Rgba32.HotPink.R, Rgba32.HotPink.G, Rgba32.HotPink.B, 150); using (var image = provider.GetImage() as Image) diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs index 6e01bf182d..c0039bb37c 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs @@ -50,8 +50,8 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats int times = 200000; int count = 1024; - using (Buffer source = Configuration.Default.MemoryManager.Allocate(count)) - using (Buffer dest = Configuration.Default.MemoryManager.Allocate(count)) + using (IBuffer source = Configuration.Default.MemoryManager.Allocate(count)) + using (IBuffer dest = Configuration.Default.MemoryManager.Allocate(count)) { this.Measure( times, @@ -334,7 +334,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats where TDest : struct { public TSource[] SourceBuffer { get; } - public Buffer ActualDestBuffer { get; } + public IBuffer ActualDestBuffer { get; } public TDest[] ExpectedDestBuffer { get; } public TestBuffers(TSource[] source, TDest[] expectedDest) diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs index 9dbfeaca91..d1270dcfd7 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs int length = source.Length; Guard.MustBeSizedAtLeast(dest, length, nameof(dest)); - using (Buffer rgbaBuffer = Configuration.Default.MemoryManager.Allocate(length)) + using (IBuffer rgbaBuffer = Configuration.Default.MemoryManager.Allocate(length)) { Span rgbaSpan = rgbaBuffer.Span; PixelOperations.Instance.ToRgba32(source, rgbaSpan, length); @@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs int length = source.Length; Guard.MustBeSizedAtLeast(dest, length, nameof(dest)); - using (Buffer rgbaBuffer = Configuration.Default.MemoryManager.Allocate(length)) + using (IBuffer rgbaBuffer = Configuration.Default.MemoryManager.Allocate(length)) { Span rgbaSpan = rgbaBuffer.Span; PixelOperations.Instance.ToRgba32(source, rgbaSpan, length); @@ -61,7 +61,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs int length = source.Length; Guard.MustBeSizedAtLeast(dest, length, nameof(dest)); - using (Buffer rgbBuffer = Configuration.Default.MemoryManager.Allocate(length)) + using (IBuffer rgbBuffer = Configuration.Default.MemoryManager.Allocate(length)) { Span rgbSpan = rgbBuffer.Span; PixelOperations.Instance.ToRgb24(source, rgbSpan, length); From 8e7aef62a772ab40aaea5a9f423e5866dd85771c Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 20 Feb 2018 03:32:21 +0100 Subject: [PATCH 071/151] MemoryManager-s should provide their own IBuffer implementations --- .../Processors/FillRegionProcessor.cs | 2 +- .../Common/Decoder/JpegBlockPostProcessor.cs | 2 +- .../PdfJsPort/Components/PdfJsHuffmanTable.cs | 12 ++--- src/ImageSharp/Image/PixelAccessor{TPixel}.cs | 2 +- .../ArrayPoolMemoryManager.Buffer{T}.cs | 50 +++++++++++++++++++ .../Memory/ArrayPoolMemoryManager.cs | 21 ++------ src/ImageSharp/Memory/BasicArrayBuffer.cs | 22 +------- src/ImageSharp/Memory/BasicByteBuffer.cs | 10 ++++ src/ImageSharp/Memory/Buffer{T}.cs | 4 +- src/ImageSharp/Memory/ManagedByteBuffer.cs | 12 ----- src/ImageSharp/Memory/MemoryManager.cs | 11 +--- .../Memory/MemoryManagerExtensions.cs | 2 +- .../Memory/SimpleManagedMemoryManager.cs | 9 +--- 13 files changed, 78 insertions(+), 81 deletions(-) create mode 100644 src/ImageSharp/Memory/ArrayPoolMemoryManager.Buffer{T}.cs create mode 100644 src/ImageSharp/Memory/BasicByteBuffer.cs delete mode 100644 src/ImageSharp/Memory/ManagedByteBuffer.cs diff --git a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs index ae5ee2d9f6..076785526c 100644 --- a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs @@ -183,7 +183,7 @@ namespace SixLabors.ImageSharp.Drawing.Processors } } - applicator.Apply(scanline, minX, y); + applicator.Apply(scanline.Span, minX, y); } } } diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegBlockPostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegBlockPostProcessor.cs index 574967b6bf..5e8e8fa2cc 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegBlockPostProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegBlockPostProcessor.cs @@ -8,7 +8,7 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder { /// - /// Encapsulates the implementation of processing "raw" -s into Jpeg image channels. + /// Encapsulates the implementation of processing "raw" -s into Jpeg image channels. /// [StructLayout(LayoutKind.Sequential)] internal struct JpegBlockPostProcessor diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs index b8694c538e..3c43ba2444 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs @@ -30,13 +30,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components this.valOffset = memoryManager.AllocateFake(18); this.maxcode = memoryManager.AllocateFake(18); - using (BasicArrayBuffer huffsize = memoryManager.AllocateFake(257)) - using (BasicArrayBuffer huffcode = memoryManager.AllocateFake(257)) + using (IBuffer huffsize = memoryManager.Allocate(257)) + using (IBuffer huffcode = memoryManager.Allocate(257)) { - GenerateSizeTable(lengths, huffsize); - GenerateCodeTable(huffsize, huffcode); - GenerateDecoderTables(lengths, huffcode, this.valOffset, this.maxcode); - GenerateLookaheadTables(lengths, values, this.lookahead); + GenerateSizeTable(lengths, huffsize.Span); + GenerateCodeTable(huffsize.Span, huffcode.Span); + GenerateDecoderTables(lengths, huffcode.Span, this.valOffset.Span, this.maxcode.Span); + GenerateLookaheadTables(lengths, values, this.lookahead.Span); } this.huffval = memoryManager.AllocateManagedByteBuffer(values.Length, true); diff --git a/src/ImageSharp/Image/PixelAccessor{TPixel}.cs b/src/ImageSharp/Image/PixelAccessor{TPixel}.cs index 80c0ce4e66..3a894fb9ad 100644 --- a/src/ImageSharp/Image/PixelAccessor{TPixel}.cs +++ b/src/ImageSharp/Image/PixelAccessor{TPixel}.cs @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp { #pragma warning disable SA1401 // Fields must be private /// - /// The containing the pixel data. + /// The containing the pixel data. /// internal Buffer2D PixelBuffer; private bool ownedBuffer; diff --git a/src/ImageSharp/Memory/ArrayPoolMemoryManager.Buffer{T}.cs b/src/ImageSharp/Memory/ArrayPoolMemoryManager.Buffer{T}.cs new file mode 100644 index 0000000000..5b03520106 --- /dev/null +++ b/src/ImageSharp/Memory/ArrayPoolMemoryManager.Buffer{T}.cs @@ -0,0 +1,50 @@ +using System; + +namespace SixLabors.ImageSharp.Memory +{ + /// + /// Contains and + /// + public partial class ArrayPoolMemoryManager + { + private class Buffer : IBuffer + where T : struct + { + private readonly ArrayPoolMemoryManager memoryManager; + + private readonly int length; + + public Buffer(byte[] data, int length, ArrayPoolMemoryManager memoryManager) + { + this.memoryManager = memoryManager; + this.Data = data; + this.length = length; + } + + protected byte[] Data { get; private set; } + + public Span Span => this.Data.AsSpan().NonPortableCast().Slice(0, this.length); + + public void Dispose() + { + if (this.Data == null) + { + return; + } + + this.memoryManager.pool.Return(this.Data); + this.Data = null; + } + } + + private class ManagedByteBuffer : Buffer, IManagedByteBuffer + { + public ManagedByteBuffer(byte[] data, int length, ArrayPoolMemoryManager memoryManager) + : base(data, length, memoryManager) + { + } + + public byte[] Array => this.Data; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs b/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs index de26b0ba5e..41ef847847 100644 --- a/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs +++ b/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs @@ -1,5 +1,4 @@ -using System; -using System.Buffers; +using System.Buffers; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -8,7 +7,7 @@ namespace SixLabors.ImageSharp.Memory /// /// Implements by allocating memory from . /// - public class ArrayPoolMemoryManager : MemoryManager + public partial class ArrayPoolMemoryManager : MemoryManager { /// /// Defines the default maximum size of pooled arrays. @@ -44,7 +43,7 @@ namespace SixLabors.ImageSharp.Memory int bufferSizeInBytes = length * itemSizeBytes; byte[] byteBuffer = this.pool.Rent(bufferSizeInBytes); - var buffer = new Buffer(Unsafe.As(byteBuffer), length, this); + var buffer = new Buffer(byteBuffer, length, this); if (clear) { buffer.Clear(); @@ -64,19 +63,5 @@ namespace SixLabors.ImageSharp.Memory return buffer; } - - /// - internal override void Release(Buffer buffer) - { - T[] array = (buffer as IGetArray)?.GetArray(); - if (array == null) - { - return; - } - - // TODO: OMG Do not do this! - byte[] byteBuffer = Unsafe.As(array); - this.pool.Return(byteBuffer); - } } } \ No newline at end of file diff --git a/src/ImageSharp/Memory/BasicArrayBuffer.cs b/src/ImageSharp/Memory/BasicArrayBuffer.cs index d9eb5a19a1..17bf4c8439 100644 --- a/src/ImageSharp/Memory/BasicArrayBuffer.cs +++ b/src/ImageSharp/Memory/BasicArrayBuffer.cs @@ -36,27 +36,7 @@ namespace SixLabors.ImageSharp.Memory return ref span[index]; } } - - /// - /// Converts to an . - /// - /// The to convert. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator ReadOnlySpan(BasicArrayBuffer buffer) - { - return new ReadOnlySpan(buffer.Array, 0, buffer.Length); - } - - /// - /// Converts to an . - /// - /// The to convert. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator Span(BasicArrayBuffer buffer) - { - return new Span(buffer.Array, 0, buffer.Length); - } - + public void Dispose() { } diff --git a/src/ImageSharp/Memory/BasicByteBuffer.cs b/src/ImageSharp/Memory/BasicByteBuffer.cs new file mode 100644 index 0000000000..96b69ad3bf --- /dev/null +++ b/src/ImageSharp/Memory/BasicByteBuffer.cs @@ -0,0 +1,10 @@ +namespace SixLabors.ImageSharp.Memory +{ + internal class BasicByteBuffer : BasicArrayBuffer, IManagedByteBuffer + { + internal BasicByteBuffer(byte[] array) + : base(array) + { + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Memory/Buffer{T}.cs b/src/ImageSharp/Memory/Buffer{T}.cs index b1449f9b61..547e6f209d 100644 --- a/src/ImageSharp/Memory/Buffer{T}.cs +++ b/src/ImageSharp/Memory/Buffer{T}.cs @@ -58,9 +58,7 @@ namespace SixLabors.ImageSharp.Memory { return; } - - this.memoryManager?.Release(this); - + this.memoryManager = null; this.array = null; this.Length = 0; diff --git a/src/ImageSharp/Memory/ManagedByteBuffer.cs b/src/ImageSharp/Memory/ManagedByteBuffer.cs deleted file mode 100644 index 94d08e2aa7..0000000000 --- a/src/ImageSharp/Memory/ManagedByteBuffer.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace SixLabors.ImageSharp.Memory -{ - internal class ManagedByteBuffer : Buffer, IManagedByteBuffer - { - internal ManagedByteBuffer(byte[] array, int length, MemoryManager memoryManager) - : base(array, length, memoryManager) - { - } - - public byte[] Array => this.array; - } -} \ No newline at end of file diff --git a/src/ImageSharp/Memory/MemoryManager.cs b/src/ImageSharp/Memory/MemoryManager.cs index fe6bb7823f..7445c66b7e 100644 --- a/src/ImageSharp/Memory/MemoryManager.cs +++ b/src/ImageSharp/Memory/MemoryManager.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Memory public abstract class MemoryManager { /// - /// Allocates a of size , optionally + /// Allocates an of size , optionally /// clearing the buffer before it gets returned. /// /// Type of the data stored in the buffer @@ -24,15 +24,6 @@ namespace SixLabors.ImageSharp.Memory internal abstract IManagedByteBuffer AllocateManagedByteBuffer(int length, bool clear); - /// - /// Releases the memory allocated for . After this, the buffer - /// is no longer usable. - /// - /// Type of the data stored in the buffer - /// The buffer to release - internal abstract void Release(Buffer buffer) - where T : struct; - /// /// Temporal workaround. A method providing a "Buffer" based on a generic array without the 'Unsafe.As()' hackery. /// Should be replaced with 'Allocate()' as soon as SixLabors.Shapes has Span-based API-s! diff --git a/src/ImageSharp/Memory/MemoryManagerExtensions.cs b/src/ImageSharp/Memory/MemoryManagerExtensions.cs index 21c8f71248..b7fcaf4b36 100644 --- a/src/ImageSharp/Memory/MemoryManagerExtensions.cs +++ b/src/ImageSharp/Memory/MemoryManagerExtensions.cs @@ -6,7 +6,7 @@ internal static class MemoryManagerExtensions { /// - /// Allocates a of size . + /// Allocates a of size . /// Note: Depending on the implementation, the buffer may not cleared before /// returning, so it may contain data from an earlier use. /// diff --git a/src/ImageSharp/Memory/SimpleManagedMemoryManager.cs b/src/ImageSharp/Memory/SimpleManagedMemoryManager.cs index 804a468fd6..701c71ad43 100644 --- a/src/ImageSharp/Memory/SimpleManagedMemoryManager.cs +++ b/src/ImageSharp/Memory/SimpleManagedMemoryManager.cs @@ -8,17 +8,12 @@ /// internal override IBuffer Allocate(int length, bool clear) { - return new Buffer(new T[length], length, this); + return new BasicArrayBuffer(new T[length]); } internal override IManagedByteBuffer AllocateManagedByteBuffer(int length, bool clear) { - return new ManagedByteBuffer(new byte[length], length, this); - } - - /// - internal override void Release(Buffer buffer) - { + return new BasicByteBuffer(new byte[length]); } } } From 563eb6b3b98eae133df69fd2ffb8ca69823f787d Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 20 Feb 2018 03:37:51 +0100 Subject: [PATCH 072/151] goodbye top-level Buffer! --- .../CieXyChromaticityCoordinates.cs | 2 +- .../Jpeg/PdfJsPort/Components/PdfJsIDCT.cs | 3 +- src/ImageSharp/Memory/BasicArrayBuffer.cs | 2 +- src/ImageSharp/Memory/Buffer2DExtensions.cs | 1 - src/ImageSharp/Memory/Buffer{T}.cs | 77 ------------------- src/ImageSharp/Memory/IGetArray.cs | 14 ---- 6 files changed, 3 insertions(+), 96 deletions(-) delete mode 100644 src/ImageSharp/Memory/Buffer{T}.cs delete mode 100644 src/ImageSharp/Memory/IGetArray.cs diff --git a/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs b/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs index 92687a5630..487f464d8e 100644 --- a/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs +++ b/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs @@ -5,8 +5,8 @@ using System; using System.ComponentModel; using System.Numerics; using System.Runtime.CompilerServices; -// ReSharper disable CompareOfFloatsByEqualityOperator +// ReSharper disable CompareOfFloatsByEqualityOperator namespace SixLabors.ImageSharp.ColorSpaces { /// diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsIDCT.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsIDCT.cs index f2e269f6c2..00fa1985dd 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsIDCT.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsIDCT.cs @@ -3,11 +3,10 @@ using System; using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components { - using SixLabors.ImageSharp.Memory; - /// /// Performs the inverse Descrete Cosine Transform on each frame component. /// diff --git a/src/ImageSharp/Memory/BasicArrayBuffer.cs b/src/ImageSharp/Memory/BasicArrayBuffer.cs index 17bf4c8439..d2f8653f6f 100644 --- a/src/ImageSharp/Memory/BasicArrayBuffer.cs +++ b/src/ImageSharp/Memory/BasicArrayBuffer.cs @@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Memory return ref span[index]; } } - + public void Dispose() { } diff --git a/src/ImageSharp/Memory/Buffer2DExtensions.cs b/src/ImageSharp/Memory/Buffer2DExtensions.cs index 0cbffde775..ac5ab09dbd 100644 --- a/src/ImageSharp/Memory/Buffer2DExtensions.cs +++ b/src/ImageSharp/Memory/Buffer2DExtensions.cs @@ -12,7 +12,6 @@ namespace SixLabors.ImageSharp.Memory /// internal static class Buffer2DExtensions { - /// /// Gets a to the row 'y' beginning from the pixel at 'x'. /// diff --git a/src/ImageSharp/Memory/Buffer{T}.cs b/src/ImageSharp/Memory/Buffer{T}.cs deleted file mode 100644 index 547e6f209d..0000000000 --- a/src/ImageSharp/Memory/Buffer{T}.cs +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.Memory -{ - /// - /// - /// Manages a buffer of value type objects as a Disposable resource. - /// The backing array is either pooled or comes from the outside. - /// - /// The value type. - internal class Buffer : IBuffer, IGetArray - where T : struct - { - private MemoryManager memoryManager; - - // why is there such a rule? :S Protected should be fine for a field! -#pragma warning disable SA1401 // Fields should be private - /// - /// The backing array. - /// - protected T[] array; -#pragma warning restore SA1401 // Fields should be private - - internal Buffer(T[] array, int length, MemoryManager memoryManager) - { - if (array.Length < length) - { - throw new ArgumentException("Can't initialize a PinnedBuffer with array.Length < count", nameof(array)); - } - - this.Length = length; - this.array = array; - this.memoryManager = memoryManager; - } - - /// - /// Gets the count of "relevant" elements. It's usually smaller than 'Array.Length' when is pooled. - /// - public int Length { get; private set; } - - /// - /// Gets a to the backing buffer. - /// - public Span Span => new Span(this.array, 0, this.Length); - - /// - /// Disposes the instance by unpinning the array, and returning the pooled buffer when necessary. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Dispose() - { - if (this.array == null) - { - return; - } - - this.memoryManager = null; - this.array = null; - this.Length = 0; - - GC.SuppressFinalize(this); - } - - /// - /// TODO: Refactor this - /// - T[] IGetArray.GetArray() - { - return this.array; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Memory/IGetArray.cs b/src/ImageSharp/Memory/IGetArray.cs deleted file mode 100644 index 9b46058d19..0000000000 --- a/src/ImageSharp/Memory/IGetArray.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace SixLabors.ImageSharp.Memory -{ - /// - /// Absolutely temporal. - /// - internal interface IGetArray - where T : struct - { - /// - /// Absolutely temporal. - /// - T[] GetArray(); - } -} \ No newline at end of file From 1e78de07f9babf4180cd7385a6e96f22092008ba Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 20 Feb 2018 17:20:39 +0100 Subject: [PATCH 073/151] Buffer2DTests using a mock MemoryManager --- src/ImageSharp/Memory/BasicArrayBuffer.cs | 13 +++-- .../ImageSharp.Tests/Memory/Buffer2DTests.cs | 50 +++++++++++++------ 2 files changed, 46 insertions(+), 17 deletions(-) diff --git a/src/ImageSharp/Memory/BasicArrayBuffer.cs b/src/ImageSharp/Memory/BasicArrayBuffer.cs index d2f8653f6f..30ca210ac4 100644 --- a/src/ImageSharp/Memory/BasicArrayBuffer.cs +++ b/src/ImageSharp/Memory/BasicArrayBuffer.cs @@ -9,16 +9,23 @@ namespace SixLabors.ImageSharp.Memory internal class BasicArrayBuffer : IBuffer where T : struct { - public BasicArrayBuffer(T[] array) + public BasicArrayBuffer(T[] array, int length) { + DebugGuard.MustBeLessThanOrEqualTo(length, array.Length, nameof(length)); this.Array = array; + this.Length = length; + } + + public BasicArrayBuffer(T[] array) + : this(array, array.Length) + { } public T[] Array { get; } - public Span Span => this.Array; + public int Length { get; } - public int Length => this.Array.Length; + public Span Span => this.Array.AsSpan().Slice(0, this.Length); /// /// Returns a reference to specified element of the buffer. diff --git a/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs index 6afce94fd3..a765a77b12 100644 --- a/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs +++ b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs @@ -26,12 +26,38 @@ namespace SixLabors.ImageSharp.Tests.Memory } } + private MemoryManager MemoryManager { get; } = new MockMemoryManager(); + + private class MockMemoryManager : MemoryManager + { + internal override IBuffer Allocate(int length, bool clear) + { + T[] array = new T[length + 42]; + + if (!clear) + { + Span data = array.AsSpan().NonPortableCast(); + for (int i = 0; i < data.Length; i++) + { + data[i] = 42; + } + } + + return new BasicArrayBuffer(array, length); + } + + internal override IManagedByteBuffer AllocateManagedByteBuffer(int length, bool clear) + { + throw new NotImplementedException(); + } + } + [Theory] [InlineData(7, 42)] [InlineData(1025, 17)] public void Construct(int width, int height) { - using (Buffer2D buffer = Configuration.Default.MemoryManager.Allocate2D(width, height)) + using (Buffer2D buffer = this.MemoryManager.Allocate2D(width, height)) { Assert.Equal(width, buffer.Width); Assert.Equal(height, buffer.Height); @@ -42,16 +68,12 @@ namespace SixLabors.ImageSharp.Tests.Memory [Fact] public void CreateClean() { - for (int i = 0; i < 100; i++) + using (Buffer2D buffer = this.MemoryManager.Allocate2D(42, 42, true)) { - using (Buffer2D buffer = Configuration.Default.MemoryManager.Allocate2D(42, 42, true)) + Span span = buffer.Span; + for (int j = 0; j < span.Length; j++) { - Span span = buffer.Span; - for (int j = 0; j < span.Length; j++) - { - Assert.Equal(0, span[j]); - span[j] = 666; - } + Assert.Equal(0, span[j]); } } } @@ -62,7 +84,7 @@ namespace SixLabors.ImageSharp.Tests.Memory [InlineData(17, 42, 41)] public void GetRowSpanY(int width, int height, int y) { - using (Buffer2D buffer = Configuration.Default.MemoryManager.Allocate2D(width, height)) + using (Buffer2D buffer = this.MemoryManager.Allocate2D(width, height)) { Span span = buffer.GetRowSpan(y); @@ -78,7 +100,7 @@ namespace SixLabors.ImageSharp.Tests.Memory [InlineData(17, 42, 0, 41)] public void GetRowSpanXY(int width, int height, int x, int y) { - using (Buffer2D buffer = Configuration.Default.MemoryManager.Allocate2D(width, height)) + using (Buffer2D buffer = this.MemoryManager.Allocate2D(width, height)) { Span span = buffer.GetRowSpan(x, y); @@ -94,13 +116,13 @@ namespace SixLabors.ImageSharp.Tests.Memory [InlineData(99, 88, 98, 87)] public void Indexer(int width, int height, int x, int y) { - using (Buffer2D buffer = Configuration.Default.MemoryManager.Allocate2D(width, height)) + using (Buffer2D buffer = this.MemoryManager.Allocate2D(width, height)) { - Span array = buffer.Buffer.Span; + Span span = buffer.Buffer.Span; ref TestStructs.Foo actual = ref buffer[x, y]; - ref TestStructs.Foo expected = ref array[y * width + x]; + ref TestStructs.Foo expected = ref span[y * width + x]; Assert.True(Unsafe.AreSame(ref expected, ref actual)); } From f5f5f6f7492a9fbf49939fabac8bdee7fdc20c3b Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 20 Feb 2018 17:42:21 +0100 Subject: [PATCH 074/151] ArrayPoolMemoryManagerTests --- .../Memory/ArrayPoolMemoryManagerTests.cs | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 tests/ImageSharp.Tests/Memory/ArrayPoolMemoryManagerTests.cs diff --git a/tests/ImageSharp.Tests/Memory/ArrayPoolMemoryManagerTests.cs b/tests/ImageSharp.Tests/Memory/ArrayPoolMemoryManagerTests.cs new file mode 100644 index 0000000000..2a6a23116e --- /dev/null +++ b/tests/ImageSharp.Tests/Memory/ArrayPoolMemoryManagerTests.cs @@ -0,0 +1,90 @@ +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Tests.Memory +{ + using System.Linq; + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; + + using SixLabors.ImageSharp.Memory; + + using Xunit; + + public class ArrayPoolMemoryManagerTests + { + private const int MaxPooledBufferSizeInBytes = 2048; + + private MemoryManager MemoryManager { get; } = new ArrayPoolMemoryManager(MaxPooledBufferSizeInBytes); + + /// + /// Rent 'n' buffers -> return all -> re-rent, verify if there is at least one in common. + /// + private bool CheckIsPooled(int size) + where T : struct + { + IBuffer buf1 = this.MemoryManager.Allocate(size); + IBuffer buf2 = this.MemoryManager.Allocate(size); + IBuffer buf3 = this.MemoryManager.Allocate(size); + + ref T buf1FirstPrev = ref buf1.DangerousGetPinnableReference(); + ref T buf2FirstPrev = ref buf2.DangerousGetPinnableReference(); + ref T buf3FirstPrev = ref buf3.DangerousGetPinnableReference(); + + buf1.Dispose(); + buf2.Dispose(); + buf3.Dispose(); + + buf1 = this.MemoryManager.Allocate(size); + buf2 = this.MemoryManager.Allocate(size); + buf3 = this.MemoryManager.Allocate(size); + + bool same1 = Unsafe.AreSame(ref buf1FirstPrev, ref buf1.DangerousGetPinnableReference()); + bool same2 = Unsafe.AreSame(ref buf2FirstPrev, ref buf2.DangerousGetPinnableReference()); + bool same3 = Unsafe.AreSame(ref buf3FirstPrev, ref buf3.DangerousGetPinnableReference()); + + buf1.Dispose(); + buf2.Dispose(); + buf3.Dispose(); + + return same1 || same2 || same3; + } + + [StructLayout(LayoutKind.Explicit, Size = MaxPooledBufferSizeInBytes / 4)] + struct LargeStruct + { + } + + [Theory] + [InlineData(32)] + [InlineData(512)] + [InlineData(MaxPooledBufferSizeInBytes - 1)] + public void SmallBuffersArePooled_OfByte(int size) + { + Assert.True(this.CheckIsPooled(size)); + } + + + [Theory] + [InlineData(128 * 1024 * 1024)] + [InlineData(MaxPooledBufferSizeInBytes + 1)] + public void LargeBuffersAreNotPooled_OfByte(int size) + { + Assert.False(this.CheckIsPooled(size)); + } + + [Fact] + public unsafe void SmallBuffersArePooled_OfBigValueType() + { + int count = MaxPooledBufferSizeInBytes / sizeof(LargeStruct) - 1; + + Assert.True(this.CheckIsPooled(count)); + } + + [Fact] + public unsafe void LaregeBuffersAreNotPooled_OfBigValueType() + { + int count = MaxPooledBufferSizeInBytes / sizeof(LargeStruct) + 1; + + Assert.False(this.CheckIsPooled(count)); + } + } +} \ No newline at end of file From f25b112466788bfba437b0327338481f473423a4 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 20 Feb 2018 19:18:33 +0100 Subject: [PATCH 075/151] Covering ArrayPoolMemoryManager and it's buffer. Took hours, but worth it! --- src/ImageSharp/Memory/IBuffer{T}.cs | 3 + src/ImageSharp/Memory/IManagedByteBuffer.cs | 3 + src/ImageSharp/Memory/MemoryManager.cs | 7 +- .../Memory/ArrayPoolMemoryManagerTests.cs | 74 +++-- .../Memory/BufferTestSuite.cs | 285 ++++++++++++++++++ .../Memory/SimpleManagedMemoryManagerTests.cs | 15 + 6 files changed, 351 insertions(+), 36 deletions(-) create mode 100644 tests/ImageSharp.Tests/Memory/BufferTestSuite.cs create mode 100644 tests/ImageSharp.Tests/Memory/SimpleManagedMemoryManagerTests.cs diff --git a/src/ImageSharp/Memory/IBuffer{T}.cs b/src/ImageSharp/Memory/IBuffer{T}.cs index a0f80063f8..db6bf5b389 100644 --- a/src/ImageSharp/Memory/IBuffer{T}.cs +++ b/src/ImageSharp/Memory/IBuffer{T}.cs @@ -1,3 +1,6 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + using System; namespace SixLabors.ImageSharp.Memory diff --git a/src/ImageSharp/Memory/IManagedByteBuffer.cs b/src/ImageSharp/Memory/IManagedByteBuffer.cs index 541957f422..d75fb9b6c7 100644 --- a/src/ImageSharp/Memory/IManagedByteBuffer.cs +++ b/src/ImageSharp/Memory/IManagedByteBuffer.cs @@ -1,3 +1,6 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + namespace SixLabors.ImageSharp.Memory { /// diff --git a/src/ImageSharp/Memory/MemoryManager.cs b/src/ImageSharp/Memory/MemoryManager.cs index 7445c66b7e..7318a313e3 100644 --- a/src/ImageSharp/Memory/MemoryManager.cs +++ b/src/ImageSharp/Memory/MemoryManager.cs @@ -1,8 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Memory { diff --git a/tests/ImageSharp.Tests/Memory/ArrayPoolMemoryManagerTests.cs b/tests/ImageSharp.Tests/Memory/ArrayPoolMemoryManagerTests.cs index 2a6a23116e..fcd4c1b3b2 100644 --- a/tests/ImageSharp.Tests/Memory/ArrayPoolMemoryManagerTests.cs +++ b/tests/ImageSharp.Tests/Memory/ArrayPoolMemoryManagerTests.cs @@ -1,3 +1,6 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests.Memory { @@ -16,39 +19,31 @@ namespace SixLabors.ImageSharp.Tests.Memory private MemoryManager MemoryManager { get; } = new ArrayPoolMemoryManager(MaxPooledBufferSizeInBytes); /// - /// Rent 'n' buffers -> return all -> re-rent, verify if there is at least one in common. + /// Rent a buffer -> return it -> re-rent -> verify if it's span points to the previous location /// - private bool CheckIsPooled(int size) + private bool CheckIsRentingPooledBuffer(int length) where T : struct { - IBuffer buf1 = this.MemoryManager.Allocate(size); - IBuffer buf2 = this.MemoryManager.Allocate(size); - IBuffer buf3 = this.MemoryManager.Allocate(size); - - ref T buf1FirstPrev = ref buf1.DangerousGetPinnableReference(); - ref T buf2FirstPrev = ref buf2.DangerousGetPinnableReference(); - ref T buf3FirstPrev = ref buf3.DangerousGetPinnableReference(); - - buf1.Dispose(); - buf2.Dispose(); - buf3.Dispose(); - - buf1 = this.MemoryManager.Allocate(size); - buf2 = this.MemoryManager.Allocate(size); - buf3 = this.MemoryManager.Allocate(size); - - bool same1 = Unsafe.AreSame(ref buf1FirstPrev, ref buf1.DangerousGetPinnableReference()); - bool same2 = Unsafe.AreSame(ref buf2FirstPrev, ref buf2.DangerousGetPinnableReference()); - bool same3 = Unsafe.AreSame(ref buf3FirstPrev, ref buf3.DangerousGetPinnableReference()); - - buf1.Dispose(); - buf2.Dispose(); - buf3.Dispose(); + IBuffer buffer = this.MemoryManager.Allocate(length); + ref T ptrToPrevPosition0 = ref buffer.DangerousGetPinnableReference(); + buffer.Dispose(); + + buffer = this.MemoryManager.Allocate(length); + bool sameBuffers = Unsafe.AreSame(ref ptrToPrevPosition0, ref buffer.DangerousGetPinnableReference()); + buffer.Dispose(); + + return sameBuffers; + } - return same1 || same2 || same3; + public class BufferTests : BufferTestSuite + { + public BufferTests() + : base(new ArrayPoolMemoryManager(MaxPooledBufferSizeInBytes)) + { + } } - [StructLayout(LayoutKind.Explicit, Size = MaxPooledBufferSizeInBytes / 4)] + [StructLayout(LayoutKind.Explicit, Size = MaxPooledBufferSizeInBytes / 5)] struct LargeStruct { } @@ -59,7 +54,7 @@ namespace SixLabors.ImageSharp.Tests.Memory [InlineData(MaxPooledBufferSizeInBytes - 1)] public void SmallBuffersArePooled_OfByte(int size) { - Assert.True(this.CheckIsPooled(size)); + Assert.True(this.CheckIsRentingPooledBuffer(size)); } @@ -68,7 +63,7 @@ namespace SixLabors.ImageSharp.Tests.Memory [InlineData(MaxPooledBufferSizeInBytes + 1)] public void LargeBuffersAreNotPooled_OfByte(int size) { - Assert.False(this.CheckIsPooled(size)); + Assert.False(this.CheckIsRentingPooledBuffer(size)); } [Fact] @@ -76,7 +71,7 @@ namespace SixLabors.ImageSharp.Tests.Memory { int count = MaxPooledBufferSizeInBytes / sizeof(LargeStruct) - 1; - Assert.True(this.CheckIsPooled(count)); + Assert.True(this.CheckIsRentingPooledBuffer(count)); } [Fact] @@ -84,7 +79,24 @@ namespace SixLabors.ImageSharp.Tests.Memory { int count = MaxPooledBufferSizeInBytes / sizeof(LargeStruct) + 1; - Assert.False(this.CheckIsPooled(count)); + Assert.False(this.CheckIsRentingPooledBuffer(count)); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void CleaningRequests_AreControlledByAllocationParameter_Clean(bool clean) + { + using (IBuffer firstAlloc = this.MemoryManager.Allocate(42)) + { + firstAlloc.Span.Fill(666); + } + + using (IBuffer secondAlloc = this.MemoryManager.Allocate(42, clean)) + { + int expected = clean ? 0 : 666; + Assert.Equal(expected, secondAlloc.Span[0]); + } } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Memory/BufferTestSuite.cs b/tests/ImageSharp.Tests/Memory/BufferTestSuite.cs new file mode 100644 index 0000000000..50477cb5cf --- /dev/null +++ b/tests/ImageSharp.Tests/Memory/BufferTestSuite.cs @@ -0,0 +1,285 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Memory; +using Xunit; +// ReSharper disable InconsistentNaming + +namespace SixLabors.ImageSharp.Tests.Memory +{ + + + /// + /// Inherit this class to test an implementation (provided by ). + /// + public abstract class BufferTestSuite + { + protected BufferTestSuite(MemoryManager memoryManager) + { + this.MemoryManager = memoryManager; + } + + protected MemoryManager MemoryManager { get; } + + public struct CustomStruct : IEquatable + { + public long A; + + public byte B; + + public float C; + + public CustomStruct(long a, byte b, float c) + { + this.A = a; + this.B = b; + this.C = c; + } + + public bool Equals(CustomStruct other) + { + return this.A == other.A && this.B == other.B && this.C.Equals(other.C); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + return obj is CustomStruct && this.Equals((CustomStruct)obj); + } + + public override int GetHashCode() + { + unchecked + { + int hashCode = this.A.GetHashCode(); + hashCode = (hashCode * 397) ^ this.B.GetHashCode(); + hashCode = (hashCode * 397) ^ this.C.GetHashCode(); + return hashCode; + } + } + } + + public static readonly TheoryData LenthValues = new TheoryData { 0, 1, 7, 1023, 1024 }; + + [Theory] + [MemberData(nameof(LenthValues))] + public void HasCorrectLength_byte(int desiredLength) + { + this.TestHasCorrectLength(desiredLength); + } + + [Theory] + [MemberData(nameof(LenthValues))] + public void HasCorrectLength_float(int desiredLength) + { + this.TestHasCorrectLength(desiredLength); + } + + [Theory] + [MemberData(nameof(LenthValues))] + public void HasCorrectLength_CustomStruct(int desiredLength) + { + this.TestHasCorrectLength(desiredLength); + } + + private void TestHasCorrectLength(int desiredLength) + where T : struct + { + using (IBuffer buffer = this.MemoryManager.Allocate(desiredLength)) + { + Assert.Equal(desiredLength, buffer.Span.Length); + } + } + + [Theory] + [MemberData(nameof(LenthValues))] + public void CanAllocateCleanBuffer_byte(int desiredLength) + { + this.TestCanAllocateCleanBuffer(desiredLength, false); + this.TestCanAllocateCleanBuffer(desiredLength, true); + } + + [Theory] + [MemberData(nameof(LenthValues))] + public void CanAllocateCleanBuffer_double(int desiredLength) + { + this.TestCanAllocateCleanBuffer(desiredLength); + } + + [Theory] + [MemberData(nameof(LenthValues))] + public void CanAllocateCleanBuffer_CustomStruct(int desiredLength) + { + this.TestCanAllocateCleanBuffer(desiredLength); + } + + private IBuffer Allocate(int desiredLength, bool clean, bool managedByteBuffer) + where T : struct + { + if (managedByteBuffer) + { + if (!(this.MemoryManager.AllocateManagedByteBuffer(desiredLength, clean) is IBuffer buffer)) + { + throw new InvalidOperationException("typeof(T) != typeof(byte)"); + } + + return buffer; + } + + return this.MemoryManager.Allocate(desiredLength, clean); + } + + private void TestCanAllocateCleanBuffer(int desiredLength, bool testManagedByteBuffer = false) + where T : struct, IEquatable + { + ReadOnlySpan expected = new T[desiredLength]; + + for (int i = 0; i < 10; i++) + { + using (IBuffer buffer = this.Allocate(desiredLength, true, testManagedByteBuffer)) + { + Assert.True(buffer.Span.SequenceEqual(expected)); + } + } + } + + [Theory] + [MemberData(nameof(LenthValues))] + public void SpanPropertyIsAlwaysTheSame_int(int desiredLength) + { + this.TestSpanPropertyIsAlwaysTheSame(desiredLength); + } + + [Theory] + [MemberData(nameof(LenthValues))] + public void SpanPropertyIsAlwaysTheSame_byte(int desiredLength) + { + this.TestSpanPropertyIsAlwaysTheSame(desiredLength, false); + this.TestSpanPropertyIsAlwaysTheSame(desiredLength, true); + } + + private void TestSpanPropertyIsAlwaysTheSame(int desiredLength, bool testManagedByteBuffer = false) + where T : struct + { + using (IBuffer buffer = this.Allocate(desiredLength, false, testManagedByteBuffer)) + { + ref T a = ref buffer.Span.DangerousGetPinnableReference(); + ref T b = ref buffer.Span.DangerousGetPinnableReference(); + ref T c = ref buffer.Span.DangerousGetPinnableReference(); + + Assert.True(Unsafe.AreSame(ref a, ref b)); + Assert.True(Unsafe.AreSame(ref b, ref c)); + } + } + + [Theory] + [MemberData(nameof(LenthValues))] + public void WriteAndReadElements_float(int desiredLength) + { + this.TestWriteAndReadElements(desiredLength, x => x * 1.2f); + } + + [Theory] + [MemberData(nameof(LenthValues))] + public void WriteAndReadElements_byte(int desiredLength) + { + this.TestWriteAndReadElements(desiredLength, x => (byte)(x+1), false); + this.TestWriteAndReadElements(desiredLength, x => (byte)(x + 1), true); + } + + private void TestWriteAndReadElements(int desiredLength, Func getExpectedValue, bool testManagedByteBuffer = false) + where T : struct + { + using (IBuffer buffer = this.Allocate(desiredLength, false, testManagedByteBuffer)) + { + T[] expectedVals = new T[buffer.Length()]; + + for (int i = 0; i < buffer.Length(); i++) + { + Span span = buffer.Span; + expectedVals[i] = getExpectedValue(i); + span[i] = expectedVals[i]; + } + + for (int i = 0; i < buffer.Length(); i++) + { + Span span = buffer.Span; + Assert.Equal(expectedVals[i], span[i]); + } + } + } + + [Theory] + [MemberData(nameof(LenthValues))] + public void IndexingSpan_WhenOutOfRange_Throws_byte(int desiredLength) + { + this.TestIndexOutOfRangeShouldThrow(desiredLength, false); + this.TestIndexOutOfRangeShouldThrow(desiredLength, true); + } + + [Theory] + [MemberData(nameof(LenthValues))] + public void IndexingSpan_WhenOutOfRange_Throws_long(int desiredLength) + { + this.TestIndexOutOfRangeShouldThrow(desiredLength); + } + + [Theory] + [MemberData(nameof(LenthValues))] + public void IndexingSpan_WhenOutOfRange_Throws_CustomStruct(int desiredLength) + { + this.TestIndexOutOfRangeShouldThrow(desiredLength); + } + + private T TestIndexOutOfRangeShouldThrow(int desiredLength, bool testManagedByteBuffer = false) + where T : struct, IEquatable + { + var dummy = default(T); + + using (IBuffer buffer = this.Allocate(desiredLength, false, testManagedByteBuffer)) + { + Assert.ThrowsAny( + () => + { + Span span = buffer.Span; + dummy = span[desiredLength]; + }); + + Assert.ThrowsAny( + () => + { + Span span = buffer.Span; + dummy = span[desiredLength + 1]; + }); + + Assert.ThrowsAny( + () => + { + Span span = buffer.Span; + dummy = span[desiredLength + 42]; + }); + } + + return dummy; + } + + [Theory] + [InlineData(1)] + [InlineData(7)] + [InlineData(1024)] + [InlineData(6666)] + public void ManagedByteBuffer_ArrayIsCorrect(int desiredLength) + { + using (IManagedByteBuffer buffer = this.MemoryManager.AllocateManagedByteBuffer(desiredLength)) + { + ref byte array0 = ref buffer.Array[0]; + ref byte span0 = ref buffer.DangerousGetPinnableReference(); + + Assert.True(Unsafe.AreSame(ref span0, ref array0)); + Assert.True(buffer.Array.Length >= buffer.Span.Length); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Memory/SimpleManagedMemoryManagerTests.cs b/tests/ImageSharp.Tests/Memory/SimpleManagedMemoryManagerTests.cs new file mode 100644 index 0000000000..eb74145820 --- /dev/null +++ b/tests/ImageSharp.Tests/Memory/SimpleManagedMemoryManagerTests.cs @@ -0,0 +1,15 @@ +namespace SixLabors.ImageSharp.Tests.Memory +{ + using SixLabors.ImageSharp.Memory; + + public class SimpleManagedMemoryManagerTests + { + public class BufferTests : BufferTestSuite + { + public BufferTests() + : base(new SimpleManagedMemoryManager()) + { + } + } + } +} \ No newline at end of file From 73a20cb6de60929693e016d229554202ee87826a Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 20 Feb 2018 21:37:18 +0100 Subject: [PATCH 076/151] ArrayPoolMemoryManager uses a different ArrayPool for large buffers + implemented ReleaseRetainedResources() --- .../ArrayPoolMemoryManager.Buffer{T}.cs | 20 ++-- .../Memory/ArrayPoolMemoryManager.cs | 91 ++++++++++++++++--- src/ImageSharp/Memory/BufferExtensions.cs | 3 + src/ImageSharp/Memory/MemoryManager.cs | 8 ++ .../Memory/ArrayPoolMemoryManagerTests.cs | 69 +++++++++++++- 5 files changed, 169 insertions(+), 22 deletions(-) diff --git a/src/ImageSharp/Memory/ArrayPoolMemoryManager.Buffer{T}.cs b/src/ImageSharp/Memory/ArrayPoolMemoryManager.Buffer{T}.cs index 5b03520106..78e275e8cf 100644 --- a/src/ImageSharp/Memory/ArrayPoolMemoryManager.Buffer{T}.cs +++ b/src/ImageSharp/Memory/ArrayPoolMemoryManager.Buffer{T}.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; namespace SixLabors.ImageSharp.Memory { @@ -10,15 +14,15 @@ namespace SixLabors.ImageSharp.Memory private class Buffer : IBuffer where T : struct { - private readonly ArrayPoolMemoryManager memoryManager; - private readonly int length; - public Buffer(byte[] data, int length, ArrayPoolMemoryManager memoryManager) + private readonly ArrayPool sourcePool; + + public Buffer(byte[] data, int length, ArrayPool sourcePool) { - this.memoryManager = memoryManager; this.Data = data; this.length = length; + this.sourcePool = sourcePool; } protected byte[] Data { get; private set; } @@ -32,15 +36,15 @@ namespace SixLabors.ImageSharp.Memory return; } - this.memoryManager.pool.Return(this.Data); + this.sourcePool.Return(this.Data); this.Data = null; } } private class ManagedByteBuffer : Buffer, IManagedByteBuffer { - public ManagedByteBuffer(byte[] data, int length, ArrayPoolMemoryManager memoryManager) - : base(data, length, memoryManager) + public ManagedByteBuffer(byte[] data, int length, ArrayPool sourcePool) + : base(data, length, sourcePool) { } diff --git a/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs b/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs index 41ef847847..4f80b15ecb 100644 --- a/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs +++ b/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs @@ -1,27 +1,46 @@ -using System.Buffers; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.Memory { + using Guard = SixLabors.Guard; + /// /// Implements by allocating memory from . /// public partial class ArrayPoolMemoryManager : MemoryManager { /// - /// Defines the default maximum size of pooled arrays. - /// Currently set to a value equivalent to 16 MegaPixels of an image. + /// The default value for: maximum size of pooled arrays in bytes. + /// Currently set to 32MB, which is equivalent to 8 megapixels of raw data. + /// + internal const int DefaultMaxPooledBufferSizeInBytes = 32 * 1024 * 1024; + + /// + /// The value for: The threshold to pool arrays in which has less buckets for memory safety. /// - public const int DefaultMaxSizeInBytes = 4096 * 4096 * 4; + private const int DefaultLargeBufferThresholdInBytes = 8 * 1024 * 1024; - private readonly ArrayPool pool; + /// + /// The for huge buffers, which is not kept clean. + /// + private ArrayPool largeArrayPool; + + /// + /// The for small-to-medium buffers which is not kept clean. + /// + private ArrayPool normalArrayPool; /// /// Initializes a new instance of the class. /// public ArrayPoolMemoryManager() - : this(DefaultMaxSizeInBytes) + : this(DefaultMaxPooledBufferSizeInBytes, DefaultLargeBufferThresholdInBytes) { } @@ -30,10 +49,40 @@ namespace SixLabors.ImageSharp.Memory /// /// The maximum size of pooled arrays. Arrays over the thershold are gonna be always allocated. public ArrayPoolMemoryManager(int maxPoolSizeInBytes) + : this(maxPoolSizeInBytes, GetLargeBufferThresholdInBytes(maxPoolSizeInBytes)) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The maximum size of pooled arrays. Arrays over the thershold are gonna be always allocated. + /// The threshold to pool arrays in which has less buckets for memory safety. + public ArrayPoolMemoryManager(int maxPoolSizeInBytes, int largeBufferThresholdInBytes) { Guard.MustBeGreaterThan(maxPoolSizeInBytes, 0, nameof(maxPoolSizeInBytes)); + Guard.MustBeLessThanOrEqualTo(largeBufferThresholdInBytes, maxPoolSizeInBytes, nameof(largeBufferThresholdInBytes)); + + this.MaxPoolSizeInBytes = maxPoolSizeInBytes; + this.LargeBufferThresholdInBytes = largeBufferThresholdInBytes; - this.pool = ArrayPool.Create(maxPoolSizeInBytes, 50); + this.InitArrayPools(); + } + + /// + /// Gets the maximum size of pooled arrays in bytes. + /// + public int MaxPoolSizeInBytes { get; } + + /// + /// Gets the threshold to pool arrays in which has less buckets for memory safety. + /// + public int LargeBufferThresholdInBytes { get; } + + /// + public override void ReleaseRetainedResources() + { + this.InitArrayPools(); } /// @@ -42,8 +91,10 @@ namespace SixLabors.ImageSharp.Memory int itemSizeBytes = Unsafe.SizeOf(); int bufferSizeInBytes = length * itemSizeBytes; - byte[] byteBuffer = this.pool.Rent(bufferSizeInBytes); - var buffer = new Buffer(byteBuffer, length, this); + ArrayPool pool = this.GetArrayPool(bufferSizeInBytes); + byte[] byteArray = pool.Rent(bufferSizeInBytes); + + var buffer = new Buffer(byteArray, length, pool); if (clear) { buffer.Clear(); @@ -54,8 +105,10 @@ namespace SixLabors.ImageSharp.Memory internal override IManagedByteBuffer AllocateManagedByteBuffer(int length, bool clear) { - byte[] array = this.pool.Rent(length); - var buffer = new ManagedByteBuffer(array, length, this); + ArrayPool pool = this.GetArrayPool(length); + byte[] byteArray = pool.Rent(length); + + var buffer = new ManagedByteBuffer(byteArray, length, pool); if (clear) { buffer.Clear(); @@ -63,5 +116,21 @@ namespace SixLabors.ImageSharp.Memory return buffer; } + + private static int GetLargeBufferThresholdInBytes(int maxPoolSizeInBytes) + { + return maxPoolSizeInBytes / 4; + } + + private ArrayPool GetArrayPool(int bufferSizeInBytes) + { + return bufferSizeInBytes <= this.LargeBufferThresholdInBytes ? this.normalArrayPool : this.largeArrayPool; + } + + private void InitArrayPools() + { + this.largeArrayPool = ArrayPool.Create(this.MaxPoolSizeInBytes, 8); + this.normalArrayPool = ArrayPool.Create(this.LargeBufferThresholdInBytes, 24); + } } } \ No newline at end of file diff --git a/src/ImageSharp/Memory/BufferExtensions.cs b/src/ImageSharp/Memory/BufferExtensions.cs index 8975d3b45d..b863dfc9aa 100644 --- a/src/ImageSharp/Memory/BufferExtensions.cs +++ b/src/ImageSharp/Memory/BufferExtensions.cs @@ -1,3 +1,6 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + using System; using System.Runtime.CompilerServices; diff --git a/src/ImageSharp/Memory/MemoryManager.cs b/src/ImageSharp/Memory/MemoryManager.cs index 7318a313e3..8e2df8cec2 100644 --- a/src/ImageSharp/Memory/MemoryManager.cs +++ b/src/ImageSharp/Memory/MemoryManager.cs @@ -30,5 +30,13 @@ namespace SixLabors.ImageSharp.Memory { return new BasicArrayBuffer(new T[length]); } + + /// + /// Releases all retained resources not being in use. + /// Eg: by resetting array pools and letting GC to free the arrays. + /// + public virtual void ReleaseRetainedResources() + { + } } } diff --git a/tests/ImageSharp.Tests/Memory/ArrayPoolMemoryManagerTests.cs b/tests/ImageSharp.Tests/Memory/ArrayPoolMemoryManagerTests.cs index fcd4c1b3b2..0bd243fda2 100644 --- a/tests/ImageSharp.Tests/Memory/ArrayPoolMemoryManagerTests.cs +++ b/tests/ImageSharp.Tests/Memory/ArrayPoolMemoryManagerTests.cs @@ -4,6 +4,7 @@ // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests.Memory { + using System; using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -15,8 +16,10 @@ namespace SixLabors.ImageSharp.Tests.Memory public class ArrayPoolMemoryManagerTests { private const int MaxPooledBufferSizeInBytes = 2048; - - private MemoryManager MemoryManager { get; } = new ArrayPoolMemoryManager(MaxPooledBufferSizeInBytes); + + private const int LargeBufferThresholdInBytes = MaxPooledBufferSizeInBytes / 2; + + private MemoryManager MemoryManager { get; } = new ArrayPoolMemoryManager(MaxPooledBufferSizeInBytes, LargeBufferThresholdInBytes); /// /// Rent a buffer -> return it -> re-rent -> verify if it's span points to the previous location @@ -38,11 +41,36 @@ namespace SixLabors.ImageSharp.Tests.Memory public class BufferTests : BufferTestSuite { public BufferTests() - : base(new ArrayPoolMemoryManager(MaxPooledBufferSizeInBytes)) + : base(new ArrayPoolMemoryManager(MaxPooledBufferSizeInBytes, LargeBufferThresholdInBytes)) { } } + public class Constructor + { + [Fact] + public void WhenBothParametersPassedByUser() + { + var mgr = new ArrayPoolMemoryManager(1111, 666); + Assert.Equal(1111, mgr.MaxPoolSizeInBytes); + Assert.Equal(666, mgr.LargeBufferThresholdInBytes); + } + + [Fact] + public void WhenPassedOnly_MaxPooledBufferSizeInBytes_SmallerThresholdIsAutoCalculated() + { + var mgr = new ArrayPoolMemoryManager(5000); + Assert.Equal(5000, mgr.MaxPoolSizeInBytes); + Assert.True(mgr.LargeBufferThresholdInBytes < mgr.MaxPoolSizeInBytes); + } + + [Fact] + public void When_LargeBufferThresholdInBytes_IsGreaterThan_MaxPooledBufferSizeInBytes_Throws() + { + Assert.ThrowsAny(() => { new ArrayPoolMemoryManager(100, 200); }); + } + } + [StructLayout(LayoutKind.Explicit, Size = MaxPooledBufferSizeInBytes / 5)] struct LargeStruct { @@ -98,5 +126,40 @@ namespace SixLabors.ImageSharp.Tests.Memory Assert.Equal(expected, secondAlloc.Span[0]); } } + + [Fact] + public void ReleaseRetainedResources_ReplacesInnerArrayPool() + { + IBuffer buffer = this.MemoryManager.Allocate(32); + ref int ptrToPrev0 = ref buffer.Span.DangerousGetPinnableReference(); + buffer.Dispose(); + + this.MemoryManager.ReleaseRetainedResources(); + buffer = this.MemoryManager.Allocate(32); + + Assert.False(Unsafe.AreSame(ref ptrToPrev0, ref buffer.DangerousGetPinnableReference())); + } + + [Fact] + public void ReleaseRetainedResources_DisposingPreviouslyAllocatedBuffer_IsAllowed() + { + IBuffer buffer = this.MemoryManager.Allocate(32); + this.MemoryManager.ReleaseRetainedResources(); + buffer.Dispose(); + } + + [Fact] + public void AllocationOverLargeArrayThreshold_UsesDifferentPool() + { + int arrayLengthThreshold = LargeBufferThresholdInBytes / sizeof(int); + + IBuffer small = this.MemoryManager.Allocate(arrayLengthThreshold - 1); + ref int ptr2Small = ref small.DangerousGetPinnableReference(); + small.Dispose(); + + IBuffer large = this.MemoryManager.Allocate(arrayLengthThreshold + 1); + + Assert.False(Unsafe.AreSame(ref ptr2Small, ref large.DangerousGetPinnableReference())); + } } } \ No newline at end of file From 16d5f36e724533b8707a513841739e41b25723f0 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 20 Feb 2018 21:58:26 +0100 Subject: [PATCH 077/151] allowing bucket sizes to be passed to ArrayPoolMemoryManager --- .../Memory/ArrayPoolMemoryManager.cs | 46 ++++++++++++------- .../Memory/ArrayPoolMemoryManagerTests.cs | 16 +++---- 2 files changed, 38 insertions(+), 24 deletions(-) diff --git a/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs b/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs index 4f80b15ecb..7c2240a574 100644 --- a/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs +++ b/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs @@ -1,15 +1,11 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; using System.Buffers; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.Memory { - using Guard = SixLabors.Guard; - /// /// Implements by allocating memory from . /// @@ -27,14 +23,18 @@ namespace SixLabors.ImageSharp.Memory private const int DefaultLargeBufferThresholdInBytes = 8 * 1024 * 1024; /// - /// The for huge buffers, which is not kept clean. + /// The for small-to-medium buffers which is not kept clean. /// - private ArrayPool largeArrayPool; + private ArrayPool normalArrayPool; /// - /// The for small-to-medium buffers which is not kept clean. + /// The for huge buffers, which is not kept clean. /// - private ArrayPool normalArrayPool; + private ArrayPool largeArrayPool; + + private readonly int maxArraysPerBucketNormalPool; + + private readonly int maxArraysPerBucketLargePool; /// /// Initializes a new instance of the class. @@ -57,14 +57,28 @@ namespace SixLabors.ImageSharp.Memory /// Initializes a new instance of the class. /// /// The maximum size of pooled arrays. Arrays over the thershold are gonna be always allocated. - /// The threshold to pool arrays in which has less buckets for memory safety. - public ArrayPoolMemoryManager(int maxPoolSizeInBytes, int largeBufferThresholdInBytes) + /// Arrays over this threshold will be pooled in which has less buckets for memory safety. + public ArrayPoolMemoryManager(int maxPoolSizeInBytes, int poolSelectorThresholdInBytes) + : this(maxPoolSizeInBytes, poolSelectorThresholdInBytes, 8, 24) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The maximum size of pooled arrays. Arrays over the thershold are gonna be always allocated. + /// The threshold to pool arrays in which has less buckets for memory safety. + /// Max arrays per bucket for the large array pool + /// Max arrays per bucket for the normal array pool + public ArrayPoolMemoryManager(int maxPoolSizeInBytes, int poolSelectorThresholdInBytes, int maxArraysPerBucketLargePool, int maxArraysPerBucketNormalPool) { Guard.MustBeGreaterThan(maxPoolSizeInBytes, 0, nameof(maxPoolSizeInBytes)); - Guard.MustBeLessThanOrEqualTo(largeBufferThresholdInBytes, maxPoolSizeInBytes, nameof(largeBufferThresholdInBytes)); + Guard.MustBeLessThanOrEqualTo(poolSelectorThresholdInBytes, maxPoolSizeInBytes, nameof(poolSelectorThresholdInBytes)); this.MaxPoolSizeInBytes = maxPoolSizeInBytes; - this.LargeBufferThresholdInBytes = largeBufferThresholdInBytes; + this.PoolSelectorThresholdInBytes = poolSelectorThresholdInBytes; + this.maxArraysPerBucketLargePool = maxArraysPerBucketLargePool; + this.maxArraysPerBucketNormalPool = maxArraysPerBucketNormalPool; this.InitArrayPools(); } @@ -77,7 +91,7 @@ namespace SixLabors.ImageSharp.Memory /// /// Gets the threshold to pool arrays in which has less buckets for memory safety. /// - public int LargeBufferThresholdInBytes { get; } + public int PoolSelectorThresholdInBytes { get; } /// public override void ReleaseRetainedResources() @@ -124,13 +138,13 @@ namespace SixLabors.ImageSharp.Memory private ArrayPool GetArrayPool(int bufferSizeInBytes) { - return bufferSizeInBytes <= this.LargeBufferThresholdInBytes ? this.normalArrayPool : this.largeArrayPool; + return bufferSizeInBytes <= this.PoolSelectorThresholdInBytes ? this.normalArrayPool : this.largeArrayPool; } private void InitArrayPools() { - this.largeArrayPool = ArrayPool.Create(this.MaxPoolSizeInBytes, 8); - this.normalArrayPool = ArrayPool.Create(this.LargeBufferThresholdInBytes, 24); + this.largeArrayPool = ArrayPool.Create(this.MaxPoolSizeInBytes, this.maxArraysPerBucketLargePool); + this.normalArrayPool = ArrayPool.Create(this.PoolSelectorThresholdInBytes, this.maxArraysPerBucketNormalPool); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Memory/ArrayPoolMemoryManagerTests.cs b/tests/ImageSharp.Tests/Memory/ArrayPoolMemoryManagerTests.cs index 0bd243fda2..581e0b78d9 100644 --- a/tests/ImageSharp.Tests/Memory/ArrayPoolMemoryManagerTests.cs +++ b/tests/ImageSharp.Tests/Memory/ArrayPoolMemoryManagerTests.cs @@ -17,9 +17,9 @@ namespace SixLabors.ImageSharp.Tests.Memory { private const int MaxPooledBufferSizeInBytes = 2048; - private const int LargeBufferThresholdInBytes = MaxPooledBufferSizeInBytes / 2; + private const int PoolSelectorThresholdInBytes = MaxPooledBufferSizeInBytes / 2; - private MemoryManager MemoryManager { get; } = new ArrayPoolMemoryManager(MaxPooledBufferSizeInBytes, LargeBufferThresholdInBytes); + private MemoryManager MemoryManager { get; } = new ArrayPoolMemoryManager(MaxPooledBufferSizeInBytes, PoolSelectorThresholdInBytes); /// /// Rent a buffer -> return it -> re-rent -> verify if it's span points to the previous location @@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Tests.Memory public class BufferTests : BufferTestSuite { public BufferTests() - : base(new ArrayPoolMemoryManager(MaxPooledBufferSizeInBytes, LargeBufferThresholdInBytes)) + : base(new ArrayPoolMemoryManager(MaxPooledBufferSizeInBytes, PoolSelectorThresholdInBytes)) { } } @@ -53,19 +53,19 @@ namespace SixLabors.ImageSharp.Tests.Memory { var mgr = new ArrayPoolMemoryManager(1111, 666); Assert.Equal(1111, mgr.MaxPoolSizeInBytes); - Assert.Equal(666, mgr.LargeBufferThresholdInBytes); + Assert.Equal(666, mgr.PoolSelectorThresholdInBytes); } [Fact] - public void WhenPassedOnly_MaxPooledBufferSizeInBytes_SmallerThresholdIsAutoCalculated() + public void WhenPassedOnly_MaxPooledBufferSizeInBytes_SmallerThresholdValueIsAutoCalculated() { var mgr = new ArrayPoolMemoryManager(5000); Assert.Equal(5000, mgr.MaxPoolSizeInBytes); - Assert.True(mgr.LargeBufferThresholdInBytes < mgr.MaxPoolSizeInBytes); + Assert.True(mgr.PoolSelectorThresholdInBytes < mgr.MaxPoolSizeInBytes); } [Fact] - public void When_LargeBufferThresholdInBytes_IsGreaterThan_MaxPooledBufferSizeInBytes_Throws() + public void When_PoolSelectorThresholdInBytes_IsGreaterThan_MaxPooledBufferSizeInBytes_ExceptionIsThrown() { Assert.ThrowsAny(() => { new ArrayPoolMemoryManager(100, 200); }); } @@ -151,7 +151,7 @@ namespace SixLabors.ImageSharp.Tests.Memory [Fact] public void AllocationOverLargeArrayThreshold_UsesDifferentPool() { - int arrayLengthThreshold = LargeBufferThresholdInBytes / sizeof(int); + int arrayLengthThreshold = PoolSelectorThresholdInBytes / sizeof(int); IBuffer small = this.MemoryManager.Allocate(arrayLengthThreshold - 1); ref int ptr2Small = ref small.DangerousGetPinnableReference(); From 67b1a25728182c74d5bf937f235852f609d02bc1 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Wed, 21 Feb 2018 00:32:09 +0100 Subject: [PATCH 078/151] ArrayPoolMemoryManager factory methods --- src/ImageSharp/Configuration.cs | 2 +- ...yPoolMemoryManager.CommonFactoryMethods.cs | 46 +++++++++++++++++++ .../Memory/ArrayPoolMemoryManager.cs | 13 +----- .../Memory/ArrayPoolMemoryManagerTests.cs | 28 ++++++++++- 4 files changed, 75 insertions(+), 14 deletions(-) create mode 100644 src/ImageSharp/Memory/ArrayPoolMemoryManager.CommonFactoryMethods.cs diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index bd6c11235f..2fe3c26e27 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -86,7 +86,7 @@ namespace SixLabors.ImageSharp /// /// Gets or sets the that is currently in use. /// - public MemoryManager MemoryManager { get; set; } = new ArrayPoolMemoryManager(); + public MemoryManager MemoryManager { get; set; } = ArrayPoolMemoryManager.CreateWithNormalPooling(); /// /// Gets the maximum header size of all the formats. diff --git a/src/ImageSharp/Memory/ArrayPoolMemoryManager.CommonFactoryMethods.cs b/src/ImageSharp/Memory/ArrayPoolMemoryManager.CommonFactoryMethods.cs new file mode 100644 index 0000000000..918c5d41af --- /dev/null +++ b/src/ImageSharp/Memory/ArrayPoolMemoryManager.CommonFactoryMethods.cs @@ -0,0 +1,46 @@ +namespace SixLabors.ImageSharp.Memory +{ + /// + /// Contains common factory methods and configuration constants. + /// + public partial class ArrayPoolMemoryManager + { + /// + /// The default value for: maximum size of pooled arrays in bytes. + /// Currently set to 32MB, which is equivalent to 8 megapixels of raw data. + /// + internal const int DefaultMaxPooledBufferSizeInBytes = 32 * 1024 * 1024; + + /// + /// The value for: The threshold to pool arrays in which has less buckets for memory safety. + /// + private const int DefaultBufferSelectorThresholdInBytes = 8 * 1024 * 1024; + + /// + /// This is the default. Should be good for most use cases. + /// + /// The memory manager + public static ArrayPoolMemoryManager CreateWithNormalPooling() + { + return new ArrayPoolMemoryManager(DefaultMaxPooledBufferSizeInBytes, DefaultBufferSelectorThresholdInBytes, 8, 24); + } + + /// + /// For environments with limited memory capabilities. Only small images are pooled, which can result in reduced througput. + /// + /// The memory manager + public static ArrayPoolMemoryManager CreateWithModeratePooling() + { + return new ArrayPoolMemoryManager(1024 * 1024, 1024 * 16, 16, 24); + } + + /// + /// RAM is not an issue for me, gimme maximum througput! + /// + /// The memory manager + public static ArrayPoolMemoryManager CreateWithAggressivePooling() + { + return new ArrayPoolMemoryManager(128 * 1024 * 1024, 32 * 1024 * 1024, 16, 32); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs b/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs index 7c2240a574..36bc3a8703 100644 --- a/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs +++ b/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs @@ -11,17 +11,6 @@ namespace SixLabors.ImageSharp.Memory /// public partial class ArrayPoolMemoryManager : MemoryManager { - /// - /// The default value for: maximum size of pooled arrays in bytes. - /// Currently set to 32MB, which is equivalent to 8 megapixels of raw data. - /// - internal const int DefaultMaxPooledBufferSizeInBytes = 32 * 1024 * 1024; - - /// - /// The value for: The threshold to pool arrays in which has less buckets for memory safety. - /// - private const int DefaultLargeBufferThresholdInBytes = 8 * 1024 * 1024; - /// /// The for small-to-medium buffers which is not kept clean. /// @@ -40,7 +29,7 @@ namespace SixLabors.ImageSharp.Memory /// Initializes a new instance of the class. /// public ArrayPoolMemoryManager() - : this(DefaultMaxPooledBufferSizeInBytes, DefaultLargeBufferThresholdInBytes) + : this(DefaultMaxPooledBufferSizeInBytes, DefaultBufferSelectorThresholdInBytes) { } diff --git a/tests/ImageSharp.Tests/Memory/ArrayPoolMemoryManagerTests.cs b/tests/ImageSharp.Tests/Memory/ArrayPoolMemoryManagerTests.cs index 581e0b78d9..f99ee4dded 100644 --- a/tests/ImageSharp.Tests/Memory/ArrayPoolMemoryManagerTests.cs +++ b/tests/ImageSharp.Tests/Memory/ArrayPoolMemoryManagerTests.cs @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Tests.Memory private const int PoolSelectorThresholdInBytes = MaxPooledBufferSizeInBytes / 2; - private MemoryManager MemoryManager { get; } = new ArrayPoolMemoryManager(MaxPooledBufferSizeInBytes, PoolSelectorThresholdInBytes); + private MemoryManager MemoryManager { get; set; } = new ArrayPoolMemoryManager(MaxPooledBufferSizeInBytes, PoolSelectorThresholdInBytes); /// /// Rent a buffer -> return it -> re-rent -> verify if it's span points to the previous location @@ -161,5 +161,31 @@ namespace SixLabors.ImageSharp.Tests.Memory Assert.False(Unsafe.AreSame(ref ptr2Small, ref large.DangerousGetPinnableReference())); } + + [Fact] + public void CreateWithAggressivePooling() + { + this.MemoryManager = ArrayPoolMemoryManager.CreateWithAggressivePooling(); + + Assert.True(this.CheckIsRentingPooledBuffer(4096 * 4096)); + } + + [Fact] + public void CreateWithNormalPooling() + { + this.MemoryManager = ArrayPoolMemoryManager.CreateWithNormalPooling(); + + Assert.False(this.CheckIsRentingPooledBuffer(2 * 4096 * 4096)); + Assert.True(this.CheckIsRentingPooledBuffer(2048 * 2048)); + } + + [Fact] + public void CreateWithModeratePooling() + { + this.MemoryManager = ArrayPoolMemoryManager.CreateWithModeratePooling(); + + Assert.False(this.CheckIsRentingPooledBuffer(2048 * 2048)); + Assert.True(this.CheckIsRentingPooledBuffer(1024 * 16)); + } } } \ No newline at end of file From bcb9c0057a1d2b06bb707d4135261fed7268c490 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Wed, 21 Feb 2018 01:06:59 +0100 Subject: [PATCH 079/151] passing MemoryManager to pixel blenders --- .../Brushes/ImageBrush{TPixel}.cs | 2 +- .../Brushes/PatternBrush{TPixel}.cs | 8 +- .../Brushes/Processors/BrushApplicator.cs | 8 +- .../Brushes/RecolorBrush{TPixel}.cs | 8 +- .../Brushes/SolidBrush{TPixel}.cs | 7 +- .../Processors/DrawImageProcessor.cs | 7 +- src/ImageSharp/ApplyProcessors.cs | 1 - .../DefaultInternalImageProcessorContext.cs | 2 +- .../IImageProcessingContext{TPixel}.cs | 13 +- .../DefaultPixelBlenders.Generated.cs | 126 +++++++++--------- .../DefaultPixelBlenders.Generated.tt | 6 +- .../PixelFormats/PixelBlender{TPixel}.cs | 4 +- .../Processing/ColorMatrix/Lomograph.cs | 4 +- .../Processing/ColorMatrix/Polaroid.cs | 4 +- .../Processing/Effects/BackgroundColor.cs | 4 +- src/ImageSharp/Processing/Overlays/Glow.cs | 4 +- .../Processing/Overlays/Vignette.cs | 4 +- .../Effects/BackgroundColorProcessor.cs | 2 +- .../Processors/Overlays/GlowProcessor.cs | 2 +- .../Processors/Overlays/VignetteProcessor.cs | 2 +- .../Processing/Transforms/Resize.cs | 4 +- src/ImageSharp/Quantizers/Quantize.cs | 2 +- .../FakeImageOperationsProvider.cs | 2 +- .../PorterDuffFunctionsTests_TPixel.cs | 22 +-- 24 files changed, 132 insertions(+), 116 deletions(-) diff --git a/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs index 6b3ce36fe1..320c94c96b 100644 --- a/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs @@ -142,7 +142,7 @@ namespace SixLabors.ImageSharp.Drawing.Brushes } Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); - this.Blender.Blend(destinationRow, destinationRow, overlaySpan, amountSpan); + this.Blender.Blend(this.source.MemoryManager, destinationRow, destinationRow, overlaySpan, amountSpan); } } } diff --git a/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs index 449a23da9f..cc22b26391 100644 --- a/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs @@ -152,8 +152,10 @@ namespace SixLabors.ImageSharp.Drawing.Brushes internal override void Apply(Span scanline, int x, int y) { int patternY = y % this.pattern.Height; - using (IBuffer amountBuffer = this.Target.MemoryManager.Allocate(scanline.Length)) - using (IBuffer overlay = this.Target.MemoryManager.Allocate(scanline.Length)) + MemoryManager memoryManager = this.Target.MemoryManager; + + using (IBuffer amountBuffer = memoryManager.Allocate(scanline.Length)) + using (IBuffer overlay = memoryManager.Allocate(scanline.Length)) { Span amountSpan = amountBuffer.Span; Span overlaySpan = overlay.Span; @@ -167,7 +169,7 @@ namespace SixLabors.ImageSharp.Drawing.Brushes } Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); - this.Blender.Blend(destinationRow, destinationRow, overlaySpan, amountSpan); + this.Blender.Blend(memoryManager, destinationRow, destinationRow, overlaySpan, amountSpan); } } } diff --git a/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs b/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs index 68bc13ecb5..d8ea435586 100644 --- a/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs +++ b/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs @@ -65,8 +65,10 @@ namespace SixLabors.ImageSharp.Drawing.Brushes.Processors /// scanlineBuffer will be > scanlineWidth but provide and offset in case we want to share a larger buffer across runs. internal virtual void Apply(Span scanline, int x, int y) { - using (IBuffer amountBuffer = this.Target.MemoryManager.Allocate(scanline.Length)) - using (IBuffer overlay = this.Target.MemoryManager.Allocate(scanline.Length)) + MemoryManager memoryManager = this.Target.MemoryManager; + + using (IBuffer amountBuffer = memoryManager.Allocate(scanline.Length)) + using (IBuffer overlay = memoryManager.Allocate(scanline.Length)) { Span amountSpan = amountBuffer.Span; Span overlaySpan = overlay.Span; @@ -82,7 +84,7 @@ namespace SixLabors.ImageSharp.Drawing.Brushes.Processors } Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); - this.Blender.Blend(destinationRow, destinationRow, overlaySpan, amountSpan); + this.Blender.Blend(memoryManager, destinationRow, destinationRow, overlaySpan, amountSpan); } } } diff --git a/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs index 37cba42c9e..39afd965c8 100644 --- a/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs @@ -144,8 +144,10 @@ namespace SixLabors.ImageSharp.Drawing.Brushes /// internal override void Apply(Span scanline, int x, int y) { - using (IBuffer amountBuffer = this.Target.MemoryManager.Allocate(scanline.Length)) - using (IBuffer overlay = this.Target.MemoryManager.Allocate(scanline.Length)) + MemoryManager memoryManager = this.Target.MemoryManager; + + using (IBuffer amountBuffer = memoryManager.Allocate(scanline.Length)) + using (IBuffer overlay = memoryManager.Allocate(scanline.Length)) { Span amountSpan = amountBuffer.Span; Span overlaySpan = overlay.Span; @@ -162,7 +164,7 @@ namespace SixLabors.ImageSharp.Drawing.Brushes } Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); - this.Blender.Blend(destinationRow, destinationRow, overlaySpan, amountSpan); + this.Blender.Blend(memoryManager, destinationRow, destinationRow, overlaySpan, amountSpan); } } } diff --git a/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs index 90286cb6c9..6928895565 100644 --- a/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs @@ -93,7 +93,9 @@ namespace SixLabors.ImageSharp.Drawing.Brushes { Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); - using (IBuffer amountBuffer = this.Target.MemoryManager.Allocate(scanline.Length)) + MemoryManager memoryManager = this.Target.MemoryManager; + + using (IBuffer amountBuffer = memoryManager.Allocate(scanline.Length)) { Span amountSpan = amountBuffer.Span; @@ -102,11 +104,12 @@ namespace SixLabors.ImageSharp.Drawing.Brushes amountSpan[i] = scanline[i] * this.Options.BlendPercentage; } - this.Blender.Blend(destinationRow, destinationRow, this.Colors.Span, amountSpan); + this.Blender.Blend(memoryManager, destinationRow, destinationRow, this.Colors.Span, amountSpan); } } catch (Exception) { + // TODO: Why are we catching exceptions here silently ??? throw; } } diff --git a/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs b/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs index 21e13d47af..54fb38ee9f 100644 --- a/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs @@ -84,7 +84,10 @@ namespace SixLabors.ImageSharp.Drawing.Processors maxY = Math.Min(this.Location.Y + this.Size.Height, maxY); int width = maxX - minX; - using (IBuffer amount = this.Image.GetConfiguration().MemoryManager.Allocate(width)) + + MemoryManager memoryManager = this.Image.GetConfiguration().MemoryManager; + + using (IBuffer amount = memoryManager.Allocate(width)) { amount.Span.Fill(this.Alpha); @@ -96,7 +99,7 @@ namespace SixLabors.ImageSharp.Drawing.Processors { Span background = source.GetPixelRowSpan(y).Slice(minX, width); Span foreground = targetImage.GetPixelRowSpan(y - this.Location.Y).Slice(targetX, width); - this.blender.Blend(background, background, foreground, amount.Span); + this.blender.Blend(memoryManager, background, background, foreground, amount.Span); }); } } diff --git a/src/ImageSharp/ApplyProcessors.cs b/src/ImageSharp/ApplyProcessors.cs index 58a952c406..c4954ef0d1 100644 --- a/src/ImageSharp/ApplyProcessors.cs +++ b/src/ImageSharp/ApplyProcessors.cs @@ -4,7 +4,6 @@ using System; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; namespace SixLabors.ImageSharp { diff --git a/src/ImageSharp/DefaultInternalImageProcessorContext.cs b/src/ImageSharp/DefaultInternalImageProcessorContext.cs index ac56d02778..d58f6236f9 100644 --- a/src/ImageSharp/DefaultInternalImageProcessorContext.cs +++ b/src/ImageSharp/DefaultInternalImageProcessorContext.cs @@ -75,6 +75,6 @@ namespace SixLabors.ImageSharp return this.ApplyProcessor(processor, this.source.Bounds()); } - public MemoryManager GetMemoryManager() => this.source.GetConfiguration().MemoryManager; + public MemoryManager MemoryManager => this.source.GetConfiguration().MemoryManager; } } \ No newline at end of file diff --git a/src/ImageSharp/IImageProcessingContext{TPixel}.cs b/src/ImageSharp/IImageProcessingContext{TPixel}.cs index 68b0a030a5..b73337905a 100644 --- a/src/ImageSharp/IImageProcessingContext{TPixel}.cs +++ b/src/ImageSharp/IImageProcessingContext{TPixel}.cs @@ -15,6 +15,12 @@ namespace SixLabors.ImageSharp public interface IImageProcessingContext where TPixel : struct, IPixel { + /// + /// Gets a reference to the used to allocate buffers + /// for this context. + /// + MemoryManager MemoryManager { get; } + /// /// Adds the processor to the current set of image operations to be applied. /// @@ -29,13 +35,6 @@ namespace SixLabors.ImageSharp /// The processor to apply /// The current operations class to allow chaining of operations. IImageProcessingContext ApplyProcessor(IImageProcessor processor); - - /// - /// Returns a reference to the used to allocate buffers - /// for this context. - /// - /// A to use for buffer allocations. - MemoryManager GetMemoryManager(); } /// diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs index 17f75898db..d3c6cf16ce 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs @@ -26,7 +26,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders internal class Normal : PixelBlender { - /// /// Gets the static instance of this blender. /// @@ -39,13 +38,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } /// - public override void Blend(Span destination, Span background, Span source, Span amount) + public override void Blend(MemoryManager memoryManager, Span destination, Span background, Span source, Span amount) { Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3, false)) + using (IBuffer buffer = memoryManager.Allocate(destination.Length * 3, false)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -63,9 +62,9 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } } + internal class Multiply : PixelBlender { - /// /// Gets the static instance of this blender. /// @@ -78,13 +77,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } /// - public override void Blend(Span destination, Span background, Span source, Span amount) + public override void Blend(MemoryManager memoryManager, Span destination, Span background, Span source, Span amount) { Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3, false)) + using (IBuffer buffer = memoryManager.Allocate(destination.Length * 3, false)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -102,9 +101,9 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } } + internal class Add : PixelBlender { - /// /// Gets the static instance of this blender. /// @@ -117,13 +116,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } /// - public override void Blend(Span destination, Span background, Span source, Span amount) + public override void Blend(MemoryManager memoryManager, Span destination, Span background, Span source, Span amount) { Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3, false)) + using (IBuffer buffer = memoryManager.Allocate(destination.Length * 3, false)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -141,9 +140,9 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } } + internal class Substract : PixelBlender { - /// /// Gets the static instance of this blender. /// @@ -156,13 +155,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } /// - public override void Blend(Span destination, Span background, Span source, Span amount) + public override void Blend(MemoryManager memoryManager, Span destination, Span background, Span source, Span amount) { Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3, false)) + using (IBuffer buffer = memoryManager.Allocate(destination.Length * 3, false)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -180,9 +179,9 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } } + internal class Screen : PixelBlender { - /// /// Gets the static instance of this blender. /// @@ -195,13 +194,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } /// - public override void Blend(Span destination, Span background, Span source, Span amount) + public override void Blend(MemoryManager memoryManager, Span destination, Span background, Span source, Span amount) { Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3, false)) + using (IBuffer buffer = memoryManager.Allocate(destination.Length * 3, false)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -219,9 +218,9 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } } + internal class Darken : PixelBlender { - /// /// Gets the static instance of this blender. /// @@ -234,13 +233,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } /// - public override void Blend(Span destination, Span background, Span source, Span amount) + public override void Blend(MemoryManager memoryManager, Span destination, Span background, Span source, Span amount) { Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3, false)) + using (IBuffer buffer = memoryManager.Allocate(destination.Length * 3, false)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -258,9 +257,9 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } } + internal class Lighten : PixelBlender { - /// /// Gets the static instance of this blender. /// @@ -273,13 +272,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } /// - public override void Blend(Span destination, Span background, Span source, Span amount) + public override void Blend(MemoryManager memoryManager, Span destination, Span background, Span source, Span amount) { Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3, false)) + using (IBuffer buffer = memoryManager.Allocate(destination.Length * 3, false)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -297,9 +296,9 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } } + internal class Overlay : PixelBlender { - /// /// Gets the static instance of this blender. /// @@ -312,13 +311,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } /// - public override void Blend(Span destination, Span background, Span source, Span amount) + public override void Blend(MemoryManager memoryManager, Span destination, Span background, Span source, Span amount) { Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3, false)) + using (IBuffer buffer = memoryManager.Allocate(destination.Length * 3, false)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -336,9 +335,9 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } } + internal class HardLight : PixelBlender { - /// /// Gets the static instance of this blender. /// @@ -351,13 +350,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } /// - public override void Blend(Span destination, Span background, Span source, Span amount) + public override void Blend(MemoryManager memoryManager, Span destination, Span background, Span source, Span amount) { Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3, false)) + using (IBuffer buffer = memoryManager.Allocate(destination.Length * 3, false)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -375,9 +374,9 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } } + internal class Src : PixelBlender { - /// /// Gets the static instance of this blender. /// @@ -390,13 +389,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } /// - public override void Blend(Span destination, Span background, Span source, Span amount) + public override void Blend(MemoryManager memoryManager, Span destination, Span background, Span source, Span amount) { Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3, false)) + using (IBuffer buffer = memoryManager.Allocate(destination.Length * 3, false)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -414,9 +413,9 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } } + internal class Atop : PixelBlender { - /// /// Gets the static instance of this blender. /// @@ -429,13 +428,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } /// - public override void Blend(Span destination, Span background, Span source, Span amount) + public override void Blend(MemoryManager memoryManager, Span destination, Span background, Span source, Span amount) { Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3, false)) + using (IBuffer buffer = memoryManager.Allocate(destination.Length * 3, false)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -453,9 +452,9 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } } + internal class Over : PixelBlender { - /// /// Gets the static instance of this blender. /// @@ -468,13 +467,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } /// - public override void Blend(Span destination, Span background, Span source, Span amount) + public override void Blend(MemoryManager memoryManager, Span destination, Span background, Span source, Span amount) { Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3, false)) + using (IBuffer buffer = memoryManager.Allocate(destination.Length * 3, false)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -492,9 +491,9 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } } + internal class In : PixelBlender { - /// /// Gets the static instance of this blender. /// @@ -507,13 +506,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } /// - public override void Blend(Span destination, Span background, Span source, Span amount) + public override void Blend(MemoryManager memoryManager, Span destination, Span background, Span source, Span amount) { Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3, false)) + using (IBuffer buffer = memoryManager.Allocate(destination.Length * 3, false)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -531,9 +530,9 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } } + internal class Out : PixelBlender { - /// /// Gets the static instance of this blender. /// @@ -546,13 +545,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } /// - public override void Blend(Span destination, Span background, Span source, Span amount) + public override void Blend(MemoryManager memoryManager, Span destination, Span background, Span source, Span amount) { Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3, false)) + using (IBuffer buffer = memoryManager.Allocate(destination.Length * 3, false)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -570,9 +569,9 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } } + internal class Dest : PixelBlender { - /// /// Gets the static instance of this blender. /// @@ -585,13 +584,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } /// - public override void Blend(Span destination, Span background, Span source, Span amount) + public override void Blend(MemoryManager memoryManager, Span destination, Span background, Span source, Span amount) { Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3, false)) + using (IBuffer buffer = memoryManager.Allocate(destination.Length * 3, false)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -609,9 +608,9 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } } + internal class DestAtop : PixelBlender { - /// /// Gets the static instance of this blender. /// @@ -624,13 +623,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } /// - public override void Blend(Span destination, Span background, Span source, Span amount) + public override void Blend(MemoryManager memoryManager, Span destination, Span background, Span source, Span amount) { Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3, false)) + using (IBuffer buffer = memoryManager.Allocate(destination.Length * 3, false)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -648,9 +647,9 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } } + internal class DestOver : PixelBlender { - /// /// Gets the static instance of this blender. /// @@ -663,13 +662,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } /// - public override void Blend(Span destination, Span background, Span source, Span amount) + public override void Blend(MemoryManager memoryManager, Span destination, Span background, Span source, Span amount) { Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3, false)) + using (IBuffer buffer = memoryManager.Allocate(destination.Length * 3, false)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -687,9 +686,9 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } } + internal class DestIn : PixelBlender { - /// /// Gets the static instance of this blender. /// @@ -702,13 +701,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } /// - public override void Blend(Span destination, Span background, Span source, Span amount) + public override void Blend(MemoryManager memoryManager, Span destination, Span background, Span source, Span amount) { Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3, false)) + using (IBuffer buffer = memoryManager.Allocate(destination.Length * 3, false)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -726,9 +725,9 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } } + internal class DestOut : PixelBlender { - /// /// Gets the static instance of this blender. /// @@ -741,13 +740,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } /// - public override void Blend(Span destination, Span background, Span source, Span amount) + public override void Blend(MemoryManager memoryManager, Span destination, Span background, Span source, Span amount) { Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3, false)) + using (IBuffer buffer = memoryManager.Allocate(destination.Length * 3, false)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -765,9 +764,9 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } } + internal class Clear : PixelBlender { - /// /// Gets the static instance of this blender. /// @@ -780,13 +779,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } /// - public override void Blend(Span destination, Span background, Span source, Span amount) + public override void Blend(MemoryManager memoryManager, Span destination, Span background, Span source, Span amount) { Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3, false)) + using (IBuffer buffer = memoryManager.Allocate(destination.Length * 3, false)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -804,9 +803,9 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } } + internal class Xor : PixelBlender { - /// /// Gets the static instance of this blender. /// @@ -819,13 +818,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } /// - public override void Blend(Span destination, Span background, Span source, Span amount) + public override void Blend(MemoryManager memoryManager, Span destination, Span background, Span source, Span amount) { Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3, false)) + using (IBuffer buffer = memoryManager.Allocate(destination.Length * 3, false)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -843,5 +842,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } } + } } \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt index 75c6b2d81c..eebee676fc 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt @@ -68,7 +68,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders #> internal class <#=blender#> : PixelBlender { - /// /// Gets the static instance of this blender. /// @@ -81,13 +80,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } /// - public override void Blend(Span destination, Span background, Span source, Span amount) + public override void Blend(MemoryManager memoryManager, Span destination, Span background, Span source, Span amount) { Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (IBuffer buffer = Configuration.Default.MemoryManager.Allocate(destination.Length * 3, false)) + using (IBuffer buffer = memoryManager.Allocate(destination.Length * 3, false)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -105,6 +104,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } } + <# } diff --git a/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs index 54cb09c28a..666fb38913 100644 --- a/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.PixelFormats { @@ -27,6 +28,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// Blend 2 pixels together. /// + /// The /// The destination span. /// The background span. /// The source span. @@ -34,6 +36,6 @@ namespace SixLabors.ImageSharp.PixelFormats /// A value between 0 and 1 indicating the weight of the second source vector. /// At amount = 0, "from" is returned, at amount = 1, "to" is returned. /// - public abstract void Blend(Span destination, Span background, Span source, Span amount); + public abstract void Blend(MemoryManager memoryManager, Span destination, Span background, Span source, Span amount); } } diff --git a/src/ImageSharp/Processing/ColorMatrix/Lomograph.cs b/src/ImageSharp/Processing/ColorMatrix/Lomograph.cs index 96231e168b..ca79841c76 100644 --- a/src/ImageSharp/Processing/ColorMatrix/Lomograph.cs +++ b/src/ImageSharp/Processing/ColorMatrix/Lomograph.cs @@ -51,7 +51,7 @@ namespace SixLabors.ImageSharp public static IImageProcessingContext Lomograph(this IImageProcessingContext source, GraphicsOptions options) where TPixel : struct, IPixel { - source.ApplyProcessor(new LomographProcessor(source.GetMemoryManager(), options)); + source.ApplyProcessor(new LomographProcessor(source.MemoryManager, options)); return source; } @@ -68,7 +68,7 @@ namespace SixLabors.ImageSharp public static IImageProcessingContext Lomograph(this IImageProcessingContext source, Rectangle rectangle, GraphicsOptions options) where TPixel : struct, IPixel { - source.ApplyProcessor(new LomographProcessor(source.GetMemoryManager(), options), rectangle); + source.ApplyProcessor(new LomographProcessor(source.MemoryManager, options), rectangle); return source; } } diff --git a/src/ImageSharp/Processing/ColorMatrix/Polaroid.cs b/src/ImageSharp/Processing/ColorMatrix/Polaroid.cs index 7cc0e2419f..d12cc76122 100644 --- a/src/ImageSharp/Processing/ColorMatrix/Polaroid.cs +++ b/src/ImageSharp/Processing/ColorMatrix/Polaroid.cs @@ -51,7 +51,7 @@ namespace SixLabors.ImageSharp public static IImageProcessingContext Polaroid(this IImageProcessingContext source, GraphicsOptions options) where TPixel : struct, IPixel { - source.ApplyProcessor(new PolaroidProcessor(source.GetMemoryManager(), options)); + source.ApplyProcessor(new PolaroidProcessor(source.MemoryManager, options)); return source; } @@ -68,7 +68,7 @@ namespace SixLabors.ImageSharp public static IImageProcessingContext Polaroid(this IImageProcessingContext source, Rectangle rectangle, GraphicsOptions options) where TPixel : struct, IPixel { - source.ApplyProcessor(new PolaroidProcessor(source.GetMemoryManager(), options), rectangle); + source.ApplyProcessor(new PolaroidProcessor(source.MemoryManager, options), rectangle); return source; } } diff --git a/src/ImageSharp/Processing/Effects/BackgroundColor.cs b/src/ImageSharp/Processing/Effects/BackgroundColor.cs index 18caa67cca..22aad9ca6a 100644 --- a/src/ImageSharp/Processing/Effects/BackgroundColor.cs +++ b/src/ImageSharp/Processing/Effects/BackgroundColor.cs @@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp /// The . public static IImageProcessingContext BackgroundColor(this IImageProcessingContext source, TPixel color, GraphicsOptions options) where TPixel : struct, IPixel - => source.ApplyProcessor(new BackgroundColorProcessor(source.GetMemoryManager(), color, options)); + => source.ApplyProcessor(new BackgroundColorProcessor(source.MemoryManager, color, options)); /// /// Replaces the background color of image with the given one. @@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp /// The . public static IImageProcessingContext BackgroundColor(this IImageProcessingContext source, TPixel color, Rectangle rectangle, GraphicsOptions options) where TPixel : struct, IPixel - => source.ApplyProcessor(new BackgroundColorProcessor(source.GetMemoryManager(), color, options), rectangle); + => source.ApplyProcessor(new BackgroundColorProcessor(source.MemoryManager, color, options), rectangle); /// /// Replaces the background color of image with the given one. diff --git a/src/ImageSharp/Processing/Overlays/Glow.cs b/src/ImageSharp/Processing/Overlays/Glow.cs index a8994b18a1..0c3552b4d2 100644 --- a/src/ImageSharp/Processing/Overlays/Glow.cs +++ b/src/ImageSharp/Processing/Overlays/Glow.cs @@ -157,7 +157,7 @@ namespace SixLabors.ImageSharp /// The . private static IImageProcessingContext Glow(this IImageProcessingContext source, TPixel color, ValueSize radius, Rectangle rectangle, GraphicsOptions options) where TPixel : struct, IPixel - => source.ApplyProcessor(new GlowProcessor(source.GetMemoryManager(), color, radius, options), rectangle); + => source.ApplyProcessor(new GlowProcessor(source.MemoryManager, color, radius, options), rectangle); /// /// Applies a radial glow effect to an image. @@ -170,6 +170,6 @@ namespace SixLabors.ImageSharp /// The . private static IImageProcessingContext Glow(this IImageProcessingContext source, TPixel color, ValueSize radius, GraphicsOptions options) where TPixel : struct, IPixel - => source.ApplyProcessor(new GlowProcessor(source.GetMemoryManager(), color, radius, options)); + => source.ApplyProcessor(new GlowProcessor(source.MemoryManager, color, radius, options)); } } diff --git a/src/ImageSharp/Processing/Overlays/Vignette.cs b/src/ImageSharp/Processing/Overlays/Vignette.cs index 3a691ed00d..4b9f2f866c 100644 --- a/src/ImageSharp/Processing/Overlays/Vignette.cs +++ b/src/ImageSharp/Processing/Overlays/Vignette.cs @@ -151,10 +151,10 @@ namespace SixLabors.ImageSharp private static IImageProcessingContext VignetteInternal(this IImageProcessingContext source, TPixel color, ValueSize radiusX, ValueSize radiusY, Rectangle rectangle, GraphicsOptions options) where TPixel : struct, IPixel - => source.ApplyProcessor(new VignetteProcessor(source.GetMemoryManager(), color, radiusX, radiusY, options), rectangle); + => source.ApplyProcessor(new VignetteProcessor(source.MemoryManager, color, radiusX, radiusY, options), rectangle); private static IImageProcessingContext VignetteInternal(this IImageProcessingContext source, TPixel color, ValueSize radiusX, ValueSize radiusY, GraphicsOptions options) where TPixel : struct, IPixel - => source.ApplyProcessor(new VignetteProcessor(source.GetMemoryManager(), color, radiusX, radiusY, options)); + => source.ApplyProcessor(new VignetteProcessor(source.MemoryManager, color, radiusX, radiusY, options)); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs index e97495a3db..720b876913 100644 --- a/src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs @@ -94,7 +94,7 @@ namespace SixLabors.ImageSharp.Processing.Processors Span destination = source.GetPixelRowSpan(y - startY).Slice(minX - startX, width); // This switched color & destination in the 2nd and 3rd places because we are applying the target colour under the current one - blender.Blend(destination, colors.Span, destination, amount.Span); + blender.Blend(this.memoryManager, destination, colors.Span, destination, amount.Span); }); } } diff --git a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs index 85c592ceaa..9ab301718c 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs @@ -116,7 +116,7 @@ namespace SixLabors.ImageSharp.Processing.Processors Span destination = source.GetPixelRowSpan(offsetY).Slice(offsetX, width); - this.blender.Blend(destination, destination, rowColors.Span, amountsSpan); + this.blender.Blend(this.memoryManager, destination, destination, rowColors.Span, amountsSpan); } }); } diff --git a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs index d0943b27bb..d47211f0cb 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs @@ -139,7 +139,7 @@ namespace SixLabors.ImageSharp.Processing.Processors Span destination = source.GetPixelRowSpan(offsetY).Slice(offsetX, width); - this.blender.Blend(destination, destination, rowColors.Span, amountsSpan); + this.blender.Blend(this.memoryManager, destination, destination, rowColors.Span, amountsSpan); } }); } diff --git a/src/ImageSharp/Processing/Transforms/Resize.cs b/src/ImageSharp/Processing/Transforms/Resize.cs index a3a62fa49f..18def03e66 100644 --- a/src/ImageSharp/Processing/Transforms/Resize.cs +++ b/src/ImageSharp/Processing/Transforms/Resize.cs @@ -193,7 +193,7 @@ namespace SixLabors.ImageSharp Guard.MustBeGreaterThan(width, 0, nameof(width)); Guard.MustBeGreaterThan(height, 0, nameof(height)); - img.Mutate(x => x.ApplyProcessor(new ResizeProcessor(source.GetMemoryManager(), sampler, width, height, targetRectangle) { Compand = compand }, sourceRectangle)); + img.Mutate(x => x.ApplyProcessor(new ResizeProcessor(source.MemoryManager, sampler, width, height, targetRectangle) { Compand = compand }, sourceRectangle)); }); } @@ -233,7 +233,7 @@ namespace SixLabors.ImageSharp Guard.MustBeGreaterThan(width, 0, nameof(width)); Guard.MustBeGreaterThan(height, 0, nameof(height)); - img.Mutate(x => x.ApplyProcessor(new ResizeProcessor(source.GetMemoryManager(), sampler, width, height, targetRectangle) { Compand = compand })); + img.Mutate(x => x.ApplyProcessor(new ResizeProcessor(source.MemoryManager, sampler, width, height, targetRectangle) { Compand = compand })); }); } } diff --git a/src/ImageSharp/Quantizers/Quantize.cs b/src/ImageSharp/Quantizers/Quantize.cs index 0e3d806dab..4052d46857 100644 --- a/src/ImageSharp/Quantizers/Quantize.cs +++ b/src/ImageSharp/Quantizers/Quantize.cs @@ -61,7 +61,7 @@ namespace SixLabors.ImageSharp QuantizedImage quantized = quantizer.Quantize(img.Frames.RootFrame, maxColors); int palleteCount = quantized.Palette.Length - 1; - using (var pixels = new PixelAccessor(source.GetMemoryManager(), quantized.Width, quantized.Height)) + using (var pixels = new PixelAccessor(source.MemoryManager, quantized.Width, quantized.Height)) { Parallel.For( 0, diff --git a/tests/ImageSharp.Tests/FakeImageOperationsProvider.cs b/tests/ImageSharp.Tests/FakeImageOperationsProvider.cs index a1c199b161..c2ed7238dd 100644 --- a/tests/ImageSharp.Tests/FakeImageOperationsProvider.cs +++ b/tests/ImageSharp.Tests/FakeImageOperationsProvider.cs @@ -88,7 +88,7 @@ namespace SixLabors.ImageSharp.Tests return this; } - public MemoryManager GetMemoryManager() => this.source.GetConfiguration().MemoryManager; + public MemoryManager MemoryManager => this.source.GetConfiguration().MemoryManager; public struct AppliedOpperation { diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs index b95f8fdf61..50babde69a 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs @@ -12,6 +12,8 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders { + using SixLabors.ImageSharp.Memory; + public class PorterDuffFunctionsTests_TPixel { private static Span AsSpan(T value) @@ -25,6 +27,8 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders { new TestPixel(1,1,1,1), new TestPixel(0,0,0,.8f), .5f, new TestPixel(0.6f, 0.6f, 0.6f, 1) }, }; + private MemoryManager MemoryManager { get; } = Configuration.Default.MemoryManager; + [Theory] [MemberData(nameof(NormalBlendFunctionData))] public void NormalBlendFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) @@ -49,7 +53,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders where TPixel : struct, IPixel { Span dest = new Span(new TPixel[1]); - new DefaultPixelBlenders.Normal().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + new DefaultPixelBlenders.Normal().Blend(this.MemoryManager, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected, dest[0], 2); } @@ -88,7 +92,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders where TPixel : struct, IPixel { Span dest = new Span(new TPixel[1]); - new DefaultPixelBlenders.Multiply().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + new DefaultPixelBlenders.Multiply().Blend(this.MemoryManager, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected, dest[0], 2); } @@ -127,7 +131,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders where TPixel : struct, IPixel { Span dest = new Span(new TPixel[1]); - new DefaultPixelBlenders.Add().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + new DefaultPixelBlenders.Add().Blend(this.MemoryManager, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected, dest[0], 2); } @@ -166,7 +170,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders where TPixel : struct, IPixel { Span dest = new Span(new TPixel[1]); - new DefaultPixelBlenders.Substract().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + new DefaultPixelBlenders.Substract().Blend(this.MemoryManager, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected, dest[0], 2); } @@ -205,7 +209,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders where TPixel : struct, IPixel { Span dest = new Span(new TPixel[1]); - new DefaultPixelBlenders.Screen().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + new DefaultPixelBlenders.Screen().Blend(this.MemoryManager, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected, dest[0], 2); } @@ -244,7 +248,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders where TPixel : struct, IPixel { Span dest = new Span(new TPixel[1]); - new DefaultPixelBlenders.Darken().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + new DefaultPixelBlenders.Darken().Blend(this.MemoryManager, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected, dest[0], 2); } @@ -283,7 +287,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders where TPixel : struct, IPixel { Span dest = new Span(new TPixel[1]); - new DefaultPixelBlenders.Lighten().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + new DefaultPixelBlenders.Lighten().Blend(this.MemoryManager, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected, dest[0], 2); } @@ -322,7 +326,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders where TPixel : struct, IPixel { Span dest = new Span(new TPixel[1]); - new DefaultPixelBlenders.Overlay().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + new DefaultPixelBlenders.Overlay().Blend(this.MemoryManager, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected, dest[0], 2); } @@ -361,7 +365,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders where TPixel : struct, IPixel { Span dest = new Span(new TPixel[1]); - new DefaultPixelBlenders.HardLight().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + new DefaultPixelBlenders.HardLight().Blend(this.MemoryManager, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected, dest[0], 2); } } From 1157dbe5be7a380cc08a3c3c6ed08e398d261886 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 22 Feb 2018 11:50:59 +1100 Subject: [PATCH 080/151] Remove Clone from DrawImage to reduce memory consumption --- src/ImageSharp.Drawing/DrawImage.cs | 25 ++++-- .../Processors/DrawImageProcessor.cs | 77 +++++++++---------- 2 files changed, 54 insertions(+), 48 deletions(-) diff --git a/src/ImageSharp.Drawing/DrawImage.cs b/src/ImageSharp.Drawing/DrawImage.cs index d55e224162..b84a1c16a4 100644 --- a/src/ImageSharp.Drawing/DrawImage.cs +++ b/src/ImageSharp.Drawing/DrawImage.cs @@ -16,7 +16,10 @@ namespace SixLabors.ImageSharp /// Draws the given image together with the current one by blending their pixels. /// /// The image this method extends. - /// The image to blend with the currently processing image. + /// + /// The image to blend with the currently processing image. + /// If the image dimensions do not match the given then the image will be resized to match. + /// /// The pixel format. /// The size to draw the blended image. /// The location to draw the blended image. @@ -45,7 +48,7 @@ namespace SixLabors.ImageSharp /// The pixel format. /// The image this method extends. /// The image to blend with the currently processing image. - /// The opacity of the image image to blend. Must be between 0 and 1. + /// The opacity of the image to blend. Must be between 0 and 1. /// The . public static IImageProcessingContext Blend(this IImageProcessingContext source, Image image, float percent) where TPixel : struct, IPixel @@ -62,7 +65,7 @@ namespace SixLabors.ImageSharp /// The image this method extends. /// The image to blend with the currently processing image. /// The blending mode. - /// The opacity of the image image to blend. Must be between 0 and 1. + /// The opacity of the image to blend. Must be between 0 and 1. /// The . public static IImageProcessingContext Blend(this IImageProcessingContext source, Image image, PixelBlenderMode blender, float percent) where TPixel : struct, IPixel @@ -79,7 +82,7 @@ namespace SixLabors.ImageSharp /// The pixel format. /// The image this method extends. /// The image to blend with the currently processing image. - /// The options, including the blending type and belnding amount. + /// The options, including the blending type and blending amount. /// The . public static IImageProcessingContext Blend(this IImageProcessingContext source, Image image, GraphicsOptions options) where TPixel : struct, IPixel @@ -91,9 +94,12 @@ namespace SixLabors.ImageSharp /// Draws the given image together with the current one by blending their pixels. /// /// The image this method extends. - /// The image to blend with the currently processing image. + /// + /// The image to blend with the currently processing image. + /// If the image dimensions do not match the given then the image will be resized to match. + /// /// The pixel format. - /// The opacity of the image image to blend. Must be between 0 and 1. + /// The opacity of the image to blend. Must be between 0 and 1. /// The size to draw the blended image. /// The location to draw the blended image. /// The . @@ -109,10 +115,13 @@ namespace SixLabors.ImageSharp /// Draws the given image together with the current one by blending their pixels. /// /// The image this method extends. - /// The image to blend with the currently processing image. + /// + /// The image to blend with the currently processing image. + /// If the image dimensions do not match the given then the image will be resized to match. + /// /// The pixel format. /// The type of bending to apply. - /// The opacity of the image image to blend. Must be between 0 and 1. + /// The opacity of the image to blend. Must be between 0 and 1. /// The size to draw the blended image. /// The location to draw the blended image. /// The . diff --git a/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs b/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs index 47763c0aaf..4bb12b9957 100644 --- a/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs @@ -24,15 +24,25 @@ namespace SixLabors.ImageSharp.Drawing.Processors /// /// Initializes a new instance of the class. /// - /// The image to blend with the currently processing image. + /// + /// The image to blend with the currently processing image. + /// If the image dimensions do not match the given then the image will be resized to match. + /// /// The size to draw the blended image. /// The location to draw the blended image. /// The opacity of the image to blend. Between 0 and 100. public DrawImageProcessor(Image image, Size size, Point location, GraphicsOptions options) { Guard.MustBeBetweenOrEqualTo(options.BlendPercentage, 0, 1, nameof(options.BlendPercentage)); - this.Image = image; + this.Size = size; + + if (image.Size() != size) + { + image.Mutate(x => x.Resize(size.Width, size.Height)); + } + + this.Image = image; this.Alpha = options.BlendPercentage; this.blender = PixelOperations.Instance.GetPixelBlender(options.BlenderMode); this.Location = location; @@ -61,51 +71,38 @@ namespace SixLabors.ImageSharp.Drawing.Processors /// protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - Image disposableImage = null; Image targetImage = this.Image; - try - { - if (targetImage.Size() != this.Size) - { - targetImage = disposableImage = this.Image.Clone(x => x.Resize(this.Size.Width, this.Size.Height)); - } + // Align start/end positions. + Rectangle bounds = targetImage.Bounds(); + int minX = Math.Max(this.Location.X, sourceRectangle.X); + int maxX = Math.Min(this.Location.X + bounds.Width, sourceRectangle.Width); + maxX = Math.Min(this.Location.X + this.Size.Width, maxX); + int targetX = minX - this.Location.X; - // Align start/end positions. - Rectangle bounds = targetImage.Bounds(); - int minX = Math.Max(this.Location.X, sourceRectangle.X); - int maxX = Math.Min(this.Location.X + bounds.Width, sourceRectangle.Width); - maxX = Math.Min(this.Location.X + this.Size.Width, maxX); - int targetX = minX - this.Location.X; + int minY = Math.Max(this.Location.Y, sourceRectangle.Y); + int maxY = Math.Min(this.Location.Y + bounds.Height, sourceRectangle.Bottom); - int minY = Math.Max(this.Location.Y, sourceRectangle.Y); - int maxY = Math.Min(this.Location.Y + bounds.Height, sourceRectangle.Bottom); + maxY = Math.Min(this.Location.Y + this.Size.Height, maxY); - maxY = Math.Min(this.Location.Y + this.Size.Height, maxY); - - int width = maxX - minX; - using (var amount = new Buffer(width)) + int width = maxX - minX; + using (var amount = new Buffer(width)) + { + for (int i = 0; i < width; i++) { - for (int i = 0; i < width; i++) - { - amount[i] = this.Alpha; - } - - Parallel.For( - minY, - maxY, - configuration.ParallelOptions, - y => - { - Span background = source.GetPixelRowSpan(y).Slice(minX, width); - Span foreground = targetImage.GetPixelRowSpan(y - this.Location.Y).Slice(targetX, width); - this.blender.Blend(background, background, foreground, amount); - }); + amount[i] = this.Alpha; } - } - finally - { - disposableImage?.Dispose(); + + Parallel.For( + minY, + maxY, + configuration.ParallelOptions, + y => + { + Span background = source.GetPixelRowSpan(y).Slice(minX, width); + Span foreground = targetImage.GetPixelRowSpan(y - this.Location.Y).Slice(targetX, width); + this.blender.Blend(background, background, foreground, amount); + }); } } } From b484dd401e7eb4175b19423024bad67d457a4d1a Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 22 Feb 2018 03:23:40 +0100 Subject: [PATCH 081/151] Super-optimized GenericBlock8x8 to replace PixelArea in JpegEncoder --- .../Jpeg/Common/GenericBlock8x8.Generated.cs | 26 ++++ .../Jpeg/Common/GenericBlock8x8.Generated.tt | 43 ++++++ .../Formats/Jpeg/Common/GenericBlock8x8.cs | 128 ++++++++++++++++++ .../Components/Encoder/RgbToYCbCrTables.cs | 4 +- .../Jpeg/GolangPort/JpegEncoderCore.cs | 2 +- .../Jpeg/GolangPort/Utils/OrigJpegUtils.cs | 2 +- src/ImageSharp/ImageSharp.csproj | 9 ++ .../Formats/Jpg/GenericBlock8x8Tests.cs | 126 +++++++++++++++++ .../Formats/Jpg/JpegUtilsTests.cs | 12 +- 9 files changed, 341 insertions(+), 11 deletions(-) create mode 100644 src/ImageSharp/Formats/Jpeg/Common/GenericBlock8x8.Generated.cs create mode 100644 src/ImageSharp/Formats/Jpeg/Common/GenericBlock8x8.Generated.tt create mode 100644 src/ImageSharp/Formats/Jpeg/Common/GenericBlock8x8.cs create mode 100644 tests/ImageSharp.Tests/Formats/Jpg/GenericBlock8x8Tests.cs diff --git a/src/ImageSharp/Formats/Jpeg/Common/GenericBlock8x8.Generated.cs b/src/ImageSharp/Formats/Jpeg/Common/GenericBlock8x8.Generated.cs new file mode 100644 index 0000000000..1bb37a7d32 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Common/GenericBlock8x8.Generated.cs @@ -0,0 +1,26 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; +using System.Runtime.CompilerServices; + +// +namespace SixLabors.ImageSharp.Formats.Jpeg.Common +{ + internal unsafe partial struct GenericBlock8x8 + { + #pragma warning disable 169 + + // It's not allowed use fix-sized buffers with generics, need to place all the fields manually: + private T _y0_x0, _y0_x1, _y0_x2, _y0_x3, _y0_x4, _y0_x5, _y0_x6, _y0_x7; + private T _y1_x0, _y1_x1, _y1_x2, _y1_x3, _y1_x4, _y1_x5, _y1_x6, _y1_x7; + private T _y2_x0, _y2_x1, _y2_x2, _y2_x3, _y2_x4, _y2_x5, _y2_x6, _y2_x7; + private T _y3_x0, _y3_x1, _y3_x2, _y3_x3, _y3_x4, _y3_x5, _y3_x6, _y3_x7; + private T _y4_x0, _y4_x1, _y4_x2, _y4_x3, _y4_x4, _y4_x5, _y4_x6, _y4_x7; + private T _y5_x0, _y5_x1, _y5_x2, _y5_x3, _y5_x4, _y5_x5, _y5_x6, _y5_x7; + private T _y6_x0, _y6_x1, _y6_x2, _y6_x3, _y6_x4, _y6_x5, _y6_x6, _y6_x7; + private T _y7_x0, _y7_x1, _y7_x2, _y7_x3, _y7_x4, _y7_x5, _y7_x6, _y7_x7; + + #pragma warning restore 169 + } +} diff --git a/src/ImageSharp/Formats/Jpeg/Common/GenericBlock8x8.Generated.tt b/src/ImageSharp/Formats/Jpeg/Common/GenericBlock8x8.Generated.tt new file mode 100644 index 0000000000..d9b15b34fa --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Common/GenericBlock8x8.Generated.tt @@ -0,0 +1,43 @@ +<# +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. +#> +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Text" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ output extension=".cs" #> +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; +using System.Runtime.CompilerServices; + +// +namespace SixLabors.ImageSharp.Formats.Jpeg.Common +{ + internal unsafe partial struct GenericBlock8x8 + { + #pragma warning disable 169 + + // It's not allowed use fix-sized buffers with generics, need to place all the fields manually: + <# + PushIndent(" "); + Write(" "); + for (int y = 0; y < 8; y++) + { + Write("private T "); + for (int x = 0; x < 8; x++) + { + Write($"_y{y}_x{x}"); + if (x < 7) Write(", "); + } + WriteLine(";"); + } + PopIndent(); + #> + + #pragma warning restore 169 + } +} diff --git a/src/ImageSharp/Formats/Jpeg/Common/GenericBlock8x8.cs b/src/ImageSharp/Formats/Jpeg/Common/GenericBlock8x8.cs new file mode 100644 index 0000000000..bc5838e9b5 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Common/GenericBlock8x8.cs @@ -0,0 +1,128 @@ +using System; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Common +{ + /// + /// A generic 8x8 block implementation, useful for manipulating custom 8x8 pixel data. + /// + // ReSharper disable once InconsistentNaming + internal unsafe partial struct GenericBlock8x8 + where T : struct + { + public const int Size = 64; + + public const int SizeInBytes = Size * 3; + + public void LoadAndStretchEdges(IPixelSource source, int sourceX, int sourceY) + where TPixel : struct, IPixel + { + var buffer = source.PixelBuffer as Buffer2D; + if (buffer == null) + { + throw new InvalidOperationException("LoadAndStretchEdges() is only valid for TPixel == T !"); + } + + this.LoadAndStretchEdges(buffer, sourceX, sourceY); + } + + /// + /// Load a 8x8 region of an image into the block. + /// The "outlying" area of the block will be stretched out with pixels on the right and bottom edge of the image. + /// + public void LoadAndStretchEdges(Buffer2D source, int sourceX, int sourceY) + { + int width = Math.Min(8, source.Width - sourceX); + int height = Math.Min(8, source.Height - sourceY); + + if (width <= 0 || height <= 0) + { + return; + } + + uint byteWidth = (uint)width * (uint)Unsafe.SizeOf(); + int remainderXCount = 8 - width; + + ref byte blockStart = ref Unsafe.As, byte>(ref this); + ref byte imageStart = ref Unsafe.As( + ref Unsafe.Add(ref source.GetRowSpan(sourceY).DangerousGetPinnableReference(), sourceX) + ); + + int blockRowSizeInBytes = 8 * Unsafe.SizeOf(); + int imageRowSizeInBytes = source.Width * Unsafe.SizeOf(); + + for (int y = 0; y < height; y++) + { + ref byte s = ref Unsafe.Add(ref imageStart, y * imageRowSizeInBytes); + ref byte d = ref Unsafe.Add(ref blockStart, y * blockRowSizeInBytes); + + Unsafe.CopyBlock(ref d, ref s, byteWidth); + + ref T last = ref Unsafe.Add(ref Unsafe.As(ref d), width - 1); + + for (int x = 1; x <= remainderXCount; x++) + { + Unsafe.Add(ref last, x) = last; + } + } + + int remainderYCount = 8 - height; + + if (remainderYCount == 0) + { + return; + } + + ref byte lastRowStart = ref Unsafe.Add(ref blockStart, (height - 1) * blockRowSizeInBytes); + + for (int y = 1; y <= remainderYCount; y++) + { + ref byte remStart = ref Unsafe.Add(ref lastRowStart, blockRowSizeInBytes * y); + Unsafe.CopyBlock(ref remStart, ref lastRowStart, (uint)blockRowSizeInBytes); + } + } + + /// + /// ONLY FOR GenericBlock instances living on the stack! + /// + public Span AsSpanUnsafe() => new Span(Unsafe.AsPointer(ref this), Size); + + /// + /// FOR TESTING ONLY! + /// Gets or sets a value at the given index + /// + /// The index + /// The value + public T this[int idx] + { + get + { + ref T selfRef = ref Unsafe.As, T>(ref this); + return Unsafe.Add(ref selfRef, idx); + } + + set + { + ref T selfRef = ref Unsafe.As, T>(ref this); + Unsafe.Add(ref selfRef, idx) = value; + } + } + + /// + /// FOR TESTING ONLY! + /// Gets or sets a value in a row+coulumn of the 8x8 block + /// + /// The x position index in the row + /// The column index + /// The value + public T this[int x, int y] + { + get => this[(y * 8) + x]; + set => this[(y * 8) + x] = value; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/RgbToYCbCrTables.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/RgbToYCbCrTables.cs index 02bd451b94..3c1d666850 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/RgbToYCbCrTables.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/RgbToYCbCrTables.cs @@ -8,6 +8,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder /// /// Provides 8-bit lookup tables for converting from Rgb to YCbCr colorspace. /// Methods to build the tables are based on libjpeg implementation. + /// TODO: Replace this logic with SIMD conversion (similar to the one in the decoder)! /// internal unsafe struct RgbToYCbCrTables { @@ -91,6 +92,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder } /// + /// TODO: Replace this logic with SIMD conversion (similar to the one in the decoder)! /// Optimized method to allocates the correct y, cb, and cr values to the DCT blocks from the given r, g, b values. /// /// The The luminance block. @@ -102,7 +104,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder /// The green value. /// The blue value. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Allocate(ref float* yBlockRaw, ref float* cbBlockRaw, ref float* crBlockRaw, ref RgbToYCbCrTables* tables, int index, int r, int g, int b) + public static void Rgb2YCbCr(float* yBlockRaw, float* cbBlockRaw, float* crBlockRaw, ref RgbToYCbCrTables* tables, int index, int r, int g, int b) { // float y = (0.299F * r) + (0.587F * g) + (0.114F * b); yBlockRaw[index] = (tables->YRTable[r] + tables->YGTable[g] + tables->YBTable[b]) >> ScaleBits; diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs index 4a9ddf3536..b03b0b5341 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs @@ -331,7 +331,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort int index = j8 + i; - RgbToYCbCrTables.Allocate(ref yBlockRaw, ref cbBlockRaw, ref crBlockRaw, ref tables, index, r, g, b); + RgbToYCbCrTables.Rgb2YCbCr(yBlockRaw, cbBlockRaw, crBlockRaw, ref tables, index, r, g, b); dataIdx += 3; } diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Utils/OrigJpegUtils.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Utils/OrigJpegUtils.cs index 01ed5063ba..42a7d56e3b 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Utils/OrigJpegUtils.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Utils/OrigJpegUtils.cs @@ -8,7 +8,7 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Utils { /// - /// Jpeg specific utilities and extension methods + /// Jpeg specific utilities and extension methods /// internal static class OrigJpegUtils { diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index b812b0b227..8b89499ea7 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -62,6 +62,10 @@ TextTemplatingFileGenerator Block8x8F.Generated.cs + + TextTemplatingFileGenerator + GenericBlock8x8.Generated.cs + TextTemplatingFileGenerator Block8x8F.Generated.cs @@ -92,6 +96,11 @@ True Block8x8F.Generated.tt + + True + True + GenericBlock8x8.Generated.tt + True True diff --git a/tests/ImageSharp.Tests/Formats/Jpg/GenericBlock8x8Tests.cs b/tests/ImageSharp.Tests/Formats/Jpg/GenericBlock8x8Tests.cs new file mode 100644 index 0000000000..193e26fcbd --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/GenericBlock8x8Tests.cs @@ -0,0 +1,126 @@ +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Tests.Formats.Jpg +{ + using System; + using System.Numerics; + + using SixLabors.ImageSharp.Formats.Jpeg.Common; + using SixLabors.ImageSharp.Helpers; + using SixLabors.ImageSharp.PixelFormats; + using SixLabors.Primitives; + + using Xunit; + + public class GenericBlock8x8Tests + { + public static Image CreateTestImage() + where TPixel : struct, IPixel + { + var image = new Image(10, 10); + using (PixelAccessor pixels = image.Lock()) + { + for (int i = 0; i < 10; i++) + { + for (int j = 0; j < 10; j++) + { + var rgba = new Rgba32((byte)(i+1), (byte)(j+1), (byte)200, (byte)255); + var color = default(TPixel); + color.PackFromRgba32(rgba); + + pixels[i, j] = color; + } + } + } + + return image; + } + + [Theory] + [WithMemberFactory(nameof(CreateTestImage), PixelTypes.Rgb24 | PixelTypes.Rgba32 /* | PixelTypes.Rgba32 | PixelTypes.Argb32*/)] + public void LoadAndStretchCorners_FromOrigo(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image s = provider.GetImage()) + { + var d = default(GenericBlock8x8); + d.LoadAndStretchEdges(s.Frames.RootFrame, 0, 0); + + TPixel a = s.Frames.RootFrame[0, 0]; + TPixel b = d[0, 0]; + + Assert.Equal(s[0, 0], d[0, 0]); + Assert.Equal(s[1, 0], d[1, 0]); + Assert.Equal(s[7, 0], d[7, 0]); + Assert.Equal(s[0, 1], d[0, 1]); + Assert.Equal(s[1, 1], d[1, 1]); + Assert.Equal(s[7, 0], d[7, 0]); + Assert.Equal(s[0, 7], d[0, 7]); + Assert.Equal(s[7, 7], d[7, 7]); + } + } + + [Theory] + [WithMemberFactory(nameof(CreateTestImage), PixelTypes.Rgb24 | PixelTypes.Rgba32)] + public unsafe void LoadAndStretchCorners_WithOffset(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image s = provider.GetImage()) + { + var d = default(GenericBlock8x8); + d.LoadAndStretchEdges(s.Frames.RootFrame, 6, 7); + + Assert.Equal(s[6, 7], d[0, 0]); + Assert.Equal(s[6, 8], d[0, 1]); + Assert.Equal(s[7, 8], d[1, 1]); + + Assert.Equal(s[6, 9], d[0, 2]); + Assert.Equal(s[6, 9], d[0, 3]); + Assert.Equal(s[6, 9], d[0, 7]); + + Assert.Equal(s[7, 9], d[1, 2]); + Assert.Equal(s[7, 9], d[1, 3]); + Assert.Equal(s[7, 9], d[1, 7]); + + Assert.Equal(s[9, 9], d[3, 2]); + Assert.Equal(s[9, 9], d[3, 3]); + Assert.Equal(s[9, 9], d[3, 7]); + + Assert.Equal(s[9, 7], d[3, 0]); + Assert.Equal(s[9, 7], d[4, 0]); + Assert.Equal(s[9, 7], d[7, 0]); + + Assert.Equal(s[9, 9], d[3, 2]); + Assert.Equal(s[9, 9], d[4, 2]); + Assert.Equal(s[9, 9], d[7, 2]); + + Assert.Equal(s[9, 9], d[4, 3]); + Assert.Equal(s[9, 9], d[7, 7]); + } + } + + [Fact] + public void Indexer() + { + var block = default(GenericBlock8x8); + Span span = block.AsSpanUnsafe(); + Assert.Equal(64, span.Length); + + for (int i = 0; i < 64; i++) + { + span[i] = new Rgb24((byte)i, (byte)(2 * i), (byte)(3 * i)); + } + + Rgb24 expected00 = new Rgb24(0, 0, 0); + Rgb24 expected07 = new Rgb24(7, 14, 21); + Rgb24 expected11 = new Rgb24(9, 18, 27); + Rgb24 expected77 = new Rgb24(63, 126, 189); + Rgb24 expected67 = new Rgb24(62, 124, 186); + + Assert.Equal(expected00, block[0, 0]); + Assert.Equal(expected07, block[7, 0]); + Assert.Equal(expected11, block[1, 1]); + Assert.Equal(expected67, block[6, 7]); + Assert.Equal(expected77, block[7, 7]); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilsTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilsTests.cs index 887e9d7e95..6fe6a9bfde 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilsTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilsTests.cs @@ -1,18 +1,14 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. - +using System.Numerics; +using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Utils; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests.Formats.Jpg { - using System.Numerics; - - using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Utils; - using SixLabors.ImageSharp.PixelFormats; - - using Xunit; - public class JpegUtilsTests { public static Image CreateTestImage() From 8c15954391c6454374ecee6af08c4e48d4349808 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 22 Feb 2018 13:31:41 +0100 Subject: [PATCH 082/151] YCbCrForwardConverter WIP --- .../Components/Encoder/RgbToYCbCrTables.cs | 58 ++++++++++++++++++- .../Jpeg/GolangPort/JpegEncoderCore.cs | 37 +++++++++--- .../Jpeg/GolangPort/Utils/OrigJpegUtils.cs | 11 ++++ 3 files changed, 95 insertions(+), 11 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/RgbToYCbCrTables.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/RgbToYCbCrTables.cs index 3c1d666850..01fe51c8d1 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/RgbToYCbCrTables.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/RgbToYCbCrTables.cs @@ -2,6 +2,10 @@ // Licensed under the Apache License, Version 2.0. using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Formats.Jpeg.Common; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder { @@ -95,16 +99,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder /// TODO: Replace this logic with SIMD conversion (similar to the one in the decoder)! /// Optimized method to allocates the correct y, cb, and cr values to the DCT blocks from the given r, g, b values. /// + /// The reference to the tables instance. /// The The luminance block. /// The red chroma block. /// The blue chroma block. - /// The reference to the tables instance. /// The current index. /// The red value. /// The green value. /// The blue value. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Rgb2YCbCr(float* yBlockRaw, float* cbBlockRaw, float* crBlockRaw, ref RgbToYCbCrTables* tables, int index, int r, int g, int b) + public static void Rgb2YCbCr(RgbToYCbCrTables* tables, float* yBlockRaw, float* cbBlockRaw, float* crBlockRaw, int index, int r, int g, int b) { // float y = (0.299F * r) + (0.587F * g) + (0.114F * b); yBlockRaw[index] = (tables->YRTable[r] + tables->YGTable[g] + tables->YBTable[b]) >> ScaleBits; @@ -116,6 +120,27 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder crBlockRaw[index] = (tables->CbBTable[r] + tables->CrGTable[g] + tables->CrBTable[b]) >> ScaleBits; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ConvertPixelInto(int r, int g, int b, ref float yResult, ref float cbResult, ref float crResult) + { + ref int start = ref Unsafe.As(ref this); + + ref int yR = ref start; + ref int yG = ref Unsafe.Add(ref start, 256 * 1); + ref int yB = ref Unsafe.Add(ref start, 256 * 2); + + ref int cbR = ref Unsafe.Add(ref start, 256 * 3); + ref int cbG = ref Unsafe.Add(ref start, 256 * 4); + ref int cbB = ref Unsafe.Add(ref start, 256 * 5); + + ref int crG = ref Unsafe.Add(ref start, 256 * 6); + ref int crB = ref Unsafe.Add(ref start, 256 * 7); + + yResult = (Unsafe.Add(ref yR, r) + Unsafe.Add(ref yG, g) + Unsafe.Add(ref yB, b)) >> ScaleBits; + cbResult = (Unsafe.Add(ref cbR, r) + Unsafe.Add(ref cbG, g) + Unsafe.Add(ref cbB, b)) >> ScaleBits; + crResult = (Unsafe.Add(ref cbB, r) + Unsafe.Add(ref crG, g) + Unsafe.Add(ref crB, b)) >> ScaleBits; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int Fix(float x) { @@ -128,4 +153,33 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder return x >> ScaleBits; } } + + // TODO! + internal struct YCbCrForwardConverter + where TPixel : struct, IPixel + { + public Block8x8F Y; + + public Block8x8F Cb; + + public Block8x8F Cr; + + private RgbToYCbCrTables colorTables; + + private GenericBlock8x8 pixelBlock; + + private GenericBlock8x8 rgbBlock; + + public static YCbCrForwardConverter Create() + { + var result = default(YCbCrForwardConverter); + result.colorTables = RgbToYCbCrTables.Create(); + return result; + } + + public void Convert(IPixelSource pixels, int x, int y) + { + + } + } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs index b03b0b5341..fcac30a2c8 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs @@ -303,15 +303,19 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort RgbToYCbCrTables* tables, int x, int y, - Block8x8F* yBlock, - Block8x8F* cbBlock, - Block8x8F* crBlock, + ref Block8x8F yBlock, + ref Block8x8F cbBlock, + ref Block8x8F crBlock, PixelArea rgbBytes) where TPixel : struct, IPixel { - float* yBlockRaw = (float*)yBlock; - float* cbBlockRaw = (float*)cbBlock; - float* crBlockRaw = (float*)crBlock; + ref float yBlockStart = ref Unsafe.As(ref yBlock); + ref float cbBlockStart = ref Unsafe.As(ref cbBlock); + ref float crBlockStart = ref Unsafe.As(ref crBlock); + + float* yBlockRaw = (float*) Unsafe.AsPointer(ref yBlock); + float* cbBlockRaw = (float*)Unsafe.AsPointer(ref cbBlock); + float* crBlockRaw = (float*)Unsafe.AsPointer(ref crBlock); rgbBytes.Reset(); pixels.CopyRGBBytesStretchedTo(rgbBytes, y, x); @@ -331,7 +335,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort int index = j8 + i; - RgbToYCbCrTables.Rgb2YCbCr(yBlockRaw, cbBlockRaw, crBlockRaw, ref tables, index, r, g, b); + RgbToYCbCrTables.Rgb2YCbCr(tables, yBlockRaw, cbBlockRaw, crBlockRaw, index, r, g, b); + //tables->ConvertPixelInto( + // r, + // g, + // b, + // ref Unsafe.Add(ref yBlockRaw, index), + // ref Unsafe.Add(ref cbBlockRaw, index), + // ref Unsafe.Add(ref crBlockRaw, index)); dataIdx += 3; } @@ -460,7 +471,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort { for (int x = 0; x < pixels.Width; x += 8) { - ToYCbCr(pixels, tables, x, y, &b, &cb, &cr, rgbBytes); + ToYCbCr(pixels, tables, x, y, ref b, ref cb, ref cr, rgbBytes); prevDCY = this.WriteBlock( QuantIndex.Luminance, @@ -920,7 +931,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort int xOff = (i & 1) * 8; int yOff = (i & 2) * 4; - ToYCbCr(pixels, tables, x + xOff, y + yOff, &b, cbPtr + i, crPtr + i, rgbBytes); + ToYCbCr( + pixels, + tables, + x + xOff, + y + yOff, + ref b, + ref Unsafe.AsRef(cbPtr + i), + ref Unsafe.AsRef(crPtr + i), + rgbBytes); prevDCY = this.WriteBlock( QuantIndex.Luminance, diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Utils/OrigJpegUtils.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Utils/OrigJpegUtils.cs index 42a7d56e3b..caa7014a4b 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Utils/OrigJpegUtils.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Utils/OrigJpegUtils.cs @@ -7,11 +7,22 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Utils { + using SixLabors.ImageSharp.Formats.Jpeg.Common; + /// /// Jpeg specific utilities and extension methods /// internal static class OrigJpegUtils { + /// + /// Stack only TPixel -> Rgb24 conversion method on 8x8 blocks. + /// + public static void ConvertToRgbUnsafe(ref GenericBlock8x8 source, ref GenericBlock8x8 dest) + where TPixel : struct, IPixel + { + PixelOperations.Instance.ToRgb24(source.AsSpanUnsafe(), dest.AsSpanUnsafe(), 64); + } + /// /// Copy a region of an image into dest. De "outlier" area will be stretched out with pixels on the right and bottom of the image. /// From 6ab7954bfd30d9ce5e383ab8c58a9b9dae47ff49 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 22 Feb 2018 14:46:07 +0100 Subject: [PATCH 083/151] MakeOpaque + fixing BmpEncoderTests --- .../Processing/ColorMatrix/Opacity.cs | 4 +- .../Formats/Bmp/BmpEncoderTests.cs | 6 ++- .../Formats/Jpg/JpegEncoderTests.cs | 2 +- .../Formats/Png/PngEncoderTests.cs | 21 ++++----- .../TestUtilities/TestImageExtensions.cs | 45 +++++++++++++++---- .../Tests/ReferenceCodecTests.cs | 6 +-- 6 files changed, 57 insertions(+), 27 deletions(-) diff --git a/src/ImageSharp/Processing/ColorMatrix/Opacity.cs b/src/ImageSharp/Processing/ColorMatrix/Opacity.cs index b310b4b915..b59c068905 100644 --- a/src/ImageSharp/Processing/ColorMatrix/Opacity.cs +++ b/src/ImageSharp/Processing/ColorMatrix/Opacity.cs @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp public static partial class ImageExtensions { /// - /// Alters the alpha component of the image. + /// Multiplies the alpha component of the image. /// /// The pixel format. /// The image this method extends. @@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp => source.ApplyProcessor(new OpacityProcessor(amount)); /// - /// Alters the alpha component of the image. + /// Multiplies the alpha component of the image. /// /// The pixel format. /// The image this method extends. diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs index b1eea79d80..362df2be68 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs @@ -51,13 +51,15 @@ namespace SixLabors.ImageSharp.Tests TestBmpEncoderCore(provider, bitsPerPixel); } + + private static void TestBmpEncoderCore(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) where TPixel : struct, IPixel - { + { using (Image image = provider.GetImage()) { // there is no alpha in bmp! - image.Mutate(c => c.Opacity(1)); + image.Mutate(c => c.MakeOpaque()); var encoder = new BmpEncoder { BitsPerPixel = bitsPerPixel }; diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs index 8610356b56..10657ea78f 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs @@ -80,7 +80,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg using (Image image = provider.GetImage()) { // There is no alpha in Jpeg! - image.Mutate(c => c.Opacity(1)); + image.Mutate(c => c.MakeOpaque()); var encoder = new JpegEncoder() { diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index 28f156279c..c4f9b0debb 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -123,26 +123,27 @@ namespace SixLabors.ImageSharp.Tests { if (!HasAlpha(pngColorType)) { - image.Mutate(c => c.Opacity(1)); + image.Mutate(c => c.MakeOpaque()); } var encoder = new PngEncoder { PngColorType = pngColorType, CompressionLevel = compressionLevel}; - string pngColorTypeInfo = appendPixelType ? pngColorType.ToString() : ""; + string pngColorTypeInfo = appendPngColorType ? pngColorType.ToString() : ""; string compressionLevelInfo = appendCompressionLevel ? $"_C{compressionLevel}" : ""; string debugInfo = $"{pngColorTypeInfo}{compressionLevelInfo}"; - string referenceInfo = $"{pngColorTypeInfo}"; - + // Does DebugSave & load reference CompareToReferenceInput(): - string path = ((ITestImageProvider)provider).Utility.SaveTestOutputFile(image, "png", encoder, debugInfo, appendPixelType); + string actualOutputFile = ((ITestImageProvider)provider).Utility.SaveTestOutputFile(image, "png", encoder, debugInfo, appendPixelType); - IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(path); - string referenceOutputFile = ((ITestImageProvider)provider).Utility.GetReferenceOutputFileName("png", referenceInfo, appendPixelType); + IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(actualOutputFile); - using (var encodedImage = Image.Load(referenceOutputFile, referenceDecoder)) + using (var actualImage = Image.Load(actualOutputFile, referenceDecoder)) { - ImageComparer comparer = pngColorType== PngColorType.Palette ? ImageComparer.Tolerant(ToleranceThresholdForPaletteEncoder) : ImageComparer.Exact; - comparer.CompareImagesOrFrames(image, encodedImage); + ImageComparer comparer = pngColorType == PngColorType.Palette + ? ImageComparer.Tolerant(ToleranceThresholdForPaletteEncoder) + : ImageComparer.Exact; + + comparer.VerifySimilarity(image, actualImage); } } } diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index 33dbc911e4..6f15cf439e 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -22,6 +22,38 @@ namespace SixLabors.ImageSharp.Tests public static class TestImageExtensions { + /// + /// TODO: This should be a common processing method! The image.Opacity(val) multiplies the alpha channel! + /// + /// + /// + public static void MakeOpaque(this IImageProcessingContext ctx) + where TPixel : struct, IPixel + { + ctx.Apply( + img => + { + using (var temp = new Buffer2D(img.Width, img.Height)) + { + Span tempSpan = temp.Span; + foreach (ImageFrame frame in img.Frames) + { + Span pixelSpan = frame.GetPixelSpan(); + + PixelOperations.Instance.ToVector4(pixelSpan, tempSpan, pixelSpan.Length); + + for (int i = 0; i < tempSpan.Length; i++) + { + ref Vector4 v = ref tempSpan[i]; + v.W = 1.0f; + } + + PixelOperations.Instance.PackFromVector4(tempSpan, pixelSpan, pixelSpan.Length); + } + } + }); + } + /// /// Saves the image only when not running in the CI server. /// @@ -357,18 +389,13 @@ namespace SixLabors.ImageSharp.Tests ) where TPixel : struct, IPixel { + string actualOutputFile = provider.Utility.SaveTestOutputFile(image, extension, encoder, testOutputDetails, appendPixelTypeToFileName); + IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(actualOutputFile); - provider.Utility.SaveTestOutputFile(image, extension, encoder, testOutputDetails, appendPixelTypeToFileName); - - referenceImageExtension = referenceImageExtension ?? extension; - string referenceOutputFile = provider.Utility.GetReferenceOutputFileName(referenceImageExtension, testOutputDetails, appendPixelTypeToFileName); - - IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(referenceOutputFile); - - using (var encodedImage = Image.Load(referenceOutputFile, referenceDecoder)) + using (var actualImage = Image.Load(actualOutputFile, referenceDecoder)) { ImageComparer comparer = customComparer ?? ImageComparer.Exact; - comparer.CompareImagesOrFrames(image, encodedImage); + comparer.VerifySimilarity(actualImage, image); } } diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceCodecTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceCodecTests.cs index dde34fcc43..c67e4e06bf 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceCodecTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceCodecTests.cs @@ -56,7 +56,7 @@ namespace SixLabors.ImageSharp.Tests { if (pngColorType != PngColorType.RgbWithAlpha) { - sourceImage.Mutate(c => c.Opacity(1)); + sourceImage.Mutate(c => c.MakeOpaque()); } var encoder = new PngEncoder() { PngColorType = pngColorType }; @@ -93,12 +93,12 @@ namespace SixLabors.ImageSharp.Tests using (Image original = provider.GetImage()) { - original.Mutate(c => c.Opacity(1)); + original.Mutate(c => c.MakeOpaque()); using (var sdBitmap = new System.Drawing.Bitmap(path)) { using (Image resaved = SystemDrawingBridge.FromFromRgb24SystemDrawingBitmap(sdBitmap)) { - resaved.Mutate(c => c.Opacity(1)); + resaved.Mutate(c => c.MakeOpaque()); ImageComparer comparer = ImageComparer.Exact; comparer.VerifySimilarity(original, resaved); } From 9dc3261310527fc277a3864f0eedeca7372ea9aa Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 22 Feb 2018 15:13:08 +0100 Subject: [PATCH 084/151] fixed PngEncoderTests --- .../Formats/Png/PngEncoderTests.cs | 47 ++++++++----------- tests/Images/External | 2 +- 2 files changed, 20 insertions(+), 29 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index c4f9b0debb..0010bb41d3 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -68,7 +68,7 @@ namespace SixLabors.ImageSharp.Tests public void IsNotBoundToSinglePixelType(TestImageProvider provider, PngColorType pngColorType) where TPixel : struct, IPixel { - TestPngEncoderCore(provider, pngColorType, appendPixelType: true); + TestPngEncoderCore(provider, pngColorType, appendPixelType: true, appendPngColorType: true); } [Theory] @@ -78,33 +78,13 @@ namespace SixLabors.ImageSharp.Tests { TestPngEncoderCore(provider, PngColorType.RgbWithAlpha, compressionLevel, appendCompressionLevel: true); } - + [Theory] [WithFile(TestImages.Png.Palette8Bpp, nameof(PaletteLargeOnly), PixelTypes.Rgba32)] - public void PaletteColorType_WuQuantizer_File( - TestImageProvider provider, - int paletteSize) - where TPixel : struct, IPixel - { - this.PaletteColorType_WuQuantizer(provider, paletteSize); - } - - [Theory] - [WithTestPatternImages(nameof(PaletteSizes), 72, 72, PixelTypes.Rgba32)] public void PaletteColorType_WuQuantizer(TestImageProvider provider, int paletteSize) where TPixel : struct, IPixel { - using (Image image = provider.GetImage()) - { - var encoder = new PngEncoder - { - PngColorType = PngColorType.Palette, - PaletteSize = paletteSize, - Quantizer = new WuQuantizer() - }; - - image.VerifyEncoder(provider, "png", $"PaletteSize-{paletteSize}", encoder, appendPixelTypeToFileName: false); - } + TestPngEncoderCore(provider, PngColorType.Palette, paletteSize: paletteSize, appendPaletteSize: true); } private static bool HasAlpha(PngColorType pngColorType) => @@ -114,9 +94,11 @@ namespace SixLabors.ImageSharp.Tests TestImageProvider provider, PngColorType pngColorType, int compressionLevel = 6, + int paletteSize = 0, bool appendPngColorType = false, bool appendPixelType = false, - bool appendCompressionLevel = false) + bool appendCompressionLevel = false, + bool appendPaletteSize = false) where TPixel : struct, IPixel { using (Image image = provider.GetImage()) @@ -126,24 +108,33 @@ namespace SixLabors.ImageSharp.Tests image.Mutate(c => c.MakeOpaque()); } - var encoder = new PngEncoder { PngColorType = pngColorType, CompressionLevel = compressionLevel}; + var encoder = new PngEncoder + { + PngColorType = pngColorType, + CompressionLevel = compressionLevel, + PaletteSize = paletteSize + }; string pngColorTypeInfo = appendPngColorType ? pngColorType.ToString() : ""; string compressionLevelInfo = appendCompressionLevel ? $"_C{compressionLevel}" : ""; - string debugInfo = $"{pngColorTypeInfo}{compressionLevelInfo}"; - + string paletteSizeInfo = appendPaletteSize ? $"_PaletteSize-{paletteSize}" : ""; + string debugInfo = $"{pngColorTypeInfo}{compressionLevelInfo}{paletteSizeInfo}"; + //string referenceInfo = $"{pngColorTypeInfo}"; + // Does DebugSave & load reference CompareToReferenceInput(): string actualOutputFile = ((ITestImageProvider)provider).Utility.SaveTestOutputFile(image, "png", encoder, debugInfo, appendPixelType); IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(actualOutputFile); + string referenceOutputFile = ((ITestImageProvider)provider).Utility.GetReferenceOutputFileName("png", debugInfo, appendPixelType); using (var actualImage = Image.Load(actualOutputFile, referenceDecoder)) + using (var referenceImage = Image.Load(referenceOutputFile, referenceDecoder)) { ImageComparer comparer = pngColorType == PngColorType.Palette ? ImageComparer.Tolerant(ToleranceThresholdForPaletteEncoder) : ImageComparer.Exact; - comparer.VerifySimilarity(image, actualImage); + comparer.VerifySimilarity(referenceImage, actualImage); } } } diff --git a/tests/Images/External b/tests/Images/External index b3be1178d4..d9c7d37074 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit b3be1178d4e970efc624181480094e50b0d57a90 +Subproject commit d9c7d37074e112eabeccdde6381286f69b01b8b1 From 1e777f595d99ce685a8dc6945a609fc2f9b4a476 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 22 Feb 2018 18:26:01 +0100 Subject: [PATCH 085/151] fixed JpegEncoderTests --- .../Formats/Jpg/JpegEncoderTests.cs | 28 ++++++++++++------- tests/Images/External | 2 +- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs index 10657ea78f..cc030bbf7c 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs @@ -15,6 +15,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; + using SixLabors.Primitives; using Xunit; using Xunit.Abstractions; @@ -55,20 +56,27 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg TestJpegEncoderCore(provider, subsample, quality); } - private static ImageComparer GetComparer(int quality) + /// + /// Anton's SUPER-SCIENTIFIC tolerance threshold calculation + /// + private static ImageComparer GetComparer(int quality, JpegSubsample subsample) { - if (quality > 90) - { - return ImageComparer.Tolerant(0.0005f / 100); - } - else if (quality > 50) + float tolerance = 0.015f; // ~1.5% + + if (quality < 50) { - return ImageComparer.Tolerant(0.005f / 100); + tolerance *= 10f; } - else + else if (quality < 75 || subsample == JpegSubsample.Ratio420) { - return ImageComparer.Tolerant(0.01f / 100); + tolerance *= 5f; + if (subsample == JpegSubsample.Ratio420) + { + tolerance *= 2f; + } } + + return ImageComparer.Tolerant(tolerance); } private static void TestJpegEncoderCore( @@ -88,7 +96,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg Quality = quality }; string info = $"{subsample}-Q{quality}"; - ImageComparer comparer = GetComparer(quality); + ImageComparer comparer = GetComparer(quality, subsample); // Does DebugSave & load reference CompareToReferenceInput(): image.VerifyEncoder(provider, "jpeg", info, encoder, comparer, referenceImageExtension: "png"); diff --git a/tests/Images/External b/tests/Images/External index d9c7d37074..20f83891ce 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit d9c7d37074e112eabeccdde6381286f69b01b8b1 +Subproject commit 20f83891ce75597486f5532010a8c5dea1419a4d From de816e3c049395b56c5b10a75e7a678e363748ff Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 22 Feb 2018 18:48:32 +0100 Subject: [PATCH 086/151] build fix: MakeOpaque() --- tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index 1315654c69..e9dc09989c 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -30,10 +30,11 @@ namespace SixLabors.ImageSharp.Tests public static void MakeOpaque(this IImageProcessingContext ctx) where TPixel : struct, IPixel { + MemoryManager memoryManager = ctx.MemoryManager; ctx.Apply( img => { - using (var temp = new Buffer2D(img.Width, img.Height)) + using (Buffer2D temp = memoryManager.Allocate2D(img.Width, img.Height)) { Span tempSpan = temp.Span; foreach (ImageFrame frame in img.Frames) From e2cf90e683fe211cd1e4e3a49cadf270dd10762e Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 22 Feb 2018 20:22:53 +0100 Subject: [PATCH 087/151] YCbCrForwardConverter --- .../Components/Encoder/RgbToYCbCrTables.cs | 24 +++++++++++++++++++ .../Jpeg/GolangPort/JpegEncoderCore.cs | 23 ++++++++---------- 2 files changed, 34 insertions(+), 13 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/RgbToYCbCrTables.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/RgbToYCbCrTables.cs index 01fe51c8d1..86b3fc9034 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/RgbToYCbCrTables.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/RgbToYCbCrTables.cs @@ -9,6 +9,8 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder { + using System; + /// /// Provides 8-bit lookup tables for converting from Rgb to YCbCr colorspace. /// Methods to build the tables are based on libjpeg implementation. @@ -179,7 +181,29 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder public void Convert(IPixelSource pixels, int x, int y) { + this.pixelBlock.LoadAndStretchEdges(pixels, x, y); + + Span rgbSpan = this.rgbBlock.AsSpanUnsafe(); + PixelOperations.Instance.ToRgb24(this.pixelBlock.AsSpanUnsafe(), rgbSpan, 64); + + ref float yBlockStart = ref Unsafe.As(ref this.Y); + ref float cbBlockStart = ref Unsafe.As(ref this.Cb); + ref float crBlockStart = ref Unsafe.As(ref this.Cr); + ref Rgb24 rgbStart = ref rgbSpan[0]; + for (int i = 0; i < 64; i++) + { + ref Rgb24 c = ref Unsafe.Add(ref rgbStart, i); + + this.colorTables.ConvertPixelInto( + c.R, + c.G, + c.B, + ref Unsafe.Add(ref yBlockStart, i), + ref Unsafe.Add(ref cbBlockStart, i), + ref Unsafe.Add(ref crBlockStart, i) + ); + } } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs index fcac30a2c8..e67a3f0205 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs @@ -313,11 +313,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort ref float cbBlockStart = ref Unsafe.As(ref cbBlock); ref float crBlockStart = ref Unsafe.As(ref crBlock); - float* yBlockRaw = (float*) Unsafe.AsPointer(ref yBlock); - float* cbBlockRaw = (float*)Unsafe.AsPointer(ref cbBlock); - float* crBlockRaw = (float*)Unsafe.AsPointer(ref crBlock); - - rgbBytes.Reset(); pixels.CopyRGBBytesStretchedTo(rgbBytes, y, x); ref byte data0 = ref rgbBytes.Bytes[0]; @@ -335,14 +330,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort int index = j8 + i; - RgbToYCbCrTables.Rgb2YCbCr(tables, yBlockRaw, cbBlockRaw, crBlockRaw, index, r, g, b); - //tables->ConvertPixelInto( - // r, - // g, - // b, - // ref Unsafe.Add(ref yBlockRaw, index), - // ref Unsafe.Add(ref cbBlockRaw, index), - // ref Unsafe.Add(ref crBlockRaw, index)); + // RgbToYCbCrTables.Rgb2YCbCr(tables, yBlockRaw, cbBlockRaw, crBlockRaw, index, r, g, b); + tables->ConvertPixelInto( + r, + g, + b, + ref Unsafe.Add(ref yBlockStart, index), + ref Unsafe.Add(ref cbBlockStart, index), + ref Unsafe.Add(ref crBlockStart, index)); dataIdx += 3; } @@ -463,6 +458,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort // ReSharper disable once InconsistentNaming int prevDCY = 0, prevDCCb = 0, prevDCCr = 0; + YCbCrForwardConverter pixelConverter = YCbCrForwardConverter.Create(); + fixed (RgbToYCbCrTables* tables = &rgbToYCbCrTables) { using (PixelArea rgbBytes = new PixelArea(8, 8, ComponentOrder.Xyz)) From d137c05046f3f589148f39979325233615deeeb5 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 22 Feb 2018 20:33:12 +0100 Subject: [PATCH 088/151] Encode444 using YCbCrForwardConverter --- .../Jpeg/GolangPort/JpegEncoderCore.cs | 86 +++++++++---------- 1 file changed, 39 insertions(+), 47 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs index e67a3f0205..c8be8b02fe 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs @@ -232,10 +232,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort this.WriteDefineHuffmanTables(componentCount); // Write the image data. - using (PixelAccessor pixels = image.Lock()) - { - this.WriteStartOfScan(pixels); - } + this.WriteStartOfScan(image); // Write the End Of Image marker. this.buffer[0] = OrigJpegConstants.Markers.XFF; @@ -439,14 +436,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort /// /// The pixel format. /// The pixel accessor providing access to the image pixels. - private void Encode444(PixelAccessor pixels) + private void Encode444(Image pixels) where TPixel : struct, IPixel { // TODO: Need a JpegScanEncoder class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.) - Block8x8F b = default(Block8x8F); - Block8x8F cb = default(Block8x8F); - Block8x8F cr = default(Block8x8F); - + // (Partially done with YCbCrForwardConverter) Block8x8F temp1 = default(Block8x8F); Block8x8F temp2 = default(Block8x8F); @@ -460,42 +454,36 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort YCbCrForwardConverter pixelConverter = YCbCrForwardConverter.Create(); - fixed (RgbToYCbCrTables* tables = &rgbToYCbCrTables) + for (int y = 0; y < pixels.Height; y += 8) { - using (PixelArea rgbBytes = new PixelArea(8, 8, ComponentOrder.Xyz)) + for (int x = 0; x < pixels.Width; x += 8) { - for (int y = 0; y < pixels.Height; y += 8) - { - for (int x = 0; x < pixels.Width; x += 8) - { - ToYCbCr(pixels, tables, x, y, ref b, ref cb, ref cr, rgbBytes); - - prevDCY = this.WriteBlock( - QuantIndex.Luminance, - prevDCY, - &b, - &temp1, - &temp2, - &onStackLuminanceQuantTable, - unzig.Data); - prevDCCb = this.WriteBlock( - QuantIndex.Chrominance, - prevDCCb, - &cb, - &temp1, - &temp2, - &onStackChrominanceQuantTable, - unzig.Data); - prevDCCr = this.WriteBlock( - QuantIndex.Chrominance, - prevDCCr, - &cr, - &temp1, - &temp2, - &onStackChrominanceQuantTable, - unzig.Data); - } - } + pixelConverter.Convert(pixels.Frames.RootFrame, x, y); + + prevDCY = this.WriteBlock( + QuantIndex.Luminance, + prevDCY, + &pixelConverter.Y, + &temp1, + &temp2, + &onStackLuminanceQuantTable, + unzig.Data); + prevDCCb = this.WriteBlock( + QuantIndex.Chrominance, + prevDCCb, + &pixelConverter.Cb, + &temp1, + &temp2, + &onStackChrominanceQuantTable, + unzig.Data); + prevDCCr = this.WriteBlock( + QuantIndex.Chrominance, + prevDCCr, + &pixelConverter.Cr, + &temp1, + &temp2, + &onStackChrominanceQuantTable, + unzig.Data); } } } @@ -866,8 +854,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort /// Writes the StartOfScan marker. /// /// The pixel format. - /// The pixel accessor providing access to the image pixels. - private void WriteStartOfScan(PixelAccessor pixels) + /// The pixel accessor providing access to the image pixels. + private void WriteStartOfScan(Image image) where TPixel : struct, IPixel { // TODO: Need a JpegScanEncoder class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.) @@ -877,10 +865,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort switch (this.subsample) { case JpegSubsample.Ratio444: - this.Encode444(pixels); + this.Encode444(image); break; case JpegSubsample.Ratio420: - this.Encode420(pixels); + using (var pixels = image.Lock()) + { + this.Encode420(pixels); + } + break; } From dc7a0efda02b77487ea81b1fe834c6d52c1e58a9 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 22 Feb 2018 20:49:06 +0100 Subject: [PATCH 089/151] All PixelArea usages in JpegEncoder replaced by GenericBlock8x8 + YCbCrForwardConverter --- .../Formats/Jpeg/Common/GenericBlock8x8.cs | 5 +- .../Components/Encoder/RgbToYCbCrTables.cs | 78 +------------- .../Encoder/YCbCrForwardConverter{TPixel}.cs | 83 +++++++++++++++ .../Jpeg/GolangPort/JpegEncoderCore.cs | 100 ++++++++---------- 4 files changed, 132 insertions(+), 134 deletions(-) create mode 100644 src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/YCbCrForwardConverter{TPixel}.cs diff --git a/src/ImageSharp/Formats/Jpeg/Common/GenericBlock8x8.cs b/src/ImageSharp/Formats/Jpeg/Common/GenericBlock8x8.cs index bc5838e9b5..aefc86eb11 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/GenericBlock8x8.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/GenericBlock8x8.cs @@ -7,9 +7,12 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Formats.Jpeg.Common { + using System.Runtime.InteropServices; + /// /// A generic 8x8 block implementation, useful for manipulating custom 8x8 pixel data. /// + [StructLayout(LayoutKind.Sequential)] // ReSharper disable once InconsistentNaming internal unsafe partial struct GenericBlock8x8 where T : struct @@ -87,7 +90,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common } /// - /// ONLY FOR GenericBlock instances living on the stack! + /// Only for on-stack instances! /// public Span AsSpanUnsafe() => new Span(Unsafe.AsPointer(ref this), Size); diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/RgbToYCbCrTables.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/RgbToYCbCrTables.cs index 86b3fc9034..923fe244eb 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/RgbToYCbCrTables.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/RgbToYCbCrTables.cs @@ -2,15 +2,11 @@ // Licensed under the Apache License, Version 2.0. using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Formats.Jpeg.Common; + using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder { - using System; - /// /// Provides 8-bit lookup tables for converting from Rgb to YCbCr colorspace. /// Methods to build the tables are based on libjpeg implementation. @@ -101,27 +97,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder /// TODO: Replace this logic with SIMD conversion (similar to the one in the decoder)! /// Optimized method to allocates the correct y, cb, and cr values to the DCT blocks from the given r, g, b values. /// - /// The reference to the tables instance. - /// The The luminance block. - /// The red chroma block. - /// The blue chroma block. - /// The current index. - /// The red value. - /// The green value. - /// The blue value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Rgb2YCbCr(RgbToYCbCrTables* tables, float* yBlockRaw, float* cbBlockRaw, float* crBlockRaw, int index, int r, int g, int b) - { - // float y = (0.299F * r) + (0.587F * g) + (0.114F * b); - yBlockRaw[index] = (tables->YRTable[r] + tables->YGTable[g] + tables->YBTable[b]) >> ScaleBits; - - // float cb = 128F + ((-0.168736F * r) - (0.331264F * g) + (0.5F * b)); - cbBlockRaw[index] = (tables->CbRTable[r] + tables->CbGTable[g] + tables->CbBTable[b]) >> ScaleBits; - - // float b = MathF.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero); - crBlockRaw[index] = (tables->CbBTable[r] + tables->CrGTable[g] + tables->CrBTable[b]) >> ScaleBits; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ConvertPixelInto(int r, int g, int b, ref float yResult, ref float cbResult, ref float crResult) { @@ -155,55 +130,4 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder return x >> ScaleBits; } } - - // TODO! - internal struct YCbCrForwardConverter - where TPixel : struct, IPixel - { - public Block8x8F Y; - - public Block8x8F Cb; - - public Block8x8F Cr; - - private RgbToYCbCrTables colorTables; - - private GenericBlock8x8 pixelBlock; - - private GenericBlock8x8 rgbBlock; - - public static YCbCrForwardConverter Create() - { - var result = default(YCbCrForwardConverter); - result.colorTables = RgbToYCbCrTables.Create(); - return result; - } - - public void Convert(IPixelSource pixels, int x, int y) - { - this.pixelBlock.LoadAndStretchEdges(pixels, x, y); - - Span rgbSpan = this.rgbBlock.AsSpanUnsafe(); - PixelOperations.Instance.ToRgb24(this.pixelBlock.AsSpanUnsafe(), rgbSpan, 64); - - ref float yBlockStart = ref Unsafe.As(ref this.Y); - ref float cbBlockStart = ref Unsafe.As(ref this.Cb); - ref float crBlockStart = ref Unsafe.As(ref this.Cr); - ref Rgb24 rgbStart = ref rgbSpan[0]; - - for (int i = 0; i < 64; i++) - { - ref Rgb24 c = ref Unsafe.Add(ref rgbStart, i); - - this.colorTables.ConvertPixelInto( - c.R, - c.G, - c.B, - ref Unsafe.Add(ref yBlockStart, i), - ref Unsafe.Add(ref cbBlockStart, i), - ref Unsafe.Add(ref crBlockStart, i) - ); - } - } - } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/YCbCrForwardConverter{TPixel}.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/YCbCrForwardConverter{TPixel}.cs new file mode 100644 index 0000000000..e3d7cd3e5a --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/YCbCrForwardConverter{TPixel}.cs @@ -0,0 +1,83 @@ +using System; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Formats.Jpeg.Common; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder +{ + /// + /// On-stack worker struct to efficiently encapsulate the TPixel -> Rgb24 -> YCbCr conversion chain of 8x8 pixel blocks. + /// + /// The pixel type to work on + internal struct YCbCrForwardConverter + where TPixel : struct, IPixel + { + /// + /// The Y component + /// + public Block8x8F Y; + + /// + /// The Cb component + /// + public Block8x8F Cb; + + /// + /// The Cr component + /// + public Block8x8F Cr; + + /// + /// The color conversion tables + /// + private RgbToYCbCrTables colorTables; + + /// + /// Temporal 8x8 block to hold TPixel data + /// + private GenericBlock8x8 pixelBlock; + + /// + /// Temporal RGB block + /// + private GenericBlock8x8 rgbBlock; + + public static YCbCrForwardConverter Create() + { + var result = default(YCbCrForwardConverter); + result.colorTables = RgbToYCbCrTables.Create(); + return result; + } + + /// + /// Converts a 8x8 image area inside 'pixels' at position (x,y) placing the result members of the structure (, , ) + /// + public void Convert(IPixelSource pixels, int x, int y) + { + this.pixelBlock.LoadAndStretchEdges(pixels, x, y); + + Span rgbSpan = this.rgbBlock.AsSpanUnsafe(); + PixelOperations.Instance.ToRgb24(this.pixelBlock.AsSpanUnsafe(), rgbSpan, 64); + + ref float yBlockStart = ref Unsafe.As(ref this.Y); + ref float cbBlockStart = ref Unsafe.As(ref this.Cb); + ref float crBlockStart = ref Unsafe.As(ref this.Cr); + ref Rgb24 rgbStart = ref rgbSpan[0]; + + for (int i = 0; i < 64; i++) + { + ref Rgb24 c = ref Unsafe.Add(ref rgbStart, i); + + this.colorTables.ConvertPixelInto( + c.R, + c.G, + c.B, + ref Unsafe.Add(ref yBlockStart, i), + ref Unsafe.Add(ref cbBlockStart, i), + ref Unsafe.Add(ref crBlockStart, i) + ); + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs index c8be8b02fe..d9ebd265cc 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs @@ -868,11 +868,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort this.Encode444(image); break; case JpegSubsample.Ratio420: - using (var pixels = image.Lock()) - { - this.Encode420(pixels); - } - + this.Encode420(image); break; } @@ -886,7 +882,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort /// /// The pixel format. /// The pixel accessor providing access to the image pixels. - private void Encode420(PixelAccessor pixels) + private void Encode420(Image pixels) where TPixel : struct, IPixel { // TODO: Need a JpegScanEncoder class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.) @@ -905,62 +901,54 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort ZigZag unzig = ZigZag.CreateUnzigTable(); + var pixelConverter = YCbCrForwardConverter.Create(); + // ReSharper disable once InconsistentNaming int prevDCY = 0, prevDCCb = 0, prevDCCr = 0; - fixed (RgbToYCbCrTables* tables = &rgbToYCbCrTables) + + for (int y = 0; y < pixels.Height; y += 16) { - using (PixelArea rgbBytes = new PixelArea(8, 8, ComponentOrder.Xyz)) + for (int x = 0; x < pixels.Width; x += 16) { - for (int y = 0; y < pixels.Height; y += 16) + for (int i = 0; i < 4; i++) { - for (int x = 0; x < pixels.Width; x += 16) - { - for (int i = 0; i < 4; i++) - { - int xOff = (i & 1) * 8; - int yOff = (i & 2) * 4; - - ToYCbCr( - pixels, - tables, - x + xOff, - y + yOff, - ref b, - ref Unsafe.AsRef(cbPtr + i), - ref Unsafe.AsRef(crPtr + i), - rgbBytes); - - prevDCY = this.WriteBlock( - QuantIndex.Luminance, - prevDCY, - &b, - &temp1, - &temp2, - &onStackLuminanceQuantTable, - unzig.Data); - } - - Block8x8F.Scale16X16To8X8(&b, cbPtr); - prevDCCb = this.WriteBlock( - QuantIndex.Chrominance, - prevDCCb, - &b, - &temp1, - &temp2, - &onStackChrominanceQuantTable, - unzig.Data); - - Block8x8F.Scale16X16To8X8(&b, crPtr); - prevDCCr = this.WriteBlock( - QuantIndex.Chrominance, - prevDCCr, - &b, - &temp1, - &temp2, - &onStackChrominanceQuantTable, - unzig.Data); - } + int xOff = (i & 1) * 8; + int yOff = (i & 2) * 4; + + pixelConverter.Convert(pixels.Frames.RootFrame, x + xOff, y + yOff); + + cbPtr[i] = pixelConverter.Cb; + crPtr[i] = pixelConverter.Cr; + + prevDCY = this.WriteBlock( + QuantIndex.Luminance, + prevDCY, + &pixelConverter.Y, + &temp1, + &temp2, + &onStackLuminanceQuantTable, + unzig.Data); } + + Block8x8F.Scale16X16To8X8(&b, cbPtr); + prevDCCb = this.WriteBlock( + QuantIndex.Chrominance, + prevDCCb, + &b, + &temp1, + &temp2, + &onStackChrominanceQuantTable, + unzig.Data); + + Block8x8F.Scale16X16To8X8(&b, crPtr); + prevDCCr = this.WriteBlock( + QuantIndex.Chrominance, + prevDCCr, + &b, + &temp1, + &temp2, + &onStackChrominanceQuantTable, + unzig.Data); } } } From 99320bb74fa964747af8b20e41dfb9bf8c8373e6 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 22 Feb 2018 20:55:55 +0100 Subject: [PATCH 090/151] fixing StyleCop errors --- .../DefaultInternalImageProcessorContext.cs | 5 +- .../Formats/Jpeg/Common/GenericBlock8x8.cs | 76 +++++++++---------- .../Encoder/YCbCrForwardConverter{TPixel}.cs | 3 +- .../Jpeg/GolangPort/Utils/OrigJpegUtils.cs | 3 +- 4 files changed, 42 insertions(+), 45 deletions(-) diff --git a/src/ImageSharp/DefaultInternalImageProcessorContext.cs b/src/ImageSharp/DefaultInternalImageProcessorContext.cs index d58f6236f9..5aba9ae69b 100644 --- a/src/ImageSharp/DefaultInternalImageProcessorContext.cs +++ b/src/ImageSharp/DefaultInternalImageProcessorContext.cs @@ -36,6 +36,9 @@ namespace SixLabors.ImageSharp } } + /// + public MemoryManager MemoryManager => this.source.GetConfiguration().MemoryManager; + /// public Image Apply() { @@ -74,7 +77,5 @@ namespace SixLabors.ImageSharp { return this.ApplyProcessor(processor, this.source.Bounds()); } - - public MemoryManager MemoryManager => this.source.GetConfiguration().MemoryManager; } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Common/GenericBlock8x8.cs b/src/ImageSharp/Formats/Jpeg/Common/GenericBlock8x8.cs index aefc86eb11..e20e850d74 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/GenericBlock8x8.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/GenericBlock8x8.cs @@ -1,19 +1,18 @@ using System; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; +// ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Formats.Jpeg.Common { - using System.Runtime.InteropServices; - /// /// A generic 8x8 block implementation, useful for manipulating custom 8x8 pixel data. /// [StructLayout(LayoutKind.Sequential)] - // ReSharper disable once InconsistentNaming internal unsafe partial struct GenericBlock8x8 where T : struct { @@ -21,6 +20,40 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common public const int SizeInBytes = Size * 3; + /// + /// FOR TESTING ONLY! + /// Gets or sets a value at the given index + /// + /// The index + /// The value + public T this[int idx] + { + get + { + ref T selfRef = ref Unsafe.As, T>(ref this); + return Unsafe.Add(ref selfRef, idx); + } + + set + { + ref T selfRef = ref Unsafe.As, T>(ref this); + Unsafe.Add(ref selfRef, idx) = value; + } + } + + /// + /// FOR TESTING ONLY! + /// Gets or sets a value in a row+coulumn of the 8x8 block + /// + /// The x position index in the row + /// The column index + /// The value + public T this[int x, int y] + { + get => this[(y * 8) + x]; + set => this[(y * 8) + x] = value; + } + public void LoadAndStretchEdges(IPixelSource source, int sourceX, int sourceY) where TPixel : struct, IPixel { @@ -52,8 +85,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common ref byte blockStart = ref Unsafe.As, byte>(ref this); ref byte imageStart = ref Unsafe.As( - ref Unsafe.Add(ref source.GetRowSpan(sourceY).DangerousGetPinnableReference(), sourceX) - ); + ref Unsafe.Add(ref source.GetRowSpan(sourceY).DangerousGetPinnableReference(), sourceX)); int blockRowSizeInBytes = 8 * Unsafe.SizeOf(); int imageRowSizeInBytes = source.Width * Unsafe.SizeOf(); @@ -93,39 +125,5 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common /// Only for on-stack instances! /// public Span AsSpanUnsafe() => new Span(Unsafe.AsPointer(ref this), Size); - - /// - /// FOR TESTING ONLY! - /// Gets or sets a value at the given index - /// - /// The index - /// The value - public T this[int idx] - { - get - { - ref T selfRef = ref Unsafe.As, T>(ref this); - return Unsafe.Add(ref selfRef, idx); - } - - set - { - ref T selfRef = ref Unsafe.As, T>(ref this); - Unsafe.Add(ref selfRef, idx) = value; - } - } - - /// - /// FOR TESTING ONLY! - /// Gets or sets a value in a row+coulumn of the 8x8 block - /// - /// The x position index in the row - /// The column index - /// The value - public T this[int x, int y] - { - get => this[(y * 8) + x]; - set => this[(y * 8) + x] = value; - } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/YCbCrForwardConverter{TPixel}.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/YCbCrForwardConverter{TPixel}.cs index e3d7cd3e5a..3c95a85080 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/YCbCrForwardConverter{TPixel}.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/YCbCrForwardConverter{TPixel}.cs @@ -75,8 +75,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder c.B, ref Unsafe.Add(ref yBlockStart, i), ref Unsafe.Add(ref cbBlockStart, i), - ref Unsafe.Add(ref crBlockStart, i) - ); + ref Unsafe.Add(ref crBlockStart, i)); } } } diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Utils/OrigJpegUtils.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Utils/OrigJpegUtils.cs index caa7014a4b..ef52f6af38 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Utils/OrigJpegUtils.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Utils/OrigJpegUtils.cs @@ -3,12 +3,11 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Formats.Jpeg.Common; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Utils { - using SixLabors.ImageSharp.Formats.Jpeg.Common; - /// /// Jpeg specific utilities and extension methods /// From c64374bbe7c0cf62910defd2e5c15584bb3d8c63 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 22 Feb 2018 21:02:01 +0100 Subject: [PATCH 091/151] cleanup --- .../Jpeg/GolangPort/JpegEncoderCore.cs | 59 ---------- .../Jpeg/GolangPort/Utils/OrigJpegUtils.cs | 98 ----------------- src/ImageSharp/ImageSharp.csproj | 3 + .../Formats/Jpg/JpegUtilsTests.cs | 102 ------------------ .../Jpg/ReferenceImplementationsTests.cs | 2 - ...ceImplementations.LLM_FloatingPoint_DCT.cs | 1 - 6 files changed, 3 insertions(+), 262 deletions(-) delete mode 100644 src/ImageSharp/Formats/Jpeg/GolangPort/Utils/OrigJpegUtils.cs delete mode 100644 tests/ImageSharp.Tests/Formats/Jpg/JpegUtilsTests.cs diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs index d9ebd265cc..ba40ef72b8 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs @@ -7,7 +7,6 @@ using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Formats.Jpeg.Common; using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components; using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder; -using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.MetaData.Profiles.Exif; using SixLabors.ImageSharp.MetaData.Profiles.Icc; @@ -283,64 +282,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort } } - /// - /// Converts the 8x8 region of the image whose top-left corner is x,y to its YCbCr values. - /// - /// The pixel format. - /// The pixel accessor. - /// The reference to the tables instance. - /// The x-position within the image. - /// The y-position within the image. - /// The luminance block. - /// The red chroma block. - /// The blue chroma block. - /// Temporal provided by the caller - private static void ToYCbCr( - PixelAccessor pixels, - RgbToYCbCrTables* tables, - int x, - int y, - ref Block8x8F yBlock, - ref Block8x8F cbBlock, - ref Block8x8F crBlock, - PixelArea rgbBytes) - where TPixel : struct, IPixel - { - ref float yBlockStart = ref Unsafe.As(ref yBlock); - ref float cbBlockStart = ref Unsafe.As(ref cbBlock); - ref float crBlockStart = ref Unsafe.As(ref crBlock); - - pixels.CopyRGBBytesStretchedTo(rgbBytes, y, x); - - ref byte data0 = ref rgbBytes.Bytes[0]; - int dataIdx = 0; - - for (int j = 0; j < 8; j++) - { - int j8 = j * 8; - for (int i = 0; i < 8; i++) - { - // Convert returned bytes into the YCbCr color space. Assume RGBA - int r = Unsafe.Add(ref data0, dataIdx); - int g = Unsafe.Add(ref data0, dataIdx + 1); - int b = Unsafe.Add(ref data0, dataIdx + 2); - - int index = j8 + i; - - // RgbToYCbCrTables.Rgb2YCbCr(tables, yBlockRaw, cbBlockRaw, crBlockRaw, index, r, g, b); - tables->ConvertPixelInto( - r, - g, - b, - ref Unsafe.Add(ref yBlockStart, index), - ref Unsafe.Add(ref cbBlockStart, index), - ref Unsafe.Add(ref crBlockStart, index)); - - dataIdx += 3; - } - } - } - /// /// Emits the least significant count of bits of bits to the bit-stream. /// The precondition is bits diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Utils/OrigJpegUtils.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Utils/OrigJpegUtils.cs deleted file mode 100644 index ef52f6af38..0000000000 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Utils/OrigJpegUtils.cs +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using SixLabors.ImageSharp.Formats.Jpeg.Common; -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Utils -{ - /// - /// Jpeg specific utilities and extension methods - /// - internal static class OrigJpegUtils - { - /// - /// Stack only TPixel -> Rgb24 conversion method on 8x8 blocks. - /// - public static void ConvertToRgbUnsafe(ref GenericBlock8x8 source, ref GenericBlock8x8 dest) - where TPixel : struct, IPixel - { - PixelOperations.Instance.ToRgb24(source.AsSpanUnsafe(), dest.AsSpanUnsafe(), 64); - } - - /// - /// Copy a region of an image into dest. De "outlier" area will be stretched out with pixels on the right and bottom of the image. - /// - /// The pixel type - /// The input pixel acessor - /// The destination - /// Starting Y coord - /// Starting X coord - public static void CopyRGBBytesStretchedTo( - this PixelAccessor pixels, - PixelArea dest, - int sourceY, - int sourceX) - where TPixel : struct, IPixel - { - pixels.SafeCopyTo(dest, sourceY, sourceX); - int stretchFromX = pixels.Width - sourceX; - int stretchFromY = pixels.Height - sourceY; - StretchPixels(dest, stretchFromX, stretchFromY); - } - - // Nothing to stretch if (fromX, fromY) is outside the area, or is at (0,0) - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool IsInvalidStretchStartingPosition(PixelArea area, int fromX, int fromY) - where TPixel : struct, IPixel - { - return fromX <= 0 || fromY <= 0 || fromX >= area.Width || fromY >= area.Height; - } - - private static void StretchPixels(PixelArea area, int fromX, int fromY) - where TPixel : struct, IPixel - { - if (IsInvalidStretchStartingPosition(area, fromX, fromY)) - { - return; - } - - for (int y = 0; y < fromY; y++) - { - ref RGB24 ptrBase = ref GetRowStart(area, y); - - for (int x = fromX; x < area.Width; x++) - { - // Copy the left neighbour pixel to the current one - Unsafe.Add(ref ptrBase, x) = Unsafe.Add(ref ptrBase, x - 1); - } - } - - for (int y = fromY; y < area.Height; y++) - { - ref RGB24 currBase = ref GetRowStart(area, y); - ref RGB24 prevBase = ref GetRowStart(area, y - 1); - - for (int x = 0; x < area.Width; x++) - { - // Copy the top neighbour pixel to the current one - Unsafe.Add(ref currBase, x) = Unsafe.Add(ref prevBase, x); - } - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ref RGB24 GetRowStart(PixelArea area, int y) - where TPixel : struct, IPixel - { - return ref Unsafe.As(ref area.GetRowSpan(y).DangerousGetPinnableReference()); - } - - [StructLayout(LayoutKind.Sequential, Size = 3)] - private struct RGB24 - { - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index 8b89499ea7..833b9b96d7 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -127,4 +127,7 @@ PorterDuffFunctions.Generated.tt + + + \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilsTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilsTests.cs deleted file mode 100644 index 6fe6a9bfde..0000000000 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilsTests.cs +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Numerics; -using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Utils; -using SixLabors.ImageSharp.PixelFormats; -using Xunit; - -// ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Tests.Formats.Jpg -{ - public class JpegUtilsTests - { - public static Image CreateTestImage() - where TPixel : struct, IPixel - { - var image = new Image(10, 10); - using (PixelAccessor pixels = image.Lock()) - { - for (int i = 0; i < 10; i++) - { - for (int j = 0; j < 10; j++) - { - var v = new Vector4(i / 10f, j / 10f, 0, 1); - - var color = default(TPixel); - color.PackFromVector4(v); - - pixels[i, j] = color; - } - } - } - - return image; - } - - [Theory] - [WithMemberFactory(nameof(CreateTestImage), PixelTypes.Rgba32| PixelTypes.Rgba32 | PixelTypes.Argb32)] - public void CopyStretchedRGBTo_FromOrigo(TestImageProvider provider) - where TPixel : struct, IPixel - { - using (Image src = provider.GetImage()) - using (Image dest = new Image(8,8)) - using (PixelArea area = new PixelArea(8, 8, ComponentOrder.Xyz)) - using (PixelAccessor s = src.Lock()) - using (PixelAccessor d = dest.Lock()) - { - s.CopyRGBBytesStretchedTo(area, 0, 0); - d.CopyFrom(area, 0, 0); - - Assert.Equal(s[0, 0], d[0, 0]); - Assert.Equal(s[7, 0], d[7, 0]); - Assert.Equal(s[0, 7], d[0, 7]); - Assert.Equal(s[7, 7], d[7, 7]); - } - - } - - [Theory] - [WithMemberFactory(nameof(CreateTestImage), PixelTypes.Rgba32| PixelTypes.Rgba32 | PixelTypes.Argb32)] - public void CopyStretchedRGBTo_WithOffset(TestImageProvider provider) - where TPixel : struct, IPixel - { - using (Image src = provider.GetImage()) - using (PixelArea area = new PixelArea(8, 8, ComponentOrder.Xyz)) - using (Image dest = new Image(8, 8)) - using (PixelAccessor s = src.Lock()) - using (PixelAccessor d = dest.Lock()) - { - s.CopyRGBBytesStretchedTo(area, 7, 6); - d.CopyFrom(area, 0, 0); - - Assert.Equal(s[6, 7], d[0, 0]); - Assert.Equal(s[6, 8], d[0, 1]); - Assert.Equal(s[7, 8], d[1, 1]); - - Assert.Equal(s[6, 9], d[0, 2]); - Assert.Equal(s[6, 9], d[0, 3]); - Assert.Equal(s[6, 9], d[0, 7]); - - Assert.Equal(s[7, 9], d[1, 2]); - Assert.Equal(s[7, 9], d[1, 3]); - Assert.Equal(s[7, 9], d[1, 7]); - - Assert.Equal(s[9, 9], d[3, 2]); - Assert.Equal(s[9, 9], d[3, 3]); - Assert.Equal(s[9, 9], d[3, 7]); - - Assert.Equal(s[9, 7], d[3, 0]); - Assert.Equal(s[9, 7], d[4, 0]); - Assert.Equal(s[9, 7], d[7, 0]); - - Assert.Equal(s[9, 9], d[3, 2]); - Assert.Equal(s[9, 9], d[4, 2]); - Assert.Equal(s[9, 9], d[7, 2]); - - Assert.Equal(s[9, 9], d[4, 3]); - Assert.Equal(s[9, 9], d[7, 7]); - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.cs index 26ec454f91..0276e17085 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Utils; - using Xunit.Abstractions; // ReSharper disable InconsistentNaming diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.LLM_FloatingPoint_DCT.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.LLM_FloatingPoint_DCT.cs index ef9a73d12d..37d42eb72f 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.LLM_FloatingPoint_DCT.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.LLM_FloatingPoint_DCT.cs @@ -6,7 +6,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Formats.Jpeg.Common; - using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Utils; using Xunit.Abstractions; From 70893d22ad1f7f3ac6762efd43d34e2c221c5d8d Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 22 Feb 2018 21:34:50 +0100 Subject: [PATCH 092/151] replaced some of the PixelArea usages in bmp decoder --- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 25 +++++++++++++---- src/ImageSharp/Formats/Bmp/BmpEncoder.cs | 3 ++- src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs | 27 +++++++++++++++---- src/ImageSharp/Memory/BufferExtensions.cs | 12 +++++++++ .../Memory/MemoryManagerExtensions.cs | 18 +++++++++++++ .../Formats/Bmp/BmpEncoderTests.cs | 2 -- 6 files changed, 74 insertions(+), 13 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 201c041a24..0c77fc482f 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -69,7 +69,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// private BmpInfoHeader infoHeader; - private Configuration configuration; + private readonly Configuration configuration; + + private readonly MemoryManager memoryManager; /// /// Initializes a new instance of the class. @@ -79,6 +81,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp public BmpDecoderCore(Configuration configuration, IBmpDecoderOptions options) { this.configuration = configuration; + this.memoryManager = configuration.MemoryManager; } /// @@ -432,16 +435,28 @@ namespace SixLabors.ImageSharp.Formats.Bmp where TPixel : struct, IPixel { int padding = CalculatePadding(width, 3); - using (var row = new PixelArea(width, ComponentOrder.Zyx, padding)) + + using (IManagedByteBuffer row = this.memoryManager.AllocatePaddedPixelRowBuffer(width, 3, padding)) { for (int y = 0; y < height; y++) { - row.Read(this.currentStream); - + this.currentStream.Read(row); int newY = Invert(y, height, inverted); - pixels.CopyFrom(row, newY); + Span pixelSpan = pixels.GetRowSpan(newY); + PixelOperations.Instance.PackFromBgr24Bytes(row.Span, pixelSpan, width); } } + + //using (var row = new PixelArea(width, ComponentOrder.Zyx, padding)) + //{ + // for (int y = 0; y < height; y++) + // { + // row.Read(this.currentStream); + + // int newY = Invert(y, height, inverted); + // pixels.CopyFrom(row, newY); + // } + //} } /// diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoder.cs b/src/ImageSharp/Formats/Bmp/BmpEncoder.cs index 366afceb5f..d80e43c63b 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoder.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoder.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.IO; +using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Bmp @@ -23,7 +24,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp public void Encode(Image image, Stream stream) where TPixel : struct, IPixel { - var encoder = new BmpEncoderCore(this); + var encoder = new BmpEncoderCore(this, image.GetMemoryManager()); encoder.Encode(image, stream); } } diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index d34d170847..26d16cea32 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -8,6 +8,8 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Bmp { + using SixLabors.ImageSharp.Memory; + /// /// Image encoder for writing an image to a stream as a Windows bitmap. /// @@ -21,14 +23,18 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// /// Gets or sets the number of bits per pixel. /// - private BmpBitsPerPixel bitsPerPixel; + private readonly BmpBitsPerPixel bitsPerPixel; + + private readonly MemoryManager memoryManager; /// /// Initializes a new instance of the class. /// /// The encoder options - public BmpEncoderCore(IBmpEncoderOptions options) + /// The memory manager + public BmpEncoderCore(IBmpEncoderOptions options, MemoryManager memoryManager) { + this.memoryManager = memoryManager; this.bitsPerPixel = options.BitsPerPixel; } @@ -173,14 +179,25 @@ namespace SixLabors.ImageSharp.Formats.Bmp private void Write24Bit(EndianBinaryWriter writer, PixelAccessor pixels) where TPixel : struct, IPixel { - using (PixelArea row = new PixelArea(pixels.Width, ComponentOrder.Zyx, this.padding)) + using (IManagedByteBuffer row = + this.memoryManager.AllocatePaddedPixelRowBuffer(pixels.Width, 3, this.padding)) { for (int y = pixels.Height - 1; y >= 0; y--) { - pixels.CopyTo(row, y); - writer.Write(row.Bytes, 0, row.Length); + Span pixelSpan = pixels.GetRowSpan(y); + PixelOperations.Instance.ToBgr24Bytes(pixelSpan, row.Span, pixelSpan.Length); + writer.Write(row.Array, 0, row.Length()); } } + + //using (PixelArea row = new PixelArea(pixels.Width, ComponentOrder.Zyx, this.padding)) + //{ + // for (int y = pixels.Height - 1; y >= 0; y--) + // { + // pixels.CopyTo(row, y); + // writer.Write(row.Bytes, 0, row.Length); + // } + //} } } } diff --git a/src/ImageSharp/Memory/BufferExtensions.cs b/src/ImageSharp/Memory/BufferExtensions.cs index b863dfc9aa..882b8875fc 100644 --- a/src/ImageSharp/Memory/BufferExtensions.cs +++ b/src/ImageSharp/Memory/BufferExtensions.cs @@ -6,6 +6,8 @@ using System.Runtime.CompilerServices; namespace SixLabors.ImageSharp.Memory { + using System.IO; + internal static class BufferExtensions { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -53,5 +55,15 @@ namespace SixLabors.ImageSharp.Memory public static ref T DangerousGetPinnableReference(this IBuffer buffer) where T : struct => ref buffer.Span.DangerousGetPinnableReference(); + + public static void Read(this Stream stream, IManagedByteBuffer buffer) + { + stream.Read(buffer.Array, 0, buffer.Length()); + } + + public static void Write(this Stream stream, IManagedByteBuffer buffer) + { + stream.Write(buffer.Array, 0, buffer.Length()); + } } } \ No newline at end of file diff --git a/src/ImageSharp/Memory/MemoryManagerExtensions.cs b/src/ImageSharp/Memory/MemoryManagerExtensions.cs index b7fcaf4b36..3511d20390 100644 --- a/src/ImageSharp/Memory/MemoryManagerExtensions.cs +++ b/src/ImageSharp/Memory/MemoryManagerExtensions.cs @@ -51,5 +51,23 @@ public static Buffer2D AllocateClean2D(this MemoryManager memoryManager, int width, int height) where T : struct => Allocate2D(memoryManager, width, height, true); + + /// + /// Allocates padded buffers for BMP encoder/decoder. (Replacing old PixelRow/PixelArea) + /// + /// The + /// Pixel count in the row + /// The pixel size in bytes, eg. 3 for RGB + /// The padding + /// A + public static IManagedByteBuffer AllocatePaddedPixelRowBuffer( + this MemoryManager memoryManager, + int width, + int pixelSizeInBytes, + int paddingInBytes) + { + int length = (width * pixelSizeInBytes) + paddingInBytes; + return memoryManager.AllocateManagedByteBuffer(length); + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs index 362df2be68..a4ed0c0fd2 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs @@ -15,8 +15,6 @@ namespace SixLabors.ImageSharp.Tests public class BmpEncoderTests : FileTestBase { - private const PixelTypes PixelTypesToTest = PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.Rgb24; - public static readonly TheoryData BitsPerPixel = new TheoryData { From a61707661c0b2303411039011b955288633af9fe Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 23 Feb 2018 00:52:44 +0100 Subject: [PATCH 093/151] removed PixelArea! --- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 20 +- src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs | 24 +- src/ImageSharp/Formats/Gif/GifDecoderCore.cs | 47 +-- src/ImageSharp/Image/ImageFrame{TPixel}.cs | 42 +-- src/ImageSharp/Image/PixelAccessor{TPixel}.cs | 303 ------------------ src/ImageSharp/Image/PixelArea{TPixel}.cs | 212 ------------ src/ImageSharp/Memory/BufferArea{T}.cs | 31 ++ .../Image/PixelAccessorTests.cs | 246 -------------- .../Memory/BufferAreaTests.cs | 33 ++ 9 files changed, 125 insertions(+), 833 deletions(-) delete mode 100644 src/ImageSharp/Image/PixelArea{TPixel}.cs delete mode 100644 tests/ImageSharp.Tests/Image/PixelAccessorTests.cs diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 0c77fc482f..953a6fcb23 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -446,17 +446,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp PixelOperations.Instance.PackFromBgr24Bytes(row.Span, pixelSpan, width); } } - - //using (var row = new PixelArea(width, ComponentOrder.Zyx, padding)) - //{ - // for (int y = 0; y < height; y++) - // { - // row.Read(this.currentStream); - - // int newY = Invert(y, height, inverted); - // pixels.CopyFrom(row, newY); - // } - //} } /// @@ -471,14 +460,15 @@ namespace SixLabors.ImageSharp.Formats.Bmp where TPixel : struct, IPixel { int padding = CalculatePadding(width, 4); - using (var row = new PixelArea(width, ComponentOrder.Zyxw, padding)) + + using (IManagedByteBuffer row = this.memoryManager.AllocatePaddedPixelRowBuffer(width, 4, padding)) { for (int y = 0; y < height; y++) { - row.Read(this.currentStream); - + this.currentStream.Read(row); int newY = Invert(y, height, inverted); - pixels.CopyFrom(row, newY); + Span pixelSpan = pixels.GetRowSpan(newY); + PixelOperations.Instance.PackFromBgra32Bytes(row.Span, pixelSpan, width); } } } diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index 26d16cea32..5b85e9cb9e 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -151,6 +151,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp } } + private IManagedByteBuffer AllocateRow(int width, int bytesPerPixel) + { + return this.memoryManager.AllocatePaddedPixelRowBuffer(width, bytesPerPixel, this.padding); + } + /// /// Writes the 32bit color palette to the stream. /// @@ -160,12 +165,13 @@ namespace SixLabors.ImageSharp.Formats.Bmp private void Write32Bit(EndianBinaryWriter writer, PixelAccessor pixels) where TPixel : struct, IPixel { - using (PixelArea row = new PixelArea(pixels.Width, ComponentOrder.Zyxw, this.padding)) + using (IManagedByteBuffer row = this.AllocateRow(pixels.Width, 4)) { for (int y = pixels.Height - 1; y >= 0; y--) { - pixels.CopyTo(row, y); - writer.Write(row.Bytes, 0, row.Length); + Span pixelSpan = pixels.GetRowSpan(y); + PixelOperations.Instance.ToBgra32Bytes(pixelSpan, row.Span, pixelSpan.Length); + writer.Write(row.Array, 0, row.Length()); } } } @@ -179,8 +185,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp private void Write24Bit(EndianBinaryWriter writer, PixelAccessor pixels) where TPixel : struct, IPixel { - using (IManagedByteBuffer row = - this.memoryManager.AllocatePaddedPixelRowBuffer(pixels.Width, 3, this.padding)) + using (IManagedByteBuffer row = this.AllocateRow(pixels.Width, 3)) { for (int y = pixels.Height - 1; y >= 0; y--) { @@ -189,15 +194,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp writer.Write(row.Array, 0, row.Length()); } } - - //using (PixelArea row = new PixelArea(pixels.Width, ComponentOrder.Zyx, this.padding)) - //{ - // for (int y = pixels.Height - 1; y >= 0; y--) - // { - // pixels.CopyTo(row, y); - // writer.Write(row.Bytes, 0, row.Length); - // } - //} } } } diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index da92665be5..8fd7f04d28 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -542,28 +542,31 @@ namespace SixLabors.ImageSharp.Formats.Gif return; } - // Optimization for when the size of the frame is the same as the image size. - if (this.restoreArea.Value.Width == imageWidth && - this.restoreArea.Value.Height == imageHeight) - { - using (PixelAccessor pixelAccessor = frame.Lock()) - { - pixelAccessor.Reset(); - } - } - else - { - using (var emptyRow = new PixelArea(this.restoreArea.Value.Width, ComponentOrder.Xyzw)) - { - using (PixelAccessor pixelAccessor = frame.Lock()) - { - for (int y = this.restoreArea.Value.Top; y < this.restoreArea.Value.Top + this.restoreArea.Value.Height; y++) - { - pixelAccessor.CopyFrom(emptyRow, y, this.restoreArea.Value.Left); - } - } - } - } + BufferArea pixelArea = frame.PixelBuffer.GetArea(this.restoreArea.Value); + pixelArea.Clear(); + + //if (this.restoreArea.Value.Width == imageWidth && + // this.restoreArea.Value.Height == imageHeight) + //{ + // using (PixelAccessor pixelAccessor = frame.Lock()) + // { + // pixelAccessor.Reset(); + // } + //} + //else + //{ + + // using (var emptyRow = new PixelArea(this.restoreArea.Value.Width, ComponentOrder.Xyzw)) + // { + // using (PixelAccessor pixelAccessor = frame.Lock()) + // { + // for (int y = this.restoreArea.Value.Top; y < this.restoreArea.Value.Top + this.restoreArea.Value.Height; y++) + // { + // pixelAccessor.CopyFrom(emptyRow, y, this.restoreArea.Value.Left); + // } + // } + // } + //} this.restoreArea = null; } diff --git a/src/ImageSharp/Image/ImageFrame{TPixel}.cs b/src/ImageSharp/Image/ImageFrame{TPixel}.cs index 5170522de3..163c7a3b08 100644 --- a/src/ImageSharp/Image/ImageFrame{TPixel}.cs +++ b/src/ImageSharp/Image/ImageFrame{TPixel}.cs @@ -21,11 +21,6 @@ namespace SixLabors.ImageSharp public sealed class ImageFrame : IPixelSource, IDisposable where TPixel : struct, IPixel { - /// - /// The image pixels. Not private as Buffer2D requires an array in its constructor. - /// - private Buffer2D pixelBuffer; - private bool isDisposed; /// @@ -54,7 +49,7 @@ namespace SixLabors.ImageSharp Guard.NotNull(metaData, nameof(metaData)); this.MemoryManager = memoryManager; - this.pixelBuffer = memoryManager.AllocateClean2D(width, height); + this.PixelBuffer = memoryManager.AllocateClean2D(width, height); this.MetaData = metaData; } @@ -77,8 +72,8 @@ namespace SixLabors.ImageSharp internal ImageFrame(MemoryManager memoryManager, ImageFrame source) { this.MemoryManager = memoryManager; - this.pixelBuffer = memoryManager.Allocate2D(source.pixelBuffer.Width, source.pixelBuffer.Height); - source.pixelBuffer.Span.CopyTo(this.pixelBuffer.Span); + this.PixelBuffer = memoryManager.Allocate2D(source.PixelBuffer.Width, source.PixelBuffer.Height); + source.PixelBuffer.Span.CopyTo(this.PixelBuffer.Span); this.MetaData = source.MetaData.Clone(); } @@ -87,18 +82,23 @@ namespace SixLabors.ImageSharp /// public MemoryManager MemoryManager { get; } + /// + /// Gets the image pixels. Not private as Buffer2D requires an array in its constructor. + /// + internal Buffer2D PixelBuffer { get; private set; } + /// - Buffer2D IPixelSource.PixelBuffer => this.pixelBuffer; + Buffer2D IPixelSource.PixelBuffer => this.PixelBuffer; /// /// Gets the width. /// - public int Width => this.pixelBuffer.Width; + public int Width => this.PixelBuffer.Width; /// /// Gets the height. /// - public int Height => this.pixelBuffer.Height; + public int Height => this.PixelBuffer.Height; /// /// Gets the meta data of the frame. @@ -116,13 +116,13 @@ namespace SixLabors.ImageSharp [MethodImpl(MethodImplOptions.AggressiveInlining)] get { - return this.pixelBuffer[x, y]; + return this.PixelBuffer[x, y]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] set { - this.pixelBuffer[x, y] = value; + this.PixelBuffer[x, y] = value; } } @@ -135,7 +135,7 @@ namespace SixLabors.ImageSharp [MethodImpl(MethodImplOptions.AggressiveInlining)] internal ref TPixel GetPixelReference(int x, int y) { - return ref this.pixelBuffer[x, y]; + return ref this.PixelBuffer[x, y]; } /// @@ -168,8 +168,8 @@ namespace SixLabors.ImageSharp 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; + Buffer2D newPixels = pixelSource.SwapBufferOwnership(this.PixelBuffer); + this.PixelBuffer = newPixels; } /// @@ -180,9 +180,9 @@ namespace SixLabors.ImageSharp { Guard.NotNull(pixelSource, nameof(pixelSource)); - Buffer2D temp = this.pixelBuffer; - this.pixelBuffer = pixelSource.pixelBuffer; - pixelSource.pixelBuffer = temp; + Buffer2D temp = this.PixelBuffer; + this.PixelBuffer = pixelSource.PixelBuffer; + pixelSource.PixelBuffer = temp; } /// @@ -195,8 +195,8 @@ namespace SixLabors.ImageSharp return; } - this.pixelBuffer?.Dispose(); - this.pixelBuffer = null; + this.PixelBuffer?.Dispose(); + this.PixelBuffer = null; // Note disposing is done. this.isDisposed = true; diff --git a/src/ImageSharp/Image/PixelAccessor{TPixel}.cs b/src/ImageSharp/Image/PixelAccessor{TPixel}.cs index 3a894fb9ad..f1c8531cc4 100644 --- a/src/ImageSharp/Image/PixelAccessor{TPixel}.cs +++ b/src/ImageSharp/Image/PixelAccessor{TPixel}.cs @@ -158,65 +158,6 @@ namespace SixLabors.ImageSharp this.PixelBuffer.Buffer.Clear(); } - /// - /// Copy an area of pixels to the image. - /// - /// The area. - /// The target row index. - /// The target column index. - /// - /// Thrown when an unsupported component order value is passed. - /// - internal void CopyFrom(PixelArea area, int targetY, int targetX = 0) - { - this.CheckCoordinates(area, targetX, targetY); - - this.CopyFrom(area, targetX, targetY, area.Width, area.Height); - } - - /// - /// Copy pixels from the image to an area of pixels. - /// - /// The area. - /// The source row index. - /// The source column index. - /// - /// Thrown when an unsupported component order value is passed. - /// - internal void CopyTo(PixelArea area, int sourceY, int sourceX = 0) - { - this.CheckCoordinates(area, sourceX, sourceY); - - this.CopyTo(area, sourceX, sourceY, area.Width, area.Height); - } - - /// - /// Copy pixels from the image to an area of pixels. This method will make sure that the pixels - /// that are copied are within the bounds of the image. - /// - /// The area. - /// The source row index. - /// The source column index. - /// - /// Thrown when an unsupported component order value is passed. - /// - internal void SafeCopyTo(PixelArea area, int sourceY, int sourceX = 0) - { - int width = Math.Min(area.Width, this.Width - sourceX); - if (width < 1) - { - return; - } - - int height = Math.Min(area.Height, this.Height - sourceY); - if (height < 1) - { - return; - } - - this.CopyTo(area, sourceX, sourceY, width, height); - } - /// /// Sets the pixel buffer in an unsafe manner. This should not be used unless you know what its doing!!! /// @@ -239,161 +180,6 @@ namespace SixLabors.ImageSharp SpanHelper.Copy(this.PixelBuffer.Span, target.PixelBuffer.Span); } - /// - /// Copies from an area in format. - /// - /// The area. - /// The target column index. - /// The target row index. - /// The width. - /// The height. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void CopyFromZyx(PixelArea area, int targetX, int targetY, int width, int height) - { - for (int y = 0; y < height; y++) - { - Span source = area.GetRowSpan(y); - Span destination = this.GetRowSpan(targetX, targetY + y); - - Operations.PackFromBgr24Bytes(source, destination, width); - } - } - - /// - /// Copies from an area in format. - /// - /// The area. - /// The target column index. - /// The target row index. - /// The width. - /// The height. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void CopyFromZyxw(PixelArea area, int targetX, int targetY, int width, int height) - { - for (int y = 0; y < height; y++) - { - Span source = area.GetRowSpan(y); - Span destination = this.GetRowSpan(targetX, targetY + y); - - Operations.PackFromBgra32Bytes(source, destination, width); - } - } - - /// - /// Copies from an area in format. - /// - /// The area. - /// The target column index. - /// The target row index. - /// The width. - /// The height. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void CopyFromXyz(PixelArea area, int targetX, int targetY, int width, int height) - { - for (int y = 0; y < height; y++) - { - Span source = area.GetRowSpan(y); - Span destination = this.GetRowSpan(targetX, targetY + y); - - Operations.PackFromRgb24Bytes(source, destination, width); - } - } - - /// - /// Copies from an area in format. - /// - /// The area. - /// The target column index. - /// The target row index. - /// The width. - /// The height. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void CopyFromXyzw(PixelArea area, int targetX, int targetY, int width, int height) - { - for (int y = 0; y < height; y++) - { - Span source = area.GetRowSpan(y); - Span destination = this.GetRowSpan(targetX, targetY + y); - Operations.PackFromRgba32Bytes(source, destination, width); - } - } - - /// - /// Copies to an area in format. - /// - /// The row. - /// The source column index. - /// The source row index. - /// The width. - /// The height. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void CopyToZyx(PixelArea area, int sourceX, int sourceY, int width, int height) - { - for (int y = 0; y < height; y++) - { - Span source = this.GetRowSpan(sourceX, sourceY + y); - Span destination = area.GetRowSpan(y); - Operations.ToBgr24Bytes(source, destination, width); - } - } - - /// - /// Copies to an area in format. - /// - /// The row. - /// The source column index. - /// The source row index. - /// The width. - /// The height. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void CopyToZyxw(PixelArea area, int sourceX, int sourceY, int width, int height) - { - for (int y = 0; y < height; y++) - { - Span source = this.GetRowSpan(sourceX, sourceY + y); - Span destination = area.GetRowSpan(y); - Operations.ToBgra32Bytes(source, destination, width); - } - } - - /// - /// Copies to an area in format. - /// - /// The row. - /// The source column index. - /// The source row index. - /// The width. - /// The height. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void CopyToXyz(PixelArea area, int sourceX, int sourceY, int width, int height) - { - for (int y = 0; y < height; y++) - { - Span source = this.GetRowSpan(sourceX, sourceY + y); - Span destination = area.GetRowSpan(y); - Operations.ToRgb24Bytes(source, destination, width); - } - } - - /// - /// Copies to an area in format. - /// - /// The row. - /// The source column index. - /// The source row index. - /// The width. - /// The height. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void CopyToXyzw(PixelArea area, int sourceX, int sourceY, int width, int height) - { - for (int y = 0; y < height; y++) - { - Span source = this.GetRowSpan(sourceX, sourceY + y); - Span destination = area.GetRowSpan(y); - Operations.ToRgba32Bytes(source, destination, width); - } - } - /// /// Sets the pixel buffer in an unsafe manor this should not be used unless you know what its doing!!! /// @@ -410,95 +196,6 @@ namespace SixLabors.ImageSharp this.RowStride = this.Width * this.PixelSize; } - /// - /// Copy an area of pixels to the image. - /// - /// The area. - /// The target column index. - /// The target row index. - /// The width of the area to copy. - /// The height of the area to copy. - /// - /// Thrown when an unsupported component order value is passed. - /// - private void CopyFrom(PixelArea area, int targetX, int targetY, int width, int height) - { - switch (area.ComponentOrder) - { - case ComponentOrder.Zyx: - this.CopyFromZyx(area, targetX, targetY, width, height); - break; - case ComponentOrder.Zyxw: - this.CopyFromZyxw(area, targetX, targetY, width, height); - break; - case ComponentOrder.Xyz: - this.CopyFromXyz(area, targetX, targetY, width, height); - break; - case ComponentOrder.Xyzw: - this.CopyFromXyzw(area, targetX, targetY, width, height); - break; - default: - throw new NotSupportedException(); - } - } - - /// - /// Copy pixels from the image to an area of pixels. - /// - /// The area. - /// The source column index. - /// The source row index. - /// The width of the area to copy. - /// The height of the area to copy. - /// - /// Thrown when an unsupported component order value is passed. - /// - private void CopyTo(PixelArea area, int sourceX, int sourceY, int width, int height) - { - switch (area.ComponentOrder) - { - case ComponentOrder.Zyx: - this.CopyToZyx(area, sourceX, sourceY, width, height); - break; - case ComponentOrder.Zyxw: - this.CopyToZyxw(area, sourceX, sourceY, width, height); - break; - case ComponentOrder.Xyz: - this.CopyToXyz(area, sourceX, sourceY, width, height); - break; - case ComponentOrder.Xyzw: - this.CopyToXyzw(area, sourceX, sourceY, width, height); - break; - default: - throw new NotSupportedException(); - } - } - - /// - /// Checks that the given area and offset are within the bounds of the image. - /// - /// The area. - /// 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 dimensions are not within the bounds of the image. - /// - [Conditional("DEBUG")] - private void CheckCoordinates(PixelArea area, int x, int y) - { - int width = Math.Min(area.Width, this.Width - x); - if (width < 1) - { - throw new ArgumentOutOfRangeException(nameof(width), width, "Invalid area size specified."); - } - - int height = Math.Min(area.Height, this.Height - y); - if (height < 1) - { - throw new ArgumentOutOfRangeException(nameof(height), height, "Invalid area size specified."); - } - } - /// /// Checks the coordinates to ensure they are within bounds. /// diff --git a/src/ImageSharp/Image/PixelArea{TPixel}.cs b/src/ImageSharp/Image/PixelArea{TPixel}.cs deleted file mode 100644 index 7648017222..0000000000 --- a/src/ImageSharp/Image/PixelArea{TPixel}.cs +++ /dev/null @@ -1,212 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Diagnostics; -using System.IO; -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp -{ - /// - /// Represents an area of generic pixels. - /// - /// The pixel format. - internal sealed class PixelArea : IDisposable - where TPixel : struct, IPixel - { - /// - /// 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; - - /// - /// The underlying buffer containing the raw pixel data. - /// - private readonly IManagedByteBuffer byteBuffer; - - /// - /// Initializes a new instance of the class. - /// - /// The width. - /// The component order. - public PixelArea(int width, ComponentOrder componentOrder) - : this(width, 1, componentOrder, 0) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The width. - /// The component order. - /// The number of bytes to pad each row. - public PixelArea(int width, ComponentOrder componentOrder, int padding) - : this(width, 1, componentOrder, padding) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The width. - /// The height. - /// The component order. - public PixelArea(int width, int height, ComponentOrder componentOrder) - : this(width, height, componentOrder, 0) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The width. - /// The height. - /// The component order. - /// The number of bytes to pad each row. - public PixelArea(int width, int height, ComponentOrder componentOrder, int padding) - { - this.Width = width; - this.Height = height; - this.ComponentOrder = componentOrder; - this.RowStride = (width * GetComponentCount(componentOrder)) + padding; - this.Length = this.RowStride * height; - - this.byteBuffer = Configuration.Default.MemoryManager.AllocateCleanManagedByteBuffer(this.Length); - } - - /// - /// Gets the data in bytes. - /// - public byte[] Bytes => this.byteBuffer.Array; - - /// - /// Gets the length of the buffer. - /// - public int Length { get; } - - /// - /// Gets the component order. - /// - public ComponentOrder ComponentOrder { get; } - - /// - /// Gets the height. - /// - public int Height { get; } - - /// - /// Gets the width of one row in the number of bytes. - /// - public int RowStride { get; } - - /// - /// Gets the width. - /// - public int Width { get; } - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public void Dispose() - { - if (this.isDisposed) - { - return; - } - - this.byteBuffer.Dispose(); - this.isDisposed = true; - } - - /// - /// Reads the stream to the area. - /// - /// The stream. - public void Read(Stream stream) - { - stream.Read(this.Bytes, 0, this.Length); - } - - /// - /// Writes the area to the stream. - /// - /// The stream. - public void Write(Stream stream) - { - stream.Write(this.Bytes, 0, this.Length); - } - - /// - /// Resets the bytes of the array to it's initial value. - /// - public void Reset() - { - this.byteBuffer.Clear(); - } - - /// - /// Gets a to the row y. - /// - /// The y coordinate - /// The - internal Span GetRowSpan(int y) - { - return this.byteBuffer.Slice(y * this.RowStride); - } - - /// - /// Gets component count for the given order. - /// - /// The component order. - /// - /// The . - /// - /// - /// Thrown if an invalid order is given. - /// - private static int GetComponentCount(ComponentOrder componentOrder) - { - switch (componentOrder) - { - case ComponentOrder.Zyx: - case ComponentOrder.Xyz: - return 3; - case ComponentOrder.Zyxw: - case ComponentOrder.Xyzw: - return 4; - } - - throw new NotSupportedException(); - } - - /// - /// Checks that the length of the byte array to ensure that it matches the given width and height. - /// - /// The width. - /// The height. - /// The byte array. - /// The component order. - /// - /// Thrown if the byte array is th incorrect length. - /// - [Conditional("DEBUG")] - private void CheckBytesLength(int width, int height, byte[] bytes, ComponentOrder componentOrder) - { - int requiredLength = (width * GetComponentCount(componentOrder)) * height; - if (bytes.Length != requiredLength) - { - throw new ArgumentOutOfRangeException( - nameof(bytes), - $"Invalid byte array length. Length {bytes.Length}; Should be {requiredLength}."); - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Memory/BufferArea{T}.cs b/src/ImageSharp/Memory/BufferArea{T}.cs index b5ed3566fa..850cc4c164 100644 --- a/src/ImageSharp/Memory/BufferArea{T}.cs +++ b/src/ImageSharp/Memory/BufferArea{T}.cs @@ -45,11 +45,26 @@ namespace SixLabors.ImageSharp.Memory /// public Size Size => this.Rectangle.Size; + /// + /// Gets the width + /// + public int Width => this.Rectangle.Width; + + /// + /// Gets the height + /// + public int Height => this.Rectangle.Height; + /// /// Gets the pixel stride which is equal to the width of . /// public int Stride => this.DestinationBuffer.Width; + /// + /// Gets a value indicating whether the area refers to the entire + /// + public bool IsFullBufferArea => this.Size == this.DestinationBuffer.Size(); + /// /// Gets or sets a value at the given index. /// @@ -126,5 +141,21 @@ namespace SixLabors.ImageSharp.Memory { return (y + this.Rectangle.Y) * this.DestinationBuffer.Width; } + + public void Clear() + { + // Optimization for when the size of the area is the same as the buffer size. + if (this.IsFullBufferArea) + { + this.DestinationBuffer.Span.Clear(); + return; + } + + for (int y = 0; y < this.Rectangle.Height; y++) + { + Span row = this.GetRowSpan(y); + row.Clear(); + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Image/PixelAccessorTests.cs b/tests/ImageSharp.Tests/Image/PixelAccessorTests.cs deleted file mode 100644 index 1ab3f2ce9f..0000000000 --- a/tests/ImageSharp.Tests/Image/PixelAccessorTests.cs +++ /dev/null @@ -1,246 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Numerics; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; -using Xunit; - -namespace SixLabors.ImageSharp.Tests -{ - /// - /// Tests the class. - /// - public class PixelAccessorTests - { - public static Image CreateTestImage() - where TPixel : struct, IPixel - { - var image = new Image(10, 10); - using (PixelAccessor pixels = image.Lock()) - { - for (int i = 0; i < 10; i++) - { - for (int j = 0; j < 10; j++) - { - var v = new Vector4(i, j, 0, 1); - v /= 10; - - var color = default(TPixel); - color.PackFromVector4(v); - - pixels[i, j] = color; - } - } - } - return image; - } - - [Theory] - [WithMemberFactory(nameof(CreateTestImage), PixelTypes.All, ComponentOrder.Xyz)] - [WithMemberFactory(nameof(CreateTestImage), PixelTypes.All, ComponentOrder.Zyx)] - [WithMemberFactory(nameof(CreateTestImage), PixelTypes.All, ComponentOrder.Xyzw)] - [WithMemberFactory(nameof(CreateTestImage), PixelTypes.All, ComponentOrder.Zyxw)] - internal void CopyTo_Then_CopyFrom_OnFullImageRect(TestImageProvider provider, ComponentOrder order) - where TPixel : struct, IPixel - { - using (Image src = provider.GetImage()) - { - using (Image dest = new Image(src.Width, src.Height)) - { - using (PixelArea area = new PixelArea(src.Width, src.Height, order)) - { - using (PixelAccessor srcPixels = src.Lock()) - { - srcPixels.CopyTo(area, 0, 0); - } - - using (PixelAccessor destPixels = dest.Lock()) - { - destPixels.CopyFrom(area, 0, 0); - } - } - - Assert.True(src.IsEquivalentTo(dest, false)); - } - } - } - - [Theory] - [WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Xyz)] - [WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Zyx)] - [WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Xyzw)] - [WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Zyxw)] - internal void CopyToThenCopyFromWithOffset(TestImageProvider provider, ComponentOrder order) - where TPixel : struct, IPixel - { - using (Image destImage = new Image(8, 8)) - { - using (Image srcImage = provider.GetImage()) - { - srcImage.Mutate(x => x.Fill(NamedColors.Red, new Rectangle(4, 4, 8, 8))); - using (PixelAccessor srcPixels = srcImage.Lock()) - { - using (PixelArea area = new PixelArea(8, 8, order)) - { - srcPixels.CopyTo(area, 4, 4); - - using (PixelAccessor destPixels = destImage.Lock()) - { - destPixels.CopyFrom(area, 0, 0); - } - } - } - } - - provider.Utility.SourceFileOrDescription = order.ToString(); - provider.Utility.SaveTestOutputFile(destImage, "bmp"); - - using (Image expectedImage = new Image(8, 8)) - { - expectedImage.Mutate(x => x.Fill(NamedColors.Red)); - Assert.True(destImage.IsEquivalentTo(expectedImage)); - } - } - } - - - [Fact] - public void CopyFromZYX() - { - using (Image image = new Image(1, 1)) - { - CopyFromZYXImpl(image); - } - } - - [Fact] - public void CopyFromZYXW() - { - using (Image image = new Image(1, 1)) - { - CopyFromZYXWImpl(image); - } - } - - [Fact] - public void CopyToZYX() - { - using (Image image = new Image(1, 1)) - { - CopyToZYXImpl(image); - } - } - - [Fact] - public void CopyToZYXW() - { - using (Image image = new Image(1, 1)) - { - CopyToZYXWImpl(image); - } - } - - private static void CopyFromZYXImpl(Image image) - where TPixel : struct, IPixel - { - using (PixelAccessor pixels = image.Lock()) - { - byte red = 1; - byte green = 2; - byte blue = 3; - byte alpha = 255; - - using (PixelArea row = new PixelArea(1, ComponentOrder.Zyx)) - { - row.Bytes[0] = blue; - row.Bytes[1] = green; - row.Bytes[2] = red; - - pixels.CopyFrom(row, 0); - - Rgba32 color = (Rgba32)(object)pixels[0, 0]; - Assert.Equal(red, color.R); - Assert.Equal(green, color.G); - Assert.Equal(blue, color.B); - Assert.Equal(alpha, color.A); - } - } - } - - private static void CopyFromZYXWImpl(Image image) - where TPixel : struct, IPixel - { - using (PixelAccessor pixels = image.Lock()) - { - byte red = 1; - byte green = 2; - byte blue = 3; - byte alpha = 4; - - using (PixelArea row = new PixelArea(1, ComponentOrder.Zyxw)) - { - row.Bytes[0] = blue; - row.Bytes[1] = green; - row.Bytes[2] = red; - row.Bytes[3] = alpha; - - pixels.CopyFrom(row, 0); - - Rgba32 color = (Rgba32)(object)pixels[0, 0]; - Assert.Equal(red, color.R); - Assert.Equal(green, color.G); - Assert.Equal(blue, color.B); - Assert.Equal(alpha, color.A); - } - } - } - - private static void CopyToZYXImpl(Image image) - where TPixel : struct, IPixel - { - using (PixelAccessor pixels = image.Lock()) - { - byte red = 1; - byte green = 2; - byte blue = 3; - - using (PixelArea row = new PixelArea(1, ComponentOrder.Zyx)) - { - pixels[0, 0] = (TPixel)(object)new Rgba32(red, green, blue); - - pixels.CopyTo(row, 0); - - Assert.Equal(blue, row.Bytes[0]); - Assert.Equal(green, row.Bytes[1]); - Assert.Equal(red, row.Bytes[2]); - } - } - } - - private static void CopyToZYXWImpl(Image image) - where TPixel : struct, IPixel - { - using (PixelAccessor pixels = image.Lock()) - { - byte red = 1; - byte green = 2; - byte blue = 3; - byte alpha = 4; - - using (PixelArea row = new PixelArea(1, ComponentOrder.Zyxw)) - { - pixels[0, 0] = (TPixel)(object)new Rgba32(red, green, blue, alpha); - - pixels.CopyTo(row, 0); - - Assert.Equal(blue, row.Bytes[0]); - Assert.Equal(green, row.Bytes[1]); - Assert.Equal(red, row.Bytes[2]); - Assert.Equal(alpha, row.Bytes[3]); - } - } - } - } -} diff --git a/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs b/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs index 2ca409dc15..e96aa2e375 100644 --- a/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs +++ b/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs @@ -110,5 +110,38 @@ namespace SixLabors.ImageSharp.Tests.Memory Assert.Equal(expected, r); } } + + [Fact] + public void Clear_FullArea() + { + using (Buffer2D buffer = CreateTestBuffer(22, 13)) + { + buffer.GetArea().Clear(); + Span fullSpan = buffer.Span; + Assert.True(fullSpan.SequenceEqual(new int[fullSpan.Length])); + } + } + + [Fact] + public void Clear_SubArea() + { + using (Buffer2D buffer = CreateTestBuffer(20, 30)) + { + BufferArea area = buffer.GetArea(5, 5, 10, 10); + area.Clear(); + + Assert.NotEqual(0, buffer[4, 4]); + Assert.NotEqual(0, buffer[15, 15]); + + Assert.Equal(0, buffer[5, 5]); + Assert.Equal(0, buffer[14, 14]); + + for (int y = area.Rectangle.Y; y < area.Rectangle.Bottom; y++) + { + Span span = buffer.GetRowSpan(y).Slice(area.Rectangle.X, area.Width); + Assert.True(span.SequenceEqual(new int[area.Width])); + } + } + } } } \ No newline at end of file From 07baed95c26d67d8ebd6199f3ddf81d2fb39fe4c Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 23 Feb 2018 01:30:30 +0100 Subject: [PATCH 094/151] replaced PixelAccessor with Buffer2D in several processors --- src/ImageSharp/Formats/Gif/GifDecoderCore.cs | 29 ++----------------- src/ImageSharp/Image/ImageFrame{TPixel}.cs | 20 +++++++++++-- src/ImageSharp/Memory/Buffer2D{T}.cs | 22 ++++++++++++-- .../Memory/MemoryManagerExtensions.cs | 6 ++++ .../Convolution/Convolution2DProcessor.cs | 4 +-- .../Convolution/Convolution2PassProcessor.cs | 13 ++++----- .../Convolution/ConvolutionProcessor.cs | 5 ++-- .../Effects/OilPaintingProcessor.cs | 6 ++-- .../Processors/Transforms/CropProcessor.cs | 4 +-- .../Processors/Transforms/FlipProcessor.cs | 7 +++-- .../Formats/Bmp/BmpEncoderTests.cs | 2 -- .../ImageSharp.Tests/Memory/Buffer2DTests.cs | 18 ++++++++++++ .../Convolution/GaussianBlurTest.cs | 9 ++---- .../Processors/Effects/OilPaintTest.cs | 10 +++---- .../Processors/Transforms/FlipTests.cs | 2 +- 15 files changed, 90 insertions(+), 67 deletions(-) diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index 8fd7f04d28..48c8ceb8c9 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -442,7 +442,7 @@ namespace SixLabors.ImageSharp.Formats.Gif imageFrame = currentFrame; - this.RestoreToBackground(imageFrame, image.Width, image.Height); + this.RestoreToBackground(imageFrame); } int i = 0; @@ -532,9 +532,7 @@ namespace SixLabors.ImageSharp.Formats.Gif /// /// The pixel format. /// The frame. - /// Width of the image. - /// Height of the image. - private void RestoreToBackground(ImageFrame frame, int imageWidth, int imageHeight) + private void RestoreToBackground(ImageFrame frame) where TPixel : struct, IPixel { if (this.restoreArea == null) @@ -545,29 +543,6 @@ namespace SixLabors.ImageSharp.Formats.Gif BufferArea pixelArea = frame.PixelBuffer.GetArea(this.restoreArea.Value); pixelArea.Clear(); - //if (this.restoreArea.Value.Width == imageWidth && - // this.restoreArea.Value.Height == imageHeight) - //{ - // using (PixelAccessor pixelAccessor = frame.Lock()) - // { - // pixelAccessor.Reset(); - // } - //} - //else - //{ - - // using (var emptyRow = new PixelArea(this.restoreArea.Value.Width, ComponentOrder.Xyzw)) - // { - // using (PixelAccessor pixelAccessor = frame.Lock()) - // { - // for (int y = this.restoreArea.Value.Top; y < this.restoreArea.Value.Top + this.restoreArea.Value.Height; y++) - // { - // pixelAccessor.CopyFrom(emptyRow, y, this.restoreArea.Value.Left); - // } - // } - // } - //} - this.restoreArea = null; } diff --git a/src/ImageSharp/Image/ImageFrame{TPixel}.cs b/src/ImageSharp/Image/ImageFrame{TPixel}.cs index 163c7a3b08..0dc730fe5f 100644 --- a/src/ImageSharp/Image/ImageFrame{TPixel}.cs +++ b/src/ImageSharp/Image/ImageFrame{TPixel}.cs @@ -14,6 +14,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp { + using SixLabors.ImageSharp.Helpers; + /// /// Represents a single frame in a animation. /// @@ -151,12 +153,26 @@ namespace SixLabors.ImageSharp } /// - /// Copies the pixels to another of the same size. + /// Copies the pixels to a of the same size. /// /// The target pixel buffer accessor. internal void CopyTo(PixelAccessor target) { - SpanHelper.Copy(this.GetPixelSpan(), target.PixelBuffer.Span); + this.CopyTo(target.PixelBuffer); + } + + /// + /// Copies the pixels to a of the same size. + /// + /// The target pixel buffer accessor. + internal void CopyTo(Buffer2D target) + { + if (this.Size() != target.Size()) + { + throw new ArgumentException("ImageFrame.CopyTo(): target must be of the same size!", nameof(target)); + } + + SpanHelper.Copy(this.GetPixelSpan(), target.Span); } /// diff --git a/src/ImageSharp/Memory/Buffer2D{T}.cs b/src/ImageSharp/Memory/Buffer2D{T}.cs index 6b18aaa8fc..d9e645845e 100644 --- a/src/ImageSharp/Memory/Buffer2D{T}.cs +++ b/src/ImageSharp/Memory/Buffer2D{T}.cs @@ -29,14 +29,14 @@ namespace SixLabors.ImageSharp.Memory } /// - public int Width { get; } + public int Width { get; private set; } /// - public int Height { get; } + public int Height { get; private set; } public Span Span => this.Buffer.Span; - public IBuffer Buffer { get; } + public IBuffer Buffer { get; private set; } /// /// Gets a reference to the element at the specified position. @@ -60,5 +60,21 @@ namespace SixLabors.ImageSharp.Memory { this.Buffer?.Dispose(); } + + public static void SwapContents(Buffer2D a, Buffer2D b) + { + Size aSize = a.Size(); + Size bSize = b.Size(); + + IBuffer temp = a.Buffer; + a.Buffer = b.Buffer; + b.Buffer = temp; + + b.Width = aSize.Width; + b.Height = aSize.Height; + + a.Width = bSize.Width; + a.Height = bSize.Height; + } } } \ No newline at end of file diff --git a/src/ImageSharp/Memory/MemoryManagerExtensions.cs b/src/ImageSharp/Memory/MemoryManagerExtensions.cs index 3511d20390..810af13db5 100644 --- a/src/ImageSharp/Memory/MemoryManagerExtensions.cs +++ b/src/ImageSharp/Memory/MemoryManagerExtensions.cs @@ -1,5 +1,7 @@ namespace SixLabors.ImageSharp.Memory { + using SixLabors.Primitives; + /// /// Extension methods for . /// @@ -44,6 +46,10 @@ return new Buffer2D(buffer, width, height); } + public static Buffer2D Allocate2D(this MemoryManager memoryManager, Size size) + where T : struct => + Allocate2D(memoryManager, size.Width, size.Height, false); + public static Buffer2D Allocate2D(this MemoryManager memoryManager, int width, int height) where T : struct => Allocate2D(memoryManager, width, height, false); diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs index 378978d63f..2c13ead162 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs @@ -56,7 +56,7 @@ namespace SixLabors.ImageSharp.Processing.Processors int maxY = endY - 1; int maxX = endX - 1; - using (var targetPixels = new PixelAccessor(configuration.MemoryManager, source.Width, source.Height)) + using (Buffer2D targetPixels = configuration.MemoryManager.Allocate2D(source.Width, source.Height)) { source.CopyTo(targetPixels); @@ -122,7 +122,7 @@ namespace SixLabors.ImageSharp.Processing.Processors } }); - source.SwapPixelsBuffers(targetPixels); + Buffer2D.SwapContents(source.PixelBuffer, targetPixels); } } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs index 7730f1fa77..0d87aa1dc4 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs @@ -43,15 +43,12 @@ namespace SixLabors.ImageSharp.Processing.Processors /// protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - int width = source.Width; - int height = source.Height; ParallelOptions parallelOptions = configuration.ParallelOptions; - using (var firstPassPixels = new PixelAccessor(configuration.MemoryManager, width, height)) - using (PixelAccessor sourcePixels = source.Lock()) + using (Buffer2D firstPassPixels = configuration.MemoryManager.Allocate2D(source.Size())) { - this.ApplyConvolution(firstPassPixels, sourcePixels, source.Bounds(), this.KernelX, parallelOptions); - this.ApplyConvolution(sourcePixels, firstPassPixels, sourceRectangle, this.KernelY, parallelOptions); + this.ApplyConvolution(firstPassPixels, source.PixelBuffer, source.Bounds(), this.KernelX, parallelOptions); + this.ApplyConvolution(source.PixelBuffer, firstPassPixels, sourceRectangle, this.KernelY, parallelOptions); } } @@ -67,8 +64,8 @@ namespace SixLabors.ImageSharp.Processing.Processors /// The kernel operator. /// The parellel options private void ApplyConvolution( - PixelAccessor targetPixels, - PixelAccessor sourcePixels, + Buffer2D targetPixels, + Buffer2D sourcePixels, Rectangle sourceRectangle, Fast2DArray kernel, ParallelOptions parallelOptions) diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs index b700343bf4..2f369113d9 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.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; @@ -45,7 +46,7 @@ namespace SixLabors.ImageSharp.Processing.Processors int maxY = endY - 1; int maxX = endX - 1; - using (var targetPixels = new PixelAccessor(configuration.MemoryManager, source.Width, source.Height)) + using (Buffer2D targetPixels = configuration.MemoryManager.Allocate2D(source.Size())) { source.CopyTo(targetPixels); @@ -94,7 +95,7 @@ namespace SixLabors.ImageSharp.Processing.Processors } }); - source.SwapPixelsBuffers(targetPixels); + Buffer2D.SwapContents(source.PixelBuffer, targetPixels); } } } diff --git a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs index af2c297592..950ffc60fb 100644 --- a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs @@ -11,6 +11,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors { + using SixLabors.ImageSharp.Helpers; + /// /// An to apply an oil painting effect to an . /// @@ -65,7 +67,7 @@ namespace SixLabors.ImageSharp.Processing.Processors int radius = this.BrushSize >> 1; int levels = this.Levels; - using (var targetPixels = new PixelAccessor(configuration.MemoryManager, source.Width, source.Height)) + using (Buffer2D targetPixels = configuration.MemoryManager.Allocate2D(source.Size())) { source.CopyTo(targetPixels); @@ -133,7 +135,7 @@ namespace SixLabors.ImageSharp.Processing.Processors } }); - source.SwapPixelsBuffers(targetPixels); + Buffer2D.SwapContents(source.PixelBuffer, targetPixels); } } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs index 9448aac5e0..37c76bdcfb 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs @@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Processing.Processors int minX = Math.Max(this.CropRectangle.X, sourceRectangle.X); int maxX = Math.Min(this.CropRectangle.Right, sourceRectangle.Right); - using (var targetPixels = new PixelAccessor(configuration.MemoryManager, this.CropRectangle.Width, this.CropRectangle.Height)) + using (Buffer2D targetPixels = configuration.MemoryManager.Allocate2D(this.CropRectangle.Size)) { Parallel.For( minY, @@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Processing.Processors SpanHelper.Copy(sourceRow, targetRow, maxX - minX); }); - source.SwapPixelsBuffers(targetPixels); + Buffer2D.SwapContents(source.PixelBuffer, targetPixels); } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs index f52bc97c11..eb16725917 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs @@ -10,6 +10,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors { + using SixLabors.ImageSharp.Helpers; + /// /// Provides methods that allow the flipping of an image around its center point. /// @@ -54,11 +56,10 @@ namespace SixLabors.ImageSharp.Processing.Processors /// The configuration. private void FlipX(ImageFrame source, Configuration configuration) { - int width = source.Width; int height = source.Height; int halfHeight = (int)Math.Ceiling(source.Height * .5F); - using (var targetPixels = new PixelAccessor(configuration.MemoryManager, width, height)) + using (Buffer2D targetPixels = configuration.MemoryManager.Allocate2D(source.Size())) { Parallel.For( 0, @@ -76,7 +77,7 @@ namespace SixLabors.ImageSharp.Processing.Processors altSourceRow.CopyTo(targetRow); }); - source.SwapPixelsBuffers(targetPixels); + Buffer2D.SwapContents(source.PixelBuffer, targetPixels); } } diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs index a4ed0c0fd2..3a5fbe8387 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs @@ -48,9 +48,7 @@ namespace SixLabors.ImageSharp.Tests { TestBmpEncoderCore(provider, bitsPerPixel); } - - private static void TestBmpEncoderCore(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) where TPixel : struct, IPixel { diff --git a/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs index a765a77b12..565e06572b 100644 --- a/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs +++ b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs @@ -8,6 +8,7 @@ namespace SixLabors.ImageSharp.Tests.Memory using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Tests.Common; + using SixLabors.Primitives; using Xunit; @@ -127,5 +128,22 @@ namespace SixLabors.ImageSharp.Tests.Memory Assert.True(Unsafe.AreSame(ref expected, ref actual)); } } + + [Fact] + public void SwapContents() + { + using (Buffer2D a = this.MemoryManager.Allocate2D(10, 5)) + using (Buffer2D b = this.MemoryManager.Allocate2D(3, 7)) + { + IBuffer aa = a.Buffer; + IBuffer bb = b.Buffer; + + Buffer2D.SwapContents(a, b); + + Assert.Equal(bb, a.Buffer); + Assert.Equal(new Size(3, 7), a.Size()); + Assert.Equal(new Size(10, 5), b.Size()); + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs index 3508d544be..89e9a13b5c 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs @@ -11,12 +11,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution { public class GaussianBlurTest : FileTestBase { - public static readonly TheoryData GaussianBlurValues - = new TheoryData - { - 3, - 5 - }; + public static readonly TheoryData GaussianBlurValues = new TheoryData { 3, 5 }; [Theory] [WithFileCollection(nameof(DefaultFiles), nameof(GaussianBlurValues), DefaultPixelType)] @@ -36,7 +31,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution where TPixel : struct, IPixel { using (Image source = provider.GetImage()) - using (var image = source.Clone()) + using (Image image = source.Clone()) { var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); diff --git a/tests/ImageSharp.Tests/Processing/Processors/Effects/OilPaintTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Effects/OilPaintTest.cs index 608fcf10cf..a9127f61dc 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Effects/OilPaintTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Effects/OilPaintTest.cs @@ -11,12 +11,10 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects { public class OilPaintTest : FileTestBase { - public static readonly TheoryData OilPaintValues - = new TheoryData - { - { 15, 10 }, - { 6, 5 } - }; + public static readonly TheoryData OilPaintValues = new TheoryData + { + { 15, 10 }, { 6, 5 } + }; [Theory] [WithFileCollection(nameof(DefaultFiles), nameof(OilPaintValues), DefaultPixelType)] diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/FlipTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/FlipTests.cs index d4de4c3d2e..9ca3994986 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/FlipTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/FlipTests.cs @@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms using (Image image = provider.GetImage()) { image.Mutate(x => x.Flip(flipType)); - image.DebugSave(provider, flipType, Extensions.Bmp); + image.DebugSave(provider, flipType); } } } From a7750e8838292219d83981fc5725cf39527c2499 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 23 Feb 2018 01:43:39 +0100 Subject: [PATCH 095/151] PixelAccessor is now a meaningless thin wrapper around Buffer2D --- src/ImageSharp/Image/PixelAccessor{TPixel}.cs | 77 ++----------------- .../Processors/Transforms/FlipProcessor.cs | 4 +- src/ImageSharp/Quantizers/Quantize.cs | 23 +++--- 3 files changed, 21 insertions(+), 83 deletions(-) diff --git a/src/ImageSharp/Image/PixelAccessor{TPixel}.cs b/src/ImageSharp/Image/PixelAccessor{TPixel}.cs index f1c8531cc4..63e4c015c1 100644 --- a/src/ImageSharp/Image/PixelAccessor{TPixel}.cs +++ b/src/ImageSharp/Image/PixelAccessor{TPixel}.cs @@ -17,25 +17,6 @@ namespace SixLabors.ImageSharp internal sealed class PixelAccessor : IDisposable, IBuffer2D where TPixel : struct, IPixel { -#pragma warning disable SA1401 // Fields must be private - /// - /// The containing the pixel data. - /// - internal Buffer2D PixelBuffer; - private bool ownedBuffer; -#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. /// @@ -46,43 +27,13 @@ namespace SixLabors.ImageSharp Guard.MustBeGreaterThan(image.PixelBuffer.Width, 0, "image width"); Guard.MustBeGreaterThan(image.PixelBuffer.Height, 0, "image height"); - this.SetPixelBufferUnsafe(image.PixelBuffer, false); + this.SetPixelBufferUnsafe(image.PixelBuffer); } /// - /// Initializes a new instance of the class. + /// Gets the containing the pixel data. /// - /// The to use for buffer allocations. - /// The width of the image represented by the pixel buffer. - /// The height of the image represented by the pixel buffer. - public PixelAccessor(MemoryManager memoryManager, int width, int height) - : this(width, height, memoryManager.Allocate2D(width, height, true), true) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The width of the image represented by the pixel buffer. - /// The height of the image represented by the pixel buffer. - /// The pixel buffer. - /// 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(pixels, ownedBuffer); - } - - /// - /// Finalizes an instance of the class. - /// - ~PixelAccessor() - { - this.Dispose(); - } + internal Buffer2D PixelBuffer { get; private set; } /// /// Gets the size of a single pixel in the number of bytes. @@ -132,22 +83,6 @@ namespace SixLabors.ImageSharp /// public void Dispose() { - if (this.isDisposed || !this.ownedBuffer) - { - return; - } - - // Note disposing is done. - this.isDisposed = true; - - this.PixelBuffer.Dispose(); - - // 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); } /// @@ -167,7 +102,7 @@ namespace SixLabors.ImageSharp internal Buffer2D SwapBufferOwnership(Buffer2D pixels) { Buffer2D oldPixels = this.PixelBuffer; - this.SetPixelBufferUnsafe(pixels, this.ownedBuffer); + this.SetPixelBufferUnsafe(pixels); return oldPixels; } @@ -184,11 +119,9 @@ namespace SixLabors.ImageSharp /// Sets the pixel buffer in an unsafe manor this should not be used unless you know what its doing!!! /// /// The pixel buffer - /// if set to true then this instance ownes the buffer and thus should dispose of it afterwards. - private void SetPixelBufferUnsafe(Buffer2D pixels, bool ownedBuffer) + private void SetPixelBufferUnsafe(Buffer2D pixels) { this.PixelBuffer = pixels; - this.ownedBuffer = ownedBuffer; this.Width = pixels.Width; this.Height = pixels.Height; diff --git a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs index eb16725917..7a6f5d9da3 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs @@ -93,7 +93,7 @@ namespace SixLabors.ImageSharp.Processing.Processors int height = source.Height; int halfWidth = (int)Math.Ceiling(width * .5F); - using (var targetPixels = new PixelAccessor(configuration.MemoryManager, width, height)) + using (Buffer2D targetPixels = configuration.MemoryManager.Allocate2D(source.Size())) { Parallel.For( 0, @@ -112,7 +112,7 @@ namespace SixLabors.ImageSharp.Processing.Processors } }); - source.SwapPixelsBuffers(targetPixels); + Buffer2D.SwapContents(source.PixelBuffer, targetPixels); } } } diff --git a/src/ImageSharp/Quantizers/Quantize.cs b/src/ImageSharp/Quantizers/Quantize.cs index 4052d46857..ee9c4d608c 100644 --- a/src/ImageSharp/Quantizers/Quantize.cs +++ b/src/ImageSharp/Quantizers/Quantize.cs @@ -9,6 +9,8 @@ using SixLabors.ImageSharp.Quantizers; namespace SixLabors.ImageSharp { + using SixLabors.ImageSharp.Memory; + /// /// Extension methods for the type. /// @@ -61,23 +63,26 @@ namespace SixLabors.ImageSharp QuantizedImage quantized = quantizer.Quantize(img.Frames.RootFrame, maxColors); int palleteCount = quantized.Palette.Length - 1; - using (var pixels = new PixelAccessor(source.MemoryManager, quantized.Width, quantized.Height)) + using (Buffer2D pixels = source.MemoryManager.Allocate2D(quantized.Width, quantized.Height)) { Parallel.For( 0, pixels.Height, img.GetConfiguration().ParallelOptions, y => - { - for (int x = 0; x < pixels.Width; x++) { - int i = x + (y * pixels.Width); - TPixel color = quantized.Palette[Math.Min(palleteCount, quantized.Pixels[i])]; - pixels[x, y] = color; - } - }); + Span row = pixels.GetRowSpan(y); + int yy = y * pixels.Width; + for (int x = 0; x < pixels.Width; x++) + { + int i = x + yy; + TPixel color = quantized.Palette[Math.Min(palleteCount, quantized.Pixels[i])]; + row[x] = color; + //pixels[x, y] = color; + } + }); - img.Frames[0].SwapPixelsBuffers(pixels); + Buffer2D.SwapContents(img.Frames[0].PixelBuffer, pixels); } }); } From a6fb21a8a05729ff97dbc3e892fc475e15731d46 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 23 Feb 2018 01:55:47 +0100 Subject: [PATCH 096/151] do not use Configuration.Default.MemoryManager in AddFrame() --- src/ImageSharp/Image/ImageFrame.LoadPixelData.cs | 10 ++++++---- src/ImageSharp/Image/ImageFrameCollection.cs | 6 +++++- src/ImageSharp/Memory/Buffer2D{T}.cs | 15 +++++++++++++++ 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/ImageSharp/Image/ImageFrame.LoadPixelData.cs b/src/ImageSharp/Image/ImageFrame.LoadPixelData.cs index 153a757e18..b9341a1b24 100644 --- a/src/ImageSharp/Image/ImageFrame.LoadPixelData.cs +++ b/src/ImageSharp/Image/ImageFrame.LoadPixelData.cs @@ -20,30 +20,32 @@ namespace SixLabors.ImageSharp /// /// Create a new instance of the class from the given byte array in format. /// + /// The memory manager to use for allocations /// 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) + public static ImageFrame LoadPixelData(MemoryManager memoryManager, Span data, int width, int height) where TPixel : struct, IPixel - => LoadPixelData(data.NonPortableCast(), width, height); + => LoadPixelData(memoryManager, data.NonPortableCast(), width, height); /// /// Create a new instance of the class from the raw data. /// + /// The memory manager to use for allocations /// 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) + public static ImageFrame LoadPixelData(MemoryManager memoryManager, 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(Configuration.Default.MemoryManager, width, height); + var image = new ImageFrame(memoryManager, width, height); SpanHelper.Copy(data, image.GetPixelSpan(), count); return image; diff --git a/src/ImageSharp/Image/ImageFrameCollection.cs b/src/ImageSharp/Image/ImageFrameCollection.cs index bfdf1df76b..aefeacce15 100644 --- a/src/ImageSharp/Image/ImageFrameCollection.cs +++ b/src/ImageSharp/Image/ImageFrameCollection.cs @@ -80,7 +80,11 @@ namespace SixLabors.ImageSharp /// public ImageFrame AddFrame(TPixel[] data) { - var frame = ImageFrame.LoadPixelData(new Span(data), this.RootFrame.Width, this.RootFrame.Height); + var frame = ImageFrame.LoadPixelData( + this.parent.GetMemoryManager(), + new Span(data), + this.RootFrame.Width, + this.RootFrame.Height); this.frames.Add(frame); return frame; } diff --git a/src/ImageSharp/Memory/Buffer2D{T}.cs b/src/ImageSharp/Memory/Buffer2D{T}.cs index d9e645845e..7f9fb59c4c 100644 --- a/src/ImageSharp/Memory/Buffer2D{T}.cs +++ b/src/ImageSharp/Memory/Buffer2D{T}.cs @@ -34,8 +34,14 @@ namespace SixLabors.ImageSharp.Memory /// public int Height { get; private set; } + /// + /// Gets the span to the whole area. + /// public Span Span => this.Buffer.Span; + /// + /// Gets the backing + /// public IBuffer Buffer { get; private set; } /// @@ -56,11 +62,20 @@ namespace SixLabors.ImageSharp.Memory } } + /// + /// Disposes the instance + /// public void Dispose() { this.Buffer?.Dispose(); } + /// + /// Swap the contents (, , ) of the two buffers. + /// Useful to transfer the contents of a temporal to a persistent + /// + /// The first buffer + /// The second buffer public static void SwapContents(Buffer2D a, Buffer2D b) { Size aSize = a.Size(); From d9dcfbe5c4e4ce96557e8b6cfddfc7301153251b Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 23 Feb 2018 02:00:37 +0100 Subject: [PATCH 097/151] code analyzers fighting each other --- src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs | 3 +-- src/ImageSharp/Image/ImageFrame{TPixel}.cs | 3 +-- src/ImageSharp/Memory/BufferExtensions.cs | 3 +-- src/ImageSharp/Memory/MemoryManagerExtensions.cs | 6 +++--- .../Processing/Processors/Effects/OilPaintingProcessor.cs | 3 +-- .../Processing/Processors/Transforms/FlipProcessor.cs | 3 +-- src/ImageSharp/Quantizers/Quantize.cs | 4 +--- 7 files changed, 9 insertions(+), 16 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index 5b85e9cb9e..66c8b6c086 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -4,12 +4,11 @@ using System; using System.IO; using SixLabors.ImageSharp.IO; +using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Bmp { - using SixLabors.ImageSharp.Memory; - /// /// Image encoder for writing an image to a stream as a Windows bitmap. /// diff --git a/src/ImageSharp/Image/ImageFrame{TPixel}.cs b/src/ImageSharp/Image/ImageFrame{TPixel}.cs index 0dc730fe5f..833a22f7c7 100644 --- a/src/ImageSharp/Image/ImageFrame{TPixel}.cs +++ b/src/ImageSharp/Image/ImageFrame{TPixel}.cs @@ -7,6 +7,7 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Helpers; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.MetaData; using SixLabors.ImageSharp.PixelFormats; @@ -14,8 +15,6 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp { - using SixLabors.ImageSharp.Helpers; - /// /// Represents a single frame in a animation. /// diff --git a/src/ImageSharp/Memory/BufferExtensions.cs b/src/ImageSharp/Memory/BufferExtensions.cs index 882b8875fc..919a6ef345 100644 --- a/src/ImageSharp/Memory/BufferExtensions.cs +++ b/src/ImageSharp/Memory/BufferExtensions.cs @@ -2,12 +2,11 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.IO; using System.Runtime.CompilerServices; namespace SixLabors.ImageSharp.Memory { - using System.IO; - internal static class BufferExtensions { [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/Memory/MemoryManagerExtensions.cs b/src/ImageSharp/Memory/MemoryManagerExtensions.cs index 810af13db5..2060002450 100644 --- a/src/ImageSharp/Memory/MemoryManagerExtensions.cs +++ b/src/ImageSharp/Memory/MemoryManagerExtensions.cs @@ -1,7 +1,7 @@ -namespace SixLabors.ImageSharp.Memory -{ - using SixLabors.Primitives; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Memory +{ /// /// Extension methods for . /// diff --git a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs index 950ffc60fb..c199a32c8a 100644 --- a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs @@ -5,14 +5,13 @@ 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; namespace SixLabors.ImageSharp.Processing.Processors { - using SixLabors.ImageSharp.Helpers; - /// /// An to apply an oil painting effect to an . /// diff --git a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs index 7a6f5d9da3..9b8b785b58 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs @@ -4,14 +4,13 @@ using System; using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Helpers; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors { - using SixLabors.ImageSharp.Helpers; - /// /// Provides methods that allow the flipping of an image around its center point. /// diff --git a/src/ImageSharp/Quantizers/Quantize.cs b/src/ImageSharp/Quantizers/Quantize.cs index ee9c4d608c..f2a09abb77 100644 --- a/src/ImageSharp/Quantizers/Quantize.cs +++ b/src/ImageSharp/Quantizers/Quantize.cs @@ -4,13 +4,12 @@ using System; using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Quantizers; namespace SixLabors.ImageSharp { - using SixLabors.ImageSharp.Memory; - /// /// Extension methods for the type. /// @@ -78,7 +77,6 @@ namespace SixLabors.ImageSharp int i = x + yy; TPixel color = quantized.Palette[Math.Min(palleteCount, quantized.Pixels[i])]; row[x] = color; - //pixels[x, y] = color; } }); From 2fc6d4991ee969e15497df76280d3e6d624469b2 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 23 Feb 2018 15:18:02 +1100 Subject: [PATCH 098/151] Remove Size parameter and minor transform cleanup. --- src/ImageSharp.Drawing/DrawImage.cs | 66 +++++++------------ .../Processors/DrawImageProcessor.cs | 47 +++++-------- src/ImageSharp/GraphicsOptions.cs | 2 +- .../Transforms/AffineTransformProcessor.cs | 5 +- .../CenteredAffineTransformProcessor.cs | 13 +--- .../CenteredProjectiveTransformProcessor.cs | 11 ++-- .../ProjectiveTransformProcessor.cs | 39 ++++++----- .../Processing/Transforms/Transform.cs | 10 ++- .../Processing/Transforms/TransformHelpers.cs | 48 +++++++++++++- .../ImageSharp.Tests/Drawing/DrawImageTest.cs | 38 ++++++----- 10 files changed, 146 insertions(+), 133 deletions(-) diff --git a/src/ImageSharp.Drawing/DrawImage.cs b/src/ImageSharp.Drawing/DrawImage.cs index b84a1c16a4..f1db72db60 100644 --- a/src/ImageSharp.Drawing/DrawImage.cs +++ b/src/ImageSharp.Drawing/DrawImage.cs @@ -16,29 +16,15 @@ namespace SixLabors.ImageSharp /// Draws the given image together with the current one by blending their pixels. /// /// The image this method extends. - /// - /// The image to blend with the currently processing image. - /// If the image dimensions do not match the given then the image will be resized to match. - /// + /// The image to blend with the currently processing image. /// The pixel format. - /// The size to draw the blended image. /// The location to draw the blended image. /// The options. /// The . - public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, Size size, Point location, GraphicsOptions options) + public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, Point location, GraphicsOptions options) where TPixel : struct, IPixel { - if (size == default(Size)) - { - size = new Size(image.Width, image.Height); - } - - if (location == default(Point)) - { - location = Point.Empty; - } - - source.ApplyProcessor(new DrawImageProcessor(image, size, location, options)); + source.ApplyProcessor(new DrawImageProcessor(image, location, options)); return source; } @@ -48,14 +34,14 @@ namespace SixLabors.ImageSharp /// The pixel format. /// The image this method extends. /// The image to blend with the currently processing image. - /// The opacity of the image to blend. Must be between 0 and 1. + /// The opacity of the image to blend. Must be between 0 and 1. /// The . - public static IImageProcessingContext Blend(this IImageProcessingContext source, Image image, float percent) + public static IImageProcessingContext Blend(this IImageProcessingContext source, Image image, float opacity) where TPixel : struct, IPixel { GraphicsOptions options = GraphicsOptions.Default; - options.BlendPercentage = percent; - return DrawImage(source, image, default(Size), default(Point), options); + options.BlendPercentage = opacity; + return DrawImage(source, image, Point.Empty, options); } /// @@ -65,15 +51,15 @@ namespace SixLabors.ImageSharp /// The image this method extends. /// The image to blend with the currently processing image. /// The blending mode. - /// The opacity of the image to blend. Must be between 0 and 1. + /// The opacity of the image to blend. Must be between 0 and 1. /// The . - public static IImageProcessingContext Blend(this IImageProcessingContext source, Image image, PixelBlenderMode blender, float percent) + public static IImageProcessingContext Blend(this IImageProcessingContext source, Image image, PixelBlenderMode blender, float opacity) where TPixel : struct, IPixel { GraphicsOptions options = GraphicsOptions.Default; - options.BlendPercentage = percent; + options.BlendPercentage = opacity; options.BlenderMode = blender; - return DrawImage(source, image, default(Size), default(Point), options); + return DrawImage(source, image, Point.Empty, options); } /// @@ -87,51 +73,43 @@ namespace SixLabors.ImageSharp public static IImageProcessingContext Blend(this IImageProcessingContext source, Image image, GraphicsOptions options) where TPixel : struct, IPixel { - return DrawImage(source, image, default(Size), default(Point), options); + return DrawImage(source, image, Point.Empty, options); } /// /// Draws the given image together with the current one by blending their pixels. /// /// The image this method extends. - /// - /// The image to blend with the currently processing image. - /// If the image dimensions do not match the given then the image will be resized to match. - /// + /// The image to blend with the currently processing image. /// The pixel format. - /// The opacity of the image to blend. Must be between 0 and 1. - /// The size to draw the blended image. + /// The opacity of the image to blend. Must be between 0 and 1. /// The location to draw the blended image. /// The . - public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, float percent, Size size, Point location) + public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, float opacity, Point location) where TPixel : struct, IPixel { GraphicsOptions options = GraphicsOptions.Default; - options.BlendPercentage = percent; - return source.DrawImage(image, size, location, options); + options.BlendPercentage = opacity; + return source.DrawImage(image, location, options); } /// /// Draws the given image together with the current one by blending their pixels. /// /// The image this method extends. - /// - /// The image to blend with the currently processing image. - /// If the image dimensions do not match the given then the image will be resized to match. - /// + /// The image to blend with the currently processing image. /// The pixel format. /// The type of bending to apply. - /// The opacity of the image to blend. Must be between 0 and 1. - /// The size to draw the blended image. + /// The opacity of the image to blend. Must be between 0 and 1. /// The location to draw the blended image. /// The . - public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, PixelBlenderMode blender, float percent, Size size, Point location) + public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, PixelBlenderMode blender, float opacity, Point location) where TPixel : struct, IPixel { GraphicsOptions options = GraphicsOptions.Default; options.BlenderMode = blender; - options.BlendPercentage = percent; - return source.DrawImage(image, size, location, options); + options.BlendPercentage = opacity; + return source.DrawImage(image, location, options); } } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs b/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs index 4bb12b9957..42d15035ad 100644 --- a/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs @@ -19,52 +19,39 @@ namespace SixLabors.ImageSharp.Drawing.Processors internal class DrawImageProcessor : ImageProcessor where TPixel : struct, IPixel { - private readonly PixelBlender blender; - /// /// Initializes a new instance of the class. /// - /// - /// The image to blend with the currently processing image. - /// If the image dimensions do not match the given then the image will be resized to match. - /// - /// The size to draw the blended image. + /// The image to blend with the currently processing image. /// The location to draw the blended image. - /// The opacity of the image to blend. Between 0 and 100. - public DrawImageProcessor(Image image, Size size, Point location, GraphicsOptions options) + /// The opacity of the image to blend. Between 0 and 1. + public DrawImageProcessor(Image image, Point location, GraphicsOptions options) { Guard.MustBeBetweenOrEqualTo(options.BlendPercentage, 0, 1, nameof(options.BlendPercentage)); - this.Size = size; - - if (image.Size() != size) - { - image.Mutate(x => x.Resize(size.Width, size.Height)); - } - this.Image = image; - this.Alpha = options.BlendPercentage; - this.blender = PixelOperations.Instance.GetPixelBlender(options.BlenderMode); + this.Opacity = options.BlendPercentage; + this.Blender = PixelOperations.Instance.GetPixelBlender(options.BlenderMode); this.Location = location; } /// - /// Gets the image to blend. + /// Gets the image to blend /// public Image Image { get; } /// - /// Gets the alpha percentage value. + /// Gets the opacity of the image to blend /// - public float Alpha { get; } + public float Opacity { get; } /// - /// Gets the size to draw the blended image. + /// Gets the pixel blender /// - public Size Size { get; } + public PixelBlender Blender { get; } /// - /// Gets the location to draw the blended image. + /// Gets the location to draw the blended image /// public Point Location { get; } @@ -72,25 +59,25 @@ namespace SixLabors.ImageSharp.Drawing.Processors protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { Image targetImage = this.Image; + PixelBlender blender = this.Blender; + int locationY = this.Location.Y; // Align start/end positions. Rectangle bounds = targetImage.Bounds(); + int minX = Math.Max(this.Location.X, sourceRectangle.X); int maxX = Math.Min(this.Location.X + bounds.Width, sourceRectangle.Width); - maxX = Math.Min(this.Location.X + this.Size.Width, maxX); int targetX = minX - this.Location.X; int minY = Math.Max(this.Location.Y, sourceRectangle.Y); int maxY = Math.Min(this.Location.Y + bounds.Height, sourceRectangle.Bottom); - maxY = Math.Min(this.Location.Y + this.Size.Height, maxY); - int width = maxX - minX; using (var amount = new Buffer(width)) { for (int i = 0; i < width; i++) { - amount[i] = this.Alpha; + amount[i] = this.Opacity; } Parallel.For( @@ -100,8 +87,8 @@ namespace SixLabors.ImageSharp.Drawing.Processors y => { Span background = source.GetPixelRowSpan(y).Slice(minX, width); - Span foreground = targetImage.GetPixelRowSpan(y - this.Location.Y).Slice(targetX, width); - this.blender.Blend(background, background, foreground, amount); + Span foreground = targetImage.GetPixelRowSpan(y - locationY).Slice(targetX, width); + blender.Blend(background, background, foreground, amount); }); } } diff --git a/src/ImageSharp/GraphicsOptions.cs b/src/ImageSharp/GraphicsOptions.cs index 114eaab2a6..a094abacbe 100644 --- a/src/ImageSharp/GraphicsOptions.cs +++ b/src/ImageSharp/GraphicsOptions.cs @@ -67,7 +67,7 @@ namespace SixLabors.ImageSharp // some API thought post V1. /// - /// Gets or sets a value indicating the blending percentage to apply to the drawing operation + /// Gets or sets a value indicating the blending mode to apply to the drawing operation /// public PixelBlenderMode BlenderMode { diff --git a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs index 8595e86922..5ea23726e8 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs @@ -51,8 +51,6 @@ namespace SixLabors.ImageSharp.Processing.Processors public AffineTransformProcessor(Matrix3x2 matrix, IResampler sampler, Size targetDimensions) : base(sampler) { - // Transforms are inverted else the output is the opposite of the expected. - Matrix3x2.Invert(matrix, out matrix); this.TransformMatrix = matrix; this.targetDimensions = targetDimensions; } @@ -95,6 +93,9 @@ namespace SixLabors.ImageSharp.Processing.Processors // Since could potentially be resizing the canvas we might need to re-calculate the matrix Matrix3x2 matrix = this.GetProcessingMatrix(sourceBounds, targetBounds); + // Convert from screen to world space. + Matrix3x2.Invert(matrix, out matrix); + if (this.Sampler is NearestNeighborResampler) { Parallel.For( diff --git a/src/ImageSharp/Processing/Processors/Transforms/CenteredAffineTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/CenteredAffineTransformProcessor.cs index 34a0866615..1e24b7c280 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/CenteredAffineTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/CenteredAffineTransformProcessor.cs @@ -27,23 +27,14 @@ namespace SixLabors.ImageSharp.Processing.Processors /// protected override Matrix3x2 GetProcessingMatrix(Rectangle sourceRectangle, Rectangle destinationRectangle) { - var translationToTargetCenter = Matrix3x2.CreateTranslation(-destinationRectangle.Width * .5F, -destinationRectangle.Height * .5F); - var translateToSourceCenter = Matrix3x2.CreateTranslation(sourceRectangle.Width * .5F, sourceRectangle.Height * .5F); - return translationToTargetCenter * this.TransformMatrix * translateToSourceCenter; + return TransformHelpers.GetCenteredTransformMatrix(sourceRectangle, destinationRectangle, this.TransformMatrix); } /// protected override Size GetTransformedDimensions(Size sourceDimensions, Matrix3x2 matrix) { var sourceRectangle = new Rectangle(0, 0, sourceDimensions.Width, sourceDimensions.Height); - - if (!Matrix3x2.Invert(this.TransformMatrix, out Matrix3x2 sizeMatrix)) - { - // TODO: Shouldn't we throw an exception instead? - return sourceDimensions; - } - - return TransformHelpers.GetTransformedBoundingRectangle(sourceRectangle, sizeMatrix).Size; + return TransformHelpers.GetTransformedBoundingRectangle(sourceRectangle, this.TransformMatrix).Size; } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/CenteredProjectiveTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/CenteredProjectiveTransformProcessor.cs index dc2dd28ab1..92a008d7ab 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/CenteredProjectiveTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/CenteredProjectiveTransformProcessor.cs @@ -27,17 +27,14 @@ namespace SixLabors.ImageSharp.Processing.Processors /// protected override Matrix4x4 GetProcessingMatrix(Rectangle sourceRectangle, Rectangle destinationRectangle) { - var translationToTargetCenter = Matrix4x4.CreateTranslation(-destinationRectangle.Width * .5F, -destinationRectangle.Height * .5F, 0); - var translateToSourceCenter = Matrix4x4.CreateTranslation(sourceRectangle.Width * .5F, sourceRectangle.Height * .5F, 0); - return translationToTargetCenter * this.TransformMatrix * translateToSourceCenter; + return TransformHelpers.GetCenteredTransformMatrix(sourceRectangle, destinationRectangle, this.TransformMatrix); } /// - protected override Rectangle GetTransformedBoundingRectangle(Rectangle sourceRectangle, Matrix4x4 matrix) + protected override Size GetTransformedDimensions(Size sourceDimensions, Matrix4x4 matrix) { - return Matrix4x4.Invert(this.TransformMatrix, out Matrix4x4 sizeMatrix) - ? TransformHelpers.GetTransformedBoundingRectangle(sourceRectangle, sizeMatrix) - : sourceRectangle; + var sourceRectangle = new Rectangle(0, 0, sourceDimensions.Width, sourceDimensions.Height); + return TransformHelpers.GetTransformedBoundingRectangle(sourceRectangle, this.TransformMatrix).Size; } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs index 7e547727e6..90e33bddf2 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs @@ -22,8 +22,7 @@ namespace SixLabors.ImageSharp.Processing.Processors internal class ProjectiveTransformProcessor : InterpolatedTransformProcessorBase where TPixel : struct, IPixel { - // TODO: We should use a Size instead! (See AffineTransformProcessor) - private Rectangle targetRectangle; + private Size targetDimensions; /// /// Initializes a new instance of the class. @@ -40,7 +39,7 @@ namespace SixLabors.ImageSharp.Processing.Processors /// The transform matrix /// The sampler to perform the transform operation. public ProjectiveTransformProcessor(Matrix4x4 matrix, IResampler sampler) - : this(matrix, sampler, Rectangle.Empty) + : this(matrix, sampler, Size.Empty) { } @@ -49,32 +48,31 @@ namespace SixLabors.ImageSharp.Processing.Processors /// /// The transform matrix /// The sampler to perform the transform operation. - /// The rectangle to constrain the transformed image to. - public ProjectiveTransformProcessor(Matrix4x4 matrix, IResampler sampler, Rectangle rectangle) + /// The target dimensions to constrain the transformed image to. + public ProjectiveTransformProcessor(Matrix4x4 matrix, IResampler sampler, Size targetDimensions) : base(sampler) { - // Transforms are inverted else the output is the opposite of the expected. - Matrix4x4.Invert(matrix, out matrix); this.TransformMatrix = matrix; - this.targetRectangle = rectangle; + this.targetDimensions = targetDimensions; } /// - /// Gets the matrix used to supply the non-affine transform + /// Gets the matrix used to supply the projective transform /// public Matrix4x4 TransformMatrix { get; } /// protected override Image CreateDestination(Image source, Rectangle sourceRectangle) { - if (this.targetRectangle == Rectangle.Empty) + if (this.targetDimensions == Size.Empty) { - this.targetRectangle = this.GetTransformedBoundingRectangle(sourceRectangle, this.TransformMatrix); + // TODO: CreateDestination() should not modify the processors state! (kinda CQRS) + this.targetDimensions = this.GetTransformedDimensions(sourceRectangle.Size, this.TransformMatrix); } // We will always be creating the clone even for mutate because we may need to resize the canvas IEnumerable> frames = - source.Frames.Select(x => new ImageFrame(this.targetRectangle.Width, this.targetRectangle.Height, x.MetaData.Clone())); + source.Frames.Select(x => new ImageFrame(this.targetDimensions.Width, this.targetDimensions.Height, x.MetaData.Clone())); // Use the overload to prevent an extra frame being added return new Image(source.GetConfiguration(), source.MetaData.Clone(), frames); @@ -83,12 +81,17 @@ namespace SixLabors.ImageSharp.Processing.Processors /// protected override void OnApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) { - int height = this.targetRectangle.Height; - int width = this.targetRectangle.Width; + int height = this.targetDimensions.Height; + int width = this.targetDimensions.Width; + Rectangle sourceBounds = source.Bounds(); + var targetBounds = new Rectangle(0, 0, width, height); // Since could potentially be resizing the canvas we might need to re-calculate the matrix - Matrix4x4 matrix = this.GetProcessingMatrix(sourceBounds, this.targetRectangle); + Matrix4x4 matrix = this.GetProcessingMatrix(sourceBounds, targetBounds); + + // Convert from screen to world space. + Matrix4x4.Invert(matrix, out matrix); if (this.Sampler is NearestNeighborResampler) { @@ -229,12 +232,12 @@ namespace SixLabors.ImageSharp.Processing.Processors /// /// Gets the bounding relative to the source for the given transformation matrix. /// - /// The source rectangle. + /// The source rectangle. /// The transformation matrix. /// The - protected virtual Rectangle GetTransformedBoundingRectangle(Rectangle sourceRectangle, Matrix4x4 matrix) + protected virtual Size GetTransformedDimensions(Size sourceDimensions, Matrix4x4 matrix) { - return sourceRectangle; + return sourceDimensions; } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Transforms/Transform.cs b/src/ImageSharp/Processing/Transforms/Transform.cs index e39da8dc0f..326ed75862 100644 --- a/src/ImageSharp/Processing/Transforms/Transform.cs +++ b/src/ImageSharp/Processing/Transforms/Transform.cs @@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp /// The public static IImageProcessingContext Transform(this IImageProcessingContext source, Matrix3x2 matrix) where TPixel : struct, IPixel - => Transform(source, matrix, KnownResamplers.NearestNeighbor); + => Transform(source, matrix, KnownResamplers.Bicubic); /// /// Transforms an image by the given matrix using the specified sampling algorithm. @@ -90,7 +90,7 @@ namespace SixLabors.ImageSharp /// The internal static IImageProcessingContext Transform(this IImageProcessingContext source, Matrix4x4 matrix) where TPixel : struct, IPixel - => Transform(source, matrix, KnownResamplers.NearestNeighbor); + => Transform(source, matrix, KnownResamplers.Bicubic); /// /// Applies a projective transform to the image by the given matrix using the specified sampling algorithm. @@ -117,6 +117,10 @@ namespace SixLabors.ImageSharp /// The internal static IImageProcessingContext Transform(this IImageProcessingContext source, Matrix4x4 matrix, IResampler sampler, Rectangle rectangle) where TPixel : struct, IPixel - => source.ApplyProcessor(new ProjectiveTransformProcessor(matrix, sampler, rectangle)); + { + var t = Matrix4x4.CreateTranslation(new Vector3(-rectangle.Location, 0)); + Matrix4x4 combinedMatrix = t * matrix; + return source.ApplyProcessor(new ProjectiveTransformProcessor(combinedMatrix, sampler, rectangle.Size)); + } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Transforms/TransformHelpers.cs b/src/ImageSharp/Processing/Transforms/TransformHelpers.cs index bfb06c4707..1567c11619 100644 --- a/src/ImageSharp/Processing/Transforms/TransformHelpers.cs +++ b/src/ImageSharp/Processing/Transforms/TransformHelpers.cs @@ -59,7 +59,51 @@ namespace SixLabors.ImageSharp } /// - /// Returns the bounding relative to the source for the given transformation matrix. + /// Gets the centered transform matrix based upon the source and destination rectangles + /// + /// The source image bounds. + /// The destination image bounds. + /// The transformation matrix. + /// The + public static Matrix3x2 GetCenteredTransformMatrix(Rectangle sourceRectangle, Rectangle destinationRectangle, Matrix3x2 matrix) + { + // We invert the matrix to handle the transformation from screen to world space. + // This ensures scaling matrices are correct. + Matrix3x2.Invert(matrix, out Matrix3x2 inverted); + + var translationToTargetCenter = Matrix3x2.CreateTranslation(-destinationRectangle.Width * .5F, -destinationRectangle.Height * .5F); + var translateToSourceCenter = Matrix3x2.CreateTranslation(sourceRectangle.Width * .5F, sourceRectangle.Height * .5F); + + Matrix3x2.Invert(translationToTargetCenter * inverted * translateToSourceCenter, out Matrix3x2 centered); + + // Translate back to world to pass to the Transform method. + return centered; + } + + /// + /// Gets the centered transform matrix based upon the source and destination rectangles + /// + /// The source image bounds. + /// The destination image bounds. + /// The transformation matrix. + /// The + public static Matrix4x4 GetCenteredTransformMatrix(Rectangle sourceRectangle, Rectangle destinationRectangle, Matrix4x4 matrix) + { + // We invert the matrix to handle the transformation from screen to world space. + // This ensures scaling matrices are correct. + Matrix4x4.Invert(matrix, out Matrix4x4 inverted); + + var translationToTargetCenter = Matrix4x4.CreateTranslation(-destinationRectangle.Width * .5F, -destinationRectangle.Height * .5F, 0); + var translateToSourceCenter = Matrix4x4.CreateTranslation(sourceRectangle.Width * .5F, sourceRectangle.Height * .5F, 0); + + Matrix4x4.Invert(translationToTargetCenter * inverted * translateToSourceCenter, out Matrix4x4 centered); + + // Translate back to world to pass to the Transform method. + return centered; + } + + /// + /// Returns the bounding rectangle relative to the source for the given transformation matrix. /// /// The source rectangle. /// The transformation matrix. @@ -79,7 +123,7 @@ namespace SixLabors.ImageSharp } /// - /// Returns the bounding relative to the source for the given transformation matrix. + /// Returns the bounding rectangle relative to the source for the given transformation matrix. /// /// The source rectangle. /// The transformation matrix. diff --git a/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs b/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs index 6a55d8a561..5fcb860be5 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs @@ -1,17 +1,16 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System.IO; -using System.Linq; +using System; +using System.Numerics; +using SixLabors.ImageSharp.Helpers; +using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; using Xunit; namespace SixLabors.ImageSharp.Tests { - using System; - using System.Numerics; - public class DrawImageTest : FileTestBase { private const PixelTypes PixelTypes = Tests.PixelTypes.Rgba32; @@ -23,8 +22,6 @@ namespace SixLabors.ImageSharp.Tests TestImages.Gif.Rings }; - object[][] Modes = System.Enum.GetValues(typeof(PixelBlenderMode)).Cast().Select(x => new object[] { x }).ToArray(); - [Theory] [WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Normal)] [WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Multiply)] @@ -39,9 +36,10 @@ namespace SixLabors.ImageSharp.Tests where TPixel : struct, IPixel { using (Image image = provider.GetImage()) - using (Image blend = Image.Load(TestFile.Create(TestImages.Bmp.Car).Bytes)) + using (var blend = Image.Load(TestFile.Create(TestImages.Bmp.Car).Bytes)) { - image.Mutate(x => x.DrawImage(blend, mode, .75f, new Size(image.Width / 2, image.Height / 2), new Point(image.Width / 4, image.Height / 4))); + blend.Mutate(x => x.Resize(image.Width / 2, image.Height / 2)); + image.Mutate(x => x.DrawImage(blend, mode, .75f, new Point(image.Width / 4, image.Height / 4))); image.DebugSave(provider, new { mode }); } } @@ -52,15 +50,25 @@ namespace SixLabors.ImageSharp.Tests where TPixel : struct, IPixel { using (Image image = provider.GetImage()) - using (Image blend = Image.Load(TestFile.Create(TestImages.Bmp.Car).Bytes)) + using (var blend = Image.Load(TestFile.Create(TestImages.Bmp.Car).Bytes)) { Matrix3x2 rotate = Matrix3x2Extensions.CreateRotationDegrees(45F); Matrix3x2 scale = Matrix3x2Extensions.CreateScale(new SizeF(.25F, .25F)); + Matrix3x2 matrix = rotate * scale; + + // Lets center the matrix so we can tell whether any cut-off issues we may have belong to the drawing processor + Rectangle srcBounds = blend.Bounds(); + Rectangle destBounds = TransformHelpers.GetTransformedBoundingRectangle(srcBounds, matrix); + Matrix3x2 centeredMatrix = TransformHelpers.GetCenteredTransformMatrix(srcBounds, destBounds, matrix); - blend.Mutate(x => x.Transform(rotate * scale)); + // We pass a new rectangle here based on the dest bounds since we've offset the matrix + blend.Mutate(x => x.Transform( + centeredMatrix, + KnownResamplers.Bicubic, + new Rectangle(0, 0, destBounds.Width, destBounds.Height))); var position = new Point((image.Width - blend.Width) / 2, (image.Height - blend.Height) / 2); - image.Mutate(x => x.DrawImage(blend, mode, .75F, new Size(blend.Width, blend.Height), position)); + image.Mutate(x => x.DrawImage(blend, mode, .75F, position)); image.DebugSave(provider, new[] { "Transformed" }); } } @@ -78,7 +86,7 @@ namespace SixLabors.ImageSharp.Tests Rgba32 backgroundPixel = background[0, 0]; Rgba32 overlayPixel = overlay[Math.Abs(xy) + 1, Math.Abs(xy) + 1]; - background.Mutate(x => x.DrawImage(overlay, PixelBlenderMode.Normal, 1F, new Size(overlay.Width, overlay.Height), new Point(xy, xy))); + background.Mutate(x => x.DrawImage(overlay, PixelBlenderMode.Normal, 1F, new Point(xy, xy))); Assert.Equal(Rgba32.White, backgroundPixel); Assert.Equal(overlayPixel, background[0, 0]); @@ -100,7 +108,7 @@ namespace SixLabors.ImageSharp.Tests Rgba32 backgroundPixel = background[xy - 1, xy - 1]; Rgba32 overlayPixel = overlay[0, 0]; - background.Mutate(x => x.DrawImage(overlay, PixelBlenderMode.Normal, 1F, new Size(overlay.Width, overlay.Height), new Point(xy, xy))); + background.Mutate(x => x.DrawImage(overlay, PixelBlenderMode.Normal, 1F, new Point(xy, xy))); Assert.Equal(Rgba32.White, backgroundPixel); Assert.Equal(overlayPixel, background[xy, xy]); @@ -109,4 +117,4 @@ namespace SixLabors.ImageSharp.Tests } } } -} +} \ No newline at end of file From 7f4d8a542d596e8ce1605e02ce6086d8ba724218 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 23 Feb 2018 20:17:27 +1100 Subject: [PATCH 099/151] Temp disable randomly failing test. --- tests/ImageSharp.Tests/Image/PixelAccessorTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/Image/PixelAccessorTests.cs b/tests/ImageSharp.Tests/Image/PixelAccessorTests.cs index 1ab3f2ce9f..36f2628327 100644 --- a/tests/ImageSharp.Tests/Image/PixelAccessorTests.cs +++ b/tests/ImageSharp.Tests/Image/PixelAccessorTests.cs @@ -71,7 +71,7 @@ namespace SixLabors.ImageSharp.Tests [WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Xyz)] [WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Zyx)] [WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Xyzw)] - [WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Zyxw)] + // [WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Zyxw)] TODO: This fails sometimes on Travis. Investigate internal void CopyToThenCopyFromWithOffset(TestImageProvider provider, ComponentOrder order) where TPixel : struct, IPixel { From 9e51967c98d498c5ae32bcc07a943882eee45c25 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 25 Feb 2018 00:37:42 +1100 Subject: [PATCH 100/151] Make resize and transform processors immutable #473 --- .../DefaultInternalImageProcessorContext.cs | 5 +- .../IImageProcessingContext{TPixel}.cs | 8 +- .../Transforms/AffineTransformProcessor.cs | 55 +---- .../Transforms/AutoOrientProcessor.cs | 12 +- .../CenteredAffineTransformProcessor.cs | 10 +- .../CenteredProjectiveTransformProcessor.cs | 10 +- .../Processors/Transforms/CropProcessor.cs | 1 + .../ProjectiveTransformProcessor.cs | 55 +---- .../Transforms/ResamplingWeightedProcessor.cs | 164 -------------- .../Processors/Transforms/ResizeProcessor.cs | 205 +++++++++++++++++- .../Processors/Transforms/RotateProcessor.cs | 10 +- .../Processors/Transforms/SkewProcessor.cs | 10 +- .../Transforms/Options/ResizeHelper.cs | 119 +++------- .../Transforms/Options/ResizeOptions.cs | 2 +- .../Processing/Transforms/Resize.cs | 120 +++------- .../Processing/Transforms/Rotate.cs | 2 +- src/ImageSharp/Processing/Transforms/Skew.cs | 2 +- .../Processing/Transforms/Transform.cs | 4 +- .../BaseImageOperationsExtensionTest.cs | 14 +- .../FakeImageOperationsProvider.cs | 42 ++-- tests/ImageSharp.Tests/ImageOperationTests.cs | 2 +- .../Transforms/ResizeProfilingBenchmarks.cs | 7 +- 22 files changed, 348 insertions(+), 511 deletions(-) delete mode 100644 src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs diff --git a/src/ImageSharp/DefaultInternalImageProcessorContext.cs b/src/ImageSharp/DefaultInternalImageProcessorContext.cs index 6e6feed84e..cdfd582be0 100644 --- a/src/ImageSharp/DefaultInternalImageProcessorContext.cs +++ b/src/ImageSharp/DefaultInternalImageProcessorContext.cs @@ -46,6 +46,9 @@ namespace SixLabors.ImageSharp return this.destination; } + /// + public Rectangle Bounds() => this.source.Bounds(); + /// public IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle) { @@ -70,7 +73,7 @@ namespace SixLabors.ImageSharp /// public IImageProcessingContext ApplyProcessor(IImageProcessor processor) { - return this.ApplyProcessor(processor, this.source.Bounds()); + return this.ApplyProcessor(processor, this.Bounds()); } } } \ No newline at end of file diff --git a/src/ImageSharp/IImageProcessingContext{TPixel}.cs b/src/ImageSharp/IImageProcessingContext{TPixel}.cs index 552e8d579d..281c925673 100644 --- a/src/ImageSharp/IImageProcessingContext{TPixel}.cs +++ b/src/ImageSharp/IImageProcessingContext{TPixel}.cs @@ -14,6 +14,12 @@ namespace SixLabors.ImageSharp public interface IImageProcessingContext where TPixel : struct, IPixel { + /// + /// Gets the source image bounds + /// + /// The + Rectangle Bounds(); + /// /// Adds the processor to the current set of image operations to be applied. /// @@ -40,7 +46,7 @@ namespace SixLabors.ImageSharp /// /// Adds the processors to the current image /// - /// The current image or a new image depending on withere this is alloed to mutate the source image. + /// The current image or a new image depending on whether this is allowed to mutate the source image. Image Apply(); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs index 5ea23726e8..b39ae9e6a9 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs @@ -21,27 +21,6 @@ namespace SixLabors.ImageSharp.Processing.Processors internal class AffineTransformProcessor : InterpolatedTransformProcessorBase where TPixel : struct, IPixel { - private Size targetDimensions; - - /// - /// Initializes a new instance of the class. - /// - /// The transform matrix - public AffineTransformProcessor(Matrix3x2 matrix) - : this(matrix, KnownResamplers.Bicubic) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The transform matrix - /// The sampler to perform the transform operation. - public AffineTransformProcessor(Matrix3x2 matrix, IResampler sampler) - : this(matrix, sampler, Size.Empty) - { - } - /// /// Initializes a new instance of the class. /// @@ -52,7 +31,7 @@ namespace SixLabors.ImageSharp.Processing.Processors : base(sampler) { this.TransformMatrix = matrix; - this.targetDimensions = targetDimensions; + this.TargetDimensions = targetDimensions; } /// @@ -60,18 +39,17 @@ namespace SixLabors.ImageSharp.Processing.Processors /// public Matrix3x2 TransformMatrix { get; } + /// + /// Gets the target dimensions to constrain the transformed image to + /// + public Size TargetDimensions { get; } + /// protected override Image CreateDestination(Image source, Rectangle sourceRectangle) { - if (this.targetDimensions == Size.Empty) - { - // TODO: CreateDestination() should not modify the processors state! (kinda CQRS) - this.targetDimensions = this.GetTransformedDimensions(sourceRectangle.Size, this.TransformMatrix); - } - // We will always be creating the clone even for mutate because we may need to resize the canvas IEnumerable> frames = - source.Frames.Select(x => new ImageFrame(this.targetDimensions, x.MetaData.Clone())); + source.Frames.Select(x => new ImageFrame(this.TargetDimensions, x.MetaData.Clone())); // Use the overload to prevent an extra frame being added return new Image(source.GetConfiguration(), source.MetaData.Clone(), frames); @@ -84,8 +62,8 @@ namespace SixLabors.ImageSharp.Processing.Processors Rectangle sourceRectangle, Configuration configuration) { - int height = this.targetDimensions.Height; - int width = this.targetDimensions.Width; + int height = this.TargetDimensions.Height; + int width = this.TargetDimensions.Width; Rectangle sourceBounds = source.Bounds(); var targetBounds = new Rectangle(0, 0, width, height); @@ -205,8 +183,8 @@ namespace SixLabors.ImageSharp.Processing.Processors var vector = source[i, j].ToVector4(); // Values are first premultiplied to prevent darkening of edge pixels - Vector4 mupltiplied = vector.Premultiply(); - sum += mupltiplied * xWeight * yWeight; + Vector4 multiplied = vector.Premultiply(); + sum += multiplied * xWeight * yWeight; } } @@ -231,16 +209,5 @@ namespace SixLabors.ImageSharp.Processing.Processors { return this.TransformMatrix; } - - /// - /// Gets the bounding relative to the source for the given transformation matrix. - /// - /// The source rectangle. - /// The transformation matrix. - /// The - protected virtual Size GetTransformedDimensions(Size sourceDimensions, Matrix3x2 matrix) - { - return sourceDimensions; - } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs index c39311bc33..7f811eebc1 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Processing.Processors protected override void BeforeImageApply(Image source, Rectangle sourceRectangle) { Orientation orientation = GetExifOrientation(source); - + Size size = sourceRectangle.Size; switch (orientation) { case Orientation.TopRight: @@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Processing.Processors break; case Orientation.BottomRight: - new RotateProcessor((int)RotateType.Rotate180).Apply(source, sourceRectangle); + new RotateProcessor((int)RotateType.Rotate180, size).Apply(source, sourceRectangle); break; case Orientation.BottomLeft: @@ -35,21 +35,21 @@ namespace SixLabors.ImageSharp.Processing.Processors break; case Orientation.LeftTop: - new RotateProcessor((int)RotateType.Rotate90).Apply(source, sourceRectangle); + new RotateProcessor((int)RotateType.Rotate90, size).Apply(source, sourceRectangle); new FlipProcessor(FlipType.Horizontal).Apply(source, sourceRectangle); break; case Orientation.RightTop: - new RotateProcessor((int)RotateType.Rotate90).Apply(source, sourceRectangle); + new RotateProcessor((int)RotateType.Rotate90, size).Apply(source, sourceRectangle); break; case Orientation.RightBottom: new FlipProcessor(FlipType.Vertical).Apply(source, sourceRectangle); - new RotateProcessor((int)RotateType.Rotate270).Apply(source, sourceRectangle); + new RotateProcessor((int)RotateType.Rotate270, size).Apply(source, sourceRectangle); break; case Orientation.LeftBottom: - new RotateProcessor((int)RotateType.Rotate270).Apply(source, sourceRectangle); + new RotateProcessor((int)RotateType.Rotate270, size).Apply(source, sourceRectangle); break; case Orientation.Unknown: diff --git a/src/ImageSharp/Processing/Processors/Transforms/CenteredAffineTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/CenteredAffineTransformProcessor.cs index 1e24b7c280..6b8314d172 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/CenteredAffineTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/CenteredAffineTransformProcessor.cs @@ -19,8 +19,9 @@ namespace SixLabors.ImageSharp.Processing.Processors /// /// The transform matrix /// The sampler to perform the transform operation. - protected CenteredAffineTransformProcessor(Matrix3x2 matrix, IResampler sampler) - : base(matrix, sampler) + /// The source image size + protected CenteredAffineTransformProcessor(Matrix3x2 matrix, IResampler sampler, Size sourceSize) + : base(matrix, sampler, GetTransformedDimensions(sourceSize, matrix)) { } @@ -30,11 +31,10 @@ namespace SixLabors.ImageSharp.Processing.Processors return TransformHelpers.GetCenteredTransformMatrix(sourceRectangle, destinationRectangle, this.TransformMatrix); } - /// - protected override Size GetTransformedDimensions(Size sourceDimensions, Matrix3x2 matrix) + private static Size GetTransformedDimensions(Size sourceDimensions, Matrix3x2 matrix) { var sourceRectangle = new Rectangle(0, 0, sourceDimensions.Width, sourceDimensions.Height); - return TransformHelpers.GetTransformedBoundingRectangle(sourceRectangle, this.TransformMatrix).Size; + return TransformHelpers.GetTransformedBoundingRectangle(sourceRectangle, matrix).Size; } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/CenteredProjectiveTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/CenteredProjectiveTransformProcessor.cs index 92a008d7ab..081ea84610 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/CenteredProjectiveTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/CenteredProjectiveTransformProcessor.cs @@ -19,8 +19,9 @@ namespace SixLabors.ImageSharp.Processing.Processors /// /// The transform matrix /// The sampler to perform the transform operation. - protected CenteredProjectiveTransformProcessor(Matrix4x4 matrix, IResampler sampler) - : base(matrix, sampler) + /// The source image size + protected CenteredProjectiveTransformProcessor(Matrix4x4 matrix, IResampler sampler, Size sourceSize) + : base(matrix, sampler, GetTransformedDimensions(sourceSize, matrix)) { } @@ -30,11 +31,10 @@ namespace SixLabors.ImageSharp.Processing.Processors return TransformHelpers.GetCenteredTransformMatrix(sourceRectangle, destinationRectangle, this.TransformMatrix); } - /// - protected override Size GetTransformedDimensions(Size sourceDimensions, Matrix4x4 matrix) + private static Size GetTransformedDimensions(Size sourceDimensions, Matrix4x4 matrix) { var sourceRectangle = new Rectangle(0, 0, sourceDimensions.Width, sourceDimensions.Height); - return TransformHelpers.GetTransformedBoundingRectangle(sourceRectangle, this.TransformMatrix).Size; + return TransformHelpers.GetTransformedBoundingRectangle(sourceRectangle, matrix).Size; } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs index 00547d0147..91cdfac734 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs @@ -8,6 +8,7 @@ using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; +// TODO: Convert this into a cloning processor inheriting TransformProcessor once Anton's memory PR is merged namespace SixLabors.ImageSharp.Processing.Processors { /// diff --git a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs index 90e33bddf2..34147b44f9 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs @@ -22,27 +22,6 @@ namespace SixLabors.ImageSharp.Processing.Processors internal class ProjectiveTransformProcessor : InterpolatedTransformProcessorBase where TPixel : struct, IPixel { - private Size targetDimensions; - - /// - /// Initializes a new instance of the class. - /// - /// The transform matrix - public ProjectiveTransformProcessor(Matrix4x4 matrix) - : this(matrix, KnownResamplers.Bicubic) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The transform matrix - /// The sampler to perform the transform operation. - public ProjectiveTransformProcessor(Matrix4x4 matrix, IResampler sampler) - : this(matrix, sampler, Size.Empty) - { - } - /// /// Initializes a new instance of the class. /// @@ -53,7 +32,7 @@ namespace SixLabors.ImageSharp.Processing.Processors : base(sampler) { this.TransformMatrix = matrix; - this.targetDimensions = targetDimensions; + this.TargetDimensions = targetDimensions; } /// @@ -61,18 +40,17 @@ namespace SixLabors.ImageSharp.Processing.Processors /// public Matrix4x4 TransformMatrix { get; } + /// + /// Gets the target dimensions to constrain the transformed image to + /// + public Size TargetDimensions { get; } + /// protected override Image CreateDestination(Image source, Rectangle sourceRectangle) { - if (this.targetDimensions == Size.Empty) - { - // TODO: CreateDestination() should not modify the processors state! (kinda CQRS) - this.targetDimensions = this.GetTransformedDimensions(sourceRectangle.Size, this.TransformMatrix); - } - // We will always be creating the clone even for mutate because we may need to resize the canvas IEnumerable> frames = - source.Frames.Select(x => new ImageFrame(this.targetDimensions.Width, this.targetDimensions.Height, x.MetaData.Clone())); + source.Frames.Select(x => new ImageFrame(this.TargetDimensions.Width, this.TargetDimensions.Height, x.MetaData.Clone())); // Use the overload to prevent an extra frame being added return new Image(source.GetConfiguration(), source.MetaData.Clone(), frames); @@ -81,8 +59,8 @@ namespace SixLabors.ImageSharp.Processing.Processors /// protected override void OnApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) { - int height = this.targetDimensions.Height; - int width = this.targetDimensions.Width; + int height = this.TargetDimensions.Height; + int width = this.TargetDimensions.Width; Rectangle sourceBounds = source.Bounds(); var targetBounds = new Rectangle(0, 0, width, height); @@ -202,8 +180,8 @@ namespace SixLabors.ImageSharp.Processing.Processors var vector = source[i, j].ToVector4(); // Values are first premultiplied to prevent darkening of edge pixels - Vector4 mupltiplied = vector.Premultiply(); - sum += mupltiplied * xWeight * yWeight; + Vector4 multiplied = vector.Premultiply(); + sum += multiplied * xWeight * yWeight; } } @@ -228,16 +206,5 @@ namespace SixLabors.ImageSharp.Processing.Processors { return this.TransformMatrix; } - - /// - /// Gets the bounding relative to the source for the given transformation matrix. - /// - /// The source rectangle. - /// The transformation matrix. - /// The - protected virtual Size GetTransformedDimensions(Size sourceDimensions, Matrix4x4 matrix) - { - return sourceDimensions; - } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs deleted file mode 100644 index b9cb58707c..0000000000 --- a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; - -namespace SixLabors.ImageSharp.Processing.Processors -{ - /// - /// Provides methods that allow the resizing of images using various algorithms. - /// Adapted from - /// - /// The pixel format. - internal abstract class ResamplingWeightedProcessor : TransformProcessorBase - where TPixel : struct, IPixel - { - /// - /// Initializes a new instance of the class. - /// - /// The sampler to perform the resize operation. - /// The target width. - /// The target height. - /// - /// The structure that specifies the portion of the target image object to draw to. - /// - protected ResamplingWeightedProcessor(IResampler sampler, int width, int height, Rectangle resizeRectangle) - { - Guard.NotNull(sampler, nameof(sampler)); - Guard.MustBeGreaterThan(width, 0, nameof(width)); - Guard.MustBeGreaterThan(height, 0, nameof(height)); - - this.Sampler = sampler; - this.Width = width; - this.Height = height; - this.ResizeRectangle = resizeRectangle; - } - - /// - /// Gets the sampler to perform the resize operation. - /// - public IResampler Sampler { get; } - - /// - /// Gets or sets the width. - /// - public int Width { get; protected set; } - - /// - /// Gets or sets the height. - /// - public int Height { get; protected set; } - - /// - /// Gets or sets the resize rectangle. - /// - public Rectangle ResizeRectangle { get; protected set; } - - /// - /// Gets or sets the horizontal weights. - /// - protected WeightsBuffer HorizontalWeights { get; set; } - - /// - /// Gets or sets the vertical weights. - /// - protected WeightsBuffer VerticalWeights { get; set; } - - /// - /// Computes the weights to apply at each pixel when resizing. - /// - /// The destination size - /// The source size - /// The - // TODO: Made internal to simplify experimenting with weights data. Make it protected again when finished figuring out how to optimize all the stuff! - internal unsafe WeightsBuffer PrecomputeWeights(int destinationSize, int sourceSize) - { - float ratio = (float)sourceSize / destinationSize; - float scale = ratio; - - if (scale < 1F) - { - scale = 1F; - } - - IResampler sampler = this.Sampler; - float radius = MathF.Ceiling(scale * sampler.Radius); - var result = new WeightsBuffer(sourceSize, destinationSize); - - for (int i = 0; i < destinationSize; i++) - { - float center = ((i + .5F) * ratio) - .5F; - - // Keep inside bounds. - int left = (int)Math.Ceiling(center - radius); - if (left < 0) - { - left = 0; - } - - int right = (int)Math.Floor(center + radius); - if (right > sourceSize - 1) - { - right = sourceSize - 1; - } - - float sum = 0; - - WeightsWindow ws = result.GetWeightsWindow(i, left, right); - result.Weights[i] = ws; - - ref float weightsBaseRef = ref ws.GetStartReference(); - - for (int j = left; j <= right; j++) - { - float weight = sampler.GetValue((j - center) / scale); - sum += weight; - - // weights[j - left] = weight: - Unsafe.Add(ref weightsBaseRef, j - left) = weight; - } - - // Normalise, best to do it here rather than in the pixel loop later on. - if (sum > 0) - { - for (int w = 0; w < ws.Length; w++) - { - // weights[w] = weights[w] / sum: - ref float wRef = ref Unsafe.Add(ref weightsBaseRef, w); - wRef = wRef / sum; - } - } - } - - return result; - } - - /// - protected override void BeforeApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) - { - if (!(this.Sampler is NearestNeighborResampler)) - { - this.HorizontalWeights = this.PrecomputeWeights( - this.ResizeRectangle.Width, - sourceRectangle.Width); - - this.VerticalWeights = this.PrecomputeWeights( - this.ResizeRectangle.Height, - sourceRectangle.Height); - } - } - - /// - protected override void AfterApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) - { - base.AfterApply(source, destination, sourceRectangle, configuration); - this.HorizontalWeights?.Dispose(); - this.HorizontalWeights = null; - this.VerticalWeights?.Dispose(); - this.VerticalWeights = null; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs index b05d77868f..bccf665a48 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs @@ -5,10 +5,10 @@ using System; using System.Collections.Generic; using System.Linq; using System.Numerics; +using System.Runtime.CompilerServices; using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.MetaData.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -16,19 +16,62 @@ namespace SixLabors.ImageSharp.Processing.Processors { /// /// Provides methods that allow the resizing of images using various algorithms. + /// Adapted from /// /// The pixel format. - internal class ResizeProcessor : ResamplingWeightedProcessor + internal class ResizeProcessor : TransformProcessorBase where TPixel : struct, IPixel { + // The following fields are not immutable but are optionally created on demand. + private WeightsBuffer horizontalWeights; + private WeightsBuffer verticalWeights; + + /// + /// Initializes a new instance of the class. + /// + /// The resize options + /// The source image size + public ResizeProcessor(ResizeOptions options, Size sourceSize) + { + Guard.NotNull(options, nameof(options)); + Guard.NotNull(options.Sampler, nameof(options.Sampler)); + + int tempWidth = options.Size.Width; + int tempHeight = options.Size.Height; + + // Ensure size is populated across both dimensions. + // These dimensions are used to calculate the final dimensions determined by the mode algorithm. + if (tempWidth == 0 && tempHeight > 0) + { + tempWidth = (int)MathF.Round(sourceSize.Width * tempHeight / (float)sourceSize.Height); + } + + if (tempHeight == 0 && tempWidth > 0) + { + tempHeight = (int)MathF.Round(sourceSize.Height * tempWidth / (float)sourceSize.Width); + } + + Guard.MustBeGreaterThan(tempWidth, 0, nameof(tempWidth)); + Guard.MustBeGreaterThan(tempHeight, 0, nameof(tempHeight)); + + (Size size, Rectangle rectangle) locationBounds = ResizeHelper.CalculateTargetLocationAndBounds(sourceSize, options, tempWidth, tempHeight); + + this.Sampler = options.Sampler; + this.Width = locationBounds.size.Width; + this.Height = locationBounds.size.Height; + this.ResizeRectangle = locationBounds.rectangle; + this.Compand = options.Compand; + } + /// /// Initializes a new instance of the class. /// /// The sampler to perform the resize operation. /// The target width. /// The target height. - public ResizeProcessor(IResampler sampler, int width, int height) - : base(sampler, width, height, new Rectangle(0, 0, width, height)) + /// The source image size + public ResizeProcessor(IResampler sampler, int width, int height, Size sourceSize) + : this(sampler, width, height, sourceSize, new Rectangle(0, 0, width, height), false) { } @@ -38,18 +81,131 @@ namespace SixLabors.ImageSharp.Processing.Processors /// The sampler to perform the resize operation. /// The target width. /// The target height. + /// The source image size /// /// The structure that specifies the portion of the target image object to draw to. /// - public ResizeProcessor(IResampler sampler, int width, int height, Rectangle resizeRectangle) - : base(sampler, width, height, resizeRectangle) + /// Whether to compress or expand individual pixel color values on processing. + public ResizeProcessor(IResampler sampler, int width, int height, Size sourceSize, Rectangle resizeRectangle, bool compand) { + Guard.NotNull(sampler, nameof(sampler)); + + // Ensure size is populated across both dimensions. + if (width == 0 && height > 0) + { + width = (int)MathF.Round(sourceSize.Width * height / (float)sourceSize.Height); + resizeRectangle.Width = width; + } + + if (height == 0 && width > 0) + { + height = (int)MathF.Round(sourceSize.Height * width / (float)sourceSize.Width); + resizeRectangle.Height = height; + } + + Guard.MustBeGreaterThan(width, 0, nameof(width)); + Guard.MustBeGreaterThan(height, 0, nameof(height)); + + this.Sampler = sampler; + this.Width = width; + this.Height = height; + this.ResizeRectangle = resizeRectangle; + this.Compand = compand; } /// - /// Gets or sets a value indicating whether to compress or expand individual pixel color values on processing. + /// Gets the sampler to perform the resize operation. /// - public bool Compand { get; set; } + public IResampler Sampler { get; } + + /// + /// Gets the target width. + /// + public int Width { get; } + + /// + /// Gets the target height. + /// + public int Height { get; } + + /// + /// Gets the resize rectangle. + /// + public Rectangle ResizeRectangle { get; } + + /// + /// Gets a value indicating whether to compress or expand individual pixel color values on processing. + /// + public bool Compand { get; } + + /// + /// Computes the weights to apply at each pixel when resizing. + /// + /// The destination size + /// The source size + /// The + // TODO: Made internal to simplify experimenting with weights data. Make it private when finished figuring out how to optimize all the stuff! + internal WeightsBuffer PrecomputeWeights(int destinationSize, int sourceSize) + { + float ratio = (float)sourceSize / destinationSize; + float scale = ratio; + + if (scale < 1F) + { + scale = 1F; + } + + IResampler sampler = this.Sampler; + float radius = MathF.Ceiling(scale * sampler.Radius); + var result = new WeightsBuffer(sourceSize, destinationSize); + + for (int i = 0; i < destinationSize; i++) + { + float center = ((i + .5F) * ratio) - .5F; + + // Keep inside bounds. + int left = (int)Math.Ceiling(center - radius); + if (left < 0) + { + left = 0; + } + + int right = (int)Math.Floor(center + radius); + if (right > sourceSize - 1) + { + right = sourceSize - 1; + } + + float sum = 0; + + WeightsWindow ws = result.GetWeightsWindow(i, left, right); + result.Weights[i] = ws; + + ref float weightsBaseRef = ref ws.GetStartReference(); + + for (int j = left; j <= right; j++) + { + float weight = sampler.GetValue((j - center) / scale); + sum += weight; + + // weights[j - left] = weight: + Unsafe.Add(ref weightsBaseRef, j - left) = weight; + } + + // Normalize, best to do it here rather than in the pixel loop later on. + if (sum > 0) + { + for (int w = 0; w < ws.Length; w++) + { + // weights[w] = weights[w] / sum: + ref float wRef = ref Unsafe.Add(ref weightsBaseRef, w); + wRef = wRef / sum; + } + } + } + + return result; + } /// protected override Image CreateDestination(Image source, Rectangle sourceRectangle) @@ -62,6 +218,21 @@ namespace SixLabors.ImageSharp.Processing.Processors return new Image(source.GetConfiguration(), source.MetaData.Clone(), frames); } + /// + protected override void BeforeApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) + { + if (!(this.Sampler is NearestNeighborResampler)) + { + this.horizontalWeights = this.PrecomputeWeights( + this.ResizeRectangle.Width, + sourceRectangle.Width); + + this.verticalWeights = this.PrecomputeWeights( + this.ResizeRectangle.Height, + sourceRectangle.Height); + } + } + /// protected override void OnApply(ImageFrame source, ImageFrame cloned, Rectangle sourceRectangle, Configuration configuration) { @@ -139,7 +310,7 @@ namespace SixLabors.ImageSharp.Processing.Processors { for (int x = minX; x < maxX; x++) { - WeightsWindow window = this.HorizontalWeights.Weights[x - startX]; + WeightsWindow window = this.horizontalWeights.Weights[x - startX]; firstPassRow[x] = window.ComputeExpandedWeightedRowSum(tempRowBuffer, sourceX); } } @@ -147,7 +318,7 @@ namespace SixLabors.ImageSharp.Processing.Processors { for (int x = minX; x < maxX; x++) { - WeightsWindow window = this.HorizontalWeights.Weights[x - startX]; + WeightsWindow window = this.horizontalWeights.Weights[x - startX]; firstPassRow[x] = window.ComputeWeightedRowSum(tempRowBuffer, sourceX); } } @@ -161,8 +332,8 @@ namespace SixLabors.ImageSharp.Processing.Processors configuration.ParallelOptions, y => { - // Ensure offsets are normalised for cropping and padding. - WeightsWindow window = this.VerticalWeights.Weights[y - startY]; + // Ensure offsets are normalized for cropping and padding. + WeightsWindow window = this.verticalWeights.Weights[y - startY]; Span targetRow = cloned.GetPixelRowSpan(y); if (this.Compand) @@ -191,5 +362,15 @@ namespace SixLabors.ImageSharp.Processing.Processors }); } } + + /// + protected override void AfterApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) + { + base.AfterApply(source, destination, sourceRectangle, configuration); + this.horizontalWeights?.Dispose(); + this.horizontalWeights = null; + this.verticalWeights?.Dispose(); + this.verticalWeights = null; + } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs index b93c869152..824ae4310f 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs @@ -22,8 +22,9 @@ namespace SixLabors.ImageSharp.Processing.Processors /// Initializes a new instance of the class. /// /// The angle of rotation in degrees. - public RotateProcessor(float degrees) - : this(degrees, KnownResamplers.Bicubic) + /// The source image size + public RotateProcessor(float degrees, Size sourceSize) + : this(degrees, KnownResamplers.Bicubic, sourceSize) { } @@ -32,8 +33,9 @@ namespace SixLabors.ImageSharp.Processing.Processors /// /// The angle of rotation in degrees. /// The sampler to perform the rotating operation. - public RotateProcessor(float degrees, IResampler sampler) - : base(Matrix3x2Extensions.CreateRotationDegrees(degrees, PointF.Empty), sampler) + /// The source image size + public RotateProcessor(float degrees, IResampler sampler, Size sourceSize) + : base(Matrix3x2Extensions.CreateRotationDegrees(degrees, PointF.Empty), sampler, sourceSize) { this.Degrees = degrees; } diff --git a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs index 8c47b35274..8e3ab7c34f 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs @@ -18,8 +18,9 @@ namespace SixLabors.ImageSharp.Processing.Processors /// /// The angle in degrees to perform the skew along the x-axis. /// The angle in degrees to perform the skew along the y-axis. - public SkewProcessor(float degreesX, float degreesY) - : this(degreesX, degreesY, KnownResamplers.Bicubic) + /// The source image size + public SkewProcessor(float degreesX, float degreesY, Size sourceSize) + : this(degreesX, degreesY, KnownResamplers.Bicubic, sourceSize) { } @@ -29,8 +30,9 @@ namespace SixLabors.ImageSharp.Processing.Processors /// The angle in degrees to perform the skew along the x-axis. /// The angle in degrees to perform the skew along the y-axis. /// The sampler to perform the skew operation. - public SkewProcessor(float degreesX, float degreesY, IResampler sampler) - : base(Matrix3x2Extensions.CreateSkewDegrees(degreesX, degreesY, PointF.Empty), sampler) + /// The source image size + public SkewProcessor(float degreesX, float degreesY, IResampler sampler, Size sourceSize) + : base(Matrix3x2Extensions.CreateSkewDegrees(degreesX, degreesY, PointF.Empty), sampler, sourceSize) { this.DegreesX = degreesX; this.DegreesY = degreesY; diff --git a/src/ImageSharp/Processing/Transforms/Options/ResizeHelper.cs b/src/ImageSharp/Processing/Transforms/Options/ResizeHelper.cs index 17a0cc428f..ba6f4509d8 100644 --- a/src/ImageSharp/Processing/Transforms/Options/ResizeHelper.cs +++ b/src/ImageSharp/Processing/Transforms/Options/ResizeHelper.cs @@ -3,7 +3,6 @@ using System; using System.Linq; -using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing @@ -17,52 +16,39 @@ namespace SixLabors.ImageSharp.Processing /// /// Calculates the target location and bounds to perform the resize operation against. /// - /// The pixel format. - /// The source image. + /// The source image size. /// The resize options. + /// The target width + /// The target height /// - /// The . + /// The . /// - public static Rectangle CalculateTargetLocationAndBounds(ImageFrame source, ResizeOptions options) - where TPixel : struct, IPixel + public static (Size, Rectangle) CalculateTargetLocationAndBounds(Size sourceSize, ResizeOptions options, int width, int height) { switch (options.Mode) { case ResizeMode.Crop: - return CalculateCropRectangle(source, options); + return CalculateCropRectangle(sourceSize, options, width, height); case ResizeMode.Pad: - return CalculatePadRectangle(source, options); + return CalculatePadRectangle(sourceSize, options, width, height); case ResizeMode.BoxPad: - return CalculateBoxPadRectangle(source, options); + return CalculateBoxPadRectangle(sourceSize, options, width, height); case ResizeMode.Max: - return CalculateMaxRectangle(source, options); + return CalculateMaxRectangle(sourceSize, options, width, height); case ResizeMode.Min: - return CalculateMinRectangle(source, options); + return CalculateMinRectangle(sourceSize, options, width, height); // Last case ResizeMode.Stretch: default: - return new Rectangle(0, 0, options.Size.Width, options.Size.Height); + return (new Size(width, height), new Rectangle(0, 0, width, height)); } } - /// - /// Calculates the target rectangle for crop mode. - /// - /// The pixel format. - /// The source image. - /// The resize options. - /// - /// The . - /// - private static Rectangle CalculateCropRectangle(ImageFrame source, ResizeOptions options) - where TPixel : struct, IPixel + private static (Size, Rectangle) CalculateCropRectangle(Size source, ResizeOptions options, int width, int height) { - int width = options.Size.Width; - int height = options.Size.Height; - if (width <= 0 || height <= 0) { - return new Rectangle(0, 0, source.Width, source.Height); + return (new Size(source.Width, source.Height), new Rectangle(0, 0, source.Width, source.Height)); } float ratio; @@ -161,27 +147,14 @@ namespace SixLabors.ImageSharp.Processing destinationWidth = (int)MathF.Ceiling(sourceWidth * percentHeight); } - return new Rectangle(destinationX, destinationY, destinationWidth, destinationHeight); + return (new Size(width, height), new Rectangle(destinationX, destinationY, destinationWidth, destinationHeight)); } - /// - /// Calculates the target rectangle for pad mode. - /// - /// The pixel format. - /// The source image. - /// The resize options. - /// - /// The . - /// - private static Rectangle CalculatePadRectangle(ImageFrame source, ResizeOptions options) - where TPixel : struct, IPixel + private static (Size, Rectangle) CalculatePadRectangle(Size source, ResizeOptions options, int width, int height) { - int width = options.Size.Width; - int height = options.Size.Height; - if (width <= 0 || height <= 0) { - return new Rectangle(0, 0, source.Width, source.Height); + return (new Size(source.Width, source.Height), new Rectangle(0, 0, source.Width, source.Height)); } float ratio; @@ -242,27 +215,14 @@ namespace SixLabors.ImageSharp.Processing } } - return new Rectangle(destinationX, destinationY, destinationWidth, destinationHeight); + return (new Size(width, height), new Rectangle(destinationX, destinationY, destinationWidth, destinationHeight)); } - /// - /// Calculates the target rectangle for box pad mode. - /// - /// The pixel format. - /// The source image. - /// The resize options. - /// - /// The . - /// - private static Rectangle CalculateBoxPadRectangle(ImageFrame source, ResizeOptions options) - where TPixel : struct, IPixel + private static (Size, Rectangle) CalculateBoxPadRectangle(Size source, ResizeOptions options, int width, int height) { - int width = options.Size.Width; - int height = options.Size.Height; - if (width <= 0 || height <= 0) { - return new Rectangle(0, 0, source.Width, source.Height); + return (new Size(source.Width, source.Height), new Rectangle(0, 0, source.Width, source.Height)); } int sourceWidth = source.Width; @@ -325,27 +285,15 @@ namespace SixLabors.ImageSharp.Processing break; } - return new Rectangle(destinationX, destinationY, destinationWidth, destinationHeight); + return (new Size(width, height), new Rectangle(destinationX, destinationY, destinationWidth, destinationHeight)); } // Switch to pad mode to downscale and calculate from there. - return CalculatePadRectangle(source, options); + return CalculatePadRectangle(source, options, width, height); } - /// - /// Calculates the target rectangle for max mode. - /// - /// The pixel format. - /// The source image. - /// The resize options. - /// - /// The . - /// - private static Rectangle CalculateMaxRectangle(ImageFrame source, ResizeOptions options) - where TPixel : struct, IPixel + private static (Size, Rectangle) CalculateMaxRectangle(Size source, ResizeOptions options, int width, int height) { - int width = options.Size.Width; - int height = options.Size.Height; int destinationWidth = width; int destinationHeight = height; @@ -369,24 +317,11 @@ namespace SixLabors.ImageSharp.Processing } // Replace the size to match the rectangle. - options.Size = new Size(width, height); - return new Rectangle(0, 0, destinationWidth, destinationHeight); + return (new Size(width, height), new Rectangle(0, 0, destinationWidth, destinationHeight)); } - /// - /// Calculates the target rectangle for min mode. - /// - /// The pixel format. - /// The source image. - /// The resize options. - /// - /// The . - /// - private static Rectangle CalculateMinRectangle(ImageFrame source, ResizeOptions options) - where TPixel : struct, IPixel + private static (Size, Rectangle) CalculateMinRectangle(Size source, ResizeOptions options, int width, int height) { - int width = options.Size.Width; - int height = options.Size.Height; int sourceWidth = source.Width; int sourceHeight = source.Height; int destinationWidth; @@ -395,8 +330,7 @@ namespace SixLabors.ImageSharp.Processing // Don't upscale if (width > sourceWidth || height > sourceHeight) { - options.Size = new Size(sourceWidth, sourceHeight); - return new Rectangle(0, 0, sourceWidth, sourceHeight); + return (new Size(sourceWidth, sourceWidth), new Rectangle(0, 0, sourceWidth, sourceHeight)); } // Fractional variants for preserving aspect ratio. @@ -438,8 +372,7 @@ namespace SixLabors.ImageSharp.Processing } // Replace the size to match the rectangle. - options.Size = new Size(width, height); - return new Rectangle(0, 0, destinationWidth, destinationHeight); + return (new Size(width, height), new Rectangle(0, 0, destinationWidth, destinationHeight)); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Transforms/Options/ResizeOptions.cs b/src/ImageSharp/Processing/Transforms/Options/ResizeOptions.cs index d20eaefb11..f13fa77c99 100644 --- a/src/ImageSharp/Processing/Transforms/Options/ResizeOptions.cs +++ b/src/ImageSharp/Processing/Transforms/Options/ResizeOptions.cs @@ -41,6 +41,6 @@ namespace SixLabors.ImageSharp.Processing /// Gets or sets a value indicating whether to compress /// or expand individual pixel colors the value on processing. /// - public bool Compand { get; set; } + public bool Compand { get; set; } = false; } } diff --git a/src/ImageSharp/Processing/Transforms/Resize.cs b/src/ImageSharp/Processing/Transforms/Resize.cs index 832b02dea7..853f461339 100644 --- a/src/ImageSharp/Processing/Transforms/Resize.cs +++ b/src/ImageSharp/Processing/Transforms/Resize.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors; @@ -24,29 +23,10 @@ namespace SixLabors.ImageSharp /// Passing zero for one of height or width within the resize options will automatically preserve the aspect ratio of the original image public static IImageProcessingContext Resize(this IImageProcessingContext source, ResizeOptions options) where TPixel : struct, IPixel - { - return source.Apply(img => - { - // Cheat and bound through a run, inside here we should just be mutating, this really needs moving over to a processor - // Ensure size is populated across both dimensions. - if (options.Size.Width == 0 && options.Size.Height > 0) - { - options.Size = new Size((int)MathF.Round(img.Width * options.Size.Height / (float)img.Height), options.Size.Height); - } - - if (options.Size.Height == 0 && options.Size.Width > 0) - { - options.Size = new Size(options.Size.Width, (int)MathF.Round(img.Height * options.Size.Width / (float)img.Width)); - } - - Rectangle targetRectangle = ResizeHelper.CalculateTargetLocationAndBounds(img.Frames.RootFrame, options); - - img.Mutate(x => Resize(x, options.Size.Width, options.Size.Height, options.Sampler, targetRectangle, options.Compand)); - }); - } + => source.ApplyProcessor(new ResizeProcessor(options, source.Bounds().Size)); /// - /// Resizes an image to the given . + /// Resizes an image to the given . /// /// The pixel format. /// The image to resize. @@ -55,12 +35,10 @@ namespace SixLabors.ImageSharp /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image public static IImageProcessingContext Resize(this IImageProcessingContext source, Size size) where TPixel : struct, IPixel - { - return Resize(source, size.Width, size.Height, KnownResamplers.Bicubic, false); - } + => Resize(source, size.Width, size.Height, KnownResamplers.Bicubic, false); /// - /// Resizes an image to the given . + /// Resizes an image to the given . /// /// The pixel format. /// The image to resize. @@ -70,9 +48,7 @@ namespace SixLabors.ImageSharp /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image public static IImageProcessingContext Resize(this IImageProcessingContext source, Size size, bool compand) where TPixel : struct, IPixel - { - return Resize(source, size.Width, size.Height, KnownResamplers.Bicubic, compand); - } + => Resize(source, size.Width, size.Height, KnownResamplers.Bicubic, compand); /// /// Resizes an image to the given width and height. @@ -85,9 +61,7 @@ namespace SixLabors.ImageSharp /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height) where TPixel : struct, IPixel - { - return Resize(source, width, height, KnownResamplers.Bicubic, false); - } + => Resize(source, width, height, KnownResamplers.Bicubic, false); /// /// Resizes an image to the given width and height. @@ -101,9 +75,7 @@ namespace SixLabors.ImageSharp /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, bool compand) where TPixel : struct, IPixel - { - return Resize(source, width, height, KnownResamplers.Bicubic, compand); - } + => Resize(source, width, height, KnownResamplers.Bicubic, compand); /// /// Resizes an image to the given width and height with the given sampler. @@ -117,9 +89,7 @@ namespace SixLabors.ImageSharp /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, IResampler sampler) where TPixel : struct, IPixel - { - return Resize(source, width, height, sampler, false); - } + => Resize(source, width, height, sampler, false); /// /// Resizes an image to the given width and height with the given sampler. @@ -133,9 +103,7 @@ namespace SixLabors.ImageSharp /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image public static IImageProcessingContext Resize(this IImageProcessingContext source, Size size, IResampler sampler, bool compand) where TPixel : struct, IPixel - { - return Resize(source, size.Width, size.Height, sampler, new Rectangle(0, 0, size.Width, size.Height), compand); - } + => Resize(source, size.Width, size.Height, sampler, new Rectangle(0, 0, size.Width, size.Height), compand); /// /// Resizes an image to the given width and height with the given sampler. @@ -150,9 +118,7 @@ namespace SixLabors.ImageSharp /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, IResampler sampler, bool compand) where TPixel : struct, IPixel - { - return Resize(source, width, height, sampler, new Rectangle(0, 0, width, height), compand); - } + => Resize(source, width, height, sampler, new Rectangle(0, 0, width, height), compand); /// /// Resizes an image to the given width and height with the given sampler and @@ -172,34 +138,19 @@ namespace SixLabors.ImageSharp /// Whether to compress and expand the image color-space to gamma correct the image during processing. /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image - public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, IResampler sampler, Rectangle sourceRectangle, Rectangle targetRectangle, bool compand) + public static IImageProcessingContext Resize( + this IImageProcessingContext source, + int width, + int height, + IResampler sampler, + Rectangle sourceRectangle, + Rectangle targetRectangle, + bool compand) where TPixel : struct, IPixel - { - return source.Apply(img => - { - // TODO : Stop cheating here and move this stuff into the processors itself - if (width == 0 && height > 0) - { - width = (int)MathF.Round(img.Width * height / (float)img.Height); - targetRectangle.Width = width; - } - - if (height == 0 && width > 0) - { - height = (int)MathF.Round(img.Height * width / (float)img.Width); - targetRectangle.Height = height; - } - - Guard.MustBeGreaterThan(width, 0, nameof(width)); - Guard.MustBeGreaterThan(height, 0, nameof(height)); - - img.Mutate(x => x.ApplyProcessor(new ResizeProcessor(sampler, width, height, targetRectangle) { Compand = compand }, sourceRectangle)); - }); - } + => source.ApplyProcessor(new ResizeProcessor(sampler, width, height, source.Bounds().Size, targetRectangle, compand), sourceRectangle); /// - /// Resizes an image to the given width and height with the given sampler and - /// source rectangle. + /// Resizes an image to the given width and height with the given sampler and source rectangle. /// /// The pixel format. /// The image to resize. @@ -212,29 +163,14 @@ namespace SixLabors.ImageSharp /// Whether to compress and expand the image color-space to gamma correct the image during processing. /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image - public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, IResampler sampler, Rectangle targetRectangle, bool compand) + public static IImageProcessingContext Resize( + this IImageProcessingContext source, + int width, + int height, + IResampler sampler, + Rectangle targetRectangle, + bool compand) where TPixel : struct, IPixel - { - return source.Apply(img => - { - // TODO : stop cheating here and move this stuff into the processors itself - if (width == 0 && height > 0) - { - width = (int)MathF.Round(img.Width * height / (float)img.Height); - targetRectangle.Width = width; - } - - if (height == 0 && width > 0) - { - height = (int)MathF.Round(img.Height * width / (float)img.Width); - targetRectangle.Height = height; - } - - Guard.MustBeGreaterThan(width, 0, nameof(width)); - Guard.MustBeGreaterThan(height, 0, nameof(height)); - - img.Mutate(x => x.ApplyProcessor(new ResizeProcessor(sampler, width, height, targetRectangle) { Compand = compand })); - }); - } + => source.ApplyProcessor(new ResizeProcessor(sampler, width, height, source.Bounds().Size, targetRectangle, compand)); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Transforms/Rotate.cs b/src/ImageSharp/Processing/Transforms/Rotate.cs index 69fb7ebf0c..6ffefca5cf 100644 --- a/src/ImageSharp/Processing/Transforms/Rotate.cs +++ b/src/ImageSharp/Processing/Transforms/Rotate.cs @@ -44,6 +44,6 @@ namespace SixLabors.ImageSharp /// The public static IImageProcessingContext Rotate(this IImageProcessingContext source, float degrees, IResampler sampler) where TPixel : struct, IPixel - => source.ApplyProcessor(new RotateProcessor(degrees, sampler)); + => source.ApplyProcessor(new RotateProcessor(degrees, sampler, source.Bounds().Size)); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Transforms/Skew.cs b/src/ImageSharp/Processing/Transforms/Skew.cs index 0613a690b8..25db33059a 100644 --- a/src/ImageSharp/Processing/Transforms/Skew.cs +++ b/src/ImageSharp/Processing/Transforms/Skew.cs @@ -35,6 +35,6 @@ namespace SixLabors.ImageSharp /// The public static IImageProcessingContext Skew(this IImageProcessingContext source, float degreesX, float degreesY, IResampler sampler) where TPixel : struct, IPixel - => source.ApplyProcessor(new SkewProcessor(degreesX, degreesY, sampler)); + => source.ApplyProcessor(new SkewProcessor(degreesX, degreesY, sampler, source.Bounds().Size)); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Transforms/Transform.cs b/src/ImageSharp/Processing/Transforms/Transform.cs index 326ed75862..58d3ae13e7 100644 --- a/src/ImageSharp/Processing/Transforms/Transform.cs +++ b/src/ImageSharp/Processing/Transforms/Transform.cs @@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp /// The public static IImageProcessingContext Transform(this IImageProcessingContext source, Matrix3x2 matrix, IResampler sampler) where TPixel : struct, IPixel - => Transform(source, matrix, sampler, Size.Empty); + => Transform(source, matrix, sampler, source.Bounds()); /// /// Transforms an image by the given matrix using the specified sampling algorithm @@ -103,7 +103,7 @@ namespace SixLabors.ImageSharp /// The internal static IImageProcessingContext Transform(this IImageProcessingContext source, Matrix4x4 matrix, IResampler sampler) where TPixel : struct, IPixel - => Transform(source, matrix, sampler, Rectangle.Empty); + => Transform(source, matrix, sampler, source.Bounds()); /// /// Applies a projective transform to the image by the given matrix using the specified sampling algorithm. diff --git a/tests/ImageSharp.Tests/BaseImageOperationsExtensionTest.cs b/tests/ImageSharp.Tests/BaseImageOperationsExtensionTest.cs index 37696987cc..ec46e66107 100644 --- a/tests/ImageSharp.Tests/BaseImageOperationsExtensionTest.cs +++ b/tests/ImageSharp.Tests/BaseImageOperationsExtensionTest.cs @@ -16,29 +16,31 @@ namespace SixLabors.ImageSharp.Tests private readonly FakeImageOperationsProvider.FakeImageOperations internalOperations; protected readonly Rectangle rect; protected readonly GraphicsOptions options; + private Image source; public BaseImageOperationsExtensionTest() { - this.options = new GraphicsOptions(false) { }; + this.options = new GraphicsOptions(false); + this.source = new Image(91 + 324, 123 + 56); this.rect = new Rectangle(91, 123, 324, 56); // make this random? - this.internalOperations = new FakeImageOperationsProvider.FakeImageOperations(null, false); + this.internalOperations = new FakeImageOperationsProvider.FakeImageOperations(this.source, false); this.operations = this.internalOperations; } public T Verify(int index = 0) { - Assert.InRange(index, 0, this.internalOperations.applied.Count - 1); + Assert.InRange(index, 0, this.internalOperations.Applied.Count - 1); - var operation = this.internalOperations.applied[index]; + var operation = this.internalOperations.Applied[index]; return Assert.IsType(operation.Processor); } public T Verify(Rectangle rect, int index = 0) { - Assert.InRange(index, 0, this.internalOperations.applied.Count - 1); + Assert.InRange(index, 0, this.internalOperations.Applied.Count - 1); - var operation = this.internalOperations.applied[index]; + var operation = this.internalOperations.Applied[index]; Assert.Equal(rect, operation.Rectangle); return Assert.IsType(operation.Processor); diff --git a/tests/ImageSharp.Tests/FakeImageOperationsProvider.cs b/tests/ImageSharp.Tests/FakeImageOperationsProvider.cs index f750bfcfad..9803af9f8d 100644 --- a/tests/ImageSharp.Tests/FakeImageOperationsProvider.cs +++ b/tests/ImageSharp.Tests/FakeImageOperationsProvider.cs @@ -1,12 +1,11 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; using System.Collections.Generic; using System.Linq; -using System.Text; + +using SixLabors.ImageSharp.Helpers; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Tests @@ -23,13 +22,13 @@ namespace SixLabors.ImageSharp.Tests public IEnumerable> Created(Image source) where TPixel : struct, IPixel { return this.ImageOperators.OfType>() - .Where(x => x.source == source); + .Where(x => x.Source == source); } - public IEnumerable.AppliedOpperation> AppliedOperations(Image source) where TPixel : struct, IPixel + public IEnumerable.AppliedOperation> AppliedOperations(Image source) where TPixel : struct, IPixel { return Created(source) - .SelectMany(x => x.applied); + .SelectMany(x => x.Applied); } public IInternalImageProcessingContext CreateImageProcessingContext(Image source, bool mutate) where TPixel : struct, IPixel @@ -43,32 +42,31 @@ namespace SixLabors.ImageSharp.Tests public class FakeImageOperations : IInternalImageProcessingContext where TPixel : struct, IPixel { - public Image source; - - public List applied = new List(); - public bool mutate; + private bool mutate; public FakeImageOperations(Image source, bool mutate) { this.mutate = mutate; - if (mutate) - { - this.source = source; - } - else - { - this.source = source?.Clone(); - } + this.Source = mutate ? source : source?.Clone(); } + public Image Source { get; } + + public List Applied { get; } = new List(); + public Image Apply() { - return source; + return this.Source; + } + + public Rectangle Bounds() + { + return this.Source.Bounds(); } public IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle) { - applied.Add(new AppliedOpperation + this.Applied.Add(new AppliedOperation { Processor = processor, Rectangle = rectangle @@ -78,13 +76,13 @@ namespace SixLabors.ImageSharp.Tests public IImageProcessingContext ApplyProcessor(IImageProcessor processor) { - applied.Add(new AppliedOpperation + this.Applied.Add(new AppliedOperation { Processor = processor }); return this; } - public struct AppliedOpperation + public struct AppliedOperation { public Rectangle? Rectangle { get; set; } public IImageProcessor Processor { get; set; } diff --git a/tests/ImageSharp.Tests/ImageOperationTests.cs b/tests/ImageSharp.Tests/ImageOperationTests.cs index 59722a84d2..a5d6d2eb96 100644 --- a/tests/ImageSharp.Tests/ImageOperationTests.cs +++ b/tests/ImageSharp.Tests/ImageOperationTests.cs @@ -93,7 +93,7 @@ namespace SixLabors.ImageSharp.Tests { var operations = new FakeImageOperationsProvider.FakeImageOperations(null, false); operations.ApplyProcessors(this.processor); - Assert.Contains(this.processor, operations.applied.Select(x => x.Processor)); + Assert.Contains(this.processor, operations.Applied.Select(x => x.Processor)); } public void Dispose() diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeProfilingBenchmarks.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeProfilingBenchmarks.cs index 98dbbadaba..77471b2ba2 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeProfilingBenchmarks.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeProfilingBenchmarks.cs @@ -11,6 +11,8 @@ using Xunit.Abstractions; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms { + using SixLabors.Primitives; + public class ResizeProfilingBenchmarks : MeasureFixture { public ResizeProfilingBenchmarks(ITestOutputHelper output) @@ -38,9 +40,10 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms // [Fact] public void PrintWeightsData() { - var proc = new ResizeProcessor(KnownResamplers.Bicubic, 200, 200); + var size = new Size(500, 500); + var proc = new ResizeProcessor(KnownResamplers.Bicubic, 200, 200, size); - WeightsBuffer weights = proc.PrecomputeWeights(200, 500); + WeightsBuffer weights = proc.PrecomputeWeights(proc.Width, size.Width); var bld = new StringBuilder(); From d0d95912316426eab00b1067aed6945b243f7aad Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 25 Feb 2018 01:20:27 +1100 Subject: [PATCH 101/151] Disable those randomly failing tests on Travis, they're going soon anyway. --- tests/ImageSharp.Tests/Image/PixelAccessorTests.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/ImageSharp.Tests/Image/PixelAccessorTests.cs b/tests/ImageSharp.Tests/Image/PixelAccessorTests.cs index 36f2628327..97e388b1b9 100644 --- a/tests/ImageSharp.Tests/Image/PixelAccessorTests.cs +++ b/tests/ImageSharp.Tests/Image/PixelAccessorTests.cs @@ -67,10 +67,10 @@ namespace SixLabors.ImageSharp.Tests } } - [Theory] - [WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Xyz)] - [WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Zyx)] - [WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Xyzw)] + // [Theory] + // [WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Xyz)] + // [WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Zyx)] + // [WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Xyzw)] // [WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Zyxw)] TODO: This fails sometimes on Travis. Investigate internal void CopyToThenCopyFromWithOffset(TestImageProvider provider, ComponentOrder order) where TPixel : struct, IPixel From 28eb6d4bcb07655b228ac5367c8de396944fff53 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 25 Feb 2018 23:42:02 +1100 Subject: [PATCH 102/151] Add previously skipped pad and resize tests --- src/ImageSharp/Processing/Transforms/Pad.cs | 6 +- .../Processing/Transforms/PadTest.cs | 24 ++++-- .../Processing/Transforms/ResizeTests.cs | 84 +++++++++++++++++-- 3 files changed, 95 insertions(+), 19 deletions(-) diff --git a/src/ImageSharp/Processing/Transforms/Pad.cs b/src/ImageSharp/Processing/Transforms/Pad.cs index eb0f2e9c27..2f637aa162 100644 --- a/src/ImageSharp/Processing/Transforms/Pad.cs +++ b/src/ImageSharp/Processing/Transforms/Pad.cs @@ -1,10 +1,8 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Processors; using SixLabors.Primitives; namespace SixLabors.ImageSharp @@ -25,11 +23,11 @@ namespace SixLabors.ImageSharp public static IImageProcessingContext Pad(this IImageProcessingContext source, int width, int height) where TPixel : struct, IPixel { - ResizeOptions options = new ResizeOptions + var options = new ResizeOptions { Size = new Size(width, height), Mode = ResizeMode.BoxPad, - Sampler = new NearestNeighborResampler() + Sampler = KnownResamplers.NearestNeighbor }; return Resize(source, options); diff --git a/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs index 58fc7fa802..a9c2922d48 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs @@ -1,20 +1,30 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; -using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; + using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Transforms { + + public class PadTest : BaseImageOperationsExtensionTest { -#pragma warning disable xUnit1004 // Test methods should not be skipped - [Fact(Skip = "Skip this is a helper around resize, skip until resize can be refactord")] -#pragma warning restore xUnit1004 // Test methods should not be skipped - public void Pad_width_height_ResizeProcessorWithCorrectOPtionsSet() + [Fact] + public void PadWidthHeightResizeProcessorWithCorrectOptionsSet() { - throw new NotImplementedException("Write test here"); + int width = 500; + int height = 565; + IResampler sampler = KnownResamplers.NearestNeighbor; + + this.operations.Pad(width, height); + ResizeProcessor resizeProcessor = this.Verify>(); + + Assert.Equal(width, resizeProcessor.Width); + Assert.Equal(height, resizeProcessor.Height); + Assert.Equal(sampler, resizeProcessor.Sampler); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs index d2f5cb2c3d..b51d342cf1 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs @@ -1,23 +1,91 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.Primitives; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Transforms { + using SixLabors.ImageSharp.Processing.Processors; + public class ResizeTests : BaseImageOperationsExtensionTest { -#pragma warning disable xUnit1004 // Test methods should not be skipped - [Fact(Skip = "Skip resize tests as they need refactoring to be simpler and just pass data into the resize processor.")] -#pragma warning restore xUnit1004 // Test methods should not be skipped - public void TestMissing() + [Fact] + public void ResizeWidthAndHeight() + { + int width = 50; + int height = 100; + this.operations.Resize(width, height); + ResizeProcessor resizeProcessor = this.Verify>(); + + Assert.Equal(width, resizeProcessor.Width); + Assert.Equal(height, resizeProcessor.Height); + } + + [Fact] + public void ResizeWidthAndHeightAndSampler() + { + int width = 50; + int height = 100; + IResampler sampler = KnownResamplers.Lanczos3; + this.operations.Resize(width, height, sampler); + ResizeProcessor resizeProcessor = this.Verify>(); + + Assert.Equal(width, resizeProcessor.Width); + Assert.Equal(height, resizeProcessor.Height); + Assert.Equal(sampler, resizeProcessor.Sampler); + } + + [Fact] + public void ResizeWidthAndHeightAndSamplerAndCompand() { - // - throw new NotImplementedException("Write test here"); + int width = 50; + int height = 100; + IResampler sampler = KnownResamplers.Lanczos3; + bool compand = true; + + // ReSharper disable once ConditionIsAlwaysTrueOrFalse + this.operations.Resize(width, height, sampler, compand); + ResizeProcessor resizeProcessor = this.Verify>(); + + Assert.Equal(width, resizeProcessor.Width); + Assert.Equal(height, resizeProcessor.Height); + Assert.Equal(sampler, resizeProcessor.Sampler); + Assert.Equal(compand, resizeProcessor.Compand); + } + + [Fact] + public void ResizeWithOptions() + { + int width = 50; + int height = 100; + IResampler sampler = KnownResamplers.Lanczos3; + bool compand = true; + ResizeMode mode = ResizeMode.Stretch; + + var resizeOptions = new ResizeOptions + { + Size = new Size(width, height), + Sampler = sampler, + Compand = compand, + Mode = mode + }; + + this.operations.Resize(resizeOptions); + ResizeProcessor resizeProcessor = this.Verify>(); + + Assert.Equal(width, resizeProcessor.Width); + Assert.Equal(height, resizeProcessor.Height); + Assert.Equal(sampler, resizeProcessor.Sampler); + Assert.Equal(compand, resizeProcessor.Compand); + + // Ensure options are not altered. + Assert.Equal(width, resizeOptions.Size.Width); + Assert.Equal(height, resizeOptions.Size.Height); + Assert.Equal(sampler, resizeOptions.Sampler); + Assert.Equal(compand, resizeOptions.Compand); + Assert.Equal(mode, resizeOptions.Mode); } } } \ No newline at end of file From c792b03279d45e172eb494270c210d6f6d4e2ae3 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 25 Feb 2018 20:28:39 +0100 Subject: [PATCH 103/151] using WeakReference in ArrayPoolMemoryManager.Buffer --- .../Memory/ArrayPoolMemoryManager.Buffer{T}.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/Memory/ArrayPoolMemoryManager.Buffer{T}.cs b/src/ImageSharp/Memory/ArrayPoolMemoryManager.Buffer{T}.cs index 78e275e8cf..65a91bfdf7 100644 --- a/src/ImageSharp/Memory/ArrayPoolMemoryManager.Buffer{T}.cs +++ b/src/ImageSharp/Memory/ArrayPoolMemoryManager.Buffer{T}.cs @@ -16,13 +16,13 @@ namespace SixLabors.ImageSharp.Memory { private readonly int length; - private readonly ArrayPool sourcePool; + private WeakReference> sourcePoolReference; public Buffer(byte[] data, int length, ArrayPool sourcePool) { this.Data = data; this.length = length; - this.sourcePool = sourcePool; + this.sourcePoolReference = new WeakReference>(sourcePool); } protected byte[] Data { get; private set; } @@ -31,12 +31,17 @@ namespace SixLabors.ImageSharp.Memory public void Dispose() { - if (this.Data == null) + if (this.Data == null || this.sourcePoolReference == null) { return; } - this.sourcePool.Return(this.Data); + if (this.sourcePoolReference.TryGetTarget(out ArrayPool pool)) + { + pool.Return(this.Data); + } + + this.sourcePoolReference = null; this.Data = null; } } From c1b4707113885bee0dcffeb11eb1abff782ffbb3 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 25 Feb 2018 20:38:32 +0100 Subject: [PATCH 104/151] tuning ArrayPoolMemoryManager configuration based on benchmark results --- src/ImageSharp/Configuration.cs | 2 +- .../ArrayPoolMemoryManager.Buffer{T}.cs | 17 ++++++++++ ...yPoolMemoryManager.CommonFactoryMethods.cs | 33 ++++++++++++++++--- .../Memory/ArrayPoolMemoryManager.cs | 3 +- src/ImageSharp/Memory/IManagedByteBuffer.cs | 2 +- src/ImageSharp/Memory/MemoryManager.cs | 6 ++++ 6 files changed, 55 insertions(+), 8 deletions(-) diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index 2fe3c26e27..9a627eeb77 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -86,7 +86,7 @@ namespace SixLabors.ImageSharp /// /// Gets or sets the that is currently in use. /// - public MemoryManager MemoryManager { get; set; } = ArrayPoolMemoryManager.CreateWithNormalPooling(); + public MemoryManager MemoryManager { get; set; } = ArrayPoolMemoryManager.CreateDefault(); /// /// Gets the maximum header size of all the formats. diff --git a/src/ImageSharp/Memory/ArrayPoolMemoryManager.Buffer{T}.cs b/src/ImageSharp/Memory/ArrayPoolMemoryManager.Buffer{T}.cs index 65a91bfdf7..a00ee8c305 100644 --- a/src/ImageSharp/Memory/ArrayPoolMemoryManager.Buffer{T}.cs +++ b/src/ImageSharp/Memory/ArrayPoolMemoryManager.Buffer{T}.cs @@ -11,11 +11,20 @@ namespace SixLabors.ImageSharp.Memory /// public partial class ArrayPoolMemoryManager { + /// + /// The buffer implementation of + /// private class Buffer : IBuffer where T : struct { + /// + /// The length of the buffer + /// private readonly int length; + /// + /// A weak reference to the source pool. + /// private WeakReference> sourcePoolReference; public Buffer(byte[] data, int length, ArrayPool sourcePool) @@ -25,10 +34,15 @@ namespace SixLabors.ImageSharp.Memory this.sourcePoolReference = new WeakReference>(sourcePool); } + /// + /// Gets the buffer as a byte array. + /// protected byte[] Data { get; private set; } + /// public Span Span => this.Data.AsSpan().NonPortableCast().Slice(0, this.length); + /// public void Dispose() { if (this.Data == null || this.sourcePoolReference == null) @@ -46,6 +60,9 @@ namespace SixLabors.ImageSharp.Memory } } + /// + /// The implementation of . + /// private class ManagedByteBuffer : Buffer, IManagedByteBuffer { public ManagedByteBuffer(byte[] data, int length, ArrayPool sourcePool) diff --git a/src/ImageSharp/Memory/ArrayPoolMemoryManager.CommonFactoryMethods.cs b/src/ImageSharp/Memory/ArrayPoolMemoryManager.CommonFactoryMethods.cs index 918c5d41af..d1424870da 100644 --- a/src/ImageSharp/Memory/ArrayPoolMemoryManager.CommonFactoryMethods.cs +++ b/src/ImageSharp/Memory/ArrayPoolMemoryManager.CommonFactoryMethods.cs @@ -7,22 +7,36 @@ { /// /// The default value for: maximum size of pooled arrays in bytes. - /// Currently set to 32MB, which is equivalent to 8 megapixels of raw data. + /// Currently set to 24MB, which is equivalent to 8 megapixels of raw data. /// - internal const int DefaultMaxPooledBufferSizeInBytes = 32 * 1024 * 1024; + internal const int DefaultMaxPooledBufferSizeInBytes = 24 * 1024 * 1024; /// /// The value for: The threshold to pool arrays in which has less buckets for memory safety. /// private const int DefaultBufferSelectorThresholdInBytes = 8 * 1024 * 1024; + /// + /// The default bucket count for . + /// + private const int DefaultLargePoolBucketCount = 6; + + /// + /// The default bucket count for . + /// + private const int DefaultNormalPoolBucketCount = 16; + /// /// This is the default. Should be good for most use cases. /// /// The memory manager - public static ArrayPoolMemoryManager CreateWithNormalPooling() + public static ArrayPoolMemoryManager CreateDefault() { - return new ArrayPoolMemoryManager(DefaultMaxPooledBufferSizeInBytes, DefaultBufferSelectorThresholdInBytes, 8, 24); + return new ArrayPoolMemoryManager( + DefaultMaxPooledBufferSizeInBytes, + DefaultBufferSelectorThresholdInBytes, + DefaultLargePoolBucketCount, + DefaultNormalPoolBucketCount); } /// @@ -31,7 +45,16 @@ /// The memory manager public static ArrayPoolMemoryManager CreateWithModeratePooling() { - return new ArrayPoolMemoryManager(1024 * 1024, 1024 * 16, 16, 24); + return new ArrayPoolMemoryManager(1024 * 1024, 32 * 1024, 16, 24); + } + + /// + /// Only pool small buffers like image rows. + /// + /// The memory manager + public static ArrayPoolMemoryManager CreateWithMinimalPooling() + { + return new ArrayPoolMemoryManager(64 * 1024, 32 * 1024, 8, 24); } /// diff --git a/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs b/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs index 36bc3a8703..7b8c7ab326 100644 --- a/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs +++ b/src/ImageSharp/Memory/ArrayPoolMemoryManager.cs @@ -48,7 +48,7 @@ namespace SixLabors.ImageSharp.Memory /// The maximum size of pooled arrays. Arrays over the thershold are gonna be always allocated. /// Arrays over this threshold will be pooled in which has less buckets for memory safety. public ArrayPoolMemoryManager(int maxPoolSizeInBytes, int poolSelectorThresholdInBytes) - : this(maxPoolSizeInBytes, poolSelectorThresholdInBytes, 8, 24) + : this(maxPoolSizeInBytes, poolSelectorThresholdInBytes, DefaultLargePoolBucketCount, DefaultNormalPoolBucketCount) { } @@ -106,6 +106,7 @@ namespace SixLabors.ImageSharp.Memory return buffer; } + /// internal override IManagedByteBuffer AllocateManagedByteBuffer(int length, bool clear) { ArrayPool pool = this.GetArrayPool(length); diff --git a/src/ImageSharp/Memory/IManagedByteBuffer.cs b/src/ImageSharp/Memory/IManagedByteBuffer.cs index d75fb9b6c7..4d159ce863 100644 --- a/src/ImageSharp/Memory/IManagedByteBuffer.cs +++ b/src/ImageSharp/Memory/IManagedByteBuffer.cs @@ -4,7 +4,7 @@ namespace SixLabors.ImageSharp.Memory { /// - /// Represents a byte buffer backed by a managed array. + /// Represents a byte buffer backed by a managed array. Useful for interop with classic .NET API-s. /// internal interface IManagedByteBuffer : IBuffer { diff --git a/src/ImageSharp/Memory/MemoryManager.cs b/src/ImageSharp/Memory/MemoryManager.cs index 8e2df8cec2..52bdc897fc 100644 --- a/src/ImageSharp/Memory/MemoryManager.cs +++ b/src/ImageSharp/Memory/MemoryManager.cs @@ -19,6 +19,12 @@ namespace SixLabors.ImageSharp.Memory internal abstract IBuffer Allocate(int length, bool clear) where T : struct; + /// + /// Allocates an + /// + /// The requested buffer length + /// A value indicating whether to clean the buffer + /// The internal abstract IManagedByteBuffer AllocateManagedByteBuffer(int length, bool clear); /// From b0a1ebc5f76c7f5bf4bd7e00f1971dad759b0b91 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 25 Feb 2018 20:39:32 +0100 Subject: [PATCH 105/151] tuning ArrayPoolMemoryManager configuration based on benchmark results --- tests/ImageSharp.Tests/Memory/ArrayPoolMemoryManagerTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ImageSharp.Tests/Memory/ArrayPoolMemoryManagerTests.cs b/tests/ImageSharp.Tests/Memory/ArrayPoolMemoryManagerTests.cs index f99ee4dded..805bc908cd 100644 --- a/tests/ImageSharp.Tests/Memory/ArrayPoolMemoryManagerTests.cs +++ b/tests/ImageSharp.Tests/Memory/ArrayPoolMemoryManagerTests.cs @@ -171,9 +171,9 @@ namespace SixLabors.ImageSharp.Tests.Memory } [Fact] - public void CreateWithNormalPooling() + public void CreateDefault() { - this.MemoryManager = ArrayPoolMemoryManager.CreateWithNormalPooling(); + this.MemoryManager = ArrayPoolMemoryManager.CreateDefault(); Assert.False(this.CheckIsRentingPooledBuffer(2 * 4096 * 4096)); Assert.True(this.CheckIsRentingPooledBuffer(2048 * 2048)); From 3f4cdf168580afce7624529024910fb142a79964 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 25 Feb 2018 21:59:59 +0100 Subject: [PATCH 106/151] build fix after merge --- .../Processors/DrawImageProcessor.cs | 10 +++++----- .../Transforms/ProjectiveTransformProcessor.cs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs b/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs index 8800980885..632b4d449d 100644 --- a/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs @@ -74,11 +74,11 @@ namespace SixLabors.ImageSharp.Drawing.Processors int width = maxX - minX; - MemoryManager memoryManager = this.Image.GetConfiguration().MemoryManager; + MemoryManager memoryManager = this.Image.GetConfiguration().MemoryManager; - using (IBuffer amount = memoryManager.Allocate(width)) - { - amount.Span.Fill(this.Alpha); + using (IBuffer amount = memoryManager.Allocate(width)) + { + amount.Span.Fill(this.Opacity); Parallel.For( minY, @@ -88,7 +88,7 @@ namespace SixLabors.ImageSharp.Drawing.Processors { Span background = source.GetPixelRowSpan(y).Slice(minX, width); Span foreground = targetImage.GetPixelRowSpan(y - locationY).Slice(targetX, width); - this.blender.Blend(memoryManager, background, background, foreground, amount.Span); + blender.Blend(memoryManager, background, background, foreground, amount.Span); }); } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs index 63ef3bfe27..458871cdc2 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs @@ -74,7 +74,7 @@ namespace SixLabors.ImageSharp.Processing.Processors IEnumerable> frames = source.Frames.Select( x => new ImageFrame( source.GetMemoryManager(), - this.targetRectangle.Size, + this.targetDimensions, x.MetaData.Clone())); // Use the overload to prevent an extra frame being added From 62381eff8f9f3f3461c04b2f4da24d2343547921 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 25 Feb 2018 22:12:20 +0100 Subject: [PATCH 107/151] removing duplicate reference to SixLabors.Core --- src/ImageSharp/ImageSharp.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index 833b9b96d7..cb0539f786 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -34,7 +34,6 @@ - From cec6083d49fa833eacef7e36d2d37b763e397225 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 26 Feb 2018 01:05:18 +0100 Subject: [PATCH 108/151] SimpleManagedMemoryManager -> SimpleGcMemoryManager --- ...SimpleManagedMemoryManager.cs => SimpleGcMemoryManager.cs} | 4 ++-- ...gedMemoryManagerTests.cs => SimpleGcMemoryManagerTests.cs} | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) rename src/ImageSharp/Memory/{SimpleManagedMemoryManager.cs => SimpleGcMemoryManager.cs} (73%) rename tests/ImageSharp.Tests/Memory/{SimpleManagedMemoryManagerTests.cs => SimpleGcMemoryManagerTests.cs} (68%) diff --git a/src/ImageSharp/Memory/SimpleManagedMemoryManager.cs b/src/ImageSharp/Memory/SimpleGcMemoryManager.cs similarity index 73% rename from src/ImageSharp/Memory/SimpleManagedMemoryManager.cs rename to src/ImageSharp/Memory/SimpleGcMemoryManager.cs index 701c71ad43..f4518bbb9d 100644 --- a/src/ImageSharp/Memory/SimpleManagedMemoryManager.cs +++ b/src/ImageSharp/Memory/SimpleGcMemoryManager.cs @@ -1,9 +1,9 @@ namespace SixLabors.ImageSharp.Memory { /// - /// Implements by allocating new buffers on every call. + /// Implements by newing up arrays by the GC on every allocation requests. /// - public class SimpleManagedMemoryManager : MemoryManager + public class SimpleGcMemoryManager : MemoryManager { /// internal override IBuffer Allocate(int length, bool clear) diff --git a/tests/ImageSharp.Tests/Memory/SimpleManagedMemoryManagerTests.cs b/tests/ImageSharp.Tests/Memory/SimpleGcMemoryManagerTests.cs similarity index 68% rename from tests/ImageSharp.Tests/Memory/SimpleManagedMemoryManagerTests.cs rename to tests/ImageSharp.Tests/Memory/SimpleGcMemoryManagerTests.cs index eb74145820..0d1c2beb8f 100644 --- a/tests/ImageSharp.Tests/Memory/SimpleManagedMemoryManagerTests.cs +++ b/tests/ImageSharp.Tests/Memory/SimpleGcMemoryManagerTests.cs @@ -2,12 +2,12 @@ namespace SixLabors.ImageSharp.Tests.Memory { using SixLabors.ImageSharp.Memory; - public class SimpleManagedMemoryManagerTests + public class SimpleGcMemoryManagerTests { public class BufferTests : BufferTestSuite { public BufferTests() - : base(new SimpleManagedMemoryManager()) + : base(new SimpleGcMemoryManager()) { } } From 072847b6f44342b9cc93909ddcd1d6ea94bc4500 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 26 Feb 2018 12:25:18 +1100 Subject: [PATCH 109/151] Ensure Bounds() is accurate per operation --- .../DefaultInternalImageProcessorContext.cs | 2 +- .../Image/ImageProcessingContextTests.cs | 58 +++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 tests/ImageSharp.Tests/Image/ImageProcessingContextTests.cs diff --git a/src/ImageSharp/DefaultInternalImageProcessorContext.cs b/src/ImageSharp/DefaultInternalImageProcessorContext.cs index cdfd582be0..974d0bc110 100644 --- a/src/ImageSharp/DefaultInternalImageProcessorContext.cs +++ b/src/ImageSharp/DefaultInternalImageProcessorContext.cs @@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp } /// - public Rectangle Bounds() => this.source.Bounds(); + public Rectangle Bounds() => this.destination?.Bounds() ?? this.source.Bounds(); /// public IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle) diff --git a/tests/ImageSharp.Tests/Image/ImageProcessingContextTests.cs b/tests/ImageSharp.Tests/Image/ImageProcessingContextTests.cs new file mode 100644 index 0000000000..8411dd2f01 --- /dev/null +++ b/tests/ImageSharp.Tests/Image/ImageProcessingContextTests.cs @@ -0,0 +1,58 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests +{ + public class ImageProcessingContextTests + { + [Fact] + public void MutatedBoundsAreAccuratePerOperation() + { + var x500 = new Size(500, 500); + var x400 = new Size(400, 400); + var x300 = new Size(300, 300); + var x200 = new Size(200, 200); + var x100 = new Size(100, 100); + using (var image = new Image(500, 500)) + { + image.Mutate(x => + x.AssertBounds(x500) + .Resize(x400).AssertBounds(x400) + .Resize(x300).AssertBounds(x300) + .Resize(x200).AssertBounds(x200) + .Resize(x100).AssertBounds(x100)); + } + } + + [Fact] + public void ClonedBoundsAreAccuratePerOperation() + { + var x500 = new Size(500, 500); + var x400 = new Size(400, 400); + var x300 = new Size(300, 300); + var x200 = new Size(200, 200); + var x100 = new Size(100, 100); + using (var image = new Image(500, 500)) + { + image.Clone(x => + x.AssertBounds(x500) + .Resize(x400).AssertBounds(x400) + .Resize(x300).AssertBounds(x300) + .Resize(x200).AssertBounds(x200) + .Resize(x100).AssertBounds(x100)); + } + } + } + + public static class BoundsAssertationExtensions + { + public static IImageProcessingContext AssertBounds(this IImageProcessingContext context, Size size) + { + Assert.Equal(size, context.Bounds().Size); + return context; + } + } +} \ No newline at end of file From a7a2b0b5199b11ba03d739751a74844ebd42cec6 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 26 Feb 2018 19:25:07 +0100 Subject: [PATCH 110/151] adding a few TODO notes --- src/ImageSharp/Processing/Processors/CloningImageProcessor.cs | 3 +++ .../Processing/Processors/Transforms/ResizeProcessor.cs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/ImageSharp/Processing/Processors/CloningImageProcessor.cs b/src/ImageSharp/Processing/Processors/CloningImageProcessor.cs index 4672b2ad45..7257bd6643 100644 --- a/src/ImageSharp/Processing/Processors/CloningImageProcessor.cs +++ b/src/ImageSharp/Processing/Processors/CloningImageProcessor.cs @@ -96,6 +96,7 @@ namespace SixLabors.ImageSharp.Processing /// /// This method is called before the process is applied to prepare the processor. + /// TODO: We should probably name this 'BeforeFrameApply' /// /// The source image. Cannot be null. /// The cloned/destination image. Cannot be null. @@ -108,6 +109,7 @@ namespace SixLabors.ImageSharp.Processing /// /// Applies the process to the specified portion of the specified at the specified location /// and with the specified size. + /// TODO: We should probably name this 'ApplyToFrame' /// /// The source image. Cannot be null. /// The cloned/destination image. Cannot be null. @@ -117,6 +119,7 @@ namespace SixLabors.ImageSharp.Processing /// /// This method is called after the process is applied to prepare the processor. + /// TODO: We should probably name this 'AfterFrameApply' /// /// The source image. Cannot be null. /// The cloned/destination image. Cannot be null. diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs index bccf665a48..8638990913 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs @@ -223,6 +223,7 @@ namespace SixLabors.ImageSharp.Processing.Processors { if (!(this.Sampler is NearestNeighborResampler)) { + // TODO: Optimization opportunity: if we could assume that all frames are of the same size, we can move this into 'BeforeImageApply()` this.horizontalWeights = this.PrecomputeWeights( this.ResizeRectangle.Width, sourceRectangle.Width); @@ -367,6 +368,8 @@ namespace SixLabors.ImageSharp.Processing.Processors protected override void AfterApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) { base.AfterApply(source, destination, sourceRectangle, configuration); + + // TODO: An exception in the processing chain can leave these buffers undisposed. We should consider making image processors IDisposable! this.horizontalWeights?.Dispose(); this.horizontalWeights = null; this.verticalWeights?.Dispose(); From 6d3b8576402686a14147aba93b242f7ac8418e32 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 26 Feb 2018 21:04:09 +0100 Subject: [PATCH 111/151] review cleanup --- .../Brushes/SolidBrush{TPixel}.cs | 28 +++++++------------ src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 6 ++-- .../Formats/Jpeg/Common/Block8x8F.CopyTo.cs | 4 +-- src/ImageSharp/Memory/BufferArea{T}.cs | 2 +- 4 files changed, 16 insertions(+), 24 deletions(-) diff --git a/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs index 6928895565..9630c707ef 100644 --- a/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs @@ -89,28 +89,20 @@ namespace SixLabors.ImageSharp.Drawing.Brushes /// internal override void Apply(Span scanline, int x, int y) { - try - { - Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); - - MemoryManager memoryManager = this.Target.MemoryManager; + Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); - using (IBuffer amountBuffer = memoryManager.Allocate(scanline.Length)) - { - Span amountSpan = amountBuffer.Span; + MemoryManager memoryManager = this.Target.MemoryManager; - for (int i = 0; i < scanline.Length; i++) - { - amountSpan[i] = scanline[i] * this.Options.BlendPercentage; - } + using (IBuffer amountBuffer = memoryManager.Allocate(scanline.Length)) + { + Span amountSpan = amountBuffer.Span; - this.Blender.Blend(memoryManager, destinationRow, destinationRow, this.Colors.Span, amountSpan); + for (int i = 0; i < scanline.Length; i++) + { + amountSpan[i] = scanline[i] * this.Options.BlendPercentage; } - } - catch (Exception) - { - // TODO: Why are we catching exceptions here silently ??? - throw; + + this.Blender.Blend(memoryManager, destinationRow, destinationRow, this.Colors.Span, amountSpan); } } } diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 953a6fcb23..9f4dba5b4f 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -224,7 +224,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp var color = default(TPixel); var rgba = new Rgba32(0, 0, 0, 255); - using (var buffer = this.configuration.MemoryManager.Allocate2D(width, height, true)) + using (var buffer = this.memoryManager.AllocateClean2D(width, height)) { this.UncompressRle8(width, buffer.Span); @@ -346,7 +346,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp padding = 4 - padding; } - using (IManagedByteBuffer row = this.configuration.MemoryManager.AllocateManagedByteBuffer(arrayWidth + padding, true)) + using (IManagedByteBuffer row = this.memoryManager.AllocateCleanManagedByteBuffer(arrayWidth + padding)) { var color = default(TPixel); var rgba = new Rgba32(0, 0, 0, 255); @@ -398,7 +398,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp var color = default(TPixel); var rgba = new Rgba32(0, 0, 0, 255); - using (var buffer = this.configuration.MemoryManager.AllocateManagedByteBuffer(stride)) + using (var buffer = this.memoryManager.AllocateManagedByteBuffer(stride)) { for (int y = 0; y < height; y++) { diff --git a/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.CopyTo.cs b/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.CopyTo.cs index 39a6bee2e4..ca167015b1 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.CopyTo.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.CopyTo.cs @@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common public void CopyTo(BufferArea area) { ref byte selfBase = ref Unsafe.As(ref this); - ref byte destBase = ref Unsafe.As(ref area.GetReferenceToOrigo()); + ref byte destBase = ref Unsafe.As(ref area.GetReferenceToOrigin()); int destStride = area.Stride * sizeof(float); CopyRowImpl(ref selfBase, ref destBase, destStride, 0); @@ -76,7 +76,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common private void CopyTo2x2(BufferArea area) { - ref float destBase = ref area.GetReferenceToOrigo(); + ref float destBase = ref area.GetReferenceToOrigin(); int destStride = area.Stride; this.WidenCopyImpl2x2(ref destBase, 0, destStride); diff --git a/src/ImageSharp/Memory/BufferArea{T}.cs b/src/ImageSharp/Memory/BufferArea{T}.cs index 850cc4c164..e88ed6ca83 100644 --- a/src/ImageSharp/Memory/BufferArea{T}.cs +++ b/src/ImageSharp/Memory/BufferArea{T}.cs @@ -78,7 +78,7 @@ namespace SixLabors.ImageSharp.Memory /// /// The reference to the [0,0] element [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref T GetReferenceToOrigo() => + public ref T GetReferenceToOrigin() => ref this.DestinationBuffer.Span[(this.Rectangle.Y * this.DestinationBuffer.Width) + this.Rectangle.X]; /// From ef630f152e3f2e2fee09b1886dbd0257f02deeec Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 26 Feb 2018 21:17:18 +0100 Subject: [PATCH 112/151] added comments for the WeakReference stuff --- .../Memory/ArrayPoolMemoryManager.Buffer{T}.cs | 4 ++++ .../Memory/ArrayPoolMemoryManagerTests.cs | 13 ++++++++++--- tests/ImageSharp.Tests/Memory/BufferAreaTests.cs | 2 +- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/Memory/ArrayPoolMemoryManager.Buffer{T}.cs b/src/ImageSharp/Memory/ArrayPoolMemoryManager.Buffer{T}.cs index a00ee8c305..d4f58fb6fb 100644 --- a/src/ImageSharp/Memory/ArrayPoolMemoryManager.Buffer{T}.cs +++ b/src/ImageSharp/Memory/ArrayPoolMemoryManager.Buffer{T}.cs @@ -25,6 +25,10 @@ namespace SixLabors.ImageSharp.Memory /// /// A weak reference to the source pool. /// + /// + /// By using a weak reference here, we are making sure that array pools and their retained arrays are always GC-ed + /// after a call to , regardless of having buffer instances still being in use. + /// private WeakReference> sourcePoolReference; public Buffer(byte[] data, int length, ArrayPool sourcePool) diff --git a/tests/ImageSharp.Tests/Memory/ArrayPoolMemoryManagerTests.cs b/tests/ImageSharp.Tests/Memory/ArrayPoolMemoryManagerTests.cs index 805bc908cd..a199bb319d 100644 --- a/tests/ImageSharp.Tests/Memory/ArrayPoolMemoryManagerTests.cs +++ b/tests/ImageSharp.Tests/Memory/ArrayPoolMemoryManagerTests.cs @@ -127,14 +127,21 @@ namespace SixLabors.ImageSharp.Tests.Memory } } - [Fact] - public void ReleaseRetainedResources_ReplacesInnerArrayPool() + [Theory] + [InlineData(false)] + [InlineData(true)] + public void ReleaseRetainedResources_ReplacesInnerArrayPool(bool keepBufferAlive) { IBuffer buffer = this.MemoryManager.Allocate(32); ref int ptrToPrev0 = ref buffer.Span.DangerousGetPinnableReference(); - buffer.Dispose(); + + if (!keepBufferAlive) + { + buffer.Dispose(); + } this.MemoryManager.ReleaseRetainedResources(); + buffer = this.MemoryManager.Allocate(32); Assert.False(Unsafe.AreSame(ref ptrToPrev0, ref buffer.DangerousGetPinnableReference())); diff --git a/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs b/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs index e96aa2e375..db7367d972 100644 --- a/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs +++ b/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs @@ -104,7 +104,7 @@ namespace SixLabors.ImageSharp.Tests.Memory { BufferArea area0 = buffer.GetArea(6, 8, 10, 10); - ref int r = ref area0.GetReferenceToOrigo(); + ref int r = ref area0.GetReferenceToOrigin(); int expected = buffer[6, 8]; Assert.Equal(expected, r); From 87fe71511a1bc857fcb8bcba2ab8e74504b94665 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 27 Feb 2018 00:36:44 +0100 Subject: [PATCH 113/151] optimize ResizeProcessor parallel behavior and Span usage --- .../Processors/Transforms/ResizeProcessor.cs | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs index 1e76422508..0708a6c232 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs @@ -132,33 +132,35 @@ namespace SixLabors.ImageSharp.Processing.Processors 0, sourceRectangle.Bottom, configuration.ParallelOptions, - y => + () => this.MemoryManager.Allocate(source.Width), + (int y, ParallelLoopState sate, IBuffer tempRowBuffer) => { - // TODO: Without Parallel.For() this buffer object could be reused: - using (IBuffer tempRowBuffer = this.MemoryManager.Allocate(source.Width)) - { - Span firstPassRow = firstPassPixels.GetRowSpan(y); - Span sourceRow = source.GetPixelRowSpan(y); - PixelOperations.Instance.ToVector4(sourceRow, tempRowBuffer.Span, sourceRow.Length); + Span firstPassRow = firstPassPixels.GetRowSpan(y); + Span sourceRow = source.GetPixelRowSpan(y); + Span tempRowSpan = tempRowBuffer.Span; - if (this.Compand) + PixelOperations.Instance.ToVector4(sourceRow, tempRowSpan, sourceRow.Length); + + if (this.Compand) + { + for (int x = minX; x < maxX; x++) { - for (int x = minX; x < maxX; x++) - { - WeightsWindow window = this.HorizontalWeights.Weights[x - startX]; - firstPassRow[x] = window.ComputeExpandedWeightedRowSum(tempRowBuffer.Span, sourceX); - } + WeightsWindow window = this.HorizontalWeights.Weights[x - startX]; + firstPassRow[x] = window.ComputeExpandedWeightedRowSum(tempRowSpan, sourceX); } - else + } + else + { + for (int x = minX; x < maxX; x++) { - for (int x = minX; x < maxX; x++) - { - WeightsWindow window = this.HorizontalWeights.Weights[x - startX]; - firstPassRow[x] = window.ComputeWeightedRowSum(tempRowBuffer.Span, sourceX); - } + WeightsWindow window = this.HorizontalWeights.Weights[x - startX]; + firstPassRow[x] = window.ComputeWeightedRowSum(tempRowSpan, sourceX); } } - }); + + return tempRowBuffer; + }, + (IBuffer tmp) => tmp.Dispose()); // Now process the rows. Parallel.For( From 09d993b503a186fd6a28f24c496d8476ce06a0ae Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 27 Feb 2018 00:37:23 +0100 Subject: [PATCH 114/151] introducing ParallelFor.WithTemporalBuffer(): common utility for the parallel buffer reusal trick --- src/ImageSharp/Common/Helpers/ParallelFor.cs | 60 +++++++++++++++++++ .../Processors/Transforms/ResizeProcessor.cs | 13 ++-- 2 files changed, 65 insertions(+), 8 deletions(-) create mode 100644 src/ImageSharp/Common/Helpers/ParallelFor.cs diff --git a/src/ImageSharp/Common/Helpers/ParallelFor.cs b/src/ImageSharp/Common/Helpers/ParallelFor.cs new file mode 100644 index 0000000000..9a30b0b508 --- /dev/null +++ b/src/ImageSharp/Common/Helpers/ParallelFor.cs @@ -0,0 +1,60 @@ +using System; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp +{ + /// + /// Utility methods for Parallel.For() execution. Use this instead of raw calls! + /// + internal static class ParallelFor + { + /// + /// Helper method to execute Parallel.For using the settings in + /// + public static void WithConfiguration(int fromInclusive, int toExclusive, Configuration configuration, Action body) + { + Parallel.For(fromInclusive, toExclusive, configuration.ParallelOptions, body); + } + + /// + /// Helper method to execute Parallel.For with temporal worker buffer in an optimized way. + /// The buffer will be only instantiated for each worker Task, the contents are not cleaned automatically. + /// + /// The value type of the buffer + /// The start index, inclusive. + /// The end index, exclusive. + /// The used for getting the and + /// The length of the requested parallel buffer + /// The delegate that is invoked once per iteration. + public static void WithTemporalBuffer( + int fromInclusive, + int toExclusive, + Configuration configuration, + int bufferLength, + Action> body) + where T : struct + { + MemoryManager memoryManager = configuration.MemoryManager; + ParallelOptions parallelOptions = configuration.ParallelOptions; + + IBuffer InitBuffer() + { + return memoryManager.Allocate(bufferLength); + } + + void CleanUpBuffer(IBuffer buffer) + { + buffer.Dispose(); + } + + IBuffer BodyFunc(int i, ParallelLoopState state, IBuffer buffer) + { + body(i, buffer); + return buffer; + } + + Parallel.For(fromInclusive, toExclusive, parallelOptions, InitBuffer, BodyFunc, CleanUpBuffer); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs index 0708a6c232..169496a982 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs @@ -128,12 +128,12 @@ namespace SixLabors.ImageSharp.Processing.Processors { firstPassPixels.Buffer.Clear(); - Parallel.For( + ParallelFor.WithTemporalBuffer( 0, sourceRectangle.Bottom, - configuration.ParallelOptions, - () => this.MemoryManager.Allocate(source.Width), - (int y, ParallelLoopState sate, IBuffer tempRowBuffer) => + configuration, + source.Width, + (int y, IBuffer tempRowBuffer) => { Span firstPassRow = firstPassPixels.GetRowSpan(y); Span sourceRow = source.GetPixelRowSpan(y); @@ -157,10 +157,7 @@ namespace SixLabors.ImageSharp.Processing.Processors firstPassRow[x] = window.ComputeWeightedRowSum(tempRowSpan, sourceX); } } - - return tempRowBuffer; - }, - (IBuffer tmp) => tmp.Dispose()); + }); // Now process the rows. Parallel.For( From 220f8b58d4125a0aaf8bd945292c5cf845df2037 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 27 Feb 2018 11:14:52 +1100 Subject: [PATCH 115/151] Better method description --- src/ImageSharp/IImageProcessingContext{TPixel}.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/IImageProcessingContext{TPixel}.cs b/src/ImageSharp/IImageProcessingContext{TPixel}.cs index 281c925673..d93d72317b 100644 --- a/src/ImageSharp/IImageProcessingContext{TPixel}.cs +++ b/src/ImageSharp/IImageProcessingContext{TPixel}.cs @@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp where TPixel : struct, IPixel { /// - /// Gets the source image bounds + /// Gets the image bounds at the current point in the processing pipeline. /// /// The Rectangle Bounds(); From 70839cad77d910d5f4126f959b6f6754f1a2e222 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 27 Feb 2018 02:03:11 +0100 Subject: [PATCH 116/151] removed Span bottleneck from Block8x8F.CopyTo() + removed unnecessary pinning from OrigHuffmanTree (cherry picked from commit 81f6a9407b81be97706286f7974c419583dddf8a) --- .../Formats/Jpeg/Common/Block8x8F.CopyTo.cs | 7 ++- .../Components/Decoder/InputProcessor.cs | 4 +- .../Components/Decoder/OrigHuffmanTree.cs | 60 +++++++++++-------- src/ImageSharp/Memory/BufferArea{T}.cs | 2 +- 4 files changed, 45 insertions(+), 28 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.CopyTo.cs b/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.CopyTo.cs index ca167015b1..d8963a8b60 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.CopyTo.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.CopyTo.cs @@ -26,6 +26,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common return; } + ref float destBase = ref area.GetReferenceToOrigin(); + // TODO: Optimize: implement all the cases with loopless special code! (T4?) for (int y = 0; y < 8; y++) { @@ -40,9 +42,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common for (int i = 0; i < verticalScale; i++) { + int baseIdx = ((yy + i) * area.Stride) + xx; + for (int j = 0; j < horizontalScale; j++) { - area[xx + j, yy + i] = value; + // area[xx + j, yy + i] = value; + Unsafe.Add(ref destBase, baseIdx + j) = value; } } } diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs index f065092004..e9f468a85d 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs @@ -223,7 +223,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder if (this.LastErrorCode == OrigDecoderErrorCode.NoError) { int lutIndex = (this.Bits.Accumulator >> (this.Bits.UnreadBits - OrigHuffmanTree.LutSizeLog2)) & 0xFF; - int v = huffmanTree.ReadLut(lutIndex); + int v = huffmanTree.Lut[lutIndex]; if (v != 0) { @@ -262,7 +262,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder this.Bits.UnreadBits--; this.Bits.Mask >>= 1; - if (code <= huffmanTree.GetMaxCode(i)) + if (code <= huffmanTree.MaxCodes[i]) { result = huffmanTree.GetValue(code, i); return this.LastErrorCode = OrigDecoderErrorCode.NoError; diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigHuffmanTree.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigHuffmanTree.cs index f773a12dfa..85273c69ed 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigHuffmanTree.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigHuffmanTree.cs @@ -4,12 +4,14 @@ using System; using System.Buffers; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder { /// /// Represents a Huffman tree /// + [StructLayout(LayoutKind.Sequential)] internal unsafe struct OrigHuffmanTree { /// @@ -68,29 +70,29 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder /// are 1 plus the code length, or 0 if the value is too large to fit in /// lutSize bits. /// - public fixed int Lut[MaxNCodes]; + public FixedInt32Buffer256 Lut; /// /// Gets the the decoded values, sorted by their encoding. /// - public fixed int Values[MaxNCodes]; + public FixedInt32Buffer256 Values; /// /// Gets the array of minimum codes. /// MinCodes[i] is the minimum code of length i, or -1 if there are no codes of that length. /// - public fixed int MinCodes[MaxCodeLength]; + public FixedInt32Buffer16 MinCodes; /// /// Gets the array of maximum codes. /// MaxCodes[i] is the maximum code of length i, or -1 if there are no codes of that length. /// - public fixed int MaxCodes[MaxCodeLength]; + public FixedInt32Buffer16 MaxCodes; /// /// Gets the array of indices. Indices[i] is the index into Values of MinCodes[i]. /// - public fixed int Indices[MaxCodeLength]; + public FixedInt32Buffer16 Indices; /// /// Creates and initializes an array of instances of size @@ -143,8 +145,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder byte[] values = new byte[MaxNCodes]; inputProcessor.ReadFull(values, 0, this.Length); - fixed (int* valuesPtr = this.Values) - fixed (int* lutPtr = this.Lut) + fixed (int* valuesPtr = this.Values.Data) + fixed (int* lutPtr = this.Lut.Data) { for (int i = 0; i < values.Length; i++) { @@ -184,9 +186,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder } } - fixed (int* minCodesPtr = this.MinCodes) - fixed (int* maxCodesPtr = this.MaxCodes) - fixed (int* indicesPtr = this.Indices) + fixed (int* minCodesPtr = this.MinCodes.Data) + fixed (int* maxCodesPtr = this.MaxCodes.Data) + fixed (int* indicesPtr = this.Indices.Data) { // Derive minCodes, maxCodes, and indices. int c = 0, index = 0; @@ -219,31 +221,41 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder /// The code /// The code length /// The value + [MethodImpl(MethodImplOptions.AggressiveInlining)] public int GetValue(int code, int codeLength) { - fixed (int* valuesPtr = this.Values) - fixed (int* minCodesPtr = this.MinCodes) - fixed (int* indicesPtr = this.Indices) - { - return valuesPtr[indicesPtr[codeLength] + code - minCodesPtr[codeLength]]; - } + return this.Values[this.Indices[codeLength] + code - this.MinCodes[codeLength]]; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int ReadLut(int index) + [StructLayout(LayoutKind.Sequential)] + internal struct FixedInt32Buffer256 { - fixed (int* lutPtr = this.Lut) + public fixed int Data[256]; + + public int this[int idx] { - return lutPtr[index]; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ref int self = ref Unsafe.As(ref this); + return Unsafe.Add(ref self, idx); + } } } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int GetMaxCode(int index) + [StructLayout(LayoutKind.Sequential)] + internal struct FixedInt32Buffer16 { - fixed (int* maxCodesPtr = this.MaxCodes) + public fixed int Data[16]; + + public int this[int idx] { - return maxCodesPtr[index]; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ref int self = ref Unsafe.As(ref this); + return Unsafe.Add(ref self, idx); + } } } } diff --git a/src/ImageSharp/Memory/BufferArea{T}.cs b/src/ImageSharp/Memory/BufferArea{T}.cs index e88ed6ca83..588eae483d 100644 --- a/src/ImageSharp/Memory/BufferArea{T}.cs +++ b/src/ImageSharp/Memory/BufferArea{T}.cs @@ -137,7 +137,7 @@ namespace SixLabors.ImageSharp.Memory } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private int GetRowIndex(int y) + internal int GetRowIndex(int y) { return (y + this.Rectangle.Y) * this.DestinationBuffer.Width; } From 90937b57caf88964d0bfeb6218231e7dc3d3b0eb Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 27 Feb 2018 02:19:01 +0100 Subject: [PATCH 117/151] Removing all the buffer magic from the Bytes struct. It only made things worse! (cherry picked from commit 0bf64a09598ba988318c170259a7aab4d9391cb5) --- .../GolangPort/Components/Decoder/Bytes.cs | 35 ++++++++----------- .../Components/Decoder/InputProcessor.cs | 4 +-- 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bytes.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bytes.cs index c10771b462..3fc46093eb 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bytes.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bytes.cs @@ -28,12 +28,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder /// buffer[i:j] are the buffered bytes read from the underlying /// stream that haven't yet been passed further on. /// - public IManagedByteBuffer Buffer; + public byte[] Buffer; /// /// Values of converted to -s /// - public IBuffer BufferAsInt; + public int[] BufferAsInt; /// /// Start of bytes read @@ -58,10 +58,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder /// The bytes created public static Bytes Create(MemoryManager memoryManager) { + // DO NOT bother with buffers and array pooling here! + // It only makes things worse! return new Bytes { - Buffer = memoryManager.AllocateManagedByteBuffer(BufferSize), - BufferAsInt = memoryManager.Allocate(BufferSize) + Buffer = new byte[BufferSize], + BufferAsInt = new int[BufferSize] }; } @@ -70,9 +72,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder /// public void Dispose() { - this.Buffer?.Dispose(); - this.BufferAsInt?.Dispose(); - this.Buffer = null; this.BufferAsInt = null; } @@ -88,8 +87,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder // Take the fast path if bytes.buf contains at least two bytes. if (this.I + 2 <= this.J) { - Span bufferSpan = this.BufferAsInt.Span; - x = bufferSpan[this.I]; + x = this.BufferAsInt[this.I]; this.I++; this.UnreadableBytes = 1; if (x != OrigJpegConstants.Markers.XFFInt) @@ -97,7 +95,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder return OrigDecoderErrorCode.NoError; } - if (bufferSpan[this.I] != 0x00) + if (this.BufferAsInt[this.I] != 0x00) { return OrigDecoderErrorCode.MissingFF00; } @@ -171,7 +169,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder } } - result = this.Buffer.Span[this.I]; + result = this.Buffer[this.I]; this.I++; this.UnreadableBytes = 0; return errorCode; @@ -197,7 +195,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder } } - result = this.BufferAsInt.Span[this.I]; + result = this.BufferAsInt[this.I]; this.I++; this.UnreadableBytes = 0; return errorCode; @@ -231,20 +229,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder DecoderThrowHelper.ThrowImageFormatException.FillCalledWhenUnreadBytesExist(); } - Span byteSpan = this.Buffer.Span; - // Move the last 2 bytes to the start of the buffer, in case we need // to call UnreadByteStuffedByte. if (this.J > 2) { - byteSpan[0] = byteSpan[this.J - 2]; - byteSpan[1] = byteSpan[this.J - 1]; + this.Buffer[0] = this.Buffer[this.J - 2]; + this.Buffer[1] = this.Buffer[this.J - 1]; this.I = 2; this.J = 2; } // Fill in the rest of the buffer. - int n = inputStream.Read(this.Buffer.Array, this.J, byteSpan.Length - this.J); + int n = inputStream.Read(this.Buffer, this.J, this.Buffer.Length - this.J); if (n == 0) { return OrigDecoderErrorCode.UnexpectedEndOfStream; @@ -252,10 +248,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder this.J += n; - Span intSpan = this.BufferAsInt.Span; - for (int i = 0; i < byteSpan.Length; i++) + for (int i = 0; i < this.Buffer.Length; i++) { - intSpan[i] = byteSpan[i]; + this.BufferAsInt[i] = this.Buffer[i]; } return OrigDecoderErrorCode.NoError; diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs index e9f468a85d..e7c58f2346 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs @@ -158,13 +158,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder { if (this.Bytes.J - this.Bytes.I >= length) { - Array.Copy(this.Bytes.Buffer.Array, this.Bytes.I, data, offset, length); + Array.Copy(this.Bytes.Buffer, this.Bytes.I, data, offset, length); this.Bytes.I += length; length -= length; } else { - Array.Copy(this.Bytes.Buffer.Array, this.Bytes.I, data, offset, this.Bytes.J - this.Bytes.I); + Array.Copy(this.Bytes.Buffer, this.Bytes.I, data, offset, this.Bytes.J - this.Bytes.I); offset += this.Bytes.J - this.Bytes.I; length -= this.Bytes.J - this.Bytes.I; this.Bytes.I += this.Bytes.J - this.Bytes.I; From 34e3920a4fd28fcd9473592d5b9f11975eb9b10f Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 27 Feb 2018 17:34:09 +1100 Subject: [PATCH 118/151] Remove unneeded parameter --- .../Formats/Jpeg/GolangPort/Components/Decoder/Bytes.cs | 6 +----- .../Jpeg/GolangPort/Components/Decoder/InputProcessor.cs | 5 ++--- .../Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs | 2 +- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bytes.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bytes.cs index 3fc46093eb..2a3817400c 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bytes.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bytes.cs @@ -2,12 +2,9 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Buffers; using System.IO; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Memory; - namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder { /// @@ -54,9 +51,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder /// /// Creates a new instance of the , and initializes it's buffer. /// - /// The to use for buffer allocations. /// The bytes created - public static Bytes Create(MemoryManager memoryManager) + public static Bytes Create() { // DO NOT bother with buffers and array pooling here! // It only makes things worse! diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs index e7c58f2346..01bd65bfc7 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs @@ -28,13 +28,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder /// /// Initializes a new instance of the struct. /// - /// The to use for buffer allocations. /// The input /// Temporal buffer, same as - public InputProcessor(MemoryManager memoryManager, Stream inputStream, byte[] temp) + public InputProcessor(Stream inputStream, byte[] temp) { this.Bits = default(Bits); - this.Bytes = Bytes.Create(memoryManager); + this.Bytes = Bytes.Create(); this.InputStream = inputStream; this.Temp = temp; this.LastErrorCode = OrigDecoderErrorCode.NoError; diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs index ddc294fa44..33d6257257 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs @@ -231,7 +231,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort { this.MetaData = new ImageMetaData(); this.InputStream = stream; - this.InputProcessor = new InputProcessor(this.configuration.MemoryManager, stream, this.Temp); + this.InputProcessor = new InputProcessor(stream, this.Temp); // Check for the Start Of Image marker. this.InputProcessor.ReadFull(this.Temp, 0, 2); From 59f1f0fc05a2e3c9cb70ff69b86eca60994b00ed Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 27 Feb 2018 16:44:43 +0100 Subject: [PATCH 119/151] Temporary Vortex --- src/ImageSharp.Drawing/Paths/ShapeRegion.cs | 2 +- src/ImageSharp/Common/Extensions/SimdUtils.cs | 4 ++-- src/ImageSharp/Common/Helpers/ParallelFor.cs | 6 +++--- .../Jpeg/Common/Decoder/JpegComponentPostProcessor.cs | 2 +- .../Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs | 2 +- .../Jpeg/GolangPort/Components/Decoder/InputProcessor.cs | 2 +- .../Jpeg/GolangPort/Components/Decoder/OrigHuffmanTree.cs | 2 +- src/ImageSharp/Memory/Buffer2D{T}.cs | 2 +- .../Processing/Processors/Transforms/ResizeProcessor.cs | 2 +- .../ReferenceImplementations.LLM_FloatingPoint_DCT.cs | 2 +- .../ImageSharp.Tests/TestUtilities/TestImageExtensions.cs | 8 ++++---- 11 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/ImageSharp.Drawing/Paths/ShapeRegion.cs b/src/ImageSharp.Drawing/Paths/ShapeRegion.cs index 072a38cf86..cc27f7fbb8 100644 --- a/src/ImageSharp.Drawing/Paths/ShapeRegion.cs +++ b/src/ImageSharp.Drawing/Paths/ShapeRegion.cs @@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp.Drawing var start = new PointF(this.Bounds.Left - 1, y); var end = new PointF(this.Bounds.Right + 1, y); - // TODO: This is a temporal workaround because of the lack of Span API-s on IPath. We should use MemoryManager.Allocate() here! + // TODO: This is a temporary workaround because of the lack of Span API-s on IPath. We should use MemoryManager.Allocate() here! PointF[] innerBuffer = new PointF[buffer.Length]; int count = this.Shape.FindIntersections(start, end, innerBuffer, 0); diff --git a/src/ImageSharp/Common/Extensions/SimdUtils.cs b/src/ImageSharp/Common/Extensions/SimdUtils.cs index 0188bc03cf..7f46b7a847 100644 --- a/src/ImageSharp/Common/Extensions/SimdUtils.cs +++ b/src/ImageSharp/Common/Extensions/SimdUtils.cs @@ -83,7 +83,7 @@ namespace SixLabors.ImageSharp Vector magick = new Vector(32768.0f); Vector scale = new Vector(255f) / new Vector(256f); - // need to copy to a temporal struct, because + // need to copy to a temporary struct, because // SimdUtils.Octet.OfUInt32 temp = Unsafe.As, SimdUtils.Octet.OfUInt32>(ref x) // does not work. TODO: This might be a CoreClr bug, need to ask/report var temp = default(Octet.OfUInt32); @@ -124,7 +124,7 @@ namespace SixLabors.ImageSharp Vector magick = new Vector(32768.0f); Vector scale = new Vector(255f) / new Vector(256f); - // need to copy to a temporal struct, because + // need to copy to a temporary struct, because // SimdUtils.Octet.OfUInt32 temp = Unsafe.As, SimdUtils.Octet.OfUInt32>(ref x) // does not work. TODO: This might be a CoreClr bug, need to ask/report var temp = default(Octet.OfUInt32); diff --git a/src/ImageSharp/Common/Helpers/ParallelFor.cs b/src/ImageSharp/Common/Helpers/ParallelFor.cs index 9a30b0b508..da91259051 100644 --- a/src/ImageSharp/Common/Helpers/ParallelFor.cs +++ b/src/ImageSharp/Common/Helpers/ParallelFor.cs @@ -18,8 +18,8 @@ namespace SixLabors.ImageSharp } /// - /// Helper method to execute Parallel.For with temporal worker buffer in an optimized way. - /// The buffer will be only instantiated for each worker Task, the contents are not cleaned automatically. + /// Helper method to execute Parallel.For with temporary worker buffer shared between executing tasks. + /// The buffer is not guaranteed to be clean! /// /// The value type of the buffer /// The start index, inclusive. @@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp /// The used for getting the and /// The length of the requested parallel buffer /// The delegate that is invoked once per iteration. - public static void WithTemporalBuffer( + public static void WithTemporaryBuffer( int fromInclusive, int toExclusive, Configuration configuration, diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegComponentPostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegComponentPostProcessor.cs index ea9e52ae1b..1be637b6df 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegComponentPostProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegComponentPostProcessor.cs @@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder public IJpegComponent Component { get; } /// - /// Gets the temporal working buffer of color values. + /// Gets the temporary working buffer of color values. /// public Buffer2D ColorBuffer { get; } diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs index aa1c216a75..2adf3e02d0 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs @@ -74,7 +74,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder public int NumberOfPostProcessorSteps { get; } /// - /// Gets the size of the temporal buffers we need to allocate into . + /// Gets the size of the temporary buffers we need to allocate into . /// public Size PostProcessorBufferSize { get; } diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs index e7c58f2346..3bef32551f 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs @@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder public Stream InputStream { get; } /// - /// Gets the temporal buffer, same instance as + /// Gets the temporary buffer, same instance as /// public byte[] Temp { get; } diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigHuffmanTree.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigHuffmanTree.cs index 85273c69ed..dbc7bb0f7f 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigHuffmanTree.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigHuffmanTree.cs @@ -107,7 +107,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder /// Internal part of the DHT processor, whatever does it mean /// /// The decoder instance - /// The temporal buffer that holds the data that has been read from the Jpeg stream + /// The temporary buffer that holds the data that has been read from the Jpeg stream /// Remaining bits public void ProcessDefineHuffmanTablesMarkerLoop( ref InputProcessor inputProcessor, diff --git a/src/ImageSharp/Memory/Buffer2D{T}.cs b/src/ImageSharp/Memory/Buffer2D{T}.cs index 7f9fb59c4c..dc992368c9 100644 --- a/src/ImageSharp/Memory/Buffer2D{T}.cs +++ b/src/ImageSharp/Memory/Buffer2D{T}.cs @@ -72,7 +72,7 @@ namespace SixLabors.ImageSharp.Memory /// /// Swap the contents (, , ) of the two buffers. - /// Useful to transfer the contents of a temporal to a persistent + /// Useful to transfer the contents of a temporary to a persistent /// /// The first buffer /// The second buffer diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs index 169496a982..2c18dc29b2 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs @@ -128,7 +128,7 @@ namespace SixLabors.ImageSharp.Processing.Processors { firstPassPixels.Buffer.Clear(); - ParallelFor.WithTemporalBuffer( + ParallelFor.WithTemporaryBuffer( 0, sourceRectangle.Bottom, configuration, diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.LLM_FloatingPoint_DCT.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.LLM_FloatingPoint_DCT.cs index 37d42eb72f..e18323f848 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.LLM_FloatingPoint_DCT.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.LLM_FloatingPoint_DCT.cs @@ -131,7 +131,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils /// /// Original: https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L239 - /// Applyies IDCT transformation on "s" copying transformed values to "d", using temporal block "temp" + /// Applyies IDCT transformation on "s" copying transformed values to "d", using temporary block "temp" /// /// /// diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index e9dc09989c..6014e25334 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -254,7 +254,7 @@ namespace SixLabors.ImageSharp.Tests testOutputDetails, appendPixelTypeToFileName); - var temporalFrameImages = new List>(); + var temporaryFrameImages = new List>(); IImageDecoder decoder = TestEnvironment.GetReferenceDecoder(frameFiles[0]); @@ -266,14 +266,14 @@ namespace SixLabors.ImageSharp.Tests } var tempImage = Image.Load(path, decoder); - temporalFrameImages.Add(tempImage); + temporaryFrameImages.Add(tempImage); } - Image firstTemp = temporalFrameImages[0]; + Image firstTemp = temporaryFrameImages[0]; var result = new Image(firstTemp.Width, firstTemp.Height); - foreach (Image fi in temporalFrameImages) + foreach (Image fi in temporaryFrameImages) { result.Frames.AddFrame(fi.Frames.RootFrame); fi.Dispose(); From 3c788ebf7f1282acf2d81b70803da03a9af648a5 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Wed, 28 Feb 2018 01:40:58 +0100 Subject: [PATCH 120/151] added failing tests for #464 --- tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs | 1 + tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs | 1 + tests/ImageSharp.Tests/TestImages.cs | 1 + tests/Images/External | 2 +- .../Input/Jpg/issues/Issue464-CMYK-DecodedAsGrayscale.jpg | 3 +++ 5 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 tests/Images/Input/Jpg/issues/Issue464-CMYK-DecodedAsGrayscale.jpg diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index 9d04cf354e..d78277528c 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -43,6 +43,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg TestImages.Jpeg.Baseline.Bad.BadEOF, TestImages.Jpeg.Baseline.Bad.ExifUndefType, TestImages.Jpeg.Issues.MultiHuffmanBaseline394, + TestImages.Jpeg.Issues.CmykIssueBaseline464 }; public static string[] ProgressiveTestJpegs = diff --git a/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs index 4508c6863a..32c3be47d0 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs @@ -29,6 +29,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg TestImages.Jpeg.Baseline.Jpeg444, TestImages.Jpeg.Baseline.Testorig420, TestImages.Jpeg.Baseline.Jpeg420Small, TestImages.Jpeg.Baseline.Bad.BadEOF, TestImages.Jpeg.Baseline.Bad.ExifUndefType, + TestImages.Jpeg.Issues.CmykIssueBaseline464 }; public static readonly string[] ProgressiveTestJpegs = diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 864c963327..bb66414fb1 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -129,6 +129,7 @@ namespace SixLabors.ImageSharp.Tests public const string BadCoeffsProgressive178 = "Jpg/issues/Issue178-BadCoeffsProgressive-Lemon.jpg"; public const string BadZigZagProgressive385 = "Jpg/issues/Issue385-BadZigZag-Progressive.jpg"; public const string MultiHuffmanBaseline394 = "Jpg/issues/Issue394-MultiHuffmanBaseline-Speakers.jpg"; + public const string CmykIssueBaseline464 = "Jpg/issues/Issue464-CMYK-DecodedAsGrayscale.jpg"; } public static readonly string[] All = Baseline.All.Concat(Progressive.All).ToArray(); diff --git a/tests/Images/External b/tests/Images/External index 20f83891ce..cdc178daa5 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 20f83891ce75597486f5532010a8c5dea1419a4d +Subproject commit cdc178daa552856157d1834ad136f9a28996a0f9 diff --git a/tests/Images/Input/Jpg/issues/Issue464-CMYK-DecodedAsGrayscale.jpg b/tests/Images/Input/Jpg/issues/Issue464-CMYK-DecodedAsGrayscale.jpg new file mode 100644 index 0000000000..a158e0ba2c --- /dev/null +++ b/tests/Images/Input/Jpg/issues/Issue464-CMYK-DecodedAsGrayscale.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:96f29bee2175f34b9637d684f8336bc3e5d2bb2711cef352bc9def6ed4424d04 +size 47443 From c1eaf6e5da94e190ef3ac1f8001257e142c8ea63 Mon Sep 17 00:00:00 2001 From: Jason Nelson Date: Tue, 27 Feb 2018 18:42:31 -0800 Subject: [PATCH 121/151] Update System.Memory --- .../Advanced/AdvancedImageExtensions.cs | 3 +- src/ImageSharp/Common/Extensions/SimdUtils.cs | 8 ++--- .../Formats/Jpeg/Common/Block8x8.cs | 5 +-- .../Formats/Jpeg/Common/Block8x8F.cs | 4 +-- .../JpegColorConverter.FromYCbCrSimd.cs | 9 ++--- .../JpegColorConverter.FromYCbCrSimdAvx2.cs | 9 ++--- .../ColorConverters/JpegColorConverter.cs | 2 +- .../Formats/Jpeg/Common/GenericBlock8x8.cs | 2 +- .../Formats/Png/Filters/AverageFilter.cs | 11 ++++--- .../Formats/Png/Filters/PaethFilter.cs | 11 ++++--- .../Formats/Png/Filters/SubFilter.cs | 7 ++-- .../Formats/Png/Filters/UpFilter.cs | 11 ++++--- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 4 +-- src/ImageSharp/ImageSharp.csproj | 6 ++-- src/ImageSharp/Memory/BufferExtensions.cs | 3 +- src/ImageSharp/Memory/SpanHelper.cs | 7 ++-- .../PixelOperations{TPixel}.Generated.cs | 33 ++++++++++--------- .../Rgba32.PixelOperations.Generated.cs | 26 +++++++-------- .../PixelFormats/PixelOperations{TPixel}.cs | 9 ++--- .../PixelFormats/Rgba32.PixelOperations.cs | 4 +-- .../Processors/Transforms/WeightsWindow.cs | 6 ++-- .../ImageSharp.Benchmarks.csproj | 4 +-- .../ImageSharp.Tests/ImageSharp.Tests.csproj | 3 +- .../Memory/ArrayPoolMemoryManagerTests.cs | 2 +- .../ImageSharp.Tests/Memory/Buffer2DTests.cs | 4 +-- .../Memory/BufferTestSuite.cs | 7 ++-- .../Memory/SpanUtilityTests.cs | 20 +++++------ 27 files changed, 116 insertions(+), 104 deletions(-) diff --git a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs index 612ced5d8d..24d2dd4cc4 100644 --- a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs +++ b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -148,6 +149,6 @@ namespace SixLabors.ImageSharp.Advanced /// A reference to the element. private static ref TPixel DangerousGetPinnableReferenceToPixelBuffer(IPixelSource source) where TPixel : struct, IPixel - => ref source.PixelBuffer.Span.DangerousGetPinnableReference(); + => ref MemoryMarshal.GetReference(source.PixelBuffer.Span); } } diff --git a/src/ImageSharp/Common/Extensions/SimdUtils.cs b/src/ImageSharp/Common/Extensions/SimdUtils.cs index 7f46b7a847..7b77fefcac 100644 --- a/src/ImageSharp/Common/Extensions/SimdUtils.cs +++ b/src/ImageSharp/Common/Extensions/SimdUtils.cs @@ -76,8 +76,8 @@ namespace SixLabors.ImageSharp return; } - ref Vector srcBase = ref Unsafe.As>(ref source.DangerousGetPinnableReference()); - ref Octet.OfByte destBase = ref Unsafe.As(ref dest.DangerousGetPinnableReference()); + ref Vector srcBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref Octet.OfByte destBase = ref Unsafe.As(ref MemoryMarshal.GetReference(dest)); int n = source.Length / 8; Vector magick = new Vector(32768.0f); @@ -117,8 +117,8 @@ namespace SixLabors.ImageSharp return; } - ref Vector srcBase = ref Unsafe.As>(ref source.DangerousGetPinnableReference()); - ref Octet.OfByte destBase = ref Unsafe.As(ref dest.DangerousGetPinnableReference()); + ref Vector srcBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref Octet.OfByte destBase = ref Unsafe.As(ref MemoryMarshal.GetReference(dest)); int n = source.Length / 8; Vector magick = new Vector(32768.0f); diff --git a/src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs b/src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs index 1066cfa808..11a456ef9b 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs @@ -4,6 +4,7 @@ using System; using System.Diagnostics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Text; namespace SixLabors.ImageSharp.Formats.Jpeg.Common @@ -34,7 +35,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common public Block8x8(Span coefficients) { ref byte selfRef = ref Unsafe.As(ref this); - ref byte sourceRef = ref coefficients.NonPortableCast().DangerousGetPinnableReference(); + ref byte sourceRef = ref MemoryMarshal.GetReference(coefficients.NonPortableCast()); Unsafe.CopyBlock(ref selfRef, ref sourceRef, Size * sizeof(short)); } @@ -204,7 +205,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common public void CopyTo(Span destination) { ref byte selfRef = ref Unsafe.As(ref this); - ref byte destRef = ref destination.NonPortableCast().DangerousGetPinnableReference(); + ref byte destRef = ref MemoryMarshal.GetReference(destination.NonPortableCast()); Unsafe.CopyBlock(ref destRef, ref selfRef, Size * sizeof(short)); } diff --git a/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs b/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs index 2dd42288cb..f45b5df4eb 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs @@ -163,7 +163,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common [MethodImpl(MethodImplOptions.AggressiveInlining)] public void LoadFrom(Span source) { - ref byte s = ref Unsafe.As(ref source.DangerousGetPinnableReference()); + ref byte s = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); ref byte d = ref Unsafe.As(ref this); Unsafe.CopyBlock(ref d, ref s, Size * sizeof(float)); @@ -203,7 +203,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common [MethodImpl(MethodImplOptions.AggressiveInlining)] public void CopyTo(Span dest) { - ref byte d = ref Unsafe.As(ref dest.DangerousGetPinnableReference()); + ref byte d = ref Unsafe.As(ref MemoryMarshal.GetReference(dest)); ref byte s = ref Unsafe.As(ref this); Unsafe.CopyBlock(ref d, ref s, Size * sizeof(float)); diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs index a7fc136afe..2f214f88a9 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs @@ -4,6 +4,7 @@ using System; using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Common.Tuples; namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters @@ -37,14 +38,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters DebugGuard.IsTrue(result.Length % 8 == 0, nameof(result), "result.Length should be divisable by 8!"); ref Vector4Pair yBase = - ref Unsafe.As(ref values.Component0.DangerousGetPinnableReference()); + ref Unsafe.As(ref MemoryMarshal.GetReference(values.Component0)); ref Vector4Pair cbBase = - ref Unsafe.As(ref values.Component1.DangerousGetPinnableReference()); + ref Unsafe.As(ref MemoryMarshal.GetReference(values.Component1)); ref Vector4Pair crBase = - ref Unsafe.As(ref values.Component2.DangerousGetPinnableReference()); + ref Unsafe.As(ref MemoryMarshal.GetReference(values.Component2)); ref Vector4Octet resultBase = - ref Unsafe.As(ref result.DangerousGetPinnableReference()); + ref Unsafe.As(ref MemoryMarshal.GetReference(result)); var chromaOffset = new Vector4(-128f); diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs index 77e74c32b0..f8a4514221 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs @@ -4,6 +4,7 @@ using System; using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Common.Tuples; // ReSharper disable ImpureMethodCallOnReadonlyValueField @@ -46,14 +47,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters } ref Vector yBase = - ref Unsafe.As>(ref values.Component0.DangerousGetPinnableReference()); + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0)); ref Vector cbBase = - ref Unsafe.As>(ref values.Component1.DangerousGetPinnableReference()); + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1)); ref Vector crBase = - ref Unsafe.As>(ref values.Component2.DangerousGetPinnableReference()); + ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2)); ref Vector4Octet resultBase = - ref Unsafe.As(ref result.DangerousGetPinnableReference()); + ref Unsafe.As(ref MemoryMarshal.GetReference(result)); var chromaOffset = new Vector(-128f); diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.cs index e0abc3215c..23c2071cc6 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.cs @@ -66,7 +66,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters /// /// A stack-only struct to reference the input buffers using -s. /// - public struct ComponentValues + public ref struct ComponentValues { /// /// The component count diff --git a/src/ImageSharp/Formats/Jpeg/Common/GenericBlock8x8.cs b/src/ImageSharp/Formats/Jpeg/Common/GenericBlock8x8.cs index e20e850d74..09a7eb73aa 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/GenericBlock8x8.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/GenericBlock8x8.cs @@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common ref byte blockStart = ref Unsafe.As, byte>(ref this); ref byte imageStart = ref Unsafe.As( - ref Unsafe.Add(ref source.GetRowSpan(sourceY).DangerousGetPinnableReference(), sourceX)); + ref Unsafe.Add(ref MemoryMarshal.GetReference(source.GetRowSpan(sourceY)), sourceX)); int blockRowSizeInBytes = 8 * Unsafe.SizeOf(); int imageRowSizeInBytes = source.Width * Unsafe.SizeOf(); diff --git a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs index 0d3a65dbd8..de62d47029 100644 --- a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs @@ -3,6 +3,7 @@ using System; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.Formats.Png.Filters { @@ -24,8 +25,8 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters { DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline)); - ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); - ref byte prevBaseRef = ref previousScanline.DangerousGetPinnableReference(); + ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline); + ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline); // Average(x) + floor((Raw(x-bpp)+Prior(x))/2) for (int x = 1; x < scanline.Length; x++) @@ -60,9 +61,9 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline)); DebugGuard.MustBeSizedAtLeast(result, scanline, nameof(result)); - ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); - ref byte prevBaseRef = ref previousScanline.DangerousGetPinnableReference(); - ref byte resultBaseRef = ref result.DangerousGetPinnableReference(); + ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline); + ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline); + ref byte resultBaseRef = ref MemoryMarshal.GetReference(result); sum = 0; // Average(x) = Raw(x) - floor((Raw(x-bpp)+Prior(x))/2) diff --git a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs index 08e4938804..7e05d736f9 100644 --- a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs @@ -3,6 +3,7 @@ using System; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.Formats.Png.Filters { @@ -25,8 +26,8 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters { DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline)); - ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); - ref byte prevBaseRef = ref previousScanline.DangerousGetPinnableReference(); + ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline); + ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline); // Paeth(x) + PaethPredictor(Raw(x-bpp), Prior(x), Prior(x-bpp)) int offset = bytesPerPixel + 1; @@ -61,9 +62,9 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline)); DebugGuard.MustBeSizedAtLeast(result, scanline, nameof(result)); - ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); - ref byte prevBaseRef = ref previousScanline.DangerousGetPinnableReference(); - ref byte resultBaseRef = ref result.DangerousGetPinnableReference(); + ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline); + ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline); + ref byte resultBaseRef = ref MemoryMarshal.GetReference(result); sum = 0; // Paeth(x) = Raw(x) - PaethPredictor(Raw(x-bpp), Prior(x), Prior(x - bpp)) diff --git a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs index 5ee8664400..c0db7da935 100644 --- a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs @@ -3,6 +3,7 @@ using System; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.Formats.Png.Filters { @@ -21,7 +22,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Decode(Span scanline, int bytesPerPixel) { - ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); + ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline); // Sub(x) + Raw(x-bpp) for (int x = 1; x < scanline.Length; x++) @@ -52,8 +53,8 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters { DebugGuard.MustBeSizedAtLeast(result, scanline, nameof(result)); - ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); - ref byte resultBaseRef = ref result.DangerousGetPinnableReference(); + ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline); + ref byte resultBaseRef = ref MemoryMarshal.GetReference(result); sum = 0; // Sub(x) = Raw(x) - Raw(x-bpp) diff --git a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs index 6e8f780e5c..81c063ea9e 100644 --- a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs @@ -3,6 +3,7 @@ using System; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.Formats.Png.Filters { @@ -23,8 +24,8 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters { DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline)); - ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); - ref byte prevBaseRef = ref previousScanline.DangerousGetPinnableReference(); + ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline); + ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline); // Up(x) + Prior(x) for (int x = 1; x < scanline.Length; x++) @@ -48,9 +49,9 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline)); DebugGuard.MustBeSizedAtLeast(result, scanline, nameof(result)); - ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); - ref byte prevBaseRef = ref previousScanline.DangerousGetPinnableReference(); - ref byte resultBaseRef = ref result.DangerousGetPinnableReference(); + ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline); + ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline); + ref byte resultBaseRef = ref MemoryMarshal.GetReference(result); sum = 0; // Up(x) = Raw(x) - Prior(x) diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index fbff0ae1d9..c1dccdcafc 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -701,7 +701,7 @@ namespace SixLabors.ImageSharp.Formats.Png Span rowSpan = pixels.GetPixelRowSpan(this.currentRow); // Trim the first marker byte from the buffer - var scanlineBuffer = new Span(defilteredScanline, 1); + var scanlineBuffer = new Span(defilteredScanline, 1, defilteredScanline.Length - 1); switch (this.pngColorType) { @@ -932,7 +932,7 @@ namespace SixLabors.ImageSharp.Formats.Png var color = default(TPixel); // Trim the first marker byte from the buffer - var scanlineBuffer = new Span(defilteredScanline, 1); + var scanlineBuffer = new Span(defilteredScanline, 1, defilteredScanline.Length - 1); switch (this.pngColorType) { diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index cb0539f786..86a0ab7ea5 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -29,6 +29,7 @@ portable True IOperation + 7.2 @@ -40,12 +41,11 @@ All - - + + - diff --git a/src/ImageSharp/Memory/BufferExtensions.cs b/src/ImageSharp/Memory/BufferExtensions.cs index 919a6ef345..dd3114c21c 100644 --- a/src/ImageSharp/Memory/BufferExtensions.cs +++ b/src/ImageSharp/Memory/BufferExtensions.cs @@ -4,6 +4,7 @@ using System; using System.IO; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.Memory { @@ -53,7 +54,7 @@ namespace SixLabors.ImageSharp.Memory public static ref T DangerousGetPinnableReference(this IBuffer buffer) where T : struct => - ref buffer.Span.DangerousGetPinnableReference(); + ref MemoryMarshal.GetReference(buffer.Span); public static void Read(this Stream stream, IManagedByteBuffer buffer) { diff --git a/src/ImageSharp/Memory/SpanHelper.cs b/src/ImageSharp/Memory/SpanHelper.cs index 73bc5f55d8..0c327484a0 100644 --- a/src/ImageSharp/Memory/SpanHelper.cs +++ b/src/ImageSharp/Memory/SpanHelper.cs @@ -4,6 +4,7 @@ using System; using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.Memory { @@ -22,7 +23,7 @@ namespace SixLabors.ImageSharp.Memory public static ref Vector FetchVector(this Span span) where T : struct { - return ref Unsafe.As>(ref span.DangerousGetPinnableReference()); + return ref Unsafe.As>(ref MemoryMarshal.GetReference(span)); } /// @@ -39,8 +40,8 @@ namespace SixLabors.ImageSharp.Memory DebugGuard.MustBeLessThanOrEqualTo(count, source.Length, nameof(count)); DebugGuard.MustBeLessThanOrEqualTo(count, destination.Length, nameof(count)); - ref byte srcRef = ref Unsafe.As(ref source.DangerousGetPinnableReference()); - ref byte destRef = ref Unsafe.As(ref destination.DangerousGetPinnableReference()); + ref byte srcRef = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); + ref byte destRef = ref Unsafe.As(ref MemoryMarshal.GetReference(destination)); int byteCount = Unsafe.SizeOf() * count; diff --git a/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.cs b/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.cs index 9553ec82d6..964fa98f95 100644 --- a/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.cs +++ b/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.cs @@ -6,6 +6,7 @@ namespace SixLabors.ImageSharp.PixelFormats { using System; using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; public partial class PixelOperations { @@ -20,8 +21,8 @@ namespace SixLabors.ImageSharp.PixelFormats { GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); - ref Rgba32 sourceRef = ref source.DangerousGetPinnableReference(); - ref TPixel destRef = ref destPixels.DangerousGetPinnableReference(); + ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(source); + ref TPixel destRef = ref MemoryMarshal.GetReference(destPixels); Rgba32 rgba = new Rgba32(0, 0, 0, 255); @@ -57,8 +58,8 @@ namespace SixLabors.ImageSharp.PixelFormats { GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count); - ref TPixel sourceBaseRef = ref sourcePixels.DangerousGetPinnableReference(); - ref Rgba32 destBaseRef = ref dest.DangerousGetPinnableReference(); + ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgba32 destBaseRef = ref MemoryMarshal.GetReference(dest); for (int i = 0; i < count; i++) { @@ -91,8 +92,8 @@ namespace SixLabors.ImageSharp.PixelFormats { GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); - ref Bgra32 sourceRef = ref source.DangerousGetPinnableReference(); - ref TPixel destRef = ref destPixels.DangerousGetPinnableReference(); + ref Bgra32 sourceRef = ref MemoryMarshal.GetReference(source); + ref TPixel destRef = ref MemoryMarshal.GetReference(destPixels); Rgba32 rgba = new Rgba32(0, 0, 0, 255); @@ -128,8 +129,8 @@ namespace SixLabors.ImageSharp.PixelFormats { GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count); - ref TPixel sourceBaseRef = ref sourcePixels.DangerousGetPinnableReference(); - ref Bgra32 destBaseRef = ref dest.DangerousGetPinnableReference(); + ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Bgra32 destBaseRef = ref MemoryMarshal.GetReference(dest); for (int i = 0; i < count; i++) { @@ -162,8 +163,8 @@ namespace SixLabors.ImageSharp.PixelFormats { GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); - ref Rgb24 sourceRef = ref source.DangerousGetPinnableReference(); - ref TPixel destRef = ref destPixels.DangerousGetPinnableReference(); + ref Rgb24 sourceRef = ref MemoryMarshal.GetReference(source); + ref TPixel destRef = ref MemoryMarshal.GetReference(destPixels); Rgba32 rgba = new Rgba32(0, 0, 0, 255); @@ -199,8 +200,8 @@ namespace SixLabors.ImageSharp.PixelFormats { GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count); - ref TPixel sourceBaseRef = ref sourcePixels.DangerousGetPinnableReference(); - ref Rgb24 destBaseRef = ref dest.DangerousGetPinnableReference(); + ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgb24 destBaseRef = ref MemoryMarshal.GetReference(dest); for (int i = 0; i < count; i++) { @@ -233,8 +234,8 @@ namespace SixLabors.ImageSharp.PixelFormats { GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); - ref Bgr24 sourceRef = ref source.DangerousGetPinnableReference(); - ref TPixel destRef = ref destPixels.DangerousGetPinnableReference(); + ref Bgr24 sourceRef = ref MemoryMarshal.GetReference(source); + ref TPixel destRef = ref MemoryMarshal.GetReference(destPixels); Rgba32 rgba = new Rgba32(0, 0, 0, 255); @@ -270,8 +271,8 @@ namespace SixLabors.ImageSharp.PixelFormats { GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count); - ref TPixel sourceBaseRef = ref sourcePixels.DangerousGetPinnableReference(); - ref Bgr24 destBaseRef = ref dest.DangerousGetPinnableReference(); + ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Bgr24 destBaseRef = ref MemoryMarshal.GetReference(dest); for (int i = 0; i < count; i++) { diff --git a/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.cs index 659e702281..43060539dc 100644 --- a/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.cs @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp { using System; using System.Runtime.CompilerServices; - + using System.Runtime.InteropServices; using SixLabors.ImageSharp.PixelFormats; /// @@ -22,8 +22,8 @@ namespace SixLabors.ImageSharp { GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); - ref Rgb24 sourceRef = ref source.DangerousGetPinnableReference(); - ref Rgba32 destRef = ref destPixels.DangerousGetPinnableReference(); + ref Rgb24 sourceRef = ref MemoryMarshal.GetReference(source); + ref Rgba32 destRef = ref MemoryMarshal.GetReference(destPixels); for (int i = 0; i < count; i++) { @@ -38,8 +38,8 @@ namespace SixLabors.ImageSharp { GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count); - ref Rgba32 sourceRef = ref sourcePixels.DangerousGetPinnableReference(); - ref Rgb24 destRef = ref dest.DangerousGetPinnableReference(); + ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgb24 destRef = ref MemoryMarshal.GetReference(dest); for (int i = 0; i < count; i++) { @@ -54,8 +54,8 @@ namespace SixLabors.ImageSharp { GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); - ref Bgr24 sourceRef = ref source.DangerousGetPinnableReference(); - ref Rgba32 destRef = ref destPixels.DangerousGetPinnableReference(); + ref Bgr24 sourceRef = ref MemoryMarshal.GetReference(source); + ref Rgba32 destRef = ref MemoryMarshal.GetReference(destPixels); for (int i = 0; i < count; i++) { @@ -70,8 +70,8 @@ namespace SixLabors.ImageSharp { GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count); - ref Rgba32 sourceRef = ref sourcePixels.DangerousGetPinnableReference(); - ref Bgr24 destRef = ref dest.DangerousGetPinnableReference(); + ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Bgr24 destRef = ref MemoryMarshal.GetReference(dest); for (int i = 0; i < count; i++) { @@ -86,8 +86,8 @@ namespace SixLabors.ImageSharp { GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); - ref Bgra32 sourceRef = ref source.DangerousGetPinnableReference(); - ref Rgba32 destRef = ref destPixels.DangerousGetPinnableReference(); + ref Bgra32 sourceRef = ref MemoryMarshal.GetReference(source); + ref Rgba32 destRef = ref MemoryMarshal.GetReference(destPixels); for (int i = 0; i < count; i++) { @@ -102,8 +102,8 @@ namespace SixLabors.ImageSharp { GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count); - ref Rgba32 sourceRef = ref sourcePixels.DangerousGetPinnableReference(); - ref Bgra32 destRef = ref dest.DangerousGetPinnableReference(); + ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Bgra32 destRef = ref MemoryMarshal.GetReference(dest); for (int i = 0; i < count; i++) { diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs index 4f879fbdc7..6f79752406 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs @@ -4,6 +4,7 @@ using System; using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.PixelFormats { @@ -30,8 +31,8 @@ namespace SixLabors.ImageSharp.PixelFormats { GuardSpans(sourceVectors, nameof(sourceVectors), destColors, nameof(destColors), count); - ref Vector4 sourceRef = ref sourceVectors.DangerousGetPinnableReference(); - ref TPixel destRef = ref destColors.DangerousGetPinnableReference(); + ref Vector4 sourceRef = ref MemoryMarshal.GetReference(sourceVectors); + ref TPixel destRef = ref MemoryMarshal.GetReference(destColors); for (int i = 0; i < count; i++) { @@ -51,8 +52,8 @@ namespace SixLabors.ImageSharp.PixelFormats { GuardSpans(sourceColors, nameof(sourceColors), destVectors, nameof(destVectors), count); - ref TPixel sourceRef = ref sourceColors.DangerousGetPinnableReference(); - ref Vector4 destRef = ref destVectors.DangerousGetPinnableReference(); + ref TPixel sourceRef = ref MemoryMarshal.GetReference(sourceColors); + ref Vector4 destRef = ref MemoryMarshal.GetReference(destVectors); for (int i = 0; i < count; i++) { diff --git a/src/ImageSharp/PixelFormats/Rgba32.PixelOperations.cs b/src/ImageSharp/PixelFormats/Rgba32.PixelOperations.cs index 552ac0a018..89a4aba264 100644 --- a/src/ImageSharp/PixelFormats/Rgba32.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/Rgba32.PixelOperations.cs @@ -57,8 +57,8 @@ namespace SixLabors.ImageSharp int unpackedRawCount = count * 4; - ref uint sourceBase = ref Unsafe.As(ref sourceColors.DangerousGetPinnableReference()); - ref UnpackedRGBA destBaseAsUnpacked = ref Unsafe.As(ref destVectors.DangerousGetPinnableReference()); + ref uint sourceBase = ref Unsafe.As(ref MemoryMarshal.GetReference(sourceColors)); + ref UnpackedRGBA destBaseAsUnpacked = ref Unsafe.As(ref MemoryMarshal.GetReference(destVectors)); ref Vector destBaseAsUInt = ref Unsafe.As>(ref destBaseAsUnpacked); ref Vector destBaseAsFloat = ref Unsafe.As>(ref destBaseAsUnpacked); diff --git a/src/ImageSharp/Processing/Processors/Transforms/WeightsWindow.cs b/src/ImageSharp/Processing/Processors/Transforms/WeightsWindow.cs index 399b3db842..26aaec502f 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/WeightsWindow.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/WeightsWindow.cs @@ -4,7 +4,7 @@ using System; using System.Numerics; using System.Runtime.CompilerServices; - +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Processing.Processors @@ -79,7 +79,7 @@ namespace SixLabors.ImageSharp.Processing.Processors { ref float horizontalValues = ref this.GetStartReference(); int left = this.Left; - ref Vector4 vecPtr = ref Unsafe.Add(ref rowSpan.DangerousGetPinnableReference(), left + sourceX); + ref Vector4 vecPtr = ref Unsafe.Add(ref MemoryMarshal.GetReference(rowSpan), left + sourceX); // Destination color components Vector4 result = Vector4.Zero; @@ -106,7 +106,7 @@ namespace SixLabors.ImageSharp.Processing.Processors { ref float horizontalValues = ref this.GetStartReference(); int left = this.Left; - ref Vector4 vecPtr = ref Unsafe.Add(ref rowSpan.DangerousGetPinnableReference(), left + sourceX); + ref Vector4 vecPtr = ref Unsafe.Add(ref MemoryMarshal.GetReference(rowSpan), left + sourceX); // Destination color components Vector4 result = Vector4.Zero; diff --git a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj index 2e0b935155..021b9ead74 100644 --- a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj +++ b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj @@ -19,9 +19,7 @@ - - - + diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index 16f062c6ef..d6ea4a130f 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -16,7 +16,8 @@ - + + diff --git a/tests/ImageSharp.Tests/Memory/ArrayPoolMemoryManagerTests.cs b/tests/ImageSharp.Tests/Memory/ArrayPoolMemoryManagerTests.cs index a199bb319d..c73ce96313 100644 --- a/tests/ImageSharp.Tests/Memory/ArrayPoolMemoryManagerTests.cs +++ b/tests/ImageSharp.Tests/Memory/ArrayPoolMemoryManagerTests.cs @@ -133,7 +133,7 @@ namespace SixLabors.ImageSharp.Tests.Memory public void ReleaseRetainedResources_ReplacesInnerArrayPool(bool keepBufferAlive) { IBuffer buffer = this.MemoryManager.Allocate(32); - ref int ptrToPrev0 = ref buffer.Span.DangerousGetPinnableReference(); + ref int ptrToPrev0 = ref MemoryMarshal.GetReference(buffer.Span); if (!keepBufferAlive) { diff --git a/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs index 565e06572b..82163d2bb4 100644 --- a/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs +++ b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs @@ -5,7 +5,7 @@ namespace SixLabors.ImageSharp.Tests.Memory { using System; using System.Runtime.CompilerServices; - + using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Tests.Common; using SixLabors.Primitives; @@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.Tests.Memory public static void SpanPointsTo(Span span, IBuffer buffer, int bufferOffset = 0) where T : struct { - ref T actual = ref span.DangerousGetPinnableReference(); + ref T actual = ref MemoryMarshal.GetReference(span); ref T expected = ref Unsafe.Add(ref buffer.DangerousGetPinnableReference(), bufferOffset); Assert.True(Unsafe.AreSame(ref expected, ref actual), "span does not point to the expected position"); diff --git a/tests/ImageSharp.Tests/Memory/BufferTestSuite.cs b/tests/ImageSharp.Tests/Memory/BufferTestSuite.cs index 50477cb5cf..eff1f197a0 100644 --- a/tests/ImageSharp.Tests/Memory/BufferTestSuite.cs +++ b/tests/ImageSharp.Tests/Memory/BufferTestSuite.cs @@ -3,6 +3,7 @@ using System; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; using Xunit; // ReSharper disable InconsistentNaming @@ -165,9 +166,9 @@ namespace SixLabors.ImageSharp.Tests.Memory { using (IBuffer buffer = this.Allocate(desiredLength, false, testManagedByteBuffer)) { - ref T a = ref buffer.Span.DangerousGetPinnableReference(); - ref T b = ref buffer.Span.DangerousGetPinnableReference(); - ref T c = ref buffer.Span.DangerousGetPinnableReference(); + ref T a = ref MemoryMarshal.GetReference(buffer.Span); + ref T b = ref MemoryMarshal.GetReference(buffer.Span); + ref T c = ref MemoryMarshal.GetReference(buffer.Span); Assert.True(Unsafe.AreSame(ref a, ref b)); Assert.True(Unsafe.AreSame(ref b, ref c)); diff --git a/tests/ImageSharp.Tests/Memory/SpanUtilityTests.cs b/tests/ImageSharp.Tests/Memory/SpanUtilityTests.cs index 049c4c6ba9..23bc297436 100644 --- a/tests/ImageSharp.Tests/Memory/SpanUtilityTests.cs +++ b/tests/ImageSharp.Tests/Memory/SpanUtilityTests.cs @@ -78,8 +78,8 @@ namespace SixLabors.ImageSharp.Tests.Memory TestStructs.Foo[] source = TestStructs.Foo.CreateArray(count + 2); TestStructs.Foo[] dest = new TestStructs.Foo[count + 5]; - var apSource = new Span(source, 1); - var apDest = new Span(dest, 1); + var apSource = new Span(source, 1, source.Length - 1); + var apDest = new Span(dest, 1, dest.Length - 1); SpanHelper.Copy(apSource, apDest, count - 1); @@ -101,8 +101,8 @@ namespace SixLabors.ImageSharp.Tests.Memory TestStructs.AlignedFoo[] source = TestStructs.AlignedFoo.CreateArray(count + 2); TestStructs.AlignedFoo[] dest = new TestStructs.AlignedFoo[count + 5]; - var apSource = new Span(source, 1); - var apDest = new Span(dest, 1); + var apSource = new Span(source, 1, source.Length - 1); + var apDest = new Span(dest, 1, dest.Length - 1); SpanHelper.Copy(apSource, apDest, count - 1); @@ -124,8 +124,8 @@ namespace SixLabors.ImageSharp.Tests.Memory int[] source = CreateTestInts(count + 2); int[] dest = new int[count + 5]; - var apSource = new Span(source, 1); - var apDest = new Span(dest, 1); + var apSource = new Span(source, 1, source.Length - 1); + var apDest = new Span(dest, 1, dest.Length - 1); SpanHelper.Copy(apSource, apDest, count - 1); @@ -148,8 +148,8 @@ namespace SixLabors.ImageSharp.Tests.Memory TestStructs.Foo[] source = TestStructs.Foo.CreateArray(count + 2); byte[] dest = new byte[destCount + sizeof(TestStructs.Foo) * 2]; - var apSource = new Span(source, 1); - var apDest = new Span(dest, sizeof(TestStructs.Foo)); + var apSource = new Span(source, 1, source.Length - 1); + var apDest = new Span(dest, sizeof(TestStructs.Foo), dest.Length - sizeof(TestStructs.Foo)); SpanHelper.Copy(apSource.AsBytes(), apDest, (count - 1) * sizeof(TestStructs.Foo)); @@ -171,8 +171,8 @@ namespace SixLabors.ImageSharp.Tests.Memory TestStructs.AlignedFoo[] source = TestStructs.AlignedFoo.CreateArray(count + 2); byte[] dest = new byte[destCount + sizeof(TestStructs.AlignedFoo) * 2]; - var apSource = new Span(source, 1); - var apDest = new Span(dest, sizeof(TestStructs.AlignedFoo)); + var apSource = new Span(source, 1, source.Length - 1); + var apDest = new Span(dest, sizeof(TestStructs.AlignedFoo), dest.Length - sizeof(TestStructs.AlignedFoo)); SpanHelper.Copy(apSource.AsBytes(), apDest, (count - 1) * sizeof(TestStructs.AlignedFoo)); From dd26af1a5005d4beb46d73e283fddb4498091d5b Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 28 Feb 2018 18:12:29 +1100 Subject: [PATCH 122/151] Fix #464 --- .../Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs index 33d6257257..aa89abf2e6 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs @@ -351,12 +351,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort return; } - // when this is a progressive image this gets called a number of times - // need to know how many times this should be called in total. this.ProcessStartOfScanMarker(remaining); - if (this.InputProcessor.ReachedEOF || !this.IsProgressive) + if (this.InputProcessor.ReachedEOF) { - // if unexpeced EOF reached or this is not a progressive image we can stop processing bytes as we now have the image data. + // If unexpected EOF reached. We can stop processing bytes as we now have the image data. processBytes = false; } From a1e04d8ea9c4306704323e74187da3b38958aa49 Mon Sep 17 00:00:00 2001 From: Vicente Penades Date: Wed, 28 Feb 2018 10:10:41 +0100 Subject: [PATCH 123/151] Refactored Image Formats management into its own class --- src/ImageSharp/Configuration.cs | 94 +++------ src/ImageSharp/Formats/ImageFormatsManager.cs | 185 ++++++++++++++++++ 2 files changed, 209 insertions(+), 70 deletions(-) create mode 100644 src/ImageSharp/Formats/ImageFormatsManager.cs diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index 9a627eeb77..d53d9a9a59 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -24,28 +24,8 @@ namespace SixLabors.ImageSharp /// /// A lazily initialized configuration default instance. /// - private static readonly Lazy Lazy = new Lazy(CreateDefaultInstance); - - /// - /// The list of supported keyed to mime types. - /// - private readonly ConcurrentDictionary mimeTypeEncoders = new ConcurrentDictionary(); - - /// - /// The list of supported keyed to mime types. - /// - private readonly ConcurrentDictionary mimeTypeDecoders = new ConcurrentDictionary(); - - /// - /// The list of supported s. - /// - private readonly ConcurrentBag imageFormats = new ConcurrentBag(); - - /// - /// The list of supported s. - /// - private ConcurrentBag imageFormatDetectors = new ConcurrentBag(); - + private static readonly Lazy Lazy = new Lazy(CreateDefaultInstance); + /// /// Initializes a new instance of the class. /// @@ -81,7 +61,12 @@ namespace SixLabors.ImageSharp /// /// Gets the currently registered s. /// - public IEnumerable ImageFormats => this.imageFormats; + public IEnumerable ImageFormats => this.FormatsManager.ImageFormats; + + /// + /// Gets or sets the that is currently in use. + /// + public ImageFormatsManager FormatsManager { get; set; } = new ImageFormatsManager(); /// /// Gets or sets the that is currently in use. @@ -91,22 +76,22 @@ namespace SixLabors.ImageSharp /// /// Gets the maximum header size of all the formats. /// - internal int MaxHeaderSize { get; private set; } - + internal int MaxHeaderSize => this.FormatsManager.MaxHeaderSize; + /// /// Gets the currently registered s. /// - internal IEnumerable FormatDetectors => this.imageFormatDetectors; + internal IEnumerable FormatDetectors => this.FormatsManager.FormatDetectors; /// /// Gets the currently registered s. /// - internal IEnumerable> ImageDecoders => this.mimeTypeDecoders; + internal IEnumerable> ImageDecoders => this.FormatsManager.ImageDecoders; /// /// Gets the currently registered s. /// - internal IEnumerable> ImageEncoders => this.mimeTypeEncoders; + internal IEnumerable> ImageEncoders => this.FormatsManager.ImageEncoders; #if !NETSTANDARD1_1 /// @@ -135,11 +120,8 @@ namespace SixLabors.ImageSharp /// /// The format to register as a known format. public void AddImageFormat(IImageFormat format) - { - Guard.NotNull(format, nameof(format)); - Guard.NotNull(format.MimeTypes, nameof(format.MimeTypes)); - Guard.NotNull(format.FileExtensions, nameof(format.FileExtensions)); - this.imageFormats.Add(format); + { + this.FormatsManager.AddImageFormat(format); } /// @@ -149,7 +131,7 @@ namespace SixLabors.ImageSharp /// The if found otherwise null public IImageFormat FindFormatByFileExtension(string extension) { - return this.imageFormats.FirstOrDefault(x => x.FileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)); + return this.FormatsManager.FindFormatByFileExtension(extension); } /// @@ -159,7 +141,7 @@ namespace SixLabors.ImageSharp /// The if found; otherwise null public IImageFormat FindFormatByMimeType(string mimeType) { - return this.imageFormats.FirstOrDefault(x => x.MimeTypes.Contains(mimeType, StringComparer.OrdinalIgnoreCase)); + return this.FormatsManager.FindFormatByMimeType(mimeType); } /// @@ -169,10 +151,7 @@ namespace SixLabors.ImageSharp /// The encoder to use, public void SetEncoder(IImageFormat imageFormat, IImageEncoder encoder) { - Guard.NotNull(imageFormat, nameof(imageFormat)); - Guard.NotNull(encoder, nameof(encoder)); - this.AddImageFormat(imageFormat); - this.mimeTypeEncoders.AddOrUpdate(imageFormat, encoder, (s, e) => encoder); + this.FormatsManager.SetEncoder(imageFormat, encoder); } /// @@ -182,10 +161,7 @@ namespace SixLabors.ImageSharp /// The decoder to use, public void SetDecoder(IImageFormat imageFormat, IImageDecoder decoder) { - Guard.NotNull(imageFormat, nameof(imageFormat)); - Guard.NotNull(decoder, nameof(decoder)); - this.AddImageFormat(imageFormat); - this.mimeTypeDecoders.AddOrUpdate(imageFormat, decoder, (s, e) => decoder); + this.FormatsManager.SetDecoder(imageFormat, decoder); } /// @@ -193,7 +169,7 @@ namespace SixLabors.ImageSharp /// public void ClearImageFormatDetectors() { - this.imageFormatDetectors = new ConcurrentBag(); + this.FormatsManager.ClearImageFormatDetectors(); } /// @@ -202,9 +178,7 @@ namespace SixLabors.ImageSharp /// The detector to add public void AddImageFormatDetector(IImageFormatDetector detector) { - Guard.NotNull(detector, nameof(detector)); - this.imageFormatDetectors.Add(detector); - this.SetMaxHeaderSize(); + this.FormatsManager.AddImageFormatDetector(detector); } /// @@ -214,13 +188,7 @@ namespace SixLabors.ImageSharp /// The if found otherwise null public IImageDecoder FindDecoder(IImageFormat format) { - Guard.NotNull(format, nameof(format)); - if (this.mimeTypeDecoders.TryGetValue(format, out IImageDecoder decoder)) - { - return decoder; - } - - return null; + return this.FormatsManager.FindDecoder(format); } /// @@ -230,13 +198,7 @@ namespace SixLabors.ImageSharp /// The if found otherwise null public IImageEncoder FindEncoder(IImageFormat format) { - Guard.NotNull(format, nameof(format)); - if (this.mimeTypeEncoders.TryGetValue(format, out IImageEncoder encoder)) - { - return encoder; - } - - return null; + return this.FormatsManager.FindEncoder(format); } /// @@ -254,14 +216,6 @@ namespace SixLabors.ImageSharp new JpegConfigurationModule(), new GifConfigurationModule(), new BmpConfigurationModule()); - } - - /// - /// Sets the max header size. - /// - private void SetMaxHeaderSize() - { - this.MaxHeaderSize = this.imageFormatDetectors.Max(x => x.HeaderSize); - } + } } } diff --git a/src/ImageSharp/Formats/ImageFormatsManager.cs b/src/ImageSharp/Formats/ImageFormatsManager.cs new file mode 100644 index 0000000000..b9307e4a05 --- /dev/null +++ b/src/ImageSharp/Formats/ImageFormatsManager.cs @@ -0,0 +1,185 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace SixLabors.ImageSharp.Formats +{ + /// + /// Collection of Image Formats to be used in class. + /// + public class ImageFormatsManager + { + /// + /// Initializes a new instance of the class. + /// + public ImageFormatsManager() + { + } + + /// + /// The list of supported keyed to mime types. + /// + private readonly ConcurrentDictionary mimeTypeEncoders = new ConcurrentDictionary(); + + /// + /// The list of supported keyed to mime types. + /// + private readonly ConcurrentDictionary mimeTypeDecoders = new ConcurrentDictionary(); + + /// + /// The list of supported s. + /// + private readonly ConcurrentBag imageFormats = new ConcurrentBag(); + + /// + /// The list of supported s. + /// + private ConcurrentBag imageFormatDetectors = new ConcurrentBag(); + + /// + /// Gets the maximum header size of all the formats. + /// + internal int MaxHeaderSize { get; private set; } + + /// + /// Gets the currently registered s. + /// + public IEnumerable ImageFormats => this.imageFormats; + + /// + /// Gets the currently registered s. + /// + internal IEnumerable FormatDetectors => this.imageFormatDetectors; + + /// + /// Gets the currently registered s. + /// + internal IEnumerable> ImageDecoders => this.mimeTypeDecoders; + + /// + /// Gets the currently registered s. + /// + internal IEnumerable> ImageEncoders => this.mimeTypeEncoders; + + /// + /// Registers a new format provider. + /// + /// The format to register as a known format. + public void AddImageFormat(IImageFormat format) + { + Guard.NotNull(format, nameof(format)); + Guard.NotNull(format.MimeTypes, nameof(format.MimeTypes)); + Guard.NotNull(format.FileExtensions, nameof(format.FileExtensions)); + this.imageFormats.Add(format); + } + + /// + /// For the specified file extensions type find the e . + /// + /// The extension to discover + /// The if found otherwise null + public IImageFormat FindFormatByFileExtension(string extension) + { + return this.imageFormats.FirstOrDefault(x => x.FileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)); + } + + /// + /// For the specified mime type find the . + /// + /// The mime-type to discover + /// The if found; otherwise null + public IImageFormat FindFormatByMimeType(string mimeType) + { + return this.imageFormats.FirstOrDefault(x => x.MimeTypes.Contains(mimeType, StringComparer.OrdinalIgnoreCase)); + } + + /// + /// Sets a specific image encoder as the encoder for a specific image format. + /// + /// The image format to register the encoder for. + /// The encoder to use, + public void SetEncoder(IImageFormat imageFormat, IImageEncoder encoder) + { + Guard.NotNull(imageFormat, nameof(imageFormat)); + Guard.NotNull(encoder, nameof(encoder)); + this.AddImageFormat(imageFormat); + this.mimeTypeEncoders.AddOrUpdate(imageFormat, encoder, (s, e) => encoder); + } + + /// + /// Sets a specific image decoder as the decoder for a specific image format. + /// + /// The image format to register the encoder for. + /// The decoder to use, + public void SetDecoder(IImageFormat imageFormat, IImageDecoder decoder) + { + Guard.NotNull(imageFormat, nameof(imageFormat)); + Guard.NotNull(decoder, nameof(decoder)); + this.AddImageFormat(imageFormat); + this.mimeTypeDecoders.AddOrUpdate(imageFormat, decoder, (s, e) => decoder); + } + + /// + /// Removes all the registered image format detectors. + /// + public void ClearImageFormatDetectors() + { + this.imageFormatDetectors = new ConcurrentBag(); + } + + /// + /// Adds a new detector for detecting mime types. + /// + /// The detector to add + public void AddImageFormatDetector(IImageFormatDetector detector) + { + Guard.NotNull(detector, nameof(detector)); + this.imageFormatDetectors.Add(detector); + this.SetMaxHeaderSize(); + } + + /// + /// For the specified mime type find the decoder. + /// + /// The format to discover + /// The if found otherwise null + public IImageDecoder FindDecoder(IImageFormat format) + { + Guard.NotNull(format, nameof(format)); + if (this.mimeTypeDecoders.TryGetValue(format, out IImageDecoder decoder)) + { + return decoder; + } + + return null; + } + + /// + /// For the specified mime type find the encoder. + /// + /// The format to discover + /// The if found otherwise null + public IImageEncoder FindEncoder(IImageFormat format) + { + Guard.NotNull(format, nameof(format)); + if (this.mimeTypeEncoders.TryGetValue(format, out IImageEncoder encoder)) + { + return encoder; + } + + return null; + } + + /// + /// Sets the max header size. + /// + private void SetMaxHeaderSize() + { + this.MaxHeaderSize = this.imageFormatDetectors.Max(x => x.HeaderSize); + } + + + } +} From 68896c7c3e4e10a64ee66925dba78f7f51b0a176 Mon Sep 17 00:00:00 2001 From: Vicente Penades Date: Wed, 28 Feb 2018 10:36:43 +0100 Subject: [PATCH 124/151] stylecop whitespaces --- src/ImageSharp/Configuration.cs | 426 +++++++++--------- src/ImageSharp/Formats/ImageFormatsManager.cs | 316 +++++++------ 2 files changed, 370 insertions(+), 372 deletions(-) diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index d53d9a9a59..65633f2c8a 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -1,221 +1,221 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using SixLabors.ImageSharp.Formats; -using SixLabors.ImageSharp.Formats.Bmp; -using SixLabors.ImageSharp.Formats.Gif; -using SixLabors.ImageSharp.Formats.Jpeg; -using SixLabors.ImageSharp.Formats.Png; -using SixLabors.ImageSharp.IO; -using SixLabors.ImageSharp.Memory; - -namespace SixLabors.ImageSharp -{ - /// - /// Provides initialization code which allows extending the library. - /// - public sealed class Configuration - { - /// - /// A lazily initialized configuration default instance. - /// +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.Formats.Jpeg; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.IO; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp +{ + /// + /// Provides initialization code which allows extending the library. + /// + public sealed class Configuration + { + /// + /// A lazily initialized configuration default instance. + /// private static readonly Lazy Lazy = new Lazy(CreateDefaultInstance); - /// - /// Initializes a new instance of the class. - /// - public Configuration() - { - } - - /// - /// Initializes a new instance of the class. - /// - /// A collection of configuration modules to register - public Configuration(params IConfigurationModule[] configurationModules) - { - if (configurationModules != null) - { - foreach (IConfigurationModule p in configurationModules) - { - p.Configure(this); - } - } - } - - /// - /// Gets the default instance. - /// - public static Configuration Default { get; } = Lazy.Value; - - /// - /// Gets the global parallel options for processing tasks in parallel. - /// - public ParallelOptions ParallelOptions { get; } = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount }; - - /// - /// Gets the currently registered s. - /// + /// + /// Initializes a new instance of the class. + /// + public Configuration() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// A collection of configuration modules to register + public Configuration(params IConfigurationModule[] configurationModules) + { + if (configurationModules != null) + { + foreach (IConfigurationModule p in configurationModules) + { + p.Configure(this); + } + } + } + + /// + /// Gets the default instance. + /// + public static Configuration Default { get; } = Lazy.Value; + + /// + /// Gets the global parallel options for processing tasks in parallel. + /// + public ParallelOptions ParallelOptions { get; } = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount }; + + /// + /// Gets the currently registered s. + /// public IEnumerable ImageFormats => this.FormatsManager.ImageFormats; - /// - /// Gets or sets the that is currently in use. - /// - public ImageFormatsManager FormatsManager { get; set; } = new ImageFormatsManager(); - - /// - /// Gets or sets the that is currently in use. - /// - public MemoryManager MemoryManager { get; set; } = ArrayPoolMemoryManager.CreateDefault(); - - /// - /// Gets the maximum header size of all the formats. - /// + /// + /// Gets or sets the that is currently in use. + /// + public ImageFormatsManager FormatsManager { get; set; } = new ImageFormatsManager(); + + /// + /// Gets or sets the that is currently in use. + /// + public MemoryManager MemoryManager { get; set; } = ArrayPoolMemoryManager.CreateDefault(); + + /// + /// Gets the maximum header size of all the formats. + /// internal int MaxHeaderSize => this.FormatsManager.MaxHeaderSize; - /// - /// Gets the currently registered s. - /// - internal IEnumerable FormatDetectors => this.FormatsManager.FormatDetectors; - - /// - /// Gets the currently registered s. - /// - internal IEnumerable> ImageDecoders => this.FormatsManager.ImageDecoders; - - /// - /// Gets the currently registered s. - /// - internal IEnumerable> ImageEncoders => this.FormatsManager.ImageEncoders; - -#if !NETSTANDARD1_1 - /// - /// Gets or sets the filesystem helper for accessing the local file system. - /// - internal IFileSystem FileSystem { get; set; } = new LocalFileSystem(); -#endif - - /// - /// Gets or sets the image operations provider factory. - /// - internal IImageProcessingContextFactory ImageOperationsProvider { get; set; } = new DefaultImageOperationsProviderFactory(); - - /// - /// Registers a new format provider. - /// - /// The configuration provider to call configure on. - public void Configure(IConfigurationModule configuration) - { - Guard.NotNull(configuration, nameof(configuration)); - configuration.Configure(this); - } - - /// - /// Registers a new format provider. - /// - /// The format to register as a known format. - public void AddImageFormat(IImageFormat format) + /// + /// Gets the currently registered s. + /// + internal IEnumerable FormatDetectors => this.FormatsManager.FormatDetectors; + + /// + /// Gets the currently registered s. + /// + internal IEnumerable> ImageDecoders => this.FormatsManager.ImageDecoders; + + /// + /// Gets the currently registered s. + /// + internal IEnumerable> ImageEncoders => this.FormatsManager.ImageEncoders; + +#if !NETSTANDARD1_1 + /// + /// Gets or sets the filesystem helper for accessing the local file system. + /// + internal IFileSystem FileSystem { get; set; } = new LocalFileSystem(); +#endif + + /// + /// Gets or sets the image operations provider factory. + /// + internal IImageProcessingContextFactory ImageOperationsProvider { get; set; } = new DefaultImageOperationsProviderFactory(); + + /// + /// Registers a new format provider. + /// + /// The configuration provider to call configure on. + public void Configure(IConfigurationModule configuration) + { + Guard.NotNull(configuration, nameof(configuration)); + configuration.Configure(this); + } + + /// + /// Registers a new format provider. + /// + /// The format to register as a known format. + public void AddImageFormat(IImageFormat format) + { + this.FormatsManager.AddImageFormat(format); + } + + /// + /// For the specified file extensions type find the e . + /// + /// The extension to discover + /// The if found otherwise null + public IImageFormat FindFormatByFileExtension(string extension) + { + return this.FormatsManager.FindFormatByFileExtension(extension); + } + + /// + /// For the specified mime type find the . + /// + /// The mime-type to discover + /// The if found; otherwise null + public IImageFormat FindFormatByMimeType(string mimeType) + { + return this.FormatsManager.FindFormatByMimeType(mimeType); + } + + /// + /// Sets a specific image encoder as the encoder for a specific image format. + /// + /// The image format to register the encoder for. + /// The encoder to use, + public void SetEncoder(IImageFormat imageFormat, IImageEncoder encoder) + { + this.FormatsManager.SetEncoder(imageFormat, encoder); + } + + /// + /// Sets a specific image decoder as the decoder for a specific image format. + /// + /// The image format to register the encoder for. + /// The decoder to use, + public void SetDecoder(IImageFormat imageFormat, IImageDecoder decoder) + { + this.FormatsManager.SetDecoder(imageFormat, decoder); + } + + /// + /// Removes all the registered image format detectors. + /// + public void ClearImageFormatDetectors() + { + this.FormatsManager.ClearImageFormatDetectors(); + } + + /// + /// Adds a new detector for detecting mime types. + /// + /// The detector to add + public void AddImageFormatDetector(IImageFormatDetector detector) + { + this.FormatsManager.AddImageFormatDetector(detector); + } + + /// + /// For the specified mime type find the decoder. + /// + /// The format to discover + /// The if found otherwise null + public IImageDecoder FindDecoder(IImageFormat format) + { + return this.FormatsManager.FindDecoder(format); + } + + /// + /// For the specified mime type find the encoder. + /// + /// The format to discover + /// The if found otherwise null + public IImageEncoder FindEncoder(IImageFormat format) + { + return this.FormatsManager.FindEncoder(format); + } + + /// + /// Creates the default instance with the following s preregistered: + /// + /// + /// + /// + /// + /// The default configuration of + internal static Configuration CreateDefaultInstance() { - this.FormatsManager.AddImageFormat(format); - } - - /// - /// For the specified file extensions type find the e . - /// - /// The extension to discover - /// The if found otherwise null - public IImageFormat FindFormatByFileExtension(string extension) - { - return this.FormatsManager.FindFormatByFileExtension(extension); - } - - /// - /// For the specified mime type find the . - /// - /// The mime-type to discover - /// The if found; otherwise null - public IImageFormat FindFormatByMimeType(string mimeType) - { - return this.FormatsManager.FindFormatByMimeType(mimeType); - } - - /// - /// Sets a specific image encoder as the encoder for a specific image format. - /// - /// The image format to register the encoder for. - /// The encoder to use, - public void SetEncoder(IImageFormat imageFormat, IImageEncoder encoder) - { - this.FormatsManager.SetEncoder(imageFormat, encoder); - } - - /// - /// Sets a specific image decoder as the decoder for a specific image format. - /// - /// The image format to register the encoder for. - /// The decoder to use, - public void SetDecoder(IImageFormat imageFormat, IImageDecoder decoder) - { - this.FormatsManager.SetDecoder(imageFormat, decoder); - } - - /// - /// Removes all the registered image format detectors. - /// - public void ClearImageFormatDetectors() - { - this.FormatsManager.ClearImageFormatDetectors(); - } - - /// - /// Adds a new detector for detecting mime types. - /// - /// The detector to add - public void AddImageFormatDetector(IImageFormatDetector detector) - { - this.FormatsManager.AddImageFormatDetector(detector); - } - - /// - /// For the specified mime type find the decoder. - /// - /// The format to discover - /// The if found otherwise null - public IImageDecoder FindDecoder(IImageFormat format) - { - return this.FormatsManager.FindDecoder(format); - } - - /// - /// For the specified mime type find the encoder. - /// - /// The format to discover - /// The if found otherwise null - public IImageEncoder FindEncoder(IImageFormat format) - { - return this.FormatsManager.FindEncoder(format); - } - - /// - /// Creates the default instance with the following s preregistered: - /// - /// - /// - /// - /// - /// The default configuration of - internal static Configuration CreateDefaultInstance() - { - return new Configuration( - new PngConfigurationModule(), - new JpegConfigurationModule(), - new GifConfigurationModule(), - new BmpConfigurationModule()); + return new Configuration( + new PngConfigurationModule(), + new JpegConfigurationModule(), + new GifConfigurationModule(), + new BmpConfigurationModule()); } - } -} + } +} diff --git a/src/ImageSharp/Formats/ImageFormatsManager.cs b/src/ImageSharp/Formats/ImageFormatsManager.cs index b9307e4a05..3a78965612 100644 --- a/src/ImageSharp/Formats/ImageFormatsManager.cs +++ b/src/ImageSharp/Formats/ImageFormatsManager.cs @@ -6,180 +6,178 @@ using System.Text; namespace SixLabors.ImageSharp.Formats { - /// - /// Collection of Image Formats to be used in class. + /// + /// Collection of Image Formats to be used in class. /// public class ImageFormatsManager { - /// - /// Initializes a new instance of the class. - /// - public ImageFormatsManager() - { - } + /// + /// The list of supported keyed to mime types. + /// + private readonly ConcurrentDictionary mimeTypeEncoders = new ConcurrentDictionary(); + + /// + /// The list of supported keyed to mime types. + /// + private readonly ConcurrentDictionary mimeTypeDecoders = new ConcurrentDictionary(); + + /// + /// The list of supported s. + /// + private readonly ConcurrentBag imageFormats = new ConcurrentBag(); - /// - /// The list of supported keyed to mime types. - /// - private readonly ConcurrentDictionary mimeTypeEncoders = new ConcurrentDictionary(); - - /// - /// The list of supported keyed to mime types. - /// - private readonly ConcurrentDictionary mimeTypeDecoders = new ConcurrentDictionary(); - - /// - /// The list of supported s. - /// - private readonly ConcurrentBag imageFormats = new ConcurrentBag(); - - /// - /// The list of supported s. - /// + /// + /// The list of supported s. + /// private ConcurrentBag imageFormatDetectors = new ConcurrentBag(); - /// - /// Gets the maximum header size of all the formats. - /// + /// + /// Initializes a new instance of the class. + /// + public ImageFormatsManager() + { + } + + /// + /// Gets the maximum header size of all the formats. + /// internal int MaxHeaderSize { get; private set; } - /// - /// Gets the currently registered s. - /// + /// + /// Gets the currently registered s. + /// public IEnumerable ImageFormats => this.imageFormats; - /// - /// Gets the currently registered s. - /// - internal IEnumerable FormatDetectors => this.imageFormatDetectors; - - /// - /// Gets the currently registered s. - /// - internal IEnumerable> ImageDecoders => this.mimeTypeDecoders; - - /// - /// Gets the currently registered s. - /// + /// + /// Gets the currently registered s. + /// + internal IEnumerable FormatDetectors => this.imageFormatDetectors; + + /// + /// Gets the currently registered s. + /// + internal IEnumerable> ImageDecoders => this.mimeTypeDecoders; + + /// + /// Gets the currently registered s. + /// internal IEnumerable> ImageEncoders => this.mimeTypeEncoders; - /// - /// Registers a new format provider. - /// - /// The format to register as a known format. - public void AddImageFormat(IImageFormat format) - { - Guard.NotNull(format, nameof(format)); - Guard.NotNull(format.MimeTypes, nameof(format.MimeTypes)); - Guard.NotNull(format.FileExtensions, nameof(format.FileExtensions)); - this.imageFormats.Add(format); - } - - /// - /// For the specified file extensions type find the e . - /// - /// The extension to discover - /// The if found otherwise null - public IImageFormat FindFormatByFileExtension(string extension) - { - return this.imageFormats.FirstOrDefault(x => x.FileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)); - } - - /// - /// For the specified mime type find the . - /// - /// The mime-type to discover - /// The if found; otherwise null - public IImageFormat FindFormatByMimeType(string mimeType) - { - return this.imageFormats.FirstOrDefault(x => x.MimeTypes.Contains(mimeType, StringComparer.OrdinalIgnoreCase)); - } - - /// - /// Sets a specific image encoder as the encoder for a specific image format. - /// - /// The image format to register the encoder for. - /// The encoder to use, - public void SetEncoder(IImageFormat imageFormat, IImageEncoder encoder) - { - Guard.NotNull(imageFormat, nameof(imageFormat)); - Guard.NotNull(encoder, nameof(encoder)); - this.AddImageFormat(imageFormat); - this.mimeTypeEncoders.AddOrUpdate(imageFormat, encoder, (s, e) => encoder); - } - - /// - /// Sets a specific image decoder as the decoder for a specific image format. - /// - /// The image format to register the encoder for. - /// The decoder to use, - public void SetDecoder(IImageFormat imageFormat, IImageDecoder decoder) - { - Guard.NotNull(imageFormat, nameof(imageFormat)); - Guard.NotNull(decoder, nameof(decoder)); - this.AddImageFormat(imageFormat); - this.mimeTypeDecoders.AddOrUpdate(imageFormat, decoder, (s, e) => decoder); - } - - /// - /// Removes all the registered image format detectors. - /// - public void ClearImageFormatDetectors() - { - this.imageFormatDetectors = new ConcurrentBag(); - } - - /// - /// Adds a new detector for detecting mime types. - /// - /// The detector to add - public void AddImageFormatDetector(IImageFormatDetector detector) - { - Guard.NotNull(detector, nameof(detector)); - this.imageFormatDetectors.Add(detector); - this.SetMaxHeaderSize(); - } - - /// - /// For the specified mime type find the decoder. - /// - /// The format to discover - /// The if found otherwise null - public IImageDecoder FindDecoder(IImageFormat format) - { - Guard.NotNull(format, nameof(format)); - if (this.mimeTypeDecoders.TryGetValue(format, out IImageDecoder decoder)) - { - return decoder; - } - - return null; - } - - /// - /// For the specified mime type find the encoder. - /// - /// The format to discover - /// The if found otherwise null - public IImageEncoder FindEncoder(IImageFormat format) - { - Guard.NotNull(format, nameof(format)); - if (this.mimeTypeEncoders.TryGetValue(format, out IImageEncoder encoder)) - { - return encoder; - } - - return null; + /// + /// Registers a new format provider. + /// + /// The format to register as a known format. + public void AddImageFormat(IImageFormat format) + { + Guard.NotNull(format, nameof(format)); + Guard.NotNull(format.MimeTypes, nameof(format.MimeTypes)); + Guard.NotNull(format.FileExtensions, nameof(format.FileExtensions)); + this.imageFormats.Add(format); + } + + /// + /// For the specified file extensions type find the e . + /// + /// The extension to discover + /// The if found otherwise null + public IImageFormat FindFormatByFileExtension(string extension) + { + return this.imageFormats.FirstOrDefault(x => x.FileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)); + } + + /// + /// For the specified mime type find the . + /// + /// The mime-type to discover + /// The if found; otherwise null + public IImageFormat FindFormatByMimeType(string mimeType) + { + return this.imageFormats.FirstOrDefault(x => x.MimeTypes.Contains(mimeType, StringComparer.OrdinalIgnoreCase)); + } + + /// + /// Sets a specific image encoder as the encoder for a specific image format. + /// + /// The image format to register the encoder for. + /// The encoder to use, + public void SetEncoder(IImageFormat imageFormat, IImageEncoder encoder) + { + Guard.NotNull(imageFormat, nameof(imageFormat)); + Guard.NotNull(encoder, nameof(encoder)); + this.AddImageFormat(imageFormat); + this.mimeTypeEncoders.AddOrUpdate(imageFormat, encoder, (s, e) => encoder); + } + + /// + /// Sets a specific image decoder as the decoder for a specific image format. + /// + /// The image format to register the encoder for. + /// The decoder to use, + public void SetDecoder(IImageFormat imageFormat, IImageDecoder decoder) + { + Guard.NotNull(imageFormat, nameof(imageFormat)); + Guard.NotNull(decoder, nameof(decoder)); + this.AddImageFormat(imageFormat); + this.mimeTypeDecoders.AddOrUpdate(imageFormat, decoder, (s, e) => decoder); + } + + /// + /// Removes all the registered image format detectors. + /// + public void ClearImageFormatDetectors() + { + this.imageFormatDetectors = new ConcurrentBag(); + } + + /// + /// Adds a new detector for detecting mime types. + /// + /// The detector to add + public void AddImageFormatDetector(IImageFormatDetector detector) + { + Guard.NotNull(detector, nameof(detector)); + this.imageFormatDetectors.Add(detector); + this.SetMaxHeaderSize(); + } + + /// + /// For the specified mime type find the decoder. + /// + /// The format to discover + /// The if found otherwise null + public IImageDecoder FindDecoder(IImageFormat format) + { + Guard.NotNull(format, nameof(format)); + if (this.mimeTypeDecoders.TryGetValue(format, out IImageDecoder decoder)) + { + return decoder; + } + + return null; } - /// - /// Sets the max header size. - /// - private void SetMaxHeaderSize() - { - this.MaxHeaderSize = this.imageFormatDetectors.Max(x => x.HeaderSize); + /// + /// For the specified mime type find the encoder. + /// + /// The format to discover + /// The if found otherwise null + public IImageEncoder FindEncoder(IImageFormat format) + { + Guard.NotNull(format, nameof(format)); + if (this.mimeTypeEncoders.TryGetValue(format, out IImageEncoder encoder)) + { + return encoder; + } + + return null; } - + /// + /// Sets the max header size. + /// + private void SetMaxHeaderSize() + { + this.MaxHeaderSize = this.imageFormatDetectors.Max(x => x.HeaderSize); + } } } From 4fc02be38f4f9756ded61685ce05fd5fd86cb8ba Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 28 Feb 2018 21:08:13 +1100 Subject: [PATCH 125/151] Better EOF handling. --- .../Components/Decoder/DecoderThrowHelper.cs | 8 ++++++- .../Jpeg/GolangPort/OrigJpegDecoderCore.cs | 24 ++++++++++++------- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/DecoderThrowHelper.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/DecoderThrowHelper.cs index d5a9340d72..c1fde41ed0 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/DecoderThrowHelper.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/DecoderThrowHelper.cs @@ -24,8 +24,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder throw new ArgumentException("ThrowExceptionForErrorCode() called with NoError!", nameof(errorCode)); case OrigDecoderErrorCode.MissingFF00: throw new MissingFF00Exception(); + case OrigDecoderErrorCode.UnexpectedEndOfStream: - throw new EOFException(); + + // TODO: + // Disabled for now since we want to avoid throwing for most bad eof. + // Will probably delete + // throw new EOFException(); + break; default: throw new ArgumentOutOfRangeException(nameof(errorCode), errorCode, null); } diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs index aa89abf2e6..6d3c8f5d19 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs @@ -235,6 +235,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort // Check for the Start Of Image marker. this.InputProcessor.ReadFull(this.Temp, 0, 2); + if (this.Temp[0] != OrigJpegConstants.Markers.XFF || this.Temp[1] != OrigJpegConstants.Markers.SOI) { throw new ImageFormatException("Missing SOI marker."); @@ -247,6 +248,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort while (processBytes) { this.InputProcessor.ReadFull(this.Temp, 0, 2); + + if (this.InputProcessor.ReachedEOF) + { + // We've reached the end of the stream. + processBytes = false; + } + while (this.Temp[0] != 0xff) { // Strictly speaking, this is a format error. However, libjpeg is @@ -282,6 +290,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort // Section B.1.1.2 says, "Any marker may optionally be preceded by any // number of fill bytes, which are bytes assigned code X'FF'". marker = this.InputProcessor.ReadByte(); + + if (this.InputProcessor.ReachedEOF) + { + // We've reached the end of the stream. + processBytes = false; + break; + } } // End Of Image. @@ -388,15 +403,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort { this.InputProcessor.Skip(remaining); } - else if (marker < OrigJpegConstants.Markers.SOF0) - { - // See Table B.1 "Marker code assignments". - throw new ImageFormatException("Unknown marker"); - } - else - { - throw new ImageFormatException("Unknown marker"); - } break; } From 02dee3cad964ef5db5e6583f92c11b5e3b7d0ec8 Mon Sep 17 00:00:00 2001 From: Vicente Penades Date: Wed, 28 Feb 2018 11:49:27 +0100 Subject: [PATCH 126/151] Coverage test --- src/ImageSharp/Formats/ImageFormatsManager.cs | 5 +- .../Formats/ImageFormatsManagerTests.cs | 117 ++++++++++++++++++ 2 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 tests/ImageSharp.Tests/Formats/ImageFormatsManagerTests.cs diff --git a/src/ImageSharp/Formats/ImageFormatsManager.cs b/src/ImageSharp/Formats/ImageFormatsManager.cs index 3a78965612..350c28b0a3 100644 --- a/src/ImageSharp/Formats/ImageFormatsManager.cs +++ b/src/ImageSharp/Formats/ImageFormatsManager.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; diff --git a/tests/ImageSharp.Tests/Formats/ImageFormatsManagerTests.cs b/tests/ImageSharp.Tests/Formats/ImageFormatsManagerTests.cs new file mode 100644 index 0000000000..3aeeff9379 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/ImageFormatsManagerTests.cs @@ -0,0 +1,117 @@ +// 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.Linq; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.IO; +using SixLabors.ImageSharp.PixelFormats; +using Moq; +using Xunit; + +namespace SixLabors.ImageSharp.Tests +{ + public class ImageFormatsManagerTests + { + public ImageFormatsManager FormatsManagerEmpty { get; private set; } + public ImageFormatsManager DefaultFormatsManager { get; private set; } + + public ImageFormatsManagerTests() + { + this.DefaultFormatsManager = Configuration.Default.FormatsManager; + this.FormatsManagerEmpty = new ImageFormatsManager(); + } + + [Fact] + public void IfAutoloadWellknownFormatsIsTrueAllFormatsAreLoaded() + { + Assert.Equal(4, this.DefaultFormatsManager.ImageEncoders.Count()); + Assert.Equal(4, this.DefaultFormatsManager.ImageDecoders.Count()); + } + + [Fact] + public void AddImageFormatDetectorNullthrows() + { + Assert.Throws(() => + { + this.DefaultFormatsManager.AddImageFormatDetector(null); + }); + } + + [Fact] + public void RegisterNullMimeTypeEncoder() + { + Assert.Throws(() => + { + this.DefaultFormatsManager.SetEncoder(null, new Mock().Object); + }); + Assert.Throws(() => + { + this.DefaultFormatsManager.SetEncoder(ImageFormats.Bmp, null); + }); + Assert.Throws(() => + { + this.DefaultFormatsManager.SetEncoder(null, null); + }); + } + + [Fact] + public void RegisterNullSetDecoder() + { + Assert.Throws(() => + { + this.DefaultFormatsManager.SetDecoder(null, new Mock().Object); + }); + Assert.Throws(() => + { + this.DefaultFormatsManager.SetDecoder(ImageFormats.Bmp, null); + }); + Assert.Throws(() => + { + this.DefaultFormatsManager.SetDecoder(null, null); + }); + } + + [Fact] + public void RegisterMimeTypeEncoderReplacesLast() + { + IImageEncoder encoder1 = new Mock().Object; + this.FormatsManagerEmpty.SetEncoder(TestFormat.GlobalTestFormat, encoder1); + IImageEncoder found = this.FormatsManagerEmpty.FindEncoder(TestFormat.GlobalTestFormat); + Assert.Equal(encoder1, found); + + IImageEncoder encoder2 = new Mock().Object; + this.FormatsManagerEmpty.SetEncoder(TestFormat.GlobalTestFormat, encoder2); + IImageEncoder found2 = this.FormatsManagerEmpty.FindEncoder(TestFormat.GlobalTestFormat); + Assert.Equal(encoder2, found2); + Assert.NotEqual(found, found2); + } + + [Fact] + public void RegisterMimeTypeDecoderReplacesLast() + { + IImageDecoder decoder1 = new Mock().Object; + this.FormatsManagerEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder1); + IImageDecoder found = this.FormatsManagerEmpty.FindDecoder(TestFormat.GlobalTestFormat); + Assert.Equal(decoder1, found); + + IImageDecoder decoder2 = new Mock().Object; + this.FormatsManagerEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder2); + IImageDecoder found2 = this.FormatsManagerEmpty.FindDecoder(TestFormat.GlobalTestFormat); + Assert.Equal(decoder2, found2); + Assert.NotEqual(found, found2); + } + + [Fact] + public void AddFormatCallsConfig() + { + var provider = new Mock(); + var config = new Configuration(); + config.Configure(provider.Object); + + provider.Verify(x => x.Configure(config)); + } + } +} From 08fbee99faea9d49d18ae3d046081dda89c086d9 Mon Sep 17 00:00:00 2001 From: Vicente Penades Date: Wed, 28 Feb 2018 12:50:55 +0100 Subject: [PATCH 127/151] renaming ImageFormatsManager to ImageFormatManager --- src/ImageSharp/Configuration.cs | 32 +++++++++---------- ...ormatsManager.cs => ImageFormatManager.cs} | 6 ++-- ...gerTests.cs => ImageFormatManagerTests.cs} | 14 ++++---- 3 files changed, 26 insertions(+), 26 deletions(-) rename src/ImageSharp/Formats/{ImageFormatsManager.cs => ImageFormatManager.cs} (96%) rename tests/ImageSharp.Tests/Formats/{ImageFormatsManagerTests.cs => ImageFormatManagerTests.cs} (89%) diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index 65633f2c8a..0f69194f2d 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -61,12 +61,12 @@ namespace SixLabors.ImageSharp /// /// Gets the currently registered s. /// - public IEnumerable ImageFormats => this.FormatsManager.ImageFormats; + public IEnumerable ImageFormats => this.ImageFormatsManager.ImageFormats; /// - /// Gets or sets the that is currently in use. + /// Gets or sets the that is currently in use. /// - public ImageFormatsManager FormatsManager { get; set; } = new ImageFormatsManager(); + public ImageFormatManager ImageFormatsManager { get; set; } = new ImageFormatManager(); /// /// Gets or sets the that is currently in use. @@ -76,22 +76,22 @@ namespace SixLabors.ImageSharp /// /// Gets the maximum header size of all the formats. /// - internal int MaxHeaderSize => this.FormatsManager.MaxHeaderSize; + internal int MaxHeaderSize => this.ImageFormatsManager.MaxHeaderSize; /// /// Gets the currently registered s. /// - internal IEnumerable FormatDetectors => this.FormatsManager.FormatDetectors; + internal IEnumerable FormatDetectors => this.ImageFormatsManager.FormatDetectors; /// /// Gets the currently registered s. /// - internal IEnumerable> ImageDecoders => this.FormatsManager.ImageDecoders; + internal IEnumerable> ImageDecoders => this.ImageFormatsManager.ImageDecoders; /// /// Gets the currently registered s. /// - internal IEnumerable> ImageEncoders => this.FormatsManager.ImageEncoders; + internal IEnumerable> ImageEncoders => this.ImageFormatsManager.ImageEncoders; #if !NETSTANDARD1_1 /// @@ -121,7 +121,7 @@ namespace SixLabors.ImageSharp /// The format to register as a known format. public void AddImageFormat(IImageFormat format) { - this.FormatsManager.AddImageFormat(format); + this.ImageFormatsManager.AddImageFormat(format); } /// @@ -131,7 +131,7 @@ namespace SixLabors.ImageSharp /// The if found otherwise null public IImageFormat FindFormatByFileExtension(string extension) { - return this.FormatsManager.FindFormatByFileExtension(extension); + return this.ImageFormatsManager.FindFormatByFileExtension(extension); } /// @@ -141,7 +141,7 @@ namespace SixLabors.ImageSharp /// The if found; otherwise null public IImageFormat FindFormatByMimeType(string mimeType) { - return this.FormatsManager.FindFormatByMimeType(mimeType); + return this.ImageFormatsManager.FindFormatByMimeType(mimeType); } /// @@ -151,7 +151,7 @@ namespace SixLabors.ImageSharp /// The encoder to use, public void SetEncoder(IImageFormat imageFormat, IImageEncoder encoder) { - this.FormatsManager.SetEncoder(imageFormat, encoder); + this.ImageFormatsManager.SetEncoder(imageFormat, encoder); } /// @@ -161,7 +161,7 @@ namespace SixLabors.ImageSharp /// The decoder to use, public void SetDecoder(IImageFormat imageFormat, IImageDecoder decoder) { - this.FormatsManager.SetDecoder(imageFormat, decoder); + this.ImageFormatsManager.SetDecoder(imageFormat, decoder); } /// @@ -169,7 +169,7 @@ namespace SixLabors.ImageSharp /// public void ClearImageFormatDetectors() { - this.FormatsManager.ClearImageFormatDetectors(); + this.ImageFormatsManager.ClearImageFormatDetectors(); } /// @@ -178,7 +178,7 @@ namespace SixLabors.ImageSharp /// The detector to add public void AddImageFormatDetector(IImageFormatDetector detector) { - this.FormatsManager.AddImageFormatDetector(detector); + this.ImageFormatsManager.AddImageFormatDetector(detector); } /// @@ -188,7 +188,7 @@ namespace SixLabors.ImageSharp /// The if found otherwise null public IImageDecoder FindDecoder(IImageFormat format) { - return this.FormatsManager.FindDecoder(format); + return this.ImageFormatsManager.FindDecoder(format); } /// @@ -198,7 +198,7 @@ namespace SixLabors.ImageSharp /// The if found otherwise null public IImageEncoder FindEncoder(IImageFormat format) { - return this.FormatsManager.FindEncoder(format); + return this.ImageFormatsManager.FindEncoder(format); } /// diff --git a/src/ImageSharp/Formats/ImageFormatsManager.cs b/src/ImageSharp/Formats/ImageFormatManager.cs similarity index 96% rename from src/ImageSharp/Formats/ImageFormatsManager.cs rename to src/ImageSharp/Formats/ImageFormatManager.cs index 350c28b0a3..67ba111474 100644 --- a/src/ImageSharp/Formats/ImageFormatsManager.cs +++ b/src/ImageSharp/Formats/ImageFormatManager.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Formats /// /// Collection of Image Formats to be used in class. /// - public class ImageFormatsManager + public class ImageFormatManager { /// /// The list of supported keyed to mime types. @@ -35,9 +35,9 @@ namespace SixLabors.ImageSharp.Formats private ConcurrentBag imageFormatDetectors = new ConcurrentBag(); /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public ImageFormatsManager() + public ImageFormatManager() { } diff --git a/tests/ImageSharp.Tests/Formats/ImageFormatsManagerTests.cs b/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs similarity index 89% rename from tests/ImageSharp.Tests/Formats/ImageFormatsManagerTests.cs rename to tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs index 3aeeff9379..bfaf26c6ac 100644 --- a/tests/ImageSharp.Tests/Formats/ImageFormatsManagerTests.cs +++ b/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs @@ -13,19 +13,19 @@ using Xunit; namespace SixLabors.ImageSharp.Tests { - public class ImageFormatsManagerTests + public class ImageFormatManagerTests { - public ImageFormatsManager FormatsManagerEmpty { get; private set; } - public ImageFormatsManager DefaultFormatsManager { get; private set; } + public ImageFormatManager FormatsManagerEmpty { get; private set; } + public ImageFormatManager DefaultFormatsManager { get; private set; } - public ImageFormatsManagerTests() + public ImageFormatManagerTests() { - this.DefaultFormatsManager = Configuration.Default.FormatsManager; - this.FormatsManagerEmpty = new ImageFormatsManager(); + this.DefaultFormatsManager = Configuration.Default.ImageFormatsManager; + this.FormatsManagerEmpty = new ImageFormatManager(); } [Fact] - public void IfAutoloadWellknownFormatsIsTrueAllFormatsAreLoaded() + public void IfAutoloadWellKnownFormatsIsTrueAllFormatsAreLoaded() { Assert.Equal(4, this.DefaultFormatsManager.ImageEncoders.Count()); Assert.Equal(4, this.DefaultFormatsManager.ImageDecoders.Count()); From 1d45fe8d3c7a66098b0769a7c22b9baa17d6dd25 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 28 Feb 2018 23:18:11 +1100 Subject: [PATCH 128/151] Introducing ReadOrigin. Fix #477 --- src/ImageSharp/Image/Image.FromStream.cs | 152 +++++++++++++++++------ src/ImageSharp/Image/ReadOrigin.cs | 21 ++++ 2 files changed, 138 insertions(+), 35 deletions(-) create mode 100644 src/ImageSharp/Image/ReadOrigin.cs diff --git a/src/ImageSharp/Image/Image.FromStream.cs b/src/ImageSharp/Image/Image.FromStream.cs index 62668dd023..30d8014233 100644 --- a/src/ImageSharp/Image/Image.FromStream.cs +++ b/src/ImageSharp/Image/Image.FromStream.cs @@ -20,10 +20,15 @@ namespace SixLabors.ImageSharp /// /// The image stream to read the header from. /// The mime type or null if none found. - public static IImageFormat DetectFormat(Stream stream) - { - return DetectFormat(null, stream); - } + public static IImageFormat DetectFormat(Stream stream) => DetectFormat(null, stream); + + /// + /// By reading the header on the provided stream this calculates the images mime type. + /// + /// The image stream to read the header from. + /// The position in the stream to use for reading. + /// The mime type or null if none found. + public static IImageFormat DetectFormat(Stream stream, ReadOrigin origin) => DetectFormat(null, stream, origin); /// /// By reading the header on the provided stream this calculates the images mime type. @@ -31,10 +36,17 @@ namespace SixLabors.ImageSharp /// The configuration. /// The image stream to read the header from. /// The mime type or null if none found. - public static IImageFormat DetectFormat(Configuration config, Stream stream) - { - return WithSeekableStream(stream, s => InternalDetectFormat(s, config ?? Configuration.Default)); - } + public static IImageFormat DetectFormat(Configuration config, Stream stream) => DetectFormat(config, stream, ReadOrigin.Begin); + + /// + /// By reading the header on the provided stream this calculates the images mime type. + /// + /// The configuration. + /// The image stream to read the header from. + /// The position in the stream to use for reading. + /// The mime type or null if none found. + public static IImageFormat DetectFormat(Configuration config, Stream stream, ReadOrigin origin) + => WithSeekableStream(stream, origin, s => InternalDetectFormat(s, config ?? Configuration.Default)); /// /// By reading the header on the provided stream this reads the raw image information. @@ -46,10 +58,7 @@ namespace SixLabors.ImageSharp /// /// The or null if suitable info detector not found. /// - public static IImageInfo Identify(Stream stream) - { - return Identify(null, stream); - } + public static IImageInfo Identify(Stream stream) => Identify(null, stream); /// /// Reads the raw image information from the specified stream without fully decoding it. @@ -62,10 +71,20 @@ namespace SixLabors.ImageSharp /// /// The or null if suitable info detector is not found. /// - public static IImageInfo Identify(Configuration config, Stream stream) - { - return WithSeekableStream(stream, s => InternalIdentity(s, config ?? Configuration.Default)); - } + public static IImageInfo Identify(Configuration config, Stream stream) => Identify(config, stream, ReadOrigin.Begin); + + /// + /// Reads the raw image information from the specified stream without fully decoding it. + /// + /// The configuration. + /// The image stream to read the information from. + /// The position in the stream to use for reading. + /// Thrown if the stream is not readable. + /// + /// The or null if suitable info detector is not found. + /// + public static IImageInfo Identify(Configuration config, Stream stream, ReadOrigin origin) + => WithSeekableStream(stream, origin, s => InternalIdentity(s, config ?? Configuration.Default)); /// /// Create a new instance of the class from the given stream. @@ -120,7 +139,8 @@ namespace SixLabors.ImageSharp /// Thrown if the stream is not readable nor seekable. /// /// A new .> - public static Image Load(Configuration config, Stream stream, out IImageFormat format) => Load(config, stream, out format); + public static Image Load(Configuration config, Stream stream, out IImageFormat format) + => Load(config, stream, out format); /// /// Create a new instance of the class from the given stream. @@ -133,9 +153,7 @@ namespace SixLabors.ImageSharp /// A new .> public static Image Load(Stream stream) where TPixel : struct, IPixel - { - return Load(null, stream); - } + => Load(null, stream); /// /// Create a new instance of the class from the given stream. @@ -149,9 +167,7 @@ namespace SixLabors.ImageSharp /// A new .> public static Image Load(Stream stream, out IImageFormat format) where TPixel : struct, IPixel - { - return Load(null, stream, out format); - } + => Load(null, stream, out format); /// /// Create a new instance of the class from the given stream. @@ -165,9 +181,22 @@ namespace SixLabors.ImageSharp /// A new .> public static Image Load(Stream stream, IImageDecoder decoder) where TPixel : struct, IPixel - { - return WithSeekableStream(stream, s => decoder.Decode(Configuration.Default, s)); - } + => Load(stream, ReadOrigin.Begin, decoder); + + /// + /// Create a new instance of the class from the given stream. + /// + /// The stream containing image information. + /// The position in the stream to use for reading. + /// The decoder. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// The pixel format. + /// A new .> + public static Image Load(Stream stream, ReadOrigin origin, IImageDecoder decoder) + where TPixel : struct, IPixel + => WithSeekableStream(stream, origin, s => decoder.Decode(Configuration.Default, s)); /// /// Create a new instance of the class from the given stream. @@ -182,9 +211,23 @@ namespace SixLabors.ImageSharp /// A new .> public static Image Load(Configuration config, Stream stream, IImageDecoder decoder) where TPixel : struct, IPixel - { - return WithSeekableStream(stream, s => decoder.Decode(config, s)); - } + => Load(config, stream, ReadOrigin.Begin, decoder); + + /// + /// Create a new instance of the class from the given stream. + /// + /// The Configuration. + /// The stream containing image information. + /// The position in the stream to use for reading. + /// The decoder. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// The pixel format. + /// A new .> + public static Image Load(Configuration config, Stream stream, ReadOrigin origin, IImageDecoder decoder) + where TPixel : struct, IPixel + => WithSeekableStream(stream, origin, s => decoder.Decode(config, s)); /// /// Create a new instance of the class from the given stream. @@ -198,9 +241,22 @@ namespace SixLabors.ImageSharp /// A new .> public static Image Load(Configuration config, Stream stream) where TPixel : struct, IPixel - { - return Load(config, stream, out var _); - } + => Load(config, stream, out IImageFormat _); + + /// + /// Create a new instance of the class from the given stream. + /// + /// The configuration options. + /// The stream containing image information. + /// The position in the stream to use for reading. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// The pixel format. + /// A new .> + public static Image Load(Configuration config, Stream stream, ReadOrigin origin) + where TPixel : struct, IPixel + => Load(config, stream, origin, out IImageFormat _); /// /// Create a new instance of the class from the given stream. @@ -214,10 +270,26 @@ namespace SixLabors.ImageSharp /// The pixel format. /// A new .> public static Image Load(Configuration config, Stream stream, out IImageFormat format) - where TPixel : struct, IPixel + where TPixel : struct, IPixel + => Load(config, stream, ReadOrigin.Begin, out format); + + /// + /// Create a new instance of the class from the given stream. + /// + /// The configuration options. + /// The stream containing image information. + /// The position in the stream to use for reading. + /// the mime type of the decoded image. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// The pixel format. + /// A new .> + public static Image Load(Configuration config, Stream stream, ReadOrigin origin, out IImageFormat format) + where TPixel : struct, IPixel { config = config ?? Configuration.Default; - (Image img, IImageFormat format) data = WithSeekableStream(stream, s => Decode(s, config)); + (Image img, IImageFormat format) data = WithSeekableStream(stream, origin, s => Decode(s, config)); format = data.format; @@ -237,7 +309,7 @@ namespace SixLabors.ImageSharp throw new NotSupportedException(stringBuilder.ToString()); } - private static T WithSeekableStream(Stream stream, Func action) + private static T WithSeekableStream(Stream stream, ReadOrigin origin, Func action) { if (!stream.CanRead) { @@ -246,9 +318,19 @@ namespace SixLabors.ImageSharp if (stream.CanSeek) { + if (origin == ReadOrigin.Begin) + { + stream.Position = 0; + } + return action(stream); } + if (origin == ReadOrigin.Current) + { + throw new NotSupportedException("Cannot seek within the stream."); + } + // We want to be able to load images from things like HttpContext.Request.Body using (var memoryStream = new MemoryStream()) { diff --git a/src/ImageSharp/Image/ReadOrigin.cs b/src/ImageSharp/Image/ReadOrigin.cs new file mode 100644 index 0000000000..f17bc82f18 --- /dev/null +++ b/src/ImageSharp/Image/ReadOrigin.cs @@ -0,0 +1,21 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp +{ + /// + /// Specifies the position in a stream to use for reading. + /// + public enum ReadOrigin + { + /// + /// Specifies the beginning of a stream. + /// + Begin, + + /// + /// Specifies the current position within a stream. + /// + Current + } +} From dad8b1bed50cf62a82f5a3cce8b9a15b183067bf Mon Sep 17 00:00:00 2001 From: Vicente Penades Date: Wed, 28 Feb 2018 13:52:03 +0100 Subject: [PATCH 129/151] changed test to ensure proper encoders/decoders are set --- .../Formats/ImageFormatManagerTests.cs | 222 +++++++++--------- 1 file changed, 117 insertions(+), 105 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs b/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs index bfaf26c6ac..a6f6600f05 100644 --- a/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs +++ b/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs @@ -1,117 +1,129 @@ -// 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.Linq; -using SixLabors.ImageSharp.Formats; -using SixLabors.ImageSharp.IO; -using SixLabors.ImageSharp.PixelFormats; -using Moq; +// 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.Linq; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.IO; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.Formats.Jpeg; +using SixLabors.ImageSharp.Formats.Gif; +using Moq; using Xunit; + namespace SixLabors.ImageSharp.Tests { public class ImageFormatManagerTests { - public ImageFormatManager FormatsManagerEmpty { get; private set; } - public ImageFormatManager DefaultFormatsManager { get; private set; } - - public ImageFormatManagerTests() - { - this.DefaultFormatsManager = Configuration.Default.ImageFormatsManager; - this.FormatsManagerEmpty = new ImageFormatManager(); - } - - [Fact] - public void IfAutoloadWellKnownFormatsIsTrueAllFormatsAreLoaded() - { - Assert.Equal(4, this.DefaultFormatsManager.ImageEncoders.Count()); - Assert.Equal(4, this.DefaultFormatsManager.ImageDecoders.Count()); + public ImageFormatManager FormatsManagerEmpty { get; private set; } + public ImageFormatManager DefaultFormatsManager { get; private set; } + + public ImageFormatManagerTests() + { + this.DefaultFormatsManager = Configuration.Default.ImageFormatsManager; + this.FormatsManagerEmpty = new ImageFormatManager(); + } + + [Fact] + public void IfAutoloadWellKnownFormatsIsTrueAllFormatsAreLoaded() + { + Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType().Count()); + Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType().Count()); + Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType().Count()); + Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType().Count()); + + Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType().Count()); + Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType().Count()); + Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType().Count()); + Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType().Count()); + } + + [Fact] + public void AddImageFormatDetectorNullthrows() + { + Assert.Throws(() => + { + this.DefaultFormatsManager.AddImageFormatDetector(null); + }); } - [Fact] - public void AddImageFormatDetectorNullthrows() - { - Assert.Throws(() => - { - this.DefaultFormatsManager.AddImageFormatDetector(null); - }); - } - - [Fact] - public void RegisterNullMimeTypeEncoder() - { - Assert.Throws(() => + [Fact] + public void RegisterNullMimeTypeEncoder() + { + Assert.Throws(() => + { + this.DefaultFormatsManager.SetEncoder(null, new Mock().Object); + }); + Assert.Throws(() => { - this.DefaultFormatsManager.SetEncoder(null, new Mock().Object); - }); - Assert.Throws(() => - { - this.DefaultFormatsManager.SetEncoder(ImageFormats.Bmp, null); - }); - Assert.Throws(() => - { - this.DefaultFormatsManager.SetEncoder(null, null); - }); - } - - [Fact] - public void RegisterNullSetDecoder() - { - Assert.Throws(() => - { - this.DefaultFormatsManager.SetDecoder(null, new Mock().Object); - }); - Assert.Throws(() => - { - this.DefaultFormatsManager.SetDecoder(ImageFormats.Bmp, null); - }); - Assert.Throws(() => - { - this.DefaultFormatsManager.SetDecoder(null, null); - }); - } - - [Fact] - public void RegisterMimeTypeEncoderReplacesLast() - { - IImageEncoder encoder1 = new Mock().Object; - this.FormatsManagerEmpty.SetEncoder(TestFormat.GlobalTestFormat, encoder1); - IImageEncoder found = this.FormatsManagerEmpty.FindEncoder(TestFormat.GlobalTestFormat); - Assert.Equal(encoder1, found); - - IImageEncoder encoder2 = new Mock().Object; - this.FormatsManagerEmpty.SetEncoder(TestFormat.GlobalTestFormat, encoder2); - IImageEncoder found2 = this.FormatsManagerEmpty.FindEncoder(TestFormat.GlobalTestFormat); - Assert.Equal(encoder2, found2); - Assert.NotEqual(found, found2); - } - - [Fact] - public void RegisterMimeTypeDecoderReplacesLast() - { - IImageDecoder decoder1 = new Mock().Object; - this.FormatsManagerEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder1); - IImageDecoder found = this.FormatsManagerEmpty.FindDecoder(TestFormat.GlobalTestFormat); - Assert.Equal(decoder1, found); - - IImageDecoder decoder2 = new Mock().Object; - this.FormatsManagerEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder2); - IImageDecoder found2 = this.FormatsManagerEmpty.FindDecoder(TestFormat.GlobalTestFormat); - Assert.Equal(decoder2, found2); - Assert.NotEqual(found, found2); - } - - [Fact] - public void AddFormatCallsConfig() - { - var provider = new Mock(); - var config = new Configuration(); - config.Configure(provider.Object); - - provider.Verify(x => x.Configure(config)); + this.DefaultFormatsManager.SetEncoder(ImageFormats.Bmp, null); + }); + Assert.Throws(() => + { + this.DefaultFormatsManager.SetEncoder(null, null); + }); + } + + [Fact] + public void RegisterNullSetDecoder() + { + Assert.Throws(() => + { + this.DefaultFormatsManager.SetDecoder(null, new Mock().Object); + }); + Assert.Throws(() => + { + this.DefaultFormatsManager.SetDecoder(ImageFormats.Bmp, null); + }); + Assert.Throws(() => + { + this.DefaultFormatsManager.SetDecoder(null, null); + }); + } + + [Fact] + public void RegisterMimeTypeEncoderReplacesLast() + { + IImageEncoder encoder1 = new Mock().Object; + this.FormatsManagerEmpty.SetEncoder(TestFormat.GlobalTestFormat, encoder1); + IImageEncoder found = this.FormatsManagerEmpty.FindEncoder(TestFormat.GlobalTestFormat); + Assert.Equal(encoder1, found); + + IImageEncoder encoder2 = new Mock().Object; + this.FormatsManagerEmpty.SetEncoder(TestFormat.GlobalTestFormat, encoder2); + IImageEncoder found2 = this.FormatsManagerEmpty.FindEncoder(TestFormat.GlobalTestFormat); + Assert.Equal(encoder2, found2); + Assert.NotEqual(found, found2); + } + + [Fact] + public void RegisterMimeTypeDecoderReplacesLast() + { + IImageDecoder decoder1 = new Mock().Object; + this.FormatsManagerEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder1); + IImageDecoder found = this.FormatsManagerEmpty.FindDecoder(TestFormat.GlobalTestFormat); + Assert.Equal(decoder1, found); + + IImageDecoder decoder2 = new Mock().Object; + this.FormatsManagerEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder2); + IImageDecoder found2 = this.FormatsManagerEmpty.FindDecoder(TestFormat.GlobalTestFormat); + Assert.Equal(decoder2, found2); + Assert.NotEqual(found, found2); + } + + [Fact] + public void AddFormatCallsConfig() + { + var provider = new Mock(); + var config = new Configuration(); + config.Configure(provider.Object); + + provider.Verify(x => x.Configure(config)); } } } From 4579e5958d0b8115dbe496459b20bbc891f45a82 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Wed, 28 Feb 2018 14:17:31 +0100 Subject: [PATCH 130/151] better EOF handling logic --- .../Components/Decoder/DecoderThrowHelper.cs | 11 ++++------- .../GolangPort/Components/Decoder/InputProcessor.cs | 7 +++++++ .../Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs | 4 ++-- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/DecoderThrowHelper.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/DecoderThrowHelper.cs index c1fde41ed0..904ce00dde 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/DecoderThrowHelper.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/DecoderThrowHelper.cs @@ -18,20 +18,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowExceptionForErrorCode(this OrigDecoderErrorCode errorCode) { + // REMARK: If this method throws for an image that is expected to be decodable, + // consider using the ***Unsafe variant of the parsing method that asks for ThrowExceptionForErrorCode() + // then verify the error code + implement fallback logic manually! switch (errorCode) { case OrigDecoderErrorCode.NoError: throw new ArgumentException("ThrowExceptionForErrorCode() called with NoError!", nameof(errorCode)); case OrigDecoderErrorCode.MissingFF00: throw new MissingFF00Exception(); - case OrigDecoderErrorCode.UnexpectedEndOfStream: - - // TODO: - // Disabled for now since we want to avoid throwing for most bad eof. - // Will probably delete - // throw new EOFException(); - break; + throw new EOFException(); default: throw new ArgumentOutOfRangeException(nameof(errorCode), errorCode, null); } diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs index d88481ba40..88599808fc 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs @@ -107,6 +107,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder return this.Bytes.ReadByte(this.InputStream); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public OrigDecoderErrorCode ReadByteUnsafe(out byte result) + { + this.LastErrorCode = this.Bytes.ReadByteUnsafe(this.InputStream, out result); + return this.LastErrorCode; + } + /// /// Decodes a single bit /// TODO: This method (and also the usages) could be optimized by batching! diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs index 6d3c8f5d19..58513fd297 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs @@ -289,7 +289,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort { // Section B.1.1.2 says, "Any marker may optionally be preceded by any // number of fill bytes, which are bytes assigned code X'FF'". - marker = this.InputProcessor.ReadByte(); + this.InputProcessor.ReadByteUnsafe(out marker); if (this.InputProcessor.ReachedEOF) { @@ -318,7 +318,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort // Read the 16-bit length of the segment. The value includes the 2 bytes for the // length itself, so we subtract 2 to get the number of remaining bytes. - this.InputProcessor.ReadFull(this.Temp, 0, 2); + this.InputProcessor.ReadFullUnsafe(this.Temp, 0, 2); int remaining = (this.Temp[0] << 8) + this.Temp[1] - 2; if (remaining < 0) { From c5f7f27e19b5ebfe73d54aba65b90b962fe2801b Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 1 Mar 2018 00:29:55 +1100 Subject: [PATCH 131/151] Use configuration to set ReadOrigin --- src/ImageSharp/Configuration.cs | 5 + src/ImageSharp/Image/Image.FromStream.cs | 101 +++---------------- tests/ImageSharp.Tests/ConfigurationTests.cs | 11 +- 3 files changed, 27 insertions(+), 90 deletions(-) diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index 9a627eeb77..7b02f21c46 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -83,6 +83,11 @@ namespace SixLabors.ImageSharp /// public IEnumerable ImageFormats => this.imageFormats; + /// + /// Gets or sets the position in a stream to use for reading. + /// + public ReadOrigin ReadOrigin { get; set; } = ReadOrigin.Begin; + /// /// Gets or sets the that is currently in use. /// diff --git a/src/ImageSharp/Image/Image.FromStream.cs b/src/ImageSharp/Image/Image.FromStream.cs index 30d8014233..a0c71133c8 100644 --- a/src/ImageSharp/Image/Image.FromStream.cs +++ b/src/ImageSharp/Image/Image.FromStream.cs @@ -20,15 +20,7 @@ namespace SixLabors.ImageSharp /// /// The image stream to read the header from. /// The mime type or null if none found. - public static IImageFormat DetectFormat(Stream stream) => DetectFormat(null, stream); - - /// - /// By reading the header on the provided stream this calculates the images mime type. - /// - /// The image stream to read the header from. - /// The position in the stream to use for reading. - /// The mime type or null if none found. - public static IImageFormat DetectFormat(Stream stream, ReadOrigin origin) => DetectFormat(null, stream, origin); + public static IImageFormat DetectFormat(Stream stream) => DetectFormat(Configuration.Default, stream); /// /// By reading the header on the provided stream this calculates the images mime type. @@ -36,17 +28,8 @@ namespace SixLabors.ImageSharp /// The configuration. /// The image stream to read the header from. /// The mime type or null if none found. - public static IImageFormat DetectFormat(Configuration config, Stream stream) => DetectFormat(config, stream, ReadOrigin.Begin); - - /// - /// By reading the header on the provided stream this calculates the images mime type. - /// - /// The configuration. - /// The image stream to read the header from. - /// The position in the stream to use for reading. - /// The mime type or null if none found. - public static IImageFormat DetectFormat(Configuration config, Stream stream, ReadOrigin origin) - => WithSeekableStream(stream, origin, s => InternalDetectFormat(s, config ?? Configuration.Default)); + public static IImageFormat DetectFormat(Configuration config, Stream stream) + => WithSeekableStream(config, stream, s => InternalDetectFormat(s, config)); /// /// By reading the header on the provided stream this reads the raw image information. @@ -58,33 +41,19 @@ namespace SixLabors.ImageSharp /// /// The or null if suitable info detector not found. /// - public static IImageInfo Identify(Stream stream) => Identify(null, stream); - - /// - /// Reads the raw image information from the specified stream without fully decoding it. - /// - /// The configuration. - /// The image stream to read the information from. - /// - /// Thrown if the stream is not readable nor seekable. - /// - /// - /// The or null if suitable info detector is not found. - /// - public static IImageInfo Identify(Configuration config, Stream stream) => Identify(config, stream, ReadOrigin.Begin); + public static IImageInfo Identify(Stream stream) => Identify(Configuration.Default, stream); /// /// Reads the raw image information from the specified stream without fully decoding it. /// /// The configuration. /// The image stream to read the information from. - /// The position in the stream to use for reading. /// Thrown if the stream is not readable. /// /// The or null if suitable info detector is not found. /// - public static IImageInfo Identify(Configuration config, Stream stream, ReadOrigin origin) - => WithSeekableStream(stream, origin, s => InternalIdentity(s, config ?? Configuration.Default)); + public static IImageInfo Identify(Configuration config, Stream stream) + => WithSeekableStream(config, stream, s => InternalIdentity(s, config ?? Configuration.Default)); /// /// Create a new instance of the class from the given stream. @@ -181,22 +150,7 @@ namespace SixLabors.ImageSharp /// A new .> public static Image Load(Stream stream, IImageDecoder decoder) where TPixel : struct, IPixel - => Load(stream, ReadOrigin.Begin, decoder); - - /// - /// Create a new instance of the class from the given stream. - /// - /// The stream containing image information. - /// The position in the stream to use for reading. - /// The decoder. - /// - /// Thrown if the stream is not readable nor seekable. - /// - /// The pixel format. - /// A new .> - public static Image Load(Stream stream, ReadOrigin origin, IImageDecoder decoder) - where TPixel : struct, IPixel - => WithSeekableStream(stream, origin, s => decoder.Decode(Configuration.Default, s)); + => WithSeekableStream(Configuration.Default, stream, s => decoder.Decode(Configuration.Default, s)); /// /// Create a new instance of the class from the given stream. @@ -211,23 +165,7 @@ namespace SixLabors.ImageSharp /// A new .> public static Image Load(Configuration config, Stream stream, IImageDecoder decoder) where TPixel : struct, IPixel - => Load(config, stream, ReadOrigin.Begin, decoder); - - /// - /// Create a new instance of the class from the given stream. - /// - /// The Configuration. - /// The stream containing image information. - /// The position in the stream to use for reading. - /// The decoder. - /// - /// Thrown if the stream is not readable nor seekable. - /// - /// The pixel format. - /// A new .> - public static Image Load(Configuration config, Stream stream, ReadOrigin origin, IImageDecoder decoder) - where TPixel : struct, IPixel - => WithSeekableStream(stream, origin, s => decoder.Decode(config, s)); + => WithSeekableStream(config, stream, s => decoder.Decode(config, s)); /// /// Create a new instance of the class from the given stream. @@ -243,21 +181,6 @@ namespace SixLabors.ImageSharp where TPixel : struct, IPixel => Load(config, stream, out IImageFormat _); - /// - /// Create a new instance of the class from the given stream. - /// - /// The configuration options. - /// The stream containing image information. - /// The position in the stream to use for reading. - /// - /// Thrown if the stream is not readable nor seekable. - /// - /// The pixel format. - /// A new .> - public static Image Load(Configuration config, Stream stream, ReadOrigin origin) - where TPixel : struct, IPixel - => Load(config, stream, origin, out IImageFormat _); - /// /// Create a new instance of the class from the given stream. /// @@ -289,7 +212,7 @@ namespace SixLabors.ImageSharp where TPixel : struct, IPixel { config = config ?? Configuration.Default; - (Image img, IImageFormat format) data = WithSeekableStream(stream, origin, s => Decode(s, config)); + (Image img, IImageFormat format) data = WithSeekableStream(config, stream, s => Decode(s, config)); format = data.format; @@ -309,7 +232,7 @@ namespace SixLabors.ImageSharp throw new NotSupportedException(stringBuilder.ToString()); } - private static T WithSeekableStream(Stream stream, ReadOrigin origin, Func action) + private static T WithSeekableStream(Configuration config, Stream stream, Func action) { if (!stream.CanRead) { @@ -318,7 +241,7 @@ namespace SixLabors.ImageSharp if (stream.CanSeek) { - if (origin == ReadOrigin.Begin) + if (config.ReadOrigin == ReadOrigin.Begin) { stream.Position = 0; } @@ -326,7 +249,7 @@ namespace SixLabors.ImageSharp return action(stream); } - if (origin == ReadOrigin.Current) + if (config.ReadOrigin == ReadOrigin.Current) { throw new NotSupportedException("Cannot seek within the stream."); } diff --git a/tests/ImageSharp.Tests/ConfigurationTests.cs b/tests/ImageSharp.Tests/ConfigurationTests.cs index 18d4abdd1b..fc97b22090 100644 --- a/tests/ImageSharp.Tests/ConfigurationTests.cs +++ b/tests/ImageSharp.Tests/ConfigurationTests.cs @@ -59,6 +59,15 @@ namespace SixLabors.ImageSharp.Tests Assert.True(Configuration.Default.ParallelOptions != null); } + /// + /// Test that the default configuration read origin options is set to begin. + /// + [Fact] + public void TestDefultConfigurationReadOriginIsBegin() + { + Assert.True(Configuration.Default.ReadOrigin == ReadOrigin.Begin); + } + /// /// Test that the default configuration parallel options max degrees of parallelism matches the /// environment processor count. @@ -83,7 +92,7 @@ namespace SixLabors.ImageSharp.Tests { Assert.Throws(() => { - this.DefaultConfiguration.SetEncoder(null, new Mock().Object); + this.DefaultConfiguration.SetEncoder(null, new Mock().Object); }); Assert.Throws(() => { From 78368a785c57710f61740a099f9b3d0b4af0863a Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Wed, 28 Feb 2018 14:31:18 +0100 Subject: [PATCH 132/151] removing the "Issue" label from #464-s test image + ExifUndefType.jpg is actually progressive! --- tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs | 8 ++++---- .../Formats/Jpg/JpegImagePostProcessorTests.cs | 4 ++-- tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs | 6 +++--- .../MetaData/Profiles/Exif/ExifProfileTests.cs | 2 +- tests/ImageSharp.Tests/TestImages.cs | 4 ++-- tests/Images/External | 2 +- .../MultiScanBaselineCMYK.jpg} | 0 .../Input/Jpg/{baseline => progressive}/ExifUndefType.jpg | 0 8 files changed, 13 insertions(+), 13 deletions(-) rename tests/Images/Input/Jpg/{issues/Issue464-CMYK-DecodedAsGrayscale.jpg => baseline/MultiScanBaselineCMYK.jpg} (100%) rename tests/Images/Input/Jpg/{baseline => progressive}/ExifUndefType.jpg (100%) diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index d78277528c..139fa351bb 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -41,9 +41,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg TestImages.Jpeg.Baseline.Jpeg444, TestImages.Jpeg.Baseline.Bad.BadEOF, - TestImages.Jpeg.Baseline.Bad.ExifUndefType, TestImages.Jpeg.Issues.MultiHuffmanBaseline394, - TestImages.Jpeg.Issues.CmykIssueBaseline464 + TestImages.Jpeg.Baseline.MultiScanBaselineCMYK }; public static string[] ProgressiveTestJpegs = @@ -52,14 +51,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg TestImages.Jpeg.Progressive.Festzug, TestImages.Jpeg.Progressive.Bad.BadEOF, TestImages.Jpeg.Issues.BadCoeffsProgressive178, TestImages.Jpeg.Issues.MissingFF00ProgressiveGirl159, - TestImages.Jpeg.Issues.BadZigZagProgressive385 + TestImages.Jpeg.Issues.BadZigZagProgressive385, + TestImages.Jpeg.Progressive.Bad.ExifUndefType }; private static readonly Dictionary CustomToleranceValues = new Dictionary { // Baseline: [TestImages.Jpeg.Baseline.Calliphora] = 0.00002f / 100, - [TestImages.Jpeg.Baseline.Bad.ExifUndefType] = 0.011f / 100, [TestImages.Jpeg.Baseline.Bad.BadEOF] = 0.38f / 100, [TestImages.Jpeg.Baseline.Testorig420] = 0.38f / 100, @@ -71,6 +70,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [TestImages.Jpeg.Progressive.Fb] = 0.16f / 100, [TestImages.Jpeg.Progressive.Progress] = 0.31f / 100, [TestImages.Jpeg.Issues.BadZigZagProgressive385] = 0.23f / 100, + [TestImages.Jpeg.Progressive.Bad.ExifUndefType] = 0.011f / 100, }; public const PixelTypes CommonNonDefaultPixelTypes = PixelTypes.Rgba32 | PixelTypes.Argb32 | PixelTypes.RgbaVector; diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegImagePostProcessorTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegImagePostProcessorTests.cs index 423673ef93..ec4a42104a 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegImagePostProcessorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegImagePostProcessorTests.cs @@ -21,13 +21,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg TestImages.Jpeg.Baseline.Jpeg420Small, TestImages.Jpeg.Baseline.Jpeg444, TestImages.Jpeg.Baseline.Bad.BadEOF, - TestImages.Jpeg.Baseline.Bad.ExifUndefType, }; public static string[] ProgressiveTestJpegs = { TestImages.Jpeg.Progressive.Fb, TestImages.Jpeg.Progressive.Progress, - TestImages.Jpeg.Progressive.Festzug, TestImages.Jpeg.Progressive.Bad.BadEOF + TestImages.Jpeg.Progressive.Festzug, TestImages.Jpeg.Progressive.Bad.BadEOF, + TestImages.Jpeg.Progressive.Bad.ExifUndefType, }; public JpegImagePostProcessorTests(ITestOutputHelper output) diff --git a/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs index 32c3be47d0..6816b84656 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs @@ -28,14 +28,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg TestImages.Jpeg.Baseline.Calliphora, TestImages.Jpeg.Baseline.Cmyk, TestImages.Jpeg.Baseline.Jpeg400, TestImages.Jpeg.Baseline.Jpeg444, TestImages.Jpeg.Baseline.Testorig420, TestImages.Jpeg.Baseline.Jpeg420Small, TestImages.Jpeg.Baseline.Bad.BadEOF, - TestImages.Jpeg.Baseline.Bad.ExifUndefType, - TestImages.Jpeg.Issues.CmykIssueBaseline464 + TestImages.Jpeg.Baseline.MultiScanBaselineCMYK }; public static readonly string[] ProgressiveTestJpegs = { TestImages.Jpeg.Progressive.Fb, TestImages.Jpeg.Progressive.Progress, - TestImages.Jpeg.Progressive.Festzug, TestImages.Jpeg.Progressive.Bad.BadEOF + TestImages.Jpeg.Progressive.Festzug, TestImages.Jpeg.Progressive.Bad.BadEOF, + TestImages.Jpeg.Progressive.Bad.ExifUndefType, }; public static readonly string[] AllTestJpegs = BaselineTestJpegs.Concat(ProgressiveTestJpegs).ToArray(); diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs index edeeebd28e..5e7e9e3a79 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs @@ -272,7 +272,7 @@ namespace SixLabors.ImageSharp.Tests [Fact] public void ExifTypeUndefined() { - Image image = TestFile.Create(TestImages.Jpeg.Baseline.Bad.ExifUndefType).CreateImage(); + Image image = TestFile.Create(TestImages.Jpeg.Progressive.Bad.ExifUndefType).CreateImage(); Assert.NotNull(image); ExifProfile profile = image.MetaData.ExifProfile; diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index bb66414fb1..f1f989581f 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -85,6 +85,7 @@ namespace SixLabors.ImageSharp.Tests public static class Bad { public const string BadEOF = "Jpg/progressive/BadEofProgressive.jpg"; + public const string ExifUndefType = "Jpg/progressive/ExifUndefType.jpg"; } public static readonly string[] All = { Fb, Progress, Festzug }; @@ -95,7 +96,6 @@ namespace SixLabors.ImageSharp.Tests public static class Bad { public const string BadEOF = "Jpg/baseline/badeof.jpg"; - public const string ExifUndefType = "Jpg/baseline/ExifUndefType.jpg"; } public const string Cmyk = "Jpg/baseline/cmyk.jpg"; @@ -113,6 +113,7 @@ namespace SixLabors.ImageSharp.Tests public const string Jpeg444 = "Jpg/baseline/jpeg444.jpg"; public const string Jpeg420Small = "Jpg/baseline/jpeg420small.jpg"; public const string Testorig420 = "Jpg/baseline/testorig.jpg"; + public const string MultiScanBaselineCMYK = "Jpg/baseline/MultiScanBaselineCMYK.jpg"; public static readonly string[] All = { @@ -129,7 +130,6 @@ namespace SixLabors.ImageSharp.Tests public const string BadCoeffsProgressive178 = "Jpg/issues/Issue178-BadCoeffsProgressive-Lemon.jpg"; public const string BadZigZagProgressive385 = "Jpg/issues/Issue385-BadZigZag-Progressive.jpg"; public const string MultiHuffmanBaseline394 = "Jpg/issues/Issue394-MultiHuffmanBaseline-Speakers.jpg"; - public const string CmykIssueBaseline464 = "Jpg/issues/Issue464-CMYK-DecodedAsGrayscale.jpg"; } public static readonly string[] All = Baseline.All.Concat(Progressive.All).ToArray(); diff --git a/tests/Images/External b/tests/Images/External index cdc178daa5..8714b94dc4 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit cdc178daa552856157d1834ad136f9a28996a0f9 +Subproject commit 8714b94dc4bab6788fcbb6254174db2b9c8f69c9 diff --git a/tests/Images/Input/Jpg/issues/Issue464-CMYK-DecodedAsGrayscale.jpg b/tests/Images/Input/Jpg/baseline/MultiScanBaselineCMYK.jpg similarity index 100% rename from tests/Images/Input/Jpg/issues/Issue464-CMYK-DecodedAsGrayscale.jpg rename to tests/Images/Input/Jpg/baseline/MultiScanBaselineCMYK.jpg diff --git a/tests/Images/Input/Jpg/baseline/ExifUndefType.jpg b/tests/Images/Input/Jpg/progressive/ExifUndefType.jpg similarity index 100% rename from tests/Images/Input/Jpg/baseline/ExifUndefType.jpg rename to tests/Images/Input/Jpg/progressive/ExifUndefType.jpg From 24b42172969e8d8c392c927aa8809c7307c4687b Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 1 Mar 2018 00:49:43 +1100 Subject: [PATCH 133/151] So many nulls! --- src/ImageSharp/Image/Image.FromBytes.cs | 14 +++++--------- src/ImageSharp/Image/Image.FromFile.cs | 11 ++++------- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/src/ImageSharp/Image/Image.FromBytes.cs b/src/ImageSharp/Image/Image.FromBytes.cs index 9da9c5e432..44c53d7767 100644 --- a/src/ImageSharp/Image/Image.FromBytes.cs +++ b/src/ImageSharp/Image/Image.FromBytes.cs @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp /// The format or null if none found. public static IImageFormat DetectFormat(byte[] data) { - return DetectFormat(null, data); + return DetectFormat(Configuration.Default, data); } /// @@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp /// /// The byte array containing image data. /// A new . - public static Image Load(byte[] data) => Load(null, data); + public static Image Load(byte[] data) => Load(Configuration.Default, data); /// /// Create a new instance of the class from the given byte array. @@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp /// The byte array containing image data. /// The mime type of the decoded image. /// A new . - public static Image Load(byte[] data, out IImageFormat format) => Load(null, data, out format); + public static Image Load(byte[] data, out IImageFormat format) => Load(Configuration.Default, data, out format); /// /// Create a new instance of the class from the given byte array. @@ -93,9 +93,7 @@ namespace SixLabors.ImageSharp /// A new . public static Image Load(byte[] data) where TPixel : struct, IPixel - { - return Load(null, data); - } + => Load(Configuration.Default, data); /// /// Create a new instance of the class from the given byte array. @@ -106,9 +104,7 @@ namespace SixLabors.ImageSharp /// A new . public static Image Load(byte[] data, out IImageFormat format) where TPixel : struct, IPixel - { - return Load(null, data, out format); - } + => Load(Configuration.Default, data, out format); /// /// Create a new instance of the class from the given byte array. diff --git a/src/ImageSharp/Image/Image.FromFile.cs b/src/ImageSharp/Image/Image.FromFile.cs index 0474a6d476..ad8f3426f2 100644 --- a/src/ImageSharp/Image/Image.FromFile.cs +++ b/src/ImageSharp/Image/Image.FromFile.cs @@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp /// The mime type or null if none found. public static IImageFormat DetectFormat(string filePath) { - return DetectFormat(null, filePath); + return DetectFormat(Configuration.Default, filePath); } /// @@ -118,7 +118,7 @@ namespace SixLabors.ImageSharp public static Image Load(string path) where TPixel : struct, IPixel { - return Load(null, path); + return Load(Configuration.Default, path); } /// @@ -134,7 +134,7 @@ namespace SixLabors.ImageSharp public static Image Load(string path, out IImageFormat format) where TPixel : struct, IPixel { - return Load(null, path, out format); + return Load(Configuration.Default, path, out format); } /// @@ -150,7 +150,6 @@ namespace SixLabors.ImageSharp public static Image Load(Configuration config, string path) where TPixel : struct, IPixel { - config = config ?? Configuration.Default; using (Stream stream = config.FileSystem.OpenRead(path)) { return Load(config, stream); @@ -171,7 +170,6 @@ namespace SixLabors.ImageSharp public static Image Load(Configuration config, string path, out IImageFormat format) where TPixel : struct, IPixel { - config = config ?? Configuration.Default; using (Stream stream = config.FileSystem.OpenRead(path)) { return Load(config, stream, out format); @@ -191,7 +189,7 @@ namespace SixLabors.ImageSharp public static Image Load(string path, IImageDecoder decoder) where TPixel : struct, IPixel { - return Load(null, path, decoder); + return Load(Configuration.Default, path, decoder); } /// @@ -208,7 +206,6 @@ namespace SixLabors.ImageSharp public static Image Load(Configuration config, string path, IImageDecoder decoder) where TPixel : struct, IPixel { - config = config ?? Configuration.Default; using (Stream stream = config.FileSystem.OpenRead(path)) { return Load(config, stream, decoder); From 2064002b20ca5051bd363ecd0753d067f17526bb Mon Sep 17 00:00:00 2001 From: Jason Nelson Date: Wed, 28 Feb 2018 10:05:04 -0800 Subject: [PATCH 134/151] Fix breaking changes from System.Memory in benchmarks --- tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs | 6 +++--- tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs index 7d8519875b..7bac44a982 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs @@ -3,7 +3,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk { using System.Numerics; using System.Runtime.CompilerServices; - + using System.Runtime.InteropServices; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Memory; @@ -37,8 +37,8 @@ namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk [Benchmark(Baseline = true)] public void PerElement() { - ref Vector4 s = ref this.source.Span.DangerousGetPinnableReference(); - ref TPixel d = ref this.destination.Span.DangerousGetPinnableReference(); + ref Vector4 s = ref MemoryMarshal.GetReference(this.source.Span); + ref TPixel d = ref MemoryMarshal.GetReference(this.destination.Span); for (int i = 0; i < this.Count; i++) { diff --git a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj index 021b9ead74..6dcfbaf818 100644 --- a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj +++ b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj @@ -17,7 +17,9 @@ - + + + From 862998974b6e694fcf00aecbc76c8b2e118ce271 Mon Sep 17 00:00:00 2001 From: Jason Nelson Date: Wed, 28 Feb 2018 10:17:23 -0800 Subject: [PATCH 135/151] Annotate readonly structs --- src/ImageSharp/ColorSpaces/CieLab.cs | 2 +- src/ImageSharp/ColorSpaces/CieLch.cs | 2 +- src/ImageSharp/ColorSpaces/CieLchuv.cs | 2 +- src/ImageSharp/ColorSpaces/CieLuv.cs | 2 +- src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs | 2 +- src/ImageSharp/ColorSpaces/CieXyy.cs | 2 +- src/ImageSharp/ColorSpaces/CieXyz.cs | 2 +- src/ImageSharp/ColorSpaces/Cmyk.cs | 2 +- .../Conversion/Implementation/Rgb/RgbWorkingSpace.cs | 2 +- src/ImageSharp/ColorSpaces/Hsl.cs | 2 +- src/ImageSharp/ColorSpaces/Hsv.cs | 2 +- src/ImageSharp/ColorSpaces/HunterLab.cs | 2 +- src/ImageSharp/ColorSpaces/LinearRgb.cs | 2 +- src/ImageSharp/ColorSpaces/Lms.cs | 2 +- src/ImageSharp/ColorSpaces/Rgb.cs | 2 +- src/ImageSharp/ColorSpaces/YCbCr.cs | 2 +- src/ImageSharp/Formats/Gif/PackedField.cs | 2 +- src/ImageSharp/Formats/Jpeg/Common/Decoder/AdobeMarker.cs | 2 +- src/ImageSharp/Formats/Jpeg/Common/Decoder/JFifMarker.cs | 2 +- .../Jpeg/GolangPort/Components/Decoder/InputProcessor.cs | 2 -- .../Formats/Jpeg/GolangPort/Components/Encoder/HuffmanLut.cs | 2 +- .../Formats/Jpeg/GolangPort/Components/Encoder/HuffmanSpec.cs | 2 +- .../Formats/Jpeg/PdfJsPort/Components/PdfJsYCbCrToRgbTables.cs | 2 +- src/ImageSharp/Memory/BufferArea{T}.cs | 2 +- .../MetaData/Profiles/ICC/Various/IccColorantTableEntry.cs | 2 +- src/ImageSharp/MetaData/Profiles/ICC/Various/IccNamedColor.cs | 2 +- .../MetaData/Profiles/ICC/Various/IccPositionNumber.cs | 2 +- src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileId.cs | 2 +- .../MetaData/Profiles/ICC/Various/IccResponseNumber.cs | 2 +- .../MetaData/Profiles/ICC/Various/IccScreeningChannel.cs | 2 +- .../MetaData/Profiles/ICC/Various/IccTagTableEntry.cs | 2 +- src/ImageSharp/Numerics/ValueSize.cs | 2 +- src/ImageSharp/Processing/Processors/Dithering/PixelPair.cs | 2 +- 33 files changed, 32 insertions(+), 34 deletions(-) diff --git a/src/ImageSharp/ColorSpaces/CieLab.cs b/src/ImageSharp/ColorSpaces/CieLab.cs index 107be4cb2b..cb08d08bf9 100644 --- a/src/ImageSharp/ColorSpaces/CieLab.cs +++ b/src/ImageSharp/ColorSpaces/CieLab.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// Represents a CIE L*a*b* 1976 color. /// /// - internal struct CieLab : IColorVector, IEquatable, IAlmostEquatable + internal readonly struct CieLab : IColorVector, IEquatable, IAlmostEquatable { /// /// D50 standard illuminant. diff --git a/src/ImageSharp/ColorSpaces/CieLch.cs b/src/ImageSharp/ColorSpaces/CieLch.cs index 834ef56a89..94443fd863 100644 --- a/src/ImageSharp/ColorSpaces/CieLch.cs +++ b/src/ImageSharp/ColorSpaces/CieLch.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// Represents the CIE L*C*h°, cylindrical form of the CIE L*a*b* 1976 color. /// /// - internal struct CieLch : IColorVector, IEquatable, IAlmostEquatable + internal readonly struct CieLch : IColorVector, IEquatable, IAlmostEquatable { /// /// D50 standard illuminant. diff --git a/src/ImageSharp/ColorSpaces/CieLchuv.cs b/src/ImageSharp/ColorSpaces/CieLchuv.cs index f35914d641..705b770d35 100644 --- a/src/ImageSharp/ColorSpaces/CieLchuv.cs +++ b/src/ImageSharp/ColorSpaces/CieLchuv.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// Represents the CIE L*C*h°, cylindrical form of the CIE L*u*v* 1976 color. /// /// - internal struct CieLchuv : IColorVector, IEquatable, IAlmostEquatable + internal readonly struct CieLchuv : IColorVector, IEquatable, IAlmostEquatable { /// /// D50 standard illuminant. diff --git a/src/ImageSharp/ColorSpaces/CieLuv.cs b/src/ImageSharp/ColorSpaces/CieLuv.cs index 9b52517083..b0ae048ab7 100644 --- a/src/ImageSharp/ColorSpaces/CieLuv.cs +++ b/src/ImageSharp/ColorSpaces/CieLuv.cs @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// attempted perceptual uniformity /// /// - internal struct CieLuv : IColorVector, IEquatable, IAlmostEquatable + internal readonly struct CieLuv : IColorVector, IEquatable, IAlmostEquatable { /// /// D65 standard illuminant. diff --git a/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs b/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs index 487f464d8e..d0a70dd191 100644 --- a/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs +++ b/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// /// Represents the coordinates of CIEXY chromaticity space /// - internal struct CieXyChromaticityCoordinates : IEquatable, IAlmostEquatable + internal readonly struct CieXyChromaticityCoordinates : IEquatable, IAlmostEquatable { /// /// Represents a that has X, Y values set to zero. diff --git a/src/ImageSharp/ColorSpaces/CieXyy.cs b/src/ImageSharp/ColorSpaces/CieXyy.cs index d5ef4b15d3..751830a0ba 100644 --- a/src/ImageSharp/ColorSpaces/CieXyy.cs +++ b/src/ImageSharp/ColorSpaces/CieXyy.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// Represents an CIE xyY 1931 color /// /// - internal struct CieXyy : IColorVector, IEquatable, IAlmostEquatable + internal readonly struct CieXyy : IColorVector, IEquatable, IAlmostEquatable { /// /// Represents a that has X, Y, and Y values set to zero. diff --git a/src/ImageSharp/ColorSpaces/CieXyz.cs b/src/ImageSharp/ColorSpaces/CieXyz.cs index 908408000a..0f1866009b 100644 --- a/src/ImageSharp/ColorSpaces/CieXyz.cs +++ b/src/ImageSharp/ColorSpaces/CieXyz.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// Represents an CIE XYZ 1931 color /// /// - internal struct CieXyz : IColorVector, IEquatable, IAlmostEquatable + internal readonly struct CieXyz : IColorVector, IEquatable, IAlmostEquatable { /// /// Represents a that has X, Y, and Z values set to zero. diff --git a/src/ImageSharp/ColorSpaces/Cmyk.cs b/src/ImageSharp/ColorSpaces/Cmyk.cs index 2a58a5762a..2eb148a8c3 100644 --- a/src/ImageSharp/ColorSpaces/Cmyk.cs +++ b/src/ImageSharp/ColorSpaces/Cmyk.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// /// Represents an CMYK (cyan, magenta, yellow, keyline) color. /// - internal struct Cmyk : IEquatable, IAlmostEquatable + internal readonly struct Cmyk : IEquatable, IAlmostEquatable { /// /// Represents a that has C, M, Y, and K values set to zero. diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs index 8a2c66a80c..5a5c39647f 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap /// /// Trivial implementation of /// - internal struct RgbWorkingSpace : IRgbWorkingSpace + internal readonly struct RgbWorkingSpace : IRgbWorkingSpace { /// /// Initializes a new instance of the struct. diff --git a/src/ImageSharp/ColorSpaces/Hsl.cs b/src/ImageSharp/ColorSpaces/Hsl.cs index cf880f1548..1944ac0c6b 100644 --- a/src/ImageSharp/ColorSpaces/Hsl.cs +++ b/src/ImageSharp/ColorSpaces/Hsl.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// /// Represents a Hsl (hue, saturation, lightness) color. /// - internal struct Hsl : IColorVector, IEquatable, IAlmostEquatable + internal readonly struct Hsl : IColorVector, IEquatable, IAlmostEquatable { /// /// Represents a that has H, S, and L values set to zero. diff --git a/src/ImageSharp/ColorSpaces/Hsv.cs b/src/ImageSharp/ColorSpaces/Hsv.cs index 9f47393792..45ffd7f121 100644 --- a/src/ImageSharp/ColorSpaces/Hsv.cs +++ b/src/ImageSharp/ColorSpaces/Hsv.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// /// Represents a HSV (hue, saturation, value) color. Also known as HSB (hue, saturation, brightness). /// - internal struct Hsv : IColorVector, IEquatable, IAlmostEquatable + internal readonly struct Hsv : IColorVector, IEquatable, IAlmostEquatable { /// /// Represents a that has H, S, and V values set to zero. diff --git a/src/ImageSharp/ColorSpaces/HunterLab.cs b/src/ImageSharp/ColorSpaces/HunterLab.cs index b5ba7c86c7..de42518d76 100644 --- a/src/ImageSharp/ColorSpaces/HunterLab.cs +++ b/src/ImageSharp/ColorSpaces/HunterLab.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// Represents an Hunter LAB color. /// /// - internal struct HunterLab : IColorVector, IEquatable, IAlmostEquatable + internal readonly struct HunterLab : IColorVector, IEquatable, IAlmostEquatable { /// /// D50 standard illuminant. diff --git a/src/ImageSharp/ColorSpaces/LinearRgb.cs b/src/ImageSharp/ColorSpaces/LinearRgb.cs index 07889c3529..b8c446285a 100644 --- a/src/ImageSharp/ColorSpaces/LinearRgb.cs +++ b/src/ImageSharp/ColorSpaces/LinearRgb.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// /// Represents an linear Rgb color with specified working space /// - internal struct LinearRgb : IColorVector, IEquatable, IAlmostEquatable + internal readonly struct LinearRgb : IColorVector, IEquatable, IAlmostEquatable { /// /// Represents a that has R, G, and B values set to zero. diff --git a/src/ImageSharp/ColorSpaces/Lms.cs b/src/ImageSharp/ColorSpaces/Lms.cs index 82c291de3d..72ac16f213 100644 --- a/src/ImageSharp/ColorSpaces/Lms.cs +++ b/src/ImageSharp/ColorSpaces/Lms.cs @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// named after their responsivity (sensitivity) at long, medium and short wavelengths. /// /// - internal struct Lms : IColorVector, IEquatable, IAlmostEquatable + internal readonly struct Lms : IColorVector, IEquatable, IAlmostEquatable { /// /// Represents a that has L, M, and S values set to zero. diff --git a/src/ImageSharp/ColorSpaces/Rgb.cs b/src/ImageSharp/ColorSpaces/Rgb.cs index 8ac8411b20..53fa6086df 100644 --- a/src/ImageSharp/ColorSpaces/Rgb.cs +++ b/src/ImageSharp/ColorSpaces/Rgb.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// /// Represents an RGB color with specified working space /// - internal struct Rgb : IColorVector, IEquatable, IAlmostEquatable + internal readonly struct Rgb : IColorVector, IEquatable, IAlmostEquatable { /// /// Represents a that has R, G, and B values set to zero. diff --git a/src/ImageSharp/ColorSpaces/YCbCr.cs b/src/ImageSharp/ColorSpaces/YCbCr.cs index 708a74308a..44a0b245d5 100644 --- a/src/ImageSharp/ColorSpaces/YCbCr.cs +++ b/src/ImageSharp/ColorSpaces/YCbCr.cs @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// /// /// - internal struct YCbCr : IColorVector, IEquatable, IAlmostEquatable + internal readonly struct YCbCr : IColorVector, IEquatable, IAlmostEquatable { /// /// Represents a that has Y, Cb, and Cr values set to zero. diff --git a/src/ImageSharp/Formats/Gif/PackedField.cs b/src/ImageSharp/Formats/Gif/PackedField.cs index 962e2082bf..28a415e2b8 100644 --- a/src/ImageSharp/Formats/Gif/PackedField.cs +++ b/src/ImageSharp/Formats/Gif/PackedField.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Gif /// Represents a byte of data in a GIF data stream which contains a number /// of data items. /// - internal struct PackedField : IEquatable + internal readonly struct PackedField : IEquatable { /// /// The individual bits representing the packed byte. diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/AdobeMarker.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/AdobeMarker.cs index 0ec2297d76..d55e36bd48 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/AdobeMarker.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/AdobeMarker.cs @@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder /// Provides information about the Adobe marker segment. /// /// See the included 5116.DCT.pdf file in the source for more information. - internal struct AdobeMarker : IEquatable + internal readonly struct AdobeMarker : IEquatable { /// /// Gets the length of an adobe marker segment. diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JFifMarker.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JFifMarker.cs index cba7be5539..c856fd04a6 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JFifMarker.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JFifMarker.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder /// Provides information about the JFIF marker segment /// TODO: Thumbnail? /// - internal struct JFifMarker : IEquatable + internal readonly struct JFifMarker : IEquatable { /// /// Gets the length of an JFIF marker segment. diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs index 88599808fc..fd6a7833a7 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs @@ -5,8 +5,6 @@ using System; using System.IO; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.Memory; - namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder { /// diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/HuffmanLut.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/HuffmanLut.cs index 2fb01c5c8c..7756a7e3ba 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/HuffmanLut.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/HuffmanLut.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder /// codeword size in bits and the 24 least significant bits hold the codeword. /// The maximum codeword size is 16 bits. /// - internal struct HuffmanLut + internal readonly struct HuffmanLut { /// /// The compiled representations of theHuffmanSpec. diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/HuffmanSpec.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/HuffmanSpec.cs index 8e40cb3689..1c8228aaa2 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/HuffmanSpec.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/HuffmanSpec.cs @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder /// /// The Huffman encoding specifications. /// - internal struct HuffmanSpec + internal readonly struct HuffmanSpec { #pragma warning disable SA1118 // ParameterMustNotSpanMultipleLines diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsYCbCrToRgbTables.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsYCbCrToRgbTables.cs index ddc577270b..203a7b1eb2 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsYCbCrToRgbTables.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsYCbCrToRgbTables.cs @@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components /// Provides 8-bit lookup tables for converting from YCbCr to Rgb colorspace. /// Methods to build the tables are based on libjpeg implementation. /// - internal struct PdfJsYCbCrToRgbTables + internal readonly struct PdfJsYCbCrToRgbTables { /// /// The red red-chrominance table diff --git a/src/ImageSharp/Memory/BufferArea{T}.cs b/src/ImageSharp/Memory/BufferArea{T}.cs index 588eae483d..990b494fc7 100644 --- a/src/ImageSharp/Memory/BufferArea{T}.cs +++ b/src/ImageSharp/Memory/BufferArea{T}.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Memory /// This type is kind-of 2D Span, but it can live on heap. /// /// The element type - internal struct BufferArea + internal readonly struct BufferArea where T : struct { /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccColorantTableEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccColorantTableEntry.cs index 17127110d6..c5b005ea09 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccColorantTableEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccColorantTableEntry.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc /// /// Entry of ICC colorant table /// - internal struct IccColorantTableEntry : IEquatable + internal readonly struct IccColorantTableEntry : IEquatable { /// /// Initializes a new instance of the struct. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccNamedColor.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccNamedColor.cs index 98107e8281..22916c1344 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccNamedColor.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccNamedColor.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc /// /// A specific color with a name /// - internal struct IccNamedColor : IEquatable + internal readonly struct IccNamedColor : IEquatable { /// /// Initializes a new instance of the struct. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccPositionNumber.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccPositionNumber.cs index 6258ca2f36..d886dc099c 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccPositionNumber.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccPositionNumber.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc /// /// Position of an object within an ICC profile /// - internal struct IccPositionNumber : IEquatable + internal readonly struct IccPositionNumber : IEquatable { /// /// Initializes a new instance of the struct. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileId.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileId.cs index 1f96540df3..4070f835d6 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileId.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileId.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc /// /// ICC Profile ID /// - public struct IccProfileId : IEquatable + public readonly struct IccProfileId : IEquatable { /// /// A profile ID with all values set to zero diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccResponseNumber.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccResponseNumber.cs index a10c55f4e4..c786a0fd45 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccResponseNumber.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccResponseNumber.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc /// /// Associates a normalized device code with a measurement value /// - internal struct IccResponseNumber : IEquatable + internal readonly struct IccResponseNumber : IEquatable { /// /// Initializes a new instance of the struct. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccScreeningChannel.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccScreeningChannel.cs index f41858f303..e1f1bb32fe 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccScreeningChannel.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccScreeningChannel.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc /// /// A single channel of a /// - internal struct IccScreeningChannel : IEquatable + internal readonly struct IccScreeningChannel : IEquatable { /// /// Initializes a new instance of the struct. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccTagTableEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccTagTableEntry.cs index 5464de9c5f..7cb5c7901e 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccTagTableEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccTagTableEntry.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc /// /// Entry of ICC tag table /// - internal struct IccTagTableEntry : IEquatable + internal readonly struct IccTagTableEntry : IEquatable { /// /// Initializes a new instance of the struct. diff --git a/src/ImageSharp/Numerics/ValueSize.cs b/src/ImageSharp/Numerics/ValueSize.cs index 659e0ebfe1..fcf61a586d 100644 --- a/src/ImageSharp/Numerics/ValueSize.cs +++ b/src/ImageSharp/Numerics/ValueSize.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp /// /// Represents a value in relation to a value on the image /// - internal struct ValueSize : IEquatable + internal readonly struct ValueSize : IEquatable { /// /// Initializes a new instance of the struct. diff --git a/src/ImageSharp/Processing/Processors/Dithering/PixelPair.cs b/src/ImageSharp/Processing/Processors/Dithering/PixelPair.cs index e3b9c11bdf..07045bb5ab 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/PixelPair.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/PixelPair.cs @@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Processing.Processors /// Represents a composite pair of pixels. Used for caching color distance lookups. /// /// The pixel format. - internal struct PixelPair : IEquatable> + internal readonly struct PixelPair : IEquatable> where TPixel : struct, IPixel { /// From 7e02d037fb98d9e6c3cfd1a790a860fd121e3a40 Mon Sep 17 00:00:00 2001 From: Jason Nelson Date: Wed, 28 Feb 2018 10:42:24 -0800 Subject: [PATCH 136/151] Work around compiler bug --- .../Common/Decoder/ColorConverters/JpegColorConverter.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.cs index 23c2071cc6..0427289061 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.cs @@ -63,10 +63,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters private static JpegColorConverter GetYCbCrConverter() => FromYCbCrSimdAvx2.IsAvailable ? (JpegColorConverter)new FromYCbCrSimdAvx2() : new FromYCbCrSimd(); + /// /// A stack-only struct to reference the input buffers using -s. /// - public ref struct ComponentValues +#pragma warning disable SA1206 // Declaration keywords should follow order + public readonly ref struct ComponentValues +#pragma warning restore SA1206 // Declaration keywords should follow order { /// /// The component count From f6c55f33f7f28e51b018e282fbe8b6eb49c7212a Mon Sep 17 00:00:00 2001 From: Jason Nelson Date: Wed, 28 Feb 2018 11:38:48 -0800 Subject: [PATCH 137/151] Remove rouge blank line --- .../Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.cs index 0427289061..187b65f72b 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ColorConverters/JpegColorConverter.cs @@ -63,7 +63,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder.ColorConverters private static JpegColorConverter GetYCbCrConverter() => FromYCbCrSimdAvx2.IsAvailable ? (JpegColorConverter)new FromYCbCrSimdAvx2() : new FromYCbCrSimd(); - /// /// A stack-only struct to reference the input buffers using -s. /// From 924c77f9736de8de6bb4271ab3b74a5b44f6ab4b Mon Sep 17 00:00:00 2001 From: Jason Nelson Date: Wed, 28 Feb 2018 17:19:51 -0800 Subject: [PATCH 138/151] Update T4 templates --- .../Generated/PixelOperations{TPixel}.Generated.cs | 2 +- .../Generated/PixelOperations{TPixel}.Generated.tt | 13 ++++++++----- .../Generated/Rgba32.PixelOperations.Generated.cs | 2 +- .../Generated/Rgba32.PixelOperations.Generated.tt | 12 ++++++------ 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.cs b/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.cs index 964fa98f95..9505ee6cf7 100644 --- a/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.cs +++ b/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.cs @@ -7,7 +7,7 @@ namespace SixLabors.ImageSharp.PixelFormats using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; - + public partial class PixelOperations { diff --git a/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.tt b/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.tt index aa88b6606c..e23f196212 100644 --- a/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.tt +++ b/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.tt @@ -4,9 +4,11 @@ #> <#@ template debug="false" hostspecific="false" language="C#" #> <#@ assembly name="System.Core" #> +<#@ assembly name="$(NuGetPackageRoot)/system.memory/4.5.0-preview1-26216-02/lib/netstandard2.0/System.Memory.dll" #> <#@ import namespace="System.Linq" #> <#@ import namespace="System.Text" #> <#@ import namespace="System.Collections.Generic" #> +<#@ import namespace="System.Runtime.InteropServices" #> <#@ output extension=".cs" #> <# void GenerateToDestFormatMethods(string pixelType) @@ -24,8 +26,8 @@ { GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count); - ref TPixel sourceBaseRef = ref sourcePixels.DangerousGetPinnableReference(); - ref <#=pixelType#> destBaseRef = ref dest.DangerousGetPinnableReference(); + ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); + ref <#=pixelType#> destBaseRef = ref MemoryMarshal.GetReference(dest); for (int i = 0; i < count; i++) { @@ -64,8 +66,8 @@ { GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); - ref <#=pixelType#> sourceRef = ref source.DangerousGetPinnableReference(); - ref TPixel destRef = ref destPixels.DangerousGetPinnableReference(); + ref <#=pixelType#> sourceRef = ref MemoryMarshal.GetReference(source); + ref TPixel destRef = ref MemoryMarshal.GetReference(destPixels); Rgba32 rgba = new Rgba32(0, 0, 0, 255); @@ -101,7 +103,8 @@ namespace SixLabors.ImageSharp.PixelFormats { using System; using System.Runtime.CompilerServices; - + using System.Runtime.InteropServices; + public partial class PixelOperations { <# diff --git a/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.cs index 43060539dc..edf6a88e1f 100644 --- a/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.PixelFormats; - + /// /// Provides optimized overrides for bulk operations. /// diff --git a/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.tt b/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.tt index 9d22293947..d83e49f770 100644 --- a/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.tt +++ b/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.tt @@ -18,8 +18,8 @@ { GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); - ref <#=pixelType#> sourceRef = ref source.DangerousGetPinnableReference(); - ref Rgba32 destRef = ref destPixels.DangerousGetPinnableReference(); + ref <#=pixelType#> sourceRef = ref MemoryMarshal.GetReference(source); + ref Rgba32 destRef = ref MemoryMarshal.GetReference(destPixels); for (int i = 0; i < count; i++) { @@ -40,8 +40,8 @@ { GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count); - ref Rgba32 sourceRef = ref sourcePixels.DangerousGetPinnableReference(); - ref <#=pixelType#> destRef = ref dest.DangerousGetPinnableReference(); + ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref <#=pixelType#> destRef = ref MemoryMarshal.GetReference(dest); for (int i = 0; i < count; i++) { @@ -61,9 +61,9 @@ namespace SixLabors.ImageSharp { using System; using System.Runtime.CompilerServices; - + using System.Runtime.InteropServices; using SixLabors.ImageSharp.PixelFormats; - + /// /// Provides optimized overrides for bulk operations. /// From 72e5e51ed80bd2a4f21abb0da9b2428d81be2c6a Mon Sep 17 00:00:00 2001 From: Jason Nelson Date: Wed, 28 Feb 2018 17:24:50 -0800 Subject: [PATCH 139/151] Remove T4 reference --- .../PixelFormats/Generated/PixelOperations{TPixel}.Generated.tt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.tt b/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.tt index e23f196212..365f5cb514 100644 --- a/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.tt +++ b/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.tt @@ -4,7 +4,6 @@ #> <#@ template debug="false" hostspecific="false" language="C#" #> <#@ assembly name="System.Core" #> -<#@ assembly name="$(NuGetPackageRoot)/system.memory/4.5.0-preview1-26216-02/lib/netstandard2.0/System.Memory.dll" #> <#@ import namespace="System.Linq" #> <#@ import namespace="System.Text" #> <#@ import namespace="System.Collections.Generic" #> From 74ee173fc0f69c6a7c5f9f58024d3131361182cd Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 1 Mar 2018 14:52:50 +1100 Subject: [PATCH 140/151] Use Size --- .../DefaultInternalImageProcessorContext.cs | 4 +-- .../IImageProcessingContext{TPixel}.cs | 4 +-- .../Processing/Transforms/Resize.cs | 6 ++-- .../Processing/Transforms/Rotate.cs | 2 +- src/ImageSharp/Processing/Transforms/Skew.cs | 2 +- .../Processing/Transforms/Transform.cs | 4 +-- .../FakeImageOperationsProvider.cs | 4 +-- .../Image/ImageProcessingContextTests.cs | 30 +++++++++---------- 8 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/ImageSharp/DefaultInternalImageProcessorContext.cs b/src/ImageSharp/DefaultInternalImageProcessorContext.cs index 9dbd108e54..c3378cf984 100644 --- a/src/ImageSharp/DefaultInternalImageProcessorContext.cs +++ b/src/ImageSharp/DefaultInternalImageProcessorContext.cs @@ -52,7 +52,7 @@ namespace SixLabors.ImageSharp } /// - public Rectangle Bounds() => this.destination?.Bounds() ?? this.source.Bounds(); + public Size GetCurrentSize() => this.destination?.Size() ?? this.source.Size(); /// public IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle) @@ -78,7 +78,7 @@ namespace SixLabors.ImageSharp /// public IImageProcessingContext ApplyProcessor(IImageProcessor processor) { - return this.ApplyProcessor(processor, this.Bounds()); + return this.ApplyProcessor(processor, this.source.Bounds()); } } } \ No newline at end of file diff --git a/src/ImageSharp/IImageProcessingContext{TPixel}.cs b/src/ImageSharp/IImageProcessingContext{TPixel}.cs index c58dffcecb..0e8efde3b3 100644 --- a/src/ImageSharp/IImageProcessingContext{TPixel}.cs +++ b/src/ImageSharp/IImageProcessingContext{TPixel}.cs @@ -22,10 +22,10 @@ namespace SixLabors.ImageSharp MemoryManager MemoryManager { get; } /// - /// Gets the image bounds at the current point in the processing pipeline. + /// Gets the image dimensions at the current point in the processing pipeline. /// /// The - Rectangle Bounds(); + Size GetCurrentSize(); /// /// Adds the processor to the current set of image operations to be applied. diff --git a/src/ImageSharp/Processing/Transforms/Resize.cs b/src/ImageSharp/Processing/Transforms/Resize.cs index 853f461339..285f3206dc 100644 --- a/src/ImageSharp/Processing/Transforms/Resize.cs +++ b/src/ImageSharp/Processing/Transforms/Resize.cs @@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp /// Passing zero for one of height or width within the resize options will automatically preserve the aspect ratio of the original image public static IImageProcessingContext Resize(this IImageProcessingContext source, ResizeOptions options) where TPixel : struct, IPixel - => source.ApplyProcessor(new ResizeProcessor(options, source.Bounds().Size)); + => source.ApplyProcessor(new ResizeProcessor(options, source.GetCurrentSize())); /// /// Resizes an image to the given . @@ -147,7 +147,7 @@ namespace SixLabors.ImageSharp Rectangle targetRectangle, bool compand) where TPixel : struct, IPixel - => source.ApplyProcessor(new ResizeProcessor(sampler, width, height, source.Bounds().Size, targetRectangle, compand), sourceRectangle); + => source.ApplyProcessor(new ResizeProcessor(sampler, width, height, source.GetCurrentSize(), targetRectangle, compand), sourceRectangle); /// /// Resizes an image to the given width and height with the given sampler and source rectangle. @@ -171,6 +171,6 @@ namespace SixLabors.ImageSharp Rectangle targetRectangle, bool compand) where TPixel : struct, IPixel - => source.ApplyProcessor(new ResizeProcessor(sampler, width, height, source.Bounds().Size, targetRectangle, compand)); + => source.ApplyProcessor(new ResizeProcessor(sampler, width, height, source.GetCurrentSize(), targetRectangle, compand)); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Transforms/Rotate.cs b/src/ImageSharp/Processing/Transforms/Rotate.cs index 6ffefca5cf..faecdf91f9 100644 --- a/src/ImageSharp/Processing/Transforms/Rotate.cs +++ b/src/ImageSharp/Processing/Transforms/Rotate.cs @@ -44,6 +44,6 @@ namespace SixLabors.ImageSharp /// The public static IImageProcessingContext Rotate(this IImageProcessingContext source, float degrees, IResampler sampler) where TPixel : struct, IPixel - => source.ApplyProcessor(new RotateProcessor(degrees, sampler, source.Bounds().Size)); + => source.ApplyProcessor(new RotateProcessor(degrees, sampler, source.GetCurrentSize())); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Transforms/Skew.cs b/src/ImageSharp/Processing/Transforms/Skew.cs index 25db33059a..fb054211a0 100644 --- a/src/ImageSharp/Processing/Transforms/Skew.cs +++ b/src/ImageSharp/Processing/Transforms/Skew.cs @@ -35,6 +35,6 @@ namespace SixLabors.ImageSharp /// The public static IImageProcessingContext Skew(this IImageProcessingContext source, float degreesX, float degreesY, IResampler sampler) where TPixel : struct, IPixel - => source.ApplyProcessor(new SkewProcessor(degreesX, degreesY, sampler, source.Bounds().Size)); + => source.ApplyProcessor(new SkewProcessor(degreesX, degreesY, sampler, source.GetCurrentSize())); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Transforms/Transform.cs b/src/ImageSharp/Processing/Transforms/Transform.cs index 58d3ae13e7..66d5d5de7e 100644 --- a/src/ImageSharp/Processing/Transforms/Transform.cs +++ b/src/ImageSharp/Processing/Transforms/Transform.cs @@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp /// The public static IImageProcessingContext Transform(this IImageProcessingContext source, Matrix3x2 matrix, IResampler sampler) where TPixel : struct, IPixel - => Transform(source, matrix, sampler, source.Bounds()); + => source.ApplyProcessor(new AffineTransformProcessor(matrix, sampler, source.GetCurrentSize())); /// /// Transforms an image by the given matrix using the specified sampling algorithm @@ -103,7 +103,7 @@ namespace SixLabors.ImageSharp /// The internal static IImageProcessingContext Transform(this IImageProcessingContext source, Matrix4x4 matrix, IResampler sampler) where TPixel : struct, IPixel - => Transform(source, matrix, sampler, source.Bounds()); + => source.ApplyProcessor(new ProjectiveTransformProcessor(matrix, sampler, source.GetCurrentSize())); /// /// Applies a projective transform to the image by the given matrix using the specified sampling algorithm. diff --git a/tests/ImageSharp.Tests/FakeImageOperationsProvider.cs b/tests/ImageSharp.Tests/FakeImageOperationsProvider.cs index 58f88eba4f..db1e7903de 100644 --- a/tests/ImageSharp.Tests/FakeImageOperationsProvider.cs +++ b/tests/ImageSharp.Tests/FakeImageOperationsProvider.cs @@ -61,9 +61,9 @@ namespace SixLabors.ImageSharp.Tests return this.Source; } - public Rectangle Bounds() + public Size GetCurrentSize() { - return this.Source.Bounds(); + return this.Source.Size(); } public IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle) diff --git a/tests/ImageSharp.Tests/Image/ImageProcessingContextTests.cs b/tests/ImageSharp.Tests/Image/ImageProcessingContextTests.cs index 8411dd2f01..f8f7b6758a 100644 --- a/tests/ImageSharp.Tests/Image/ImageProcessingContextTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageProcessingContextTests.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Tests public class ImageProcessingContextTests { [Fact] - public void MutatedBoundsAreAccuratePerOperation() + public void MutatedSizeIsAccuratePerOperation() { var x500 = new Size(500, 500); var x400 = new Size(400, 400); @@ -19,16 +19,16 @@ namespace SixLabors.ImageSharp.Tests using (var image = new Image(500, 500)) { image.Mutate(x => - x.AssertBounds(x500) - .Resize(x400).AssertBounds(x400) - .Resize(x300).AssertBounds(x300) - .Resize(x200).AssertBounds(x200) - .Resize(x100).AssertBounds(x100)); + x.AssertSize(x500) + .Resize(x400).AssertSize(x400) + .Resize(x300).AssertSize(x300) + .Resize(x200).AssertSize(x200) + .Resize(x100).AssertSize(x100)); } } [Fact] - public void ClonedBoundsAreAccuratePerOperation() + public void ClonedSizeIsAccuratePerOperation() { var x500 = new Size(500, 500); var x400 = new Size(400, 400); @@ -38,20 +38,20 @@ namespace SixLabors.ImageSharp.Tests using (var image = new Image(500, 500)) { image.Clone(x => - x.AssertBounds(x500) - .Resize(x400).AssertBounds(x400) - .Resize(x300).AssertBounds(x300) - .Resize(x200).AssertBounds(x200) - .Resize(x100).AssertBounds(x100)); + x.AssertSize(x500) + .Resize(x400).AssertSize(x400) + .Resize(x300).AssertSize(x300) + .Resize(x200).AssertSize(x200) + .Resize(x100).AssertSize(x100)); } } } - public static class BoundsAssertationExtensions + public static class SizeAssertationExtensions { - public static IImageProcessingContext AssertBounds(this IImageProcessingContext context, Size size) + public static IImageProcessingContext AssertSize(this IImageProcessingContext context, Size size) { - Assert.Equal(size, context.Bounds().Size); + Assert.Equal(size, context.GetCurrentSize()); return context; } } From c483cb5a53025cc801fe3e949537987ed01cf7c6 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 1 Mar 2018 15:03:28 +1100 Subject: [PATCH 141/151] Use Current as Default, Don't additionally throw --- src/ImageSharp/Configuration.cs | 4 +- src/ImageSharp/Image/Image.FromStream.cs | 59 +++++--------------- tests/ImageSharp.Tests/ConfigurationTests.cs | 4 +- 3 files changed, 19 insertions(+), 48 deletions(-) diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index 7b02f21c46..34bbb61e0b 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -84,9 +84,9 @@ namespace SixLabors.ImageSharp public IEnumerable ImageFormats => this.imageFormats; /// - /// Gets or sets the position in a stream to use for reading. + /// Gets or sets the position in a stream to use for reading when using a seekable stream as an image data source. /// - public ReadOrigin ReadOrigin { get; set; } = ReadOrigin.Begin; + public ReadOrigin ReadOrigin { get; set; } = ReadOrigin.Current; /// /// Gets or sets the that is currently in use. diff --git a/src/ImageSharp/Image/Image.FromStream.cs b/src/ImageSharp/Image/Image.FromStream.cs index a0c71133c8..c3f4a714f8 100644 --- a/src/ImageSharp/Image/Image.FromStream.cs +++ b/src/ImageSharp/Image/Image.FromStream.cs @@ -19,6 +19,7 @@ namespace SixLabors.ImageSharp /// By reading the header on the provided stream this calculates the images mime type. /// /// The image stream to read the header from. + /// Thrown if the stream is not readable. /// The mime type or null if none found. public static IImageFormat DetectFormat(Stream stream) => DetectFormat(Configuration.Default, stream); @@ -27,6 +28,7 @@ namespace SixLabors.ImageSharp /// /// The configuration. /// The image stream to read the header from. + /// Thrown if the stream is not readable. /// The mime type or null if none found. public static IImageFormat DetectFormat(Configuration config, Stream stream) => WithSeekableStream(config, stream, s => InternalDetectFormat(s, config)); @@ -35,9 +37,7 @@ namespace SixLabors.ImageSharp /// By reading the header on the provided stream this reads the raw image information. /// /// The image stream to read the header from. - /// - /// Thrown if the stream is not readable nor seekable. - /// + /// Thrown if the stream is not readable. /// /// The or null if suitable info detector not found. /// @@ -60,9 +60,7 @@ namespace SixLabors.ImageSharp /// /// The stream containing image information. /// the mime type of the decoded image. - /// - /// Thrown if the stream is not readable nor seekable. - /// + /// Thrown if the stream is not readable. /// A new .> public static Image Load(Stream stream, out IImageFormat format) => Load(stream, out format); @@ -70,9 +68,7 @@ namespace SixLabors.ImageSharp /// Create a new instance of the class from the given stream. /// /// The stream containing image information. - /// - /// Thrown if the stream is not readable nor seekable. - /// + /// Thrown if the stream is not readable. /// A new .> public static Image Load(Stream stream) => Load(stream); @@ -81,9 +77,7 @@ namespace SixLabors.ImageSharp /// /// The stream containing image information. /// The decoder. - /// - /// Thrown if the stream is not readable nor seekable. - /// + /// Thrown if the stream is not readable. /// A new .> public static Image Load(Stream stream, IImageDecoder decoder) => Load(stream, decoder); @@ -92,9 +86,7 @@ namespace SixLabors.ImageSharp /// /// The config for the decoder. /// The stream containing image information. - /// - /// Thrown if the stream is not readable nor seekable. - /// + /// Thrown if the stream is not readable. /// A new .> public static Image Load(Configuration config, Stream stream) => Load(config, stream); @@ -104,9 +96,7 @@ namespace SixLabors.ImageSharp /// The config for the decoder. /// The stream containing image information. /// the mime type of the decoded image. - /// - /// Thrown if the stream is not readable nor seekable. - /// + /// Thrown if the stream is not readable. /// A new .> public static Image Load(Configuration config, Stream stream, out IImageFormat format) => Load(config, stream, out format); @@ -115,9 +105,7 @@ namespace SixLabors.ImageSharp /// Create a new instance of the class from the given stream. /// /// The stream containing image information. - /// - /// Thrown if the stream is not readable nor seekable. - /// + /// Thrown if the stream is not readable. /// The pixel format. /// A new .> public static Image Load(Stream stream) @@ -129,9 +117,7 @@ namespace SixLabors.ImageSharp /// /// The stream containing image information. /// the mime type of the decoded image. - /// - /// Thrown if the stream is not readable nor seekable. - /// + /// Thrown if the stream is not readable. /// The pixel format. /// A new .> public static Image Load(Stream stream, out IImageFormat format) @@ -143,9 +129,7 @@ namespace SixLabors.ImageSharp /// /// The stream containing image information. /// The decoder. - /// - /// Thrown if the stream is not readable nor seekable. - /// + /// Thrown if the stream is not readable. /// The pixel format. /// A new .> public static Image Load(Stream stream, IImageDecoder decoder) @@ -158,9 +142,7 @@ namespace SixLabors.ImageSharp /// The Configuration. /// The stream containing image information. /// The decoder. - /// - /// Thrown if the stream is not readable nor seekable. - /// + /// Thrown if the stream is not readable. /// The pixel format. /// A new .> public static Image Load(Configuration config, Stream stream, IImageDecoder decoder) @@ -172,9 +154,7 @@ namespace SixLabors.ImageSharp /// /// The configuration options. /// The stream containing image information. - /// - /// Thrown if the stream is not readable nor seekable. - /// + /// Thrown if the stream is not readable. /// The pixel format. /// A new .> public static Image Load(Configuration config, Stream stream) @@ -187,9 +167,7 @@ namespace SixLabors.ImageSharp /// The configuration options. /// The stream containing image information. /// the mime type of the decoded image. - /// - /// Thrown if the stream is not readable nor seekable. - /// + /// Thrown if the stream is not readable. /// The pixel format. /// A new .> public static Image Load(Configuration config, Stream stream, out IImageFormat format) @@ -203,9 +181,7 @@ namespace SixLabors.ImageSharp /// The stream containing image information. /// The position in the stream to use for reading. /// the mime type of the decoded image. - /// - /// Thrown if the stream is not readable nor seekable. - /// + /// Thrown if the stream is not readable. /// The pixel format. /// A new .> public static Image Load(Configuration config, Stream stream, ReadOrigin origin, out IImageFormat format) @@ -249,11 +225,6 @@ namespace SixLabors.ImageSharp return action(stream); } - if (config.ReadOrigin == ReadOrigin.Current) - { - throw new NotSupportedException("Cannot seek within the stream."); - } - // We want to be able to load images from things like HttpContext.Request.Body using (var memoryStream = new MemoryStream()) { diff --git a/tests/ImageSharp.Tests/ConfigurationTests.cs b/tests/ImageSharp.Tests/ConfigurationTests.cs index fc97b22090..06f02fcf16 100644 --- a/tests/ImageSharp.Tests/ConfigurationTests.cs +++ b/tests/ImageSharp.Tests/ConfigurationTests.cs @@ -63,9 +63,9 @@ namespace SixLabors.ImageSharp.Tests /// Test that the default configuration read origin options is set to begin. /// [Fact] - public void TestDefultConfigurationReadOriginIsBegin() + public void TestDefultConfigurationReadOriginIsCurrent() { - Assert.True(Configuration.Default.ReadOrigin == ReadOrigin.Begin); + Assert.True(Configuration.Default.ReadOrigin == ReadOrigin.Current); } /// From 7ffa084cbb49cf449d74b99d2c514747784b480d Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 1 Mar 2018 15:17:35 +1100 Subject: [PATCH 142/151] Fix bounds method --- src/ImageSharp/DefaultInternalImageProcessorContext.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/DefaultInternalImageProcessorContext.cs b/src/ImageSharp/DefaultInternalImageProcessorContext.cs index c3378cf984..7ccc65e27e 100644 --- a/src/ImageSharp/DefaultInternalImageProcessorContext.cs +++ b/src/ImageSharp/DefaultInternalImageProcessorContext.cs @@ -52,7 +52,7 @@ namespace SixLabors.ImageSharp } /// - public Size GetCurrentSize() => this.destination?.Size() ?? this.source.Size(); + public Size GetCurrentSize() => this.GetCurrentBounds().Size; /// public IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle) @@ -78,7 +78,12 @@ namespace SixLabors.ImageSharp /// public IImageProcessingContext ApplyProcessor(IImageProcessor processor) { - return this.ApplyProcessor(processor, this.source.Bounds()); + return this.ApplyProcessor(processor, this.GetCurrentBounds()); + } + + private Rectangle GetCurrentBounds() + { + return this.destination?.Bounds() ?? this.source.Bounds(); } } } \ No newline at end of file From 7e8e78070f19acaf3e674c857dab89257b75cbb1 Mon Sep 17 00:00:00 2001 From: Vicente Penades Date: Thu, 1 Mar 2018 09:39:05 +0100 Subject: [PATCH 143/151] Added a convenience copy constructor. --- src/ImageSharp/Configuration.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index 0f69194f2d..771e2007fd 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -33,6 +33,18 @@ namespace SixLabors.ImageSharp { } + /// + /// Initializes a new instance of the class. + /// + /// A configuration instance to be copied + public Configuration(Configuration configuration) + { + this.ParallelOptions = configuration.ParallelOptions; + this.ImageFormatsManager = configuration.ImageFormatsManager; + this.MemoryManager = configuration.MemoryManager; + this.ImageOperationsProvider = configuration.ImageOperationsProvider; + } + /// /// Initializes a new instance of the class. /// From 450a85387c305291bff136ff8d20854939d4bc1f Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 1 Mar 2018 20:33:03 +1100 Subject: [PATCH 144/151] Remove unneeded overload. --- src/ImageSharp/Image/Image.FromStream.cs | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/ImageSharp/Image/Image.FromStream.cs b/src/ImageSharp/Image/Image.FromStream.cs index c3f4a714f8..4e294260aa 100644 --- a/src/ImageSharp/Image/Image.FromStream.cs +++ b/src/ImageSharp/Image/Image.FromStream.cs @@ -172,20 +172,6 @@ namespace SixLabors.ImageSharp /// A new .> public static Image Load(Configuration config, Stream stream, out IImageFormat format) where TPixel : struct, IPixel - => Load(config, stream, ReadOrigin.Begin, out format); - - /// - /// Create a new instance of the class from the given stream. - /// - /// The configuration options. - /// The stream containing image information. - /// The position in the stream to use for reading. - /// the mime type of the decoded image. - /// Thrown if the stream is not readable. - /// The pixel format. - /// A new .> - public static Image Load(Configuration config, Stream stream, ReadOrigin origin, out IImageFormat format) - where TPixel : struct, IPixel { config = config ?? Configuration.Default; (Image img, IImageFormat format) data = WithSeekableStream(config, stream, s => Decode(s, config)); From 53f40b0f396278bee43ed3541566179c70d9944c Mon Sep 17 00:00:00 2001 From: Vicente Penades Date: Thu, 1 Mar 2018 13:33:10 +0100 Subject: [PATCH 145/151] added missing property in copy constructor. --- src/ImageSharp/Configuration.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index 771e2007fd..afa63dacbf 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -43,6 +43,10 @@ namespace SixLabors.ImageSharp this.ImageFormatsManager = configuration.ImageFormatsManager; this.MemoryManager = configuration.MemoryManager; this.ImageOperationsProvider = configuration.ImageOperationsProvider; + + #if !NETSTANDARD1_1 + this.FileSystem = configuration.FileSystem; + #endif } /// From 2cb62b84d90bad5c1ff9fd33b4c4e00916bf0ace Mon Sep 17 00:00:00 2001 From: Vicente Penades Date: Thu, 1 Mar 2018 15:43:36 +0100 Subject: [PATCH 146/151] whitespaces removed --- src/ImageSharp/Configuration.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index b0e414c2fe..5912ac6309 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp #if !NETSTANDARD1_1 this.FileSystem = configuration.FileSystem; #endif - + this.ReadOrigin = configuration.ReadOrigin; } @@ -80,7 +80,7 @@ namespace SixLabors.ImageSharp /// Gets the currently registered s. /// public IEnumerable ImageFormats => this.ImageFormatsManager.ImageFormats; - + /// /// Gets or sets the position in a stream to use for reading when using a seekable stream as an image data source. /// From 37d8c479237182372551b4a97f63d7c2e18f25dc Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 2 Mar 2018 17:19:51 +1100 Subject: [PATCH 147/151] Can now read padded RSTn markers. Fix #481 --- .../Components/Decoder/OrigJpegScanDecoder.cs | 28 ++++++++++++++++--- .../PdfJsPort/Components/PdfJsScanDecoder.cs | 2 +- .../Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs | 2 +- .../Formats/Jpg/JpegDecoderTests.cs | 6 ++-- tests/ImageSharp.Tests/TestImages.cs | 1 + tests/Images/External | 2 +- tests/Images/Input/Jpg/baseline/badrst.jpg | 3 ++ 7 files changed, 35 insertions(+), 9 deletions(-) create mode 100644 tests/Images/Input/Jpg/baseline/badrst.jpg diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.cs index 67abba9f33..7d58d168a6 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.cs @@ -197,16 +197,36 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder if (decoder.RestartInterval > 0 && mcu % decoder.RestartInterval == 0 && mcu < decoder.TotalMCUCount) { - // A more sophisticated decoder could use RST[0-7] markers to resynchronize from corrupt input, - // but this one assumes well-formed input, and hence the restart marker follows immediately. + // Attempt to look for RST[0-7] markers to resynchronize from corrupt input. if (!decoder.InputProcessor.ReachedEOF) { decoder.InputProcessor.ReadFullUnsafe(decoder.Temp, 0, 2); if (decoder.InputProcessor.CheckEOFEnsureNoError()) { - if (decoder.Temp[0] != 0xff || decoder.Temp[1] != expectedRst) + if (decoder.Temp[0] != 0xFF || decoder.Temp[1] != expectedRst) { - throw new ImageFormatException("Bad RST marker"); + bool invalidRst = true; + + // Most jpeg's containing well-formed input will have a RST[0-7] marker following immediately + // but some, see Issue #481, contain padding bytes "0xFF" before the RST[0-7] marker. + // If we identify that case we attempt to read until we have bypassed the padded bytes. + // We then check again for our RST marker and throw if invalid. + // No other methods are attempted to resynchronize from corrupt input. + if (decoder.Temp[0] == 0xFF && decoder.Temp[1] == 0xFF) + { + while (decoder.Temp[0] == 0xFF && decoder.InputProcessor.CheckEOFEnsureNoError()) + { + decoder.InputProcessor.ReadFullUnsafe(decoder.Temp, 0, 1); + } + + // Have we found a valid restart marker? + invalidRst = decoder.Temp[0] != expectedRst; + } + + if (invalidRst) + { + throw new ImageFormatException("Bad RST marker"); + } } expectedRst++; diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsScanDecoder.cs index 9e245ea2c6..c6f6ac270f 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsScanDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsScanDecoder.cs @@ -154,7 +154,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components ushort marker = fileMarker.Marker; - // RSTn - We've alread read the bytes and altered the position so no need to skip + // RSTn - We've already read the bytes and altered the position so no need to skip if (marker >= PdfJsJpegConstants.Markers.RST0 && marker <= PdfJsJpegConstants.Markers.RST7) { continue; diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs index 4fa0bc281d..54e2833b11 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs @@ -137,7 +137,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort return new PdfJsFileMarker(PdfJsJpegConstants.Markers.EOI, (int)stream.Length - 2); } - marker[1] = (byte)value; + marker[1] = (byte)suffix; } return new PdfJsFileMarker((ushort)((marker[0] << 8) | marker[1]), (int)(stream.Position - 2)); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index 139fa351bb..95ee40e807 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -42,7 +42,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg TestImages.Jpeg.Baseline.Jpeg444, TestImages.Jpeg.Baseline.Bad.BadEOF, TestImages.Jpeg.Issues.MultiHuffmanBaseline394, - TestImages.Jpeg.Baseline.MultiScanBaselineCMYK + TestImages.Jpeg.Baseline.MultiScanBaselineCMYK, + TestImages.Jpeg.Baseline.Bad.BadRST }; public static string[] ProgressiveTestJpegs = @@ -61,6 +62,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [TestImages.Jpeg.Baseline.Calliphora] = 0.00002f / 100, [TestImages.Jpeg.Baseline.Bad.BadEOF] = 0.38f / 100, [TestImages.Jpeg.Baseline.Testorig420] = 0.38f / 100, + [TestImages.Jpeg.Baseline.Bad.BadRST] = 0.0589f / 100, // Progressive: [TestImages.Jpeg.Issues.MissingFF00ProgressiveGirl159] = 0.34f / 100, @@ -119,7 +121,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg } public const string DecodeBaselineJpegOutputName = "DecodeBaselineJpeg"; - + [Theory] [WithFile(TestImages.Jpeg.Baseline.Calliphora, CommonNonDefaultPixelTypes, false)] [WithFile(TestImages.Jpeg.Baseline.Calliphora, CommonNonDefaultPixelTypes, true)] diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index f1f989581f..db469f87e1 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -96,6 +96,7 @@ namespace SixLabors.ImageSharp.Tests public static class Bad { public const string BadEOF = "Jpg/baseline/badeof.jpg"; + public const string BadRST = "Jpg/baseline/badrst.jpg"; } public const string Cmyk = "Jpg/baseline/cmyk.jpg"; diff --git a/tests/Images/External b/tests/Images/External index 8714b94dc4..653f0c7e3c 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 8714b94dc4bab6788fcbb6254174db2b9c8f69c9 +Subproject commit 653f0c7e3c84657f68dd46e5a380186b3696b956 diff --git a/tests/Images/Input/Jpg/baseline/badrst.jpg b/tests/Images/Input/Jpg/baseline/badrst.jpg new file mode 100644 index 0000000000..61805b42d3 --- /dev/null +++ b/tests/Images/Input/Jpg/baseline/badrst.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:af18f0bf30231d2c4c0e6b80d4636237c2851f67763788de12931fd1960c4ff3 +size 74497 From 1ce9342d229b6fb779c83c369fe6dc91944ae923 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 2 Mar 2018 17:32:29 +1100 Subject: [PATCH 148/151] Check and ensure no error --- .../Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.cs index 7d58d168a6..2e1372c564 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.cs @@ -217,6 +217,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder while (decoder.Temp[0] == 0xFF && decoder.InputProcessor.CheckEOFEnsureNoError()) { decoder.InputProcessor.ReadFullUnsafe(decoder.Temp, 0, 1); + if (!decoder.InputProcessor.CheckEOFEnsureNoError()) + { + break; + } } // Have we found a valid restart marker? From ce62a947d9b6a362bcc252c9aa0080a5402d7399 Mon Sep 17 00:00:00 2001 From: Vicente Penades Date: Fri, 2 Mar 2018 10:00:41 +0100 Subject: [PATCH 149/151] replaced Configuration copy constructor with a ShallowCopy method. --- src/ImageSharp/Configuration.cs | 42 +++++++++++--------- tests/ImageSharp.Tests/ConfigurationTests.cs | 6 ++- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index 5912ac6309..8d3811a974 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -33,24 +33,6 @@ namespace SixLabors.ImageSharp { } - /// - /// Initializes a new instance of the class. - /// - /// A configuration instance to be copied - public Configuration(Configuration configuration) - { - this.ParallelOptions = configuration.ParallelOptions; - this.ImageFormatsManager = configuration.ImageFormatsManager; - this.MemoryManager = configuration.MemoryManager; - this.ImageOperationsProvider = configuration.ImageOperationsProvider; - - #if !NETSTANDARD1_1 - this.FileSystem = configuration.FileSystem; - #endif - - this.ReadOrigin = configuration.ReadOrigin; - } - /// /// Initializes a new instance of the class. /// @@ -74,7 +56,7 @@ namespace SixLabors.ImageSharp /// /// Gets the global parallel options for processing tasks in parallel. /// - public ParallelOptions ParallelOptions { get; } = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount }; + public ParallelOptions ParallelOptions { get; private set; } = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount }; /// /// Gets the currently registered s. @@ -138,6 +120,28 @@ namespace SixLabors.ImageSharp configuration.Configure(this); } + /// + /// Creates a shallow copy of the + /// + /// A new configuration instance + public Configuration ShallowCopy() + { + Configuration cfg = new Configuration(); + + cfg.ParallelOptions = this.ParallelOptions; + cfg.ImageFormatsManager = this.ImageFormatsManager; + cfg.MemoryManager = this.MemoryManager; + cfg.ImageOperationsProvider = this.ImageOperationsProvider; + +#if !NETSTANDARD1_1 + cfg.FileSystem = this.FileSystem; +#endif + + cfg.ReadOrigin = this.ReadOrigin; + + return cfg; + } + /// /// Registers a new format provider. /// diff --git a/tests/ImageSharp.Tests/ConfigurationTests.cs b/tests/ImageSharp.Tests/ConfigurationTests.cs index 06f02fcf16..d38556fa9e 100644 --- a/tests/ImageSharp.Tests/ConfigurationTests.cs +++ b/tests/ImageSharp.Tests/ConfigurationTests.cs @@ -22,8 +22,10 @@ namespace SixLabors.ImageSharp.Tests public Configuration DefaultConfiguration { get; private set; } public ConfigurationTests() - { - this.DefaultConfiguration = Configuration.CreateDefaultInstance(); + { + // the shallow copy of configuration should behave exactly like the default configuration, + // so by using the copy, we test both the default and the copy. + this.DefaultConfiguration = Configuration.CreateDefaultInstance().ShallowCopy(); this.ConfigurationEmpty = new Configuration(); } From 1f2b2b8a7a82e6c970ed43a9708af196a88088c7 Mon Sep 17 00:00:00 2001 From: Vicente Penades Date: Fri, 2 Mar 2018 10:15:42 +0100 Subject: [PATCH 150/151] removed ImageFormat redirect methods from Configuration. --- src/ImageSharp/Configuration.cs | 121 ++---------------- .../Formats/Bmp/BmpConfigurationModule.cs | 6 +- src/ImageSharp/Formats/Bmp/ImageExtensions.cs | 2 +- .../Formats/Gif/GifConfigurationModule.cs | 6 +- src/ImageSharp/Formats/Gif/ImageExtensions.cs | 2 +- .../Formats/Jpeg/ImageExtensions.cs | 2 +- .../Formats/Jpeg/JpegConfigurationModule.cs | 6 +- src/ImageSharp/Formats/Png/ImageExtensions.cs | 2 +- .../Formats/Png/PngConfigurationModule.cs | 6 +- src/ImageSharp/Image/Image.Decode.cs | 4 +- src/ImageSharp/Image/Image.FromStream.cs | 2 +- src/ImageSharp/Image/ImageExtensions.cs | 10 +- tests/ImageSharp.Tests/ConfigurationTests.cs | 83 +----------- .../Image/ImageDiscoverMimeType.cs | 2 +- .../ImageSharp.Tests/Image/ImageLoadTests.cs | 4 +- .../ImageSharp.Tests/Image/ImageSaveTests.cs | 4 +- tests/ImageSharp.Tests/TestFormat.cs | 6 +- .../TestUtilities/TestEnvironment.Formats.cs | 12 +- .../TestUtilities/TestImageExtensions.cs | 2 +- 19 files changed, 49 insertions(+), 233 deletions(-) diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index 8d3811a974..d41e48678b 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -83,21 +83,6 @@ namespace SixLabors.ImageSharp /// internal int MaxHeaderSize => this.ImageFormatsManager.MaxHeaderSize; - /// - /// Gets the currently registered s. - /// - internal IEnumerable FormatDetectors => this.ImageFormatsManager.FormatDetectors; - - /// - /// Gets the currently registered s. - /// - internal IEnumerable> ImageDecoders => this.ImageFormatsManager.ImageDecoders; - - /// - /// Gets the currently registered s. - /// - internal IEnumerable> ImageEncoders => this.ImageFormatsManager.ImageEncoders; - #if !NETSTANDARD1_1 /// /// Gets or sets the filesystem helper for accessing the local file system. @@ -126,106 +111,18 @@ namespace SixLabors.ImageSharp /// A new configuration instance public Configuration ShallowCopy() { - Configuration cfg = new Configuration(); - - cfg.ParallelOptions = this.ParallelOptions; - cfg.ImageFormatsManager = this.ImageFormatsManager; - cfg.MemoryManager = this.MemoryManager; - cfg.ImageOperationsProvider = this.ImageOperationsProvider; + return new Configuration + { + ParallelOptions = this.ParallelOptions, + ImageFormatsManager = this.ImageFormatsManager, + MemoryManager = this.MemoryManager, + ImageOperationsProvider = this.ImageOperationsProvider, + ReadOrigin = this.ReadOrigin, #if !NETSTANDARD1_1 - cfg.FileSystem = this.FileSystem; + FileSystem = this.FileSystem #endif - - cfg.ReadOrigin = this.ReadOrigin; - - return cfg; - } - - /// - /// Registers a new format provider. - /// - /// The format to register as a known format. - public void AddImageFormat(IImageFormat format) - { - this.ImageFormatsManager.AddImageFormat(format); - } - - /// - /// For the specified file extensions type find the e . - /// - /// The extension to discover - /// The if found otherwise null - public IImageFormat FindFormatByFileExtension(string extension) - { - return this.ImageFormatsManager.FindFormatByFileExtension(extension); - } - - /// - /// For the specified mime type find the . - /// - /// The mime-type to discover - /// The if found; otherwise null - public IImageFormat FindFormatByMimeType(string mimeType) - { - return this.ImageFormatsManager.FindFormatByMimeType(mimeType); - } - - /// - /// Sets a specific image encoder as the encoder for a specific image format. - /// - /// The image format to register the encoder for. - /// The encoder to use, - public void SetEncoder(IImageFormat imageFormat, IImageEncoder encoder) - { - this.ImageFormatsManager.SetEncoder(imageFormat, encoder); - } - - /// - /// Sets a specific image decoder as the decoder for a specific image format. - /// - /// The image format to register the encoder for. - /// The decoder to use, - public void SetDecoder(IImageFormat imageFormat, IImageDecoder decoder) - { - this.ImageFormatsManager.SetDecoder(imageFormat, decoder); - } - - /// - /// Removes all the registered image format detectors. - /// - public void ClearImageFormatDetectors() - { - this.ImageFormatsManager.ClearImageFormatDetectors(); - } - - /// - /// Adds a new detector for detecting mime types. - /// - /// The detector to add - public void AddImageFormatDetector(IImageFormatDetector detector) - { - this.ImageFormatsManager.AddImageFormatDetector(detector); - } - - /// - /// For the specified mime type find the decoder. - /// - /// The format to discover - /// The if found otherwise null - public IImageDecoder FindDecoder(IImageFormat format) - { - return this.ImageFormatsManager.FindDecoder(format); - } - - /// - /// For the specified mime type find the encoder. - /// - /// The format to discover - /// The if found otherwise null - public IImageEncoder FindEncoder(IImageFormat format) - { - return this.ImageFormatsManager.FindEncoder(format); + }; } /// diff --git a/src/ImageSharp/Formats/Bmp/BmpConfigurationModule.cs b/src/ImageSharp/Formats/Bmp/BmpConfigurationModule.cs index b091467bf5..956acc1578 100644 --- a/src/ImageSharp/Formats/Bmp/BmpConfigurationModule.cs +++ b/src/ImageSharp/Formats/Bmp/BmpConfigurationModule.cs @@ -11,9 +11,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// public void Configure(Configuration config) { - config.SetEncoder(ImageFormats.Bmp, new BmpEncoder()); - config.SetDecoder(ImageFormats.Bmp, new BmpDecoder()); - config.AddImageFormatDetector(new BmpImageFormatDetector()); + config.ImageFormatsManager.SetEncoder(ImageFormats.Bmp, new BmpEncoder()); + config.ImageFormatsManager.SetDecoder(ImageFormats.Bmp, new BmpDecoder()); + config.ImageFormatsManager.AddImageFormatDetector(new BmpImageFormatDetector()); } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Bmp/ImageExtensions.cs b/src/ImageSharp/Formats/Bmp/ImageExtensions.cs index 935ce8f4ad..35e168e278 100644 --- a/src/ImageSharp/Formats/Bmp/ImageExtensions.cs +++ b/src/ImageSharp/Formats/Bmp/ImageExtensions.cs @@ -36,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.GetConfiguration().FindEncoder(ImageFormats.Bmp)); + => source.Save(stream, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(ImageFormats.Bmp)); } } diff --git a/src/ImageSharp/Formats/Gif/GifConfigurationModule.cs b/src/ImageSharp/Formats/Gif/GifConfigurationModule.cs index 4c42a833c0..0bb62779eb 100644 --- a/src/ImageSharp/Formats/Gif/GifConfigurationModule.cs +++ b/src/ImageSharp/Formats/Gif/GifConfigurationModule.cs @@ -11,10 +11,10 @@ namespace SixLabors.ImageSharp.Formats.Gif /// public void Configure(Configuration config) { - config.SetEncoder(ImageFormats.Gif, new GifEncoder()); - config.SetDecoder(ImageFormats.Gif, new GifDecoder()); + config.ImageFormatsManager.SetEncoder(ImageFormats.Gif, new GifEncoder()); + config.ImageFormatsManager.SetDecoder(ImageFormats.Gif, new GifDecoder()); - config.AddImageFormatDetector(new GifImageFormatDetector()); + config.ImageFormatsManager.AddImageFormatDetector(new GifImageFormatDetector()); } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Gif/ImageExtensions.cs b/src/ImageSharp/Formats/Gif/ImageExtensions.cs index 939eb456e1..78acadb4b1 100644 --- a/src/ImageSharp/Formats/Gif/ImageExtensions.cs +++ b/src/ImageSharp/Formats/Gif/ImageExtensions.cs @@ -36,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.GetConfiguration().FindEncoder(ImageFormats.Gif)); + => source.Save(stream, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(ImageFormats.Gif)); } } diff --git a/src/ImageSharp/Formats/Jpeg/ImageExtensions.cs b/src/ImageSharp/Formats/Jpeg/ImageExtensions.cs index 9cd7b3a8bd..d3f95e40c0 100644 --- a/src/ImageSharp/Formats/Jpeg/ImageExtensions.cs +++ b/src/ImageSharp/Formats/Jpeg/ImageExtensions.cs @@ -36,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.GetConfiguration().FindEncoder(ImageFormats.Jpeg)); + => source.Save(stream, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(ImageFormats.Jpeg)); } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegConfigurationModule.cs b/src/ImageSharp/Formats/Jpeg/JpegConfigurationModule.cs index 1ab5093398..23cef59273 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegConfigurationModule.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegConfigurationModule.cs @@ -11,10 +11,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// public void Configure(Configuration config) { - config.SetEncoder(ImageFormats.Jpeg, new JpegEncoder()); - config.SetDecoder(ImageFormats.Jpeg, new JpegDecoder()); + config.ImageFormatsManager.SetEncoder(ImageFormats.Jpeg, new JpegEncoder()); + config.ImageFormatsManager.SetDecoder(ImageFormats.Jpeg, new JpegDecoder()); - config.AddImageFormatDetector(new JpegImageFormatDetector()); + config.ImageFormatsManager.AddImageFormatDetector(new JpegImageFormatDetector()); } } } diff --git a/src/ImageSharp/Formats/Png/ImageExtensions.cs b/src/ImageSharp/Formats/Png/ImageExtensions.cs index 10970fc16a..f25d2bffe2 100644 --- a/src/ImageSharp/Formats/Png/ImageExtensions.cs +++ b/src/ImageSharp/Formats/Png/ImageExtensions.cs @@ -35,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.GetConfiguration().FindEncoder(ImageFormats.Png)); + => source.Save(stream, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(ImageFormats.Png)); } } diff --git a/src/ImageSharp/Formats/Png/PngConfigurationModule.cs b/src/ImageSharp/Formats/Png/PngConfigurationModule.cs index ab6f31d49a..0036280a83 100644 --- a/src/ImageSharp/Formats/Png/PngConfigurationModule.cs +++ b/src/ImageSharp/Formats/Png/PngConfigurationModule.cs @@ -11,9 +11,9 @@ namespace SixLabors.ImageSharp.Formats.Png /// public void Configure(Configuration config) { - config.SetEncoder(ImageFormats.Png, new PngEncoder()); - config.SetDecoder(ImageFormats.Png, new PngDecoder()); - config.AddImageFormatDetector(new PngImageFormatDetector()); + config.ImageFormatsManager.SetEncoder(ImageFormats.Png, new PngEncoder()); + config.ImageFormatsManager.SetDecoder(ImageFormats.Png, new PngDecoder()); + config.ImageFormatsManager.AddImageFormatDetector(new PngImageFormatDetector()); } } } \ No newline at end of file diff --git a/src/ImageSharp/Image/Image.Decode.cs b/src/ImageSharp/Image/Image.Decode.cs index 72492a494b..6b44c893bc 100644 --- a/src/ImageSharp/Image/Image.Decode.cs +++ b/src/ImageSharp/Image/Image.Decode.cs @@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp long startPosition = stream.Position; stream.Read(buffer.Array, 0, maxHeaderSize); stream.Position = startPosition; - return config.FormatDetectors.Select(x => x.DetectFormat(buffer.Span)).LastOrDefault(x => x != null); + return config.ImageFormatsManager.FormatDetectors.Select(x => x.DetectFormat(buffer.Span)).LastOrDefault(x => x != null); } } @@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp format = InternalDetectFormat(stream, config); if (format != null) { - return config.FindDecoder(format); + return config.ImageFormatsManager.FindDecoder(format); } return null; diff --git a/src/ImageSharp/Image/Image.FromStream.cs b/src/ImageSharp/Image/Image.FromStream.cs index 4e294260aa..9061c334dc 100644 --- a/src/ImageSharp/Image/Image.FromStream.cs +++ b/src/ImageSharp/Image/Image.FromStream.cs @@ -186,7 +186,7 @@ namespace SixLabors.ImageSharp var stringBuilder = new StringBuilder(); stringBuilder.AppendLine("Image cannot be loaded. Available decoders:"); - foreach (KeyValuePair val in config.ImageDecoders) + foreach (KeyValuePair val in config.ImageFormatsManager.ImageDecoders) { stringBuilder.AppendLine($" - {val.Key.Name} : {val.Value.GetType().Name}"); } diff --git a/src/ImageSharp/Image/ImageExtensions.cs b/src/ImageSharp/Image/ImageExtensions.cs index c4de1c2988..7d23d95d9c 100644 --- a/src/ImageSharp/Image/ImageExtensions.cs +++ b/src/ImageSharp/Image/ImageExtensions.cs @@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp Guard.NotNullOrEmpty(filePath, nameof(filePath)); string ext = Path.GetExtension(filePath).Trim('.'); - IImageFormat format = source.GetConfiguration().FindFormatByFileExtension(ext); + IImageFormat format = source.GetConfiguration().ImageFormatsManager.FindFormatByFileExtension(ext); if (format == null) { var stringBuilder = new StringBuilder(); @@ -45,13 +45,13 @@ namespace SixLabors.ImageSharp throw new NotSupportedException(stringBuilder.ToString()); } - IImageEncoder encoder = source.GetConfiguration().FindEncoder(format); + IImageEncoder encoder = source.GetConfiguration().ImageFormatsManager.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.GetConfiguration().ImageEncoders) + foreach (KeyValuePair enc in source.GetConfiguration().ImageFormatsManager.ImageEncoders) { stringBuilder.AppendLine($" - {enc.Key} : {enc.Value.GetType().Name}"); } @@ -93,14 +93,14 @@ namespace SixLabors.ImageSharp where TPixel : struct, IPixel { Guard.NotNull(format, nameof(format)); - IImageEncoder encoder = source.GetConfiguration().FindEncoder(format); + IImageEncoder encoder = source.GetConfiguration().ImageFormatsManager.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.GetConfiguration().ImageEncoders) + foreach (KeyValuePair val in source.GetConfiguration().ImageFormatsManager.ImageEncoders) { stringBuilder.AppendLine($" - {val.Key.Name} : {val.Value.GetType().Name}"); } diff --git a/tests/ImageSharp.Tests/ConfigurationTests.cs b/tests/ImageSharp.Tests/ConfigurationTests.cs index d38556fa9e..cf348569ce 100644 --- a/tests/ImageSharp.Tests/ConfigurationTests.cs +++ b/tests/ImageSharp.Tests/ConfigurationTests.cs @@ -34,14 +34,7 @@ namespace SixLabors.ImageSharp.Tests { Assert.IsType(this.DefaultConfiguration.FileSystem); Assert.IsType(this.ConfigurationEmpty.FileSystem); - } - - [Fact] - public void IfAutoloadWellknwonFormatesIsTrueAllFormateAreLoaded() - { - Assert.Equal(4, this.DefaultConfiguration.ImageEncoders.Count()); - Assert.Equal(4, this.DefaultConfiguration.ImageDecoders.Count()); - } + } /// /// Test that the default configuration is not null. @@ -80,80 +73,6 @@ namespace SixLabors.ImageSharp.Tests Assert.True(Configuration.Default.ParallelOptions.MaxDegreeOfParallelism == Environment.ProcessorCount); } - [Fact] - public void AddImageFormatDetectorNullthrows() - { - Assert.Throws(() => - { - this.DefaultConfiguration.AddImageFormatDetector(null); - }); - } - - [Fact] - public void RegisterNullMimeTypeEncoder() - { - Assert.Throws(() => - { - this.DefaultConfiguration.SetEncoder(null, new Mock().Object); - }); - Assert.Throws(() => - { - this.DefaultConfiguration.SetEncoder(ImageFormats.Bmp, null); - }); - Assert.Throws(() => - { - this.DefaultConfiguration.SetEncoder(null, null); - }); - } - - [Fact] - public void RegisterNullSetDecoder() - { - Assert.Throws(() => - { - this.DefaultConfiguration.SetDecoder(null, new Mock().Object); - }); - Assert.Throws(() => - { - this.DefaultConfiguration.SetDecoder(ImageFormats.Bmp, null); - }); - Assert.Throws(() => - { - this.DefaultConfiguration.SetDecoder(null, null); - }); - } - - [Fact] - public void RegisterMimeTypeEncoderReplacesLast() - { - IImageEncoder encoder1 = new Mock().Object; - this.ConfigurationEmpty.SetEncoder(TestFormat.GlobalTestFormat, encoder1); - IImageEncoder found = this.ConfigurationEmpty.FindEncoder(TestFormat.GlobalTestFormat); - Assert.Equal(encoder1, found); - - IImageEncoder encoder2 = new Mock().Object; - this.ConfigurationEmpty.SetEncoder(TestFormat.GlobalTestFormat, encoder2); - IImageEncoder found2 = this.ConfigurationEmpty.FindEncoder(TestFormat.GlobalTestFormat); - Assert.Equal(encoder2, found2); - Assert.NotEqual(found, found2); - } - - [Fact] - public void RegisterMimeTypeDecoderReplacesLast() - { - IImageDecoder decoder1 = new Mock().Object; - this.ConfigurationEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder1); - IImageDecoder found = this.ConfigurationEmpty.FindDecoder(TestFormat.GlobalTestFormat); - Assert.Equal(decoder1, found); - - IImageDecoder decoder2 = new Mock().Object; - this.ConfigurationEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder2); - IImageDecoder found2 = this.ConfigurationEmpty.FindDecoder(TestFormat.GlobalTestFormat); - Assert.Equal(decoder2, found2); - Assert.NotEqual(found, found2); - } - - [Fact] public void ConstructorCallConfigureOnFormatProvider() { diff --git a/tests/ImageSharp.Tests/Image/ImageDiscoverMimeType.cs b/tests/ImageSharp.Tests/Image/ImageDiscoverMimeType.cs index f19fa1990c..1a2275062e 100644 --- a/tests/ImageSharp.Tests/Image/ImageDiscoverMimeType.cs +++ b/tests/ImageSharp.Tests/Image/ImageDiscoverMimeType.cs @@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Tests FileSystem = this.fileSystem.Object }; - this.LocalConfiguration.AddImageFormatDetector(this.localMimeTypeDetector); + this.LocalConfiguration.ImageFormatsManager.AddImageFormatDetector(this.localMimeTypeDetector); TestFormat.RegisterGlobalTestFormat(); this.Marker = Guid.NewGuid().ToByteArray(); diff --git a/tests/ImageSharp.Tests/Image/ImageLoadTests.cs b/tests/ImageSharp.Tests/Image/ImageLoadTests.cs index de18714e2b..97274e98b3 100644 --- a/tests/ImageSharp.Tests/Image/ImageLoadTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageLoadTests.cs @@ -54,8 +54,8 @@ namespace SixLabors.ImageSharp.Tests { FileSystem = this.fileSystem.Object }; - this.LocalConfiguration.AddImageFormatDetector(this.localMimeTypeDetector); - this.LocalConfiguration.SetDecoder(this.localImageFormatMock.Object, this.localDecoder.Object); + this.LocalConfiguration.ImageFormatsManager.AddImageFormatDetector(this.localMimeTypeDetector); + this.LocalConfiguration.ImageFormatsManager.SetDecoder(this.localImageFormatMock.Object, this.localDecoder.Object); TestFormat.RegisterGlobalTestFormat(); this.Marker = Guid.NewGuid().ToByteArray(); diff --git a/tests/ImageSharp.Tests/Image/ImageSaveTests.cs b/tests/ImageSharp.Tests/Image/ImageSaveTests.cs index 7f6e3b7dac..028313e631 100644 --- a/tests/ImageSharp.Tests/Image/ImageSaveTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageSaveTests.cs @@ -42,8 +42,8 @@ namespace SixLabors.ImageSharp.Tests { FileSystem = this.fileSystem.Object }; - config.AddImageFormatDetector(this.localMimeTypeDetector); - config.SetEncoder(this.localImageFormat.Object, this.encoder.Object); + config.ImageFormatsManager.AddImageFormatDetector(this.localMimeTypeDetector); + config.ImageFormatsManager.SetEncoder(this.localImageFormat.Object, this.encoder.Object); this.Image = new Image(config, 1, 1); } diff --git a/tests/ImageSharp.Tests/TestFormat.cs b/tests/ImageSharp.Tests/TestFormat.cs index 078b708dfd..445ace9812 100644 --- a/tests/ImageSharp.Tests/TestFormat.cs +++ b/tests/ImageSharp.Tests/TestFormat.cs @@ -116,9 +116,9 @@ namespace SixLabors.ImageSharp.Tests public void Configure(Configuration host) { - host.AddImageFormatDetector(new TestHeader(this)); - host.SetEncoder(this, new TestEncoder(this)); - host.SetDecoder(this, new TestDecoder(this)); + host.ImageFormatsManager.AddImageFormatDetector(new TestHeader(this)); + host.ImageFormatsManager.SetEncoder(this, new TestEncoder(this)); + host.ImageFormatsManager.SetDecoder(this, new TestDecoder(this)); } public struct DecodeOperation diff --git a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs index 089fed6b0a..fa9497a8f8 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs @@ -21,20 +21,20 @@ namespace SixLabors.ImageSharp.Tests internal static IImageDecoder GetReferenceDecoder(string filePath) { IImageFormat format = GetImageFormat(filePath); - return Configuration.FindDecoder(format); + return Configuration.ImageFormatsManager.FindDecoder(format); } internal static IImageEncoder GetReferenceEncoder(string filePath) { IImageFormat format = GetImageFormat(filePath); - return Configuration.FindEncoder(format); + return Configuration.ImageFormatsManager.FindEncoder(format); } internal static IImageFormat GetImageFormat(string filePath) { string extension = Path.GetExtension(filePath).ToLower(); if (extension[0] == '.') extension = extension.Substring(1); - IImageFormat format = Configuration.FindFormatByFileExtension(extension); + IImageFormat format = Configuration.ImageFormatsManager.FindFormatByFileExtension(extension); return format; } @@ -45,9 +45,9 @@ namespace SixLabors.ImageSharp.Tests IImageEncoder encoder, IImageFormatDetector detector) { - cfg.SetDecoder(imageFormat, decoder); - cfg.SetEncoder(imageFormat, encoder); - cfg.AddImageFormatDetector(detector); + cfg.ImageFormatsManager.SetDecoder(imageFormat, decoder); + cfg.ImageFormatsManager.SetEncoder(imageFormat, encoder); + cfg.ImageFormatsManager.AddImageFormatDetector(detector); } private static Configuration CreateDefaultConfiguration() diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index 6014e25334..f03f9db09a 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -360,7 +360,7 @@ namespace SixLabors.ImageSharp.Tests IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(path); IImageFormat format = TestEnvironment.GetImageFormat(path); - IImageDecoder defaultDecoder = Configuration.Default.FindDecoder(format); + IImageDecoder defaultDecoder = Configuration.Default.ImageFormatsManager.FindDecoder(format); //if (referenceDecoder.GetType() == defaultDecoder.GetType()) //{ From 0fc9002dcc83b9717d57ea62706368098507d80a Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 3 Mar 2018 01:47:44 +0100 Subject: [PATCH 151/151] sanitize MCU processing code --- .../Components/Decoder/InputProcessor.cs | 8 + .../Components/Decoder/OrigJpegScanDecoder.cs | 205 ++++++++++-------- .../Jpeg/GolangPort/OrigJpegDecoderCore.cs | 9 + 3 files changed, 133 insertions(+), 89 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs index fd6a7833a7..cb4b63cffd 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs @@ -380,5 +380,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder this.LastErrorCode = this.Bits.ReceiveExtendUnsafe(t, ref this, out x); return this.LastErrorCode; } + + /// + /// Reset the Huffman decoder. + /// + public void ResetHuffmanDecoder() + { + this.Bits = default(Bits); + } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.cs index 2e1372c564..d10def3ce7 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.cs @@ -94,6 +94,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder /// private int eobRun; + /// + /// The block counter + /// + private int blockCounter; + + /// + /// The MCU counter + /// + private int mcuCounter; + + /// + /// The expected RST marker value + /// + private byte expectedRst; + /// /// Initializes a default-constructed instance for reading data from -s stream. /// @@ -139,124 +154,136 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder { decoder.InputProcessor.ResetErrorState(); - int blockCount = 0; - int mcu = 0; - byte expectedRst = OrigJpegConstants.Markers.RST0; + this.blockCounter = 0; + this.mcuCounter = 0; + this.expectedRst = OrigJpegConstants.Markers.RST0; for (int my = 0; my < decoder.MCUCountY; my++) { for (int mx = 0; mx < decoder.MCUCountX; mx++) { - for (int scanIndex = 0; scanIndex < this.componentScanCount; scanIndex++) - { - this.ComponentIndex = this.pointers.ComponentScan[scanIndex].ComponentIndex; - OrigComponent component = decoder.Components[this.ComponentIndex]; + this.DecodeBlocksAtMcuIndex(decoder, mx, my); - this.hi = component.HorizontalSamplingFactor; - int vi = component.VerticalSamplingFactor; - - for (int j = 0; j < this.hi * vi; j++) - { - if (this.componentScanCount != 1) - { - this.bx = (this.hi * mx) + (j % this.hi); - this.by = (vi * my) + (j / this.hi); - } - else - { - int q = decoder.MCUCountX * this.hi; - this.bx = blockCount % q; - this.by = blockCount / q; - blockCount++; - if (this.bx * 8 >= decoder.ImageWidth || this.by * 8 >= decoder.ImageHeight) - { - continue; - } - } + this.mcuCounter++; - // Find the block at (bx,by) in the component's buffer: - ref Block8x8 blockRefOnHeap = ref component.GetBlockReference(this.bx, this.by); + // Handling restart intervals + // Useful info: https://stackoverflow.com/a/8751802 + if (decoder.IsAtRestartInterval(this.mcuCounter)) + { + this.ProcessRSTMarker(decoder); + this.Reset(decoder); + } + } + } + } - // Copy block to stack - this.data.Block = blockRefOnHeap; + private void DecodeBlocksAtMcuIndex(OrigJpegDecoderCore decoder, int mx, int my) + { + for (int scanIndex = 0; scanIndex < this.componentScanCount; scanIndex++) + { + this.ComponentIndex = this.pointers.ComponentScan[scanIndex].ComponentIndex; + OrigComponent component = decoder.Components[this.ComponentIndex]; - if (!decoder.InputProcessor.ReachedEOF) - { - this.DecodeBlock(decoder, scanIndex); - } + this.hi = component.HorizontalSamplingFactor; + int vi = component.VerticalSamplingFactor; - // Store the result block: - blockRefOnHeap = this.data.Block; + for (int j = 0; j < this.hi * vi; j++) + { + if (this.componentScanCount != 1) + { + this.bx = (this.hi * mx) + (j % this.hi); + this.by = (vi * my) + (j / this.hi); + } + else + { + int q = decoder.MCUCountX * this.hi; + this.bx = this.blockCounter % q; + this.by = this.blockCounter / q; + this.blockCounter++; + if (this.bx * 8 >= decoder.ImageWidth || this.by * 8 >= decoder.ImageHeight) + { + continue; } + } + + // Find the block at (bx,by) in the component's buffer: + ref Block8x8 blockRefOnHeap = ref component.GetBlockReference(this.bx, this.by); - // for j + // Copy block to stack + this.data.Block = blockRefOnHeap; + + if (!decoder.InputProcessor.ReachedEOF) + { + this.DecodeBlock(decoder, scanIndex); } - // for i - mcu++; + // Store the result block: + blockRefOnHeap = this.data.Block; + } + } + } - if (decoder.RestartInterval > 0 && mcu % decoder.RestartInterval == 0 && mcu < decoder.TotalMCUCount) + private void ProcessRSTMarker(OrigJpegDecoderCore decoder) + { + // Attempt to look for RST[0-7] markers to resynchronize from corrupt input. + if (!decoder.InputProcessor.ReachedEOF) + { + decoder.InputProcessor.ReadFullUnsafe(decoder.Temp, 0, 2); + if (decoder.InputProcessor.CheckEOFEnsureNoError()) + { + if (decoder.Temp[0] != 0xFF || decoder.Temp[1] != this.expectedRst) { - // Attempt to look for RST[0-7] markers to resynchronize from corrupt input. - if (!decoder.InputProcessor.ReachedEOF) + bool invalidRst = true; + + // Most jpeg's containing well-formed input will have a RST[0-7] marker following immediately + // but some, see Issue #481, contain padding bytes "0xFF" before the RST[0-7] marker. + // If we identify that case we attempt to read until we have bypassed the padded bytes. + // We then check again for our RST marker and throw if invalid. + // No other methods are attempted to resynchronize from corrupt input. + if (decoder.Temp[0] == 0xFF && decoder.Temp[1] == 0xFF) { - decoder.InputProcessor.ReadFullUnsafe(decoder.Temp, 0, 2); - if (decoder.InputProcessor.CheckEOFEnsureNoError()) + while (decoder.Temp[0] == 0xFF && decoder.InputProcessor.CheckEOFEnsureNoError()) { - if (decoder.Temp[0] != 0xFF || decoder.Temp[1] != expectedRst) - { - bool invalidRst = true; - - // Most jpeg's containing well-formed input will have a RST[0-7] marker following immediately - // but some, see Issue #481, contain padding bytes "0xFF" before the RST[0-7] marker. - // If we identify that case we attempt to read until we have bypassed the padded bytes. - // We then check again for our RST marker and throw if invalid. - // No other methods are attempted to resynchronize from corrupt input. - if (decoder.Temp[0] == 0xFF && decoder.Temp[1] == 0xFF) - { - while (decoder.Temp[0] == 0xFF && decoder.InputProcessor.CheckEOFEnsureNoError()) - { - decoder.InputProcessor.ReadFullUnsafe(decoder.Temp, 0, 1); - if (!decoder.InputProcessor.CheckEOFEnsureNoError()) - { - break; - } - } - - // Have we found a valid restart marker? - invalidRst = decoder.Temp[0] != expectedRst; - } - - if (invalidRst) - { - throw new ImageFormatException("Bad RST marker"); - } - } - - expectedRst++; - if (expectedRst == OrigJpegConstants.Markers.RST7 + 1) + decoder.InputProcessor.ReadFullUnsafe(decoder.Temp, 0, 1); + if (!decoder.InputProcessor.CheckEOFEnsureNoError()) { - expectedRst = OrigJpegConstants.Markers.RST0; + break; } } - } - // Reset the Huffman decoder. - decoder.InputProcessor.Bits = default(Bits); + // Have we found a valid restart marker? + invalidRst = decoder.Temp[0] != this.expectedRst; + } - // Reset the DC components, as per section F.2.1.3.1. - this.ResetDc(); + if (invalidRst) + { + throw new ImageFormatException("Bad RST marker"); + } + } - // Reset the progressive decoder state, as per section G.1.2.2. - this.eobRun = 0; + this.expectedRst++; + if (this.expectedRst == OrigJpegConstants.Markers.RST7 + 1) + { + this.expectedRst = OrigJpegConstants.Markers.RST0; } } - - // for mx } } - private void ResetDc() + private void Reset(OrigJpegDecoderCore decoder) + { + decoder.InputProcessor.ResetHuffmanDecoder(); + + this.ResetDcValues(); + + // Reset the progressive decoder state, as per section G.1.2.2. + this.eobRun = 0; + } + + /// + /// Reset the DC components, as per section F.2.1.3.1. + /// + private void ResetDcValues() { Unsafe.InitBlock(this.pointers.Dc, default(byte), sizeof(int) * OrigJpegDecoderCore.MaxComponents); } diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs index 58513fd297..6cc275d49d 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs @@ -411,6 +411,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort this.InitDerivedMetaDataProperties(); } + /// + /// Returns true if 'mcuCounter' is at restart interval + /// + public bool IsAtRestartInterval(int mcuCounter) + { + return this.RestartInterval > 0 && mcuCounter % this.RestartInterval == 0 + && mcuCounter < this.TotalMCUCount; + } + /// /// Assigns derived metadata properties to , eg. horizontal and vertical resolution if it has a JFIF header. ///