diff --git a/src/ImageSharp/Image/PixelArea{TColor}.cs b/src/ImageSharp/Image/PixelArea{TColor}.cs
index 673fe55008..f8e46a6cc9 100644
--- a/src/ImageSharp/Image/PixelArea{TColor}.cs
+++ b/src/ImageSharp/Image/PixelArea{TColor}.cs
@@ -2,10 +2,10 @@
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
//
-
namespace ImageSharp
{
using System;
+ using System.Buffers;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
@@ -39,6 +39,11 @@ namespace ImageSharp
///
private bool isDisposed;
+ ///
+ /// True if was allocated by the constructor (rented from )
+ ///
+ private bool isBufferOwner;
+
///
/// Initializes a new instance of the class.
///
@@ -96,7 +101,7 @@ namespace ImageSharp
/// The width.
/// The component order.
public PixelArea(int width, ComponentOrder componentOrder)
- : this(width, 1, componentOrder, 0)
+ : this(width, 1, componentOrder, 0)
{
}
@@ -107,7 +112,7 @@ namespace ImageSharp
/// The component order.
/// The number of bytes to pad each row.
public PixelArea(int width, ComponentOrder componentOrder, int padding)
- : this(width, 1, componentOrder, padding)
+ : this(width, 1, componentOrder, padding)
{
}
@@ -124,7 +129,8 @@ namespace ImageSharp
this.Height = height;
this.ComponentOrder = componentOrder;
this.RowByteCount = (width * GetComponentCount(componentOrder)) + padding;
- this.Bytes = new byte[this.RowByteCount * height];
+ this.Bytes = BytesPool.Rent(this.RowByteCount * height);
+ this.isBufferOwner = true;
this.pixelsHandle = GCHandle.Alloc(this.Bytes, GCHandleType.Pinned);
// TODO: Why is Resharper warning us about an impure method call?
@@ -137,7 +143,7 @@ namespace ImageSharp
///
~PixelArea()
{
- this.Dispose();
+ this.Dispose(false);
}
///
@@ -145,20 +151,30 @@ namespace ImageSharp
///
public byte[] Bytes { get; }
+ ///
+ /// Gets the component order.
+ ///
+ public ComponentOrder ComponentOrder { get; }
+
///
/// Gets the pointer to the pixel buffer.
///
public IntPtr DataPointer => this.dataPointer;
+ ///
+ /// Gets the height.
+ ///
+ public int Height { get; }
+
///
/// Gets the data pointer.
///
public byte* PixelBase { get; private set; }
///
- /// Gets the component order.
+ /// Gets number of bytes in a row.
///
- public ComponentOrder ComponentOrder { get; }
+ public int RowByteCount { get; }
///
/// Gets the width.
@@ -166,14 +182,17 @@ namespace ImageSharp
public int Width { get; }
///
- /// Gets the height.
+ /// Gets the pool used to rent , when it's not coming from an external source
///
- public int Height { get; }
+ private static ArrayPool BytesPool => ArrayPool.Shared;
///
- /// Gets number of bytes in a row.
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
///
- public int RowByteCount { get; }
+ public void Dispose()
+ {
+ this.Dispose(true);
+ }
///
/// Reads the stream to the area.
@@ -193,39 +212,6 @@ namespace ImageSharp
stream.Write(this.Bytes, 0, this.Bytes.Length);
}
- ///
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
- ///
- public void Dispose()
- {
- if (this.isDisposed)
- {
- return;
- }
-
- if (this.PixelBase == null)
- {
- return;
- }
-
- if (this.pixelsHandle.IsAllocated)
- {
- this.pixelsHandle.Free();
- }
-
- this.dataPointer = IntPtr.Zero;
- this.PixelBase = null;
-
- this.isDisposed = true;
-
- // This object will be cleaned up by the Dispose method.
- // Therefore, you should call GC.SuppressFinalize to
- // take this object off the finalization queue
- // and prevent finalization code for this object
- // from executing a second time.
- GC.SuppressFinalize(this);
- }
-
///
/// Resets the bytes of the array to it's initial value.
///
@@ -265,8 +251,45 @@ namespace ImageSharp
int requiredLength = (width * GetComponentCount(componentOrder)) * height;
if (bytes.Length != requiredLength)
{
- throw new ArgumentOutOfRangeException(nameof(bytes), $"Invalid byte array length. Length {bytes.Length}; Should be {requiredLength}.");
+ throw new ArgumentOutOfRangeException(
+ nameof(bytes),
+ $"Invalid byte array length. Length {bytes.Length}; Should be {requiredLength}.");
}
}
+
+ private void Dispose(bool disposing)
+ {
+ if (this.isDisposed)
+ {
+ return;
+ }
+
+ if (this.PixelBase == null)
+ {
+ return;
+ }
+
+ if (this.pixelsHandle.IsAllocated)
+ {
+ this.pixelsHandle.Free();
+ }
+
+ if (disposing && this.isBufferOwner)
+ {
+ BytesPool.Return(this.Bytes);
+ }
+
+ this.dataPointer = IntPtr.Zero;
+ this.PixelBase = null;
+
+ this.isDisposed = true;
+
+ // This object will be cleaned up by the Dispose method.
+ // Therefore, you should call GC.SuppressFinalize to
+ // take this object off the finalization queue
+ // and prevent finalization code for this object
+ // from executing a second time.
+ GC.SuppressFinalize(this);
+ }
}
-}
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegTests.cs
index 13d31ce518..35b1ff0d93 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/JpegTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegTests.cs
@@ -19,6 +19,7 @@ namespace ImageSharp.Tests
{
this.Output = output;
}
+
public static IEnumerable AllJpegFiles => TestImages.Jpeg.All;
[Theory]
@@ -57,7 +58,15 @@ namespace ImageSharp.Tests
}
}
- private const int BenchmarkExecTimes = 2;
+ private const int BenchmarkExecTimes = 30;
+
+ public static readonly string[] BenchmarkFiles =
+ {
+ TestImages.Bmp.Car, TestImages.Bmp.NegHeight,
+ TestImages.Bmp.F, TestImages.Png.Splash,
+ TestImages.Jpeg.Jpeg420, TestImages.Jpeg.Calliphora,
+ TestImages.Jpeg.Cmyk
+ };
[Theory(
//Skip = "Benchmark, enable manually!"