Browse Source

Merge branch 'tocsoft/IDisposable'

af/merge-core
Scott Williams 10 years ago
parent
commit
4cae668559
  1. 7
      src/ImageSharp.Processing/Processors/Convolution/Convolution2DProcessor.cs
  2. 29
      src/ImageSharp.Processing/Processors/Convolution/Convolution2PassProcessor.cs
  3. 7
      src/ImageSharp.Processing/Processors/Convolution/ConvolutionProcessor.cs
  4. 20
      src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs
  5. 7
      src/ImageSharp.Processing/Processors/Effects/OilPaintingProcessor.cs
  6. 7
      src/ImageSharp.Processing/Processors/Effects/PixelateProcessor.cs
  7. 24
      src/ImageSharp.Processing/Processors/Transforms/CompandingResizeProcessor.cs
  8. 8
      src/ImageSharp.Processing/Processors/Transforms/CropProcessor.cs
  9. 6
      src/ImageSharp.Processing/Processors/Transforms/EntropyCropProcessor.cs
  10. 16
      src/ImageSharp.Processing/Processors/Transforms/FlipProcessor.cs
  11. 24
      src/ImageSharp.Processing/Processors/Transforms/ResizeProcessor.cs
  12. 28
      src/ImageSharp.Processing/Processors/Transforms/RotateProcessor.cs
  13. 7
      src/ImageSharp.Processing/Processors/Transforms/SkewProcessor.cs
  14. 3
      src/ImageSharp.Processing/project.json
  15. 29
      src/ImageSharp/Image/IImageBase{TColor}.cs
  16. 53
      src/ImageSharp/Image/ImageBase{TColor}.cs
  17. 138
      src/ImageSharp/Image/PixelAccessor{TColor}.cs
  18. 16
      src/ImageSharp/Quantizers/Quantize.cs
  19. 18
      tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs

7
src/ImageSharp.Processing/Processors/Convolution/Convolution2DProcessor.cs

@ -54,9 +54,9 @@ namespace ImageSharp.Processing.Processors
int maxY = endY - 1; int maxY = endY - 1;
int maxX = endX - 1; int maxX = endX - 1;
TColor[] target = PixelPool<TColor>.RentPixels(source.Width * source.Height); using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(source.Width, source.Height))
{
using (PixelAccessor<TColor> sourcePixels = source.Lock()) using (PixelAccessor<TColor> sourcePixels = source.Lock())
using (PixelAccessor<TColor> targetPixels = target.Lock(source.Width, source.Height))
{ {
Parallel.For( Parallel.For(
startY, startY,
@ -120,7 +120,8 @@ namespace ImageSharp.Processing.Processors
}); });
} }
source.SetPixels(source.Width, source.Height, target); source.SwapPixelsBuffers(targetPixels);
}
} }
} }
} }

29
src/ImageSharp.Processing/Processors/Convolution/Convolution2PassProcessor.cs

@ -45,19 +45,16 @@ namespace ImageSharp.Processing.Processors
int width = source.Width; int width = source.Width;
int height = source.Height; int height = source.Height;
TColor[] target = PixelPool<TColor>.RentPixels(width * height); using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(width, height))
TColor[] firstPass = PixelPool<TColor>.RentPixels(width * height);
try
{ {
this.ApplyConvolution(width, height, firstPass, source.Pixels, sourceRectangle, kernelX); using (PixelAccessor<TColor> firstPassPixels = new PixelAccessor<TColor>(width, height))
this.ApplyConvolution(width, height, target, firstPass, sourceRectangle, kernelY); using (PixelAccessor<TColor> sourcePixels = source.Lock())
source.SetPixels(width, height, target);
}
finally
{ {
PixelPool<TColor>.ReturnPixels(firstPass); this.ApplyConvolution(width, height, firstPassPixels, sourcePixels, sourceRectangle, kernelX);
this.ApplyConvolution(width, height, targetPixels, firstPassPixels, sourceRectangle, kernelY);
}
source.SwapPixelsBuffers(targetPixels);
} }
} }
@ -67,13 +64,13 @@ namespace ImageSharp.Processing.Processors
/// </summary> /// </summary>
/// <param name="width">The image width.</param> /// <param name="width">The image width.</param>
/// <param name="height">The image height.</param> /// <param name="height">The image height.</param>
/// <param name="target">The target pixels to apply the process to.</param> /// <param name="targetPixels">The target pixels to apply the process to.</param>
/// <param name="source">The source pixels. Cannot be null.</param> /// <param name="sourcePixels">The source pixels. Cannot be null.</param>
/// <param name="sourceRectangle"> /// <param name="sourceRectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
/// </param> /// </param>
/// <param name="kernel">The kernel operator.</param> /// <param name="kernel">The kernel operator.</param>
private void ApplyConvolution(int width, int height, TColor[] target, TColor[] source, Rectangle sourceRectangle, float[][] kernel) private void ApplyConvolution(int width, int height, PixelAccessor<TColor> targetPixels, PixelAccessor<TColor> sourcePixels, Rectangle sourceRectangle, float[][] kernel)
{ {
int kernelHeight = kernel.Length; int kernelHeight = kernel.Length;
int kernelWidth = kernel[0].Length; int kernelWidth = kernel[0].Length;
@ -87,9 +84,6 @@ namespace ImageSharp.Processing.Processors
int maxY = endY - 1; int maxY = endY - 1;
int maxX = endX - 1; int maxX = endX - 1;
using (PixelAccessor<TColor> sourcePixels = source.Lock(width, height))
using (PixelAccessor<TColor> targetPixels = target.Lock(width, height))
{
Parallel.For( Parallel.For(
startY, startY,
endY, endY,
@ -128,4 +122,3 @@ namespace ImageSharp.Processing.Processors
} }
} }
} }
}

7
src/ImageSharp.Processing/Processors/Convolution/ConvolutionProcessor.cs

@ -44,9 +44,9 @@ namespace ImageSharp.Processing.Processors
int maxY = endY - 1; int maxY = endY - 1;
int maxX = endX - 1; int maxX = endX - 1;
TColor[] target = new TColor[source.Width * source.Height]; using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(source.Width, source.Height))
{
using (PixelAccessor<TColor> sourcePixels = source.Lock()) using (PixelAccessor<TColor> sourcePixels = source.Lock())
using (PixelAccessor<TColor> targetPixels = target.Lock<TColor>(source.Width, source.Height))
{ {
Parallel.For( Parallel.For(
startY, startY,
@ -97,7 +97,8 @@ namespace ImageSharp.Processing.Processors
}); });
} }
source.SetPixels(source.Width, source.Height, target); source.SwapPixelsBuffers(targetPixels);
}
} }
} }
} }

20
src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs

@ -75,10 +75,10 @@ namespace ImageSharp.Processing.Processors
int minY = Math.Max(0, startY); int minY = Math.Max(0, startY);
int maxY = Math.Min(source.Height, endY); int maxY = Math.Min(source.Height, endY);
// First run. // we need a clean copy for each pass to start from
ImageBase<TColor> target = new Image<TColor>(source.Width, source.Height); using (ImageBase<TColor> cleanCopy = new Image<TColor>(source))
target.ClonePixels(source.Width, source.Height, source.Pixels); {
new ConvolutionProcessor<TColor>(kernels[0]).Apply(target, sourceRectangle); new ConvolutionProcessor<TColor>(kernels[0]).Apply(source, sourceRectangle);
if (kernels.Length == 1) if (kernels.Length == 1)
{ {
@ -103,14 +103,12 @@ namespace ImageSharp.Processing.Processors
// ReSharper disable once ForCanBeConvertedToForeach // ReSharper disable once ForCanBeConvertedToForeach
for (int i = 1; i < kernels.Length; i++) for (int i = 1; i < kernels.Length; i++)
{ {
// Create a clone for each pass and copy the offset pixels across. using (ImageBase<TColor> pass = new Image<TColor>(cleanCopy))
ImageBase<TColor> pass = new Image<TColor>(source.Width, source.Height); {
pass.ClonePixels(source.Width, source.Height, source.Pixels);
new ConvolutionProcessor<TColor>(kernels[i]).Apply(pass, sourceRectangle); new ConvolutionProcessor<TColor>(kernels[i]).Apply(pass, sourceRectangle);
using (PixelAccessor<TColor> passPixels = pass.Lock()) using (PixelAccessor<TColor> passPixels = pass.Lock())
using (PixelAccessor<TColor> targetPixels = target.Lock()) using (PixelAccessor<TColor> targetPixels = source.Lock())
{ {
Parallel.For( Parallel.For(
minY, minY,
@ -131,8 +129,8 @@ namespace ImageSharp.Processing.Processors
}); });
} }
} }
}
source.SetPixels(source.Width, source.Height, target.Pixels); }
} }
/// <inheritdoc/> /// <inheritdoc/>

7
src/ImageSharp.Processing/Processors/Effects/OilPaintingProcessor.cs

@ -67,9 +67,9 @@ namespace ImageSharp.Processing.Processors
startX = 0; startX = 0;
} }
TColor[] target = PixelPool<TColor>.RentPixels(source.Width * source.Height); using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(source.Width, source.Height))
{
using (PixelAccessor<TColor> sourcePixels = source.Lock()) using (PixelAccessor<TColor> sourcePixels = source.Lock())
using (PixelAccessor<TColor> targetPixels = target.Lock(source.Width, source.Height))
{ {
Parallel.For( Parallel.For(
minY, minY,
@ -151,7 +151,8 @@ namespace ImageSharp.Processing.Processors
}); });
} }
source.SetPixels(source.Width, source.Height, target); source.SwapPixelsBuffers(targetPixels);
}
} }
} }
} }

7
src/ImageSharp.Processing/Processors/Effects/PixelateProcessor.cs

@ -63,10 +63,10 @@ namespace ImageSharp.Processing.Processors
// Get the range on the y-plane to choose from. // Get the range on the y-plane to choose from.
IEnumerable<int> range = EnumerableExtensions.SteppedRange(minY, i => i < maxY, size); IEnumerable<int> range = EnumerableExtensions.SteppedRange(minY, i => i < maxY, size);
TColor[] target = PixelPool<TColor>.RentPixels(source.Width * source.Height);
using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(source.Width, source.Height))
{
using (PixelAccessor<TColor> sourcePixels = source.Lock()) using (PixelAccessor<TColor> sourcePixels = source.Lock())
using (PixelAccessor<TColor> targetPixels = target.Lock(source.Width, source.Height))
{ {
Parallel.ForEach( Parallel.ForEach(
range, range,
@ -107,7 +107,8 @@ namespace ImageSharp.Processing.Processors
} }
}); });
source.SetPixels(source.Width, source.Height, target); source.SwapPixelsBuffers(targetPixels);
}
} }
} }
} }

24
src/ImageSharp.Processing/Processors/Transforms/CompandingResizeProcessor.cs

@ -66,19 +66,15 @@ namespace ImageSharp.Processing.Processors
int minY = Math.Max(0, startY); int minY = Math.Max(0, startY);
int maxY = Math.Min(height, endY); int maxY = Math.Min(height, endY);
TColor[] firstPass = null;
try
{
TColor[] target = PixelPool<TColor>.RentPixels(width * height);
if (this.Sampler is NearestNeighborResampler) if (this.Sampler is NearestNeighborResampler)
{ {
// Scaling factors // Scaling factors
float widthFactor = sourceRectangle.Width / (float)this.ResizeRectangle.Width; float widthFactor = sourceRectangle.Width / (float)this.ResizeRectangle.Width;
float heightFactor = sourceRectangle.Height / (float)this.ResizeRectangle.Height; float heightFactor = sourceRectangle.Height / (float)this.ResizeRectangle.Height;
using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(width, height))
{
using (PixelAccessor<TColor> sourcePixels = source.Lock()) using (PixelAccessor<TColor> sourcePixels = source.Lock())
using (PixelAccessor<TColor> targetPixels = target.Lock(width, height))
{ {
Parallel.For( Parallel.For(
minY, minY,
@ -98,18 +94,19 @@ namespace ImageSharp.Processing.Processors
} }
// Break out now. // Break out now.
source.SetPixels(width, height, target); source.SwapPixelsBuffers(targetPixels);
return; return;
} }
}
// Interpolate the image using the calculated weights. // Interpolate the image using the calculated weights.
// A 2-pass 1D algorithm appears to be faster than splitting a 1-pass 2D algorithm // A 2-pass 1D algorithm appears to be faster than splitting a 1-pass 2D algorithm
// First process the columns. Since we are not using multiple threads startY and endY // First process the columns. Since we are not using multiple threads startY and endY
// are the upper and lower bounds of the source rectangle. // are the upper and lower bounds of the source rectangle.
firstPass = PixelPool<TColor>.RentPixels(width * source.Height); using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(width, height))
{
using (PixelAccessor<TColor> sourcePixels = source.Lock()) using (PixelAccessor<TColor> sourcePixels = source.Lock())
using (PixelAccessor<TColor> firstPassPixels = firstPass.Lock(width, source.Height)) using (PixelAccessor<TColor> firstPassPixels = new PixelAccessor<TColor>(width, source.Height))
using (PixelAccessor<TColor> targetPixels = target.Lock(width, height))
{ {
Parallel.For( Parallel.For(
0, 0,
@ -165,12 +162,7 @@ namespace ImageSharp.Processing.Processors
}); });
} }
source.SetPixels(width, height, target); source.SwapPixelsBuffers(targetPixels);
}
finally
{
// We don't return target or source pixels as they are handled in the image itself.
PixelPool<TColor>.ReturnPixels(firstPass);
} }
} }
} }

8
src/ImageSharp.Processing/Processors/Transforms/CropProcessor.cs

@ -42,10 +42,9 @@ namespace ImageSharp.Processing.Processors
int minX = Math.Max(this.CropRectangle.X, sourceRectangle.X); int minX = Math.Max(this.CropRectangle.X, sourceRectangle.X);
int maxX = Math.Min(this.CropRectangle.Right, sourceRectangle.Right); int maxX = Math.Min(this.CropRectangle.Right, sourceRectangle.Right);
TColor[] target = PixelPool<TColor>.RentPixels(this.CropRectangle.Width * this.CropRectangle.Height); using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(this.CropRectangle.Width, this.CropRectangle.Height))
{
using (PixelAccessor<TColor> sourcePixels = source.Lock()) using (PixelAccessor<TColor> sourcePixels = source.Lock())
using (PixelAccessor<TColor> targetPixels = target.Lock(this.CropRectangle.Width, this.CropRectangle.Height))
{ {
Parallel.For( Parallel.For(
minY, minY,
@ -60,7 +59,8 @@ namespace ImageSharp.Processing.Processors
}); });
} }
source.SetPixels(this.CropRectangle.Width, this.CropRectangle.Height, target); source.SwapPixelsBuffers(targetPixels);
}
} }
} }
} }

6
src/ImageSharp.Processing/Processors/Transforms/EntropyCropProcessor.cs

@ -36,9 +36,8 @@ namespace ImageSharp.Processing.Processors
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnApply(ImageBase<TColor> source, Rectangle sourceRectangle) protected override void OnApply(ImageBase<TColor> source, Rectangle sourceRectangle)
{ {
ImageBase<TColor> temp = new Image<TColor>(source.Width, source.Height); using (ImageBase<TColor> temp = new Image<TColor>(source))
temp.ClonePixels(source.Width, source.Height, source.Pixels); {
// Detect the edges. // Detect the edges.
new SobelProcessor<TColor>().Apply(temp, sourceRectangle); new SobelProcessor<TColor>().Apply(temp, sourceRectangle);
@ -57,3 +56,4 @@ namespace ImageSharp.Processing.Processors
} }
} }
} }
}

16
src/ImageSharp.Processing/Processors/Transforms/FlipProcessor.cs

@ -55,10 +55,9 @@ namespace ImageSharp.Processing.Processors
int height = source.Height; int height = source.Height;
int halfHeight = (int)Math.Ceiling(source.Height * .5F); int halfHeight = (int)Math.Ceiling(source.Height * .5F);
TColor[] target = PixelPool<TColor>.RentPixels(width * height); using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(width, height))
{
using (PixelAccessor<TColor> sourcePixels = source.Lock()) using (PixelAccessor<TColor> sourcePixels = source.Lock())
using (PixelAccessor<TColor> targetPixels = target.Lock(width, height))
{ {
Parallel.For( Parallel.For(
0, 0,
@ -75,7 +74,8 @@ namespace ImageSharp.Processing.Processors
}); });
} }
source.SetPixels(width, height, target); source.SwapPixelsBuffers(targetPixels);
}
} }
/// <summary> /// <summary>
@ -89,10 +89,9 @@ namespace ImageSharp.Processing.Processors
int height = source.Height; int height = source.Height;
int halfWidth = (int)Math.Ceiling(width * .5F); int halfWidth = (int)Math.Ceiling(width * .5F);
TColor[] target = PixelPool<TColor>.RentPixels(width * height); using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(width, height))
{
using (PixelAccessor<TColor> sourcePixels = source.Lock()) using (PixelAccessor<TColor> sourcePixels = source.Lock())
using (PixelAccessor<TColor> targetPixels = target.Lock(width, height))
{ {
Parallel.For( Parallel.For(
0, 0,
@ -109,7 +108,8 @@ namespace ImageSharp.Processing.Processors
}); });
} }
source.SetPixels(width, height, target); source.SwapPixelsBuffers(targetPixels);
}
} }
} }
} }

24
src/ImageSharp.Processing/Processors/Transforms/ResizeProcessor.cs

@ -65,19 +65,15 @@ namespace ImageSharp.Processing.Processors
int minY = Math.Max(0, startY); int minY = Math.Max(0, startY);
int maxY = Math.Min(height, endY); int maxY = Math.Min(height, endY);
TColor[] firstPass = null;
try
{
TColor[] target = PixelPool<TColor>.RentPixels(width * height);
if (this.Sampler is NearestNeighborResampler) if (this.Sampler is NearestNeighborResampler)
{ {
// Scaling factors // Scaling factors
float widthFactor = sourceRectangle.Width / (float)this.ResizeRectangle.Width; float widthFactor = sourceRectangle.Width / (float)this.ResizeRectangle.Width;
float heightFactor = sourceRectangle.Height / (float)this.ResizeRectangle.Height; float heightFactor = sourceRectangle.Height / (float)this.ResizeRectangle.Height;
using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(width, height))
{
using (PixelAccessor<TColor> sourcePixels = source.Lock()) using (PixelAccessor<TColor> sourcePixels = source.Lock())
using (PixelAccessor<TColor> targetPixels = target.Lock(width, height))
{ {
Parallel.For( Parallel.For(
minY, minY,
@ -97,18 +93,19 @@ namespace ImageSharp.Processing.Processors
} }
// Break out now. // Break out now.
source.SetPixels(width, height, target); source.SwapPixelsBuffers(targetPixels);
return; return;
} }
}
// Interpolate the image using the calculated weights. // Interpolate the image using the calculated weights.
// A 2-pass 1D algorithm appears to be faster than splitting a 1-pass 2D algorithm // A 2-pass 1D algorithm appears to be faster than splitting a 1-pass 2D algorithm
// First process the columns. Since we are not using multiple threads startY and endY // First process the columns. Since we are not using multiple threads startY and endY
// are the upper and lower bounds of the source rectangle. // are the upper and lower bounds of the source rectangle.
firstPass = PixelPool<TColor>.RentPixels(width * source.Height); using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(width, height))
{
using (PixelAccessor<TColor> sourcePixels = source.Lock()) using (PixelAccessor<TColor> sourcePixels = source.Lock())
using (PixelAccessor<TColor> firstPassPixels = firstPass.Lock(width, source.Height)) using (PixelAccessor<TColor> firstPassPixels = new PixelAccessor<TColor>(width, source.Height))
using (PixelAccessor<TColor> targetPixels = target.Lock(width, height))
{ {
Parallel.For( Parallel.For(
0, 0,
@ -164,12 +161,7 @@ namespace ImageSharp.Processing.Processors
}); });
} }
source.SetPixels(width, height, target); source.SwapPixelsBuffers(targetPixels);
}
finally
{
// We don't return target or source pixels as they are handled in the image itself.
PixelPool<TColor>.ReturnPixels(firstPass);
} }
} }
} }

28
src/ImageSharp.Processing/Processors/Transforms/RotateProcessor.cs

@ -42,10 +42,10 @@ namespace ImageSharp.Processing.Processors
int height = this.CanvasRectangle.Height; int height = this.CanvasRectangle.Height;
int width = this.CanvasRectangle.Width; int width = this.CanvasRectangle.Width;
Matrix3x2 matrix = this.GetCenteredMatrix(source, this.processMatrix); Matrix3x2 matrix = this.GetCenteredMatrix(source, this.processMatrix);
TColor[] target = PixelPool<TColor>.RentPixels(width * height);
using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(width, height))
{
using (PixelAccessor<TColor> sourcePixels = source.Lock()) using (PixelAccessor<TColor> sourcePixels = source.Lock())
using (PixelAccessor<TColor> targetPixels = target.Lock(width, height))
{ {
Parallel.For( Parallel.For(
0, 0,
@ -64,7 +64,8 @@ namespace ImageSharp.Processing.Processors
}); });
} }
source.SetPixels(width, height, target); source.SwapPixelsBuffers(targetPixels);
}
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -124,10 +125,10 @@ namespace ImageSharp.Processing.Processors
{ {
int width = source.Width; int width = source.Width;
int height = source.Height; int height = source.Height;
TColor[] target = PixelPool<TColor>.RentPixels(width * height);
using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(height, width))
{
using (PixelAccessor<TColor> sourcePixels = source.Lock()) using (PixelAccessor<TColor> sourcePixels = source.Lock())
using (PixelAccessor<TColor> targetPixels = target.Lock(height, width))
{ {
Parallel.For( Parallel.For(
0, 0,
@ -145,7 +146,8 @@ namespace ImageSharp.Processing.Processors
}); });
} }
source.SetPixels(height, width, target); source.SwapPixelsBuffers(targetPixels);
}
} }
/// <summary> /// <summary>
@ -156,10 +158,10 @@ namespace ImageSharp.Processing.Processors
{ {
int width = source.Width; int width = source.Width;
int height = source.Height; int height = source.Height;
TColor[] target = PixelPool<TColor>.RentPixels(width * height);
using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(width, height))
{
using (PixelAccessor<TColor> sourcePixels = source.Lock()) using (PixelAccessor<TColor> sourcePixels = source.Lock())
using (PixelAccessor<TColor> targetPixels = target.Lock(width, height))
{ {
Parallel.For( Parallel.For(
0, 0,
@ -176,7 +178,8 @@ namespace ImageSharp.Processing.Processors
}); });
} }
source.SetPixels(width, height, target); source.SwapPixelsBuffers(targetPixels);
}
} }
/// <summary> /// <summary>
@ -187,10 +190,10 @@ namespace ImageSharp.Processing.Processors
{ {
int width = source.Width; int width = source.Width;
int height = source.Height; int height = source.Height;
TColor[] target = PixelPool<TColor>.RentPixels(width * height);
using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(height, width))
{
using (PixelAccessor<TColor> sourcePixels = source.Lock()) using (PixelAccessor<TColor> sourcePixels = source.Lock())
using (PixelAccessor<TColor> targetPixels = target.Lock(height, width))
{ {
Parallel.For( Parallel.For(
0, 0,
@ -206,7 +209,8 @@ namespace ImageSharp.Processing.Processors
}); });
} }
source.SetPixels(height, width, target); source.SwapPixelsBuffers(targetPixels);
}
} }
} }
} }

7
src/ImageSharp.Processing/Processors/Transforms/SkewProcessor.cs

@ -42,10 +42,10 @@ namespace ImageSharp.Processing.Processors
int height = this.CanvasRectangle.Height; int height = this.CanvasRectangle.Height;
int width = this.CanvasRectangle.Width; int width = this.CanvasRectangle.Width;
Matrix3x2 matrix = this.GetCenteredMatrix(source, this.processMatrix); Matrix3x2 matrix = this.GetCenteredMatrix(source, this.processMatrix);
TColor[] target = PixelPool<TColor>.RentPixels(width * height);
using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(width, height))
{
using (PixelAccessor<TColor> sourcePixels = source.Lock()) using (PixelAccessor<TColor> sourcePixels = source.Lock())
using (PixelAccessor<TColor> targetPixels = target.Lock(width, height))
{ {
Parallel.For( Parallel.For(
0, 0,
@ -64,7 +64,8 @@ namespace ImageSharp.Processing.Processors
}); });
} }
source.SetPixels(width, height, target); source.SwapPixelsBuffers(targetPixels);
}
} }
/// <inheritdoc/> /// <inheritdoc/>

3
src/ImageSharp.Processing/project.json

@ -39,8 +39,7 @@
}, },
"dependencies": { "dependencies": {
"ImageSharp": { "ImageSharp": {
"target": "project", "target": "project"
"version": "1.0.0-alpha1"
}, },
"StyleCop.Analyzers": { "StyleCop.Analyzers": {
"version": "1.1.0-beta001", "version": "1.1.0-beta001",

29
src/ImageSharp/Image/IImageBase{TColor}.cs

@ -31,35 +31,6 @@ namespace ImageSharp
/// </exception> /// </exception>
void InitPixels(int width, int height); void InitPixels(int width, int height);
/// <summary>
/// Sets the pixel array of the image to the given value.
/// </summary>
/// <param name="width">The new width of the image. Must be greater than zero.</param>
/// <param name="height">The new height of the image. Must be greater than zero.</param>
/// <param name="pixels">The array with pixels. Must be a multiple of the width and height.</param>
/// <exception cref="System.ArgumentOutOfRangeException">
/// Thrown if either <paramref name="width"/> or <paramref name="height"/> are less than or equal to 0.
/// </exception>
/// <exception cref="System.ArgumentException">
/// Thrown if the <paramref name="pixels"/> length is not equal to Width * Height.
/// </exception>
void SetPixels(int width, int height, TColor[] pixels);
/// <summary>
/// Sets the pixel array of the image to the given value, creating a copy of
/// the original pixels.
/// </summary>
/// <param name="width">The new width of the image. Must be greater than zero.</param>
/// <param name="height">The new height of the image. Must be greater than zero.</param>
/// <param name="pixels">The array with pixels. Must be a multiple of four times the width and height.</param>
/// <exception cref="System.ArgumentOutOfRangeException">
/// Thrown if either <paramref name="width"/> or <paramref name="height"/> are less than or equal to 0.
/// </exception>
/// <exception cref="System.ArgumentException">
/// Thrown if the <paramref name="pixels"/> length is not equal to Width * Height.
/// </exception>
void ClonePixels(int width, int height, TColor[] pixels);
/// <summary> /// <summary>
/// Locks the image providing access to the pixels. /// Locks the image providing access to the pixels.
/// <remarks> /// <remarks>

53
src/ImageSharp/Image/ImageBase{TColor}.cs

@ -146,49 +146,28 @@ namespace ImageSharp
} }
/// <inheritdoc/> /// <inheritdoc/>
public void SetPixels(int width, int height, TColor[] pixels) public virtual PixelAccessor<TColor> Lock()
{
Guard.MustBeGreaterThan(width, 0, nameof(width));
Guard.MustBeGreaterThan(height, 0, nameof(height));
Guard.NotNull(pixels, nameof(pixels));
if (!(pixels.Length >= width * height))
{ {
throw new ArgumentException($"Pixel array must have the length of at least {width * height}."); return new PixelAccessor<TColor>(this);
}
this.Width = width;
this.Height = height;
this.ReturnPixels();
this.pixelBuffer = pixels;
} }
/// <inheritdoc/> /// <summary>
public void ClonePixels(int width, int height, TColor[] pixels) /// Switches the buffers used by the image and the PixelAccessor meaning that the Image will "own" the buffer from the PixelAccessor and the PixelAccessor will now own the Images buffer.
{ /// </summary>
Guard.MustBeGreaterThan(width, 0, nameof(width)); /// <param name="pixelSource">The pixel source.</param>
Guard.MustBeGreaterThan(height, 0, nameof(height)); internal void SwapPixelsBuffers(PixelAccessor<TColor> pixelSource)
Guard.NotNull(pixels, nameof(pixels));
if (!(pixels.Length >= width * height))
{ {
throw new ArgumentException($"Pixel array must have the length of at least {width * height}."); Guard.NotNull(pixelSource, nameof(pixelSource));
} Guard.IsTrue(pixelSource.PooledMemory, nameof(pixelSource.PooledMemory), "pixelSource must be using pooled memory");
this.Width = width; int newWidth = pixelSource.Width;
this.Height = height; int newHeight = pixelSource.Height;
// Copy the pixels. TODO: use Unsafe.Copy. // push my memory into the accessor (which in turn unpins the old puffer ready for the images use)
this.ReturnPixels(); TColor[] newPixels = pixelSource.ReturnCurrentPixelsAndReplaceThemInternally(this.Width, this.Height, this.pixelBuffer, true);
this.RentPixels(); this.Width = newWidth;
Array.Copy(pixels, this.pixelBuffer, width * height); this.Height = newHeight;
} this.pixelBuffer = newPixels;
/// <inheritdoc/>
public virtual PixelAccessor<TColor> Lock()
{
return new PixelAccessor<TColor>(this);
} }
/// <summary> /// <summary>

138
src/ImageSharp/Image/PixelAccessor{TColor}.cs

@ -44,6 +44,11 @@ namespace ImageSharp
/// </remarks> /// </remarks>
private bool isDisposed; private bool isDisposed;
/// <summary>
/// The pixels data
/// </summary>
private TColor[] pixels;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="PixelAccessor{TColor}"/> class. /// Initializes a new instance of the <see cref="PixelAccessor{TColor}"/> class.
/// </summary> /// </summary>
@ -54,13 +59,7 @@ namespace ImageSharp
Guard.MustBeGreaterThan(image.Width, 0, "image width"); Guard.MustBeGreaterThan(image.Width, 0, "image width");
Guard.MustBeGreaterThan(image.Height, 0, "image height"); Guard.MustBeGreaterThan(image.Height, 0, "image height");
this.Width = image.Width; this.SetPixelBufferUnsafe(image.Width, image.Height, image.Pixels, false);
this.Height = image.Height;
this.pixelsHandle = GCHandle.Alloc(image.Pixels, GCHandleType.Pinned);
this.dataPointer = this.pixelsHandle.AddrOfPinnedObject();
this.pixelsBase = (byte*)this.dataPointer.ToPointer();
this.PixelSize = Unsafe.SizeOf<TColor>();
this.RowStride = this.Width * this.PixelSize;
this.ParallelOptions = image.Configuration.ParallelOptions; this.ParallelOptions = image.Configuration.ParallelOptions;
} }
@ -71,6 +70,28 @@ namespace ImageSharp
/// <param name="height">The height of the image represented by the pixel buffer.</param> /// <param name="height">The height of the image represented by the pixel buffer.</param>
/// <param name="pixels">The pixel buffer.</param> /// <param name="pixels">The pixel buffer.</param>
public PixelAccessor(int width, int height, TColor[] pixels) public PixelAccessor(int width, int height, TColor[] pixels)
: this(width, height, pixels, false)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="PixelAccessor{TColor}"/> class.
/// </summary>
/// <param name="width">Gets the width of the image represented by the pixel buffer.</param>
/// <param name="height">The height of the image represented by the pixel buffer.</param>
public PixelAccessor(int width, int height)
: this(width, height, PixelPool<TColor>.RentPixels(width * height), true)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="PixelAccessor{TColor}" /> class.
/// </summary>
/// <param name="width">Gets the width of the image represented by the pixel buffer.</param>
/// <param name="height">The height of the image represented by the pixel buffer.</param>
/// <param name="pixels">The pixel buffer.</param>
/// <param name="pooledMemory">if set to <c>true</c> then the TColor[] is from the PixelPool{TColor} thus should be returned once disposed.</param>
private PixelAccessor(int width, int height, TColor[] pixels, bool pooledMemory)
{ {
Guard.NotNull(pixels, nameof(pixels)); Guard.NotNull(pixels, nameof(pixels));
Guard.MustBeGreaterThan(width, 0, nameof(width)); Guard.MustBeGreaterThan(width, 0, nameof(width));
@ -81,13 +102,8 @@ namespace ImageSharp
throw new ArgumentException($"Pixel array must have the length of at least {width * height}."); throw new ArgumentException($"Pixel array must have the length of at least {width * height}.");
} }
this.Width = width; this.SetPixelBufferUnsafe(width, height, pixels, pooledMemory);
this.Height = height;
this.pixelsHandle = GCHandle.Alloc(pixels, GCHandleType.Pinned);
this.dataPointer = this.pixelsHandle.AddrOfPinnedObject();
this.pixelsBase = (byte*)this.dataPointer.ToPointer();
this.PixelSize = Unsafe.SizeOf<TColor>();
this.RowStride = this.Width * this.PixelSize;
this.ParallelOptions = Configuration.Default.ParallelOptions; this.ParallelOptions = Configuration.Default.ParallelOptions;
} }
@ -99,6 +115,14 @@ namespace ImageSharp
this.Dispose(); this.Dispose();
} }
/// <summary>
/// Gets a value indicating whether [pooled memory].
/// </summary>
/// <value>
/// <c>true</c> if [pooled memory]; otherwise, <c>false</c>.
/// </value>
public bool PooledMemory { get; private set; }
/// <summary> /// <summary>
/// Gets the pointer to the pixel buffer. /// Gets the pointer to the pixel buffer.
/// </summary> /// </summary>
@ -107,22 +131,22 @@ namespace ImageSharp
/// <summary> /// <summary>
/// Gets the size of a single pixel in the number of bytes. /// Gets the size of a single pixel in the number of bytes.
/// </summary> /// </summary>
public int PixelSize { get; } public int PixelSize { get; private set; }
/// <summary> /// <summary>
/// Gets the width of one row in the number of bytes. /// Gets the width of one row in the number of bytes.
/// </summary> /// </summary>
public int RowStride { get; } public int RowStride { get; private set; }
/// <summary> /// <summary>
/// Gets the width of the image. /// Gets the width of the image.
/// </summary> /// </summary>
public int Width { get; } public int Width { get; private set; }
/// <summary> /// <summary>
/// Gets the height of the image. /// Gets the height of the image.
/// </summary> /// </summary>
public int Height { get; } public int Height { get; private set; }
/// <summary> /// <summary>
/// Gets the global parallel options for processing tasks in parallel. /// Gets the global parallel options for processing tasks in parallel.
@ -221,13 +245,7 @@ namespace ImageSharp
return; return;
} }
if (this.pixelsHandle.IsAllocated) this.UnPinPixels();
{
this.pixelsHandle.Free();
}
this.dataPointer = IntPtr.Zero;
this.pixelsBase = null;
// Note disposing is done. // Note disposing is done.
this.isDisposed = true; this.isDisposed = true;
@ -238,6 +256,12 @@ namespace ImageSharp
// and prevent finalization code for this object // and prevent finalization code for this object
// from executing a second time. // from executing a second time.
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
if (this.PooledMemory)
{
PixelPool<TColor>.ReturnPixels(this.pixels);
this.pixels = null;
}
} }
/// <summary> /// <summary>
@ -248,6 +272,22 @@ namespace ImageSharp
Unsafe.InitBlock(this.pixelsBase, 0, (uint)(this.RowStride * this.Height)); Unsafe.InitBlock(this.pixelsBase, 0, (uint)(this.RowStride * this.Height));
} }
/// <summary>
/// Sets the pixel buffer in an unsafe manor this should not be used unless you know what its doing!!!
/// </summary>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <param name="pixels">The pixels.</param>
/// <param name="pooledMemory">if set to <c>true</c> [pooled memory].</param>
/// <returns>Returns the old pixel data thats has gust been replaced.</returns>
/// <remarks>If PixelAccessor.PooledMemory is true then caller is responsible for ensuring PixelPool.ReturnPixels() is called.</remarks>
internal TColor[] ReturnCurrentPixelsAndReplaceThemInternally(int width, int height, TColor[] pixels, bool pooledMemory)
{
TColor[] oldPixels = this.pixels;
this.SetPixelBufferUnsafe(width, height, pixels, pooledMemory);
return oldPixels;
}
/// <summary> /// <summary>
/// Copies the pixels to another <see cref="PixelAccessor{TColor}"/> of the same size. /// Copies the pixels to another <see cref="PixelAccessor{TColor}"/> of the same size.
/// </summary> /// </summary>
@ -472,6 +512,54 @@ namespace ImageSharp
return this.pixelsBase + (((y * this.Width) + x) * Unsafe.SizeOf<TColor>()); return this.pixelsBase + (((y * this.Width) + x) * Unsafe.SizeOf<TColor>());
} }
/// <summary>
/// Sets the pixel buffer in an unsafe manor this should not be used unless you know what its doing!!!
/// </summary>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <param name="pixels">The pixels.</param>
/// <param name="pooledMemory">if set to <c>true</c> [pooled memory].</param>
private void SetPixelBufferUnsafe(int width, int height, TColor[] pixels, bool pooledMemory)
{
this.pixels = pixels;
this.PooledMemory = pooledMemory;
this.Width = width;
this.Height = height;
this.PinPixels();
this.PixelSize = Unsafe.SizeOf<TColor>();
this.RowStride = this.Width * this.PixelSize;
}
/// <summary>
/// Pins the pixels data.
/// </summary>
private void PinPixels()
{
// unpin any old pixels just incase
this.UnPinPixels();
this.pixelsHandle = GCHandle.Alloc(this.pixels, GCHandleType.Pinned);
this.dataPointer = this.pixelsHandle.AddrOfPinnedObject();
this.pixelsBase = (byte*)this.dataPointer.ToPointer();
}
/// <summary>
/// Unpins pixels data.
/// </summary>
private void UnPinPixels()
{
if (this.pixelsBase != null)
{
if (this.pixelsHandle.IsAllocated)
{
this.pixelsHandle.Free();
}
this.dataPointer = IntPtr.Zero;
this.pixelsBase = null;
}
}
/// <summary> /// <summary>
/// Copy an area of pixels to the image. /// Copy an area of pixels to the image.
/// </summary> /// </summary>

16
src/ImageSharp/Quantizers/Quantize.cs

@ -60,20 +60,26 @@ namespace ImageSharp
int pixelCount = quantized.Pixels.Length; int pixelCount = quantized.Pixels.Length;
int palleteCount = quantized.Palette.Length - 1; int palleteCount = quantized.Palette.Length - 1;
TColor[] pixels = new TColor[pixelCount];
using (PixelAccessor<TColor> pixels = new PixelAccessor<TColor>(quantized.Width, quantized.Height))
{
Parallel.For( Parallel.For(
0, 0,
pixelCount, pixels.Height,
source.Configuration.ParallelOptions, source.Configuration.ParallelOptions,
i => y =>
{
for (var x = 0; x < pixels.Width; x++)
{ {
var i = x + (y * pixels.Width);
TColor color = quantized.Palette[Math.Min(palleteCount, quantized.Pixels[i])]; TColor color = quantized.Palette[Math.Min(palleteCount, quantized.Pixels[i])];
pixels[i] = color; pixels[x, y] = color;
}
}); });
source.SetPixels(source.Width, source.Height, pixels); source.SwapPixelsBuffers(pixels);
return source; return source;
} }
} }
} }
}

18
tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs

@ -71,27 +71,30 @@ namespace ImageSharp.Tests
foreach (TestFile file in Files) foreach (TestFile file in Files)
{ {
using (Image image = file.CreateImage()) using (Image srcImage = file.CreateImage())
{
using (Image image = new Image(srcImage))
{ {
Color[] pixels = new Color[image.Width * image.Height];
Array.Copy(image.Pixels, pixels, image.Width * image.Height);
using (FileStream output = File.OpenWrite($"{path}/Octree-{file.FileName}")) using (FileStream output = File.OpenWrite($"{path}/Octree-{file.FileName}"))
{ {
image.Quantize(Quantization.Octree) image.Quantize(Quantization.Octree)
.Save(output, image.CurrentImageFormat); .Save(output, image.CurrentImageFormat);
} }
}
image.SetPixels(image.Width, image.Height, pixels); using (Image image = new Image(srcImage))
{
using (FileStream output = File.OpenWrite($"{path}/Wu-{file.FileName}")) using (FileStream output = File.OpenWrite($"{path}/Wu-{file.FileName}"))
{ {
image.Quantize(Quantization.Wu) image.Quantize(Quantization.Wu)
.Save(output, image.CurrentImageFormat); .Save(output, image.CurrentImageFormat);
} }
}
image.SetPixels(image.Width, image.Height, pixels); using (Image image = new Image(srcImage))
using (FileStream output = File.OpenWrite($"{path}/Palette-{file.FileName}")) {
using (FileStream output = File.OpenWrite($"{path}/Wu-{file.FileName}"))
{ {
image.Quantize(Quantization.Palette) image.Quantize(Quantization.Palette)
.Save(output, image.CurrentImageFormat); .Save(output, image.CurrentImageFormat);
@ -99,6 +102,7 @@ namespace ImageSharp.Tests
} }
} }
} }
}
[Fact] [Fact]
public void ImageCanConvertFormat() public void ImageCanConvertFormat()

Loading…
Cancel
Save