Browse Source

Merge pull request #660 from SixLabors/af/memory-bridge

Replace IBuffer<T> with IMemoryOwner<T> + publish Image.WrapMemory()
af/merge-core
Anton Firsov 8 years ago
committed by GitHub
parent
commit
3e050e75f2
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      ImageSharp.sln.DotSettings
  2. 5
      build.ps1
  3. 3
      src/ImageSharp.Drawing/Primitives/ShapeRegion.cs
  4. 6
      src/ImageSharp.Drawing/Processing/BrushApplicator.cs
  5. 5
      src/ImageSharp.Drawing/Processing/ImageBrush{TPixel}.cs
  6. 5
      src/ImageSharp.Drawing/Processing/PatternBrush{TPixel}.cs
  7. 3
      src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs
  8. 4
      src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs
  9. 6
      src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs
  10. 5
      src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs
  11. 5
      src/ImageSharp.Drawing/Processing/RecolorBrush{TPixel}.cs
  12. 6
      src/ImageSharp.Drawing/Processing/SolidBrush{TPixel}.cs
  13. 2
      src/ImageSharp/Advanced/AdvancedImageExtensions.cs
  14. 9
      src/ImageSharp/Common/Helpers/ParallelFor.cs
  15. 7
      src/ImageSharp/Formats/Gif/LzwDecoder.cs
  16. 5
      src/ImageSharp/Formats/Gif/LzwEncoder.cs
  17. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs
  18. 3
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs
  19. 3
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs
  20. 105
      src/ImageSharp/Image.WrapMemory.cs
  21. 4
      src/ImageSharp/ImageFrameCollection.cs
  22. 11
      src/ImageSharp/ImageFrame{TPixel}.cs
  23. 15
      src/ImageSharp/Image{TPixel}.cs
  24. 1
      src/ImageSharp/Memory/ArrayPoolMemoryAllocator.Buffer{T}.cs
  25. 2
      src/ImageSharp/Memory/ArrayPoolMemoryAllocator.cs
  26. 2
      src/ImageSharp/Memory/BasicArrayBuffer.cs
  27. 4
      src/ImageSharp/Memory/Buffer2DExtensions.cs
  28. 48
      src/ImageSharp/Memory/Buffer2D{T}.cs
  29. 20
      src/ImageSharp/Memory/BufferExtensions.cs
  30. 34
      src/ImageSharp/Memory/ConsumedBuffer.cs
  31. 38
      src/ImageSharp/Memory/IBuffer{T}.cs
  32. 4
      src/ImageSharp/Memory/IManagedByteBuffer.cs
  33. 4
      src/ImageSharp/Memory/ManagedBufferBase.cs
  34. 6
      src/ImageSharp/Memory/MemoryAllocator.cs
  35. 20
      src/ImageSharp/Memory/MemoryAllocatorExtensions.cs
  36. 101
      src/ImageSharp/Memory/MemorySource.cs
  37. 6
      src/ImageSharp/Memory/SimpleGcMemoryAllocator.cs
  38. 1
      src/ImageSharp/PixelFormats/Bgra32.cs
  39. 52
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs
  40. 12
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt
  41. 5
      src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs
  42. 5
      src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor.cs
  43. 5
      src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs
  44. 5
      src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs
  45. 4
      src/ImageSharp/Processing/Processors/Quantization/QuantizedFrame{TPixel}.cs
  46. 45
      src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs
  47. 5
      src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs
  48. 7
      src/ImageSharp/Processing/Processors/Transforms/WeightsWindow.cs
  49. 23
      tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs
  50. 18
      tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs
  51. 20
      tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs
  52. 19
      tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs
  53. 18
      tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs
  54. 8
      tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs
  55. 4
      tests/ImageSharp.Benchmarks/Samplers/Glow.cs
  56. 3
      tests/ImageSharp.Tests/Advanced/AdvancedImageExtensionsTests.cs
  57. 17
      tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs
  58. 1
      tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs
  59. 3
      tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs
  60. 2
      tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs
  61. 142
      tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs
  62. 16
      tests/ImageSharp.Tests/Memory/ArrayPoolMemoryManagerTests.cs
  63. 151
      tests/ImageSharp.Tests/Memory/Buffer2DTests.cs
  64. 27
      tests/ImageSharp.Tests/Memory/BufferTestSuite.cs
  65. 163
      tests/ImageSharp.Tests/Memory/MemorySourceTests.cs
  66. 9
      tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs
  67. 3
      tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs
  68. 7
      tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs
  69. 56
      tests/ImageSharp.Tests/TestUtilities/TestMemoryAllocator.cs
  70. 22
      tests/ImageSharp.Tests/TestUtilities/TestMemoryManager.cs
  71. 6
      tests/ImageSharp.Tests/TestUtilities/TestUtils.cs

5
ImageSharp.sln.DotSettings

@ -343,8 +343,11 @@
&lt;Entry DisplayName="All other members" /&gt;&#xD;
&lt;/TypePattern&gt;&#xD;
&lt;/Patterns&gt;</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CSharpUsing/AddImportsToDeepestScope/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CSharpUsing/AddImportsToDeepestScope/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CSharpUsing/QualifiedUsingAtNestedScope/@EntryValue">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/FileHeader/FileHeaderText/@EntryValue">// Copyright (c) Six Labors and contributors.&#xD;
// Licensed under the Apache License, Version 2.0.&#xD;
</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AC/@EntryIndexedValue">AC</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=DC/@EntryIndexedValue">DC</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=DCT/@EntryIndexedValue">DCT</s:String>

5
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

3
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<PointF> tempBuffer = configuration.MemoryAllocator.Allocate<PointF>(buffer.Length))
using (IMemoryOwner<PointF> tempBuffer = configuration.MemoryAllocator.Allocate<PointF>(buffer.Length))
{
Span<PointF> innerBuffer = tempBuffer.GetSpan();
int count = this.Shape.FindIntersections(start, end, innerBuffer);

6
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<float> amountBuffer = memoryAllocator.Allocate<float>(scanline.Length))
using (IBuffer<TPixel> overlay = memoryAllocator.Allocate<TPixel>(scanline.Length))
using (IMemoryOwner<float> amountBuffer = memoryAllocator.Allocate<float>(scanline.Length))
using (IMemoryOwner<TPixel> overlay = memoryAllocator.Allocate<TPixel>(scanline.Length))
{
Span<float> amountSpan = amountBuffer.GetSpan();
Span<TPixel> overlaySpan = overlay.GetSpan();

5
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<float> scanline, int x, int y)
{
// Create a span for colors
using (IBuffer<float> amountBuffer = this.Target.MemoryAllocator.Allocate<float>(scanline.Length))
using (IBuffer<TPixel> overlay = this.Target.MemoryAllocator.Allocate<TPixel>(scanline.Length))
using (IMemoryOwner<float> amountBuffer = this.Target.MemoryAllocator.Allocate<float>(scanline.Length))
using (IMemoryOwner<TPixel> overlay = this.Target.MemoryAllocator.Allocate<TPixel>(scanline.Length))
{
Span<float> amountSpan = amountBuffer.GetSpan();
Span<TPixel> overlaySpan = overlay.GetSpan();

5
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<float> amountBuffer = memoryAllocator.Allocate<float>(scanline.Length))
using (IBuffer<TPixel> overlay = memoryAllocator.Allocate<TPixel>(scanline.Length))
using (IMemoryOwner<float> amountBuffer = memoryAllocator.Allocate<float>(scanline.Length))
using (IMemoryOwner<TPixel> overlay = memoryAllocator.Allocate<TPixel>(scanline.Length))
{
Span<float> amountSpan = amountBuffer.GetSpan();
Span<TPixel> overlaySpan = overlay.GetSpan();

3
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<float> amount = memoryAllocator.Allocate<float>(width))
using (IMemoryOwner<float> amount = memoryAllocator.Allocate<float>(width))
{
amount.GetSpan().Fill(this.Opacity);

4
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<float> amount = source.MemoryAllocator.Allocate<float>(width))
using (IMemoryOwner<float> amount = source.MemoryAllocator.Allocate<float>(width))
using (BrushApplicator<TPixel> applicator = this.brush.CreateApplicator(
source,
sourceRectangle,

6
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<TPixel> applicator = this.Brush.CreateApplicator(source, rect, this.Options))
{
int scanlineWidth = maxX - minX;
using (IBuffer<float> bBuffer = source.MemoryAllocator.Allocate<float>(maxIntersections))
using (IBuffer<float> bScanline = source.MemoryAllocator.Allocate<float>(scanlineWidth))
using (IMemoryOwner<float> bBuffer = source.MemoryAllocator.Allocate<float>(maxIntersections))
using (IMemoryOwner<float> bScanline = source.MemoryAllocator.Allocate<float>(scanlineWidth))
{
bool scanlineDirty = true;
float subpixelFraction = 1f / subpixelCount;

5
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<float> fullBuffer = this.MemoryAllocator.Allocate2D<float>(size.Width + 1, size.Height + 1, AllocationOptions.Clean);
using (IBuffer<float> bufferBacking = this.MemoryAllocator.Allocate<float>(path.MaxIntersections))
using (IBuffer<PointF> rowIntersectionBuffer = this.MemoryAllocator.Allocate<PointF>(size.Width))
using (IMemoryOwner<float> bufferBacking = this.MemoryAllocator.Allocate<float>(path.MaxIntersections))
using (IMemoryOwner<PointF> rowIntersectionBuffer = this.MemoryAllocator.Allocate<PointF>(size.Width))
{
float subpixelFraction = 1f / subpixelCount;
float subpixelFractionPoint = subpixelFraction / subpixelCount;

5
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<float> amountBuffer = memoryAllocator.Allocate<float>(scanline.Length))
using (IBuffer<TPixel> overlay = memoryAllocator.Allocate<TPixel>(scanline.Length))
using (IMemoryOwner<float> amountBuffer = memoryAllocator.Allocate<float>(scanline.Length))
using (IMemoryOwner<TPixel> overlay = memoryAllocator.Allocate<TPixel>(scanline.Length))
{
Span<float> amountSpan = amountBuffer.GetSpan();
Span<TPixel> overlaySpan = overlay.GetSpan();

6
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
/// <summary>
/// Gets the colors.
/// </summary>
protected IBuffer<TPixel> Colors { get; }
protected IMemoryOwner<TPixel> Colors { get; }
/// <summary>
/// Gets the color for a single pixel.
@ -96,7 +98,7 @@ namespace SixLabors.ImageSharp.Processing
}
else
{
using (IBuffer<float> amountBuffer = memoryAllocator.Allocate<float>(scanline.Length))
using (IMemoryOwner<float> amountBuffer = memoryAllocator.Allocate<float>(scanline.Length))
{
Span<float> amountSpan = amountBuffer.GetSpan();

2
src/ImageSharp/Advanced/AdvancedImageExtensions.cs

@ -105,7 +105,7 @@ namespace SixLabors.ImageSharp.Advanced
internal static Memory<TPixel> GetPixelMemory<TPixel>(this ImageFrame<TPixel> source)
where TPixel : struct, IPixel<TPixel>
{
return source.PixelBuffer.Buffer.Memory;
return source.PixelBuffer.MemorySource.Memory;
}
/// <summary>

9
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<int, IBuffer<T>> body)
Action<int, IMemoryOwner<T>> body)
where T : struct
{
MemoryAllocator memoryAllocator = configuration.MemoryAllocator;
ParallelOptions parallelOptions = configuration.ParallelOptions;
IBuffer<T> InitBuffer()
IMemoryOwner<T> InitBuffer()
{
return memoryAllocator.Allocate<T>(bufferLength);
}
void CleanUpBuffer(IBuffer<T> buffer)
void CleanUpBuffer(IMemoryOwner<T> buffer)
{
buffer.Dispose();
}
IBuffer<T> BodyFunc(int i, ParallelLoopState state, IBuffer<T> buffer)
IMemoryOwner<T> BodyFunc(int i, ParallelLoopState state, IMemoryOwner<T> buffer)
{
body(i, buffer);
return buffer;

7
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
/// <summary>
/// The prefix buffer.
/// </summary>
private readonly IBuffer<int> prefix;
private readonly IMemoryOwner<int> prefix;
/// <summary>
/// The suffix buffer.
/// </summary>
private readonly IBuffer<int> suffix;
private readonly IMemoryOwner<int> suffix;
/// <summary>
/// The pixel stack buffer.
/// </summary>
private readonly IBuffer<int> pixelStack;
private readonly IMemoryOwner<int> pixelStack;
/// <summary>
/// Initializes a new instance of the <see cref="LzwDecoder"/> class

5
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
/// <summary>
/// The hash table.
/// </summary>
private readonly IBuffer<int> hashTable;
private readonly IMemoryOwner<int> hashTable;
/// <summary>
/// The code table.
/// </summary>
private readonly IBuffer<int> codeTable;
private readonly IMemoryOwner<int> codeTable;
/// <summary>
/// Define the storage for the packet accumulator.

2
src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs

@ -9,7 +9,7 @@ using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
/// <summary>
/// Encapsulates the implementation of processing "raw" <see cref="IBuffer{T}"/>-s into Jpeg image channels.
/// Encapsulates the implementation of processing "raw" jpeg buffers into Jpeg image channels.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal struct JpegBlockPostProcessor

3
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
/// <summary>
/// Temporal buffer to store a row of colors.
/// </summary>
private readonly IBuffer<Vector4> rgbaBuffer;
private readonly IMemoryOwner<Vector4> rgbaBuffer;
/// <summary>
/// The <see cref="JpegColorConverter"/> corresponding to the current <see cref="JpegColorSpace"/> determined by <see cref="IRawJpegData.ColorSpace"/>.

3
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<byte> count, ReadOnlySpan<byte> values)
{
const int Length = 257;
using (IBuffer<short> huffcode = memoryAllocator.Allocate<short>(Length))
using (IMemoryOwner<short> huffcode = memoryAllocator.Allocate<short>(Length))
{
ref short huffcodeRef = ref MemoryMarshal.GetReference(huffcode.GetSpan());

105
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;
@ -13,8 +15,6 @@ namespace SixLabors.ImageSharp
/// </content>
public static partial class Image
{
// TODO: This is a WIP API, should be public when finished.
/// <summary>
/// Wraps an existing contigous memory area of 'width'x'height' pixels,
/// allowing to view/manipulate it as an ImageSharp <see cref="Image{TPixel}"/> instance.
@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp
/// <param name="height">The height of the memory image</param>
/// <param name="metaData">The <see cref="ImageMetaData"/></param>
/// <returns>An <see cref="Image{TPixel}"/> instance</returns>
internal static Image<TPixel> WrapMemory<TPixel>(
public static Image<TPixel> WrapMemory<TPixel>(
Configuration config,
Memory<TPixel> pixelMemory,
int width,
@ -34,26 +34,117 @@ namespace SixLabors.ImageSharp
ImageMetaData metaData)
where TPixel : struct, IPixel<TPixel>
{
var buffer = new ConsumedBuffer<TPixel>(pixelMemory);
return new Image<TPixel>(config, buffer, width, height, metaData);
var memorySource = new MemorySource<TPixel>(pixelMemory);
return new Image<TPixel>(config, memorySource, width, height, metaData);
}
/// <summary>
/// Wraps an existing contigous memory area of 'width'x'height' pixels,
/// allowing to view/manipulate it as an ImageSharp <see cref="Image{TPixel}"/> instance.
/// </summary>
/// <typeparam name="TPixel">The pixel type</typeparam>
/// <param name="config">The <see cref="Configuration"/></param>
/// <param name="pixelMemory">The pixel memory</param>
/// <param name="width">The width of the memory image</param>
/// <param name="height">The height of the memory image</param>
/// <returns>An <see cref="Image{TPixel}"/> instance</returns>
public static Image<TPixel> WrapMemory<TPixel>(
Configuration config,
Memory<TPixel> pixelMemory,
int width,
int height)
where TPixel : struct, IPixel<TPixel>
{
return WrapMemory(config, pixelMemory, width, height, new ImageMetaData());
}
/// <summary>
/// Wraps an existing contigous memory area of 'width'x'height' pixels,
/// allowing to view/manipulate it as an ImageSharp <see cref="Image{TPixel}"/> instance.
/// The memory is being observed, the caller remains responsible for managing it's lifecycle.
/// </summary>
/// <typeparam name="TPixel">The pixel type</typeparam>
/// <param name="pixelMemory">The pixel memory</param>
/// <param name="width">The width of the memory image</param>
/// <param name="height">The height of the memory image</param>
/// <returns>An <see cref="Image{TPixel}"/> instance</returns>
internal static Image<TPixel> WrapMemory<TPixel>(
public static Image<TPixel> WrapMemory<TPixel>(
Memory<TPixel> pixelMemory,
int width,
int height)
where TPixel : struct, IPixel<TPixel>
{
return WrapMemory(Configuration.Default, pixelMemory, width, height, new ImageMetaData());
return WrapMemory(Configuration.Default, pixelMemory, width, height);
}
/// <summary>
/// Wraps an existing contigous memory area of 'width'x'height' pixels,
/// allowing to view/manipulate it as an ImageSharp <see cref="Image{TPixel}"/> instance.
/// The ownership of the <paramref name="pixelMemoryOwner"/> is being transfered to the new <see cref="Image{TPixel}"/> instance,
/// meaning that the caller is not allowed to dispose <paramref name="pixelMemoryOwner"/>.
/// It will be disposed together with the result image.
/// </summary>
/// <typeparam name="TPixel">The pixel type</typeparam>
/// <param name="config">The <see cref="Configuration"/></param>
/// <param name="pixelMemoryOwner">The <see cref="IMemoryOwner{T}"/> that is being transfered to the image</param>
/// <param name="width">The width of the memory image</param>
/// <param name="height">The height of the memory image</param>
/// <param name="metaData">The <see cref="ImageMetaData"/></param>
/// <returns>An <see cref="Image{TPixel}"/> instance</returns>
public static Image<TPixel> WrapMemory<TPixel>(
Configuration config,
IMemoryOwner<TPixel> pixelMemoryOwner,
int width,
int height,
ImageMetaData metaData)
where TPixel : struct, IPixel<TPixel>
{
var memorySource = new MemorySource<TPixel>(pixelMemoryOwner, false);
return new Image<TPixel>(config, memorySource, width, height, metaData);
}
/// <summary>
/// Wraps an existing contigous memory area of 'width'x'height' pixels,
/// allowing to view/manipulate it as an ImageSharp <see cref="Image{TPixel}"/> instance.
/// The ownership of the <paramref name="pixelMemoryOwner"/> is being transfered to the new <see cref="Image{TPixel}"/> instance,
/// meaning that the caller is not allowed to dispose <paramref name="pixelMemoryOwner"/>.
/// It will be disposed together with the result image.
/// </summary>
/// <typeparam name="TPixel">The pixel type</typeparam>
/// <param name="config">The <see cref="Configuration"/></param>
/// <param name="pixelMemoryOwner">The <see cref="IMemoryOwner{T}"/> that is being transfered to the image</param>
/// <param name="width">The width of the memory image</param>
/// <param name="height">The height of the memory image</param>
/// <returns>An <see cref="Image{TPixel}"/> instance</returns>
public static Image<TPixel> WrapMemory<TPixel>(
Configuration config,
IMemoryOwner<TPixel> pixelMemoryOwner,
int width,
int height)
where TPixel : struct, IPixel<TPixel>
{
return WrapMemory(config, pixelMemoryOwner, width, height, new ImageMetaData());
}
/// <summary>
/// Wraps an existing contigous memory area of 'width'x'height' pixels,
/// allowing to view/manipulate it as an ImageSharp <see cref="Image{TPixel}"/> instance.
/// The ownership of the <paramref name="pixelMemoryOwner"/> is being transfered to the new <see cref="Image{TPixel}"/> instance,
/// meaning that the caller is not allowed to dispose <paramref name="pixelMemoryOwner"/>.
/// It will be disposed together with the result image.
/// </summary>
/// <typeparam name="TPixel">The pixel type</typeparam>
/// <param name="pixelMemoryOwner">The <see cref="IMemoryOwner{T}"/> that is being transfered to the image</param>
/// <param name="width">The width of the memory image</param>
/// <param name="height">The height of the memory image</param>
/// <returns>An <see cref="Image{TPixel}"/> instance</returns>
public static Image<TPixel> WrapMemory<TPixel>(
IMemoryOwner<TPixel> pixelMemoryOwner,
int width,
int height)
where TPixel : struct, IPixel<TPixel>
{
return WrapMemory(Configuration.Default, pixelMemoryOwner, width, height);
}
}
}

4
src/ImageSharp/ImageFrameCollection.cs

@ -30,14 +30,14 @@ namespace SixLabors.ImageSharp
this.frames.Add(new ImageFrame<TPixel>(parent.GetConfiguration(), width, height, backgroundColor));
}
internal ImageFrameCollection(Image<TPixel> parent, int width, int height, IBuffer<TPixel> consumedBuffer)
internal ImageFrameCollection(Image<TPixel> parent, int width, int height, MemorySource<TPixel> memorySource)
{
Guard.NotNull(parent, nameof(parent));
this.parent = parent;
// Frames are already cloned within the caller
this.frames.Add(new ImageFrame<TPixel>(parent.GetConfiguration(), width, height, consumedBuffer));
this.frames.Add(new ImageFrame<TPixel>(parent.GetConfiguration(), width, height, memorySource));
}
internal ImageFrameCollection(Image<TPixel> parent, IEnumerable<ImageFrame<TPixel>> frames)

11
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,8 +95,8 @@ namespace SixLabors.ImageSharp
/// <summary>
/// Initializes a new instance of the <see cref="ImageFrame{TPixel}" /> class wrapping an existing buffer.
/// </summary>
internal ImageFrame(Configuration configuration, int width, int height, IBuffer<TPixel> consumedBuffer)
: this(configuration, width, height, consumedBuffer, new ImageFrameMetaData())
internal ImageFrame(Configuration configuration, int width, int height, MemorySource<TPixel> memorySource)
: this(configuration, width, height, memorySource, new ImageFrameMetaData())
{
}
@ -106,7 +107,7 @@ namespace SixLabors.ImageSharp
Configuration configuration,
int width,
int height,
IBuffer<TPixel> consumedBuffer,
MemorySource<TPixel> memorySource,
ImageFrameMetaData metaData)
{
Guard.NotNull(configuration, nameof(configuration));
@ -116,7 +117,7 @@ namespace SixLabors.ImageSharp
this.configuration = configuration;
this.MemoryAllocator = configuration.MemoryAllocator;
this.PixelBuffer = new Buffer2D<TPixel>(consumedBuffer, width, height);
this.PixelBuffer = new Buffer2D<TPixel>(memorySource, width, height);
this.MetaData = metaData;
}
@ -272,7 +273,7 @@ namespace SixLabors.ImageSharp
this.Height,
this.configuration,
this.Width,
(int y, IBuffer<Vector4> tempRowBuffer) =>
(int y, IMemoryOwner<Vector4> tempRowBuffer) =>
{
Span<TPixel> sourceRow = this.GetPixelRowSpan(y);
Span<TPixel2> targetRow = target.GetPixelRowSpan(y);

15
src/ImageSharp/Image{TPixel}.cs

@ -84,23 +84,14 @@ namespace SixLabors.ImageSharp
/// <summary>
/// Initializes a new instance of the <see cref="Image{TPixel}"/> class
/// consuming an external buffer instance.
/// wrapping an external <see cref="MemorySource{T}"/>
/// </summary>
internal Image(Configuration configuration, IBuffer<TPixel> consumedBuffer, int width, int height)
: this(configuration, consumedBuffer, width, height, new ImageMetaData())
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image{TPixel}"/> class
/// consuming an external buffer instance.
/// </summary>
internal Image(Configuration configuration, IBuffer<TPixel> consumedBuffer, int width, int height, ImageMetaData metadata)
internal Image(Configuration configuration, MemorySource<TPixel> memorySource, int width, int height, ImageMetaData metadata)
{
this.configuration = configuration;
this.PixelType = new PixelTypeInfo(Unsafe.SizeOf<TPixel>() * 8);
this.MetaData = metadata;
this.frames = new ImageFrameCollection<TPixel>(this, width, height, consumedBuffer);
this.frames = new ImageFrameCollection<TPixel>(this, width, height, memorySource);
}
/// <summary>

1
src/ImageSharp/Memory/ArrayPoolMemoryAllocator.Buffer{T}.cs

@ -14,7 +14,6 @@ namespace SixLabors.Memory
{
/// <summary>
/// The buffer implementation of <see cref="ArrayPoolMemoryAllocator"/>.
/// In this implementation <see cref="IBuffer{T}.Memory"/> is owned.
/// </summary>
private class Buffer<T> : ManagedBufferBase<T>
where T : struct

2
src/ImageSharp/Memory/ArrayPoolMemoryAllocator.cs

@ -89,7 +89,7 @@ namespace SixLabors.Memory
}
/// <inheritdoc />
internal override IBuffer<T> Allocate<T>(int length, AllocationOptions options)
internal override IMemoryOwner<T> Allocate<T>(int length, AllocationOptions options = AllocationOptions.None)
{
int itemSizeBytes = Unsafe.SizeOf<T>();
int bufferSizeInBytes = length * itemSizeBytes;

2
src/ImageSharp/Memory/BasicArrayBuffer.cs

@ -7,7 +7,7 @@ using System.Runtime.CompilerServices;
namespace SixLabors.Memory
{
/// <summary>
/// Wraps an array as an <see cref="IBuffer{T}"/> instance. In this implementation <see cref="IBuffer{T}.Memory"/> is owned.
/// Wraps an array as an <see cref="IManagedByteBuffer"/> instance.
/// </summary>
internal class BasicArrayBuffer<T> : ManagedBufferBase<T>
where T : struct

4
src/ImageSharp/Memory/Buffer2DExtensions.cs

@ -18,7 +18,7 @@ namespace SixLabors.Memory
internal static Span<T> GetSpan<T>(this Buffer2D<T> buffer)
where T : struct
{
return buffer.Buffer.GetSpan();
return buffer.MemorySource.GetSpan();
}
/// <summary>
@ -61,7 +61,7 @@ namespace SixLabors.Memory
public static Memory<T> GetRowMemory<T>(this Buffer2D<T> 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);
}
/// <summary>

48
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,15 +16,17 @@ namespace SixLabors.Memory
internal sealed class Buffer2D<T> : IDisposable
where T : struct
{
private MemorySource<T> memorySource;
/// <summary>
/// Initializes a new instance of the <see cref="Buffer2D{T}"/> class.
/// </summary>
/// <param name="wrappedBuffer">The buffer to wrap</param>
/// <param name="memorySource">The buffer to wrap</param>
/// <param name="width">The number of elements in a row</param>
/// <param name="height">The number of rows</param>
public Buffer2D(IBuffer<T> wrappedBuffer, int width, int height)
public Buffer2D(MemorySource<T> memorySource, int width, int height)
{
this.Buffer = wrappedBuffer;
this.memorySource = memorySource;
this.Width = width;
this.Height = height;
}
@ -39,13 +42,13 @@ namespace SixLabors.Memory
public int Height { get; private set; }
/// <summary>
/// Gets the backing <see cref="IBuffer{T}"/>
/// Gets the backing <see cref="MemorySource{T}"/>
/// </summary>
public IBuffer<T> Buffer { get; private set; }
public MemorySource<T> MemorySource => this.memorySource;
public Memory<T> Memory => this.Buffer.Memory;
public Memory<T> Memory => this.MemorySource.Memory;
public Span<T> Span => this.Buffer.GetSpan();
public Span<T> Span => this.Memory.Span;
/// <summary>
/// Gets a reference to the element at the specified position.
@ -60,7 +63,7 @@ namespace SixLabors.Memory
{
ImageSharp.DebugGuard.MustBeLessThan(x, this.Width, nameof(x));
DebugGuard.MustBeLessThan(y, this.Height, nameof(y));
Span<T> span = this.Buffer.GetSpan();
Span<T> span = this.Span;
return ref span[(this.Width * y) + x];
}
}
@ -70,7 +73,7 @@ namespace SixLabors.Memory
/// </summary>
public void Dispose()
{
this.Buffer?.Dispose();
this.MemorySource.Dispose();
}
/// <summary>
@ -79,36 +82,15 @@ namespace SixLabors.Memory
/// </summary>
public static void SwapOrCopyContent(Buffer2D<T> destination, Buffer2D<T> 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);
}
MemorySource<T>.SwapOrCopyContent(ref destination.memorySource, ref source.memorySource);
SwapDimensionData(destination, source);
}
/// <summary>
/// Swap the contents (<see cref="Buffer"/>, <see cref="Width"/>, <see cref="Height"/>) of the two buffers.
/// Useful to transfer the contents of a temporary <see cref="Buffer2D{T}"/> to a persistent <see cref="SixLabors.ImageSharp.ImageFrame{T}.PixelBuffer"/>
/// </summary>
/// <param name="a">The first buffer</param>
/// <param name="b">The second buffer</param>
private static void SwapContents(Buffer2D<T> a, Buffer2D<T> b)
private static void SwapDimensionData(Buffer2D<T> a, Buffer2D<T> b)
{
Size aSize = a.Size();
Size bSize = b.Size();
IBuffer<T> temp = a.Buffer;
a.Buffer = b.Buffer;
b.Buffer = temp;
b.Width = aSize.Width;
b.Height = aSize.Height;

20
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<T>(this IBuffer<T> buffer)
where T : struct => buffer.GetSpan().Length;
public static Span<T> GetSpan<T>(this IMemoryOwner<T> buffer)
=> buffer.Memory.Span;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Length<T>(this IMemoryOwner<T> buffer)
=> buffer.GetSpan().Length;
/// <summary>
/// Gets a <see cref="Span{T}"/> to an offseted position inside the buffer.
@ -21,8 +26,7 @@ namespace SixLabors.Memory
/// <param name="start">The start</param>
/// <returns>The <see cref="Span{T}"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span<T> Slice<T>(this IBuffer<T> buffer, int start)
where T : struct
public static Span<T> Slice<T>(this IMemoryOwner<T> buffer, int start)
{
return buffer.GetSpan().Slice(start);
}
@ -35,8 +39,7 @@ namespace SixLabors.Memory
/// <param name="length">The length of the slice</param>
/// <returns>The <see cref="Span{T}"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span<T> Slice<T>(this IBuffer<T> buffer, int start, int length)
where T : struct
public static Span<T> Slice<T>(this IMemoryOwner<T> buffer, int start, int length)
{
return buffer.GetSpan().Slice(start, length);
}
@ -46,13 +49,12 @@ namespace SixLabors.Memory
/// </summary>
/// <param name="buffer">The buffer</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Clear<T>(this IBuffer<T> buffer)
where T : struct
public static void Clear<T>(this IMemoryOwner<T> buffer)
{
buffer.GetSpan().Clear();
}
public static ref T GetReference<T>(this IBuffer<T> buffer)
public static ref T GetReference<T>(this IMemoryOwner<T> buffer)
where T : struct =>
ref MemoryMarshal.GetReference(buffer.GetSpan());

34
src/ImageSharp/Memory/ConsumedBuffer.cs

@ -1,34 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
namespace SixLabors.Memory
{
/// <summary>
/// A buffer implementation that consumes an existing <see cref="Memory{T}"/> instance.
/// The ownership of the memory remains external.
/// </summary>
/// <typeparam name="T">The value type</typeparam>
internal sealed class ConsumedBuffer<T> : IBuffer<T>
where T : struct
{
public ConsumedBuffer(Memory<T> memory)
{
this.Memory = memory;
}
public Memory<T> Memory { get; }
public bool IsMemoryOwner => false;
public Span<T> GetSpan()
{
return this.Memory.Span;
}
public void Dispose()
{
}
}
}

38
src/ImageSharp/Memory/IBuffer{T}.cs

@ -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
{
/// <summary>
/// Represents a contigous memory buffer of value-type items.
/// Depending on it's implementation, an <see cref="IBuffer{T}"/> can (1) OWN or (2) CONSUME the <see cref="Memory{T}"/> instance it wraps.
/// For a deeper understanding of the owner/consumer model, read the following docs: <br/>
/// https://gist.github.com/GrabYourPitchforks/4c3e1935fd4d9fa2831dbfcab35dffc6
/// TODO: We need more SOC here! For owned buffers we should use <see cref="IMemoryOwner{T}"/>.
/// For the consumption case we should not use buffers at all. We need to refactor Buffer2D{T} for this.
/// </summary>
/// <typeparam name="T">The value type</typeparam>
internal interface IBuffer<T> : IDisposable
where T : struct
{
/// <summary>
/// Gets the <see cref="Memory{T}"/> ownerd/consumed by this buffer.
/// </summary>
Memory<T> Memory { get; }
/// <summary>
/// Gets a value indicating whether this instance is owning the <see cref="Memory"/>.
/// </summary>
bool IsMemoryOwner { get; }
/// <summary>
/// Gets the span to the memory "promised" by this buffer when it's OWNED (1).
/// Gets `this.Memory.Span` when the buffer CONSUMED (2).
/// </summary>
/// <returns>The <see cref="Span{T}"/></returns>
Span<T> GetSpan();
}
}

4
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
{
/// <summary>
/// Represents a byte buffer backed by a managed array. Useful for interop with classic .NET API-s.
/// </summary>
internal interface IManagedByteBuffer : IBuffer<byte>
internal interface IManagedByteBuffer : IMemoryOwner<byte>
{
/// <summary>
/// Gets the managed array backing this buffer instance.

4
src/ImageSharp/Memory/ManagedBufferBase.cs

@ -7,9 +7,9 @@ using System.Runtime.InteropServices;
namespace SixLabors.Memory
{
/// <summary>
/// Provides a base class for <see cref="IBuffer{T}"/> implementations by implementing pinning logic for <see cref="MemoryManager{T}"/> adaption.
/// Provides a base class for <see cref="IMemoryOwner{T}"/> implementations by implementing pinning logic for <see cref="MemoryManager{T}"/> adaption.
/// </summary>
internal abstract class ManagedBufferBase<T> : MemoryManager<T>, IBuffer<T>
internal abstract class ManagedBufferBase<T> : MemoryManager<T>
where T : struct
{
private GCHandle pinHandle;

6
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
{
/// <summary>
@ -9,13 +11,13 @@ namespace SixLabors.Memory
public abstract class MemoryAllocator
{
/// <summary>
/// Allocates an <see cref="IBuffer{T}"/> of size <paramref name="length"/>.
/// Allocates an <see cref="IMemoryOwner{T}" />, holding a <see cref="System.Memory{T}"/> of length <paramref name="length"/>.
/// </summary>
/// <typeparam name="T">Type of the data stored in the buffer</typeparam>
/// <param name="length">Size of the buffer to allocate</param>
/// <param name="options">The allocation options.</param>
/// <returns>A buffer of values of type <typeparamref name="T"/>.</returns>
internal abstract IBuffer<T> Allocate<T>(int length, AllocationOptions options = AllocationOptions.None)
internal abstract IMemoryOwner<T> Allocate<T>(int length, AllocationOptions options = AllocationOptions.None)
where T : struct;
/// <summary>

20
src/ImageSharp/Memory/MemoryAllocatorExtensions.cs

@ -1,4 +1,6 @@
using SixLabors.Primitives;
using System.Buffers;
using SixLabors.Primitives;
namespace SixLabors.Memory
{
@ -7,15 +9,23 @@ namespace SixLabors.Memory
/// </summary>
internal static class MemoryAllocatorExtensions
{
public static Buffer2D<T> Allocate2D<T>(this MemoryAllocator memoryAllocator, int width, int height, AllocationOptions options = AllocationOptions.None)
public static Buffer2D<T> Allocate2D<T>(
this MemoryAllocator memoryAllocator,
int width,
int height,
AllocationOptions options = AllocationOptions.None)
where T : struct
{
IBuffer<T> buffer = memoryAllocator.Allocate<T>(width * height, options);
IMemoryOwner<T> buffer = memoryAllocator.Allocate<T>(width * height, options);
var memorySource = new MemorySource<T>(buffer, true);
return new Buffer2D<T>(buffer, width, height);
return new Buffer2D<T>(memorySource, width, height);
}
public static Buffer2D<T> Allocate2D<T>(this MemoryAllocator memoryAllocator, Size size, AllocationOptions options = AllocationOptions.None)
public static Buffer2D<T> Allocate2D<T>(
this MemoryAllocator memoryAllocator,
Size size,
AllocationOptions options = AllocationOptions.None)
where T : struct =>
Allocate2D<T>(memoryAllocator, size.Width, size.Height, options);

101
src/ImageSharp/Memory/MemorySource.cs

@ -0,0 +1,101 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers;
namespace SixLabors.Memory
{
/// <summary>
/// Holds a <see cref="System.Memory{T}"/> that is either OWNED or CONSUMED.
/// When the memory is being owned, the <see cref="IMemoryOwner{T}"/> instance is also known.
/// Implements content transfer logic in <see cref="SwapOrCopyContent"/> that depends on the ownership status.
/// This is needed to transfer the contents of a temporary <see cref="Buffer2D{T}"/>
/// to a persistent <see cref="SixLabors.ImageSharp.ImageFrame{T}.PixelBuffer"/> without copying the buffer.
/// </summary>
/// <remarks>
/// For a deeper understanding of the owner/consumer model, check out the following docs: <br/>
/// https://gist.github.com/GrabYourPitchforks/4c3e1935fd4d9fa2831dbfcab35dffc6
/// https://www.codemag.com/Article/1807051/Introducing-.NET-Core-2.1-Flagship-Types-Span-T-and-Memory-T
/// </remarks>
internal struct MemorySource<T> : IDisposable
{
/// <summary>
/// Initializes a new instance of the <see cref="MemorySource{T}"/> struct
/// by wrapping an existing <see cref="IMemoryOwner{T}"/>.
/// </summary>
/// <param name="memoryOwner">The <see cref="IMemoryOwner{T}"/> to wrap</param>
/// <param name="isInternalMemorySource">
/// A value indicating whether <paramref name="memoryOwner"/> is an internal memory source managed by ImageSharp.
/// Eg. allocated by a <see cref="MemoryAllocator"/>.
/// </param>
public MemorySource(IMemoryOwner<T> memoryOwner, bool isInternalMemorySource)
{
this.MemoryOwner = memoryOwner;
this.Memory = memoryOwner.Memory;
this.HasSwappableContents = isInternalMemorySource;
}
public MemorySource(Memory<T> memory)
{
this.Memory = memory;
this.MemoryOwner = null;
this.HasSwappableContents = false;
}
public IMemoryOwner<T> MemoryOwner { get; private set; }
public Memory<T> Memory { get; private set; }
/// <summary>
/// Gets a value indicating whether we are allowed to swap the contents of this buffer
/// with an other <see cref="MemorySource{T}"/> instance.
/// The value is true only and only if <see cref="MemoryOwner"/> is present,
/// and it's coming from an internal source managed by ImageSharp (<see cref="MemoryAllocator"/>).
/// </summary>
public bool HasSwappableContents { get; }
public Span<T> GetSpan() => this.Memory.Span;
public void Clear() => this.Memory.Span.Clear();
/// <summary>
/// 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!
/// </summary>
public static void SwapOrCopyContent(ref MemorySource<T> destination, ref MemorySource<T> source)
{
if (source.HasSwappableContents && destination.HasSwappableContents)
{
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);
}
}
/// <inheritdoc />
public void Dispose()
{
this.MemoryOwner?.Dispose();
}
private static void SwapContents(ref MemorySource<T> a, ref MemorySource<T> b)
{
IMemoryOwner<T> tempOwner = a.MemoryOwner;
Memory<T> tempMemory = a.Memory;
a.MemoryOwner = b.MemoryOwner;
a.Memory = b.Memory;
b.MemoryOwner = tempOwner;
b.Memory = tempMemory;
}
}
}

6
src/ImageSharp/Memory/SimpleGcMemoryAllocator.cs

@ -1,4 +1,6 @@
namespace SixLabors.Memory
using System.Buffers;
namespace SixLabors.Memory
{
/// <summary>
/// Implements <see cref="MemoryAllocator"/> by newing up arrays by the GC on every allocation requests.
@ -6,7 +8,7 @@
public sealed class SimpleGcMemoryAllocator : MemoryAllocator
{
/// <inheritdoc />
internal override IBuffer<T> Allocate<T>(int length, AllocationOptions options)
internal override IMemoryOwner<T> Allocate<T>(int length, AllocationOptions options = AllocationOptions.None)
{
return new BasicArrayBuffer<T>(new T[length]);
}

1
src/ImageSharp/PixelFormats/Bgra32.cs

@ -11,6 +11,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <summary>
/// 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
/// <para>
/// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form.
/// </para>

52
src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs

@ -2,13 +2,13 @@
// Licensed under the Apache License, Version 2.0.
// <auto-generated />
using System;
using System.Numerics;
using System.Buffers;
using SixLabors.Memory;
namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
{
using System;
using System.Numerics;
using SixLabors.Memory;
/// <summary>
/// Collection of Porter Duff alpha blending functions applying different composition models.
/// </summary>
@ -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<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
using (IMemoryOwner<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
{
Span<Vector4> destinationSpan = buffer.Slice(0, destination.Length);
Span<Vector4> 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<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
using (IMemoryOwner<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
{
Span<Vector4> destinationSpan = buffer.Slice(0, destination.Length);
Span<Vector4> 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<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
using (IMemoryOwner<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
{
Span<Vector4> destinationSpan = buffer.Slice(0, destination.Length);
Span<Vector4> 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<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
using (IMemoryOwner<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
{
Span<Vector4> destinationSpan = buffer.Slice(0, destination.Length);
Span<Vector4> 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<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
using (IMemoryOwner<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
{
Span<Vector4> destinationSpan = buffer.Slice(0, destination.Length);
Span<Vector4> 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<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
using (IMemoryOwner<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
{
Span<Vector4> destinationSpan = buffer.Slice(0, destination.Length);
Span<Vector4> 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<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
using (IMemoryOwner<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
{
Span<Vector4> destinationSpan = buffer.Slice(0, destination.Length);
Span<Vector4> 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<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
using (IMemoryOwner<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
{
Span<Vector4> destinationSpan = buffer.Slice(0, destination.Length);
Span<Vector4> 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<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
using (IMemoryOwner<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
{
Span<Vector4> destinationSpan = buffer.Slice(0, destination.Length);
Span<Vector4> 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<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
using (IMemoryOwner<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
{
Span<Vector4> destinationSpan = buffer.Slice(0, destination.Length);
Span<Vector4> 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<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
using (IMemoryOwner<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
{
Span<Vector4> destinationSpan = buffer.Slice(0, destination.Length);
Span<Vector4> 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<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
using (IMemoryOwner<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
{
Span<Vector4> destinationSpan = buffer.Slice(0, destination.Length);
Span<Vector4> 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<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
using (IMemoryOwner<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
{
Span<Vector4> destinationSpan = buffer.Slice(0, destination.Length);
Span<Vector4> 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<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
using (IMemoryOwner<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
{
Span<Vector4> destinationSpan = buffer.Slice(0, destination.Length);
Span<Vector4> 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<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
using (IMemoryOwner<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
{
Span<Vector4> destinationSpan = buffer.Slice(0, destination.Length);
Span<Vector4> 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<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
using (IMemoryOwner<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
{
Span<Vector4> destinationSpan = buffer.Slice(0, destination.Length);
Span<Vector4> 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<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
using (IMemoryOwner<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
{
Span<Vector4> destinationSpan = buffer.Slice(0, destination.Length);
Span<Vector4> 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<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
using (IMemoryOwner<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
{
Span<Vector4> destinationSpan = buffer.Slice(0, destination.Length);
Span<Vector4> 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<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
using (IMemoryOwner<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
{
Span<Vector4> destinationSpan = buffer.Slice(0, destination.Length);
Span<Vector4> 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<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
using (IMemoryOwner<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
{
Span<Vector4> destinationSpan = buffer.Slice(0, destination.Length);
Span<Vector4> 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<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
using (IMemoryOwner<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
{
Span<Vector4> destinationSpan = buffer.Slice(0, destination.Length);
Span<Vector4> backgroundSpan = buffer.Slice(destination.Length, destination.Length);

12
src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt

@ -12,13 +12,13 @@
// Licensed under the Apache License, Version 2.0.
// <auto-generated />
using System;
using System.Numerics;
using System.Buffers;
using SixLabors.Memory;
namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
{
using System;
using System.Numerics;
using SixLabors.Memory;
/// <summary>
/// Collection of Porter Duff alpha blending functions applying different composition models.
/// </summary>
@ -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<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
using (IMemoryOwner<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
{
Span<Vector4> destinationSpan = buffer.Slice(0, destination.Length);
Span<Vector4> backgroundSpan = buffer.Slice(destination.Length, destination.Length);

5
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<TPixel> pixels = source.GetPixelSpan();
// Build the histogram of the grayscale levels.
using (IBuffer<int> histogramBuffer = memoryAllocator.Allocate<int>(this.LuminanceLevels, AllocationOptions.Clean))
using (IBuffer<int> cdfBuffer = memoryAllocator.Allocate<int>(this.LuminanceLevels, AllocationOptions.Clean))
using (IMemoryOwner<int> histogramBuffer = memoryAllocator.Allocate<int>(this.LuminanceLevels, AllocationOptions.Clean))
using (IMemoryOwner<int> cdfBuffer = memoryAllocator.Allocate<int>(this.LuminanceLevels, AllocationOptions.Clean))
{
Span<int> histogram = histogramBuffer.GetSpan();
for (int i = 0; i < pixels.Length; i++)

5
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<TPixel> colors = source.MemoryAllocator.Allocate<TPixel>(width))
using (IBuffer<float> amount = source.MemoryAllocator.Allocate<float>(width))
using (IMemoryOwner<TPixel> colors = source.MemoryAllocator.Allocate<TPixel>(width))
using (IMemoryOwner<float> amount = source.MemoryAllocator.Allocate<float>(width))
{
// Be careful! Do not capture colorSpan & amountSpan in the lambda below!
Span<TPixel> colorSpan = colors.GetSpan();

5
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<TPixel> rowColors = source.MemoryAllocator.Allocate<TPixel>(width))
using (IMemoryOwner<TPixel> rowColors = source.MemoryAllocator.Allocate<TPixel>(width))
{
// Be careful! Do not capture rowColorsSpan in the lambda below!
Span<TPixel> rowColorsSpan = rowColors.GetSpan();
@ -127,7 +128,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
configuration.ParallelOptions,
y =>
{
using (IBuffer<float> amounts = source.MemoryAllocator.Allocate<float>(width))
using (IMemoryOwner<float> amounts = source.MemoryAllocator.Allocate<float>(width))
{
Span<float> amountsSpan = amounts.GetSpan();
int offsetY = y - startY;

5
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<TPixel> rowColors = source.MemoryAllocator.Allocate<TPixel>(width))
using (IMemoryOwner<TPixel> rowColors = source.MemoryAllocator.Allocate<TPixel>(width))
{
// Be careful! Do not capture rowColorsSpan in the lambda below!
Span<TPixel> rowColorsSpan = rowColors.GetSpan();
@ -129,7 +130,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
configuration.ParallelOptions,
y =>
{
using (IBuffer<float> amounts = source.MemoryAllocator.Allocate<float>(width))
using (IMemoryOwner<float> amounts = source.MemoryAllocator.Allocate<float>(width))
{
Span<float> amountsSpan = amounts.GetSpan();
int offsetY = y - startY;

4
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<TPixel> : IDisposable
where TPixel : struct, IPixel<TPixel>
{
private IBuffer<byte> pixels;
private IMemoryOwner<byte> pixels;
/// <summary>
/// Initializes a new instance of the <see cref="QuantizedFrame{TPixel}"/> class.

45
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
/// <summary>
/// Moment of <c>P(c)</c>.
/// </summary>
private IBuffer<long> vwt;
private IMemoryOwner<long> vwt;
/// <summary>
/// Moment of <c>r*P(c)</c>.
/// </summary>
private IBuffer<long> vmr;
private IMemoryOwner<long> vmr;
/// <summary>
/// Moment of <c>g*P(c)</c>.
/// </summary>
private IBuffer<long> vmg;
private IMemoryOwner<long> vmg;
/// <summary>
/// Moment of <c>b*P(c)</c>.
/// </summary>
private IBuffer<long> vmb;
private IMemoryOwner<long> vmb;
/// <summary>
/// Moment of <c>a*P(c)</c>.
/// </summary>
private IBuffer<long> vma;
private IMemoryOwner<long> vma;
/// <summary>
/// Moment of <c>c^2*P(c)</c>.
/// </summary>
private IBuffer<float> m2;
private IMemoryOwner<float> m2;
/// <summary>
/// Color space tag.
/// </summary>
private IBuffer<byte> tag;
private IMemoryOwner<byte> tag;
/// <summary>
/// Maximum allowed color depth
@ -467,18 +468,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
Span<long> vmaSpan = this.vma.GetSpan();
Span<float> m2Span = this.m2.GetSpan();
using (IBuffer<long> volume = memoryAllocator.Allocate<long>(IndexCount * IndexAlphaCount))
using (IBuffer<long> volumeR = memoryAllocator.Allocate<long>(IndexCount * IndexAlphaCount))
using (IBuffer<long> volumeG = memoryAllocator.Allocate<long>(IndexCount * IndexAlphaCount))
using (IBuffer<long> volumeB = memoryAllocator.Allocate<long>(IndexCount * IndexAlphaCount))
using (IBuffer<long> volumeA = memoryAllocator.Allocate<long>(IndexCount * IndexAlphaCount))
using (IBuffer<float> volume2 = memoryAllocator.Allocate<float>(IndexCount * IndexAlphaCount))
using (IBuffer<long> area = memoryAllocator.Allocate<long>(IndexAlphaCount))
using (IBuffer<long> areaR = memoryAllocator.Allocate<long>(IndexAlphaCount))
using (IBuffer<long> areaG = memoryAllocator.Allocate<long>(IndexAlphaCount))
using (IBuffer<long> areaB = memoryAllocator.Allocate<long>(IndexAlphaCount))
using (IBuffer<long> areaA = memoryAllocator.Allocate<long>(IndexAlphaCount))
using (IBuffer<float> area2 = memoryAllocator.Allocate<float>(IndexAlphaCount))
using (IMemoryOwner<long> volume = memoryAllocator.Allocate<long>(IndexCount * IndexAlphaCount))
using (IMemoryOwner<long> volumeR = memoryAllocator.Allocate<long>(IndexCount * IndexAlphaCount))
using (IMemoryOwner<long> volumeG = memoryAllocator.Allocate<long>(IndexCount * IndexAlphaCount))
using (IMemoryOwner<long> volumeB = memoryAllocator.Allocate<long>(IndexCount * IndexAlphaCount))
using (IMemoryOwner<long> volumeA = memoryAllocator.Allocate<long>(IndexCount * IndexAlphaCount))
using (IMemoryOwner<float> volume2 = memoryAllocator.Allocate<float>(IndexCount * IndexAlphaCount))
using (IMemoryOwner<long> area = memoryAllocator.Allocate<long>(IndexAlphaCount))
using (IMemoryOwner<long> areaR = memoryAllocator.Allocate<long>(IndexAlphaCount))
using (IMemoryOwner<long> areaG = memoryAllocator.Allocate<long>(IndexAlphaCount))
using (IMemoryOwner<long> areaB = memoryAllocator.Allocate<long>(IndexAlphaCount))
using (IMemoryOwner<long> areaA = memoryAllocator.Allocate<long>(IndexAlphaCount))
using (IMemoryOwner<float> area2 = memoryAllocator.Allocate<float>(IndexAlphaCount))
{
Span<long> volumeSpan = volume.GetSpan();
Span<long> 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;

5
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;
@ -296,14 +297,14 @@ 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<Vector4> firstPassPixels = source.MemoryAllocator.Allocate2D<Vector4>(width, source.Height))
{
firstPassPixels.Buffer.Clear();
firstPassPixels.MemorySource.Clear();
ParallelFor.WithTemporaryBuffer(
0,
sourceRectangle.Bottom,
configuration,
source.Width,
(int y, IBuffer<Vector4> tempRowBuffer) =>
(int y, IMemoryOwner<Vector4> tempRowBuffer) =>
{
ref Vector4 firstPassRow = ref MemoryMarshal.GetReference(firstPassPixels.GetRowSpan(y));
Span<TPixel> sourceRow = source.GetPixelRowSpan(y);

7
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
/// <summary>
/// The buffer containing the weights values.
/// </summary>
private readonly IBuffer<float> buffer;
private readonly MemorySource<float> buffer;
/// <summary>
/// Initializes a new instance of the <see cref="WeightsWindow"/> struct.
@ -46,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;
}
@ -66,7 +67,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// </summary>
/// <returns>The <see cref="Span{T}"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span<float> GetWindowSpan() => this.buffer.Slice(this.flatStartIndex, this.Length);
public Span<float> GetWindowSpan() => this.buffer.GetSpan().Slice(this.flatStartIndex, this.Length);
/// <summary>
/// Computes the sum of vectors in 'rowSpan' weighted by weight values, pointed by this <see cref="WeightsWindow"/> instance.

23
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<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private IBuffer<Vector4> source;
private IMemoryOwner<Vector4> source;
private IBuffer<TPixel> destination;
private IMemoryOwner<TPixel> destination;
[Params(16, 128, 512)]
public int Count { get; set; }

18
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<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private IBuffer<TPixel> destination;
private IMemoryOwner<TPixel> destination;
private IBuffer<byte> source;
private IMemoryOwner<byte> source;
[Params(16, 128, 1024)]
public int Count { get; set; }

20
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<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private IBuffer<TPixel> source;
private IMemoryOwner<TPixel> source;
private IBuffer<Vector4> destination;
private IMemoryOwner<Vector4> destination;
[Params(64, 300, 1024)]
public int Count { get; set; }

19
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<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private IBuffer<TPixel> source;
private IMemoryOwner<TPixel> source;
private IBuffer<byte> destination;
private IMemoryOwner<byte> destination;
[Params(16, 128, 1024)]
public int Count { get; set; }

18
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<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private IBuffer<TPixel> source;
private IMemoryOwner<TPixel> source;
private IBuffer<byte> destination;
private IMemoryOwner<byte> destination;
[Params(16, 128, 1024)]
public int Count { get; set; }

8
tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs

@ -3,6 +3,8 @@
// Licensed under the Apache License, Version 2.0.
// </copyright>
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<Vector4> buffer = Configuration.Default.MemoryAllocator.Allocate<Vector4>(destination.Length * 3))
using (IMemoryOwner<Vector4> buffer = Configuration.Default.MemoryAllocator.Allocate<Vector4>(destination.Length * 3))
{
Span<Vector4> destinationSpan = buffer.Slice(0, destination.Length);
Span<Vector4> backgroundSpan = buffer.Slice(destination.Length, destination.Length);
@ -59,7 +61,7 @@ namespace SixLabors.ImageSharp.Benchmarks
{
using (var image = new Image<Rgba32>(800, 800))
{
using (IBuffer<float> amounts = Configuration.Default.MemoryAllocator.Allocate<float>(image.Width))
using (IMemoryOwner<float> amounts = Configuration.Default.MemoryAllocator.Allocate<float>(image.Width))
{
amounts.GetSpan().Fill(1);
@ -80,7 +82,7 @@ namespace SixLabors.ImageSharp.Benchmarks
{
using (var image = new Image<Rgba32>(800, 800))
{
using (IBuffer<float> amounts = Configuration.Default.MemoryAllocator.Allocate<float>(image.Width))
using (IMemoryOwner<float> amounts = Configuration.Default.MemoryAllocator.Allocate<float>(image.Width))
{
amounts.GetSpan().Fill(1);
Buffer2D<Rgba32> pixels = image.GetRootFramePixelBuffer();

4
tests/ImageSharp.Benchmarks/Samplers/Glow.cs

@ -3,6 +3,8 @@
// Licensed under the Apache License, Version 2.0.
// </copyright>
using System.Buffers;
namespace SixLabors.ImageSharp.Benchmarks
{
@ -102,7 +104,7 @@ namespace SixLabors.ImageSharp.Benchmarks
}
int width = maxX - minX;
using (IBuffer<TPixel> rowColors = Configuration.Default.MemoryAllocator.Allocate<TPixel>(width))
using (IMemoryOwner<TPixel> rowColors = Configuration.Default.MemoryAllocator.Allocate<TPixel>(width))
{
Buffer2D<TPixel> sourcePixels = source.PixelBuffer;
rowColors.GetSpan().Fill(glowColor);

3
tests/ImageSharp.Tests/Advanced/AdvancedImageExtensionsTests.cs

@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.Tests.Advanced
Memory<TPixel> externalMemory = managerOfExeternalMemory.Memory;
using (Image<TPixel> image1 = Image.WrapMemory(externalMemory, image0.Width, image0.Height))
using (var image1 = Image.WrapMemory(externalMemory, image0.Width, image0.Height))
{
Memory<TPixel> internalMemory = image1.GetPixelMemory();
Assert.Equal(targetBuffer.Length, internalMemory.Length);
@ -72,7 +72,6 @@ namespace SixLabors.ImageSharp.Tests.Advanced
image0.ComparePixelBufferTo(externalMemory.Span);
}
}
}
[Theory]

17
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<TPixel>(TestImageProvider<TPixel> provider, int x0, int y0, int w, int h)
where TPixel : struct, IPixel<TPixel>
{
FormattableString testDetails = $"(x{x0},y{y0},w{w},h{h})";
var region = new RectangleF(x0, y0, w, h);
TPixel color = TestUtils.GetPixelOfNamedColor<TPixel>("Blue");
provider.RunValidatingProcessorTestOnWrappedMemoryImage(
c => c.Fill(color, region),
testDetails,
ImageComparer.Exact,
useReferenceOutputFrom: nameof(this.FillRegion));
}
public static readonly TheoryData<bool, string, float, PixelBlenderMode, float> BlendData =
new TheoryData<bool, string, float, PixelBlenderMode, float>()
{

1
tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs

@ -38,6 +38,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing
}
}
[Theory]
[WithBlankImages(500, 500, PixelTypes.Rgba32)]
public void OverlayByFilledPolygonOpacity<TPixel>(TestImageProvider<TPixel> provider)

3
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<float>(new BasicArrayBuffer<float>(values), values.Length, 1);
var source = new MemorySource<float>(new BasicArrayBuffer<float>(values), true);
buffers[i] = new Buffer2D<float>(source, values.Length, 1);
}
return new JpegColorConverter.ComponentValues(buffers, 0);
}

2
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;

142
tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs

@ -2,7 +2,16 @@
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.Memory;
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.Shapes;
using SixLabors.ImageSharp.Processing;
using Xunit;
// ReSharper disable InconsistentNaming
@ -12,12 +21,135 @@ namespace SixLabors.ImageSharp.Tests
{
public class WrapMemory
{
/// <summary>
/// A <see cref="MemoryManager{T}"/> exposing the locked pixel memory of a <see cref="Bitmap"/> instance.
/// TODO: This should be an example in https://github.com/SixLabors/Samples
/// </summary>
class BitmapMemoryManager : MemoryManager<Bgra32>
{
private readonly Bitmap bitmap;
private readonly BitmapData bmpData;
private readonly 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;
}
public bool IsDisposed { get; private set; }
protected override void Dispose(bool disposing)
{
if (this.IsDisposed)
{
return;
}
if (disposing)
{
this.bitmap.UnlockBits(this.bmpData);
}
this.IsDisposed = true;
}
public override unsafe Span<Bgra32> GetSpan()
{
void* ptr = (void*) this.bmpData.Scan0;
return new Span<Bgra32>(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 ConsumedBuffer_IsMemoryOwner_ReturnsFalse()
public void WrapMemory_CreatedImageIsCorrect()
{
var memory = new Memory<int>(new int[55]);
var buffer = new ConsumedBuffer<int>(memory);
Assert.False(buffer.IsMemoryOwner);
Configuration cfg = Configuration.Default.ShallowCopy();
var metaData = new ImageMetaData();
var array = new Rgba32[25];
var memory = new Memory<Rgba32>(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))
{
using (var memoryManager = new BitmapMemoryManager(bmp))
{
Memory<Bgra32> memory = memoryManager.Memory;
Bgra32 bg = NamedColors<Bgra32>.Red;
Bgra32 fg = NamedColors<Bgra32>.Green;
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,
$"{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<Bgra32>.Red;
Bgra32 fg = NamedColors<Bgra32>.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);
}
}
}
}

16
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<T>(int length)
where T : struct
{
IBuffer<T> buffer = this.MemoryAllocator.Allocate<T>(length);
IMemoryOwner<T> buffer = this.MemoryAllocator.Allocate<T>(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<int> firstAlloc = this.MemoryAllocator.Allocate<int>(42))
using (IMemoryOwner<int> firstAlloc = this.MemoryAllocator.Allocate<int>(42))
{
firstAlloc.GetSpan().Fill(666);
}
using (IBuffer<int> secondAlloc = this.MemoryAllocator.Allocate<int>(42, options))
using (IMemoryOwner<int> secondAlloc = this.MemoryAllocator.Allocate<int>(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<int> buffer = this.MemoryAllocator.Allocate<int>(32);
IMemoryOwner<int> buffer = this.MemoryAllocator.Allocate<int>(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<int> buffer = this.MemoryAllocator.Allocate<int>(32);
IMemoryOwner<int> buffer = this.MemoryAllocator.Allocate<int>(32);
this.MemoryAllocator.ReleaseRetainedResources();
buffer.Dispose();
}
@ -181,11 +183,11 @@ namespace SixLabors.ImageSharp.Tests.Memory
int arrayLengthThreshold = PoolSelectorThresholdInBytes / sizeof(int);
IBuffer<int> small = this.MemoryAllocator.Allocate<int>(arrayLengthThreshold - 1);
IMemoryOwner<int> small = this.MemoryAllocator.Allocate<int>(arrayLengthThreshold - 1);
ref int ptr2Small = ref small.GetReference();
small.Dispose();
IBuffer<int> large = this.MemoryAllocator.Allocate<int>(arrayLengthThreshold + 1);
IMemoryOwner<int> large = this.MemoryAllocator.Allocate<int>(arrayLengthThreshold + 1);
Assert.False(Unsafe.AreSame(ref ptr2Small, ref large.GetReference()));
}

151
tests/ImageSharp.Tests/Memory/Buffer2DTests.cs

@ -1,26 +1,27 @@
// 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.Buffers;
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
private class Assert : Xunit.Assert
{
public static void SpanPointsTo<T>(Span<T> span, IBuffer<T> buffer, int bufferOffset = 0)
public static void SpanPointsTo<T>(Span<T> span, IMemoryOwner<T> buffer, int bufferOffset = 0)
where T : struct
{
ref T actual = ref MemoryMarshal.GetReference(span);
@ -30,31 +31,7 @@ namespace SixLabors.ImageSharp.Tests.Memory
}
}
private MemoryAllocator MemoryAllocator { get; } = new MockMemoryAllocator();
private class MockMemoryAllocator : MemoryAllocator
{
internal override IBuffer<T> Allocate<T>(int length, AllocationOptions options)
{
var array = new T[length + 42];
if (options == AllocationOptions.None)
{
Span<byte> data = MemoryMarshal.Cast<T, byte>(array.AsSpan());
for (int i = 0; i < data.Length; i++)
{
data[i] = 42;
}
}
return new BasicArrayBuffer<T>(array, length);
}
internal override IManagedByteBuffer AllocateManagedByteBuffer(int length, AllocationOptions options)
{
throw new NotImplementedException();
}
}
private MemoryAllocator MemoryAllocator { get; } = new TestMemoryAllocator();
[Theory]
[InlineData(7, 42)]
@ -65,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);
}
}
@ -94,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.MemorySource.MemoryOwner, width * y);
}
}
@ -110,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.MemorySource.MemoryOwner, width * y + x);
}
}
@ -122,7 +99,7 @@ namespace SixLabors.ImageSharp.Tests.Memory
{
using (Buffer2D<TestStructs.Foo> buffer = this.MemoryAllocator.Allocate2D<TestStructs.Foo>(width, height))
{
Span<TestStructs.Foo> span = buffer.Buffer.GetSpan();
Span<TestStructs.Foo> span = buffer.MemorySource.GetSpan();
ref TestStructs.Foo actual = ref buffer[x, y];
@ -131,92 +108,24 @@ namespace SixLabors.ImageSharp.Tests.Memory
Assert.True(Unsafe.AreSame(ref expected, ref actual));
}
}
public class SwapOrCopyContent
{
private MemoryAllocator MemoryAllocator { get; } = new MockMemoryAllocator();
[Fact]
public void WhenBothBuffersAreMemoryOwners_ShouldSwap()
{
using (Buffer2D<int> a = this.MemoryAllocator.Allocate2D<int>(10, 5))
using (Buffer2D<int> b = this.MemoryAllocator.Allocate2D<int>(3, 7))
{
IBuffer<int> aa = a.Buffer;
IBuffer<int> bb = b.Buffer;
Buffer2D<int>.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<int> a = this.MemoryAllocator.Allocate2D<int>(10, 5))
using (Buffer2D<int> b = this.MemoryAllocator.Allocate2D<int>(3, 7))
{
var data = new Rgba32[3 * 7];
var color = new Rgba32(1, 2, 3, 4);
var mmg = new TestMemoryManager<Rgba32>(data);
var aBuff = new ConsumedBuffer<Rgba32>(mmg.Memory);
using (Buffer2D<Rgba32> a = new Buffer2D<Rgba32>(aBuff, 3, 7))
{
IBuffer<Rgba32> aa = a.Buffer;
// Precondition:
Assert.Equal(aBuff, aa);
IMemoryOwner<int> aa = a.MemorySource.MemoryOwner;
IMemoryOwner<int> bb = b.MemorySource.MemoryOwner;
using (Buffer2D<Rgba32> b = this.MemoryAllocator.Allocate2D<Rgba32>(3, 7))
{
IBuffer<Rgba32> bb = b.Buffer;
bb.GetSpan()[10] = color;
Buffer2D<int>.SwapOrCopyContent(a, b);
// Act:
Buffer2D<Rgba32>.SwapOrCopyContent(a, b);
Assert.Equal(bb, a.MemorySource.MemoryOwner);
Assert.Equal(aa, b.MemorySource.MemoryOwner);
// Assert:
Assert.Equal(aBuff, a.Buffer);
Assert.Equal(bb, b.Buffer);
}
// Assert:
Assert.Equal(color, a.Buffer.GetSpan()[10]);
}
}
[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<Rgba32>(data);
var aBuff = new ConsumedBuffer<Rgba32>(mmg.Memory);
using (Buffer2D<Rgba32> a = new Buffer2D<Rgba32>(aBuff, 3, 7))
{
IBuffer<Rgba32> aa = a.Buffer;
using (Buffer2D<Rgba32> b = this.MemoryAllocator.Allocate2D<Rgba32>(3, 8))
{
IBuffer<Rgba32> bb = b.Buffer;
Assert.ThrowsAny<InvalidOperationException>(
() =>
{
Buffer2D<Rgba32>.SwapOrCopyContent(a, b);
});
}
Assert.Equal(color, a.Buffer.GetSpan()[10]);
}
Assert.Equal(new Size(3, 7), a.Size());
Assert.Equal(new Size(10, 5), b.Size());
}
}
}
}

27
tests/ImageSharp.Tests/Memory/BufferTestSuite.cs

@ -63,15 +63,6 @@ namespace SixLabors.ImageSharp.Tests.Memory
public static readonly TheoryData<int> LenthValues = new TheoryData<int> { 0, 1, 7, 1023, 1024 };
[Fact]
public void IsMemoryOwner()
{
using (IBuffer<float> buffer = this.MemoryAllocator.Allocate<float>(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<T>(int desiredLength)
where T : struct
{
using (IBuffer<T> buffer = this.MemoryAllocator.Allocate<T>(desiredLength))
using (IMemoryOwner<T> buffer = this.MemoryAllocator.Allocate<T>(desiredLength))
{
Assert.Equal(desiredLength, buffer.GetSpan().Length);
}
@ -124,12 +115,12 @@ namespace SixLabors.ImageSharp.Tests.Memory
this.TestCanAllocateCleanBuffer<CustomStruct>(desiredLength);
}
private IBuffer<T> Allocate<T>(int desiredLength, AllocationOptions options, bool managedByteBuffer)
private IMemoryOwner<T> Allocate<T>(int desiredLength, AllocationOptions options, bool managedByteBuffer)
where T : struct
{
if (managedByteBuffer)
{
if (!(this.MemoryAllocator.AllocateManagedByteBuffer(desiredLength, options) is IBuffer<T> buffer))
if (!(this.MemoryAllocator.AllocateManagedByteBuffer(desiredLength, options) is IMemoryOwner<T> 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<T> buffer = this.Allocate<T>(desiredLength, AllocationOptions.Clean, testManagedByteBuffer))
using (IMemoryOwner<T> buffer = this.Allocate<T>(desiredLength, AllocationOptions.Clean, testManagedByteBuffer))
{
Assert.True(buffer.GetSpan().SequenceEqual(expected));
}
@ -172,7 +163,7 @@ namespace SixLabors.ImageSharp.Tests.Memory
private void TestSpanPropertyIsAlwaysTheSame<T>(int desiredLength, bool testManagedByteBuffer = false)
where T : struct
{
using (IBuffer<T> buffer = this.Allocate<T>(desiredLength, AllocationOptions.None, testManagedByteBuffer))
using (IMemoryOwner<T> buffer = this.Allocate<T>(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<T>(int desiredLength, Func<int, T> getExpectedValue, bool testManagedByteBuffer = false)
where T : struct
{
using (IBuffer<T> buffer = this.Allocate<T>(desiredLength, AllocationOptions.None, testManagedByteBuffer))
using (IMemoryOwner<T> buffer = this.Allocate<T>(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<T> buffer = this.Allocate<T>(desiredLength, AllocationOptions.None, testManagedByteBuffer))
using (IMemoryOwner<T> buffer = this.Allocate<T>(desiredLength, AllocationOptions.None, testManagedByteBuffer))
{
Assert.ThrowsAny<Exception>(
() =>
@ -294,7 +285,7 @@ namespace SixLabors.ImageSharp.Tests.Memory
[Fact]
public void GetMemory_ReturnsValidMemory()
{
using (IBuffer<CustomStruct> buffer = this.MemoryAllocator.Allocate<CustomStruct>(42))
using (IMemoryOwner<CustomStruct> buffer = this.MemoryAllocator.Allocate<CustomStruct>(42))
{
Span<CustomStruct> span0 = buffer.GetSpan();
span0[10].A = 30;
@ -311,7 +302,7 @@ namespace SixLabors.ImageSharp.Tests.Memory
[Fact]
public unsafe void GetMemory_ResultIsPinnable()
{
using (IBuffer<int> buffer = this.MemoryAllocator.Allocate<int>(42))
using (IMemoryOwner<int> buffer = this.MemoryAllocator.Allocate<int>(42))
{
Span<int> span0 = buffer.GetSpan();
span0[10] = 30;

163
tests/ImageSharp.Tests/Memory/MemorySourceTests.cs

@ -0,0 +1,163 @@
// 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 MemorySourceTests
{
public class Construction
{
[Theory]
[InlineData(false)]
[InlineData(true)]
public void InitializeAsOwner(bool isInternalMemorySource)
{
var data = new Rgba32[21];
var mmg = new TestMemoryManager<Rgba32>(data);
var a = new MemorySource<Rgba32>(mmg, isInternalMemorySource);
Assert.Equal(mmg, a.MemoryOwner);
Assert.Equal(mmg.Memory, a.Memory);
Assert.Equal(isInternalMemorySource, a.HasSwappableContents);
}
[Fact]
public void InitializeAsObserver_MemoryOwner_IsNull()
{
var data = new Rgba32[21];
var mmg = new TestMemoryManager<Rgba32>(data);
var a = new MemorySource<Rgba32>(mmg.Memory);
Assert.Null(a.MemoryOwner);
Assert.Equal(mmg.Memory, a.Memory);
Assert.False(a.HasSwappableContents);
}
}
public class Dispose
{
[Theory]
[InlineData(false)]
[InlineData(true)]
public void WhenOwnershipIsTransfered_ShouldDisposeMemoryOwner(bool isInternalMemorySource)
{
var mmg = new TestMemoryManager<int>(new int[10]);
var bmg = new MemorySource<int>(mmg, isInternalMemorySource);
bmg.Dispose();
Assert.True(mmg.IsDisposed);
}
[Fact]
public void WhenMemoryObserver_ShouldNotDisposeAnything()
{
var mmg = new TestMemoryManager<int>(new int[10]);
var bmg = new MemorySource<int>(mmg.Memory);
bmg.Dispose();
Assert.False(mmg.IsDisposed);
}
}
public class SwapOrCopyContent
{
private MemoryAllocator MemoryAllocator { get; } = new TestMemoryAllocator();
private MemorySource<T> AllocateMemorySource<T>(int length, AllocationOptions options = AllocationOptions.None)
where T : struct
{
IMemoryOwner<T> owner = this.MemoryAllocator.Allocate<T>(length, options);
return new MemorySource<T>(owner, true);
}
[Fact]
public void WhenBothAreMemoryOwners_ShouldSwap()
{
MemorySource<int> a = this.AllocateMemorySource<int>(13);
MemorySource<int> b = this.AllocateMemorySource<int>(17);
IMemoryOwner<int> aa = a.MemoryOwner;
IMemoryOwner<int> bb = b.MemoryOwner;
Memory<int> aaa = a.Memory;
Memory<int> bbb = b.Memory;
MemorySource<int>.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, 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<Rgba32>(data);
var dest = new MemorySource<Rgba32>(destOwner.Memory);
IMemoryOwner<Rgba32> sourceOwner = this.MemoryAllocator.Allocate<Rgba32>(21);
MemorySource<Rgba32> source = sourceIsOwner
? new MemorySource<Rgba32>(sourceOwner, isInternalMemorySource)
: new MemorySource<Rgba32>(sourceOwner.Memory);
sourceOwner.Memory.Span[10] = color;
// Act:
MemorySource<Rgba32>.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<Rgba32>(data);
var dest = new MemorySource<Rgba32>(destOwner.Memory);
IMemoryOwner<Rgba32> sourceOwner = this.MemoryAllocator.Allocate<Rgba32>(22);
MemorySource<Rgba32> source = sourceIsOwner
? new MemorySource<Rgba32>(sourceOwner, true)
: new MemorySource<Rgba32>(sourceOwner.Memory);
sourceOwner.Memory.Span[10] = color;
// Act:
Assert.ThrowsAny<InvalidOperationException>(
() => MemorySource<Rgba32>.SwapOrCopyContent(ref dest, ref source)
);
Assert.Equal(color, source.Memory.Span[10]);
Assert.NotEqual(color, dest.Memory.Span[10]);
}
}
}
}

9
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<ImageSharp.PixelFormats.Rgba32> source = Configuration.Default.MemoryAllocator.Allocate<ImageSharp.PixelFormats.Rgba32>(count))
using (IBuffer<Vector4> dest = Configuration.Default.MemoryAllocator.Allocate<Vector4>(count))
using (IMemoryOwner<ImageSharp.PixelFormats.Rgba32> source = Configuration.Default.MemoryAllocator.Allocate<ImageSharp.PixelFormats.Rgba32>(count))
using (IMemoryOwner<Vector4> dest = Configuration.Default.MemoryAllocator.Allocate<Vector4>(count))
{
this.Measure(
times,
@ -537,7 +538,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
where TDest : struct
{
public TSource[] SourceBuffer { get; }
public IBuffer<TDest> ActualDestBuffer { get; }
public IMemoryOwner<TDest> 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, TDest>(
TSource[] source,
TDest[] expected,
Action<TSource[], IBuffer<TDest>> action)
Action<TSource[], IMemoryOwner<TDest>> action)
where TSource : struct
where TDest : struct
{

3
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<TPixel> image0 = provider.GetImage())
{
var mmg = TestMemoryManager<TPixel>.CreateAsCopyOfPixelData(image0);
var mmg = TestMemoryManager<TPixel>.CreateAsCopyOf(image0.GetPixelSpan());
using (var image1 = Image.WrapMemory(mmg.Memory, image0.Width, image0.Height))
{

7
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<TPixel>(w, h);
using (IBuffer<Bgra32> workBuffer = Configuration.Default.MemoryAllocator.Allocate<Bgra32>(w))
using (IMemoryOwner<Bgra32> workBuffer = Configuration.Default.MemoryAllocator.Allocate<Bgra32>(w))
{
fixed (Bgra32* destPtr = &workBuffer.GetReference())
{
@ -89,7 +90,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
var image = new Image<TPixel>(w, h);
using (IBuffer<Bgr24> workBuffer = Configuration.Default.MemoryAllocator.Allocate<Bgr24>(w))
using (IMemoryOwner<Bgr24> workBuffer = Configuration.Default.MemoryAllocator.Allocate<Bgr24>(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<Bgra32> workBuffer = image.GetConfiguration().MemoryAllocator.Allocate<Bgra32>(w))
using (IMemoryOwner<Bgra32> workBuffer = image.GetConfiguration().MemoryAllocator.Allocate<Bgra32>(w))
{
fixed (Bgra32* sourcePtr = &workBuffer.GetReference())
{

56
tests/ImageSharp.Tests/TestUtilities/TestMemoryAllocator.cs

@ -0,0 +1,56 @@
using System;
using System.Buffers;
using System.Runtime.InteropServices;
using SixLabors.Memory;
namespace SixLabors.ImageSharp.Tests.Memory
{
internal class TestMemoryAllocator : MemoryAllocator
{
public TestMemoryAllocator(byte dirtyValue = 42)
{
this.DirtyValue = dirtyValue;
}
/// <summary>
/// The value to initilazie the result buffer with, with non-clean options (<see cref="AllocationOptions.None"/>)
/// </summary>
public byte DirtyValue { get; }
internal override IMemoryOwner<T> Allocate<T>(int length, AllocationOptions options = AllocationOptions.None)
{
T[] array = this.AllocateArray<T>(length, options);
return new BasicArrayBuffer<T>(array, length);
}
internal override IManagedByteBuffer AllocateManagedByteBuffer(int length, AllocationOptions options = AllocationOptions.None)
{
byte[] array = this.AllocateArray<byte>(length, options);
return new ManagedByteBuffer(array);
}
private T[] AllocateArray<T>(int length, AllocationOptions options)
where T : struct
{
var array = new T[length + 42];
if (options == AllocationOptions.None)
{
Span<byte> data = MemoryMarshal.Cast<T, byte>(array.AsSpan());
data.Fill(this.DirtyValue);
}
return array;
}
private class ManagedByteBuffer : BasicArrayBuffer<byte>, IManagedByteBuffer
{
public ManagedByteBuffer(byte[] array)
: base(array)
{
}
}
}
}

22
tests/ImageSharp.Tests/TestUtilities/TestMemoryManager.cs

@ -3,22 +3,17 @@ using System.Buffers;
namespace SixLabors.ImageSharp.Tests
{
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
class TestMemoryManager<T> : MemoryManager<T>
where T : struct, IPixel<T>
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<T> GetSpan()
{
@ -35,16 +30,17 @@ namespace SixLabors.ImageSharp.Tests
throw new NotImplementedException();
}
public static TestMemoryManager<T> CreateAsCopyOfPixelData(Span<T> pixelData)
public static TestMemoryManager<T> CreateAsCopyOf(Span<T> copyThisBuffer)
{
var pixelArray = new T[pixelData.Length];
pixelData.CopyTo(pixelArray);
var pixelArray = new T[copyThisBuffer.Length];
copyThisBuffer.CopyTo(pixelArray);
return new TestMemoryManager<T>(pixelArray);
}
public static TestMemoryManager<T> CreateAsCopyOfPixelData(Image<T> image)
protected override void Dispose(bool disposing)
{
return CreateAsCopyOfPixelData(image.GetPixelSpan());
this.IsDisposed = true;
this.PixelArray = null;
}
}
}

6
tests/ImageSharp.Tests/TestUtilities/TestUtils.cs

@ -214,7 +214,7 @@ namespace SixLabors.ImageSharp.Tests
using (Image<TPixel> image0 = provider.GetImage())
{
var mmg = TestMemoryManager<TPixel>.CreateAsCopyOfPixelData(image0.GetPixelSpan());
var mmg = TestMemoryManager<TPixel>.CreateAsCopyOf(image0.GetPixelSpan());
using (var image1 = Image.WrapMemory(mmg.Memory, image0.Width, image0.Height))
{
@ -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;
}
}
}

Loading…
Cancel
Save