From 96b51193fd44ac390c309d3733457c0470cccae9 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 22 Jul 2018 17:01:39 +0200 Subject: [PATCH 01/13] introducing BufferManager --- ImageSharp.sln.DotSettings | 5 +- src/ImageSharp/Memory/BufferManager.cs | 73 ++++++++ src/ImageSharp/Memory/IBuffer{T}.cs | 8 +- .../Memory/MemoryAllocatorExtensions.cs | 11 +- .../ImageSharp.Tests/Memory/Buffer2DTests.cs | 50 ++---- .../Memory/BufferManagerTests.cs | 158 ++++++++++++++++++ .../Processors/Transforms/ResizeTests.cs | 3 +- .../TestUtilities/TestMemoryAllocator.cs | 55 ++++++ .../TestUtilities/TestMemoryManager.cs | 19 +-- .../TestUtilities/TestUtils.cs | 2 +- 10 files changed, 328 insertions(+), 56 deletions(-) create mode 100644 src/ImageSharp/Memory/BufferManager.cs create mode 100644 tests/ImageSharp.Tests/Memory/BufferManagerTests.cs create mode 100644 tests/ImageSharp.Tests/TestUtilities/TestMemoryAllocator.cs diff --git a/ImageSharp.sln.DotSettings b/ImageSharp.sln.DotSettings index 435aad73b..432f4524a 100644 --- a/ImageSharp.sln.DotSettings +++ b/ImageSharp.sln.DotSettings @@ -343,8 +343,11 @@ <Entry DisplayName="All other members" /> </TypePattern> </Patterns> - True + False True + // Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + AC DC DCT diff --git a/src/ImageSharp/Memory/BufferManager.cs b/src/ImageSharp/Memory/BufferManager.cs new file mode 100644 index 000000000..f1cbb7512 --- /dev/null +++ b/src/ImageSharp/Memory/BufferManager.cs @@ -0,0 +1,73 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; + +namespace SixLabors.Memory +{ + /// + /// Holds a that is either OWNED or CONSUMED. + /// Implements content transfer logic in that depends on the ownership status. + /// This is needed to transfer the contents of a temporary to a persistent + /// + internal struct BufferManager : IDisposable + { + public BufferManager(IMemoryOwner memoryOwner) + { + this.MemoryOwner = memoryOwner; + this.Memory = memoryOwner.Memory; + } + + public BufferManager(Memory memory) + { + this.Memory = memory; + this.MemoryOwner = null; + } + + public IMemoryOwner MemoryOwner { get; private set; } + + public Memory Memory { get; private set; } + + public bool OwnsMemory => this.MemoryOwner != null; + + /// + /// Swaps the contents of 'destination' with 'source' if the buffers are owned (1), + /// copies the contents of 'source' to 'destination' otherwise (2). Buffers should be of same size in case 2! + /// + public static void SwapOrCopyContent(ref BufferManager destination, ref BufferManager source) + { + if (source.OwnsMemory && destination.OwnsMemory) + { + SwapContents(ref destination, ref source); + } + else + { + if (destination.Memory.Length != source.Memory.Length) + { + throw new InvalidOperationException("SwapOrCopyContents(): buffers should both owned or the same size!"); + } + + source.Memory.CopyTo(destination.Memory); + } + } + + /// + public void Dispose() + { + this.MemoryOwner?.Dispose(); + } + + private static void SwapContents(ref BufferManager a, ref BufferManager b) + { + IMemoryOwner tempOwner = a.MemoryOwner; + Memory tempMemory = a.Memory; + + a.MemoryOwner = b.MemoryOwner; + a.Memory = b.Memory; + + b.MemoryOwner = tempOwner; + b.Memory = tempMemory; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Memory/IBuffer{T}.cs b/src/ImageSharp/Memory/IBuffer{T}.cs index 847f2741d..73406971a 100644 --- a/src/ImageSharp/Memory/IBuffer{T}.cs +++ b/src/ImageSharp/Memory/IBuffer{T}.cs @@ -19,14 +19,14 @@ namespace SixLabors.Memory where T : struct { /// - /// Gets the ownerd/consumed by this buffer. + /// Gets a value indicating whether this instance is owning the . /// - Memory Memory { get; } + bool IsMemoryOwner { get; } /// - /// Gets a value indicating whether this instance is owning the . + /// Gets the ownerd/consumed by this buffer. /// - bool IsMemoryOwner { get; } + Memory Memory { get; } /// /// Gets the span to the memory "promised" by this buffer when it's OWNED (1). diff --git a/src/ImageSharp/Memory/MemoryAllocatorExtensions.cs b/src/ImageSharp/Memory/MemoryAllocatorExtensions.cs index 2f296636d..899d2f0f6 100644 --- a/src/ImageSharp/Memory/MemoryAllocatorExtensions.cs +++ b/src/ImageSharp/Memory/MemoryAllocatorExtensions.cs @@ -7,7 +7,11 @@ namespace SixLabors.Memory /// internal static class MemoryAllocatorExtensions { - public static Buffer2D Allocate2D(this MemoryAllocator memoryAllocator, int width, int height, AllocationOptions options = AllocationOptions.None) + public static Buffer2D Allocate2D( + this MemoryAllocator memoryAllocator, + int width, + int height, + AllocationOptions options = AllocationOptions.None) where T : struct { IBuffer buffer = memoryAllocator.Allocate(width * height, options); @@ -15,7 +19,10 @@ namespace SixLabors.Memory return new Buffer2D(buffer, width, height); } - public static Buffer2D Allocate2D(this MemoryAllocator memoryAllocator, Size size, AllocationOptions options = AllocationOptions.None) + public static Buffer2D Allocate2D( + this MemoryAllocator memoryAllocator, + Size size, + AllocationOptions options = AllocationOptions.None) where T : struct => Allocate2D(memoryAllocator, size.Width, size.Height, options); diff --git a/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs index 5dd8572dc..af9c8bd5b 100644 --- a/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs +++ b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs @@ -1,20 +1,20 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Tests.Memory -{ - using System; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; - using SixLabors.ImageSharp.PixelFormats; - using SixLabors.Memory; - using SixLabors.ImageSharp.Tests.Common; - using SixLabors.Primitives; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Memory; +using SixLabors.Primitives; - using Xunit; +using Xunit; +// ReSharper disable InconsistentNaming + +namespace SixLabors.ImageSharp.Tests.Memory +{ public class Buffer2DTests { // ReSharper disable once ClassNeverInstantiated.Local @@ -30,31 +30,7 @@ namespace SixLabors.ImageSharp.Tests.Memory } } - private MemoryAllocator MemoryAllocator { get; } = new MockMemoryAllocator(); - - private class MockMemoryAllocator : MemoryAllocator - { - internal override IBuffer Allocate(int length, AllocationOptions options) - { - var array = new T[length + 42]; - - if (options == AllocationOptions.None) - { - Span data = MemoryMarshal.Cast(array.AsSpan()); - for (int i = 0; i < data.Length; i++) - { - data[i] = 42; - } - } - - return new BasicArrayBuffer(array, length); - } - - internal override IManagedByteBuffer AllocateManagedByteBuffer(int length, AllocationOptions options) - { - throw new NotImplementedException(); - } - } + private MemoryAllocator MemoryAllocator { get; } = new TestMemoryAllocator(); [Theory] [InlineData(7, 42)] @@ -134,7 +110,7 @@ namespace SixLabors.ImageSharp.Tests.Memory public class SwapOrCopyContent { - private MemoryAllocator MemoryAllocator { get; } = new MockMemoryAllocator(); + private MemoryAllocator MemoryAllocator { get; } = new TestMemoryAllocator(); [Fact] public void WhenBothBuffersAreMemoryOwners_ShouldSwap() diff --git a/tests/ImageSharp.Tests/Memory/BufferManagerTests.cs b/tests/ImageSharp.Tests/Memory/BufferManagerTests.cs new file mode 100644 index 000000000..d08e734e8 --- /dev/null +++ b/tests/ImageSharp.Tests/Memory/BufferManagerTests.cs @@ -0,0 +1,158 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Memory; + +using Xunit; +// ReSharper disable InconsistentNaming + +namespace SixLabors.ImageSharp.Tests.Memory +{ + public class BufferManagerTests + { + public class Construction + { + [Fact] + public void InitializeAsOwner_MemoryOwner_IsPresent() + { + var data = new Rgba32[21]; + var mmg = new TestMemoryManager(data); + + var a = new BufferManager(mmg); + + Assert.Equal(mmg, a.MemoryOwner); + Assert.Equal(mmg.Memory, a.Memory); + Assert.True(a.OwnsMemory); + } + + [Fact] + public void InitializeAsObserver_MemoryOwner_IsNull() + { + var data = new Rgba32[21]; + var mmg = new TestMemoryManager(data); + + var a = new BufferManager(mmg.Memory); + + Assert.Null(a.MemoryOwner); + Assert.Equal(mmg.Memory, a.Memory); + Assert.False(a.OwnsMemory); + } + } + + public class Dispose + { + [Fact] + public void WhenOwnershipIsTransfered_ShouldDisposeMemoryOwner() + { + var mmg = new TestMemoryManager(new int[10]); + var bmg = new BufferManager(mmg); + + bmg.Dispose(); + Assert.True(mmg.IsDisposed); + } + + [Fact] + public void WhenMemoryObserver_ShouldNotDisposeAnything() + { + var mmg = new TestMemoryManager(new int[10]); + var bmg = new BufferManager(mmg.Memory); + + bmg.Dispose(); + Assert.False(mmg.IsDisposed); + } + } + + public class SwapOrCopyContent + { + private MemoryAllocator MemoryAllocator { get; } = new TestMemoryAllocator(); + + private BufferManager AllocateBufferManager(int length, AllocationOptions options = AllocationOptions.None) + where T : struct + { + var owner = (IMemoryOwner)this.MemoryAllocator.Allocate(length, options); + return new BufferManager(owner); + } + + [Fact] + public void WhenBothAreMemoryOwners_ShouldSwap() + { + BufferManager a = this.AllocateBufferManager(13); + BufferManager b = this.AllocateBufferManager(17); + + IMemoryOwner aa = a.MemoryOwner; + IMemoryOwner bb = b.MemoryOwner; + + Memory aaa = a.Memory; + Memory bbb = b.Memory; + + BufferManager.SwapOrCopyContent(ref a, ref b); + + Assert.Equal(bb, a.MemoryOwner); + Assert.Equal(aa, b.MemoryOwner); + + Assert.Equal(bbb, a.Memory); + Assert.Equal(aaa, b.Memory); + Assert.NotEqual(a.Memory, b.Memory); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void WhenDestIsNotMemoryOwner_SameSize_ShouldCopy(bool sourceIsOwner) + { + var data = new Rgba32[21]; + var color = new Rgba32(1, 2, 3, 4); + + var destOwner = new TestMemoryManager(data); + var dest = new BufferManager(destOwner.Memory); + + var sourceOwner = (IMemoryOwner)this.MemoryAllocator.Allocate(21); + + BufferManager source = sourceIsOwner + ? new BufferManager(sourceOwner) + : new BufferManager(sourceOwner.Memory); + + sourceOwner.Memory.Span[10] = color; + + // Act: + BufferManager.SwapOrCopyContent(ref dest, ref source); + + // Assert: + Assert.Equal(color, dest.Memory.Span[10]); + Assert.NotEqual(sourceOwner, dest.MemoryOwner); + Assert.NotEqual(destOwner, source.MemoryOwner); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void WhenDestIsNotMemoryOwner_DifferentSize_Throws(bool sourceIsOwner) + { + var data = new Rgba32[21]; + var color = new Rgba32(1, 2, 3, 4); + + var destOwner = new TestMemoryManager(data); + var dest = new BufferManager(destOwner.Memory); + + var sourceOwner = (IMemoryOwner)this.MemoryAllocator.Allocate(22); + + BufferManager source = sourceIsOwner + ? new BufferManager(sourceOwner) + : new BufferManager(sourceOwner.Memory); + sourceOwner.Memory.Span[10] = color; + + // Act: + Assert.ThrowsAny( + () => BufferManager.SwapOrCopyContent(ref dest, ref source) + ); + + Assert.Equal(color, source.Memory.Span[10]); + Assert.NotEqual(color, dest.Memory.Span[10]); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs index c7efbb1e0..746d8da16 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs @@ -3,6 +3,7 @@ using System; +using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Transforms; @@ -91,7 +92,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms { using (Image image0 = provider.GetImage()) { - var mmg = TestMemoryManager.CreateAsCopyOfPixelData(image0); + var mmg = TestMemoryManager.CreateAsCopyOf(image0.GetPixelSpan()); using (var image1 = Image.WrapMemory(mmg.Memory, image0.Width, image0.Height)) { diff --git a/tests/ImageSharp.Tests/TestUtilities/TestMemoryAllocator.cs b/tests/ImageSharp.Tests/TestUtilities/TestMemoryAllocator.cs new file mode 100644 index 000000000..7993b3e99 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/TestMemoryAllocator.cs @@ -0,0 +1,55 @@ +using System; +using System.Runtime.InteropServices; + +using SixLabors.Memory; + +namespace SixLabors.ImageSharp.Tests.Memory +{ + internal class TestMemoryAllocator : MemoryAllocator + { + public TestMemoryAllocator(byte dirtyValue = 42) + { + this.DirtyValue = dirtyValue; + } + + /// + /// The value to initilazie the result buffer with, with non-clean options () + /// + public byte DirtyValue { get; } + + internal override IBuffer Allocate(int length, AllocationOptions options = AllocationOptions.None) + { + T[] array = this.AllocateArray(length, options); + + return new BasicArrayBuffer(array, length); + } + + internal override IManagedByteBuffer AllocateManagedByteBuffer(int length, AllocationOptions options = AllocationOptions.None) + { + byte[] array = this.AllocateArray(length, options); + return new ManagedByteBuffer(array); + } + + private T[] AllocateArray(int length, AllocationOptions options) + where T : struct + { + var array = new T[length + 42]; + + if (options == AllocationOptions.None) + { + Span data = MemoryMarshal.Cast(array.AsSpan()); + data.Fill(this.DirtyValue); + } + + return array; + } + + private class ManagedByteBuffer : BasicArrayBuffer, IManagedByteBuffer + { + public ManagedByteBuffer(byte[] array) + : base(array) + { + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/TestMemoryManager.cs b/tests/ImageSharp.Tests/TestUtilities/TestMemoryManager.cs index e7ecb2dda..ce3cfbc9e 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestMemoryManager.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestMemoryManager.cs @@ -7,18 +7,16 @@ namespace SixLabors.ImageSharp.Tests using SixLabors.ImageSharp.PixelFormats; class TestMemoryManager : MemoryManager - where T : struct, IPixel + where T : struct { public TestMemoryManager(T[] pixelArray) { this.PixelArray = pixelArray; } - public T[] PixelArray { get; } + public T[] PixelArray { get; private set; } - protected override void Dispose(bool disposing) - { - } + public bool IsDisposed { get; private set; } public override Span GetSpan() { @@ -35,16 +33,17 @@ namespace SixLabors.ImageSharp.Tests throw new NotImplementedException(); } - public static TestMemoryManager CreateAsCopyOfPixelData(Span pixelData) + public static TestMemoryManager CreateAsCopyOf(Span copyThisBuffer) { - var pixelArray = new T[pixelData.Length]; - pixelData.CopyTo(pixelArray); + var pixelArray = new T[copyThisBuffer.Length]; + copyThisBuffer.CopyTo(pixelArray); return new TestMemoryManager(pixelArray); } - public static TestMemoryManager CreateAsCopyOfPixelData(Image image) + protected override void Dispose(bool disposing) { - return CreateAsCopyOfPixelData(image.GetPixelSpan()); + this.IsDisposed = true; + this.PixelArray = null; } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs b/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs index 03ab422e5..a6ea76f2d 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs @@ -214,7 +214,7 @@ namespace SixLabors.ImageSharp.Tests using (Image image0 = provider.GetImage()) { - var mmg = TestMemoryManager.CreateAsCopyOfPixelData(image0.GetPixelSpan()); + var mmg = TestMemoryManager.CreateAsCopyOf(image0.GetPixelSpan()); using (var image1 = Image.WrapMemory(mmg.Memory, image0.Width, image0.Height)) { From 12bcdbb49aa294054bb9e8d6fcd42578cc8f6d39 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 22 Jul 2018 19:10:46 +0200 Subject: [PATCH 02/13] replace IBuffer with IMemoryOwner --- .../Primitives/ShapeRegion.cs | 3 +- .../Processing/BrushApplicator.cs | 6 +- .../Processing/ImageBrush{TPixel}.cs | 5 +- .../Processing/PatternBrush{TPixel}.cs | 5 +- .../Processors/Drawing/DrawImageProcessor.cs | 3 +- .../Processors/Drawing/FillProcessor.cs | 4 +- .../Processors/Drawing/FillRegionProcessor.cs | 6 +- .../Processors/Text/DrawTextProcessor.cs | 5 +- .../Processing/RecolorBrush{TPixel}.cs | 5 +- .../Processing/SolidBrush{TPixel}.cs | 6 +- src/ImageSharp/Common/Helpers/ParallelFor.cs | 9 +- src/ImageSharp/Formats/Gif/LzwDecoder.cs | 7 +- src/ImageSharp/Formats/Gif/LzwEncoder.cs | 5 +- .../Decoder/JpegBlockPostProcessor.cs | 2 +- .../Decoder/JpegImagePostProcessor.cs | 3 +- .../PdfJsPort/Components/PdfJsHuffmanTable.cs | 3 +- src/ImageSharp/Image.WrapMemory.cs | 3 +- src/ImageSharp/ImageFrameCollection.cs | 2 +- src/ImageSharp/ImageFrame{TPixel}.cs | 7 +- src/ImageSharp/Image{TPixel}.cs | 4 +- .../ArrayPoolMemoryAllocator.Buffer{T}.cs | 1 - .../Memory/ArrayPoolMemoryAllocator.cs | 2 +- src/ImageSharp/Memory/BasicArrayBuffer.cs | 2 +- src/ImageSharp/Memory/Buffer2D{T}.cs | 54 ++++------ src/ImageSharp/Memory/BufferExtensions.cs | 20 ++-- src/ImageSharp/Memory/BufferManager.cs | 9 ++ src/ImageSharp/Memory/ConsumedBuffer.cs | 34 ------ src/ImageSharp/Memory/IBuffer{T}.cs | 38 ------- src/ImageSharp/Memory/IManagedByteBuffer.cs | 4 +- src/ImageSharp/Memory/ManagedBufferBase.cs | 4 +- src/ImageSharp/Memory/MemoryAllocator.cs | 6 +- .../Memory/MemoryAllocatorExtensions.cs | 6 +- .../Memory/SimpleGcMemoryAllocator.cs | 6 +- .../DefaultPixelBlenders.Generated.cs | 52 ++++----- .../DefaultPixelBlenders.Generated.tt | 12 +-- .../HistogramEqualizationProcessor.cs | 5 +- .../Overlays/BackgroundColorProcessor.cs | 5 +- .../Processors/Overlays/GlowProcessor.cs | 5 +- .../Processors/Overlays/VignetteProcessor.cs | 5 +- .../Quantization/QuantizedFrame{TPixel}.cs | 4 +- .../Quantization/WuFrameQuantizer{TPixel}.cs | 45 ++++---- .../Processors/Transforms/ResizeProcessor.cs | 3 +- .../Processors/Transforms/WeightsWindow.cs | 5 +- .../Color/Bulk/PackFromVector4.cs | 23 ++-- .../Color/Bulk/PackFromXyzw.cs | 18 ++-- .../Color/Bulk/ToVector4.cs | 20 ++-- .../ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs | 19 ++-- .../Color/Bulk/ToXyzw.cs | 18 ++-- .../PixelBlenders/PorterDuffBulkVsPixel.cs | 8 +- tests/ImageSharp.Benchmarks/Samplers/Glow.cs | 4 +- .../Image/ImageTests.WrapMemory.cs | 7 -- .../Memory/ArrayPoolMemoryManagerTests.cs | 16 +-- .../ImageSharp.Tests/Memory/Buffer2DTests.cs | 101 +++--------------- .../Memory/BufferTestSuite.cs | 27 ++--- .../PixelFormats/PixelOperationsTests.cs | 9 +- .../ReferenceCodecs/SystemDrawingBridge.cs | 7 +- .../TestUtilities/TestMemoryAllocator.cs | 3 +- .../TestUtilities/TestMemoryManager.cs | 3 - 58 files changed, 298 insertions(+), 405 deletions(-) delete mode 100644 src/ImageSharp/Memory/ConsumedBuffer.cs delete mode 100644 src/ImageSharp/Memory/IBuffer{T}.cs diff --git a/src/ImageSharp.Drawing/Primitives/ShapeRegion.cs b/src/ImageSharp.Drawing/Primitives/ShapeRegion.cs index 9f5611dc0..fddd283e0 100644 --- a/src/ImageSharp.Drawing/Primitives/ShapeRegion.cs +++ b/src/ImageSharp.Drawing/Primitives/ShapeRegion.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using SixLabors.Memory; using SixLabors.Primitives; using SixLabors.Shapes; @@ -45,7 +46,7 @@ namespace SixLabors.ImageSharp.Primitives var start = new PointF(this.Bounds.Left - 1, y); var end = new PointF(this.Bounds.Right + 1, y); - using (IBuffer tempBuffer = configuration.MemoryAllocator.Allocate(buffer.Length)) + using (IMemoryOwner tempBuffer = configuration.MemoryAllocator.Allocate(buffer.Length)) { Span innerBuffer = tempBuffer.GetSpan(); int count = this.Shape.FindIntersections(start, end, innerBuffer); diff --git a/src/ImageSharp.Drawing/Processing/BrushApplicator.cs b/src/ImageSharp.Drawing/Processing/BrushApplicator.cs index 0ac4e4dd1..64f37eeab 100644 --- a/src/ImageSharp.Drawing/Processing/BrushApplicator.cs +++ b/src/ImageSharp.Drawing/Processing/BrushApplicator.cs @@ -2,6 +2,8 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; + using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Memory; @@ -65,8 +67,8 @@ namespace SixLabors.ImageSharp.Processing { MemoryAllocator memoryAllocator = this.Target.MemoryAllocator; - using (IBuffer amountBuffer = memoryAllocator.Allocate(scanline.Length)) - using (IBuffer overlay = memoryAllocator.Allocate(scanline.Length)) + using (IMemoryOwner amountBuffer = memoryAllocator.Allocate(scanline.Length)) + using (IMemoryOwner overlay = memoryAllocator.Allocate(scanline.Length)) { Span amountSpan = amountBuffer.GetSpan(); Span overlaySpan = overlay.GetSpan(); diff --git a/src/ImageSharp.Drawing/Processing/ImageBrush{TPixel}.cs b/src/ImageSharp.Drawing/Processing/ImageBrush{TPixel}.cs index 7e24dbbe2..5ebad0f32 100644 --- a/src/ImageSharp.Drawing/Processing/ImageBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Processing/ImageBrush{TPixel}.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Memory; @@ -118,8 +119,8 @@ namespace SixLabors.ImageSharp.Processing internal override void Apply(Span scanline, int x, int y) { // Create a span for colors - using (IBuffer amountBuffer = this.Target.MemoryAllocator.Allocate(scanline.Length)) - using (IBuffer overlay = this.Target.MemoryAllocator.Allocate(scanline.Length)) + using (IMemoryOwner amountBuffer = this.Target.MemoryAllocator.Allocate(scanline.Length)) + using (IMemoryOwner overlay = this.Target.MemoryAllocator.Allocate(scanline.Length)) { Span amountSpan = amountBuffer.GetSpan(); Span overlaySpan = overlay.GetSpan(); diff --git a/src/ImageSharp.Drawing/Processing/PatternBrush{TPixel}.cs b/src/ImageSharp.Drawing/Processing/PatternBrush{TPixel}.cs index 30d78bc83..ab48a185b 100644 --- a/src/ImageSharp.Drawing/Processing/PatternBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Processing/PatternBrush{TPixel}.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.Numerics; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; @@ -153,8 +154,8 @@ namespace SixLabors.ImageSharp.Processing int patternY = y % this.pattern.Rows; MemoryAllocator memoryAllocator = this.Target.MemoryAllocator; - using (IBuffer amountBuffer = memoryAllocator.Allocate(scanline.Length)) - using (IBuffer overlay = memoryAllocator.Allocate(scanline.Length)) + using (IMemoryOwner amountBuffer = memoryAllocator.Allocate(scanline.Length)) + using (IMemoryOwner overlay = memoryAllocator.Allocate(scanline.Length)) { Span amountSpan = amountBuffer.GetSpan(); Span overlaySpan = overlay.GetSpan(); diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs index e4b2eadb4..4a59dfe3e 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; @@ -134,7 +135,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing MemoryAllocator memoryAllocator = this.Image.GetConfiguration().MemoryAllocator; - using (IBuffer amount = memoryAllocator.Allocate(width)) + using (IMemoryOwner amount = memoryAllocator.Allocate(width)) { amount.GetSpan().Fill(this.Opacity); diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs index 595c94687..e40ba5316 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs @@ -2,10 +2,10 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; using SixLabors.Memory; using SixLabors.Primitives; @@ -76,7 +76,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing startY = 0; } - using (IBuffer amount = source.MemoryAllocator.Allocate(width)) + using (IMemoryOwner amount = source.MemoryAllocator.Allocate(width)) using (BrushApplicator applicator = this.brush.CreateApplicator( source, sourceRectangle, diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs index 1cc954dd9..b9db3f067 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs @@ -2,6 +2,8 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; + using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Utils; @@ -94,8 +96,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing using (BrushApplicator applicator = this.Brush.CreateApplicator(source, rect, this.Options)) { int scanlineWidth = maxX - minX; - using (IBuffer bBuffer = source.MemoryAllocator.Allocate(maxIntersections)) - using (IBuffer bScanline = source.MemoryAllocator.Allocate(scanlineWidth)) + using (IMemoryOwner bBuffer = source.MemoryAllocator.Allocate(maxIntersections)) + using (IMemoryOwner bScanline = source.MemoryAllocator.Allocate(scanlineWidth)) { bool scanlineDirty = true; float subpixelFraction = 1f / subpixelCount; diff --git a/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs b/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs index 8909ca453..048c4440d 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.Collections.Generic; using SixLabors.Fonts; using SixLabors.ImageSharp.Advanced; @@ -336,8 +337,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text // take the path inside the path builder, scan thing and generate a Buffer2d representing the glyph and cache it. Buffer2D fullBuffer = this.MemoryAllocator.Allocate2D(size.Width + 1, size.Height + 1, AllocationOptions.Clean); - using (IBuffer bufferBacking = this.MemoryAllocator.Allocate(path.MaxIntersections)) - using (IBuffer rowIntersectionBuffer = this.MemoryAllocator.Allocate(size.Width)) + using (IMemoryOwner bufferBacking = this.MemoryAllocator.Allocate(path.MaxIntersections)) + using (IMemoryOwner rowIntersectionBuffer = this.MemoryAllocator.Allocate(size.Width)) { float subpixelFraction = 1f / subpixelCount; float subpixelFractionPoint = subpixelFraction / subpixelCount; diff --git a/src/ImageSharp.Drawing/Processing/RecolorBrush{TPixel}.cs b/src/ImageSharp.Drawing/Processing/RecolorBrush{TPixel}.cs index 058b03d62..e1b11637d 100644 --- a/src/ImageSharp.Drawing/Processing/RecolorBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Processing/RecolorBrush{TPixel}.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.Numerics; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; @@ -138,8 +139,8 @@ namespace SixLabors.ImageSharp.Processing { MemoryAllocator memoryAllocator = this.Target.MemoryAllocator; - using (IBuffer amountBuffer = memoryAllocator.Allocate(scanline.Length)) - using (IBuffer overlay = memoryAllocator.Allocate(scanline.Length)) + using (IMemoryOwner amountBuffer = memoryAllocator.Allocate(scanline.Length)) + using (IMemoryOwner overlay = memoryAllocator.Allocate(scanline.Length)) { Span amountSpan = amountBuffer.GetSpan(); Span overlaySpan = overlay.GetSpan(); diff --git a/src/ImageSharp.Drawing/Processing/SolidBrush{TPixel}.cs b/src/ImageSharp.Drawing/Processing/SolidBrush{TPixel}.cs index 8a2d47c6c..3904f3d9b 100644 --- a/src/ImageSharp.Drawing/Processing/SolidBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Processing/SolidBrush{TPixel}.cs @@ -2,6 +2,8 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; + using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Memory; @@ -65,7 +67,7 @@ namespace SixLabors.ImageSharp.Processing /// /// Gets the colors. /// - protected IBuffer Colors { get; } + protected IMemoryOwner Colors { get; } /// /// Gets the color for a single pixel. @@ -96,7 +98,7 @@ namespace SixLabors.ImageSharp.Processing } else { - using (IBuffer amountBuffer = memoryAllocator.Allocate(scanline.Length)) + using (IMemoryOwner amountBuffer = memoryAllocator.Allocate(scanline.Length)) { Span amountSpan = amountBuffer.GetSpan(); diff --git a/src/ImageSharp/Common/Helpers/ParallelFor.cs b/src/ImageSharp/Common/Helpers/ParallelFor.cs index 7061475a7..02c6deda3 100644 --- a/src/ImageSharp/Common/Helpers/ParallelFor.cs +++ b/src/ImageSharp/Common/Helpers/ParallelFor.cs @@ -1,4 +1,5 @@ using System; +using System.Buffers; using System.Threading.Tasks; using SixLabors.Memory; @@ -32,23 +33,23 @@ namespace SixLabors.ImageSharp int toExclusive, Configuration configuration, int bufferLength, - Action> body) + Action> body) where T : struct { MemoryAllocator memoryAllocator = configuration.MemoryAllocator; ParallelOptions parallelOptions = configuration.ParallelOptions; - IBuffer InitBuffer() + IMemoryOwner InitBuffer() { return memoryAllocator.Allocate(bufferLength); } - void CleanUpBuffer(IBuffer buffer) + void CleanUpBuffer(IMemoryOwner buffer) { buffer.Dispose(); } - IBuffer BodyFunc(int i, ParallelLoopState state, IBuffer buffer) + IMemoryOwner BodyFunc(int i, ParallelLoopState state, IMemoryOwner buffer) { body(i, buffer); return buffer; diff --git a/src/ImageSharp/Formats/Gif/LzwDecoder.cs b/src/ImageSharp/Formats/Gif/LzwDecoder.cs index 2884abfe8..7a2aef180 100644 --- a/src/ImageSharp/Formats/Gif/LzwDecoder.cs +++ b/src/ImageSharp/Formats/Gif/LzwDecoder.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -32,17 +33,17 @@ namespace SixLabors.ImageSharp.Formats.Gif /// /// The prefix buffer. /// - private readonly IBuffer prefix; + private readonly IMemoryOwner prefix; /// /// The suffix buffer. /// - private readonly IBuffer suffix; + private readonly IMemoryOwner suffix; /// /// The pixel stack buffer. /// - private readonly IBuffer pixelStack; + private readonly IMemoryOwner pixelStack; /// /// Initializes a new instance of the class diff --git a/src/ImageSharp/Formats/Gif/LzwEncoder.cs b/src/ImageSharp/Formats/Gif/LzwEncoder.cs index 5a588a671..002457db3 100644 --- a/src/ImageSharp/Formats/Gif/LzwEncoder.cs +++ b/src/ImageSharp/Formats/Gif/LzwEncoder.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -66,12 +67,12 @@ namespace SixLabors.ImageSharp.Formats.Gif /// /// The hash table. /// - private readonly IBuffer hashTable; + private readonly IMemoryOwner hashTable; /// /// The code table. /// - private readonly IBuffer codeTable; + private readonly IMemoryOwner codeTable; /// /// Define the storage for the packet accumulator. diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs index 2baefff9b..87f675491 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs @@ -9,7 +9,7 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { /// - /// Encapsulates the implementation of processing "raw" -s into Jpeg image channels. + /// Encapsulates the implementation of processing "raw" jpeg buffers into Jpeg image channels. /// [StructLayout(LayoutKind.Sequential)] internal struct JpegBlockPostProcessor diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs index 99408cf57..1b513c612 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.Linq; using System.Numerics; using SixLabors.ImageSharp.Advanced; @@ -37,7 +38,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// /// Temporal buffer to store a row of colors. /// - private readonly IBuffer rgbaBuffer; + private readonly IMemoryOwner rgbaBuffer; /// /// The corresponding to the current determined by . diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs index 15ae56331..59992e52c 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.Memory; @@ -48,7 +49,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components public PdfJsHuffmanTable(MemoryAllocator memoryAllocator, ReadOnlySpan count, ReadOnlySpan values) { const int Length = 257; - using (IBuffer huffcode = memoryAllocator.Allocate(Length)) + using (IMemoryOwner huffcode = memoryAllocator.Allocate(Length)) { ref short huffcodeRef = ref MemoryMarshal.GetReference(huffcode.GetSpan()); diff --git a/src/ImageSharp/Image.WrapMemory.cs b/src/ImageSharp/Image.WrapMemory.cs index 3988120e6..3ccd809f4 100644 --- a/src/ImageSharp/Image.WrapMemory.cs +++ b/src/ImageSharp/Image.WrapMemory.cs @@ -34,8 +34,7 @@ namespace SixLabors.ImageSharp ImageMetaData metaData) where TPixel : struct, IPixel { - var buffer = new ConsumedBuffer(pixelMemory); - return new Image(config, buffer, width, height, metaData); + return new Image(config, pixelMemory, width, height, metaData); } /// diff --git a/src/ImageSharp/ImageFrameCollection.cs b/src/ImageSharp/ImageFrameCollection.cs index 14a16b691..4b8f90e9b 100644 --- a/src/ImageSharp/ImageFrameCollection.cs +++ b/src/ImageSharp/ImageFrameCollection.cs @@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp this.frames.Add(new ImageFrame(parent.GetConfiguration(), width, height, backgroundColor)); } - internal ImageFrameCollection(Image parent, int width, int height, IBuffer consumedBuffer) + internal ImageFrameCollection(Image parent, int width, int height, Memory consumedBuffer) { Guard.NotNull(parent, nameof(parent)); diff --git a/src/ImageSharp/ImageFrame{TPixel}.cs b/src/ImageSharp/ImageFrame{TPixel}.cs index aa650d380..2fa35f1e5 100644 --- a/src/ImageSharp/ImageFrame{TPixel}.cs +++ b/src/ImageSharp/ImageFrame{TPixel}.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.Numerics; using System.Runtime.CompilerServices; using System.Threading.Tasks; @@ -94,7 +95,7 @@ namespace SixLabors.ImageSharp /// /// Initializes a new instance of the class wrapping an existing buffer. /// - internal ImageFrame(Configuration configuration, int width, int height, IBuffer consumedBuffer) + internal ImageFrame(Configuration configuration, int width, int height, Memory consumedBuffer) : this(configuration, width, height, consumedBuffer, new ImageFrameMetaData()) { } @@ -106,7 +107,7 @@ namespace SixLabors.ImageSharp Configuration configuration, int width, int height, - IBuffer consumedBuffer, + Memory consumedBuffer, ImageFrameMetaData metaData) { Guard.NotNull(configuration, nameof(configuration)); @@ -272,7 +273,7 @@ namespace SixLabors.ImageSharp this.Height, this.configuration, this.Width, - (int y, IBuffer tempRowBuffer) => + (int y, IMemoryOwner tempRowBuffer) => { Span sourceRow = this.GetPixelRowSpan(y); Span targetRow = target.GetPixelRowSpan(y); diff --git a/src/ImageSharp/Image{TPixel}.cs b/src/ImageSharp/Image{TPixel}.cs index ad754bc75..9126812dd 100644 --- a/src/ImageSharp/Image{TPixel}.cs +++ b/src/ImageSharp/Image{TPixel}.cs @@ -86,7 +86,7 @@ namespace SixLabors.ImageSharp /// Initializes a new instance of the class /// consuming an external buffer instance. /// - internal Image(Configuration configuration, IBuffer consumedBuffer, int width, int height) + internal Image(Configuration configuration, Memory consumedBuffer, int width, int height) : this(configuration, consumedBuffer, width, height, new ImageMetaData()) { } @@ -95,7 +95,7 @@ namespace SixLabors.ImageSharp /// Initializes a new instance of the class /// consuming an external buffer instance. /// - internal Image(Configuration configuration, IBuffer consumedBuffer, int width, int height, ImageMetaData metadata) + internal Image(Configuration configuration, Memory consumedBuffer, int width, int height, ImageMetaData metadata) { this.configuration = configuration; this.PixelType = new PixelTypeInfo(Unsafe.SizeOf() * 8); diff --git a/src/ImageSharp/Memory/ArrayPoolMemoryAllocator.Buffer{T}.cs b/src/ImageSharp/Memory/ArrayPoolMemoryAllocator.Buffer{T}.cs index d0f68c9c6..adc8843a3 100644 --- a/src/ImageSharp/Memory/ArrayPoolMemoryAllocator.Buffer{T}.cs +++ b/src/ImageSharp/Memory/ArrayPoolMemoryAllocator.Buffer{T}.cs @@ -14,7 +14,6 @@ namespace SixLabors.Memory { /// /// The buffer implementation of . - /// In this implementation is owned. /// private class Buffer : ManagedBufferBase where T : struct diff --git a/src/ImageSharp/Memory/ArrayPoolMemoryAllocator.cs b/src/ImageSharp/Memory/ArrayPoolMemoryAllocator.cs index 08f28c5b3..32c1c6d1d 100644 --- a/src/ImageSharp/Memory/ArrayPoolMemoryAllocator.cs +++ b/src/ImageSharp/Memory/ArrayPoolMemoryAllocator.cs @@ -89,7 +89,7 @@ namespace SixLabors.Memory } /// - internal override IBuffer Allocate(int length, AllocationOptions options) + internal override IMemoryOwner Allocate(int length, AllocationOptions options = AllocationOptions.None) { int itemSizeBytes = Unsafe.SizeOf(); int bufferSizeInBytes = length * itemSizeBytes; diff --git a/src/ImageSharp/Memory/BasicArrayBuffer.cs b/src/ImageSharp/Memory/BasicArrayBuffer.cs index 5e3893d08..f40df7604 100644 --- a/src/ImageSharp/Memory/BasicArrayBuffer.cs +++ b/src/ImageSharp/Memory/BasicArrayBuffer.cs @@ -7,7 +7,7 @@ using System.Runtime.CompilerServices; namespace SixLabors.Memory { /// - /// Wraps an array as an instance. In this implementation is owned. + /// Wraps an array as an instance. /// internal class BasicArrayBuffer : ManagedBufferBase where T : struct diff --git a/src/ImageSharp/Memory/Buffer2D{T}.cs b/src/ImageSharp/Memory/Buffer2D{T}.cs index b76c06df8..7d331b46d 100644 --- a/src/ImageSharp/Memory/Buffer2D{T}.cs +++ b/src/ImageSharp/Memory/Buffer2D{T}.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.Runtime.CompilerServices; using SixLabors.Primitives; @@ -15,19 +16,31 @@ namespace SixLabors.Memory internal sealed class Buffer2D : IDisposable where T : struct { + private BufferManager buffer; + /// /// Initializes a new instance of the class. /// /// The buffer to wrap /// The number of elements in a row /// The number of rows - public Buffer2D(IBuffer wrappedBuffer, int width, int height) + public Buffer2D(BufferManager wrappedBuffer, int width, int height) { - this.Buffer = wrappedBuffer; + this.buffer = wrappedBuffer; this.Width = width; this.Height = height; } + public Buffer2D(IMemoryOwner ownedMemory, int width, int height) + : this(new BufferManager(ownedMemory), width, height) + { + } + + public Buffer2D(Memory observedMemory, int width, int height) + : this(new BufferManager(observedMemory), width, height) + { + } + /// /// Gets the width. /// @@ -39,13 +52,13 @@ namespace SixLabors.Memory public int Height { get; private set; } /// - /// Gets the backing + /// Gets the backing /// - public IBuffer Buffer { get; private set; } + public BufferManager Buffer => this.buffer; public Memory Memory => this.Buffer.Memory; - public Span Span => this.Buffer.GetSpan(); + public Span Span => this.Memory.Span; /// /// Gets a reference to the element at the specified position. @@ -60,7 +73,7 @@ namespace SixLabors.Memory { ImageSharp.DebugGuard.MustBeLessThan(x, this.Width, nameof(x)); DebugGuard.MustBeLessThan(y, this.Height, nameof(y)); - Span span = this.Buffer.GetSpan(); + Span span = this.Span; return ref span[(this.Width * y) + x]; } } @@ -70,7 +83,7 @@ namespace SixLabors.Memory /// public void Dispose() { - this.Buffer?.Dispose(); + this.Buffer.Dispose(); } /// @@ -79,36 +92,15 @@ namespace SixLabors.Memory /// public static void SwapOrCopyContent(Buffer2D destination, Buffer2D source) { - if (source.Buffer.IsMemoryOwner && destination.Buffer.IsMemoryOwner) - { - SwapContents(destination, source); - } - else - { - if (destination.Size() != source.Size()) - { - throw new InvalidOperationException("SwapOrCopyContents(): buffers should both owned or the same size!"); - } - - source.Span.CopyTo(destination.Span); - } + BufferManager.SwapOrCopyContent(ref destination.buffer, ref source.buffer); + SwapDimensionData(destination, source); } - /// - /// Swap the contents (, , ) of the two buffers. - /// Useful to transfer the contents of a temporary to a persistent - /// - /// The first buffer - /// The second buffer - private static void SwapContents(Buffer2D a, Buffer2D b) + private static void SwapDimensionData(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; diff --git a/src/ImageSharp/Memory/BufferExtensions.cs b/src/ImageSharp/Memory/BufferExtensions.cs index 8ebf866bc..800e0d975 100644 --- a/src/ImageSharp/Memory/BufferExtensions.cs +++ b/src/ImageSharp/Memory/BufferExtensions.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -11,8 +12,12 @@ namespace SixLabors.Memory internal static class BufferExtensions { [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Length(this IBuffer buffer) - where T : struct => buffer.GetSpan().Length; + public static Span GetSpan(this IMemoryOwner buffer) + => buffer.Memory.Span; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Length(this IMemoryOwner buffer) + => buffer.GetSpan().Length; /// /// Gets a to an offseted position inside the buffer. @@ -21,8 +26,7 @@ namespace SixLabors.Memory /// The start /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span Slice(this IBuffer buffer, int start) - where T : struct + public static Span Slice(this IMemoryOwner buffer, int start) { return buffer.GetSpan().Slice(start); } @@ -35,8 +39,7 @@ namespace SixLabors.Memory /// The length of the slice /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span Slice(this IBuffer buffer, int start, int length) - where T : struct + public static Span Slice(this IMemoryOwner buffer, int start, int length) { return buffer.GetSpan().Slice(start, length); } @@ -46,13 +49,12 @@ namespace SixLabors.Memory /// /// The buffer [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Clear(this IBuffer buffer) - where T : struct + public static void Clear(this IMemoryOwner buffer) { buffer.GetSpan().Clear(); } - public static ref T GetReference(this IBuffer buffer) + public static ref T GetReference(this IMemoryOwner buffer) where T : struct => ref MemoryMarshal.GetReference(buffer.GetSpan()); diff --git a/src/ImageSharp/Memory/BufferManager.cs b/src/ImageSharp/Memory/BufferManager.cs index f1cbb7512..1a53890d4 100644 --- a/src/ImageSharp/Memory/BufferManager.cs +++ b/src/ImageSharp/Memory/BufferManager.cs @@ -11,6 +11,11 @@ namespace SixLabors.Memory /// Implements content transfer logic in that depends on the ownership status. /// This is needed to transfer the contents of a temporary to a persistent /// + /// + /// For a deeper understanding of the owner/consumer model, check out the following docs:
+ /// https://gist.github.com/GrabYourPitchforks/4c3e1935fd4d9fa2831dbfcab35dffc6 + /// https://www.codemag.com/Article/1807051/Introducing-.NET-Core-2.1-Flagship-Types-Span-T-and-Memory-T + ///
internal struct BufferManager : IDisposable { public BufferManager(IMemoryOwner memoryOwner) @@ -31,6 +36,10 @@ namespace SixLabors.Memory public bool OwnsMemory => this.MemoryOwner != null; + public Span GetSpan() => this.Memory.Span; + + public void Clear() => this.Memory.Span.Clear(); + /// /// Swaps the contents of 'destination' with 'source' if the buffers are owned (1), /// copies the contents of 'source' to 'destination' otherwise (2). Buffers should be of same size in case 2! diff --git a/src/ImageSharp/Memory/ConsumedBuffer.cs b/src/ImageSharp/Memory/ConsumedBuffer.cs deleted file mode 100644 index 73468a381..000000000 --- a/src/ImageSharp/Memory/ConsumedBuffer.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; - -namespace SixLabors.Memory -{ - /// - /// A buffer implementation that consumes an existing instance. - /// The ownership of the memory remains external. - /// - /// The value type - internal sealed class ConsumedBuffer : IBuffer - where T : struct - { - public ConsumedBuffer(Memory memory) - { - this.Memory = memory; - } - - public Memory Memory { get; } - - public bool IsMemoryOwner => false; - - public Span GetSpan() - { - return this.Memory.Span; - } - - public void Dispose() - { - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Memory/IBuffer{T}.cs b/src/ImageSharp/Memory/IBuffer{T}.cs deleted file mode 100644 index 73406971a..000000000 --- a/src/ImageSharp/Memory/IBuffer{T}.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Buffers; - -namespace SixLabors.Memory -{ - /// - /// Represents a contigous memory buffer of value-type items. - /// Depending on it's implementation, an can (1) OWN or (2) CONSUME the instance it wraps. - /// For a deeper understanding of the owner/consumer model, read the following docs:
- /// https://gist.github.com/GrabYourPitchforks/4c3e1935fd4d9fa2831dbfcab35dffc6 - /// TODO: We need more SOC here! For owned buffers we should use . - /// For the consumption case we should not use buffers at all. We need to refactor Buffer2D{T} for this. - ///
- /// The value type - internal interface IBuffer : IDisposable - where T : struct - { - /// - /// Gets a value indicating whether this instance is owning the . - /// - bool IsMemoryOwner { get; } - - /// - /// Gets the ownerd/consumed by this buffer. - /// - Memory Memory { get; } - - /// - /// Gets the span to the memory "promised" by this buffer when it's OWNED (1). - /// Gets `this.Memory.Span` when the buffer CONSUMED (2). - /// - /// The - Span GetSpan(); - } -} \ No newline at end of file diff --git a/src/ImageSharp/Memory/IManagedByteBuffer.cs b/src/ImageSharp/Memory/IManagedByteBuffer.cs index a977eb68f..91c61424b 100644 --- a/src/ImageSharp/Memory/IManagedByteBuffer.cs +++ b/src/ImageSharp/Memory/IManagedByteBuffer.cs @@ -1,12 +1,14 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System.Buffers; + namespace SixLabors.Memory { /// /// Represents a byte buffer backed by a managed array. Useful for interop with classic .NET API-s. /// - internal interface IManagedByteBuffer : IBuffer + internal interface IManagedByteBuffer : IMemoryOwner { /// /// Gets the managed array backing this buffer instance. diff --git a/src/ImageSharp/Memory/ManagedBufferBase.cs b/src/ImageSharp/Memory/ManagedBufferBase.cs index 8de2f5392..8aaf199ff 100644 --- a/src/ImageSharp/Memory/ManagedBufferBase.cs +++ b/src/ImageSharp/Memory/ManagedBufferBase.cs @@ -7,9 +7,9 @@ using System.Runtime.InteropServices; namespace SixLabors.Memory { /// - /// Provides a base class for implementations by implementing pinning logic for adaption. + /// Provides a base class for implementations by implementing pinning logic for adaption. /// - internal abstract class ManagedBufferBase : MemoryManager, IBuffer + internal abstract class ManagedBufferBase : MemoryManager where T : struct { private GCHandle pinHandle; diff --git a/src/ImageSharp/Memory/MemoryAllocator.cs b/src/ImageSharp/Memory/MemoryAllocator.cs index 4a65848fc..57b721e48 100644 --- a/src/ImageSharp/Memory/MemoryAllocator.cs +++ b/src/ImageSharp/Memory/MemoryAllocator.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System.Buffers; + namespace SixLabors.Memory { /// @@ -9,13 +11,13 @@ namespace SixLabors.Memory public abstract class MemoryAllocator { /// - /// Allocates an of size . + /// Allocates an , holding a of length . /// /// Type of the data stored in the buffer /// Size of the buffer to allocate /// The allocation options. /// A buffer of values of type . - internal abstract IBuffer Allocate(int length, AllocationOptions options = AllocationOptions.None) + internal abstract IMemoryOwner Allocate(int length, AllocationOptions options = AllocationOptions.None) where T : struct; /// diff --git a/src/ImageSharp/Memory/MemoryAllocatorExtensions.cs b/src/ImageSharp/Memory/MemoryAllocatorExtensions.cs index 899d2f0f6..327bc8bbf 100644 --- a/src/ImageSharp/Memory/MemoryAllocatorExtensions.cs +++ b/src/ImageSharp/Memory/MemoryAllocatorExtensions.cs @@ -1,4 +1,6 @@ -using SixLabors.Primitives; +using System.Buffers; + +using SixLabors.Primitives; namespace SixLabors.Memory { @@ -14,7 +16,7 @@ namespace SixLabors.Memory AllocationOptions options = AllocationOptions.None) where T : struct { - IBuffer buffer = memoryAllocator.Allocate(width * height, options); + IMemoryOwner buffer = memoryAllocator.Allocate(width * height, options); return new Buffer2D(buffer, width, height); } diff --git a/src/ImageSharp/Memory/SimpleGcMemoryAllocator.cs b/src/ImageSharp/Memory/SimpleGcMemoryAllocator.cs index 8cb057366..612b53820 100644 --- a/src/ImageSharp/Memory/SimpleGcMemoryAllocator.cs +++ b/src/ImageSharp/Memory/SimpleGcMemoryAllocator.cs @@ -1,4 +1,6 @@ -namespace SixLabors.Memory +using System.Buffers; + +namespace SixLabors.Memory { /// /// Implements by newing up arrays by the GC on every allocation requests. @@ -6,7 +8,7 @@ public sealed class SimpleGcMemoryAllocator : MemoryAllocator { /// - internal override IBuffer Allocate(int length, AllocationOptions options) + internal override IMemoryOwner Allocate(int length, AllocationOptions options = AllocationOptions.None) { return new BasicArrayBuffer(new T[length]); } diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs index 84e528d24..c96a05255 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs @@ -2,13 +2,13 @@ // Licensed under the Apache License, Version 2.0. // +using System; +using System.Numerics; +using System.Buffers; +using SixLabors.Memory; + namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { - using System; - using System.Numerics; - using SixLabors.Memory; - - /// /// Collection of Porter Duff alpha blending functions applying different composition models. /// @@ -44,7 +44,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 (IBuffer buffer = memoryManager.Allocate(destination.Length * 3)) + using (IMemoryOwner buffer = memoryManager.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -83,7 +83,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 (IBuffer buffer = memoryManager.Allocate(destination.Length * 3)) + using (IMemoryOwner buffer = memoryManager.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -122,7 +122,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 (IBuffer buffer = memoryManager.Allocate(destination.Length * 3)) + using (IMemoryOwner buffer = memoryManager.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -161,7 +161,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 (IBuffer buffer = memoryManager.Allocate(destination.Length * 3)) + using (IMemoryOwner buffer = memoryManager.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -200,7 +200,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 (IBuffer buffer = memoryManager.Allocate(destination.Length * 3)) + using (IMemoryOwner buffer = memoryManager.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -239,7 +239,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 (IBuffer buffer = memoryManager.Allocate(destination.Length * 3)) + using (IMemoryOwner buffer = memoryManager.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -278,7 +278,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 (IBuffer buffer = memoryManager.Allocate(destination.Length * 3)) + using (IMemoryOwner buffer = memoryManager.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -317,7 +317,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 (IBuffer buffer = memoryManager.Allocate(destination.Length * 3)) + using (IMemoryOwner buffer = memoryManager.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -356,7 +356,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 (IBuffer buffer = memoryManager.Allocate(destination.Length * 3)) + using (IMemoryOwner buffer = memoryManager.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -395,7 +395,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 (IBuffer buffer = memoryManager.Allocate(destination.Length * 3)) + using (IMemoryOwner buffer = memoryManager.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -434,7 +434,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 (IBuffer buffer = memoryManager.Allocate(destination.Length * 3)) + using (IMemoryOwner buffer = memoryManager.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -473,7 +473,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 (IBuffer buffer = memoryManager.Allocate(destination.Length * 3)) + using (IMemoryOwner buffer = memoryManager.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -512,7 +512,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 (IBuffer buffer = memoryManager.Allocate(destination.Length * 3)) + using (IMemoryOwner buffer = memoryManager.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -551,7 +551,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 (IBuffer buffer = memoryManager.Allocate(destination.Length * 3)) + using (IMemoryOwner buffer = memoryManager.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -590,7 +590,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 (IBuffer buffer = memoryManager.Allocate(destination.Length * 3)) + using (IMemoryOwner buffer = memoryManager.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -629,7 +629,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 (IBuffer buffer = memoryManager.Allocate(destination.Length * 3)) + using (IMemoryOwner buffer = memoryManager.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -668,7 +668,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 (IBuffer buffer = memoryManager.Allocate(destination.Length * 3)) + using (IMemoryOwner buffer = memoryManager.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -707,7 +707,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 (IBuffer buffer = memoryManager.Allocate(destination.Length * 3)) + using (IMemoryOwner buffer = memoryManager.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -746,7 +746,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 (IBuffer buffer = memoryManager.Allocate(destination.Length * 3)) + using (IMemoryOwner buffer = memoryManager.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -785,7 +785,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 (IBuffer buffer = memoryManager.Allocate(destination.Length * 3)) + using (IMemoryOwner buffer = memoryManager.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -824,7 +824,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 (IBuffer buffer = memoryManager.Allocate(destination.Length * 3)) + using (IMemoryOwner buffer = 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 cf16f9705..4f627764f 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt @@ -12,13 +12,13 @@ // Licensed under the Apache License, Version 2.0. // +using System; +using System.Numerics; +using System.Buffers; +using SixLabors.Memory; + namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { - using System; - using System.Numerics; - using SixLabors.Memory; - - /// /// Collection of Porter Duff alpha blending functions applying different composition models. /// @@ -86,7 +86,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 (IBuffer buffer = memoryManager.Allocate(destination.Length * 3)) + using (IMemoryOwner buffer = 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/Processors/Normalization/HistogramEqualizationProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs index b834b8968..7b6209c30 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Advanced; @@ -43,8 +44,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization Span pixels = source.GetPixelSpan(); // Build the histogram of the grayscale levels. - using (IBuffer histogramBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean)) - using (IBuffer cdfBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean)) + using (IMemoryOwner histogramBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean)) + using (IMemoryOwner cdfBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean)) { Span histogram = histogramBuffer.GetSpan(); for (int i = 0; i < pixels.Length; i++) diff --git a/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor.cs index 797d388c0..d2e89fcd0 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; @@ -65,8 +66,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays int width = maxX - minX; - using (IBuffer colors = source.MemoryAllocator.Allocate(width)) - using (IBuffer amount = source.MemoryAllocator.Allocate(width)) + using (IMemoryOwner colors = source.MemoryAllocator.Allocate(width)) + using (IMemoryOwner amount = source.MemoryAllocator.Allocate(width)) { // Be careful! Do not capture colorSpan & amountSpan in the lambda below! Span colorSpan = colors.GetSpan(); diff --git a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs index 023643520..17d131413 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.Numerics; using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; @@ -111,7 +112,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays } int width = maxX - minX; - using (IBuffer rowColors = source.MemoryAllocator.Allocate(width)) + using (IMemoryOwner rowColors = source.MemoryAllocator.Allocate(width)) { // Be careful! Do not capture rowColorsSpan in the lambda below! Span rowColorsSpan = rowColors.GetSpan(); @@ -127,7 +128,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays configuration.ParallelOptions, y => { - using (IBuffer amounts = source.MemoryAllocator.Allocate(width)) + using (IMemoryOwner amounts = source.MemoryAllocator.Allocate(width)) { Span amountsSpan = amounts.GetSpan(); int offsetY = y - startY; diff --git a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs index 3789e6bf8..a306459d1 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.Numerics; using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; @@ -113,7 +114,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays } int width = maxX - minX; - using (IBuffer rowColors = source.MemoryAllocator.Allocate(width)) + using (IMemoryOwner rowColors = source.MemoryAllocator.Allocate(width)) { // Be careful! Do not capture rowColorsSpan in the lambda below! Span rowColorsSpan = rowColors.GetSpan(); @@ -129,7 +130,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays configuration.ParallelOptions, y => { - using (IBuffer amounts = source.MemoryAllocator.Allocate(width)) + using (IMemoryOwner amounts = source.MemoryAllocator.Allocate(width)) { Span amountsSpan = amounts.GetSpan(); int offsetY = y - startY; diff --git a/src/ImageSharp/Processing/Processors/Quantization/QuantizedFrame{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/QuantizedFrame{TPixel}.cs index 6c2e6173f..d0d79093c 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/QuantizedFrame{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/QuantizedFrame{TPixel}.cs @@ -2,6 +2,8 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; + using SixLabors.ImageSharp.PixelFormats; using SixLabors.Memory; @@ -15,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization public class QuantizedFrame : IDisposable where TPixel : struct, IPixel { - private IBuffer pixels; + private IMemoryOwner pixels; /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs index 9a2a84e81..80eefa9b3 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.Collections.Generic; using System.Numerics; using System.Runtime.CompilerServices; @@ -70,37 +71,37 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// /// Moment of P(c). /// - private IBuffer vwt; + private IMemoryOwner vwt; /// /// Moment of r*P(c). /// - private IBuffer vmr; + private IMemoryOwner vmr; /// /// Moment of g*P(c). /// - private IBuffer vmg; + private IMemoryOwner vmg; /// /// Moment of b*P(c). /// - private IBuffer vmb; + private IMemoryOwner vmb; /// /// Moment of a*P(c). /// - private IBuffer vma; + private IMemoryOwner vma; /// /// Moment of c^2*P(c). /// - private IBuffer m2; + private IMemoryOwner m2; /// /// Color space tag. /// - private IBuffer tag; + private IMemoryOwner tag; /// /// Maximum allowed color depth @@ -467,18 +468,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization Span vmaSpan = this.vma.GetSpan(); Span m2Span = this.m2.GetSpan(); - using (IBuffer volume = memoryAllocator.Allocate(IndexCount * IndexAlphaCount)) - using (IBuffer volumeR = memoryAllocator.Allocate(IndexCount * IndexAlphaCount)) - using (IBuffer volumeG = memoryAllocator.Allocate(IndexCount * IndexAlphaCount)) - using (IBuffer volumeB = memoryAllocator.Allocate(IndexCount * IndexAlphaCount)) - using (IBuffer volumeA = memoryAllocator.Allocate(IndexCount * IndexAlphaCount)) - using (IBuffer volume2 = memoryAllocator.Allocate(IndexCount * IndexAlphaCount)) - using (IBuffer area = memoryAllocator.Allocate(IndexAlphaCount)) - using (IBuffer areaR = memoryAllocator.Allocate(IndexAlphaCount)) - using (IBuffer areaG = memoryAllocator.Allocate(IndexAlphaCount)) - using (IBuffer areaB = memoryAllocator.Allocate(IndexAlphaCount)) - using (IBuffer areaA = memoryAllocator.Allocate(IndexAlphaCount)) - using (IBuffer area2 = memoryAllocator.Allocate(IndexAlphaCount)) + using (IMemoryOwner volume = memoryAllocator.Allocate(IndexCount * IndexAlphaCount)) + using (IMemoryOwner volumeR = memoryAllocator.Allocate(IndexCount * IndexAlphaCount)) + using (IMemoryOwner volumeG = memoryAllocator.Allocate(IndexCount * IndexAlphaCount)) + using (IMemoryOwner volumeB = memoryAllocator.Allocate(IndexCount * IndexAlphaCount)) + using (IMemoryOwner volumeA = memoryAllocator.Allocate(IndexCount * IndexAlphaCount)) + using (IMemoryOwner volume2 = memoryAllocator.Allocate(IndexCount * IndexAlphaCount)) + using (IMemoryOwner area = memoryAllocator.Allocate(IndexAlphaCount)) + using (IMemoryOwner areaR = memoryAllocator.Allocate(IndexAlphaCount)) + using (IMemoryOwner areaG = memoryAllocator.Allocate(IndexAlphaCount)) + using (IMemoryOwner areaB = memoryAllocator.Allocate(IndexAlphaCount)) + using (IMemoryOwner areaA = memoryAllocator.Allocate(IndexAlphaCount)) + using (IMemoryOwner area2 = memoryAllocator.Allocate(IndexAlphaCount)) { Span volumeSpan = volume.GetSpan(); Span volumeRSpan = volumeR.GetSpan(); @@ -791,7 +792,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization this.colorCube = new Box[this.colors]; float[] vv = new float[this.colors]; - ref var cube = ref this.colorCube[0]; + ref Box cube = ref this.colorCube[0]; cube.R0 = cube.G0 = cube.B0 = cube.A0 = 0; cube.R1 = cube.G1 = cube.B1 = IndexCount - 1; cube.A1 = IndexAlphaCount - 1; @@ -800,8 +801,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization for (int i = 1; i < this.colors; i++) { - ref var nextCube = ref this.colorCube[next]; - ref var currentCube = ref this.colorCube[i]; + ref Box nextCube = ref this.colorCube[next]; + ref Box currentCube = ref this.colorCube[i]; if (this.Cut(ref nextCube, ref currentCube)) { vv[next] = nextCube.Volume > 1 ? this.Variance(ref nextCube) : 0F; diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs index 8c9ab9a23..7f18faec3 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.Collections.Generic; using System.Linq; using System.Numerics; @@ -303,7 +304,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms sourceRectangle.Bottom, configuration, source.Width, - (int y, IBuffer tempRowBuffer) => + (int y, IMemoryOwner tempRowBuffer) => { ref Vector4 firstPassRow = ref MemoryMarshal.GetReference(firstPassPixels.GetRowSpan(y)); Span sourceRow = source.GetPixelRowSpan(y); diff --git a/src/ImageSharp/Processing/Processors/Transforms/WeightsWindow.cs b/src/ImageSharp/Processing/Processors/Transforms/WeightsWindow.cs index ebf2db4bf..19909aaba 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/WeightsWindow.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/WeightsWindow.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -32,7 +33,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// /// The buffer containing the weights values. /// - private readonly IBuffer buffer; + private readonly BufferManager buffer; /// /// Initializes a new instance of the struct. @@ -66,7 +67,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Span GetWindowSpan() => this.buffer.Slice(this.flatStartIndex, this.Length); + public Span GetWindowSpan() => this.buffer.GetSpan().Slice(this.flatStartIndex, this.Length); /// /// Computes the sum of vectors in 'rowSpan' weighted by weight values, pointed by this instance. diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs index ac396d42e..6f4195d6f 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs @@ -1,21 +1,24 @@ // ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk -{ - using System.Numerics; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using BenchmarkDotNet.Attributes; - using SixLabors.Memory; - using SixLabors.ImageSharp.PixelFormats; +using System.Buffers; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using BenchmarkDotNet.Attributes; +using SixLabors.Memory; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk +{ [Config(typeof(Config.ShortClr))] public abstract class PackFromVector4 where TPixel : struct, IPixel { - private IBuffer source; + private IMemoryOwner source; - private IBuffer destination; + private IMemoryOwner 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 c4c09f67c..33ad9203c 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs @@ -1,19 +1,21 @@ // ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk -{ - using System; - using BenchmarkDotNet.Attributes; +using System.Buffers; +using System; + +using BenchmarkDotNet.Attributes; - using SixLabors.Memory; - using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Memory; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk +{ public abstract class PackFromXyzw where TPixel : struct, IPixel { - private IBuffer destination; + private IMemoryOwner destination; - private IBuffer source; + private IMemoryOwner 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 3b988757d..75ca1206e 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs @@ -1,20 +1,22 @@ // ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk -{ - using System; - using System.Numerics; - using BenchmarkDotNet.Attributes; +using System.Buffers; +using System; +using System.Numerics; + +using BenchmarkDotNet.Attributes; - using SixLabors.Memory; - using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Memory; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk +{ public abstract class ToVector4 where TPixel : struct, IPixel { - private IBuffer source; + private IMemoryOwner source; - private IBuffer destination; + private IMemoryOwner 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 16743eb73..be1ff72d5 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs @@ -1,20 +1,21 @@ // ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk -{ - using System; - using System.Numerics; - using BenchmarkDotNet.Attributes; +using System.Buffers; +using System; + +using BenchmarkDotNet.Attributes; - using SixLabors.Memory; - using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Memory; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk +{ public abstract class ToXyz where TPixel : struct, IPixel { - private IBuffer source; + private IMemoryOwner source; - private IBuffer destination; + private IMemoryOwner 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 f947b6e8d..799be60cc 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs @@ -1,22 +1,20 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +using System.Buffers; +using BenchmarkDotNet.Attributes; + +using SixLabors.Memory; +using SixLabors.ImageSharp.PixelFormats; + // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk { - using BenchmarkDotNet.Attributes; - - using SixLabors.Memory; - using SixLabors.ImageSharp.PixelFormats; - public abstract class ToXyzw where TPixel : struct, IPixel { - private IBuffer source; + private IMemoryOwner source; - private IBuffer destination; + private IMemoryOwner 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 5fe8b2785..af16c5d74 100644 --- a/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs +++ b/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs @@ -3,6 +3,8 @@ // Licensed under the Apache License, Version 2.0. // +using System.Buffers; + namespace SixLabors.ImageSharp.Benchmarks { using System; @@ -24,7 +26,7 @@ namespace SixLabors.ImageSharp.Benchmarks Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (IBuffer buffer = Configuration.Default.MemoryAllocator.Allocate(destination.Length * 3)) + using (IMemoryOwner buffer = Configuration.Default.MemoryAllocator.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); @@ -59,7 +61,7 @@ namespace SixLabors.ImageSharp.Benchmarks { using (var image = new Image(800, 800)) { - using (IBuffer amounts = Configuration.Default.MemoryAllocator.Allocate(image.Width)) + using (IMemoryOwner amounts = Configuration.Default.MemoryAllocator.Allocate(image.Width)) { amounts.GetSpan().Fill(1); @@ -80,7 +82,7 @@ namespace SixLabors.ImageSharp.Benchmarks { using (var image = new Image(800, 800)) { - using (IBuffer amounts = Configuration.Default.MemoryAllocator.Allocate(image.Width)) + using (IMemoryOwner amounts = Configuration.Default.MemoryAllocator.Allocate(image.Width)) { amounts.GetSpan().Fill(1); Buffer2D pixels = image.GetRootFramePixelBuffer(); diff --git a/tests/ImageSharp.Benchmarks/Samplers/Glow.cs b/tests/ImageSharp.Benchmarks/Samplers/Glow.cs index 85c4fdd7c..f7f54f4eb 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/Glow.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/Glow.cs @@ -3,6 +3,8 @@ // Licensed under the Apache License, Version 2.0. // +using System.Buffers; + namespace SixLabors.ImageSharp.Benchmarks { @@ -102,7 +104,7 @@ namespace SixLabors.ImageSharp.Benchmarks } int width = maxX - minX; - using (IBuffer rowColors = Configuration.Default.MemoryAllocator.Allocate(width)) + using (IMemoryOwner rowColors = Configuration.Default.MemoryAllocator.Allocate(width)) { Buffer2D sourcePixels = source.PixelBuffer; rowColors.GetSpan().Fill(glowColor); diff --git a/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs b/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs index 066be4a73..252dbc869 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs @@ -12,13 +12,6 @@ namespace SixLabors.ImageSharp.Tests { public class WrapMemory { - [Fact] - public void ConsumedBuffer_IsMemoryOwner_ReturnsFalse() - { - var memory = new Memory(new int[55]); - var buffer = new ConsumedBuffer(memory); - Assert.False(buffer.IsMemoryOwner); - } } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Memory/ArrayPoolMemoryManagerTests.cs b/tests/ImageSharp.Tests/Memory/ArrayPoolMemoryManagerTests.cs index 14fbe8225..89bb9d95f 100644 --- a/tests/ImageSharp.Tests/Memory/ArrayPoolMemoryManagerTests.cs +++ b/tests/ImageSharp.Tests/Memory/ArrayPoolMemoryManagerTests.cs @@ -3,6 +3,8 @@ // ReSharper disable InconsistentNaming +using System.Buffers; + using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.Memory @@ -30,7 +32,7 @@ namespace SixLabors.ImageSharp.Tests.Memory private bool CheckIsRentingPooledBuffer(int length) where T : struct { - IBuffer buffer = this.MemoryAllocator.Allocate(length); + IMemoryOwner buffer = this.MemoryAllocator.Allocate(length); ref T ptrToPrevPosition0 = ref buffer.GetReference(); buffer.Dispose(); @@ -130,12 +132,12 @@ namespace SixLabors.ImageSharp.Tests.Memory [InlineData(AllocationOptions.Clean)] public void CleaningRequests_AreControlledByAllocationParameter_Clean(AllocationOptions options) { - using (IBuffer firstAlloc = this.MemoryAllocator.Allocate(42)) + using (IMemoryOwner firstAlloc = this.MemoryAllocator.Allocate(42)) { firstAlloc.GetSpan().Fill(666); } - using (IBuffer secondAlloc = this.MemoryAllocator.Allocate(42, options)) + using (IMemoryOwner secondAlloc = this.MemoryAllocator.Allocate(42, options)) { int expected = options == AllocationOptions.Clean ? 0 : 666; Assert.Equal(expected, secondAlloc.GetSpan()[0]); @@ -147,7 +149,7 @@ namespace SixLabors.ImageSharp.Tests.Memory [InlineData(true)] public void ReleaseRetainedResources_ReplacesInnerArrayPool(bool keepBufferAlive) { - IBuffer buffer = this.MemoryAllocator.Allocate(32); + IMemoryOwner buffer = this.MemoryAllocator.Allocate(32); ref int ptrToPrev0 = ref MemoryMarshal.GetReference(buffer.GetSpan()); if (!keepBufferAlive) @@ -165,7 +167,7 @@ namespace SixLabors.ImageSharp.Tests.Memory [Fact] public void ReleaseRetainedResources_DisposingPreviouslyAllocatedBuffer_IsAllowed() { - IBuffer buffer = this.MemoryAllocator.Allocate(32); + IMemoryOwner buffer = this.MemoryAllocator.Allocate(32); this.MemoryAllocator.ReleaseRetainedResources(); buffer.Dispose(); } @@ -181,11 +183,11 @@ namespace SixLabors.ImageSharp.Tests.Memory int arrayLengthThreshold = PoolSelectorThresholdInBytes / sizeof(int); - IBuffer small = this.MemoryAllocator.Allocate(arrayLengthThreshold - 1); + IMemoryOwner small = this.MemoryAllocator.Allocate(arrayLengthThreshold - 1); ref int ptr2Small = ref small.GetReference(); small.Dispose(); - IBuffer large = this.MemoryAllocator.Allocate(arrayLengthThreshold + 1); + IMemoryOwner large = this.MemoryAllocator.Allocate(arrayLengthThreshold + 1); Assert.False(Unsafe.AreSame(ref ptr2Small, ref large.GetReference())); } diff --git a/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs index af9c8bd5b..52d3929f6 100644 --- a/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs +++ b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -20,7 +21,7 @@ namespace SixLabors.ImageSharp.Tests.Memory // ReSharper disable once ClassNeverInstantiated.Local private class Assert : Xunit.Assert { - public static void SpanPointsTo(Span span, IBuffer buffer, int bufferOffset = 0) + public static void SpanPointsTo(Span span, IMemoryOwner buffer, int bufferOffset = 0) where T : struct { ref T actual = ref MemoryMarshal.GetReference(span); @@ -41,7 +42,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.Memory.Length); } } @@ -70,7 +71,7 @@ namespace SixLabors.ImageSharp.Tests.Memory // Assert.Equal(width * y, span.Start); Assert.Equal(width, span.Length); - Assert.SpanPointsTo(span, buffer.Buffer, width * y); + Assert.SpanPointsTo(span, buffer.Buffer.MemoryOwner, width * y); } } @@ -86,7 +87,7 @@ namespace SixLabors.ImageSharp.Tests.Memory // Assert.Equal(width * y + x, span.Start); Assert.Equal(width - x, span.Length); - Assert.SpanPointsTo(span, buffer.Buffer, width * y + x); + Assert.SpanPointsTo(span, buffer.Buffer.MemoryOwner, width * y + x); } } @@ -107,92 +108,24 @@ namespace SixLabors.ImageSharp.Tests.Memory Assert.True(Unsafe.AreSame(ref expected, ref actual)); } } - - public class SwapOrCopyContent - { - private MemoryAllocator MemoryAllocator { get; } = new TestMemoryAllocator(); - - [Fact] - public void WhenBothBuffersAreMemoryOwners_ShouldSwap() - { - using (Buffer2D a = this.MemoryAllocator.Allocate2D(10, 5)) - using (Buffer2D b = this.MemoryAllocator.Allocate2D(3, 7)) - { - IBuffer aa = a.Buffer; - IBuffer bb = b.Buffer; - Buffer2D.SwapOrCopyContent(a, b); - - Assert.Equal(bb, a.Buffer); - Assert.Equal(aa, b.Buffer); - - Assert.Equal(new Size(3, 7), a.Size()); - Assert.Equal(new Size(10, 5), b.Size()); - } - } - - [Fact] - public void WhenDestIsNotMemoryOwner_SameSize_ShouldCopy() + [Fact] + public void SwapOrCopyContent() + { + using (Buffer2D a = this.MemoryAllocator.Allocate2D(10, 5)) + using (Buffer2D b = this.MemoryAllocator.Allocate2D(3, 7)) { - var data = new Rgba32[3 * 7]; - var color = new Rgba32(1, 2, 3, 4); - - var mmg = new TestMemoryManager(data); - var aBuff = new ConsumedBuffer(mmg.Memory); - - using (Buffer2D a = new Buffer2D(aBuff, 3, 7)) - { - IBuffer aa = a.Buffer; + IMemoryOwner aa = a.Buffer.MemoryOwner; + IMemoryOwner bb = b.Buffer.MemoryOwner; - // Precondition: - Assert.Equal(aBuff, aa); + Buffer2D.SwapOrCopyContent(a, b); - using (Buffer2D b = this.MemoryAllocator.Allocate2D(3, 7)) - { - IBuffer bb = b.Buffer; - bb.GetSpan()[10] = color; + Assert.Equal(bb, a.Buffer.MemoryOwner); + Assert.Equal(aa, b.Buffer.MemoryOwner); - // Act: - Buffer2D.SwapOrCopyContent(a, b); - - // Assert: - Assert.Equal(aBuff, a.Buffer); - Assert.Equal(bb, b.Buffer); - } - - // Assert: - Assert.Equal(color, a.Buffer.GetSpan()[10]); - } + Assert.Equal(new Size(3, 7), a.Size()); + Assert.Equal(new Size(10, 5), b.Size()); } - - [Fact] - public void WhenDestIsNotMemoryOwner_DifferentSize_Throws() - { - var data = new Rgba32[3 * 7]; - var color = new Rgba32(1, 2, 3, 4); - data[10] = color; - - var mmg = new TestMemoryManager(data); - var aBuff = new ConsumedBuffer(mmg.Memory); - - using (Buffer2D a = new Buffer2D(aBuff, 3, 7)) - { - IBuffer aa = a.Buffer; - using (Buffer2D b = this.MemoryAllocator.Allocate2D(3, 8)) - { - IBuffer bb = b.Buffer; - - Assert.ThrowsAny( - () => - { - Buffer2D.SwapOrCopyContent(a, b); - }); - } - - Assert.Equal(color, a.Buffer.GetSpan()[10]); - } - } - } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Memory/BufferTestSuite.cs b/tests/ImageSharp.Tests/Memory/BufferTestSuite.cs index fd3331288..e57c13164 100644 --- a/tests/ImageSharp.Tests/Memory/BufferTestSuite.cs +++ b/tests/ImageSharp.Tests/Memory/BufferTestSuite.cs @@ -63,15 +63,6 @@ namespace SixLabors.ImageSharp.Tests.Memory public static readonly TheoryData LenthValues = new TheoryData { 0, 1, 7, 1023, 1024 }; - [Fact] - public void IsMemoryOwner() - { - using (IBuffer buffer = this.MemoryAllocator.Allocate(42)) - { - Assert.True(buffer.IsMemoryOwner); - } - } - [Theory] [MemberData(nameof(LenthValues))] public void HasCorrectLength_byte(int desiredLength) @@ -96,7 +87,7 @@ namespace SixLabors.ImageSharp.Tests.Memory private void TestHasCorrectLength(int desiredLength) where T : struct { - using (IBuffer buffer = this.MemoryAllocator.Allocate(desiredLength)) + using (IMemoryOwner buffer = this.MemoryAllocator.Allocate(desiredLength)) { Assert.Equal(desiredLength, buffer.GetSpan().Length); } @@ -124,12 +115,12 @@ namespace SixLabors.ImageSharp.Tests.Memory this.TestCanAllocateCleanBuffer(desiredLength); } - private IBuffer Allocate(int desiredLength, AllocationOptions options, bool managedByteBuffer) + private IMemoryOwner Allocate(int desiredLength, AllocationOptions options, bool managedByteBuffer) where T : struct { if (managedByteBuffer) { - if (!(this.MemoryAllocator.AllocateManagedByteBuffer(desiredLength, options) is IBuffer buffer)) + if (!(this.MemoryAllocator.AllocateManagedByteBuffer(desiredLength, options) is IMemoryOwner buffer)) { throw new InvalidOperationException("typeof(T) != typeof(byte)"); } @@ -147,7 +138,7 @@ namespace SixLabors.ImageSharp.Tests.Memory for (int i = 0; i < 10; i++) { - using (IBuffer buffer = this.Allocate(desiredLength, AllocationOptions.Clean, testManagedByteBuffer)) + using (IMemoryOwner buffer = this.Allocate(desiredLength, AllocationOptions.Clean, testManagedByteBuffer)) { Assert.True(buffer.GetSpan().SequenceEqual(expected)); } @@ -172,7 +163,7 @@ namespace SixLabors.ImageSharp.Tests.Memory private void TestSpanPropertyIsAlwaysTheSame(int desiredLength, bool testManagedByteBuffer = false) where T : struct { - using (IBuffer buffer = this.Allocate(desiredLength, AllocationOptions.None, testManagedByteBuffer)) + using (IMemoryOwner buffer = this.Allocate(desiredLength, AllocationOptions.None, testManagedByteBuffer)) { ref T a = ref MemoryMarshal.GetReference(buffer.GetSpan()); ref T b = ref MemoryMarshal.GetReference(buffer.GetSpan()); @@ -201,7 +192,7 @@ namespace SixLabors.ImageSharp.Tests.Memory private void TestWriteAndReadElements(int desiredLength, Func getExpectedValue, bool testManagedByteBuffer = false) where T : struct { - using (IBuffer buffer = this.Allocate(desiredLength, AllocationOptions.None, testManagedByteBuffer)) + using (IMemoryOwner buffer = this.Allocate(desiredLength, AllocationOptions.None, testManagedByteBuffer)) { T[] expectedVals = new T[buffer.Length()]; @@ -247,7 +238,7 @@ namespace SixLabors.ImageSharp.Tests.Memory { var dummy = default(T); - using (IBuffer buffer = this.Allocate(desiredLength, AllocationOptions.None, testManagedByteBuffer)) + using (IMemoryOwner buffer = this.Allocate(desiredLength, AllocationOptions.None, testManagedByteBuffer)) { Assert.ThrowsAny( () => @@ -294,7 +285,7 @@ namespace SixLabors.ImageSharp.Tests.Memory [Fact] public void GetMemory_ReturnsValidMemory() { - using (IBuffer buffer = this.MemoryAllocator.Allocate(42)) + using (IMemoryOwner buffer = this.MemoryAllocator.Allocate(42)) { Span span0 = buffer.GetSpan(); span0[10].A = 30; @@ -311,7 +302,7 @@ namespace SixLabors.ImageSharp.Tests.Memory [Fact] public unsafe void GetMemory_ResultIsPinnable() { - using (IBuffer buffer = this.MemoryAllocator.Allocate(42)) + using (IMemoryOwner buffer = this.MemoryAllocator.Allocate(42)) { Span span0 = buffer.GetSpan(); span0[10] = 30; diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs index c0e69a902..ca7e48d0b 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -57,8 +58,8 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats int times = 200000; int count = 1024; - using (IBuffer source = Configuration.Default.MemoryAllocator.Allocate(count)) - using (IBuffer dest = Configuration.Default.MemoryAllocator.Allocate(count)) + using (IMemoryOwner source = Configuration.Default.MemoryAllocator.Allocate(count)) + using (IMemoryOwner dest = Configuration.Default.MemoryAllocator.Allocate(count)) { this.Measure( times, @@ -537,7 +538,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats where TDest : struct { public TSource[] SourceBuffer { get; } - public IBuffer ActualDestBuffer { get; } + public IMemoryOwner ActualDestBuffer { get; } public TDest[] ExpectedDestBuffer { get; } public TestBuffers(TSource[] source, TDest[] expectedDest) @@ -586,7 +587,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 f0daa0abb..7cc369244 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.Drawing; using System.Drawing.Imaging; @@ -43,7 +44,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs var image = new Image(w, h); - using (IBuffer workBuffer = Configuration.Default.MemoryAllocator.Allocate(w)) + using (IMemoryOwner workBuffer = Configuration.Default.MemoryAllocator.Allocate(w)) { fixed (Bgra32* destPtr = &workBuffer.GetReference()) { @@ -89,7 +90,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs var image = new Image(w, h); - using (IBuffer workBuffer = Configuration.Default.MemoryAllocator.Allocate(w)) + using (IMemoryOwner workBuffer = Configuration.Default.MemoryAllocator.Allocate(w)) { fixed (Bgr24* destPtr = &workBuffer.GetReference()) { @@ -122,7 +123,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs long destRowByteCount = data.Stride; long sourceRowByteCount = w * sizeof(Bgra32); - using (IBuffer workBuffer = image.GetConfiguration().MemoryAllocator.Allocate(w)) + using (IMemoryOwner workBuffer = image.GetConfiguration().MemoryAllocator.Allocate(w)) { fixed (Bgra32* sourcePtr = &workBuffer.GetReference()) { diff --git a/tests/ImageSharp.Tests/TestUtilities/TestMemoryAllocator.cs b/tests/ImageSharp.Tests/TestUtilities/TestMemoryAllocator.cs index 7993b3e99..a580fc7ad 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestMemoryAllocator.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestMemoryAllocator.cs @@ -1,4 +1,5 @@ using System; +using System.Buffers; using System.Runtime.InteropServices; using SixLabors.Memory; @@ -17,7 +18,7 @@ namespace SixLabors.ImageSharp.Tests.Memory /// public byte DirtyValue { get; } - internal override IBuffer Allocate(int length, AllocationOptions options = AllocationOptions.None) + internal override IMemoryOwner Allocate(int length, AllocationOptions options = AllocationOptions.None) { T[] array = this.AllocateArray(length, options); diff --git a/tests/ImageSharp.Tests/TestUtilities/TestMemoryManager.cs b/tests/ImageSharp.Tests/TestUtilities/TestMemoryManager.cs index ce3cfbc9e..9274e5727 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestMemoryManager.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestMemoryManager.cs @@ -3,9 +3,6 @@ using System.Buffers; namespace SixLabors.ImageSharp.Tests { - using SixLabors.ImageSharp.Advanced; - using SixLabors.ImageSharp.PixelFormats; - class TestMemoryManager : MemoryManager where T : struct { From 3ed641f660cc7c99b80cb825ff25ef0c2cecb056 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 22 Jul 2018 19:29:25 +0200 Subject: [PATCH 03/13] FillRegion_WorksOnWrappedMemoryImage --- src/ImageSharp/ImageFrameCollection.cs | 4 ++-- src/ImageSharp/ImageFrame{TPixel}.cs | 8 ++++---- src/ImageSharp/Image{TPixel}.cs | 8 ++++---- .../Advanced/AdvancedImageExtensionsTests.cs | 3 +-- .../Drawing/FillSolidBrushTests.cs | 17 +++++++++++++++++ .../Drawing/SolidBezierTests.cs | 1 + .../ImageSharp.Tests/TestUtilities/TestUtils.cs | 4 ++++ 7 files changed, 33 insertions(+), 12 deletions(-) diff --git a/src/ImageSharp/ImageFrameCollection.cs b/src/ImageSharp/ImageFrameCollection.cs index 4b8f90e9b..3c1062df3 100644 --- a/src/ImageSharp/ImageFrameCollection.cs +++ b/src/ImageSharp/ImageFrameCollection.cs @@ -30,14 +30,14 @@ namespace SixLabors.ImageSharp this.frames.Add(new ImageFrame(parent.GetConfiguration(), width, height, backgroundColor)); } - internal ImageFrameCollection(Image parent, int width, int height, Memory consumedBuffer) + internal ImageFrameCollection(Image parent, int width, int height, Memory consumedMemory) { Guard.NotNull(parent, nameof(parent)); this.parent = parent; // Frames are already cloned within the caller - this.frames.Add(new ImageFrame(parent.GetConfiguration(), width, height, consumedBuffer)); + this.frames.Add(new ImageFrame(parent.GetConfiguration(), width, height, consumedMemory)); } internal ImageFrameCollection(Image parent, IEnumerable> frames) diff --git a/src/ImageSharp/ImageFrame{TPixel}.cs b/src/ImageSharp/ImageFrame{TPixel}.cs index 2fa35f1e5..370b3763c 100644 --- a/src/ImageSharp/ImageFrame{TPixel}.cs +++ b/src/ImageSharp/ImageFrame{TPixel}.cs @@ -95,8 +95,8 @@ namespace SixLabors.ImageSharp /// /// Initializes a new instance of the class wrapping an existing buffer. /// - internal ImageFrame(Configuration configuration, int width, int height, Memory consumedBuffer) - : this(configuration, width, height, consumedBuffer, new ImageFrameMetaData()) + internal ImageFrame(Configuration configuration, int width, int height, Memory consumedMemory) + : this(configuration, width, height, consumedMemory, new ImageFrameMetaData()) { } @@ -107,7 +107,7 @@ namespace SixLabors.ImageSharp Configuration configuration, int width, int height, - Memory consumedBuffer, + Memory consumedMemory, ImageFrameMetaData metaData) { Guard.NotNull(configuration, nameof(configuration)); @@ -117,7 +117,7 @@ namespace SixLabors.ImageSharp this.configuration = configuration; this.MemoryAllocator = configuration.MemoryAllocator; - this.PixelBuffer = new Buffer2D(consumedBuffer, width, height); + this.PixelBuffer = new Buffer2D(consumedMemory, width, height); this.MetaData = metaData; } diff --git a/src/ImageSharp/Image{TPixel}.cs b/src/ImageSharp/Image{TPixel}.cs index 9126812dd..316c381c4 100644 --- a/src/ImageSharp/Image{TPixel}.cs +++ b/src/ImageSharp/Image{TPixel}.cs @@ -86,8 +86,8 @@ namespace SixLabors.ImageSharp /// Initializes a new instance of the class /// consuming an external buffer instance. /// - internal Image(Configuration configuration, Memory consumedBuffer, int width, int height) - : this(configuration, consumedBuffer, width, height, new ImageMetaData()) + internal Image(Configuration configuration, Memory consumedMemory, int width, int height) + : this(configuration, consumedMemory, width, height, new ImageMetaData()) { } @@ -95,12 +95,12 @@ namespace SixLabors.ImageSharp /// Initializes a new instance of the class /// consuming an external buffer instance. /// - internal Image(Configuration configuration, Memory consumedBuffer, int width, int height, ImageMetaData metadata) + internal Image(Configuration configuration, Memory consumedMemory, int width, int height, ImageMetaData metadata) { this.configuration = configuration; this.PixelType = new PixelTypeInfo(Unsafe.SizeOf() * 8); this.MetaData = metadata; - this.frames = new ImageFrameCollection(this, width, height, consumedBuffer); + this.frames = new ImageFrameCollection(this, width, height, consumedMemory); } /// diff --git a/tests/ImageSharp.Tests/Advanced/AdvancedImageExtensionsTests.cs b/tests/ImageSharp.Tests/Advanced/AdvancedImageExtensionsTests.cs index 366266652..974099991 100644 --- a/tests/ImageSharp.Tests/Advanced/AdvancedImageExtensionsTests.cs +++ b/tests/ImageSharp.Tests/Advanced/AdvancedImageExtensionsTests.cs @@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.Tests.Advanced Memory externalMemory = managerOfExeternalMemory.Memory; - using (Image image1 = Image.WrapMemory(externalMemory, image0.Width, image0.Height)) + using (var image1 = Image.WrapMemory(externalMemory, image0.Width, image0.Height)) { Memory internalMemory = image1.GetPixelMemory(); Assert.Equal(targetBuffer.Length, internalMemory.Length); @@ -72,7 +72,6 @@ namespace SixLabors.ImageSharp.Tests.Advanced image0.ComparePixelBufferTo(externalMemory.Span); } } - } [Theory] diff --git a/tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs b/tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs index 1f01d54f4..45f1340be 100644 --- a/tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs +++ b/tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs @@ -81,6 +81,23 @@ namespace SixLabors.ImageSharp.Tests.Drawing provider.RunValidatingProcessorTest(c => c.Fill(color, region), testDetails, ImageComparer.Exact); } + [Theory] + [WithSolidFilledImages(16, 16, "Red", PixelTypes.Rgba32, 5, 7, 3, 8)] + [WithSolidFilledImages(16, 16, "Red", PixelTypes.Rgba32, 8, 5, 6, 4)] + public void FillRegion_WorksOnWrappedMemoryImage(TestImageProvider provider, int x0, int y0, int w, int h) + where TPixel : struct, IPixel + { + FormattableString testDetails = $"(x{x0},y{y0},w{w},h{h})"; + var region = new RectangleF(x0, y0, w, h); + TPixel color = TestUtils.GetPixelOfNamedColor("Blue"); + + provider.RunValidatingProcessorTestOnWrappedMemoryImage( + c => c.Fill(color, region), + testDetails, + ImageComparer.Exact, + useReferenceOutputFrom: nameof(this.FillRegion)); + } + public static readonly TheoryData BlendData = new TheoryData() { diff --git a/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs b/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs index 94d3d49ff..23acc1a44 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs @@ -38,6 +38,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing } } + [Theory] [WithBlankImages(500, 500, PixelTypes.Rgba32)] public void OverlayByFilledPolygonOpacity(TestImageProvider provider) diff --git a/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs b/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs index a6ea76f2d..81310c1a0 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs @@ -228,6 +228,8 @@ namespace SixLabors.ImageSharp.Tests // TODO: Investigate the cause of pixel inaccuracies under Linux if (TestEnvironment.IsWindows) { + string testNameBackup = provider.Utility.TestName; + if (useReferenceOutputFrom != null) { provider.Utility.TestName = useReferenceOutputFrom; @@ -239,6 +241,8 @@ namespace SixLabors.ImageSharp.Tests testOutputDetails, appendPixelTypeToFileName: appendPixelTypeToFileName, appendSourceFileOrDescription: appendSourceFileOrDescription); + + provider.Utility.TestName = testNameBackup; } } } From 7c53bc17850be15b98a77daa0dddb7b00a029213 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 22 Jul 2018 19:54:37 +0200 Subject: [PATCH 04/13] made WrapMemory() public + test case demonstrating using Image.WrapMemory() to draw over a System.Drawing.Bitmap instance --- src/ImageSharp/Image.WrapMemory.cs | 4 +- src/ImageSharp/PixelFormats/Bgra32.cs | 1 + .../Image/ImageTests.WrapMemory.cs | 74 +++++++++++++++++++ 3 files changed, 77 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/Image.WrapMemory.cs b/src/ImageSharp/Image.WrapMemory.cs index 3ccd809f4..7f0c4ae2e 100644 --- a/src/ImageSharp/Image.WrapMemory.cs +++ b/src/ImageSharp/Image.WrapMemory.cs @@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp /// The height of the memory image /// The /// An instance - internal static Image WrapMemory( + public static Image WrapMemory( Configuration config, Memory pixelMemory, int width, @@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp /// The width of the memory image /// The height of the memory image /// An instance - internal static Image WrapMemory( + public static Image WrapMemory( Memory pixelMemory, int width, int height) diff --git a/src/ImageSharp/PixelFormats/Bgra32.cs b/src/ImageSharp/PixelFormats/Bgra32.cs index f951be881..14b2da07c 100644 --- a/src/ImageSharp/PixelFormats/Bgra32.cs +++ b/src/ImageSharp/PixelFormats/Bgra32.cs @@ -11,6 +11,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// Packed pixel type containing four 8-bit unsigned normalized values ranging from 0 to 255. /// The color components are stored in blue, green, red, and alpha order (least significant to most significant byte). + /// The format is binary compatible with System.Drawing.Imaging.PixelFormat.Format32bppArgb /// /// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form. /// diff --git a/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs b/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs index 252dbc869..57f757176 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs @@ -2,7 +2,14 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; +using System.Drawing; +using System.Drawing.Imaging; + +using SixLabors.ImageSharp.PixelFormats; using SixLabors.Memory; +using SixLabors.Shapes; +using SixLabors.ImageSharp.Processing; using Xunit; // ReSharper disable InconsistentNaming @@ -12,6 +19,73 @@ namespace SixLabors.ImageSharp.Tests { public class WrapMemory { + class BitmapMemoryManager : MemoryManager + { + private System.Drawing.Bitmap bitmap; + + private BitmapData bmpData; + + private int length; + + public BitmapMemoryManager(Bitmap bitmap) + { + if (bitmap.PixelFormat != PixelFormat.Format32bppArgb) + { + throw new ArgumentException("bitmap.PixelFormat != PixelFormat.Format32bppArgb", nameof(bitmap)); + } + + this.bitmap = bitmap; + var rectangle = new Rectangle(0, 0, bitmap.Width, bitmap.Height); + this.bmpData = bitmap.LockBits(rectangle, ImageLockMode.ReadWrite, bitmap.PixelFormat); + this.length = bitmap.Width * bitmap.Height; + } + + protected override void Dispose(bool disposing) + { + this.bitmap.UnlockBits(this.bmpData); + } + + public override unsafe Span GetSpan() + { + void* ptr = (void*) this.bmpData.Scan0; + return new Span(ptr, this.length); + } + + public override unsafe MemoryHandle Pin(int elementIndex = 0) + { + void* ptr = (void*)this.bmpData.Scan0; + return new MemoryHandle(ptr); + } + + public override void Unpin() + { + } + } + + [Fact] + public void WrapSystemDrawingBitmap() + { + using (var bmp = new Bitmap(51, 23)) + { + using (var memoryManager = new BitmapMemoryManager(bmp)) + { + Memory memory = memoryManager.Memory; + Bgra32 bg = NamedColors.Red; + Bgra32 fg = NamedColors.Green; + + using (var image = Image.WrapMemory(memory, bmp.Width, bmp.Height)) + { + image.Mutate(c => c.Fill(bg).Fill(fg, new RectangularPolygon(10, 10, 10, 10))); + } + } + + string fn = System.IO.Path.Combine( + TestEnvironment.ActualOutputDirectoryFullPath, + "WrapSystemDrawingBitmap.bmp"); + + bmp.Save(fn, ImageFormat.Bmp); + } + } } } } \ No newline at end of file From 7f48800cef2b77bc2378ca8a9f5ca7cc9ed871e7 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 23 Jul 2018 00:20:14 +0200 Subject: [PATCH 05/13] kick AppVeyor --- build.ps1 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/build.ps1 b/build.ps1 index 4c5a36cae..35b8344dc 100644 --- a/build.ps1 +++ b/build.ps1 @@ -8,7 +8,7 @@ $tagRegex = '^v?(\d+\.\d+\.\d+)(-([a-zA-Z]+)\.?(\d*))?$' # we are running on the build server $isVersionTag = $env:APPVEYOR_REPO_TAG_NAME -match $tagRegex - if($isVersionTag){ + if($isVersionTag) { Write-Debug "Building commit tagged with a compatable version number" @@ -26,7 +26,8 @@ $isVersionTag = $env:APPVEYOR_REPO_TAG_NAME -match $tagRegex $version = "${version}${padded}" } - }else { + } + else { Write-Debug "Untagged" $lastTag = (git tag --list --sort=-taggerdate) | Out-String From f1cb97270e57a242e8f51ac292575b38fa193d50 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 24 Jul 2018 00:39:47 +0200 Subject: [PATCH 06/13] renamed BufferManager to MemorySource + enable consuming external IMemoryOwner --- .../Advanced/AdvancedImageExtensions.cs | 2 +- src/ImageSharp/Image.WrapMemory.cs | 5 +- src/ImageSharp/ImageFrameCollection.cs | 4 +- src/ImageSharp/ImageFrame{TPixel}.cs | 8 +-- src/ImageSharp/Image{TPixel}.cs | 15 +--- src/ImageSharp/Memory/Buffer2DExtensions.cs | 4 +- src/ImageSharp/Memory/Buffer2D{T}.cs | 28 +++----- .../Memory/MemoryAllocatorExtensions.cs | 3 +- .../{BufferManager.cs => MemorySource.cs} | 34 ++++++--- .../Processors/Transforms/ResizeProcessor.cs | 2 +- .../Processors/Transforms/WeightsWindow.cs | 4 +- .../Formats/Jpg/JpegColorConverterTests.cs | 3 +- .../Formats/Jpg/SpectralJpegTests.cs | 2 +- .../ImageSharp.Tests/Memory/Buffer2DTests.cs | 14 ++-- ...erManagerTests.cs => MemorySourceTests.cs} | 69 ++++++++++--------- 15 files changed, 101 insertions(+), 96 deletions(-) rename src/ImageSharp/Memory/{BufferManager.cs => MemorySource.cs} (57%) rename tests/ImageSharp.Tests/Memory/{BufferManagerTests.cs => MemorySourceTests.cs} (60%) diff --git a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs index 18b1d994b..1c73b5ed1 100644 --- a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs +++ b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs @@ -105,7 +105,7 @@ namespace SixLabors.ImageSharp.Advanced internal static Memory GetPixelMemory(this ImageFrame source) where TPixel : struct, IPixel { - return source.PixelBuffer.Buffer.Memory; + return source.PixelBuffer.MemorySource.Memory; } /// diff --git a/src/ImageSharp/Image.WrapMemory.cs b/src/ImageSharp/Image.WrapMemory.cs index 7f0c4ae2e..c24a932e1 100644 --- a/src/ImageSharp/Image.WrapMemory.cs +++ b/src/ImageSharp/Image.WrapMemory.cs @@ -13,8 +13,6 @@ namespace SixLabors.ImageSharp /// public static partial class Image { - // TODO: This is a WIP API, should be public when finished. - /// /// Wraps an existing contigous memory area of 'width'x'height' pixels, /// allowing to view/manipulate it as an ImageSharp instance. @@ -34,7 +32,8 @@ namespace SixLabors.ImageSharp ImageMetaData metaData) where TPixel : struct, IPixel { - return new Image(config, pixelMemory, width, height, metaData); + var memorySource = new MemorySource(pixelMemory); + return new Image(config, memorySource, width, height, metaData); } /// diff --git a/src/ImageSharp/ImageFrameCollection.cs b/src/ImageSharp/ImageFrameCollection.cs index 3c1062df3..154ef5014 100644 --- a/src/ImageSharp/ImageFrameCollection.cs +++ b/src/ImageSharp/ImageFrameCollection.cs @@ -30,14 +30,14 @@ namespace SixLabors.ImageSharp this.frames.Add(new ImageFrame(parent.GetConfiguration(), width, height, backgroundColor)); } - internal ImageFrameCollection(Image parent, int width, int height, Memory consumedMemory) + internal ImageFrameCollection(Image parent, int width, int height, MemorySource memorySource) { Guard.NotNull(parent, nameof(parent)); this.parent = parent; // Frames are already cloned within the caller - this.frames.Add(new ImageFrame(parent.GetConfiguration(), width, height, consumedMemory)); + this.frames.Add(new ImageFrame(parent.GetConfiguration(), width, height, memorySource)); } internal ImageFrameCollection(Image parent, IEnumerable> frames) diff --git a/src/ImageSharp/ImageFrame{TPixel}.cs b/src/ImageSharp/ImageFrame{TPixel}.cs index 370b3763c..6c04d5aea 100644 --- a/src/ImageSharp/ImageFrame{TPixel}.cs +++ b/src/ImageSharp/ImageFrame{TPixel}.cs @@ -95,8 +95,8 @@ namespace SixLabors.ImageSharp /// /// Initializes a new instance of the class wrapping an existing buffer. /// - internal ImageFrame(Configuration configuration, int width, int height, Memory consumedMemory) - : this(configuration, width, height, consumedMemory, new ImageFrameMetaData()) + internal ImageFrame(Configuration configuration, int width, int height, MemorySource memorySource) + : this(configuration, width, height, memorySource, new ImageFrameMetaData()) { } @@ -107,7 +107,7 @@ namespace SixLabors.ImageSharp Configuration configuration, int width, int height, - Memory consumedMemory, + MemorySource memorySource, ImageFrameMetaData metaData) { Guard.NotNull(configuration, nameof(configuration)); @@ -117,7 +117,7 @@ namespace SixLabors.ImageSharp this.configuration = configuration; this.MemoryAllocator = configuration.MemoryAllocator; - this.PixelBuffer = new Buffer2D(consumedMemory, width, height); + this.PixelBuffer = new Buffer2D(memorySource, width, height); this.MetaData = metaData; } diff --git a/src/ImageSharp/Image{TPixel}.cs b/src/ImageSharp/Image{TPixel}.cs index 316c381c4..5a5928d6b 100644 --- a/src/ImageSharp/Image{TPixel}.cs +++ b/src/ImageSharp/Image{TPixel}.cs @@ -84,23 +84,14 @@ namespace SixLabors.ImageSharp /// /// Initializes a new instance of the class - /// consuming an external buffer instance. + /// wrapping an external /// - internal Image(Configuration configuration, Memory consumedMemory, int width, int height) - : this(configuration, consumedMemory, width, height, new ImageMetaData()) - { - } - - /// - /// Initializes a new instance of the class - /// consuming an external buffer instance. - /// - internal Image(Configuration configuration, Memory consumedMemory, int width, int height, ImageMetaData metadata) + internal Image(Configuration configuration, MemorySource memorySource, int width, int height, ImageMetaData metadata) { this.configuration = configuration; this.PixelType = new PixelTypeInfo(Unsafe.SizeOf() * 8); this.MetaData = metadata; - this.frames = new ImageFrameCollection(this, width, height, consumedMemory); + this.frames = new ImageFrameCollection(this, width, height, memorySource); } /// diff --git a/src/ImageSharp/Memory/Buffer2DExtensions.cs b/src/ImageSharp/Memory/Buffer2DExtensions.cs index c27752570..be4f0ef15 100644 --- a/src/ImageSharp/Memory/Buffer2DExtensions.cs +++ b/src/ImageSharp/Memory/Buffer2DExtensions.cs @@ -18,7 +18,7 @@ namespace SixLabors.Memory internal static Span GetSpan(this Buffer2D buffer) where T : struct { - return buffer.Buffer.GetSpan(); + return buffer.MemorySource.GetSpan(); } /// @@ -61,7 +61,7 @@ namespace SixLabors.Memory public static Memory GetRowMemory(this Buffer2D buffer, int y) where T : struct { - return buffer.Buffer.Memory.Slice(y * buffer.Width, buffer.Width); + return buffer.MemorySource.Memory.Slice(y * buffer.Width, buffer.Width); } /// diff --git a/src/ImageSharp/Memory/Buffer2D{T}.cs b/src/ImageSharp/Memory/Buffer2D{T}.cs index 7d331b46d..844ca1ad1 100644 --- a/src/ImageSharp/Memory/Buffer2D{T}.cs +++ b/src/ImageSharp/Memory/Buffer2D{T}.cs @@ -16,31 +16,21 @@ namespace SixLabors.Memory internal sealed class Buffer2D : IDisposable where T : struct { - private BufferManager buffer; + private MemorySource memorySource; /// /// Initializes a new instance of the class. /// - /// The buffer to wrap + /// The buffer to wrap /// The number of elements in a row /// The number of rows - public Buffer2D(BufferManager wrappedBuffer, int width, int height) + public Buffer2D(MemorySource memorySource, int width, int height) { - this.buffer = wrappedBuffer; + this.memorySource = memorySource; this.Width = width; this.Height = height; } - public Buffer2D(IMemoryOwner ownedMemory, int width, int height) - : this(new BufferManager(ownedMemory), width, height) - { - } - - public Buffer2D(Memory observedMemory, int width, int height) - : this(new BufferManager(observedMemory), width, height) - { - } - /// /// Gets the width. /// @@ -52,11 +42,11 @@ namespace SixLabors.Memory public int Height { get; private set; } /// - /// Gets the backing + /// Gets the backing /// - public BufferManager Buffer => this.buffer; + public MemorySource MemorySource => this.memorySource; - public Memory Memory => this.Buffer.Memory; + public Memory Memory => this.MemorySource.Memory; public Span Span => this.Memory.Span; @@ -83,7 +73,7 @@ namespace SixLabors.Memory /// public void Dispose() { - this.Buffer.Dispose(); + this.MemorySource.Dispose(); } /// @@ -92,7 +82,7 @@ namespace SixLabors.Memory /// public static void SwapOrCopyContent(Buffer2D destination, Buffer2D source) { - BufferManager.SwapOrCopyContent(ref destination.buffer, ref source.buffer); + MemorySource.SwapOrCopyContent(ref destination.memorySource, ref source.memorySource); SwapDimensionData(destination, source); } diff --git a/src/ImageSharp/Memory/MemoryAllocatorExtensions.cs b/src/ImageSharp/Memory/MemoryAllocatorExtensions.cs index 327bc8bbf..d8c1f51f4 100644 --- a/src/ImageSharp/Memory/MemoryAllocatorExtensions.cs +++ b/src/ImageSharp/Memory/MemoryAllocatorExtensions.cs @@ -17,8 +17,9 @@ namespace SixLabors.Memory where T : struct { IMemoryOwner buffer = memoryAllocator.Allocate(width * height, options); + var memorySource = new MemorySource(buffer, true); - return new Buffer2D(buffer, width, height); + return new Buffer2D(memorySource, width, height); } public static Buffer2D Allocate2D( diff --git a/src/ImageSharp/Memory/BufferManager.cs b/src/ImageSharp/Memory/MemorySource.cs similarity index 57% rename from src/ImageSharp/Memory/BufferManager.cs rename to src/ImageSharp/Memory/MemorySource.cs index 1a53890d4..27bca11c1 100644 --- a/src/ImageSharp/Memory/BufferManager.cs +++ b/src/ImageSharp/Memory/MemorySource.cs @@ -9,32 +9,50 @@ namespace SixLabors.Memory /// /// Holds a that is either OWNED or CONSUMED. /// Implements content transfer logic in that depends on the ownership status. - /// This is needed to transfer the contents of a temporary to a persistent + /// This is needed to transfer the contents of a temporary + /// to a persistent without copying the buffer. /// /// /// For a deeper understanding of the owner/consumer model, check out the following docs:
/// https://gist.github.com/GrabYourPitchforks/4c3e1935fd4d9fa2831dbfcab35dffc6 /// https://www.codemag.com/Article/1807051/Introducing-.NET-Core-2.1-Flagship-Types-Span-T-and-Memory-T ///
- internal struct BufferManager : IDisposable + internal struct MemorySource : IDisposable { - public BufferManager(IMemoryOwner memoryOwner) + /// + /// Initializes a new instance of the struct + /// by wrapping an existing . + /// + /// The to wrap + /// + /// A value indicating whether is an internal memory source managed by ImageSharp. + /// Eg. allocated by a . + /// + public MemorySource(IMemoryOwner memoryOwner, bool isInternalMemorySource) { this.MemoryOwner = memoryOwner; this.Memory = memoryOwner.Memory; + this.HasSwappableContents = isInternalMemorySource; } - public BufferManager(Memory memory) + public MemorySource(Memory memory) { this.Memory = memory; this.MemoryOwner = null; + this.HasSwappableContents = false; } public IMemoryOwner MemoryOwner { get; private set; } public Memory Memory { get; private set; } - public bool OwnsMemory => this.MemoryOwner != null; + /// + /// Gets a value indicating whether we are allowed to swap the contents of this buffer + /// with an other instance. + /// The value is true only and only if is present, + /// and it's coming from an internal source managed by ImageSharp (). + /// + public bool HasSwappableContents { get; } public Span GetSpan() => this.Memory.Span; @@ -44,9 +62,9 @@ namespace SixLabors.Memory /// Swaps the contents of 'destination' with 'source' if the buffers are owned (1), /// copies the contents of 'source' to 'destination' otherwise (2). Buffers should be of same size in case 2! ///
- public static void SwapOrCopyContent(ref BufferManager destination, ref BufferManager source) + public static void SwapOrCopyContent(ref MemorySource destination, ref MemorySource source) { - if (source.OwnsMemory && destination.OwnsMemory) + if (source.HasSwappableContents && destination.HasSwappableContents) { SwapContents(ref destination, ref source); } @@ -67,7 +85,7 @@ namespace SixLabors.Memory this.MemoryOwner?.Dispose(); } - private static void SwapContents(ref BufferManager a, ref BufferManager b) + private static void SwapContents(ref MemorySource a, ref MemorySource b) { IMemoryOwner tempOwner = a.MemoryOwner; Memory tempMemory = a.Memory; diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs index 7f18faec3..9c09b6a22 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs @@ -297,7 +297,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms // TODO: Using a transposed variant of 'firstPassPixels' could eliminate the need for the WeightsWindow.ComputeWeightedColumnSum() method, and improve speed! using (Buffer2D firstPassPixels = source.MemoryAllocator.Allocate2D(width, source.Height)) { - firstPassPixels.Buffer.Clear(); + firstPassPixels.MemorySource.Clear(); ParallelFor.WithTemporaryBuffer( 0, diff --git a/src/ImageSharp/Processing/Processors/Transforms/WeightsWindow.cs b/src/ImageSharp/Processing/Processors/Transforms/WeightsWindow.cs index 19909aaba..6a2b6fbd1 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/WeightsWindow.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/WeightsWindow.cs @@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// /// The buffer containing the weights values. /// - private readonly BufferManager buffer; + private readonly MemorySource buffer; /// /// Initializes a new instance of the struct. @@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms { this.flatStartIndex = (index * buffer.Width) + left; this.Left = left; - this.buffer = buffer.Buffer; + this.buffer = buffer.MemorySource; this.Length = length; } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs index aed650b8e..8048dd424 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs @@ -290,7 +290,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg } // no need to dispose when buffer is not array owner - buffers[i] = new Buffer2D(new BasicArrayBuffer(values), values.Length, 1); + var source = new MemorySource(new BasicArrayBuffer(values), true); + buffers[i] = new Buffer2D(source, 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 46a688b49..9ffd42211 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs @@ -107,7 +107,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg this.Output.WriteLine($"Component{i}: {diff}"); averageDifference += diff.average; totalDifference += diff.total; - tolerance += libJpegComponent.SpectralBlocks.Buffer.GetSpan().Length; + tolerance += libJpegComponent.SpectralBlocks.MemorySource.GetSpan().Length; } averageDifference /= componentCount; diff --git a/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs index 52d3929f6..5753d92b3 100644 --- a/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs +++ b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs @@ -71,7 +71,7 @@ namespace SixLabors.ImageSharp.Tests.Memory // Assert.Equal(width * y, span.Start); Assert.Equal(width, span.Length); - Assert.SpanPointsTo(span, buffer.Buffer.MemoryOwner, width * y); + Assert.SpanPointsTo(span, buffer.MemorySource.MemoryOwner, width * y); } } @@ -87,7 +87,7 @@ namespace SixLabors.ImageSharp.Tests.Memory // Assert.Equal(width * y + x, span.Start); Assert.Equal(width - x, span.Length); - Assert.SpanPointsTo(span, buffer.Buffer.MemoryOwner, width * y + x); + Assert.SpanPointsTo(span, buffer.MemorySource.MemoryOwner, width * y + x); } } @@ -99,7 +99,7 @@ namespace SixLabors.ImageSharp.Tests.Memory { using (Buffer2D buffer = this.MemoryAllocator.Allocate2D(width, height)) { - Span span = buffer.Buffer.GetSpan(); + Span span = buffer.MemorySource.GetSpan(); ref TestStructs.Foo actual = ref buffer[x, y]; @@ -115,13 +115,13 @@ namespace SixLabors.ImageSharp.Tests.Memory using (Buffer2D a = this.MemoryAllocator.Allocate2D(10, 5)) using (Buffer2D b = this.MemoryAllocator.Allocate2D(3, 7)) { - IMemoryOwner aa = a.Buffer.MemoryOwner; - IMemoryOwner bb = b.Buffer.MemoryOwner; + IMemoryOwner aa = a.MemorySource.MemoryOwner; + IMemoryOwner bb = b.MemorySource.MemoryOwner; Buffer2D.SwapOrCopyContent(a, b); - Assert.Equal(bb, a.Buffer.MemoryOwner); - Assert.Equal(aa, b.Buffer.MemoryOwner); + Assert.Equal(bb, a.MemorySource.MemoryOwner); + Assert.Equal(aa, b.MemorySource.MemoryOwner); Assert.Equal(new Size(3, 7), a.Size()); Assert.Equal(new Size(10, 5), b.Size()); diff --git a/tests/ImageSharp.Tests/Memory/BufferManagerTests.cs b/tests/ImageSharp.Tests/Memory/MemorySourceTests.cs similarity index 60% rename from tests/ImageSharp.Tests/Memory/BufferManagerTests.cs rename to tests/ImageSharp.Tests/Memory/MemorySourceTests.cs index d08e734e8..9cdfb5635 100644 --- a/tests/ImageSharp.Tests/Memory/BufferManagerTests.cs +++ b/tests/ImageSharp.Tests/Memory/MemorySourceTests.cs @@ -12,21 +12,23 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Memory { - public class BufferManagerTests + public class MemorySourceTests { public class Construction { - [Fact] - public void InitializeAsOwner_MemoryOwner_IsPresent() + [Theory] + [InlineData(false)] + [InlineData(true)] + public void InitializeAsOwner(bool isInternalMemorySource) { var data = new Rgba32[21]; var mmg = new TestMemoryManager(data); - var a = new BufferManager(mmg); + var a = new MemorySource(mmg, isInternalMemorySource); Assert.Equal(mmg, a.MemoryOwner); Assert.Equal(mmg.Memory, a.Memory); - Assert.True(a.OwnsMemory); + Assert.Equal(isInternalMemorySource, a.HasSwappableContents); } [Fact] @@ -35,21 +37,23 @@ namespace SixLabors.ImageSharp.Tests.Memory var data = new Rgba32[21]; var mmg = new TestMemoryManager(data); - var a = new BufferManager(mmg.Memory); + var a = new MemorySource(mmg.Memory); Assert.Null(a.MemoryOwner); Assert.Equal(mmg.Memory, a.Memory); - Assert.False(a.OwnsMemory); + Assert.False(a.HasSwappableContents); } } public class Dispose { - [Fact] - public void WhenOwnershipIsTransfered_ShouldDisposeMemoryOwner() + [Theory] + [InlineData(false)] + [InlineData(true)] + public void WhenOwnershipIsTransfered_ShouldDisposeMemoryOwner(bool isInternalMemorySource) { var mmg = new TestMemoryManager(new int[10]); - var bmg = new BufferManager(mmg); + var bmg = new MemorySource(mmg, isInternalMemorySource); bmg.Dispose(); Assert.True(mmg.IsDisposed); @@ -59,7 +63,7 @@ namespace SixLabors.ImageSharp.Tests.Memory public void WhenMemoryObserver_ShouldNotDisposeAnything() { var mmg = new TestMemoryManager(new int[10]); - var bmg = new BufferManager(mmg.Memory); + var bmg = new MemorySource(mmg.Memory); bmg.Dispose(); Assert.False(mmg.IsDisposed); @@ -70,18 +74,18 @@ namespace SixLabors.ImageSharp.Tests.Memory { private MemoryAllocator MemoryAllocator { get; } = new TestMemoryAllocator(); - private BufferManager AllocateBufferManager(int length, AllocationOptions options = AllocationOptions.None) + private MemorySource AllocateMemorySource(int length, AllocationOptions options = AllocationOptions.None) where T : struct { - var owner = (IMemoryOwner)this.MemoryAllocator.Allocate(length, options); - return new BufferManager(owner); + IMemoryOwner owner = this.MemoryAllocator.Allocate(length, options); + return new MemorySource(owner, true); } [Fact] public void WhenBothAreMemoryOwners_ShouldSwap() { - BufferManager a = this.AllocateBufferManager(13); - BufferManager b = this.AllocateBufferManager(17); + MemorySource a = this.AllocateMemorySource(13); + MemorySource b = this.AllocateMemorySource(17); IMemoryOwner aa = a.MemoryOwner; IMemoryOwner bb = b.MemoryOwner; @@ -89,7 +93,7 @@ namespace SixLabors.ImageSharp.Tests.Memory Memory aaa = a.Memory; Memory bbb = b.Memory; - BufferManager.SwapOrCopyContent(ref a, ref b); + MemorySource.SwapOrCopyContent(ref a, ref b); Assert.Equal(bb, a.MemoryOwner); Assert.Equal(aa, b.MemoryOwner); @@ -100,26 +104,27 @@ namespace SixLabors.ImageSharp.Tests.Memory } [Theory] - [InlineData(false)] - [InlineData(true)] - public void WhenDestIsNotMemoryOwner_SameSize_ShouldCopy(bool sourceIsOwner) + [InlineData(false, false)] + [InlineData(true, true)] + [InlineData(true, false)] + public void WhenDestIsNotMemoryOwner_SameSize_ShouldCopy(bool sourceIsOwner, bool isInternalMemorySource) { var data = new Rgba32[21]; var color = new Rgba32(1, 2, 3, 4); var destOwner = new TestMemoryManager(data); - var dest = new BufferManager(destOwner.Memory); + var dest = new MemorySource(destOwner.Memory); - var sourceOwner = (IMemoryOwner)this.MemoryAllocator.Allocate(21); + IMemoryOwner sourceOwner = this.MemoryAllocator.Allocate(21); - BufferManager source = sourceIsOwner - ? new BufferManager(sourceOwner) - : new BufferManager(sourceOwner.Memory); + MemorySource source = sourceIsOwner + ? new MemorySource(sourceOwner, isInternalMemorySource) + : new MemorySource(sourceOwner.Memory); sourceOwner.Memory.Span[10] = color; // Act: - BufferManager.SwapOrCopyContent(ref dest, ref source); + MemorySource.SwapOrCopyContent(ref dest, ref source); // Assert: Assert.Equal(color, dest.Memory.Span[10]); @@ -136,18 +141,18 @@ namespace SixLabors.ImageSharp.Tests.Memory var color = new Rgba32(1, 2, 3, 4); var destOwner = new TestMemoryManager(data); - var dest = new BufferManager(destOwner.Memory); + var dest = new MemorySource(destOwner.Memory); - var sourceOwner = (IMemoryOwner)this.MemoryAllocator.Allocate(22); + IMemoryOwner sourceOwner = this.MemoryAllocator.Allocate(22); - BufferManager source = sourceIsOwner - ? new BufferManager(sourceOwner) - : new BufferManager(sourceOwner.Memory); + MemorySource source = sourceIsOwner + ? new MemorySource(sourceOwner, true) + : new MemorySource(sourceOwner.Memory); sourceOwner.Memory.Span[10] = color; // Act: Assert.ThrowsAny( - () => BufferManager.SwapOrCopyContent(ref dest, ref source) + () => MemorySource.SwapOrCopyContent(ref dest, ref source) ); Assert.Equal(color, source.Memory.Span[10]); From f3d41f71a37bf33b2df201595b13adb2263ce3f0 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 24 Jul 2018 01:08:41 +0200 Subject: [PATCH 07/13] WrapMemory(IMemoryOwner) + additional System.Drawing test case --- src/ImageSharp/Image.WrapMemory.cs | 95 ++++++++++++++++++- src/ImageSharp/Memory/MemorySource.cs | 1 + .../Image/ImageTests.WrapMemory.cs | 79 +++++++++++++-- 3 files changed, 167 insertions(+), 8 deletions(-) diff --git a/src/ImageSharp/Image.WrapMemory.cs b/src/ImageSharp/Image.WrapMemory.cs index c24a932e1..77432c3ad 100644 --- a/src/ImageSharp/Image.WrapMemory.cs +++ b/src/ImageSharp/Image.WrapMemory.cs @@ -2,6 +2,8 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; + using SixLabors.ImageSharp.MetaData; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Memory; @@ -41,6 +43,27 @@ namespace SixLabors.ImageSharp /// allowing to view/manipulate it as an ImageSharp instance. /// /// The pixel type + /// The + /// The pixel memory + /// The width of the memory image + /// The height of the memory image + /// An instance + public static Image WrapMemory( + Configuration config, + Memory pixelMemory, + int width, + int height) + where TPixel : struct, IPixel + { + return WrapMemory(config, pixelMemory, width, height, new ImageMetaData()); + } + + /// + /// Wraps an existing contigous memory area of 'width'x'height' pixels, + /// allowing to view/manipulate it as an ImageSharp instance. + /// The memory is being observed, the caller remains responsible for managing it's lifecycle. + /// + /// The pixel type /// The pixel memory /// The width of the memory image /// The height of the memory image @@ -51,7 +74,77 @@ namespace SixLabors.ImageSharp int height) where TPixel : struct, IPixel { - return WrapMemory(Configuration.Default, pixelMemory, width, height, new ImageMetaData()); + return WrapMemory(Configuration.Default, pixelMemory, width, height); + } + + /// + /// Wraps an existing contigous memory area of 'width'x'height' pixels, + /// allowing to view/manipulate it as an ImageSharp instance. + /// The ownership of the is being transfered to the new instance, + /// meaning that the caller is not allowed to dispose . + /// It will be disposed together with the result image. + /// + /// The pixel type + /// The + /// The that is being transfered to the image + /// The width of the memory image + /// The height of the memory image + /// The + /// An instance + public static Image WrapMemory( + Configuration config, + IMemoryOwner pixelMemoryOwner, + int width, + int height, + ImageMetaData metaData) + where TPixel : struct, IPixel + { + var memorySource = new MemorySource(pixelMemoryOwner, false); + return new Image(config, memorySource, width, height, metaData); + } + + /// + /// Wraps an existing contigous memory area of 'width'x'height' pixels, + /// allowing to view/manipulate it as an ImageSharp instance. + /// The ownership of the is being transfered to the new instance, + /// meaning that the caller is not allowed to dispose . + /// It will be disposed together with the result image. + /// + /// The pixel type + /// The + /// The that is being transfered to the image + /// The width of the memory image + /// The height of the memory image + /// An instance + public static Image WrapMemory( + Configuration config, + IMemoryOwner pixelMemoryOwner, + int width, + int height) + where TPixel : struct, IPixel + { + return WrapMemory(config, pixelMemoryOwner, width, height, new ImageMetaData()); + } + + /// + /// Wraps an existing contigous memory area of 'width'x'height' pixels, + /// allowing to view/manipulate it as an ImageSharp instance. + /// The ownership of the is being transfered to the new instance, + /// meaning that the caller is not allowed to dispose . + /// It will be disposed together with the result image. + /// + /// The pixel type + /// The that is being transfered to the image + /// The width of the memory image + /// The height of the memory image + /// An instance + public static Image WrapMemory( + IMemoryOwner pixelMemoryOwner, + int width, + int height) + where TPixel : struct, IPixel + { + return WrapMemory(Configuration.Default, pixelMemoryOwner, width, height); } } } \ No newline at end of file diff --git a/src/ImageSharp/Memory/MemorySource.cs b/src/ImageSharp/Memory/MemorySource.cs index 27bca11c1..4253307ef 100644 --- a/src/ImageSharp/Memory/MemorySource.cs +++ b/src/ImageSharp/Memory/MemorySource.cs @@ -8,6 +8,7 @@ namespace SixLabors.Memory { /// /// Holds a that is either OWNED or CONSUMED. + /// When the memory is being owned, the instance is also known. /// Implements content transfer logic in that depends on the ownership status. /// This is needed to transfer the contents of a temporary /// to a persistent without copying the buffer. diff --git a/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs b/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs index 57f757176..815684d84 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs @@ -5,9 +5,11 @@ using System; using System.Buffers; using System.Drawing; using System.Drawing.Imaging; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.MetaData; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; using SixLabors.Shapes; using SixLabors.ImageSharp.Processing; using Xunit; @@ -19,13 +21,17 @@ namespace SixLabors.ImageSharp.Tests { public class WrapMemory { + /// + /// A exposing the locked pixel memory of a instance. + /// TODO: This should be an example in https://github.com/SixLabors/Samples + /// class BitmapMemoryManager : MemoryManager { - private System.Drawing.Bitmap bitmap; + private readonly Bitmap bitmap; - private BitmapData bmpData; + private readonly BitmapData bmpData; - private int length; + private readonly int length; public BitmapMemoryManager(Bitmap bitmap) { @@ -40,9 +46,21 @@ namespace SixLabors.ImageSharp.Tests this.length = bitmap.Width * bitmap.Height; } + public bool IsDisposed { get; private set; } + protected override void Dispose(bool disposing) { - this.bitmap.UnlockBits(this.bmpData); + if (this.IsDisposed) + { + return; + } + + if (disposing) + { + this.bitmap.UnlockBits(this.bmpData); + } + + this.IsDisposed = true; } public override unsafe Span GetSpan() @@ -63,7 +81,26 @@ namespace SixLabors.ImageSharp.Tests } [Fact] - public void WrapSystemDrawingBitmap() + public void WrapMemory_CreatedImageIsCorrect() + { + Configuration cfg = Configuration.Default.ShallowCopy(); + var metaData = new ImageMetaData(); + + var array = new Rgba32[25]; + var memory = new Memory(array); + + using (var image = Image.WrapMemory(cfg, memory, 5, 5, metaData)) + { + ref Rgba32 pixel0 = ref image.GetPixelSpan()[0]; + Assert.True(Unsafe.AreSame(ref array[0], ref pixel0)); + + Assert.Equal(cfg, image.GetConfiguration()); + Assert.Equal(metaData, image.MetaData); + } + } + + [Fact] + public void WrapSystemDrawingBitmap_WhenObserved() { using (var bmp = new Bitmap(51, 23)) { @@ -75,13 +112,41 @@ namespace SixLabors.ImageSharp.Tests using (var image = Image.WrapMemory(memory, bmp.Width, bmp.Height)) { + Assert.Equal(memory, image.GetPixelMemory()); image.Mutate(c => c.Fill(bg).Fill(fg, new RectangularPolygon(10, 10, 10, 10))); } + + Assert.False(memoryManager.IsDisposed); } string fn = System.IO.Path.Combine( TestEnvironment.ActualOutputDirectoryFullPath, - "WrapSystemDrawingBitmap.bmp"); + $"{nameof(this.WrapSystemDrawingBitmap_WhenObserved)}.bmp"); + + bmp.Save(fn, ImageFormat.Bmp); + } + } + + [Fact] + public void WrapSystemDrawingBitmap_WhenOwned() + { + using (var bmp = new Bitmap(51, 23)) + { + var memoryManager = new BitmapMemoryManager(bmp); + Bgra32 bg = NamedColors.Red; + Bgra32 fg = NamedColors.Green; + + using (var image = Image.WrapMemory(memoryManager, bmp.Width, bmp.Height)) + { + Assert.Equal(memoryManager.Memory, image.GetPixelMemory()); + image.Mutate(c => c.Fill(bg).Fill(fg, new RectangularPolygon(10, 10, 10, 10))); + } + + Assert.True(memoryManager.IsDisposed); + + string fn = System.IO.Path.Combine( + TestEnvironment.ActualOutputDirectoryFullPath, + $"{nameof(this.WrapSystemDrawingBitmap_WhenOwned)}.bmp"); bmp.Save(fn, ImageFormat.Bmp); } From bb2c06718d6fab1fa19654c72ba7dd01f833cae9 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 24 Jul 2018 01:21:24 +0200 Subject: [PATCH 08/13] WhitespaceCop --- src/ImageSharp/Memory/MemorySource.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Memory/MemorySource.cs b/src/ImageSharp/Memory/MemorySource.cs index 4253307ef..c0a74b5f8 100644 --- a/src/ImageSharp/Memory/MemorySource.cs +++ b/src/ImageSharp/Memory/MemorySource.cs @@ -21,7 +21,7 @@ namespace SixLabors.Memory internal struct MemorySource : IDisposable { /// - /// Initializes a new instance of the struct + /// Initializes a new instance of the struct /// by wrapping an existing . /// /// The to wrap From ed07db23ae22bfac08776802cda82df2fea0d82b Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 24 Jul 2018 09:25:30 +0100 Subject: [PATCH 09/13] delete golang jpeg decoder --- .../Decoder}/DoubleBufferedStreamReader.cs | 2 +- .../Decoder}/FastACTables.cs | 11 +- .../Decoder}/FixedByteBuffer256.cs | 2 +- .../Decoder}/FixedByteBuffer512.cs | 2 +- .../Decoder}/FixedInt16Buffer257.cs | 2 +- .../Decoder}/FixedInt32Buffer18.cs | 2 +- .../Decoder}/FixedUInt32Buffer18.cs | 2 +- .../Decoder/HuffmanTable.cs} | 8 +- .../Decoder/HuffmanTables.cs} | 13 +- .../Decoder/JpegFileMarker.cs} | 14 +- .../Decoder/JpegFrame.cs} | 8 +- .../Decoder/JpegFrameComponent.cs} | 12 +- .../Decoder}/ScanDecoder.cs | 62 +- .../GolangPort/Components/Decoder/Bits.cs | 155 ---- .../GolangPort/Components/Decoder/Bytes.cs | 255 ------ .../Components/Decoder/DecoderThrowHelper.cs | 96 -- .../Components/Decoder/EOFException.cs | 23 - .../Components/Decoder/GolangComponent.cs | 253 ------ .../Components/Decoder/GolangComponentScan.cs | 29 - .../Decoder/GolangDecoderErrorCode.cs | 27 - .../Components/Decoder/GolangHuffmanTree.cs | 260 ------ .../GolangJpegScanDecoder.ComputationData.cs | 53 -- .../GolangJpegScanDecoder.DataPointers.cs | 51 -- .../Decoder/GolangJpegScanDecoder.cs | 705 --------------- .../Components/Decoder/InputProcessor.cs | 392 --------- .../Components/Decoder/JpegScanDecoder.md | 25 - .../Decoder/MissingFF00Exception.cs | 15 - .../Jpeg/GolangPort/GolangJpegDecoder.cs | 42 - .../Jpeg/GolangPort/GolangJpegDecoderCore.cs | 824 ------------------ src/ImageSharp/Formats/Jpeg/JpegDecoder.cs | 6 +- ...sJpegDecoderCore.cs => JpegDecoderCore.cs} | 51 +- .../Jpeg/PdfJsPort/PdfJsJpegDecoder.cs | 42 - .../Codecs/Jpeg/DecodeJpeg.cs | 20 +- .../Codecs/Jpeg/DecodeJpegMultiple.cs | 13 +- .../Codecs/Jpeg/DecodeJpegParseStreamOnly.cs | 5 +- .../Codecs/Jpeg/DoubleBufferedStreams.cs | 9 +- .../Codecs/Jpeg/EncodeJpeg.cs | 4 +- .../Codecs/Jpeg/IdentifyJpeg.cs | 19 +- .../Codecs/Jpeg/LoadResizeSave.cs | 8 +- .../Jpg/DoubleBufferedStreamReaderTests.cs | 3 +- .../Formats/Jpg/JpegDecoderTests.Baseline.cs | 45 +- .../Formats/Jpg/JpegDecoderTests.MetaData.cs | 26 +- .../Jpg/JpegDecoderTests.Progressive.cs | 40 +- .../Formats/Jpg/JpegDecoderTests.cs | 35 +- .../Jpg/JpegImagePostProcessorTests.cs | 7 +- .../Formats/Jpg/JpegProfilingBenchmarks.cs | 15 +- .../Formats/Jpg/ParseStreamTests.cs | 134 +-- .../Formats/Jpg/SpectralJpegTests.cs | 73 +- .../Formats/Jpg/Utils/JpegFixture.cs | 17 +- .../Jpg/Utils/LibJpegTools.ComponentData.cs | 55 +- .../Jpg/Utils/LibJpegTools.SpectralData.cs | 30 +- 51 files changed, 213 insertions(+), 3789 deletions(-) rename src/ImageSharp/Formats/Jpeg/{PdfJsPort/Components => Components/Decoder}/DoubleBufferedStreamReader.cs (99%) rename src/ImageSharp/Formats/Jpeg/{PdfJsPort/Components => Components/Decoder}/FastACTables.cs (89%) rename src/ImageSharp/Formats/Jpeg/{PdfJsPort/Components => Components/Decoder}/FixedByteBuffer256.cs (90%) rename src/ImageSharp/Formats/Jpeg/{PdfJsPort/Components => Components/Decoder}/FixedByteBuffer512.cs (90%) rename src/ImageSharp/Formats/Jpeg/{PdfJsPort/Components => Components/Decoder}/FixedInt16Buffer257.cs (90%) rename src/ImageSharp/Formats/Jpeg/{PdfJsPort/Components => Components/Decoder}/FixedInt32Buffer18.cs (90%) rename src/ImageSharp/Formats/Jpeg/{PdfJsPort/Components => Components/Decoder}/FixedUInt32Buffer18.cs (90%) rename src/ImageSharp/Formats/Jpeg/{PdfJsPort/Components/PdfJsHuffmanTable.cs => Components/Decoder/HuffmanTable.cs} (93%) rename src/ImageSharp/Formats/Jpeg/{PdfJsPort/Components/PdfJsHuffmanTables.cs => Components/Decoder/HuffmanTables.cs} (54%) rename src/ImageSharp/Formats/Jpeg/{PdfJsPort/Components/PdfJsFileMarker.cs => Components/Decoder/JpegFileMarker.cs} (79%) rename src/ImageSharp/Formats/Jpeg/{PdfJsPort/Components/PdfJsFrame.cs => Components/Decoder/JpegFrame.cs} (92%) rename src/ImageSharp/Formats/Jpeg/{PdfJsPort/Components/PdfJsFrameComponent.cs => Components/Decoder/JpegFrameComponent.cs} (90%) rename src/ImageSharp/Formats/Jpeg/{PdfJsPort/Components => Components/Decoder}/ScanDecoder.cs (93%) delete mode 100644 src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bits.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bytes.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/DecoderThrowHelper.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/EOFException.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangComponent.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangComponentScan.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangDecoderErrorCode.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangHuffmanTree.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangJpegScanDecoder.ComputationData.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangJpegScanDecoder.DataPointers.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangJpegScanDecoder.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/JpegScanDecoder.md delete mode 100644 src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/MissingFF00Exception.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/GolangPort/GolangJpegDecoder.cs delete mode 100644 src/ImageSharp/Formats/Jpeg/GolangPort/GolangJpegDecoderCore.cs rename src/ImageSharp/Formats/Jpeg/{PdfJsPort/PdfJsJpegDecoderCore.cs => JpegDecoderCore.cs} (93%) delete mode 100644 src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoder.cs diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/DoubleBufferedStreamReader.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/DoubleBufferedStreamReader.cs similarity index 99% rename from src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/DoubleBufferedStreamReader.cs rename to src/ImageSharp/Formats/Jpeg/Components/Decoder/DoubleBufferedStreamReader.cs index 0e42d074c..f4527966a 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/DoubleBufferedStreamReader.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/DoubleBufferedStreamReader.cs @@ -8,7 +8,7 @@ using System.Runtime.CompilerServices; using SixLabors.Memory; // TODO: This could be useful elsewhere. -namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { /// /// A stream reader that add a secondary level buffer in addition to native stream buffered reading diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FastACTables.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTables.cs similarity index 89% rename from src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FastACTables.cs rename to src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTables.cs index 0fc85c6e4..6d06abecf 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FastACTables.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTables.cs @@ -5,7 +5,7 @@ using System; using System.Runtime.CompilerServices; using SixLabors.Memory; -namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { /// /// The collection of lookup tables used for fast AC entropy scan decoding. @@ -35,10 +35,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components } /// - /// Gets a reference to the first element of the AC table indexed by - /// + /// Gets a reference to the first element of the AC table indexed by /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref short GetAcTableReference(PdfJsFrameComponent component) + public ref short GetAcTableReference(JpegFrameComponent component) { return ref this.tables.GetRowSpan(component.ACHuffmanTableId)[0]; } @@ -48,11 +47,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components /// /// The table index. /// The collection of AC Huffman tables. - public void BuildACTableLut(int index, PdfJsHuffmanTables acHuffmanTables) + public void BuildACTableLut(int index, HuffmanTables acHuffmanTables) { const int FastBits = ScanDecoder.FastBits; Span fastAC = this.tables.GetRowSpan(index); - ref PdfJsHuffmanTable huffman = ref acHuffmanTables[index]; + ref HuffmanTable huffman = ref acHuffmanTables[index]; int i; for (i = 0; i < (1 << FastBits); i++) diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedByteBuffer256.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedByteBuffer256.cs similarity index 90% rename from src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedByteBuffer256.cs rename to src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedByteBuffer256.cs index 5870e3da8..1d26178e0 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedByteBuffer256.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedByteBuffer256.cs @@ -4,7 +4,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { [StructLayout(LayoutKind.Sequential)] internal unsafe struct FixedByteBuffer256 diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedByteBuffer512.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedByteBuffer512.cs similarity index 90% rename from src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedByteBuffer512.cs rename to src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedByteBuffer512.cs index c509903c9..556e74fd5 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedByteBuffer512.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedByteBuffer512.cs @@ -4,7 +4,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { [StructLayout(LayoutKind.Sequential)] internal unsafe struct FixedByteBuffer512 diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedInt16Buffer257.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedInt16Buffer257.cs similarity index 90% rename from src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedInt16Buffer257.cs rename to src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedInt16Buffer257.cs index b304dbf8c..a3b67a700 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedInt16Buffer257.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedInt16Buffer257.cs @@ -4,7 +4,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { [StructLayout(LayoutKind.Sequential)] internal unsafe struct FixedInt16Buffer257 diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedInt32Buffer18.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedInt32Buffer18.cs similarity index 90% rename from src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedInt32Buffer18.cs rename to src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedInt32Buffer18.cs index f8507ec47..bba89f072 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedInt32Buffer18.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedInt32Buffer18.cs @@ -4,7 +4,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { [StructLayout(LayoutKind.Sequential)] internal unsafe struct FixedInt32Buffer18 diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedUInt32Buffer18.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedUInt32Buffer18.cs similarity index 90% rename from src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedUInt32Buffer18.cs rename to src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedUInt32Buffer18.cs index 9b076d9da..1d3ca9933 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/FixedUInt32Buffer18.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedUInt32Buffer18.cs @@ -4,7 +4,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { [StructLayout(LayoutKind.Sequential)] internal unsafe struct FixedUInt32Buffer18 diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs similarity index 93% rename from src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs rename to src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs index 15ae56331..c0f3b17cd 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs @@ -6,13 +6,13 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.Memory; -namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { /// /// Represents a Huffman Table /// [StructLayout(LayoutKind.Sequential)] - internal unsafe struct PdfJsHuffmanTable + internal unsafe struct HuffmanTable { /// /// Gets the max code array @@ -40,12 +40,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components public FixedInt16Buffer257 Sizes; /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the struct. /// /// The to use for buffer allocations. /// The code lengths /// The huffman values - public PdfJsHuffmanTable(MemoryAllocator memoryAllocator, ReadOnlySpan count, ReadOnlySpan values) + public HuffmanTable(MemoryAllocator memoryAllocator, ReadOnlySpan count, ReadOnlySpan values) { const int Length = 257; using (IBuffer huffcode = memoryAllocator.Allocate(Length)) diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTables.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTables.cs similarity index 54% rename from src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTables.cs rename to src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTables.cs index 5cbde2b88..dc066aa0a 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTables.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTables.cs @@ -1,24 +1,23 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System.Collections.Generic; using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { /// - /// Defines a 2 pairs of huffman tables + /// Defines a 2 pairs of huffman tables. /// - internal sealed class PdfJsHuffmanTables + internal sealed class HuffmanTables { - private readonly PdfJsHuffmanTable[] tables = new PdfJsHuffmanTable[4]; + private readonly HuffmanTable[] tables = new HuffmanTable[4]; /// /// Gets or sets the table at the given index. /// /// The index - /// The - public ref PdfJsHuffmanTable this[int index] + /// The + public ref HuffmanTable this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref this.tables[index]; diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFileMarker.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFileMarker.cs similarity index 79% rename from src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFileMarker.cs rename to src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFileMarker.cs index 85c9f9466..31f4efdcb 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFileMarker.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFileMarker.cs @@ -3,19 +3,19 @@ using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { /// - /// Represents a jpeg file marker + /// Represents a jpeg file marker. /// - internal readonly struct PdfJsFileMarker + internal readonly struct JpegFileMarker { /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the struct. /// /// The marker /// The position within the stream - public PdfJsFileMarker(byte marker, long position) + public JpegFileMarker(byte marker, long position) { this.Marker = marker; this.Position = position; @@ -23,12 +23,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components } /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the struct. /// /// The marker /// The position within the stream /// Whether the current marker is invalid - public PdfJsFileMarker(byte marker, long position, bool invalid) + public JpegFileMarker(byte marker, long position, bool invalid) { this.Marker = marker; this.Position = position; diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrame.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs similarity index 92% rename from src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrame.cs rename to src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs index 8ce981a09..a238e0734 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrame.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs @@ -3,12 +3,12 @@ using System; -namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { /// /// Represent a single jpeg frame /// - internal sealed class PdfJsFrame : IDisposable + internal sealed class JpegFrame : IDisposable { /// /// Gets or sets a value indicating whether the frame uses the extended specification @@ -48,7 +48,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components /// /// Gets or sets the frame component collection /// - public PdfJsFrameComponent[] Components { get; set; } + public JpegFrameComponent[] Components { get; set; } /// /// Gets or sets the maximum horizontal sampling factor @@ -94,7 +94,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components for (int i = 0; i < this.ComponentCount; i++) { - PdfJsFrameComponent component = this.Components[i]; + JpegFrameComponent component = this.Components[i]; component.Init(); } } diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrameComponent.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrameComponent.cs similarity index 90% rename from src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrameComponent.cs rename to src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrameComponent.cs index 7d4eb66e8..3ce7cacfa 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrameComponent.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrameComponent.cs @@ -5,21 +5,19 @@ using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.Formats.Jpeg.Components; -using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; using SixLabors.Memory; using SixLabors.Primitives; -namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { /// /// Represents a single frame component /// - internal class PdfJsFrameComponent : IDisposable, IJpegComponent + internal class JpegFrameComponent : IDisposable, IJpegComponent { private readonly MemoryAllocator memoryAllocator; - public PdfJsFrameComponent(MemoryAllocator memoryAllocator, PdfJsFrame frame, byte id, int horizontalFactor, int verticalFactor, byte quantizationTableIndex, int index) + public JpegFrameComponent(MemoryAllocator memoryAllocator, JpegFrame frame, byte id, int horizontalFactor, int verticalFactor, byte quantizationTableIndex, int index) { this.memoryAllocator = memoryAllocator; this.Frame = frame; @@ -89,7 +87,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components /// public int ACHuffmanTableId { get; set; } - public PdfJsFrame Frame { get; } + public JpegFrame Frame { get; } /// public void Dispose() @@ -125,7 +123,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components } else { - PdfJsFrameComponent c0 = this.Frame.Components[0]; + JpegFrameComponent c0 = this.Frame.Components[0]; this.SubSamplingDivisors = c0.SamplingFactors.DivideBy(this.SamplingFactors); } diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/ScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs similarity index 93% rename from src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/ScanDecoder.cs rename to src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs index 8575bac69..5afe6382f 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/ScanDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs @@ -2,10 +2,8 @@ // Licensed under the Apache License, Version 2.0. using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using SixLabors.ImageSharp.Formats.Jpeg.Components; -namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { /// /// Decodes the Huffman encoded spectral scan. @@ -23,13 +21,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components // LUT Bias[n] = (-1 << n) + 1 private static readonly int[] Bias = { 0, -1, -3, -7, -15, -31, -63, -127, -255, -511, -1023, -2047, -4095, -8191, -16383, -32767 }; - private readonly PdfJsFrame frame; - private readonly PdfJsHuffmanTables dcHuffmanTables; - private readonly PdfJsHuffmanTables acHuffmanTables; + private readonly JpegFrame frame; + private readonly HuffmanTables dcHuffmanTables; + private readonly HuffmanTables acHuffmanTables; private readonly FastACTables fastACTables; private readonly DoubleBufferedStreamReader stream; - private readonly PdfJsFrameComponent[] components; + private readonly JpegFrameComponent[] components; private readonly ZigZag dctZigZag; // The restart interval. @@ -97,9 +95,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components /// The successive approximation bit low end. public ScanDecoder( DoubleBufferedStreamReader stream, - PdfJsFrame frame, - PdfJsHuffmanTables dcHuffmanTables, - PdfJsHuffmanTables acHuffmanTables, + JpegFrame frame, + HuffmanTables dcHuffmanTables, + HuffmanTables acHuffmanTables, FastACTables fastACTables, int componentIndex, int componentsLength, @@ -177,10 +175,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components // Scan an interleaved mcu... process components in order for (int k = 0; k < this.componentsLength; k++) { - PdfJsFrameComponent component = this.components[k]; + JpegFrameComponent component = this.components[k]; - ref PdfJsHuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; - ref PdfJsHuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId]; + ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; + ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId]; ref short fastACRef = ref this.fastACTables.GetAcTableReference(component); int h = component.HorizontalSamplingFactor; int v = component.VerticalSamplingFactor; @@ -231,13 +229,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components /// private void ParseBaselineDataNonInterleaved() { - PdfJsFrameComponent component = this.components[this.componentIndex]; + JpegFrameComponent component = this.components[this.componentIndex]; int w = component.WidthInBlocks; int h = component.HeightInBlocks; - ref PdfJsHuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; - ref PdfJsHuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId]; + ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; + ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId]; ref short fastACRef = ref this.fastACTables.GetAcTableReference(component); int mcu = 0; @@ -296,8 +294,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components // Scan an interleaved mcu... process components in order for (int k = 0; k < this.componentsLength; k++) { - PdfJsFrameComponent component = this.components[k]; - ref PdfJsHuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; + JpegFrameComponent component = this.components[k]; + ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; int h = component.HorizontalSamplingFactor; int v = component.VerticalSamplingFactor; @@ -345,13 +343,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components /// private void ParseProgressiveDataNonInterleaved() { - PdfJsFrameComponent component = this.components[this.componentIndex]; + JpegFrameComponent component = this.components[this.componentIndex]; int w = component.WidthInBlocks; int h = component.HeightInBlocks; - ref PdfJsHuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; - ref PdfJsHuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId]; + ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; + ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId]; ref short fastACRef = ref this.fastACTables.GetAcTableReference(component); int mcu = 0; @@ -396,11 +394,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components } private void DecodeBlockBaseline( - PdfJsFrameComponent component, + JpegFrameComponent component, int row, int col, - ref PdfJsHuffmanTable dcTable, - ref PdfJsHuffmanTable acTable, + ref HuffmanTable dcTable, + ref HuffmanTable acTable, ref short fastACRef) { this.CheckBits(); @@ -475,10 +473,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components } private void DecodeBlockProgressiveDC( - PdfJsFrameComponent component, + JpegFrameComponent component, int row, int col, - ref PdfJsHuffmanTable dcTable) + ref HuffmanTable dcTable) { if (this.spectralEnd != 0) { @@ -511,10 +509,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components } private void DecodeBlockProgressiveAC( - PdfJsFrameComponent component, + JpegFrameComponent component, int row, int col, - ref PdfJsHuffmanTable acTable, + ref HuffmanTable acTable, ref short fastACRef) { if (this.spectralStart == 0) @@ -603,7 +601,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components } } - private void DecodeBlockProgressiveACRefined(ref short blockDataRef, ref PdfJsHuffmanTable acTable) + private void DecodeBlockProgressiveACRefined(ref short blockDataRef, ref HuffmanTable acTable) { int k; @@ -805,7 +803,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components } [MethodImpl(InliningOptions.ShortMethod)] - private int DecodeHuffman(ref PdfJsHuffmanTable table) + private int DecodeHuffman(ref HuffmanTable table) { this.CheckBits(); @@ -830,7 +828,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components } [MethodImpl(InliningOptions.ColdPath)] - private int DecodeHuffmanSlow(ref PdfJsHuffmanTable table) + private int DecodeHuffmanSlow(ref HuffmanTable table) { // Naive test is to shift the code_buffer down so k bits are // valid, then test against MaxCode. To speed this up, we've @@ -941,7 +939,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components for (int i = 0; i < this.components.Length; i++) { - PdfJsFrameComponent c = this.components[i]; + JpegFrameComponent c = this.components[i]; c.DcPredictor = 0; } diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bits.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bits.cs deleted file mode 100644 index 353eb01fe..000000000 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bits.cs +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder -{ - /// - /// Holds the unprocessed bits that have been taken from the byte-stream. - /// The n least significant bits of a form the unread bits, to be read in MSB to - /// LSB order. - /// - internal struct Bits - { - /// - /// Gets or sets the accumulator. - /// - public int Accumulator; - - /// - /// Gets or sets the mask. - /// 0, with mask==0 when unreadbits==0.]]> - /// - public int Mask; - - /// - /// Gets or sets the number of unread bits in the accumulator. - /// - public int UnreadBits; - - /// - /// Reads bytes from the byte buffer to ensure that bits.UnreadBits is at - /// least n. For best performance (avoiding function calls inside hot loops), - /// the caller is the one responsible for first checking that bits.UnreadBits < n. - /// - /// The number of bits to ensure. - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void EnsureNBits(int n, ref InputProcessor inputProcessor) - { - GolangDecoderErrorCode errorCode = this.EnsureNBitsUnsafe(n, ref inputProcessor); - errorCode.EnsureNoError(); - } - - /// - /// Reads bytes from the byte buffer to ensure that bits.UnreadBits is at - /// least n. For best performance (avoiding function calls inside hot loops), - /// the caller is the one responsible for first checking that bits.UnreadBits < n. - /// This method does not throw. Returns instead. - /// - /// The number of bits to ensure. - /// The - /// Error code - public GolangDecoderErrorCode EnsureNBitsUnsafe(int n, ref InputProcessor inputProcessor) - { - while (true) - { - GolangDecoderErrorCode errorCode = this.EnsureBitsStepImpl(ref inputProcessor); - if (errorCode != GolangDecoderErrorCode.NoError || this.UnreadBits >= n) - { - return errorCode; - } - } - } - - /// - /// Unrolled version of for n==8 - /// - /// The - /// A - public GolangDecoderErrorCode Ensure8BitsUnsafe(ref InputProcessor inputProcessor) - { - return this.EnsureBitsStepImpl(ref inputProcessor); - } - - /// - /// Unrolled version of for n==1 - /// - /// The - /// A - public GolangDecoderErrorCode Ensure1BitUnsafe(ref InputProcessor inputProcessor) - { - return this.EnsureBitsStepImpl(ref inputProcessor); - } - - /// - /// Receive extend - /// - /// Byte - /// The - /// Read bits value - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int ReceiveExtend(int t, ref InputProcessor inputProcessor) - { - GolangDecoderErrorCode errorCode = this.ReceiveExtendUnsafe(t, ref inputProcessor, out int x); - errorCode.EnsureNoError(); - return x; - } - - /// - /// Receive extend - /// - /// Byte - /// The - /// Read bits value - /// The - public GolangDecoderErrorCode ReceiveExtendUnsafe(int t, ref InputProcessor inputProcessor, out int x) - { - if (this.UnreadBits < t) - { - GolangDecoderErrorCode errorCode = this.EnsureNBitsUnsafe(t, ref inputProcessor); - if (errorCode != GolangDecoderErrorCode.NoError) - { - x = int.MaxValue; - return errorCode; - } - } - - this.UnreadBits -= t; - this.Mask >>= t; - int s = 1 << t; - x = (this.Accumulator >> this.UnreadBits) & (s - 1); - - if (x < (s >> 1)) - { - x += ((-1) << t) + 1; - } - - return GolangDecoderErrorCode.NoError; - } - - private GolangDecoderErrorCode EnsureBitsStepImpl(ref InputProcessor inputProcessor) - { - GolangDecoderErrorCode errorCode = inputProcessor.Bytes.ReadByteStuffedByteUnsafe(inputProcessor.InputStream, out int c); - - if (errorCode != GolangDecoderErrorCode.NoError) - { - return errorCode; - } - - this.Accumulator = (this.Accumulator << 8) | c; - this.UnreadBits += 8; - if (this.Mask == 0) - { - this.Mask = 1 << 7; - } - else - { - this.Mask <<= 8; - } - - return errorCode; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bytes.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bytes.cs deleted file mode 100644 index c8c68aa7e..000000000 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bytes.cs +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.IO; -using System.Runtime.CompilerServices; - -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 - { - /// - /// Specifies the buffer size for and - /// - public const int BufferSize = 4096; - - /// - /// 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. - /// - public byte[] Buffer; - - /// - /// Values of converted to -s - /// - public int[] BufferAsInt; - - /// - /// Start of bytes read - /// - public int I; - - /// - /// End of bytes read - /// - public int J; - - /// - /// Gets or sets the unreadable bytes. The number of bytes to back up i after - /// overshooting. It can be 0, 1 or 2. - /// - public int UnreadableBytes; - - /// - /// Creates a new instance of the , and initializes it's buffer. - /// - /// The bytes created - public static Bytes Create() - { - // DO NOT bother with buffers and array pooling here! - // It only makes things worse! - return new Bytes - { - Buffer = new byte[BufferSize], - BufferAsInt = new int[BufferSize] - }; - } - - /// - /// Disposes of the underlying buffer - /// - public void Dispose() - { - this.Buffer = null; - this.BufferAsInt = null; - } - - /// - /// ReadByteStuffedByte is like ReadByte but is for byte-stuffed Huffman data. - /// - /// Input stream - /// The result byte as - /// The - public GolangDecoderErrorCode ReadByteStuffedByteUnsafe(Stream inputStream, out int x) - { - // Take the fast path if bytes.buf contains at least two bytes. - if (this.I + 2 <= this.J) - { - x = this.BufferAsInt[this.I]; - this.I++; - this.UnreadableBytes = 1; - if (x != JpegConstants.Markers.XFFInt) - { - return GolangDecoderErrorCode.NoError; - } - - if (this.BufferAsInt[this.I] != 0x00) - { - return GolangDecoderErrorCode.MissingFF00; - } - - this.I++; - this.UnreadableBytes = 2; - x = JpegConstants.Markers.XFF; - return GolangDecoderErrorCode.NoError; - } - - this.UnreadableBytes = 0; - - GolangDecoderErrorCode errorCode = this.ReadByteAsIntUnsafe(inputStream, out x); - this.UnreadableBytes = 1; - if (errorCode != GolangDecoderErrorCode.NoError) - { - return errorCode; - } - - if (x != JpegConstants.Markers.XFF) - { - return GolangDecoderErrorCode.NoError; - } - - errorCode = this.ReadByteAsIntUnsafe(inputStream, out x); - this.UnreadableBytes = 2; - if (errorCode != GolangDecoderErrorCode.NoError) - { - return errorCode; - } - - if (x != 0x00) - { - return GolangDecoderErrorCode.MissingFF00; - } - - x = JpegConstants.Markers.XFF; - return GolangDecoderErrorCode.NoError; - } - - /// - /// Returns the next byte, whether buffered or not buffered. It does not care about byte stuffing. - /// - /// Input stream - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public byte ReadByte(Stream inputStream) - { - GolangDecoderErrorCode errorCode = this.ReadByteUnsafe(inputStream, out byte result); - errorCode.EnsureNoError(); - return result; - } - - /// - /// Extracts the next byte, whether buffered or not buffered into the result out parameter. It does not care about byte stuffing. - /// This method does not throw on format error, it returns a instead. - /// - /// Input stream - /// The result as out parameter - /// The - public GolangDecoderErrorCode ReadByteUnsafe(Stream inputStream, out byte result) - { - GolangDecoderErrorCode errorCode = GolangDecoderErrorCode.NoError; - while (this.I == this.J) - { - errorCode = this.FillUnsafe(inputStream); - if (errorCode != GolangDecoderErrorCode.NoError) - { - result = 0; - return errorCode; - } - } - - result = this.Buffer[this.I]; - this.I++; - this.UnreadableBytes = 0; - return errorCode; - } - - /// - /// Same as but the result is an - /// - /// The input stream - /// The result - /// A - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public GolangDecoderErrorCode ReadByteAsIntUnsafe(Stream inputStream, out int result) - { - GolangDecoderErrorCode errorCode = GolangDecoderErrorCode.NoError; - while (this.I == this.J) - { - errorCode = this.FillUnsafe(inputStream); - if (errorCode != GolangDecoderErrorCode.NoError) - { - result = 0; - return errorCode; - } - } - - result = this.BufferAsInt[this.I]; - this.I++; - this.UnreadableBytes = 0; - return errorCode; - } - - /// - /// Fills up the bytes buffer from the underlying stream. - /// It should only be called when there are no unread bytes in bytes. - /// - /// Thrown when reached end of stream unexpectedly. - /// Input stream - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Fill(Stream inputStream) - { - GolangDecoderErrorCode errorCode = this.FillUnsafe(inputStream); - errorCode.EnsureNoError(); - } - - /// - /// Fills up the bytes buffer from the underlying stream. - /// It should only be called when there are no unread bytes in bytes. - /// This method does not throw , returns a instead! - /// - /// Input stream - /// The - public GolangDecoderErrorCode FillUnsafe(Stream inputStream) - { - if (this.I != this.J) - { - // Unrecoverable error in the input, throwing! - DecoderThrowHelper.ThrowImageFormatException.FillCalledWhenUnreadBytesExist(); - } - - // 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]; - this.I = 2; - this.J = 2; - } - - // Fill in the rest of the buffer. - int n = inputStream.Read(this.Buffer, this.J, this.Buffer.Length - this.J); - if (n == 0) - { - return GolangDecoderErrorCode.UnexpectedEndOfStream; - } - - this.J += n; - - for (int i = 0; i < this.Buffer.Length; i++) - { - this.BufferAsInt[i] = this.Buffer[i]; - } - - return GolangDecoderErrorCode.NoError; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/DecoderThrowHelper.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/DecoderThrowHelper.cs deleted file mode 100644 index 2b2bc61ba..000000000 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/DecoderThrowHelper.cs +++ /dev/null @@ -1,96 +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.Formats.Jpeg.GolangPort.Components.Decoder -{ - /// - /// Encapsulates exception thrower methods for the Jpeg Encoder - /// - internal static class DecoderThrowHelper - { - /// - /// Throws an exception that belongs to the given - /// - /// The - [MethodImpl(MethodImplOptions.NoInlining)] - public static void ThrowExceptionForErrorCode(this GolangDecoderErrorCode 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 GolangDecoderErrorCode.NoError: - throw new ArgumentException("ThrowExceptionForErrorCode() called with NoError!", nameof(errorCode)); - case GolangDecoderErrorCode.MissingFF00: - throw new MissingFF00Exception(); - case GolangDecoderErrorCode.UnexpectedEndOfStream: - throw new EOFException(); - default: - throw new ArgumentOutOfRangeException(nameof(errorCode), errorCode, null); - } - } - - /// - /// Throws an exception if the given defines an error. - /// - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void EnsureNoError(this GolangDecoderErrorCode errorCode) - { - if (errorCode != GolangDecoderErrorCode.NoError) - { - ThrowExceptionForErrorCode(errorCode); - } - } - - /// - /// Throws an exception if the given is . - /// - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void EnsureNoEOF(this GolangDecoderErrorCode errorCode) - { - if (errorCode == GolangDecoderErrorCode.UnexpectedEndOfStream) - { - errorCode.ThrowExceptionForErrorCode(); - } - } - - /// - /// Encapsulates methods throwing different flavours of -s. - /// - public static class ThrowImageFormatException - { - /// - /// Throws "Fill called when unread bytes exist". - /// - [MethodImpl(MethodImplOptions.NoInlining)] - public static void FillCalledWhenUnreadBytesExist() - { - throw new ImageFormatException("Fill called when unread bytes exist!"); - } - - /// - /// Throws "Bad Huffman code". - /// - [MethodImpl(MethodImplOptions.NoInlining)] - public static void BadHuffmanCode() - { - throw new ImageFormatException("Bad Huffman code!"); - } - - /// - /// Throws "Uninitialized Huffman table". - /// - [MethodImpl(MethodImplOptions.NoInlining)] - public static void UninitializedHuffmanTable() - { - throw new ImageFormatException("Uninitialized Huffman table"); - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/EOFException.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/EOFException.cs deleted file mode 100644 index 60d9b1e1a..000000000 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/EOFException.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; - -namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder -{ - /// - /// The EOF (End of File exception). - /// Thrown when the decoder encounters an EOF marker without a proceeding EOI (End Of Image) marker - /// TODO: Rename to UnexpectedEndOfStreamException - /// - internal class EOFException : Exception - { - /// - /// Initializes a new instance of the class. - /// - public EOFException() - : base("Reached end of stream before proceeding EOI marker!") - { - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangComponent.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangComponent.cs deleted file mode 100644 index 72213eb38..000000000 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangComponent.cs +++ /dev/null @@ -1,253 +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.Formats.Jpeg.Components; -using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; -using SixLabors.Memory; -using SixLabors.Primitives; - -namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder -{ - /// - /// - /// Represents a single color component - /// - internal class GolangComponent : IDisposable, IJpegComponent - { - public GolangComponent(byte identifier, int index) - { - this.Identifier = identifier; - this.Index = index; - } - - /// - /// Gets the identifier - /// - public byte Identifier { get; } - - /// - public int Index { get; } - - public Size SizeInBlocks { get; private set; } - - public Size SamplingFactors { get; private set; } - - public Size SubSamplingDivisors { get; private set; } - - public int HorizontalSamplingFactor => this.SamplingFactors.Width; - - public int VerticalSamplingFactor => this.SamplingFactors.Height; - - /// - public int QuantizationTableIndex { get; private set; } - - /// - /// - /// Gets the storing the "raw" frequency-domain decoded blocks. - /// We need to apply IDCT, dequantiazition and unzigging to transform them into color-space blocks. - /// This is done by . - /// When us true, we are touching these blocks multiple times - each time we process a Scan. - /// - public Buffer2D SpectralBlocks { get; private set; } - - /// - /// Initializes - /// - /// The to use for buffer allocations. - /// The instance - public void InitializeDerivedData(MemoryAllocator memoryAllocator, GolangJpegDecoderCore decoder) - { - // For 4-component images (either CMYK or YCbCrK), we only support two - // hv vectors: [0x11 0x11 0x11 0x11] and [0x22 0x11 0x11 0x22]. - // Theoretically, 4-component JPEG images could mix and match hv values - // but in practice, those two combinations are the only ones in use, - // and it simplifies the applyBlack code below if we can assume that: - // - for CMYK, the C and K channels have full samples, and if the M - // and Y channels subsample, they subsample both horizontally and - // vertically. - // - for YCbCrK, the Y and K channels have full samples. - this.SizeInBlocks = decoder.ImageSizeInMCU.MultiplyBy(this.SamplingFactors); - - if (this.Index == 0 || this.Index == 3) - { - this.SubSamplingDivisors = new Size(1, 1); - } - else - { - GolangComponent c0 = decoder.Components[0]; - this.SubSamplingDivisors = c0.SamplingFactors.DivideBy(this.SamplingFactors); - } - - this.SpectralBlocks = memoryAllocator.Allocate2D(this.SizeInBlocks.Width, this.SizeInBlocks.Height, AllocationOptions.Clean); - } - - /// - /// Initializes all component data except . - /// - /// The instance - public void InitializeCoreData(GolangJpegDecoderCore decoder) - { - // Section B.2.2 states that "the value of C_i shall be different from - // the values of C_1 through C_(i-1)". - int i = this.Index; - - for (int j = 0; j < this.Index; j++) - { - if (this.Identifier == decoder.Components[j].Identifier) - { - throw new ImageFormatException("Repeated component identifier"); - } - } - - this.QuantizationTableIndex = decoder.Temp[8 + (3 * i)]; - if (this.QuantizationTableIndex > GolangJpegDecoderCore.MaxTq) - { - throw new ImageFormatException("Bad Tq value"); - } - - byte hv = decoder.Temp[7 + (3 * i)]; - int h = hv >> 4; - int v = hv & 0x0f; - if (h < 1 || h > 4 || v < 1 || v > 4) - { - throw new ImageFormatException("Unsupported Luma/chroma subsampling ratio"); - } - - if (h == 3 || v == 3) - { - throw new ImageFormatException("Lnsupported subsampling ratio"); - } - - switch (decoder.ComponentCount) - { - case 1: - - // If a JPEG image has only one component, section A.2 says "this data - // is non-interleaved by definition" and section A.2.2 says "[in this - // case...] the order of data units within a scan shall be left-to-right - // and top-to-bottom... regardless of the values of H_1 and V_1". Section - // 4.8.2 also says "[for non-interleaved data], the MCU is defined to be - // one data unit". Similarly, section A.1.1 explains that it is the ratio - // of H_i to max_j(H_j) that matters, and similarly for V. For grayscale - // images, H_1 is the maximum H_j for all components j, so that ratio is - // always 1. The component's (h, v) is effectively always (1, 1): even if - // the nominal (h, v) is (2, 1), a 20x5 image is encoded in three 8x8 - // MCUs, not two 16x8 MCUs. - h = 1; - v = 1; - break; - - case 3: - - // For YCbCr images, we only support 4:4:4, 4:4:0, 4:2:2, 4:2:0, - // 4:1:1 or 4:1:0 chroma subsampling ratios. This implies that the - // (h, v) values for the Y component are either (1, 1), (1, 2), - // (2, 1), (2, 2), (4, 1) or (4, 2), and the Y component's values - // must be a multiple of the Cb and Cr component's values. We also - // assume that the two chroma components have the same subsampling - // ratio. - switch (i) - { - case 0: - { - // Y. - // We have already verified, above, that h and v are both - // either 1, 2 or 4, so invalid (h, v) combinations are those - // with v == 4. - if (v == 4) - { - throw new ImageFormatException("Unsupported subsampling ratio"); - } - - break; - } - - case 1: - { - // Cb. - Size s0 = decoder.Components[0].SamplingFactors; - - if (s0.Width % h != 0 || s0.Height % v != 0) - { - throw new ImageFormatException("Unsupported subsampling ratio"); - } - - break; - } - - case 2: - { - // Cr. - Size s1 = decoder.Components[1].SamplingFactors; - - if (s1.Width != h || s1.Height != v) - { - throw new ImageFormatException("Unsupported subsampling ratio"); - } - - break; - } - } - - break; - - case 4: - - // For 4-component images (either CMYK or YCbCrK), we only support two - // hv vectors: [0x11 0x11 0x11 0x11] and [0x22 0x11 0x11 0x22]. - // Theoretically, 4-component JPEG images could mix and match hv values - // but in practice, those two combinations are the only ones in use, - // and it simplifies the applyBlack code below if we can assume that: - // - for CMYK, the C and K channels have full samples, and if the M - // and Y channels subsample, they subsample both horizontally and - // vertically. - // - for YCbCrK, the Y and K channels have full samples. - switch (i) - { - case 0: - if (hv != 0x11 && hv != 0x22) - { - throw new ImageFormatException("Unsupported subsampling ratio"); - } - - break; - case 1: - case 2: - if (hv != 0x11) - { - throw new ImageFormatException("Unsupported subsampling ratio"); - } - - break; - case 3: - Size s0 = decoder.Components[0].SamplingFactors; - - if (s0.Width != h || s0.Height != v) - { - throw new ImageFormatException("Unsupported subsampling ratio"); - } - - break; - } - - break; - } - - this.SamplingFactors = new Size(h, v); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref Block8x8 GetBlockReference(int column, int row) - { - return ref this.SpectralBlocks[column, row]; - } - - public void Dispose() - { - this.SpectralBlocks?.Dispose(); - } - } -} diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangComponentScan.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangComponentScan.cs deleted file mode 100644 index 6752768ff..000000000 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangComponentScan.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder -{ - /// - /// Represents a component scan - /// - [StructLayout(LayoutKind.Sequential)] - internal struct GolangComponentScan - { - /// - /// Gets or sets the component index. - /// - public byte ComponentIndex; - - /// - /// Gets or sets the DC table selector - /// - public byte DcTableSelector; - - /// - /// Gets or sets the AC table selector - /// - public byte AcTableSelector; - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangDecoderErrorCode.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangDecoderErrorCode.cs deleted file mode 100644 index fa3364527..000000000 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangDecoderErrorCode.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder -{ - /// - /// Represents "recoverable" decoder errors. - /// - internal enum GolangDecoderErrorCode - { - /// - /// NoError - /// - NoError, - - /// - /// MissingFF00 - /// - // ReSharper disable once InconsistentNaming - MissingFF00, - - /// - /// End of stream reached unexpectedly - /// - UnexpectedEndOfStream - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangHuffmanTree.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangHuffmanTree.cs deleted file mode 100644 index dccce2aaa..000000000 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangHuffmanTree.cs +++ /dev/null @@ -1,260 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -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 GolangHuffmanTree - { - /// - /// The index of the AC table row - /// - public const int AcTableIndex = 1; - - /// - /// The index of the DC table row - /// - public const int DcTableIndex = 0; - - /// - /// The maximum (inclusive) number of codes in a Huffman tree. - /// - public const int MaxNCodes = 256; - - /// - /// The maximum (inclusive) number of bits in a Huffman code. - /// - public const int MaxCodeLength = 16; - - /// - /// The maximum number of Huffman table classes - /// - public const int MaxTc = 1; - - /// - /// The maximum number of Huffman table identifiers - /// - public const int MaxTh = 3; - - /// - /// Row size of the Huffman table - /// - public const int ThRowSize = MaxTh + 1; - - /// - /// Number of Hufman Trees in the Huffman table - /// - public const int NumberOfTrees = (MaxTc + 1) * (MaxTh + 1); - - /// - /// The log-2 size of the Huffman decoder's look-up table. - /// - public const int LutSizeLog2 = 8; - - /// - /// Gets or sets the number of codes in the tree. - /// - public int Length; - - /// - /// Gets the look-up table for the next LutSize bits in the bit-stream. - /// The high 8 bits of the uint16 are the encoded value. The low 8 bits - /// are 1 plus the code length, or 0 if the value is too large to fit in - /// lutSize bits. - /// - public FixedInt32Buffer256 Lut; - - /// - /// Gets the the decoded values, sorted by their encoding. - /// - 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 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 FixedInt32Buffer16 MaxCodes; - - /// - /// Gets the array of indices. Indices[i] is the index into Values of MinCodes[i]. - /// - public FixedInt32Buffer16 Indices; - - /// - /// Creates and initializes an array of instances of size - /// - /// An array of instances representing the Huffman tables - public static GolangHuffmanTree[] CreateHuffmanTrees() - { - return new GolangHuffmanTree[NumberOfTrees]; - } - - /// - /// Internal part of the DHT processor, whatever does it mean - /// - /// The decoder instance - /// The temporary buffer that holds the data that has been read from the Jpeg stream - /// Remaining bits - public void ProcessDefineHuffmanTablesMarkerLoop( - ref InputProcessor inputProcessor, - byte[] defineHuffmanTablesData, - ref int remaining) - { - // Read nCodes and huffman.Valuess (and derive h.Length). - // nCodes[i] is the number of codes with code length i. - // h.Length is the total number of codes. - this.Length = 0; - - int[] ncodes = new int[MaxCodeLength]; - for (int i = 0; i < ncodes.Length; i++) - { - ncodes[i] = defineHuffmanTablesData[i + 1]; - this.Length += ncodes[i]; - } - - if (this.Length == 0) - { - throw new ImageFormatException("Huffman table has zero length"); - } - - if (this.Length > MaxNCodes) - { - throw new ImageFormatException("Huffman table has excessive length"); - } - - remaining -= this.Length + 17; - if (remaining < 0) - { - throw new ImageFormatException("DHT has wrong length"); - } - - byte[] values = new byte[MaxNCodes]; - inputProcessor.ReadFull(values, 0, this.Length); - - fixed (int* valuesPtr = this.Values.Data) - fixed (int* lutPtr = this.Lut.Data) - { - for (int i = 0; i < values.Length; i++) - { - valuesPtr[i] = values[i]; - } - - // Derive the look-up table. - for (int i = 0; i < MaxNCodes; i++) - { - lutPtr[i] = 0; - } - - int x = 0, code = 0; - - for (int i = 0; i < LutSizeLog2; i++) - { - code <<= 1; - - for (int j = 0; j < ncodes[i]; j++) - { - // 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++; - } - } - } - - 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; - for (int i = 0; i < ncodes.Length; i++) - { - 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; - } - } - } - - /// - /// Gets the value for the given code and index. - /// - /// The code - /// The code length - /// The value - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int GetValue(int code, int codeLength) - { - return this.Values[this.Indices[codeLength] + code - this.MinCodes[codeLength]]; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct FixedInt32Buffer256 - { - public fixed int Data[256]; - - public int this[int idx] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - ref int self = ref Unsafe.As(ref this); - return Unsafe.Add(ref self, idx); - } - } - } - - [StructLayout(LayoutKind.Sequential)] - internal struct FixedInt32Buffer16 - { - public fixed int Data[16]; - - public int this[int idx] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - ref int self = ref Unsafe.As(ref this); - return Unsafe.Add(ref self, idx); - } - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangJpegScanDecoder.ComputationData.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangJpegScanDecoder.ComputationData.cs deleted file mode 100644 index f3c8aa91b..000000000 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangJpegScanDecoder.ComputationData.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Runtime.InteropServices; - -using SixLabors.ImageSharp.Formats.Jpeg.Components; - -namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder -{ - /// - /// Conains the definition of - /// - internal unsafe partial struct GolangJpegScanDecoder - { - /// - /// Holds the "large" data blocks needed for computations. - /// - [StructLayout(LayoutKind.Sequential)] - public struct ComputationData - { - /// - /// The main input/working block - /// - public Block8x8 Block; - - /// - /// The jpeg unzig data - /// - public ZigZag Unzig; - - /// - /// The buffer storing the -s for each component - /// - public fixed byte ScanData[3 * GolangJpegDecoderCore.MaxComponents]; - - /// - /// The DC values for each component - /// - public fixed int Dc[GolangJpegDecoderCore.MaxComponents]; - - /// - /// Creates and initializes a new instance - /// - /// The - public static ComputationData Create() - { - ComputationData data = default; - data.Unzig = ZigZag.CreateUnzigTable(); - return data; - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangJpegScanDecoder.DataPointers.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangJpegScanDecoder.DataPointers.cs deleted file mode 100644 index a00da6fca..000000000 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangJpegScanDecoder.DataPointers.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.Formats.Jpeg.Components; - -namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder -{ - /// - /// Conains the definition of - /// - internal unsafe partial struct GolangJpegScanDecoder - { - /// - /// Contains pointers to the memory regions of so they can be easily passed around to pointer based utility methods of - /// - public struct DataPointers - { - /// - /// Pointer to - /// - public Block8x8* Block; - - /// - /// Pointer to as byte* - /// - public byte* Unzig; - - /// - /// Pointer to as Scan* - /// - public GolangComponentScan* ComponentScan; - - /// - /// Pointer to - /// - public int* Dc; - - /// - /// Initializes a new instance of the struct. - /// - /// The pointer pointing to - public DataPointers(ComputationData* basePtr) - { - this.Block = &basePtr->Block; - this.Unzig = basePtr->Unzig.Data; - this.ComponentScan = (GolangComponentScan*)basePtr->ScanData; - this.Dc = basePtr->Dc; - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangJpegScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangJpegScanDecoder.cs deleted file mode 100644 index 3a88cfad4..000000000 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/GolangJpegScanDecoder.cs +++ /dev/null @@ -1,705 +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.Components; - -// ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder -{ - /// - /// Encapsulates the impementation of Jpeg SOS Huffman decoding. See JpegScanDecoder.md! - /// - /// and are the spectral selection bounds. - /// and are the successive approximation high and low values. - /// The spec calls these values Ss, Se, Ah and Al. - /// For progressive JPEGs, these are the two more-or-less independent - /// aspects of progression. Spectral selection progression is when not - /// all of a block's 64 DCT coefficients are transmitted in one pass. - /// For example, three passes could transmit coefficient 0 (the DC - /// component), coefficients 1-5, and coefficients 6-63, in zig-zag - /// order. Successive approximation is when not all of the bits of a - /// band of coefficients are transmitted in one pass. For example, - /// three passes could transmit the 6 most significant bits, followed - /// by the second-least significant bit, followed by the least - /// significant bit. - /// For baseline JPEGs, these parameters are hard-coded to 0/63/0/0. - /// - [StructLayout(LayoutKind.Sequential)] - internal unsafe partial struct GolangJpegScanDecoder - { - // The JpegScanDecoder members should be ordered in a way that results in optimal memory layout. -#pragma warning disable SA1202 // ElementsMustBeOrderedByAccess - - /// - /// The buffer - /// - private ComputationData data; - - /// - /// Pointers to elements of - /// - private DataPointers pointers; - - /// - /// The current component index - /// - public int ComponentIndex; - - /// - /// X coordinate of the current block, in units of 8x8. (The third block in the first row has (bx, by) = (2, 0)) - /// - private int bx; - - /// - /// Y coordinate of the current block, in units of 8x8. (The third block in the first row has (bx, by) = (2, 0)) - /// - private int by; - - /// - /// Start index of the zig-zag selection bound - /// - private int zigStart; - - /// - /// End index of the zig-zag selection bound - /// - private int zigEnd; - - /// - /// Successive approximation high value - /// - private int ah; - - /// - /// Successive approximation low value - /// - private int al; - - /// - /// The number of component scans - /// - private int componentScanCount; - - /// - /// Horizontal sampling factor at the current component index - /// - private int hi; - - /// - /// End-of-Band run, specified in section G.1.2.2. - /// - 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. - /// - /// Pointer to on the stack - /// The instance - /// The remaining bytes in the segment block. - public static void InitStreamReading(GolangJpegScanDecoder* p, GolangJpegDecoderCore decoder, int remaining) - { - p->data = ComputationData.Create(); - p->pointers = new DataPointers(&p->data); - p->InitStreamReadingImpl(decoder, remaining); - } - - /// - /// Read Huffman data from Jpeg scans in , - /// and decode it as into . - /// - /// The blocks are traversed one MCU at a time. For 4:2:0 chroma - /// subsampling, there are four Y 8x8 blocks in every 16x16 MCU. - /// For a baseline 32x16 pixel image, the Y blocks visiting order is: - /// 0 1 4 5 - /// 2 3 6 7 - /// For progressive images, the interleaved scans (those with component count > 1) - /// are traversed as above, but non-interleaved scans are traversed left - /// to right, top to bottom: - /// 0 1 2 3 - /// 4 5 6 7 - /// Only DC scans (zigStart == 0) can be interleave AC scans must have - /// only one component. - /// To further complicate matters, for non-interleaved scans, there is no - /// data for any blocks that are inside the image at the MCU level but - /// outside the image at the pixel level. For example, a 24x16 pixel 4:2:0 - /// progressive image consists of two 16x16 MCUs. The interleaved scans - /// will process 8 Y blocks: - /// 0 1 4 5 - /// 2 3 6 7 - /// The non-interleaved scans will process only 6 Y blocks: - /// 0 1 2 - /// 3 4 5 - /// - /// The instance - public void DecodeBlocks(GolangJpegDecoderCore decoder) - { - decoder.InputProcessor.ResetErrorState(); - - this.blockCounter = 0; - this.mcuCounter = 0; - this.expectedRst = JpegConstants.Markers.RST0; - - for (int my = 0; my < decoder.MCUCountY; my++) - { - for (int mx = 0; mx < decoder.MCUCountX; mx++) - { - this.DecodeBlocksAtMcuIndex(decoder, mx, my); - - this.mcuCounter++; - - // Handling restart intervals - // Useful info: https://stackoverflow.com/a/8751802 - if (decoder.IsAtRestartInterval(this.mcuCounter)) - { - this.ProcessRSTMarker(decoder); - this.Reset(decoder); - } - } - } - } - - private void DecodeBlocksAtMcuIndex(GolangJpegDecoderCore decoder, int mx, int my) - { - for (int scanIndex = 0; scanIndex < this.componentScanCount; scanIndex++) - { - this.ComponentIndex = this.pointers.ComponentScan[scanIndex].ComponentIndex; - GolangComponent component = decoder.Components[this.ComponentIndex]; - - 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 = 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); - - // Copy block to stack - this.data.Block = blockRefOnHeap; - - if (!decoder.InputProcessor.ReachedEOF) - { - this.DecodeBlock(decoder, scanIndex); - } - - // Store the result block: - blockRefOnHeap = this.data.Block; - } - } - } - - private void ProcessRSTMarker(GolangJpegDecoderCore 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) - { - 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] != this.expectedRst; - } - - if (invalidRst) - { - throw new ImageFormatException("Bad RST marker"); - } - } - - this.expectedRst++; - if (this.expectedRst == JpegConstants.Markers.RST7 + 1) - { - this.expectedRst = JpegConstants.Markers.RST0; - } - } - } - } - - private void Reset(GolangJpegDecoderCore 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, sizeof(int) * GolangJpegDecoderCore.MaxComponents); - } - - /// - /// The implementation part of as an instance method. - /// - /// The - /// The remaining bytes - private void InitStreamReadingImpl(GolangJpegDecoderCore decoder, int remaining) - { - if (decoder.ComponentCount == 0) - { - throw new ImageFormatException("Missing SOF marker"); - } - - if (remaining < 6 || 4 + (2 * decoder.ComponentCount) < remaining || remaining % 2 != 0) - { - throw new ImageFormatException("SOS has wrong length"); - } - - decoder.InputProcessor.ReadFull(decoder.Temp, 0, remaining); - this.componentScanCount = decoder.Temp[0]; - - int scanComponentCountX2 = 2 * this.componentScanCount; - if (remaining != 4 + scanComponentCountX2) - { - throw new ImageFormatException("SOS length inconsistent with number of components"); - } - - int totalHv = 0; - - for (int i = 0; i < this.componentScanCount; i++) - { - this.InitComponentScan(decoder, i, ref this.pointers.ComponentScan[i], ref totalHv); - } - - // Section B.2.3 states that if there is more than one component then the - // total H*V values in a scan must be <= 10. - if (decoder.ComponentCount > 1 && totalHv > 10) - { - throw new ImageFormatException("Total sampling factors too large."); - } - - this.zigEnd = Block8x8F.Size - 1; - - if (decoder.IsProgressive) - { - this.zigStart = decoder.Temp[1 + scanComponentCountX2]; - this.zigEnd = decoder.Temp[2 + scanComponentCountX2]; - this.ah = decoder.Temp[3 + scanComponentCountX2] >> 4; - this.al = decoder.Temp[3 + scanComponentCountX2] & 0x0f; - - if ((this.zigStart == 0 && this.zigEnd != 0) || this.zigStart > this.zigEnd - || this.zigEnd >= Block8x8F.Size) - { - throw new ImageFormatException("Bad spectral selection bounds"); - } - - if (this.zigStart != 0 && this.componentScanCount != 1) - { - throw new ImageFormatException("Progressive AC coefficients for more than one component"); - } - - if (this.ah != 0 && this.ah != this.al + 1) - { - throw new ImageFormatException("Bad successive approximation values"); - } - } - } - - /// - /// Read the current the current block at (, ) from the decoders stream - /// - /// The decoder - /// The index of the scan - private void DecodeBlock(GolangJpegDecoderCore decoder, int scanIndex) - { - Block8x8* b = this.pointers.Block; - int huffmannIdx = (GolangHuffmanTree.AcTableIndex * GolangHuffmanTree.ThRowSize) + this.pointers.ComponentScan[scanIndex].AcTableSelector; - if (this.ah != 0) - { - this.Refine(ref decoder.InputProcessor, ref decoder.HuffmanTrees[huffmannIdx], 1 << this.al); - } - else - { - int zig = this.zigStart; - - if (zig == 0) - { - zig++; - - // Decode the DC coefficient, as specified in section F.2.2.1. - int huffmanIndex = (GolangHuffmanTree.DcTableIndex * GolangHuffmanTree.ThRowSize) + this.pointers.ComponentScan[scanIndex].DcTableSelector; - decoder.InputProcessor.DecodeHuffmanUnsafe( - ref decoder.HuffmanTrees[huffmanIndex], - out int value); - if (!decoder.InputProcessor.CheckEOF()) - { - return; - } - - if (value > 16) - { - throw new ImageFormatException("Excessive DC component"); - } - - decoder.InputProcessor.ReceiveExtendUnsafe(value, out int deltaDC); - if (!decoder.InputProcessor.CheckEOFEnsureNoError()) - { - return; - } - - this.pointers.Dc[this.ComponentIndex] += deltaDC; - - // b[0] = dc[compIndex] << al; - value = this.pointers.Dc[this.ComponentIndex] << this.al; - Block8x8.SetScalarAt(b, 0, (short)value); - } - - if (zig <= this.zigEnd && this.eobRun > 0) - { - this.eobRun--; - } - else - { - // Decode the AC coefficients, as specified in section F.2.2.2. - for (; zig <= this.zigEnd; zig++) - { - decoder.InputProcessor.DecodeHuffmanUnsafe(ref decoder.HuffmanTrees[huffmannIdx], out int value); - if (decoder.InputProcessor.HasError) - { - return; - } - - int val0 = value >> 4; - int val1 = value & 0x0f; - if (val1 != 0) - { - zig += val0; - if (zig > this.zigEnd) - { - break; - } - - decoder.InputProcessor.ReceiveExtendUnsafe(val1, out int ac); - if (decoder.InputProcessor.HasError) - { - return; - } - - // b[Unzig[zig]] = ac << al; - value = ac << this.al; - Block8x8.SetScalarAt(b, this.pointers.Unzig[zig], (short)value); - } - else - { - if (val0 != 0x0f) - { - this.eobRun = (ushort)(1 << val0); - if (val0 != 0) - { - this.DecodeEobRun(val0, ref decoder.InputProcessor); - if (!decoder.InputProcessor.CheckEOFEnsureNoError()) - { - return; - } - } - - this.eobRun--; - break; - } - - zig += 0x0f; - } - } - } - } - } - - private void DecodeEobRun(int count, ref InputProcessor processor) - { - processor.DecodeBitsUnsafe(count, out int bitsResult); - if (processor.LastErrorCode != GolangDecoderErrorCode.NoError) - { - return; - } - - this.eobRun |= bitsResult; - } - - private void InitComponentScan(GolangJpegDecoderCore decoder, int i, ref GolangComponentScan currentComponentScan, ref int totalHv) - { - // Component selector. - int cs = decoder.Temp[1 + (2 * i)]; - int compIndex = -1; - for (int j = 0; j < decoder.ComponentCount; j++) - { - // Component compv = ; - if (cs == decoder.Components[j].Identifier) - { - compIndex = j; - } - } - - if (compIndex < 0) - { - throw new ImageFormatException("Unknown component selector"); - } - - currentComponentScan.ComponentIndex = (byte)compIndex; - - this.ProcessComponentImpl(decoder, i, ref currentComponentScan, ref totalHv, decoder.Components[compIndex]); - } - - private void ProcessComponentImpl( - GolangJpegDecoderCore decoder, - int i, - ref GolangComponentScan currentComponentScan, - ref int totalHv, - GolangComponent currentComponent) - { - // Section B.2.3 states that "the value of Cs_j shall be different from - // the values of Cs_1 through Cs_(j-1)". Since we have previously - // verified that a frame's component identifiers (C_i values in section - // B.2.2) are unique, it suffices to check that the implicit indexes - // into comp are unique. - for (int j = 0; j < i; j++) - { - if (currentComponentScan.ComponentIndex == this.pointers.ComponentScan[j].ComponentIndex) - { - throw new ImageFormatException("Repeated component selector"); - } - } - - totalHv += currentComponent.HorizontalSamplingFactor * currentComponent.VerticalSamplingFactor; - - currentComponentScan.DcTableSelector = (byte)(decoder.Temp[2 + (2 * i)] >> 4); - if (currentComponentScan.DcTableSelector > GolangHuffmanTree.MaxTh) - { - throw new ImageFormatException("Bad DC table selector value"); - } - - currentComponentScan.AcTableSelector = (byte)(decoder.Temp[2 + (2 * i)] & 0x0f); - if (currentComponentScan.AcTableSelector > GolangHuffmanTree.MaxTh) - { - throw new ImageFormatException("Bad AC table selector value"); - } - } - - /// - /// Decodes a successive approximation refinement block, as specified in section G.1.2. - /// - /// The instance - /// The Huffman tree - /// The low transform offset - private void Refine(ref InputProcessor bp, ref GolangHuffmanTree h, int delta) - { - Block8x8* b = this.pointers.Block; - - // Refining a DC component is trivial. - if (this.zigStart == 0) - { - if (this.zigEnd != 0) - { - throw new ImageFormatException("Invalid state for zig DC component"); - } - - bp.DecodeBitUnsafe(out bool bit); - if (!bp.CheckEOFEnsureNoError()) - { - return; - } - - if (bit) - { - int stuff = Block8x8.GetScalarAt(b, 0); - - // int stuff = (int)b[0]; - stuff |= delta; - - // b[0] = stuff; - Block8x8.SetScalarAt(b, 0, (short)stuff); - } - - return; - } - - // Refining AC components is more complicated; see sections G.1.2.2 and G.1.2.3. - int zig = this.zigStart; - if (this.eobRun == 0) - { - for (; zig <= this.zigEnd; zig++) - { - bool done = false; - int z = 0; - - bp.DecodeHuffmanUnsafe(ref h, out int val); - if (!bp.CheckEOF()) - { - return; - } - - int val0 = val >> 4; - int val1 = val & 0x0f; - - switch (val1) - { - case 0: - if (val0 != 0x0f) - { - this.eobRun = 1 << val0; - if (val0 != 0) - { - this.DecodeEobRun(val0, ref bp); - if (!bp.CheckEOFEnsureNoError()) - { - return; - } - } - - done = true; - } - - break; - case 1: - z = delta; - - bp.DecodeBitUnsafe(out bool bit); - if (!bp.CheckEOFEnsureNoError()) - { - return; - } - - if (!bit) - { - z = -z; - } - - break; - default: - throw new ImageFormatException("Unexpected Huffman code"); - } - - if (done) - { - break; - } - - zig = this.RefineNonZeroes(ref bp, zig, val0, delta); - - if (bp.ReachedEOF || bp.HasError) - { - return; - } - - if (z != 0 && zig <= this.zigEnd) - { - // b[Unzig[zig]] = z; - Block8x8.SetScalarAt(b, this.pointers.Unzig[zig], (short)z); - } - } - } - - if (this.eobRun > 0) - { - this.eobRun--; - this.RefineNonZeroes(ref bp, zig, -1, delta); - } - } - - /// - /// Refines non-zero entries of b in zig-zag order. - /// If >= 0, the first zero entries are skipped over. - /// - /// The - /// The zig-zag start index - /// The non-zero entry - /// The low transform offset - /// The - private int RefineNonZeroes(ref InputProcessor bp, int zig, int nz, int delta) - { - Block8x8* b = this.pointers.Block; - for (; zig <= this.zigEnd; zig++) - { - int u = this.pointers.Unzig[zig]; - int bu = Block8x8.GetScalarAt(b, u); - - // TODO: Are the equality comparsions OK with floating point values? Isn't an epsilon value necessary? - if (bu == 0) - { - if (nz == 0) - { - break; - } - - nz--; - continue; - } - - bp.DecodeBitUnsafe(out bool bit); - if (bp.HasError) - { - return int.MinValue; - } - - if (!bit) - { - continue; - } - - int val = bu >= 0 ? bu + delta : bu - delta; - - Block8x8.SetScalarAt(b, u, (short)val); - } - - return zig; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs deleted file mode 100644 index c7e14ee4f..000000000 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs +++ /dev/null @@ -1,392 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.IO; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder -{ - /// - /// Encapsulates stream reading and processing data and operations for . - /// It's a value type for imporved data locality, and reduced number of CALLVIRT-s - /// - internal struct InputProcessor : IDisposable - { - /// - /// Holds the unprocessed bits that have been taken from the byte-stream. - /// - public Bits Bits; - - /// - /// The byte buffer - /// - public Bytes Bytes; - - /// - /// Initializes a new instance of the struct. - /// - /// The input - /// Temporal buffer, same as - public InputProcessor(Stream inputStream, byte[] temp) - { - this.Bits = default; - this.Bytes = Bytes.Create(); - this.InputStream = inputStream; - this.Temp = temp; - this.LastErrorCode = GolangDecoderErrorCode.NoError; - } - - /// - /// Gets the input stream - /// - public Stream InputStream { get; } - - /// - /// Gets the temporary buffer, same instance as - /// - public byte[] Temp { get; } - - /// - /// Gets a value indicating whether an unexpected EOF reached in . - /// - public bool ReachedEOF => this.LastErrorCode == GolangDecoderErrorCode.UnexpectedEndOfStream; - - public bool HasError => this.LastErrorCode != GolangDecoderErrorCode.NoError; - - public GolangDecoderErrorCode LastErrorCode { get; private set; } - - public void ResetErrorState() => this.LastErrorCode = GolangDecoderErrorCode.NoError; - - /// - /// If errorCode indicates unexpected EOF, sets to true and returns false. - /// Calls and returns true otherwise. - /// - /// A indicating whether EOF reached - public bool CheckEOFEnsureNoError() - { - if (this.LastErrorCode == GolangDecoderErrorCode.UnexpectedEndOfStream) - { - return false; - } - - this.LastErrorCode.EnsureNoError(); - return true; - } - - /// - /// If errorCode indicates unexpected EOF, sets to true and returns false. - /// Returns true otherwise. - /// - /// A indicating whether EOF reached - public bool CheckEOF() - { - if (this.LastErrorCode == GolangDecoderErrorCode.UnexpectedEndOfStream) - { - return false; - } - - return true; - } - - /// - public void Dispose() - { - this.Bytes.Dispose(); - } - - /// - /// Returns the next byte, whether buffered or not buffered. It does not care about byte stuffing. - /// - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public byte ReadByte() - { - return this.Bytes.ReadByte(this.InputStream); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public GolangDecoderErrorCode 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! - /// - /// The decoded bit as a - /// The - public GolangDecoderErrorCode DecodeBitUnsafe(out bool result) - { - if (this.Bits.UnreadBits == 0) - { - this.LastErrorCode = this.Bits.Ensure1BitUnsafe(ref this); - if (this.LastErrorCode != GolangDecoderErrorCode.NoError) - { - result = false; - return this.LastErrorCode; - } - } - - result = (this.Bits.Accumulator & this.Bits.Mask) != 0; - this.Bits.UnreadBits--; - this.Bits.Mask >>= 1; - return this.LastErrorCode = GolangDecoderErrorCode.NoError; - } - - /// - /// Reads exactly length bytes into data. It does not care about byte stuffing. - /// Does not throw on errors, returns instead! - /// - /// The data to write to. - /// The offset in the source buffer - /// The number of bytes to read - /// The - public GolangDecoderErrorCode ReadFullUnsafe(byte[] data, int offset, int length) - { - // Unread the overshot bytes, if any. - if (this.Bytes.UnreadableBytes != 0) - { - if (this.Bits.UnreadBits >= 8) - { - this.UnreadByteStuffedByte(); - } - - this.Bytes.UnreadableBytes = 0; - } - - this.LastErrorCode = GolangDecoderErrorCode.NoError; - while (length > 0 && this.LastErrorCode == GolangDecoderErrorCode.NoError) - { - if (this.Bytes.J - this.Bytes.I >= length) - { - Array.Copy(this.Bytes.Buffer, 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); - offset += this.Bytes.J - this.Bytes.I; - length -= this.Bytes.J - this.Bytes.I; - this.Bytes.I += this.Bytes.J - this.Bytes.I; - - this.LastErrorCode = this.Bytes.FillUnsafe(this.InputStream); - } - } - - return this.LastErrorCode; - } - - /// - /// Decodes the given number of bits - /// - /// The number of bits to decode. - /// The result - /// The - public GolangDecoderErrorCode DecodeBitsUnsafe(int count, out int result) - { - if (this.Bits.UnreadBits < count) - { - this.LastErrorCode = this.Bits.EnsureNBitsUnsafe(count, ref this); - if (this.LastErrorCode != GolangDecoderErrorCode.NoError) - { - result = 0; - return this.LastErrorCode; - } - } - - result = this.Bits.Accumulator >> (this.Bits.UnreadBits - count); - result = result & ((1 << count) - 1); - this.Bits.UnreadBits -= count; - this.Bits.Mask >>= count; - return this.LastErrorCode = GolangDecoderErrorCode.NoError; - } - - /// - /// Extracts the next Huffman-coded value from the bit-stream into result, decoded according to the given value. - /// - /// The huffman value - /// The decoded - /// The - public GolangDecoderErrorCode DecodeHuffmanUnsafe(ref GolangHuffmanTree huffmanTree, out int result) - { - result = 0; - - if (huffmanTree.Length == 0) - { - DecoderThrowHelper.ThrowImageFormatException.UninitializedHuffmanTable(); - } - - if (this.Bits.UnreadBits < 8) - { - this.LastErrorCode = this.Bits.Ensure8BitsUnsafe(ref this); - - if (this.LastErrorCode == GolangDecoderErrorCode.NoError) - { - int lutIndex = (this.Bits.Accumulator >> (this.Bits.UnreadBits - GolangHuffmanTree.LutSizeLog2)) & 0xFF; - int v = huffmanTree.Lut[lutIndex]; - - if (v != 0) - { - int n = (v & 0xFF) - 1; - this.Bits.UnreadBits -= n; - this.Bits.Mask >>= n; - result = v >> 8; - return this.LastErrorCode; - } - } - else - { - this.UnreadByteStuffedByte(); - return this.LastErrorCode; - } - } - - int code = 0; - for (int i = 0; i < GolangHuffmanTree.MaxCodeLength; i++) - { - if (this.Bits.UnreadBits == 0) - { - this.LastErrorCode = this.Bits.EnsureNBitsUnsafe(1, ref this); - - if (this.HasError) - { - return this.LastErrorCode; - } - } - - if ((this.Bits.Accumulator & this.Bits.Mask) != 0) - { - code |= 1; - } - - this.Bits.UnreadBits--; - this.Bits.Mask >>= 1; - - if (code <= huffmanTree.MaxCodes[i]) - { - result = huffmanTree.GetValue(code, i); - return this.LastErrorCode = GolangDecoderErrorCode.NoError; - } - - code <<= 1; - } - - // Unrecoverable error, throwing: - DecoderThrowHelper.ThrowImageFormatException.BadHuffmanCode(); - - // DUMMY RETURN! C# doesn't know we have thrown an exception! - return GolangDecoderErrorCode.NoError; - } - - /// - /// Skips the next n bytes. - /// - /// The number of bytes to ignore. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Skip(int count) - { - this.LastErrorCode = this.SkipUnsafe(count); - this.LastErrorCode.EnsureNoError(); - } - - /// - /// Skips the next n bytes. - /// Does not throw, returns instead! - /// - /// The number of bytes to ignore. - /// The - public GolangDecoderErrorCode SkipUnsafe(int count) - { - // Unread the overshot bytes, if any. - if (this.Bytes.UnreadableBytes != 0) - { - if (this.Bits.UnreadBits >= 8) - { - this.UnreadByteStuffedByte(); - } - - this.Bytes.UnreadableBytes = 0; - } - - while (true) - { - int m = this.Bytes.J - this.Bytes.I; - if (m > count) - { - m = count; - } - - this.Bytes.I += m; - count -= m; - if (count == 0) - { - break; - } - - this.LastErrorCode = this.Bytes.FillUnsafe(this.InputStream); - if (this.LastErrorCode != GolangDecoderErrorCode.NoError) - { - return this.LastErrorCode; - } - } - - return this.LastErrorCode = GolangDecoderErrorCode.NoError; - } - - /// - /// Reads exactly length bytes into data. It does not care about byte stuffing. - /// - /// The data to write to. - /// The offset in the source buffer - /// The number of bytes to read - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ReadFull(byte[] data, int offset, int length) - { - this.LastErrorCode = this.ReadFullUnsafe(data, offset, length); - this.LastErrorCode.EnsureNoError(); - } - - /// - /// Undoes the most recent ReadByteStuffedByte call, - /// giving a byte of data back from bits to bytes. The Huffman look-up table - /// requires at least 8 bits for look-up, which means that Huffman decoding can - /// sometimes overshoot and read one or two too many bytes. Two-byte overshoot - /// can happen when expecting to read a 0xff 0x00 byte-stuffed byte. - /// - public void UnreadByteStuffedByte() - { - this.Bytes.I -= this.Bytes.UnreadableBytes; - this.Bytes.UnreadableBytes = 0; - if (this.Bits.UnreadBits >= 8) - { - this.Bits.Accumulator >>= 8; - this.Bits.UnreadBits -= 8; - this.Bits.Mask >>= 8; - } - } - - /// - /// Receive extend - /// - /// Byte - /// Read bits value - /// The - public GolangDecoderErrorCode ReceiveExtendUnsafe(int t, out int x) - { - this.LastErrorCode = this.Bits.ReceiveExtendUnsafe(t, ref this, out x); - return this.LastErrorCode; - } - - /// - /// Reset the Huffman decoder. - /// - public void ResetHuffmanDecoder() - { - this.Bits = default; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/JpegScanDecoder.md b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/JpegScanDecoder.md deleted file mode 100644 index 4ca4d1f64..000000000 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/JpegScanDecoder.md +++ /dev/null @@ -1,25 +0,0 @@ -## JpegScanDecoder -Encapsulates the impementation of the Jpeg top-to bottom scan decoder triggered by the `SOS` marker. -The implementation is optimized to hold most of the necessary data in a single value type, which is intended to be used as an on-stack object. - -#### Benefits: -- Maximized locality of reference by keeping most of the operation data on the stack -- Achieving this without long parameter lists, most of the values describing the state of the decoder algorithm -are members of the `JpegScanDecoder` struct -- Most of the logic related to Scan decoding is refactored & simplified now to live in the methods of `JpegScanDecoder` -- The first step is done towards separating the stream reading from block processing. They can be refactored later to be executed in two disctinct loops. - - The input processing loop can be `async` - - The block processing loop can be parallelized - -#### Data layout - -|JpegScanDecoder | -|-------------------| -|Variables | -|DataPointers | -|ComputationData | - -- **ComputationData** holds the "large" data blocks needed for computations (Mostly `Block8x8F`-s) -- **DataPointers** contains pointers to the memory regions of `ComponentData` so they can be easily passed around to pointer based utility methods of `Block8x8F` - - diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/MissingFF00Exception.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/MissingFF00Exception.cs deleted file mode 100644 index 005034b9d..000000000 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/MissingFF00Exception.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; - -namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder -{ - /// - /// The missing ff00 exception. - /// - // ReSharper disable once InconsistentNaming - internal class MissingFF00Exception : Exception - { - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/GolangJpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/GolangJpegDecoder.cs deleted file mode 100644 index 29255204b..000000000 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/GolangJpegDecoder.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.IO; -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort -{ - /// - /// Image decoder for generating an image out of a jpg stream. - /// - internal sealed class GolangJpegDecoder : IImageDecoder, IJpegDecoderOptions, IImageInfoDetector - { - /// - /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. - /// - public bool IgnoreMetadata { get; set; } - - /// - public Image Decode(Configuration configuration, Stream stream) - where TPixel : struct, IPixel - { - Guard.NotNull(stream, nameof(stream)); - - using (var decoder = new GolangJpegDecoderCore(configuration, this)) - { - return decoder.Decode(stream); - } - } - - /// - public IImageInfo Identify(Configuration configuration, Stream stream) - { - Guard.NotNull(stream, nameof(stream)); - - using (var decoder = new GolangJpegDecoderCore(configuration, this)) - { - return decoder.Identify(stream); - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/GolangJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/GolangJpegDecoderCore.cs deleted file mode 100644 index 46cdcddb4..000000000 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/GolangJpegDecoderCore.cs +++ /dev/null @@ -1,824 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Collections.Generic; -using System.IO; - -using SixLabors.ImageSharp.Formats.Jpeg.Components; -using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; -using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder; -using SixLabors.ImageSharp.MetaData; -using SixLabors.ImageSharp.MetaData.Profiles.Exif; -using SixLabors.ImageSharp.MetaData.Profiles.Icc; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; -using SixLabors.Primitives; - -namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort -{ - /// - /// - /// Performs the jpeg decoding operation. - /// - internal sealed unsafe class GolangJpegDecoderCore : IRawJpegData - { - /// - /// The maximum number of color components - /// - public const int MaxComponents = 4; - - /// - /// The maximum number of quantization tables - /// - public const int MaxTq = 3; - - /// - /// The only supported precision - /// - public const int SupportedPrecision = 8; - - // Complex value type field + mutable + available to other classes = the field MUST NOT be private :P -#pragma warning disable SA1401 // FieldsMustBePrivate - - /// - /// Encapsulates stream reading and processing data and operations for . - /// It's a value type for improved data locality, and reduced number of CALLVIRT-s - /// - public InputProcessor InputProcessor; -#pragma warning restore SA401 - - /// - /// The global configuration - /// - private readonly Configuration configuration; - - /// - /// Whether the image has a JFIF header - /// It's faster to check this than to use the equality operator on the struct - /// - private bool isJFif; - - /// - /// Contains information about the JFIF marker - /// - private JFifMarker jFif; - - /// - /// Whether the image has a EXIF header - /// - private bool isExif; - - /// - /// Whether the image has an Adobe marker. - /// It's faster to check this than to use the equality operator on the struct - /// - private bool isAdobe; - - /// - /// Contains information about the Adobe marker - /// - private AdobeMarker adobe; - - /// - /// Initializes a new instance of the class. - /// - /// The configuration. - /// The options. - public GolangJpegDecoderCore(Configuration configuration, IJpegDecoderOptions options) - { - this.IgnoreMetadata = options.IgnoreMetadata; - this.configuration = configuration ?? Configuration.Default; - this.Temp = new byte[2 * Block8x8F.Size]; - } - - /// - public JpegColorSpace ColorSpace { get; private set; } - - /// - /// Gets the component array - /// - public GolangComponent[] Components { get; private set; } - - /// - /// Gets the huffman trees - /// - public GolangHuffmanTree[] HuffmanTrees { get; private set; } - - /// - public Block8x8F[] QuantizationTables { get; private set; } - - /// - /// Gets the temporary buffer used to store bytes read from the stream. - /// TODO: Should be stack allocated, fixed sized buffer! - /// - public byte[] Temp { get; } - - /// - public Size ImageSizeInPixels { get; private set; } - - /// - /// Gets the number of MCU blocks in the image as . - /// - public Size ImageSizeInMCU { get; private set; } - - /// - public int ComponentCount { get; private set; } - - IEnumerable IRawJpegData.Components => this.Components; - - /// - /// Gets the color depth, in number of bits per pixel. - /// - public int BitsPerPixel => this.ComponentCount * SupportedPrecision; - - /// - /// Gets the image height - /// - public int ImageHeight => this.ImageSizeInPixels.Height; - - /// - /// Gets the image width - /// - public int ImageWidth => this.ImageSizeInPixels.Width; - - /// - /// Gets the input stream. - /// - public Stream InputStream { get; private set; } - - /// - /// Gets a value indicating whether the image is interlaced (progressive) - /// - public bool IsProgressive { get; private set; } - - /// - /// Gets the restart interval - /// - public int RestartInterval { get; private set; } - - /// - /// Gets the number of MCU-s (Minimum Coded Units) in the image along the X axis - /// - public int MCUCountX => this.ImageSizeInMCU.Width; - - /// - /// Gets the number of MCU-s (Minimum Coded Units) in the image along the Y axis - /// - public int MCUCountY => this.ImageSizeInMCU.Height; - - /// - /// Gets the total number of MCU-s (Minimum Coded Units) in the image. - /// - public int TotalMCUCount => this.MCUCountX * this.MCUCountY; - - /// - /// Gets a value indicating whether the metadata should be ignored when the image is being decoded. - /// - public bool IgnoreMetadata { get; } - - /// - /// Gets the decoded by this decoder instance. - /// - public ImageMetaData MetaData { get; private set; } - - /// - /// Decodes the image from the specified and sets - /// the data to image. - /// - /// The pixel format. - /// The stream, where the image should be. - /// The decoded image. - public Image Decode(Stream stream) - where TPixel : struct, IPixel - { - this.ParseStream(stream); - return this.PostProcessIntoImage(); - } - - /// - /// Reads the raw image information from the specified stream. - /// - /// The containing image data. - public IImageInfo Identify(Stream stream) - { - this.ParseStream(stream, true); - return new ImageInfo(new PixelTypeInfo(this.BitsPerPixel), this.ImageWidth, this.ImageHeight, this.MetaData); - } - - /// - public void Dispose() - { - if (this.Components != null) - { - foreach (GolangComponent component in this.Components) - { - component?.Dispose(); - } - } - - this.InputProcessor.Dispose(); - } - - /// - /// Read metadata from stream and read the blocks in the scans into . - /// - /// The stream - /// Whether to decode metadata only. - public void ParseStream(Stream stream, bool metadataOnly = false) - { - this.MetaData = new ImageMetaData(); - this.InputStream = stream; - this.InputProcessor = new InputProcessor(stream, this.Temp); - - if (!metadataOnly) - { - this.HuffmanTrees = GolangHuffmanTree.CreateHuffmanTrees(); - this.QuantizationTables = new Block8x8F[MaxTq + 1]; - } - - // Check for the Start Of Image marker. - this.InputProcessor.ReadFull(this.Temp, 0, 2); - - if (this.Temp[0] != JpegConstants.Markers.XFF || this.Temp[1] != JpegConstants.Markers.SOI) - { - throw new ImageFormatException("Missing SOI marker."); - } - - // Process the remaining segments until the End Of Image marker. - bool processBytes = true; - - // we can't currently short circute progressive images so don't try. - 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 - // liberal in what it accepts. As of version 9, next_marker in - // jdmarker.c treats this as a warning (JWRN_EXTRANEOUS_DATA) and - // continues to decode the stream. Even before next_marker sees - // extraneous data, jpeg_fill_bit_buffer in jdhuff.c reads as many - // bytes as it can, possibly past the end of a scan's data. It - // effectively puts back any markers that it overscanned (e.g. an - // "\xff\xd9" EOI marker), but it does not put back non-marker data, - // and thus it can silently ignore a small number of extraneous - // non-marker bytes before next_marker has a chance to see them (and - // print a warning). - // We are therefore also liberal in what we accept. Extraneous data - // is silently ignore - // This is similar to, but not exactly the same as, the restart - // mechanism within a scan (the RST[0-7] markers). - // Note that extraneous 0xff bytes in e.g. SOS data are escaped as - // "\xff\x00", and so are detected a little further down below. - this.Temp[0] = this.Temp[1]; - this.Temp[1] = this.InputProcessor.ReadByte(); - } - - byte marker = this.Temp[1]; - if (marker == 0) - { - // Treat "\xff\x00" as extraneous data. - continue; - } - - while (marker == 0xff) - { - // 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'". - this.InputProcessor.ReadByteUnsafe(out marker); - - if (this.InputProcessor.ReachedEOF) - { - // We've reached the end of the stream. - processBytes = false; - break; - } - } - - // End Of Image. - if (marker == JpegConstants.Markers.EOI) - { - break; - } - - if (marker >= JpegConstants.Markers.RST0 && marker <= JpegConstants.Markers.RST7) - { - // Figures B.2 and B.16 of the specification suggest that restart markers should - // only occur between Entropy Coded Segments and not after the final ECS. - // However, some encoders may generate incorrect JPEGs with a final restart - // marker. That restart marker will be seen here instead of inside the ProcessSOS - // method, and is ignored as a harmless error. Restart markers have no extra data, - // so we check for this before we read the 16-bit length of the segment. - continue; - } - - // 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.ReadFullUnsafe(this.Temp, 0, 2); - int remaining = (this.Temp[0] << 8) + this.Temp[1] - 2; - if (remaining < 0) - { - throw new ImageFormatException("Short segment length."); - } - - switch (marker) - { - case JpegConstants.Markers.SOF0: - case JpegConstants.Markers.SOF1: - case JpegConstants.Markers.SOF2: - this.IsProgressive = marker == JpegConstants.Markers.SOF2; - this.ProcessStartOfFrameMarker(remaining, metadataOnly); - - break; - case JpegConstants.Markers.DHT: - - if (metadataOnly) - { - this.InputProcessor.Skip(remaining); - } - else - { - this.ProcessDefineHuffmanTablesMarker(remaining); - } - - break; - case JpegConstants.Markers.DQT: - if (metadataOnly) - { - this.InputProcessor.Skip(remaining); - } - else - { - this.ProcessDefineQuantizationTablesMarker(remaining); - } - - break; - case JpegConstants.Markers.SOS: - if (!metadataOnly) - { - this.ProcessStartOfScanMarker(remaining); - if (this.InputProcessor.ReachedEOF) - { - // If unexpected EOF reached. We can stop processing bytes as we now have the image data. - processBytes = false; - } - } - else - { - // It's highly unlikely that APPn related data will be found after the SOS marker - // We should have gathered everything we need by now. - processBytes = false; - } - - break; - - case JpegConstants.Markers.DRI: - if (metadataOnly) - { - this.InputProcessor.Skip(remaining); - } - else - { - this.ProcessDefineRestartIntervalMarker(remaining); - } - - break; - case JpegConstants.Markers.APP0: - this.ProcessApplicationHeaderMarker(remaining); - break; - case JpegConstants.Markers.APP1: - this.ProcessApp1Marker(remaining); - break; - case JpegConstants.Markers.APP2: - this.ProcessApp2Marker(remaining); - break; - case JpegConstants.Markers.APP14: - this.ProcessApp14Marker(remaining); - break; - default: - if ((marker >= JpegConstants.Markers.APP0 && marker <= JpegConstants.Markers.APP15) - || marker == JpegConstants.Markers.COM) - { - this.InputProcessor.Skip(remaining); - } - - break; - } - } - - 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. - /// - private void InitDerivedMetaDataProperties() - { - if (this.isJFif) - { - this.MetaData.HorizontalResolution = this.jFif.XDensity; - this.MetaData.VerticalResolution = this.jFif.YDensity; - } - else if (this.isExif) - { - double horizontalValue = this.MetaData.ExifProfile.TryGetValue(ExifTag.XResolution, out ExifValue horizonalTag) - ? ((Rational)horizonalTag.Value).ToDouble() - : 0; - - double verticalValue = this.MetaData.ExifProfile.TryGetValue(ExifTag.YResolution, out ExifValue verticalTag) - ? ((Rational)verticalTag.Value).ToDouble() - : 0; - - if (horizontalValue > 0 && verticalValue > 0) - { - this.MetaData.HorizontalResolution = horizontalValue; - this.MetaData.VerticalResolution = verticalValue; - } - } - - if (this.MetaData.IccProfile?.CheckIsValid() == false) - { - this.MetaData.IccProfile = null; - } - } - - /// - /// Processes the application header containing the JFIF identifier plus extra data. - /// - /// The remaining bytes in the segment block. - private void ProcessApplicationHeaderMarker(int remaining) - { - if (remaining < 5) - { - this.InputProcessor.Skip(remaining); - return; - } - - const int MarkerLength = JFifMarker.Length; - this.InputProcessor.ReadFull(this.Temp, 0, MarkerLength); - remaining -= MarkerLength; - - this.isJFif = JFifMarker.TryParse(this.Temp, out this.jFif); - - if (remaining > 0) - { - this.InputProcessor.Skip(remaining); - } - } - - /// - /// Processes the App1 marker retrieving any stored metadata - /// - /// The remaining bytes in the segment block. - private void ProcessApp1Marker(int remaining) - { - if (remaining < 6 || this.IgnoreMetadata) - { - this.InputProcessor.Skip(remaining); - return; - } - - byte[] profile = new byte[remaining]; - this.InputProcessor.ReadFull(profile, 0, remaining); - - if (ProfileResolver.IsProfile(profile, ProfileResolver.ExifMarker)) - { - this.isExif = true; - this.MetaData.ExifProfile = new ExifProfile(profile); - } - } - - /// - /// Processes the App2 marker retrieving any stored ICC profile information - /// - /// The remaining bytes in the segment block. - private void ProcessApp2Marker(int remaining) - { - // Length is 14 though we only need to check 12. - const int Icclength = 14; - if (remaining < Icclength || this.IgnoreMetadata) - { - this.InputProcessor.Skip(remaining); - return; - } - - byte[] identifier = new byte[Icclength]; - this.InputProcessor.ReadFull(identifier, 0, Icclength); - remaining -= Icclength; // We have read it by this point - - if (ProfileResolver.IsProfile(identifier, ProfileResolver.IccMarker)) - { - byte[] profile = new byte[remaining]; - this.InputProcessor.ReadFull(profile, 0, remaining); - - if (this.MetaData.IccProfile == null) - { - this.MetaData.IccProfile = new IccProfile(profile); - } - else - { - this.MetaData.IccProfile.Extend(profile); - } - } - else - { - // Not an ICC profile we can handle. Skip the remaining bytes so we can carry on and ignore this. - this.InputProcessor.Skip(remaining); - } - } - - /// - /// Processes the application header containing the Adobe identifier - /// which stores image encoding information for DCT filters. - /// - /// The remaining bytes in the segment block. - private void ProcessApp14Marker(int remaining) - { - const int MarkerLength = AdobeMarker.Length; - if (remaining < MarkerLength) - { - // Skip the application header length - this.InputProcessor.Skip(remaining); - return; - } - - this.InputProcessor.ReadFull(this.Temp, 0, MarkerLength); - remaining -= MarkerLength; - - this.isAdobe = AdobeMarker.TryParse(this.Temp, out this.adobe); - - if (remaining > 0) - { - this.InputProcessor.Skip(remaining); - } - } - - /// - /// Processes the Define Quantization Marker and tables. Specified in section B.2.4.1. - /// - /// The remaining bytes in the segment block. - /// - /// Thrown if the tables do not match the header - /// - private void ProcessDefineQuantizationTablesMarker(int remaining) - { - while (remaining > 0) - { - bool done = false; - - remaining--; - byte x = this.InputProcessor.ReadByte(); - int tq = x & 0x0F; - if (tq > MaxTq) - { - throw new ImageFormatException("Bad Tq value"); - } - - switch (x >> 4) - { - case 0: - if (remaining < Block8x8F.Size) - { - done = true; - break; - } - - remaining -= Block8x8F.Size; - this.InputProcessor.ReadFull(this.Temp, 0, Block8x8F.Size); - - for (int i = 0; i < Block8x8F.Size; i++) - { - this.QuantizationTables[tq][i] = this.Temp[i]; - } - - break; - case 1: - if (remaining < 2 * Block8x8F.Size) - { - done = true; - break; - } - - remaining -= 2 * Block8x8F.Size; - this.InputProcessor.ReadFull(this.Temp, 0, 2 * Block8x8F.Size); - - for (int i = 0; i < Block8x8F.Size; i++) - { - this.QuantizationTables[tq][i] = (this.Temp[2 * i] << 8) | this.Temp[(2 * i) + 1]; - } - - break; - default: - throw new ImageFormatException("Bad Pq value"); - } - - if (done) - { - break; - } - } - - if (remaining != 0) - { - throw new ImageFormatException("DQT has wrong length"); - } - } - - /// - /// Processes the Start of Frame marker. Specified in section B.2.2. - /// - /// The remaining bytes in the segment block. - /// Whether to decode metadata only. - private void ProcessStartOfFrameMarker(int remaining, bool metadataOnly) - { - if (this.ComponentCount != 0) - { - throw new ImageFormatException("Multiple SOF markers"); - } - - switch (remaining) - { - case 6 + (3 * 1): // grayscale image. - this.ComponentCount = 1; - break; - case 6 + (3 * 3): // YCbCr or RGB image. - this.ComponentCount = 3; - break; - case 6 + (3 * 4): // YCbCrK or CMYK image. - this.ComponentCount = 4; - break; - default: - throw new ImageFormatException("Incorrect number of components"); - } - - this.InputProcessor.ReadFull(this.Temp, 0, remaining); - - // We only support 8-bit precision. - if (this.Temp[0] != SupportedPrecision) - { - throw new ImageFormatException("Only 8-Bit precision supported."); - } - - int height = (this.Temp[1] << 8) + this.Temp[2]; - int width = (this.Temp[3] << 8) + this.Temp[4]; - - this.ImageSizeInPixels = new Size(width, height); - - if (this.Temp[5] != this.ComponentCount) - { - throw new ImageFormatException("SOF has wrong length"); - } - - if (!metadataOnly) - { - this.Components = new GolangComponent[this.ComponentCount]; - - for (int i = 0; i < this.ComponentCount; i++) - { - byte componentIdentifier = this.Temp[6 + (3 * i)]; - var component = new GolangComponent(componentIdentifier, i); - component.InitializeCoreData(this); - this.Components[i] = component; - } - - int h0 = this.Components[0].HorizontalSamplingFactor; - int v0 = this.Components[0].VerticalSamplingFactor; - - this.ImageSizeInMCU = this.ImageSizeInPixels.DivideRoundUp(8 * h0, 8 * v0); - - this.ColorSpace = this.DeduceJpegColorSpace(); - - foreach (GolangComponent component in this.Components) - { - component.InitializeDerivedData(this.configuration.MemoryAllocator, this); - } - } - } - - /// - /// Processes a Define Huffman Table marker, and initializes a huffman - /// struct from its contents. Specified in section B.2.4.2. - /// - /// The remaining bytes in the segment block. - private void ProcessDefineHuffmanTablesMarker(int remaining) - { - while (remaining > 0) - { - if (remaining < 17) - { - throw new ImageFormatException($"DHT has wrong length. {remaining}"); - } - - this.InputProcessor.ReadFull(this.Temp, 0, 17); - - int tc = this.Temp[0] >> 4; - if (tc > GolangHuffmanTree.MaxTc) - { - throw new ImageFormatException("Bad Tc value"); - } - - int th = this.Temp[0] & 0x0f; - if (th > GolangHuffmanTree.MaxTh) - { - throw new ImageFormatException("Bad Th value"); - } - - int huffTreeIndex = (tc * GolangHuffmanTree.ThRowSize) + th; - this.HuffmanTrees[huffTreeIndex].ProcessDefineHuffmanTablesMarkerLoop( - ref this.InputProcessor, - this.Temp, - ref remaining); - } - } - - /// - /// Processes the DRI (Define Restart Interval Marker) Which specifies the interval between RSTn markers, in - /// macroblocks - /// - /// The remaining bytes in the segment block. - private void ProcessDefineRestartIntervalMarker(int remaining) - { - if (remaining != 2) - { - throw new ImageFormatException("DRI has wrong length"); - } - - this.InputProcessor.ReadFull(this.Temp, 0, remaining); - this.RestartInterval = (this.Temp[0] << 8) + this.Temp[1]; - } - - /// - /// Processes the SOS (Start of scan marker). - /// - /// The remaining bytes in the segment block. - /// - /// Missing SOF Marker - /// SOS has wrong length - /// - private void ProcessStartOfScanMarker(int remaining) - { - GolangJpegScanDecoder scan = default; - GolangJpegScanDecoder.InitStreamReading(&scan, this, remaining); - this.InputProcessor.Bits = default; - scan.DecodeBlocks(this); - } - - private JpegColorSpace DeduceJpegColorSpace() - { - switch (this.ComponentCount) - { - case 1: - return JpegColorSpace.Grayscale; - case 3: - if (!this.isAdobe || this.adobe.ColorTransform == JpegConstants.Adobe.ColorTransformYCbCr) - { - return JpegColorSpace.YCbCr; - } - - if (this.adobe.ColorTransform == JpegConstants.Adobe.ColorTransformUnknown) - { - return JpegColorSpace.RGB; - } - - break; - case 4: - if (this.adobe.ColorTransform == JpegConstants.Adobe.ColorTransformYcck) - { - return JpegColorSpace.Ycck; - } - - return JpegColorSpace.Cmyk; - } - - throw new ImageFormatException($"Unsupported color mode. Max components 4; found {this.ComponentCount}." - + "JpegDecoder only supports YCbCr, RGB, YccK, CMYK and grayscale color spaces."); - } - - private Image PostProcessIntoImage() - where TPixel : struct, IPixel - { - using (var postProcessor = new JpegImagePostProcessor(this.configuration.MemoryAllocator, this)) - { - var image = new Image(this.configuration, this.ImageWidth, this.ImageHeight, this.MetaData); - postProcessor.PostProcess(image.Frames.RootFrame); - return image; - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs index eafbb391c..57b70dd26 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs @@ -2,8 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System.IO; - -using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Jpeg @@ -24,7 +22,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg { Guard.NotNull(stream, nameof(stream)); - using (var decoder = new PdfJsJpegDecoderCore(configuration, this)) + using (var decoder = new JpegDecoderCore(configuration, this)) { return decoder.Decode(stream); } @@ -35,7 +33,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg { Guard.NotNull(stream, nameof(stream)); - using (var decoder = new PdfJsJpegDecoderCore(configuration, this)) + using (var decoder = new JpegDecoderCore(configuration, this)) { return decoder.Identify(stream); } diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs similarity index 93% rename from src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs rename to src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 69b5e93b6..5c4dbfc24 100644 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -10,7 +10,6 @@ using System.Runtime.InteropServices; using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; -using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components; using SixLabors.ImageSharp.MetaData; using SixLabors.ImageSharp.MetaData.Profiles.Exif; using SixLabors.ImageSharp.MetaData.Profiles.Icc; @@ -19,14 +18,14 @@ using SixLabors.ImageSharp.Primitives; using SixLabors.Memory; using SixLabors.Primitives; -namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort +namespace SixLabors.ImageSharp.Formats.Jpeg { /// /// Performs the jpeg decoding operation. /// Originally ported from /// with additional fixes for both performance and common encoding errors. /// - internal sealed class PdfJsJpegDecoderCore : IRawJpegData + internal sealed class JpegDecoderCore : IRawJpegData { /// /// The only supported precision @@ -51,12 +50,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort /// /// The DC HUffman tables /// - private PdfJsHuffmanTables dcHuffmanTables; + private HuffmanTables dcHuffmanTables; /// /// The AC HUffman tables /// - private PdfJsHuffmanTables acHuffmanTables; + private HuffmanTables acHuffmanTables; /// /// The fast AC tables used for entropy decoding @@ -84,11 +83,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort private AdobeMarker adobe; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The configuration. /// The options. - public PdfJsJpegDecoderCore(Configuration configuration, IJpegDecoderOptions options) + public JpegDecoderCore(Configuration configuration, IJpegDecoderOptions options) { this.configuration = configuration ?? Configuration.Default; this.IgnoreMetadata = options.IgnoreMetadata; @@ -97,7 +96,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort /// /// Gets the frame /// - public PdfJsFrame Frame { get; private set; } + public JpegFrame Frame { get; private set; } /// public Size ImageSizeInPixels { get; private set; } @@ -146,7 +145,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort /// /// Gets the components. /// - public PdfJsFrameComponent[] Components => this.Frame.Components; + public JpegFrameComponent[] Components => this.Frame.Components; /// IEnumerable IRawJpegData.Components => this.Components; @@ -159,14 +158,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort /// /// The buffer to read file markers to /// The input stream - /// The - public static PdfJsFileMarker FindNextFileMarker(byte[] marker, DoubleBufferedStreamReader stream) + /// The + public static JpegFileMarker FindNextFileMarker(byte[] marker, DoubleBufferedStreamReader stream) { int value = stream.Read(marker, 0, 2); if (value == 0) { - return new PdfJsFileMarker(JpegConstants.Markers.EOI, stream.Length - 2); + return new JpegFileMarker(JpegConstants.Markers.EOI, stream.Length - 2); } if (marker[0] == JpegConstants.Markers.XFF) @@ -179,16 +178,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort int suffix = stream.ReadByte(); if (suffix == -1) { - return new PdfJsFileMarker(JpegConstants.Markers.EOI, stream.Length - 2); + return new JpegFileMarker(JpegConstants.Markers.EOI, stream.Length - 2); } m = suffix; } - return new PdfJsFileMarker((byte)m, stream.Position - 2); + return new JpegFileMarker((byte)m, stream.Position - 2); } - return new PdfJsFileMarker(marker[1], stream.Position - 2, true); + return new JpegFileMarker(marker[1], stream.Position - 2, true); } /// @@ -228,7 +227,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort // Check for the Start Of Image marker. this.InputStream.Read(this.markerBuffer, 0, 2); - var fileMarker = new PdfJsFileMarker(this.markerBuffer[1], 0); + var fileMarker = new JpegFileMarker(this.markerBuffer[1], 0); if (fileMarker.Marker != JpegConstants.Markers.SOI) { throw new ImageFormatException("Missing SOI marker."); @@ -236,14 +235,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort this.InputStream.Read(this.markerBuffer, 0, 2); byte marker = this.markerBuffer[1]; - fileMarker = new PdfJsFileMarker(marker, (int)this.InputStream.Position - 2); + fileMarker = new JpegFileMarker(marker, (int)this.InputStream.Position - 2); // Only assign what we need if (!metadataOnly) { this.QuantizationTables = new Block8x8F[4]; - this.dcHuffmanTables = new PdfJsHuffmanTables(); - this.acHuffmanTables = new PdfJsHuffmanTables(); + this.dcHuffmanTables = new HuffmanTables(); + this.acHuffmanTables = new HuffmanTables(); this.fastACTables = new FastACTables(this.configuration.MemoryAllocator); } @@ -630,7 +629,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort /// The remaining bytes in the segment block. /// The current frame marker. /// Whether to parse metadata only - private void ProcessStartOfFrameMarker(int remaining, PdfJsFileMarker frameMarker, bool metadataOnly) + private void ProcessStartOfFrameMarker(int remaining, JpegFileMarker frameMarker, bool metadataOnly) { if (this.Frame != null) { @@ -645,7 +644,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort throw new ImageFormatException("Only 8-Bit precision supported."); } - this.Frame = new PdfJsFrame + this.Frame = new JpegFrame { Extended = frameMarker.Marker == JpegConstants.Markers.SOF1, Progressive = frameMarker.Marker == JpegConstants.Markers.SOF2, @@ -667,7 +666,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort { // No need to pool this. They max out at 4 this.Frame.ComponentIds = new byte[this.Frame.ComponentCount]; - this.Frame.Components = new PdfJsFrameComponent[this.Frame.ComponentCount]; + this.Frame.Components = new JpegFrameComponent[this.Frame.ComponentCount]; this.ColorSpace = this.DeduceJpegColorSpace(); for (int i = 0; i < this.Frame.ComponentCount; i++) @@ -686,7 +685,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort maxV = v; } - var component = new PdfJsFrameComponent(this.configuration.MemoryAllocator, this.Frame, this.temp[index], h, v, this.temp[index + 2], i); + var component = new JpegFrameComponent(this.configuration.MemoryAllocator, this.Frame, this.temp[index], h, v, this.temp[index + 2], i); this.Frame.Components[i] = component; this.Frame.ComponentIds[i] = component.Id; @@ -794,7 +793,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort throw new ImageFormatException("Unknown component selector"); } - ref PdfJsFrameComponent component = ref this.Frame.Components[componentIndex]; + ref JpegFrameComponent component = ref this.Frame.Components[componentIndex]; int tableSpec = this.InputStream.ReadByte(); component.DCHuffmanTableId = tableSpec >> 4; component.ACHuffmanTableId = tableSpec & 15; @@ -831,9 +830,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort /// The codelengths /// The values [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void BuildHuffmanTable(PdfJsHuffmanTables tables, int index, ReadOnlySpan codeLengths, ReadOnlySpan values) + private void BuildHuffmanTable(HuffmanTables tables, int index, ReadOnlySpan codeLengths, ReadOnlySpan values) { - tables[index] = new PdfJsHuffmanTable(this.configuration.MemoryAllocator, codeLengths, values); + tables[index] = new HuffmanTable(this.configuration.MemoryAllocator, codeLengths, values); } /// diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoder.cs deleted file mode 100644 index e12278cc7..000000000 --- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoder.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.IO; -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort -{ - /// - /// Image decoder for generating an image out of a jpg stream. - /// - internal sealed class PdfJsJpegDecoder : IImageDecoder, IJpegDecoderOptions, IImageInfoDetector - { - /// - /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. - /// - public bool IgnoreMetadata { get; set; } - - /// - public Image Decode(Configuration configuration, Stream stream) - where TPixel : struct, IPixel - { - Guard.NotNull(stream, nameof(stream)); - - using (var decoder = new PdfJsJpegDecoderCore(configuration, this)) - { - return decoder.Decode(stream); - } - } - - /// - public IImageInfo Identify(Configuration configuration, Stream stream) - { - Guard.NotNull(stream, nameof(stream)); - - using (var decoder = new PdfJsJpegDecoderCore(configuration, this)) - { - return decoder.Identify(stream); - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs index f86919dd1..9b968e4db 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs @@ -4,8 +4,8 @@ using System.Drawing; using System.IO; using BenchmarkDotNet.Attributes; -using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; -using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort; + +using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests; using CoreSize = SixLabors.Primitives.Size; @@ -45,23 +45,11 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg } [Benchmark(Description = "Decode Jpeg - ImageSharp")] - public CoreSize JpegImageSharpOrig() - { - using (var memoryStream = new MemoryStream(this.jpegBytes)) - { - using (var image = Image.Load(memoryStream, new GolangJpegDecoder())) - { - return new CoreSize(image.Width, image.Height); - } - } - } - - [Benchmark(Description = "Decode Jpeg - ImageSharp PdfJs")] - public CoreSize JpegImageSharpPdfJs() + public CoreSize JpegImageSharp() { using (var memoryStream = new MemoryStream(this.jpegBytes)) { - using (var image = Image.Load(memoryStream, new PdfJsJpegDecoder())) + using (var image = Image.Load(memoryStream, new JpegDecoder())) { return new CoreSize(image.Width, image.Height); } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegMultiple.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegMultiple.cs index c4ee732f5..be0fe76b8 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegMultiple.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegMultiple.cs @@ -3,8 +3,7 @@ using System.Collections.Generic; using BenchmarkDotNet.Attributes; -using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; -using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort; +using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.PixelFormats; using SDImage = System.Drawing.Image; @@ -22,15 +21,9 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg protected override IEnumerable SearchPatterns => new[] { "*.jpg" }; [Benchmark(Description = "DecodeJpegMultiple - ImageSharp")] - public void DecodeJpegImageSharpOrig() + public void DecodeJpegImageSharp() { - this.ForEachStream(ms => Image.Load(ms, new GolangJpegDecoder())); - } - - [Benchmark(Description = "DecodeJpegMultiple - ImageSharp PDFJs")] - public void DecodeJpegImageSharpPdfJs() - { - this.ForEachStream(ms => Image.Load(ms, new PdfJsJpegDecoder())); + this.ForEachStream(ms => Image.Load(ms, new JpegDecoder())); } [Benchmark(Baseline = true, Description = "DecodeJpegMultiple - System.Drawing")] diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs index 059f312b3..5958b9f79 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegParseStreamOnly.cs @@ -5,7 +5,6 @@ using BenchmarkDotNet.Attributes; using System.Drawing; using System.IO; using SixLabors.ImageSharp.Formats.Jpeg; -using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort; using SixLabors.ImageSharp.Tests; namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg @@ -38,12 +37,12 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg } } - [Benchmark(Description = "PdfJsJpegDecoderCore.ParseStream")] + [Benchmark(Description = "JpegDecoderCore.ParseStream")] public void ParseStreamPdfJs() { using (var memoryStream = new MemoryStream(this.jpegBytes)) { - var decoder = new PdfJsJpegDecoderCore(Configuration.Default, new JpegDecoder() { IgnoreMetadata = true }); + var decoder = new JpegDecoderCore(Configuration.Default, new Formats.Jpeg.JpegDecoder() { IgnoreMetadata = true }); decoder.ParseStream(memoryStream); decoder.Dispose(); } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DoubleBufferedStreams.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DoubleBufferedStreams.cs index f91595df8..c70378464 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DoubleBufferedStreams.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DoubleBufferedStreams.cs @@ -4,16 +4,17 @@ using System; using System.IO; using BenchmarkDotNet.Attributes; -using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components; + +using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg { [Config(typeof(Config.ShortClr))] public class DoubleBufferedStreams { - private byte[] buffer = CreateTestBytes(); - private byte[] chunk1 = new byte[2]; - private byte[] chunk2 = new byte[2]; + private readonly byte[] buffer = CreateTestBytes(); + private readonly byte[] chunk1 = new byte[2]; + private readonly byte[] chunk2 = new byte[2]; private MemoryStream stream1; private MemoryStream stream2; diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpeg.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpeg.cs index 53f0630b9..d934a1d6d 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpeg.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/EncodeJpeg.cs @@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg [Benchmark(Baseline = true, Description = "System.Drawing Jpeg")] public void JpegSystemDrawing() { - using (MemoryStream memoryStream = new MemoryStream()) + using (var memoryStream = new MemoryStream()) { this.bmpDrawing.Save(memoryStream, ImageFormat.Jpeg); } @@ -54,7 +54,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg [Benchmark(Description = "ImageSharp Jpeg")] public void JpegCore() { - using (MemoryStream memoryStream = new MemoryStream()) + using (var memoryStream = new MemoryStream()) { this.bmpCore.SaveAsJpeg(memoryStream); } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/IdentifyJpeg.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/IdentifyJpeg.cs index b6ad20d12..ae32167a9 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/IdentifyJpeg.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/IdentifyJpeg.cs @@ -3,8 +3,8 @@ using System.IO; using BenchmarkDotNet.Attributes; -using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; -using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort; + +using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Tests; namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg @@ -29,22 +29,11 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg } [Benchmark] - public IImageInfo IdentifyGolang() - { - using (var memoryStream = new MemoryStream(this.jpegBytes)) - { - var decoder = new GolangJpegDecoder(); - - return decoder.Identify(Configuration.Default, memoryStream); - } - } - - [Benchmark] - public IImageInfo IdentifyPdfJs() + public IImageInfo Identify() { using (var memoryStream = new MemoryStream(this.jpegBytes)) { - var decoder = new PdfJsJpegDecoder(); + var decoder = new JpegDecoder(); return decoder.Identify(Configuration.Default, memoryStream); } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/LoadResizeSave.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/LoadResizeSave.cs index 296b3fb7a..1d485ee08 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/LoadResizeSave.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/LoadResizeSave.cs @@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); [Params( - TestImages.Jpeg.Baseline.Jpeg420Exif + TestImages.Jpeg.Baseline.Jpeg420Exif //, TestImages.Jpeg.Baseline.Calliphora )] public string TestImage { get; set; } @@ -74,10 +74,8 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg [Benchmark] public void ImageSharp() { - using (var source = Image.Load( - this.configuration, - this.sourceBytes, - new JpegDecoder { IgnoreMetadata = true })) + var source = Image.Load(this.configuration, this.sourceBytes, new JpegDecoder { IgnoreMetadata = true }); + using (source) using (var destStream = new MemoryStream(this.destBytes)) { source.Mutate(c => c.Resize(source.Width / 4, source.Height / 4)); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/DoubleBufferedStreamReaderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/DoubleBufferedStreamReaderTests.cs index 20b199441..5316ec758 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/DoubleBufferedStreamReaderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/DoubleBufferedStreamReaderTests.cs @@ -3,7 +3,8 @@ using System; using System.IO; -using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components; + +using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; using SixLabors.Memory; using Xunit; diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs index e266554d1..8169fdba3 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; -using SixLabors.Memory; + using SixLabors.ImageSharp.PixelFormats; using Xunit; // ReSharper disable InconsistentNaming @@ -13,33 +13,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg { [Theory] [WithFileCollection(nameof(BaselineTestJpegs), PixelTypes.Rgba32)] - public void DecodeBaselineJpeg_Orig(TestImageProvider provider) - where TPixel : struct, IPixel - { - if (SkipTest(provider)) - { - return; - } - - // For 32 bit test enviroments: - provider.Configuration.MemoryAllocator = ArrayPoolMemoryAllocator.CreateWithModeratePooling(); - - using (Image image = provider.GetImage(GolangJpegDecoder)) - { - image.DebugSave(provider); - provider.Utility.TestName = DecodeBaselineJpegOutputName; - image.CompareToReferenceOutput( - this.GetImageComparer(provider), - provider, - appendPixelTypeToFileName: false); - } - - provider.Configuration.MemoryAllocator.ReleaseRetainedResources(); - } - - [Theory] - [WithFileCollection(nameof(BaselineTestJpegs), PixelTypes.Rgba32)] - public void DecodeBaselineJpeg_PdfJs(TestImageProvider provider) + public void DecodeBaselineJpeg(TestImageProvider provider) where TPixel : struct, IPixel { if (SkipTest(provider)) @@ -48,7 +22,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg return; } - using (Image image = provider.GetImage(PdfJsJpegDecoder)) + using (Image image = provider.GetImage(JpegDecoder)) { image.DebugSave(provider); @@ -62,20 +36,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [Theory] [WithFile(TestImages.Jpeg.Issues.CriticalEOF214, PixelTypes.Rgba32)] - public void DecodeBaselineJpeg_CriticalEOF_ShouldThrow_Golang(TestImageProvider provider) - where TPixel : struct, IPixel - { - // TODO: We need a public ImageDecoderException class in ImageSharp! - Assert.ThrowsAny(() => provider.GetImage(GolangJpegDecoder)); - } - - [Theory] - [WithFile(TestImages.Jpeg.Issues.CriticalEOF214, PixelTypes.Rgba32)] - public void DecodeBaselineJpeg_CriticalEOF_ShouldThrow_PdfJs(TestImageProvider provider) + public void DecodeBaselineJpeg_CriticalEOF_ShouldThrow(TestImageProvider provider) where TPixel : struct, IPixel { // TODO: We need a public ImageDecoderException class in ImageSharp! - Assert.ThrowsAny(() => provider.GetImage(PdfJsJpegDecoder)); + Assert.ThrowsAny(() => provider.GetImage(JpegDecoder)); } [Theory(Skip = "Debug only, enable manually!")] diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.MetaData.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.MetaData.cs index f217f0df1..2e67c06c1 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.MetaData.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.MetaData.cs @@ -51,7 +51,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [Theory] [MemberData(nameof(MetaDataTestData))] - public void MetaDataIsParsedCorrectly_Orig( + public void MetaDataIsParsedCorrectly( bool useIdentify, string imagePath, int expectedPixelSize, @@ -60,25 +60,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg { TestMetaDataImpl( useIdentify, - GolangJpegDecoder, - imagePath, - expectedPixelSize, - exifProfilePresent, - iccProfilePresent); - } - - [Theory] - [MemberData(nameof(MetaDataTestData))] - public void MetaDataIsParsedCorrectly_PdfJs( - bool useIdentify, - string imagePath, - int expectedPixelSize, - bool exifProfilePresent, - bool iccProfilePresent) - { - TestMetaDataImpl( - useIdentify, - PdfJsJpegDecoder, + JpegDecoder, imagePath, expectedPixelSize, exifProfilePresent, @@ -216,7 +198,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [InlineData(true)] public void Decoder_Reads_Correct_Resolution_From_Jfif(bool useIdentify) { - TestImageInfo(TestImages.Jpeg.Baseline.Floorplan, DefaultJpegDecoder, useIdentify, + TestImageInfo(TestImages.Jpeg.Baseline.Floorplan, JpegDecoder, useIdentify, imageInfo => { Assert.Equal(300, imageInfo.MetaData.HorizontalResolution); @@ -229,7 +211,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [InlineData(true)] public void Decoder_Reads_Correct_Resolution_From_Exif(bool useIdentify) { - TestImageInfo(TestImages.Jpeg.Baseline.Jpeg420Exif, DefaultJpegDecoder, useIdentify, + TestImageInfo(TestImages.Jpeg.Baseline.Jpeg420Exif, JpegDecoder, useIdentify, imageInfo => { Assert.Equal(72, imageInfo.MetaData.HorizontalResolution); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Progressive.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Progressive.cs index c793b4034..9de788be2 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Progressive.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Progressive.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System.Linq; -using SixLabors.Memory; using SixLabors.ImageSharp.PixelFormats; using Xunit; // ReSharper disable InconsistentNaming @@ -15,7 +13,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [Theory] [WithFileCollection(nameof(ProgressiveTestJpegs), PixelTypes.Rgba32)] - public void DecodeProgressiveJpeg_Orig(TestImageProvider provider) + public void DecodeProgressiveJpeg(TestImageProvider provider) where TPixel : struct, IPixel { if (SkipTest(provider)) @@ -24,41 +22,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg return; } - // Golang decoder is unable to decode these: - if (PdfJsOnly.Any(fn => fn.Contains(provider.SourceFileOrDescription))) - { - return; - } - - // For 32 bit test enviroments: - provider.Configuration.MemoryAllocator = ArrayPoolMemoryAllocator.CreateWithModeratePooling(); - - using (Image image = provider.GetImage(GolangJpegDecoder)) - { - image.DebugSave(provider); - - provider.Utility.TestName = DecodeProgressiveJpegOutputName; - image.CompareToReferenceOutput( - this.GetImageComparer(provider), - provider, - appendPixelTypeToFileName: false); - } - - provider.Configuration.MemoryAllocator.ReleaseRetainedResources(); - } - - [Theory] - [WithFileCollection(nameof(ProgressiveTestJpegs), PixelTypes.Rgba32)] - public void DecodeProgressiveJpeg_PdfJs(TestImageProvider provider) - where TPixel : struct, IPixel - { - if (SkipTest(provider)) - { - // skipping to avoid OutOfMemoryException on CI - return; - } - - using (Image image = provider.GetImage(PdfJsJpegDecoder)) + using (Image image = provider.GetImage(JpegDecoder)) { image.DebugSave(provider); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index 41cc6db51..496891ce4 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -2,14 +2,10 @@ // 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.Formats.Jpeg; -using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; -using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort; using SixLabors.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; @@ -64,11 +60,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg private ITestOutputHelper Output { get; } - private static GolangJpegDecoder GolangJpegDecoder => new GolangJpegDecoder(); - - private static PdfJsJpegDecoder PdfJsJpegDecoder => new PdfJsJpegDecoder(); - - private static JpegDecoder DefaultJpegDecoder => new JpegDecoder(); + private static JpegDecoder JpegDecoder => new JpegDecoder(); [Fact] public void ParseStream_BasicPropertiesAreCorrect1_PdfJs() @@ -76,7 +68,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg byte[] bytes = TestFile.Create(TestImages.Jpeg.Progressive.Progress).Bytes; using (var ms = new MemoryStream(bytes)) { - var decoder = new PdfJsJpegDecoderCore(Configuration.Default, new JpegDecoder()); + var decoder = new JpegDecoderCore(Configuration.Default, new JpegDecoder()); decoder.ParseStream(ms); // I don't know why these numbers are different. All I know is that the decoder works @@ -89,9 +81,8 @@ 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)] - public void JpegDecoder_IsNotBoundToSinglePixelType(TestImageProvider provider, bool useOldDecoder) + [WithFile(TestImages.Jpeg.Baseline.Calliphora, CommonNonDefaultPixelTypes)] + public void JpegDecoder_IsNotBoundToSinglePixelType(TestImageProvider provider) where TPixel : struct, IPixel { if (SkipTest(provider)) @@ -102,8 +93,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg // For 32 bit test enviroments: provider.Configuration.MemoryAllocator = ArrayPoolMemoryAllocator.CreateWithModeratePooling(); - IImageDecoder decoder = useOldDecoder ? (IImageDecoder)GolangJpegDecoder : PdfJsJpegDecoder; - using (Image image = provider.GetImage(decoder)) + using (Image image = provider.GetImage(JpegDecoder)) { image.DebugSave(provider); @@ -125,7 +115,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg appendPixelTypeToFileName: false ).SingleOrDefault(); - if (report != null && report.TotalNormalizedDifference.HasValue) + if (report?.TotalNormalizedDifference != null) { return report.DifferencePercentageString; } @@ -139,17 +129,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg this.Output.WriteLine(provider.SourceFileOrDescription); provider.Utility.TestName = testName; - using (Image image = provider.GetImage(GolangJpegDecoder)) - { - string d = this.GetDifferenceInPercentageString(image, provider); - - this.Output.WriteLine($"Difference using ORIGINAL decoder: {d}"); - } - - using (Image image = provider.GetImage(PdfJsJpegDecoder)) + using (Image image = provider.GetImage(JpegDecoder)) { string d = this.GetDifferenceInPercentageString(image, provider); - this.Output.WriteLine($"Difference using PDFJS decoder: {d}"); + this.Output.WriteLine($"Difference using decoder: {d}"); } } @@ -175,7 +158,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg using (Image expectedImage = provider.GetReferenceOutputImage(appendPixelTypeToFileName: false)) using (var pdfJsOriginalResult = Image.Load(pdfJsOriginalResultPath)) - using (var pdfJsPortResult = Image.Load(sourceBytes, PdfJsJpegDecoder)) + using (var pdfJsPortResult = Image.Load(sourceBytes, JpegDecoder)) { ImageSimilarityReport originalReport = comparer.CompareImagesOrFrames(expectedImage, pdfJsOriginalResult); ImageSimilarityReport portReport = comparer.CompareImagesOrFrames(expectedImage, pdfJsPortResult); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegImagePostProcessorTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegImagePostProcessorTests.cs index 843843f79..cfa421a82 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegImagePostProcessorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegImagePostProcessorTests.cs @@ -1,9 +1,8 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; -using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; -using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; @@ -48,7 +47,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg where TPixel : struct, IPixel { string imageFile = provider.SourceFileOrDescription; - using (PdfJsJpegDecoderCore decoder = JpegFixture.ParsePdfJsStream(imageFile)) + using (JpegDecoderCore decoder = JpegFixture.ParseJpegStream(imageFile)) using (var pp = new JpegImagePostProcessor(Configuration.Default.MemoryAllocator, decoder)) using (var imageFrame = new ImageFrame(Configuration.Default, decoder.ImageWidth, decoder.ImageHeight)) { @@ -68,7 +67,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg where TPixel : struct, IPixel { string imageFile = provider.SourceFileOrDescription; - using (PdfJsJpegDecoderCore decoder = JpegFixture.ParsePdfJsStream(imageFile)) + using (JpegDecoderCore decoder = JpegFixture.ParseJpegStream(imageFile)) using (var pp = new JpegImagePostProcessor(Configuration.Default.MemoryAllocator, decoder)) using (var image = new Image(decoder.ImageWidth, decoder.ImageHeight)) { diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs index b0f342f5a..32538090d 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs @@ -8,8 +8,6 @@ using System.Numerics; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Jpeg; -using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; -using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort; using SixLabors.ImageSharp.PixelFormats; using Xunit; @@ -34,18 +32,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg TestImages.Jpeg.Baseline.Jpeg444, }; - //[Theory] // Benchmark, enable manually - //[MemberData(nameof(DecodeJpegData))] - public void DecodeJpeg_Original(string fileName) - { - this.DecodeJpegBenchmarkImpl(fileName, new GolangJpegDecoder()); - } - // [Theory] // Benchmark, enable manually // [MemberData(nameof(DecodeJpegData))] - public void DecodeJpeg_PdfJs(string fileName) + public void DecodeJpeg(string fileName) { - this.DecodeJpegBenchmarkImpl(fileName, new PdfJsJpegDecoder()); + this.DecodeJpegBenchmarkImpl(fileName, new JpegDecoder()); } private void DecodeJpegBenchmarkImpl(string fileName, IImageDecoder decoder) @@ -70,7 +61,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg ExecutionCount, () => { - Image img = Image.Load(bytes, decoder); + var img = Image.Load(bytes, decoder); }, // ReSharper disable once ExplicitCallerInfoArgument $"Decode {fileName}"); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/ParseStreamTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/ParseStreamTests.cs index 827a459cd..e4d8d29d4 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/ParseStreamTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/ParseStreamTests.cs @@ -2,12 +2,10 @@ // Licensed under the Apache License, Version 2.0. using System.Text; + +using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; -using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; -using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder; -using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort; -using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components; using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; using SixLabors.Primitives; @@ -30,53 +28,20 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [InlineData(TestImages.Jpeg.Baseline.Jpeg400, JpegColorSpace.Grayscale)] [InlineData(TestImages.Jpeg.Baseline.Ycck, JpegColorSpace.Ycck)] [InlineData(TestImages.Jpeg.Baseline.Cmyk, JpegColorSpace.Cmyk)] - public void ColorSpace_IsDeducedCorrectlyGolang(string imageFile, object expectedColorSpaceValue) + public void ColorSpace_IsDeducedCorrectly(string imageFile, object expectedColorSpaceValue) { var expecteColorSpace = (JpegColorSpace)expectedColorSpaceValue; - using (GolangJpegDecoderCore decoder = JpegFixture.ParseGolangStream(imageFile)) + using (JpegDecoderCore decoder = JpegFixture.ParseJpegStream(imageFile)) { Assert.Equal(expecteColorSpace, decoder.ColorSpace); } } - [Theory] - [InlineData(TestImages.Jpeg.Baseline.Testorig420, JpegColorSpace.YCbCr)] - [InlineData(TestImages.Jpeg.Baseline.Jpeg400, JpegColorSpace.Grayscale)] - [InlineData(TestImages.Jpeg.Baseline.Ycck, JpegColorSpace.Ycck)] - [InlineData(TestImages.Jpeg.Baseline.Cmyk, JpegColorSpace.Cmyk)] - public void ColorSpace_IsDeducedCorrectlyPdfJs(string imageFile, object expectedColorSpaceValue) - { - var expecteColorSpace = (JpegColorSpace)expectedColorSpaceValue; - - using (PdfJsJpegDecoderCore decoder = JpegFixture.ParsePdfJsStream(imageFile)) - { - Assert.Equal(expecteColorSpace, decoder.ColorSpace); - } - } - - [Fact] - public void ComponentScalingIsCorrect_1ChannelJpegGolang() - { - using (GolangJpegDecoderCore decoder = JpegFixture.ParseGolangStream(TestImages.Jpeg.Baseline.Jpeg400)) - { - Assert.Equal(1, decoder.ComponentCount); - Assert.Equal(1, decoder.Components.Length); - - Size expectedSizeInBlocks = decoder.ImageSizeInPixels.DivideRoundUp(8); - - Assert.Equal(expectedSizeInBlocks, decoder.ImageSizeInMCU); - - var uniform1 = new Size(1, 1); - GolangComponent c0 = decoder.Components[0]; - VerifyJpeg.VerifyComponent(c0, expectedSizeInBlocks, uniform1, uniform1); - } - } - [Fact] - public void ComponentScalingIsCorrect_1ChannelJpegPdfJs() + public void ComponentScalingIsCorrect_1ChannelJpeg() { - using (PdfJsJpegDecoderCore decoder = JpegFixture.ParsePdfJsStream(TestImages.Jpeg.Baseline.Jpeg400)) + using (JpegDecoderCore decoder = JpegFixture.ParseJpegStream(TestImages.Jpeg.Baseline.Jpeg400)) { Assert.Equal(1, decoder.ComponentCount); Assert.Equal(1, decoder.Components.Length); @@ -86,7 +51,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg Assert.Equal(expectedSizeInBlocks, decoder.ImageSizeInMCU); var uniform1 = new Size(1, 1); - PdfJsFrameComponent c0 = decoder.Components[0]; + JpegFrameComponent c0 = decoder.Components[0]; VerifyJpeg.VerifyComponent(c0, expectedSizeInBlocks, uniform1, uniform1); } } @@ -98,40 +63,16 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [InlineData(TestImages.Jpeg.Baseline.Testorig420)] [InlineData(TestImages.Jpeg.Baseline.Ycck)] [InlineData(TestImages.Jpeg.Baseline.Cmyk)] - public void PrintComponentDataGolang(string imageFile) - { - var sb = new StringBuilder(); - - using (GolangJpegDecoderCore decoder = JpegFixture.ParseGolangStream(imageFile)) - { - sb.AppendLine(imageFile); - sb.AppendLine($"Size:{decoder.ImageSizeInPixels} MCU:{decoder.ImageSizeInMCU}"); - GolangComponent c0 = decoder.Components[0]; - GolangComponent c1 = decoder.Components[1]; - - sb.AppendLine($"Luma: SAMP: {c0.SamplingFactors} BLOCKS: {c0.SizeInBlocks}"); - sb.AppendLine($"Chroma: {c1.SamplingFactors} BLOCKS: {c1.SizeInBlocks}"); - } - this.Output.WriteLine(sb.ToString()); - } - - [Theory] - [InlineData(TestImages.Jpeg.Baseline.Jpeg444)] - [InlineData(TestImages.Jpeg.Baseline.Jpeg420Exif)] - [InlineData(TestImages.Jpeg.Baseline.Jpeg420Small)] - [InlineData(TestImages.Jpeg.Baseline.Testorig420)] - [InlineData(TestImages.Jpeg.Baseline.Ycck)] - [InlineData(TestImages.Jpeg.Baseline.Cmyk)] - public void PrintComponentDataPdfJs(string imageFile) + public void PrintComponentData(string imageFile) { var sb = new StringBuilder(); - using (PdfJsJpegDecoderCore decoder = JpegFixture.ParsePdfJsStream(imageFile)) + using (JpegDecoderCore decoder = JpegFixture.ParseJpegStream(imageFile)) { sb.AppendLine(imageFile); sb.AppendLine($"Size:{decoder.ImageSizeInPixels} MCU:{decoder.ImageSizeInMCU}"); - PdfJsFrameComponent c0 = decoder.Components[0]; - PdfJsFrameComponent c1 = decoder.Components[1]; + JpegFrameComponent c0 = decoder.Components[0]; + JpegFrameComponent c1 = decoder.Components[1]; sb.AppendLine($"Luma: SAMP: {c0.SamplingFactors} BLOCKS: {c0.SizeInBlocks}"); sb.AppendLine($"Chroma: {c1.SamplingFactors} BLOCKS: {c1.SizeInBlocks}"); @@ -152,48 +93,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [Theory] [MemberData(nameof(ComponentVerificationData))] - public void ComponentScalingIsCorrect_MultiChannelJpegGolang( - string imageFile, - int componentCount, - object expectedLumaFactors, - object expectedChromaFactors) - { - var fLuma = (Size)expectedLumaFactors; - var fChroma = (Size)expectedChromaFactors; - - using (GolangJpegDecoderCore decoder = JpegFixture.ParseGolangStream(imageFile)) - { - Assert.Equal(componentCount, decoder.ComponentCount); - Assert.Equal(componentCount, decoder.Components.Length); - - GolangComponent c0 = decoder.Components[0]; - GolangComponent c1 = decoder.Components[1]; - GolangComponent c2 = decoder.Components[2]; - - var uniform1 = new Size(1, 1); - - Size expectedLumaSizeInBlocks = decoder.ImageSizeInMCU.MultiplyBy(fLuma); - - Size divisor = fLuma.DivideBy(fChroma); - - Size expectedChromaSizeInBlocks = expectedLumaSizeInBlocks.DivideRoundUp(divisor); - - VerifyJpeg.VerifyComponent(c0, expectedLumaSizeInBlocks, fLuma, uniform1); - VerifyJpeg.VerifyComponent(c1, expectedChromaSizeInBlocks, fChroma, divisor); - VerifyJpeg.VerifyComponent(c2, expectedChromaSizeInBlocks, fChroma, divisor); - - if (componentCount == 4) - { - GolangComponent c3 = decoder.Components[2]; - VerifyJpeg.VerifyComponent(c3, expectedLumaSizeInBlocks, fLuma, uniform1); - } - } - } - - - [Theory] - [MemberData(nameof(ComponentVerificationData))] - public void ComponentScalingIsCorrect_MultiChannelJpegPdfJs( + public void ComponentScalingIsCorrect_MultiChannelJpeg( string imageFile, int componentCount, object expectedLumaFactors, @@ -202,14 +102,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg var fLuma = (Size)expectedLumaFactors; var fChroma = (Size)expectedChromaFactors; - using (PdfJsJpegDecoderCore decoder = JpegFixture.ParsePdfJsStream(imageFile)) + using (JpegDecoderCore decoder = JpegFixture.ParseJpegStream(imageFile)) { Assert.Equal(componentCount, decoder.ComponentCount); Assert.Equal(componentCount, decoder.Components.Length); - PdfJsFrameComponent c0 = decoder.Components[0]; - PdfJsFrameComponent c1 = decoder.Components[1]; - PdfJsFrameComponent c2 = decoder.Components[2]; + JpegFrameComponent c0 = decoder.Components[0]; + JpegFrameComponent c1 = decoder.Components[1]; + JpegFrameComponent c2 = decoder.Components[2]; var uniform1 = new Size(1, 1); @@ -225,7 +125,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg if (componentCount == 4) { - PdfJsFrameComponent c3 = decoder.Components[2]; + JpegFrameComponent c3 = decoder.Components[2]; VerifyJpeg.VerifyComponent(c3, expectedLumaSizeInBlocks, fLuma, uniform1); } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs index 46a688b49..9378b1c8e 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs @@ -4,8 +4,6 @@ using System.IO; using System.Linq; using SixLabors.ImageSharp.Formats.Jpeg; -using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; -using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; @@ -42,10 +40,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg [Theory(Skip = "Debug only, enable manually!")] [WithFileCollection(nameof(AllTestJpegs), PixelTypes.Rgba32)] - public void PdfJsDecoder_ParseStream_SaveSpectralResult(TestImageProvider provider) + public void Decoder_ParseStream_SaveSpectralResult(TestImageProvider provider) where TPixel : struct, IPixel { - var decoder = new PdfJsJpegDecoderCore(Configuration.Default, new JpegDecoder()); + var decoder = new JpegDecoderCore(Configuration.Default, new JpegDecoder()); byte[] sourceBytes = TestFile.Create(provider.SourceFileOrDescription).Bytes; @@ -58,25 +56,30 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg } } - [Theory(Skip = "Debug only, enable manually!")] + [Theory] [WithFileCollection(nameof(AllTestJpegs), PixelTypes.Rgba32)] - public void OriginalDecoder_ParseStream_SaveSpectralResult(TestImageProvider provider) + public void VerifySpectralCorrectness(TestImageProvider provider) where TPixel : struct, IPixel { - var decoder = new GolangJpegDecoderCore(Configuration.Default, new JpegDecoder()); + if (!TestEnvironment.IsWindows) + { + return; + } + + var decoder = new JpegDecoderCore(Configuration.Default, new JpegDecoder()); byte[] sourceBytes = TestFile.Create(provider.SourceFileOrDescription).Bytes; using (var ms = new MemoryStream(sourceBytes)) { - decoder.ParseStream(ms, false); + decoder.ParseStream(ms); + var imageSharpData = LibJpegTools.SpectralData.LoadFromImageSharpDecoder(decoder); - var data = LibJpegTools.SpectralData.LoadFromImageSharpDecoder(decoder); - VerifyJpeg.SaveSpectralImage(provider, data); + this.VerifySpectralCorrectnessImpl(provider, imageSharpData); } } - - private void VerifySpectralCorrectness( + + private void VerifySpectralCorrectnessImpl( TestImageProvider provider, LibJpegTools.SpectralData imageSharpData) where TPixel : struct, IPixel @@ -119,51 +122,5 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg Assert.True(totalDifference < tolerance); } - - [Theory] - [WithFileCollection(nameof(AllTestJpegs), PixelTypes.Rgba32)] - public void VerifySpectralCorrectness_PdfJs(TestImageProvider provider) - where TPixel : struct, IPixel - { - if (!TestEnvironment.IsWindows) - { - return; - } - - var decoder = new PdfJsJpegDecoderCore(Configuration.Default, new JpegDecoder()); - - byte[] sourceBytes = TestFile.Create(provider.SourceFileOrDescription).Bytes; - - using (var ms = new MemoryStream(sourceBytes)) - { - decoder.ParseStream(ms); - var imageSharpData = LibJpegTools.SpectralData.LoadFromImageSharpDecoder(decoder); - - this.VerifySpectralCorrectness(provider, imageSharpData); - } - } - - [Theory] - [WithFileCollection(nameof(AllTestJpegs), PixelTypes.Rgba32)] - public void VerifySpectralCorrectness_Golang(TestImageProvider provider) - where TPixel : struct, IPixel - { - if (!TestEnvironment.IsWindows) - { - return; - } - - var decoder = new GolangJpegDecoderCore(Configuration.Default, new GolangJpegDecoder()); - - byte[] sourceBytes = TestFile.Create(provider.SourceFileOrDescription).Bytes; - - using (var ms = new MemoryStream(sourceBytes)) - { - decoder.ParseStream(ms); - var imageSharpData = LibJpegTools.SpectralData.LoadFromImageSharpDecoder(decoder); - - this.VerifySpectralCorrectness(provider, imageSharpData); - } - } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs index 7fe98e2af..d14fbc3fc 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs @@ -10,8 +10,6 @@ using System.Text; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg.Components; -using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; -using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort; using Xunit; using Xunit.Abstractions; @@ -175,23 +173,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils Assert.False(failed); } - internal static GolangJpegDecoderCore ParseGolangStream(string testFileName, bool metaDataOnly = false) + internal static JpegDecoderCore ParseJpegStream(string testFileName, bool metaDataOnly = false) { byte[] bytes = TestFile.Create(testFileName).Bytes; using (var ms = new MemoryStream(bytes)) { - var decoder = new GolangJpegDecoderCore(Configuration.Default, new JpegDecoder()); - decoder.ParseStream(ms, metaDataOnly); - return decoder; - } - } - - internal static PdfJsJpegDecoderCore ParsePdfJsStream(string testFileName, bool metaDataOnly = false) - { - byte[] bytes = TestFile.Create(testFileName).Bytes; - using (var ms = new MemoryStream(bytes)) - { - var decoder = new PdfJsJpegDecoderCore(Configuration.Default, new JpegDecoder()); + var decoder = new JpegDecoderCore(Configuration.Default, new JpegDecoder()); decoder.ParseStream(ms, metaDataOnly); return decoder; } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs index 645ee4512..5ffd5d62b 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs @@ -1,18 +1,15 @@ +using System; +using System.Linq; +using System.Numerics; + using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Memory; +using SixLabors.Primitives; namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils { - using System; - using System.Linq; - using System.Numerics; - - using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder; - using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components; - using SixLabors.Memory; - using SixLabors.Primitives; - internal static partial class LibJpegTools { /// @@ -57,7 +54,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils this.SpectralBlocks[x, y] = new Block8x8(data); } - public static ComponentData Load(PdfJsFrameComponent c, int index) + public static ComponentData Load(JpegFrameComponent c, int index) { var result = new ComponentData( c.WidthInBlocks, @@ -77,26 +74,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils return result; } - public static ComponentData Load(GolangComponent c) - { - var result = new ComponentData( - c.SizeInBlocks.Width, - c.SizeInBlocks.Height, - c.Index - ); - - for (int y = 0; y < result.HeightInBlocks; y++) - { - for (int x = 0; x < result.WidthInBlocks; x++) - { - short[] data = c.GetBlockReference(x, y).ToArray(); - result.MakeBlock(data, y, x); - } - } - - return result; - } - public Image CreateGrayScaleImage() { var result = new Image(this.WidthInBlocks * 8, this.HeightInBlocks * 8); @@ -143,8 +120,16 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils public bool Equals(ComponentData other) { - if (object.ReferenceEquals(null, other)) return false; - if (object.ReferenceEquals(this, other)) return true; + if (other is null) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + bool ok = this.Index == other.Index && this.HeightInBlocks == other.HeightInBlocks && this.WidthInBlocks == other.WidthInBlocks; //&& this.MinVal == other.MinVal @@ -165,8 +150,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils public override bool Equals(object obj) { - if (Object.ReferenceEquals(null, obj)) return false; - if (Object.ReferenceEquals(this, obj)) return true; + if (obj is null) return false; + if (object.ReferenceEquals(this, obj)) return true; if (obj.GetType() != this.GetType()) return false; return this.Equals((ComponentData)obj); } @@ -175,7 +160,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils { unchecked { - var hashCode = this.Index; + int hashCode = this.Index; hashCode = (hashCode * 397) ^ this.HeightInBlocks; hashCode = (hashCode * 397) ^ this.WidthInBlocks; hashCode = (hashCode * 397) ^ this.MinVal.GetHashCode(); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.SpectralData.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.SpectralData.cs index ae8194e1a..e9f0b1138 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.SpectralData.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.SpectralData.cs @@ -5,11 +5,9 @@ using System; using System.Linq; using System.Numerics; +using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg.Components; -using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; -using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder; -using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort; -using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components; +using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils @@ -32,17 +30,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils this.Components = components; } - public static SpectralData LoadFromImageSharpDecoder(PdfJsJpegDecoderCore decoder) + public static SpectralData LoadFromImageSharpDecoder(JpegDecoderCore decoder) { - PdfJsFrameComponent[] srcComponents = decoder.Frame.Components; - LibJpegTools.ComponentData[] destComponents = srcComponents.Select(LibJpegTools.ComponentData.Load).ToArray(); - - return new SpectralData(destComponents); - } - - public static SpectralData LoadFromImageSharpDecoder(GolangJpegDecoderCore decoder) - { - GolangComponent[] srcComponents = decoder.Components; + JpegFrameComponent[] srcComponents = decoder.Frame.Components; LibJpegTools.ComponentData[] destComponents = srcComponents.Select(LibJpegTools.ComponentData.Load).ToArray(); return new SpectralData(destComponents); @@ -108,8 +98,16 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils public bool Equals(SpectralData other) { - if (object.ReferenceEquals(null, other)) return false; - if (object.ReferenceEquals(this, other)) return true; + if (other is null) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + if (this.ComponentCount != other.ComponentCount) { return false; From 0f510f1ee8b661457889ac3dbbcd904b22411b4e Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 24 Jul 2018 10:24:23 +0100 Subject: [PATCH 10/13] Cleanup JpegFrameComponent --- .../Jpeg/Components/Decoder/FastACTables.cs | 4 ++-- ...JpegFrameComponent.cs => JpegComponent.cs} | 17 ++++--------- .../Jpeg/Components/Decoder/JpegFrame.cs | 4 ++-- .../Jpeg/Components/Decoder/ScanDecoder.cs | 24 +++++++++---------- .../Formats/Jpeg/JpegDecoderCore.cs | 8 +++---- .../Formats/Jpg/ParseStreamTests.cs | 14 +++++------ .../Jpg/Utils/LibJpegTools.ComponentData.cs | 2 +- .../Jpg/Utils/LibJpegTools.SpectralData.cs | 2 +- 8 files changed, 34 insertions(+), 41 deletions(-) rename src/ImageSharp/Formats/Jpeg/Components/Decoder/{JpegFrameComponent.cs => JpegComponent.cs} (88%) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTables.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTables.cs index 6d06abecf..95693c09b 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTables.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTables.cs @@ -35,9 +35,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder } /// - /// Gets a reference to the first element of the AC table indexed by /// + /// Gets a reference to the first element of the AC table indexed by /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref short GetAcTableReference(JpegFrameComponent component) + public ref short GetAcTableReference(JpegComponent component) { return ref this.tables.GetRowSpan(component.ACHuffmanTableId)[0]; } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrameComponent.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs similarity index 88% rename from src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrameComponent.cs rename to src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs index 3ce7cacfa..73a69a069 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrameComponent.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs @@ -13,11 +13,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// /// Represents a single frame component /// - internal class JpegFrameComponent : IDisposable, IJpegComponent + internal class JpegComponent : IDisposable, IJpegComponent { private readonly MemoryAllocator memoryAllocator; - public JpegFrameComponent(MemoryAllocator memoryAllocator, JpegFrame frame, byte id, int horizontalFactor, int verticalFactor, byte quantizationTableIndex, int index) + public JpegComponent(MemoryAllocator memoryAllocator, JpegFrame frame, byte id, int horizontalFactor, int verticalFactor, byte quantizationTableIndex, int index) { this.memoryAllocator = memoryAllocator; this.Frame = frame; @@ -123,7 +123,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder } else { - JpegFrameComponent c0 = this.Frame.Components[0]; + JpegComponent c0 = this.Frame.Components[0]; this.SubSamplingDivisors = c0.SamplingFactors.DivideBy(this.SamplingFactors); } @@ -138,16 +138,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int GetBlockBufferOffset(int row, int col) + public ref short GetBlockDataReference(int column, int row) { - return 64 * (((this.WidthInBlocks + 1) * row) + col); - } - - // TODO: we need consistence in (row, col) VS (col, row) ordering - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref short GetBlockDataReference(int row, int col) - { - ref Block8x8 blockRef = ref this.GetBlockReference(col, row); + ref Block8x8 blockRef = ref this.GetBlockReference(column, row); return ref Unsafe.As(ref blockRef); } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs index a238e0734..da089fa44 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs @@ -48,7 +48,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// /// Gets or sets the frame component collection /// - public JpegFrameComponent[] Components { get; set; } + public JpegComponent[] Components { get; set; } /// /// Gets or sets the maximum horizontal sampling factor @@ -94,7 +94,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder for (int i = 0; i < this.ComponentCount; i++) { - JpegFrameComponent component = this.Components[i]; + JpegComponent component = this.Components[i]; component.Init(); } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs index 5afe6382f..99eaf7f43 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs @@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder private readonly FastACTables fastACTables; private readonly DoubleBufferedStreamReader stream; - private readonly JpegFrameComponent[] components; + private readonly JpegComponent[] components; private readonly ZigZag dctZigZag; // The restart interval. @@ -175,7 +175,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder // Scan an interleaved mcu... process components in order for (int k = 0; k < this.componentsLength; k++) { - JpegFrameComponent component = this.components[k]; + JpegComponent component = this.components[k]; ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId]; @@ -229,7 +229,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// private void ParseBaselineDataNonInterleaved() { - JpegFrameComponent component = this.components[this.componentIndex]; + JpegComponent component = this.components[this.componentIndex]; int w = component.WidthInBlocks; int h = component.HeightInBlocks; @@ -294,7 +294,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder // Scan an interleaved mcu... process components in order for (int k = 0; k < this.componentsLength; k++) { - JpegFrameComponent component = this.components[k]; + JpegComponent component = this.components[k]; ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; int h = component.HorizontalSamplingFactor; int v = component.VerticalSamplingFactor; @@ -343,7 +343,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// private void ParseProgressiveDataNonInterleaved() { - JpegFrameComponent component = this.components[this.componentIndex]; + JpegComponent component = this.components[this.componentIndex]; int w = component.WidthInBlocks; int h = component.HeightInBlocks; @@ -394,7 +394,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder } private void DecodeBlockBaseline( - JpegFrameComponent component, + JpegComponent component, int row, int col, ref HuffmanTable dcTable, @@ -409,7 +409,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder JpegThrowHelper.ThrowBadHuffmanCode(); } - ref short blockDataRef = ref component.GetBlockDataReference(row, col); + ref short blockDataRef = ref component.GetBlockDataReference(col, row); int diff = t != 0 ? this.ExtendReceive(t) : 0; int dc = component.DcPredictor + diff; @@ -473,7 +473,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder } private void DecodeBlockProgressiveDC( - JpegFrameComponent component, + JpegComponent component, int row, int col, ref HuffmanTable dcTable) @@ -485,7 +485,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder this.CheckBits(); - ref short blockDataRef = ref component.GetBlockDataReference(row, col); + ref short blockDataRef = ref component.GetBlockDataReference(col, row); if (this.successiveHigh == 0) { @@ -509,7 +509,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder } private void DecodeBlockProgressiveAC( - JpegFrameComponent component, + JpegComponent component, int row, int col, ref HuffmanTable acTable, @@ -520,7 +520,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder JpegThrowHelper.ThrowImageFormatException("Can't merge DC and AC."); } - ref short blockDataRef = ref component.GetBlockDataReference(row, col); + ref short blockDataRef = ref component.GetBlockDataReference(col, row); if (this.successiveHigh == 0) { @@ -939,7 +939,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder for (int i = 0; i < this.components.Length; i++) { - JpegFrameComponent c = this.components[i]; + JpegComponent c = this.components[i]; c.DcPredictor = 0; } diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 5c4dbfc24..07209bc28 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -145,7 +145,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// /// Gets the components. /// - public JpegFrameComponent[] Components => this.Frame.Components; + public JpegComponent[] Components => this.Frame.Components; /// IEnumerable IRawJpegData.Components => this.Components; @@ -666,7 +666,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg { // No need to pool this. They max out at 4 this.Frame.ComponentIds = new byte[this.Frame.ComponentCount]; - this.Frame.Components = new JpegFrameComponent[this.Frame.ComponentCount]; + this.Frame.Components = new JpegComponent[this.Frame.ComponentCount]; this.ColorSpace = this.DeduceJpegColorSpace(); for (int i = 0; i < this.Frame.ComponentCount; i++) @@ -685,7 +685,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg maxV = v; } - var component = new JpegFrameComponent(this.configuration.MemoryAllocator, this.Frame, this.temp[index], h, v, this.temp[index + 2], i); + var component = new JpegComponent(this.configuration.MemoryAllocator, this.Frame, this.temp[index], h, v, this.temp[index + 2], i); this.Frame.Components[i] = component; this.Frame.ComponentIds[i] = component.Id; @@ -793,7 +793,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg throw new ImageFormatException("Unknown component selector"); } - ref JpegFrameComponent component = ref this.Frame.Components[componentIndex]; + ref JpegComponent component = ref this.Frame.Components[componentIndex]; int tableSpec = this.InputStream.ReadByte(); component.DCHuffmanTableId = tableSpec >> 4; component.ACHuffmanTableId = tableSpec & 15; diff --git a/tests/ImageSharp.Tests/Formats/Jpg/ParseStreamTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/ParseStreamTests.cs index e4d8d29d4..3657110c6 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/ParseStreamTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/ParseStreamTests.cs @@ -51,7 +51,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg Assert.Equal(expectedSizeInBlocks, decoder.ImageSizeInMCU); var uniform1 = new Size(1, 1); - JpegFrameComponent c0 = decoder.Components[0]; + JpegComponent c0 = decoder.Components[0]; VerifyJpeg.VerifyComponent(c0, expectedSizeInBlocks, uniform1, uniform1); } } @@ -71,8 +71,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg { sb.AppendLine(imageFile); sb.AppendLine($"Size:{decoder.ImageSizeInPixels} MCU:{decoder.ImageSizeInMCU}"); - JpegFrameComponent c0 = decoder.Components[0]; - JpegFrameComponent c1 = decoder.Components[1]; + JpegComponent c0 = decoder.Components[0]; + JpegComponent c1 = decoder.Components[1]; sb.AppendLine($"Luma: SAMP: {c0.SamplingFactors} BLOCKS: {c0.SizeInBlocks}"); sb.AppendLine($"Chroma: {c1.SamplingFactors} BLOCKS: {c1.SizeInBlocks}"); @@ -107,9 +107,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg Assert.Equal(componentCount, decoder.ComponentCount); Assert.Equal(componentCount, decoder.Components.Length); - JpegFrameComponent c0 = decoder.Components[0]; - JpegFrameComponent c1 = decoder.Components[1]; - JpegFrameComponent c2 = decoder.Components[2]; + JpegComponent c0 = decoder.Components[0]; + JpegComponent c1 = decoder.Components[1]; + JpegComponent c2 = decoder.Components[2]; var uniform1 = new Size(1, 1); @@ -125,7 +125,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg if (componentCount == 4) { - JpegFrameComponent c3 = decoder.Components[2]; + JpegComponent c3 = decoder.Components[2]; VerifyJpeg.VerifyComponent(c3, expectedLumaSizeInBlocks, fLuma, uniform1); } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs index 5ffd5d62b..a10deb983 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs @@ -54,7 +54,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils this.SpectralBlocks[x, y] = new Block8x8(data); } - public static ComponentData Load(JpegFrameComponent c, int index) + public static ComponentData Load(JpegComponent c, int index) { var result = new ComponentData( c.WidthInBlocks, diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.SpectralData.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.SpectralData.cs index e9f0b1138..bcfabca39 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.SpectralData.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.SpectralData.cs @@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils public static SpectralData LoadFromImageSharpDecoder(JpegDecoderCore decoder) { - JpegFrameComponent[] srcComponents = decoder.Frame.Components; + JpegComponent[] srcComponents = decoder.Frame.Components; LibJpegTools.ComponentData[] destComponents = srcComponents.Select(LibJpegTools.ComponentData.Load).ToArray(); return new SpectralData(destComponents); From ffee0fbcc71d215826ae626c77bdc7725bc4aeb7 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 24 Jul 2018 10:59:17 +0100 Subject: [PATCH 11/13] Fix sandbox --- tests/ImageSharp.Sandbox46/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ImageSharp.Sandbox46/Program.cs b/tests/ImageSharp.Sandbox46/Program.cs index f3875e24a..fa1d63878 100644 --- a/tests/ImageSharp.Sandbox46/Program.cs +++ b/tests/ImageSharp.Sandbox46/Program.cs @@ -75,7 +75,7 @@ namespace SixLabors.ImageSharp.Sandbox46 foreach (object[] data in JpegProfilingBenchmarks.DecodeJpegData) { string fileName = (string)data[0]; - benchmarks.DecodeJpeg_Original(fileName); + benchmarks.DecodeJpeg(fileName); } } } From 0e06b6b46694bdee0d803917694fd407aab0b254 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 24 Jul 2018 13:01:58 +0100 Subject: [PATCH 12/13] Improve coverage. --- .../Jpeg/Components/Decoder/JpegFileMarker.cs | 4 +-- .../Jpeg/Components/Decoder/ScanDecoder.cs | 1 + .../Formats/Jpeg/JpegDecoderCore.cs | 1 + .../DoubleBufferedStreamReader.cs | 3 +-- .../Codecs/Jpeg/DoubleBufferedStreams.cs | 3 +-- .../Formats/Jpg/JpegFileMarkerTests.cs | 25 +++++++++++++++++++ .../DoubleBufferedStreamReaderTests.cs | 25 ++++++++++++++++--- 7 files changed, 51 insertions(+), 11 deletions(-) rename src/ImageSharp/{Formats/Jpeg/Components/Decoder => IO}/DoubleBufferedStreamReader.cs (98%) create mode 100644 tests/ImageSharp.Tests/Formats/Jpg/JpegFileMarkerTests.cs rename tests/ImageSharp.Tests/{Formats/Jpg => IO}/DoubleBufferedStreamReaderTests.cs (87%) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFileMarker.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFileMarker.cs index 31f4efdcb..d2b0ee26e 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFileMarker.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFileMarker.cs @@ -16,10 +16,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// The marker /// The position within the stream public JpegFileMarker(byte marker, long position) + : this(marker, position, false) { - this.Marker = marker; - this.Position = position; - this.Invalid = false; } /// diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs index 99eaf7f43..8c525335b 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.IO; namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 07209bc28..3b34719a8 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -10,6 +10,7 @@ using System.Runtime.InteropServices; using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; +using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.MetaData; using SixLabors.ImageSharp.MetaData.Profiles.Exif; using SixLabors.ImageSharp.MetaData.Profiles.Icc; diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/DoubleBufferedStreamReader.cs b/src/ImageSharp/IO/DoubleBufferedStreamReader.cs similarity index 98% rename from src/ImageSharp/Formats/Jpeg/Components/Decoder/DoubleBufferedStreamReader.cs rename to src/ImageSharp/IO/DoubleBufferedStreamReader.cs index f4527966a..94a2f2cbf 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/DoubleBufferedStreamReader.cs +++ b/src/ImageSharp/IO/DoubleBufferedStreamReader.cs @@ -7,8 +7,7 @@ using System.Runtime.CompilerServices; using SixLabors.Memory; -// TODO: This could be useful elsewhere. -namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder +namespace SixLabors.ImageSharp.IO { /// /// A stream reader that add a secondary level buffer in addition to native stream buffered reading diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DoubleBufferedStreams.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DoubleBufferedStreams.cs index c70378464..d4cbe81e1 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DoubleBufferedStreams.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DoubleBufferedStreams.cs @@ -4,8 +4,7 @@ using System; using System.IO; using BenchmarkDotNet.Attributes; - -using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; +using SixLabors.ImageSharp.IO; namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg { diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegFileMarkerTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegFileMarkerTests.cs new file mode 100644 index 000000000..42eea2708 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegFileMarkerTests.cs @@ -0,0 +1,25 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Formats.Jpeg; +using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Formats.Jpg +{ + public class JpegFileMarkerTests + { + [Fact] + public void MarkerConstructorAssignsProperties() + { + const byte app1 = JpegConstants.Markers.APP1; + const int position = 5; + var marker = new JpegFileMarker(app1, position); + + Assert.Equal(app1, marker.Marker); + Assert.Equal(position, marker.Position); + Assert.False(marker.Invalid); + Assert.Equal(app1.ToString("X"), marker.ToString()); + } + } +} diff --git a/tests/ImageSharp.Tests/Formats/Jpg/DoubleBufferedStreamReaderTests.cs b/tests/ImageSharp.Tests/IO/DoubleBufferedStreamReaderTests.cs similarity index 87% rename from tests/ImageSharp.Tests/Formats/Jpg/DoubleBufferedStreamReaderTests.cs rename to tests/ImageSharp.Tests/IO/DoubleBufferedStreamReaderTests.cs index 5316ec758..4fac8d954 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/DoubleBufferedStreamReaderTests.cs +++ b/tests/ImageSharp.Tests/IO/DoubleBufferedStreamReaderTests.cs @@ -3,12 +3,11 @@ using System; using System.IO; - -using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; +using SixLabors.ImageSharp.IO; using SixLabors.Memory; using Xunit; -namespace SixLabors.ImageSharp.Tests.Formats.Jpg +namespace SixLabors.ImageSharp.Tests.IO { public class DoubleBufferedStreamReaderTests { @@ -30,6 +29,24 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg } } + [Fact] + public void DoubleBufferedStreamReaderCanReadSingleByteFromOffset() + { + using (MemoryStream stream = this.CreateTestStream()) + { + byte[] expected = stream.ToArray(); + const int offset = 5; + var reader = new DoubleBufferedStreamReader(this.allocator, stream); + reader.Position = offset; + + Assert.Equal(expected[offset], reader.ReadByte()); + + // We've read a whole chunk but increment by 1 in our reader. + Assert.Equal(stream.Position, DoubleBufferedStreamReader.ChunkLength + offset); + Assert.Equal(offset + 1, reader.Position); + } + } + [Fact] public void DoubleBufferedStreamReaderCanReadSubsequentSingleByteCorrectly() { @@ -139,7 +156,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg // Skip Again reader.Skip(skip2); - // First Skap + First Read + Second Skip + // First Skip + First Read + Second Skip int position = skip + plusOne + skip2; Assert.Equal(position, reader.Position); From 84d448501fcbff455ad6dcfeb81447f125e5bd9d Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 24 Jul 2018 22:15:37 +0200 Subject: [PATCH 13/13] further cleanup --- src/ImageSharp/Formats/Jpeg/README.md | 9 +++++++-- .../Formats/Jpg/JpegDecoderTests.Baseline.cs | 8 -------- .../Formats/Jpg/JpegDecoderTests.Progressive.cs | 8 -------- .../Formats/Jpg/JpegDecoderTests.cs | 17 ++--------------- 4 files changed, 9 insertions(+), 33 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/README.md b/src/ImageSharp/Formats/Jpeg/README.md index 54bc14847..2f766ca0c 100644 --- a/src/ImageSharp/Formats/Jpeg/README.md +++ b/src/ImageSharp/Formats/Jpeg/README.md @@ -1,3 +1,8 @@ -Encoder/Decoder adapted and extended from: +Encoder adapted and extended from: +https://golang.org/src/image/jpeg/ -https://golang.org/src/image/jpeg/ \ No newline at end of file +Decoder orchestration code is based on: +https://github.com/mozilla/pdf.js + +Huffmann decoder is based on: +https://github.com/rds1983/StbSharp \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs index 8169fdba3..73167a4b7 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Baseline.cs @@ -42,13 +42,5 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg // TODO: We need a public ImageDecoderException class in ImageSharp! Assert.ThrowsAny(() => provider.GetImage(JpegDecoder)); } - - [Theory(Skip = "Debug only, enable manually!")] - [WithFileCollection(nameof(BaselineTestJpegs), PixelTypes.Rgba32)] - public void CompareJpegDecoders_Baseline(TestImageProvider provider) - where TPixel : struct, IPixel - { - this.CompareJpegDecodersImpl(provider, DecodeBaselineJpegOutputName); - } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Progressive.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Progressive.cs index 9de788be2..77bc9f540 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Progressive.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Progressive.cs @@ -33,13 +33,5 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg appendPixelTypeToFileName: false); } } - - [Theory(Skip = "Debug only, enable manually!")] - [WithFileCollection(nameof(ProgressiveTestJpegs), PixelTypes.Rgba32)] - public void CompareJpegDecoders_Progressive(TestImageProvider provider) - where TPixel : struct, IPixel - { - this.CompareJpegDecodersImpl(provider, DecodeProgressiveJpegOutputName); - } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index 496891ce4..4ae955c32 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -63,7 +63,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg private static JpegDecoder JpegDecoder => new JpegDecoder(); [Fact] - public void ParseStream_BasicPropertiesAreCorrect1_PdfJs() + public void ParseStream_BasicPropertiesAreCorrect() { byte[] bytes = TestFile.Create(TestImages.Jpeg.Progressive.Progress).Bytes; using (var ms = new MemoryStream(bytes)) @@ -122,20 +122,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg return "0%"; } - - private void CompareJpegDecodersImpl(TestImageProvider provider, string testName) - where TPixel : struct, IPixel - { - this.Output.WriteLine(provider.SourceFileOrDescription); - provider.Utility.TestName = testName; - - using (Image image = provider.GetImage(JpegDecoder)) - { - string d = this.GetDifferenceInPercentageString(image, provider); - this.Output.WriteLine($"Difference using decoder: {d}"); - } - } - + // DEBUG ONLY! // The PDF.js output should be saved by "tests\ImageSharp.Tests\Formats\Jpg\pdfjs\jpeg-converter.htm" // into "\tests\Images\ActualOutput\JpegDecoderTests\"