Browse Source

Merge branch 'tocsoft/IDisposable'

af/merge-core
Scott Williams 9 years ago
parent
commit
4cae668559
  1. 109
      src/ImageSharp.Processing/Processors/Convolution/Convolution2DProcessor.cs
  2. 81
      src/ImageSharp.Processing/Processors/Convolution/Convolution2PassProcessor.cs
  3. 83
      src/ImageSharp.Processing/Processors/Convolution/ConvolutionProcessor.cs
  4. 94
      src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs
  5. 127
      src/ImageSharp.Processing/Processors/Effects/OilPaintingProcessor.cs
  6. 67
      src/ImageSharp.Processing/Processors/Effects/PixelateProcessor.cs
  7. 40
      src/ImageSharp.Processing/Processors/Transforms/CompandingResizeProcessor.cs
  8. 32
      src/ImageSharp.Processing/Processors/Transforms/CropProcessor.cs
  9. 26
      src/ImageSharp.Processing/Processors/Transforms/EntropyCropProcessor.cs
  10. 72
      src/ImageSharp.Processing/Processors/Transforms/FlipProcessor.cs
  11. 40
      src/ImageSharp.Processing/Processors/Transforms/ResizeProcessor.cs
  12. 140
      src/ImageSharp.Processing/Processors/Transforms/RotateProcessor.cs
  13. 35
      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. 30
      src/ImageSharp/Quantizers/Quantize.cs
  19. 34
      tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs

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

@ -54,73 +54,74 @@ namespace ImageSharp.Processing.Processors
int maxY = endY - 1;
int maxX = endX - 1;
TColor[] target = PixelPool<TColor>.RentPixels(source.Width * source.Height);
using (PixelAccessor<TColor> sourcePixels = source.Lock())
using (PixelAccessor<TColor> targetPixels = target.Lock(source.Width, source.Height))
using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(source.Width, source.Height))
{
Parallel.For(
startY,
endY,
this.ParallelOptions,
y =>
using (PixelAccessor<TColor> sourcePixels = source.Lock())
{
for (int x = startX; x < endX; x++)
Parallel.For(
startY,
endY,
this.ParallelOptions,
y =>
{
float rX = 0;
float gX = 0;
float bX = 0;
float rY = 0;
float gY = 0;
float bY = 0;
// Apply each matrix multiplier to the color components for each pixel.
for (int fy = 0; fy < kernelYHeight; fy++)
for (int x = startX; x < endX; x++)
{
int fyr = fy - radiusY;
int offsetY = y + fyr;
offsetY = offsetY.Clamp(0, maxY);
for (int fx = 0; fx < kernelXWidth; fx++)
float rX = 0;
float gX = 0;
float bX = 0;
float rY = 0;
float gY = 0;
float bY = 0;
// Apply each matrix multiplier to the color components for each pixel.
for (int fy = 0; fy < kernelYHeight; fy++)
{
int fxr = fx - radiusX;
int offsetX = x + fxr;
offsetX = offsetX.Clamp(0, maxX);
int fyr = fy - radiusY;
int offsetY = y + fyr;
Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4();
float r = currentColor.X;
float g = currentColor.Y;
float b = currentColor.Z;
if (fy < kernelXHeight)
{
rX += this.KernelX[fy][fx] * r;
gX += this.KernelX[fy][fx] * g;
bX += this.KernelX[fy][fx] * b;
}
offsetY = offsetY.Clamp(0, maxY);
if (fx < kernelYWidth)
for (int fx = 0; fx < kernelXWidth; fx++)
{
rY += this.KernelY[fy][fx] * r;
gY += this.KernelY[fy][fx] * g;
bY += this.KernelY[fy][fx] * b;
int fxr = fx - radiusX;
int offsetX = x + fxr;
offsetX = offsetX.Clamp(0, maxX);
Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4();
float r = currentColor.X;
float g = currentColor.Y;
float b = currentColor.Z;
if (fy < kernelXHeight)
{
rX += this.KernelX[fy][fx] * r;
gX += this.KernelX[fy][fx] * g;
bX += this.KernelX[fy][fx] * b;
}
if (fx < kernelYWidth)
{
rY += this.KernelY[fy][fx] * r;
gY += this.KernelY[fy][fx] * g;
bY += this.KernelY[fy][fx] * b;
}
}
}
}
float red = (float)Math.Sqrt((rX * rX) + (rY * rY));
float green = (float)Math.Sqrt((gX * gX) + (gY * gY));
float blue = (float)Math.Sqrt((bX * bX) + (bY * bY));
float red = (float)Math.Sqrt((rX * rX) + (rY * rY));
float green = (float)Math.Sqrt((gX * gX) + (gY * gY));
float blue = (float)Math.Sqrt((bX * bX) + (bY * bY));
TColor packed = default(TColor);
packed.PackFromVector4(new Vector4(red, green, blue, sourcePixels[x, y].ToVector4().W));
targetPixels[x, y] = packed;
}
});
}
TColor packed = default(TColor);
packed.PackFromVector4(new Vector4(red, green, blue, sourcePixels[x, y].ToVector4().W));
targetPixels[x, y] = packed;
}
});
}
source.SetPixels(source.Width, source.Height, target);
source.SwapPixelsBuffers(targetPixels);
}
}
}
}

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

@ -45,19 +45,16 @@ namespace ImageSharp.Processing.Processors
int width = source.Width;
int height = source.Height;
TColor[] target = PixelPool<TColor>.RentPixels(width * height);
TColor[] firstPass = PixelPool<TColor>.RentPixels(width * height);
try
using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(width, height))
{
this.ApplyConvolution(width, height, firstPass, source.Pixels, sourceRectangle, kernelX);
this.ApplyConvolution(width, height, target, firstPass, sourceRectangle, kernelY);
using (PixelAccessor<TColor> firstPassPixels = new PixelAccessor<TColor>(width, height))
using (PixelAccessor<TColor> sourcePixels = source.Lock())
{
this.ApplyConvolution(width, height, firstPassPixels, sourcePixels, sourceRectangle, kernelX);
this.ApplyConvolution(width, height, targetPixels, firstPassPixels, sourceRectangle, kernelY);
}
source.SetPixels(width, height, target);
}
finally
{
PixelPool<TColor>.ReturnPixels(firstPass);
source.SwapPixelsBuffers(targetPixels);
}
}
@ -67,13 +64,13 @@ namespace ImageSharp.Processing.Processors
/// </summary>
/// <param name="width">The image width.</param>
/// <param name="height">The image height.</param>
/// <param name="target">The target pixels to apply the process to.</param>
/// <param name="source">The source pixels. Cannot be null.</param>
/// <param name="targetPixels">The target pixels to apply the process to.</param>
/// <param name="sourcePixels">The source pixels. Cannot be null.</param>
/// <param name="sourceRectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
/// </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 kernelWidth = kernel[0].Length;
@ -87,45 +84,41 @@ namespace ImageSharp.Processing.Processors
int maxY = endY - 1;
int maxX = endX - 1;
using (PixelAccessor<TColor> sourcePixels = source.Lock(width, height))
using (PixelAccessor<TColor> targetPixels = target.Lock(width, height))
Parallel.For(
startY,
endY,
this.ParallelOptions,
y =>
{
Parallel.For(
startY,
endY,
this.ParallelOptions,
y =>
for (int x = startX; x < endX; x++)
{
for (int x = startX; x < endX; x++)
{
Vector4 destination = default(Vector4);
Vector4 destination = default(Vector4);
// Apply each matrix multiplier to the color components for each pixel.
for (int fy = 0; fy < kernelHeight; fy++)
{
int fyr = fy - radiusY;
int offsetY = y + fyr;
// Apply each matrix multiplier to the color components for each pixel.
for (int fy = 0; fy < kernelHeight; fy++)
{
int fyr = fy - radiusY;
int offsetY = y + fyr;
offsetY = offsetY.Clamp(0, maxY);
offsetY = offsetY.Clamp(0, maxY);
for (int fx = 0; fx < kernelWidth; fx++)
{
int fxr = fx - radiusX;
int offsetX = x + fxr;
for (int fx = 0; fx < kernelWidth; fx++)
{
int fxr = fx - radiusX;
int offsetX = x + fxr;
offsetX = offsetX.Clamp(0, maxX);
offsetX = offsetX.Clamp(0, maxX);
Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4();
destination += kernel[fy][fx] * currentColor;
}
Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4();
destination += kernel[fy][fx] * currentColor;
}
TColor packed = default(TColor);
packed.PackFromVector4(destination);
targetPixels[x, y] = packed;
}
});
}
TColor packed = default(TColor);
packed.PackFromVector4(destination);
targetPixels[x, y] = packed;
}
});
}
}
}

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

@ -44,60 +44,61 @@ namespace ImageSharp.Processing.Processors
int maxY = endY - 1;
int maxX = endX - 1;
TColor[] target = new TColor[source.Width * source.Height];
using (PixelAccessor<TColor> sourcePixels = source.Lock())
using (PixelAccessor<TColor> targetPixels = target.Lock<TColor>(source.Width, source.Height))
using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(source.Width, source.Height))
{
Parallel.For(
startY,
endY,
this.ParallelOptions,
y =>
using (PixelAccessor<TColor> sourcePixels = source.Lock())
{
for (int x = startX; x < endX; x++)
Parallel.For(
startY,
endY,
this.ParallelOptions,
y =>
{
float rX = 0;
float gX = 0;
float bX = 0;
// Apply each matrix multiplier to the color components for each pixel.
for (int fy = 0; fy < kernelLength; fy++)
for (int x = startX; x < endX; x++)
{
int fyr = fy - radius;
int offsetY = y + fyr;
offsetY = offsetY.Clamp(0, maxY);
float rX = 0;
float gX = 0;
float bX = 0;
for (int fx = 0; fx < kernelLength; fx++)
// Apply each matrix multiplier to the color components for each pixel.
for (int fy = 0; fy < kernelLength; fy++)
{
int fxr = fx - radius;
int offsetX = x + fxr;
int fyr = fy - radius;
int offsetY = y + fyr;
offsetY = offsetY.Clamp(0, maxY);
for (int fx = 0; fx < kernelLength; fx++)
{
int fxr = fx - radius;
int offsetX = x + fxr;
offsetX = offsetX.Clamp(0, maxX);
offsetX = offsetX.Clamp(0, maxX);
Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4();
float r = currentColor.X;
float g = currentColor.Y;
float b = currentColor.Z;
Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4();
float r = currentColor.X;
float g = currentColor.Y;
float b = currentColor.Z;
rX += kernelX[fy][fx] * r;
gX += kernelX[fy][fx] * g;
bX += kernelX[fy][fx] * b;
rX += kernelX[fy][fx] * r;
gX += kernelX[fy][fx] * g;
bX += kernelX[fy][fx] * b;
}
}
}
float red = rX;
float green = gX;
float blue = bX;
float red = rX;
float green = gX;
float blue = bX;
TColor packed = default(TColor);
packed.PackFromVector4(new Vector4(red, green, blue, sourcePixels[x, y].ToVector4().W));
targetPixels[x, y] = packed;
}
});
}
TColor packed = default(TColor);
packed.PackFromVector4(new Vector4(red, green, blue, sourcePixels[x, y].ToVector4().W));
targetPixels[x, y] = packed;
}
});
}
source.SetPixels(source.Width, source.Height, target);
source.SwapPixelsBuffers(targetPixels);
}
}
}
}

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

@ -75,64 +75,62 @@ namespace ImageSharp.Processing.Processors
int minY = Math.Max(0, startY);
int maxY = Math.Min(source.Height, endY);
// First run.
ImageBase<TColor> target = new Image<TColor>(source.Width, source.Height);
target.ClonePixels(source.Width, source.Height, source.Pixels);
new ConvolutionProcessor<TColor>(kernels[0]).Apply(target, sourceRectangle);
if (kernels.Length == 1)
// we need a clean copy for each pass to start from
using (ImageBase<TColor> cleanCopy = new Image<TColor>(source))
{
return;
}
new ConvolutionProcessor<TColor>(kernels[0]).Apply(source, sourceRectangle);
int shiftY = startY;
int shiftX = startX;
// Reset offset if necessary.
if (minX > 0)
{
shiftX = 0;
}
if (kernels.Length == 1)
{
return;
}
if (minY > 0)
{
shiftY = 0;
}
int shiftY = startY;
int shiftX = startX;
// Additional runs.
// ReSharper disable once ForCanBeConvertedToForeach
for (int i = 1; i < kernels.Length; i++)
{
// Create a clone for each pass and copy the offset pixels across.
ImageBase<TColor> pass = new Image<TColor>(source.Width, source.Height);
pass.ClonePixels(source.Width, source.Height, source.Pixels);
// Reset offset if necessary.
if (minX > 0)
{
shiftX = 0;
}
new ConvolutionProcessor<TColor>(kernels[i]).Apply(pass, sourceRectangle);
if (minY > 0)
{
shiftY = 0;
}
using (PixelAccessor<TColor> passPixels = pass.Lock())
using (PixelAccessor<TColor> targetPixels = target.Lock())
// Additional runs.
// ReSharper disable once ForCanBeConvertedToForeach
for (int i = 1; i < kernels.Length; i++)
{
Parallel.For(
minY,
maxY,
this.ParallelOptions,
y =>
using (ImageBase<TColor> pass = new Image<TColor>(cleanCopy))
{
new ConvolutionProcessor<TColor>(kernels[i]).Apply(pass, sourceRectangle);
using (PixelAccessor<TColor> passPixels = pass.Lock())
using (PixelAccessor<TColor> targetPixels = source.Lock())
{
int offsetY = y - shiftY;
for (int x = minX; x < maxX; x++)
{
int offsetX = x - shiftX;
// Grab the max components of the two pixels
TColor packed = default(TColor);
packed.PackFromVector4(Vector4.Max(passPixels[offsetX, offsetY].ToVector4(), targetPixels[offsetX, offsetY].ToVector4()));
targetPixels[offsetX, offsetY] = packed;
}
});
Parallel.For(
minY,
maxY,
this.ParallelOptions,
y =>
{
int offsetY = y - shiftY;
for (int x = minX; x < maxX; x++)
{
int offsetX = x - shiftX;
// Grab the max components of the two pixels
TColor packed = default(TColor);
packed.PackFromVector4(Vector4.Max(passPixels[offsetX, offsetY].ToVector4(), targetPixels[offsetX, offsetY].ToVector4()));
targetPixels[offsetX, offsetY] = packed;
}
});
}
}
}
}
source.SetPixels(source.Width, source.Height, target.Pixels);
}
/// <inheritdoc/>

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

@ -67,91 +67,92 @@ namespace ImageSharp.Processing.Processors
startX = 0;
}
TColor[] target = PixelPool<TColor>.RentPixels(source.Width * source.Height);
using (PixelAccessor<TColor> sourcePixels = source.Lock())
using (PixelAccessor<TColor> targetPixels = target.Lock(source.Width, source.Height))
using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(source.Width, source.Height))
{
Parallel.For(
minY,
maxY,
this.ParallelOptions,
y =>
{
for (int x = startX; x < endX; x++)
using (PixelAccessor<TColor> sourcePixels = source.Lock())
{
Parallel.For(
minY,
maxY,
this.ParallelOptions,
y =>
{
int maxIntensity = 0;
int maxIndex = 0;
int[] intensityBin = new int[levels];
float[] redBin = new float[levels];
float[] blueBin = new float[levels];
float[] greenBin = new float[levels];
for (int fy = 0; fy <= radius; fy++)
for (int x = startX; x < endX; x++)
{
int fyr = fy - radius;
int offsetY = y + fyr;
// Skip the current row
if (offsetY < minY)
{
continue;
}
int maxIntensity = 0;
int maxIndex = 0;
// Outwith the current bounds so break.
if (offsetY >= maxY)
{
break;
}
int[] intensityBin = new int[levels];
float[] redBin = new float[levels];
float[] blueBin = new float[levels];
float[] greenBin = new float[levels];
for (int fx = 0; fx <= radius; fx++)
for (int fy = 0; fy <= radius; fy++)
{
int fxr = fx - radius;
int offsetX = x + fxr;
int fyr = fy - radius;
int offsetY = y + fyr;
// Skip the column
if (offsetX < 0)
// Skip the current row
if (offsetY < minY)
{
continue;
}
if (offsetX < maxX)
// Outwith the current bounds so break.
if (offsetY >= maxY)
{
// ReSharper disable once AccessToDisposedClosure
Vector4 color = sourcePixels[offsetX, offsetY].ToVector4();
float sourceRed = color.X;
float sourceBlue = color.Z;
float sourceGreen = color.Y;
break;
}
int currentIntensity = (int)Math.Round((sourceBlue + sourceGreen + sourceRed) / 3.0 * (levels - 1));
for (int fx = 0; fx <= radius; fx++)
{
int fxr = fx - radius;
int offsetX = x + fxr;
intensityBin[currentIntensity] += 1;
blueBin[currentIntensity] += sourceBlue;
greenBin[currentIntensity] += sourceGreen;
redBin[currentIntensity] += sourceRed;
// Skip the column
if (offsetX < 0)
{
continue;
}
if (intensityBin[currentIntensity] > maxIntensity)
if (offsetX < maxX)
{
maxIntensity = intensityBin[currentIntensity];
maxIndex = currentIntensity;
// ReSharper disable once AccessToDisposedClosure
Vector4 color = sourcePixels[offsetX, offsetY].ToVector4();
float sourceRed = color.X;
float sourceBlue = color.Z;
float sourceGreen = color.Y;
int currentIntensity = (int)Math.Round((sourceBlue + sourceGreen + sourceRed) / 3.0 * (levels - 1));
intensityBin[currentIntensity] += 1;
blueBin[currentIntensity] += sourceBlue;
greenBin[currentIntensity] += sourceGreen;
redBin[currentIntensity] += sourceRed;
if (intensityBin[currentIntensity] > maxIntensity)
{
maxIntensity = intensityBin[currentIntensity];
maxIndex = currentIntensity;
}
}
}
}
float red = Math.Abs(redBin[maxIndex] / maxIntensity);
float green = Math.Abs(greenBin[maxIndex] / maxIntensity);
float blue = Math.Abs(blueBin[maxIndex] / maxIntensity);
float red = Math.Abs(redBin[maxIndex] / maxIntensity);
float green = Math.Abs(greenBin[maxIndex] / maxIntensity);
float blue = Math.Abs(blueBin[maxIndex] / maxIntensity);
TColor packed = default(TColor);
packed.PackFromVector4(new Vector4(red, green, blue, sourcePixels[x, y].ToVector4().W));
targetPixels[x, y] = packed;
TColor packed = default(TColor);
packed.PackFromVector4(new Vector4(red, green, blue, sourcePixels[x, y].ToVector4().W));
targetPixels[x, y] = packed;
}
}
}
});
}
});
}
source.SetPixels(source.Width, source.Height, target);
source.SwapPixelsBuffers(targetPixels);
}
}
}
}

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

@ -63,51 +63,52 @@ namespace ImageSharp.Processing.Processors
// Get the range on the y-plane to choose from.
IEnumerable<int> range = EnumerableExtensions.SteppedRange(minY, i => i < maxY, size);
TColor[] target = PixelPool<TColor>.RentPixels(source.Width * source.Height);
using (PixelAccessor<TColor> sourcePixels = source.Lock())
using (PixelAccessor<TColor> targetPixels = target.Lock(source.Width, source.Height))
using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(source.Width, source.Height))
{
Parallel.ForEach(
range,
this.ParallelOptions,
y =>
{
int offsetY = y - startY;
int offsetPy = offset;
for (int x = minX; x < maxX; x += size)
using (PixelAccessor<TColor> sourcePixels = source.Lock())
{
Parallel.ForEach(
range,
this.ParallelOptions,
y =>
{
int offsetX = x - startX;
int offsetPx = offset;
int offsetY = y - startY;
int offsetPy = offset;
// Make sure that the offset is within the boundary of the image.
while (offsetY + offsetPy >= maxY)
for (int x = minX; x < maxX; x += size)
{
offsetPy--;
}
int offsetX = x - startX;
int offsetPx = offset;
while (x + offsetPx >= maxX)
{
offsetPx--;
}
// Make sure that the offset is within the boundary of the image.
while (offsetY + offsetPy >= maxY)
{
offsetPy--;
}
// Get the pixel color in the centre of the soon to be pixelated area.
// ReSharper disable AccessToDisposedClosure
TColor pixel = sourcePixels[offsetX + offsetPx, offsetY + offsetPy];
while (x + offsetPx >= maxX)
{
offsetPx--;
}
// For each pixel in the pixelate size, set it to the centre color.
for (int l = offsetY; l < offsetY + size && l < maxY; l++)
{
for (int k = offsetX; k < offsetX + size && k < maxX; k++)
// Get the pixel color in the centre of the soon to be pixelated area.
// ReSharper disable AccessToDisposedClosure
TColor pixel = sourcePixels[offsetX + offsetPx, offsetY + offsetPy];
// For each pixel in the pixelate size, set it to the centre color.
for (int l = offsetY; l < offsetY + size && l < maxY; l++)
{
targetPixels[k, l] = pixel;
for (int k = offsetX; k < offsetX + size && k < maxX; k++)
{
targetPixels[k, l] = pixel;
}
}
}
}
});
});
source.SetPixels(source.Width, source.Height, target);
source.SwapPixelsBuffers(targetPixels);
}
}
}
}

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

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

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

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

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

@ -36,24 +36,24 @@ namespace ImageSharp.Processing.Processors
/// <inheritdoc/>
protected override void OnApply(ImageBase<TColor> source, Rectangle sourceRectangle)
{
ImageBase<TColor> temp = new Image<TColor>(source.Width, source.Height);
temp.ClonePixels(source.Width, source.Height, source.Pixels);
using (ImageBase<TColor> temp = new Image<TColor>(source))
{
// Detect the edges.
new SobelProcessor<TColor>().Apply(temp, sourceRectangle);
// Detect the edges.
new SobelProcessor<TColor>().Apply(temp, sourceRectangle);
// Apply threshold binarization filter.
new BinaryThresholdProcessor<TColor>(this.Value).Apply(temp, sourceRectangle);
// Apply threshold binarization filter.
new BinaryThresholdProcessor<TColor>(this.Value).Apply(temp, sourceRectangle);
// Search for the first white pixels
Rectangle rectangle = ImageMaths.GetFilteredBoundingRectangle(temp, 0);
// Search for the first white pixels
Rectangle rectangle = ImageMaths.GetFilteredBoundingRectangle(temp, 0);
if (rectangle == sourceRectangle)
{
return;
}
if (rectangle == sourceRectangle)
{
return;
new CropProcessor<TColor>(rectangle).Apply(source, sourceRectangle);
}
new CropProcessor<TColor>(rectangle).Apply(source, sourceRectangle);
}
}
}

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

@ -55,27 +55,27 @@ namespace ImageSharp.Processing.Processors
int height = source.Height;
int halfHeight = (int)Math.Ceiling(source.Height * .5F);
TColor[] target = PixelPool<TColor>.RentPixels(width * height);
using (PixelAccessor<TColor> sourcePixels = source.Lock())
using (PixelAccessor<TColor> targetPixels = target.Lock(width, height))
using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(width, height))
{
Parallel.For(
0,
halfHeight,
this.ParallelOptions,
y =>
{
for (int x = 0; x < width; x++)
using (PixelAccessor<TColor> sourcePixels = source.Lock())
{
Parallel.For(
0,
halfHeight,
this.ParallelOptions,
y =>
{
int newY = height - y - 1;
targetPixels[x, y] = sourcePixels[x, newY];
targetPixels[x, newY] = sourcePixels[x, y];
}
});
}
for (int x = 0; x < width; x++)
{
int newY = height - y - 1;
targetPixels[x, y] = sourcePixels[x, newY];
targetPixels[x, newY] = sourcePixels[x, y];
}
});
}
source.SetPixels(width, height, target);
source.SwapPixelsBuffers(targetPixels);
}
}
/// <summary>
@ -89,27 +89,27 @@ namespace ImageSharp.Processing.Processors
int height = source.Height;
int halfWidth = (int)Math.Ceiling(width * .5F);
TColor[] target = PixelPool<TColor>.RentPixels(width * height);
using (PixelAccessor<TColor> sourcePixels = source.Lock())
using (PixelAccessor<TColor> targetPixels = target.Lock(width, height))
using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(width, height))
{
Parallel.For(
0,
height,
this.ParallelOptions,
y =>
{
for (int x = 0; x < halfWidth; x++)
using (PixelAccessor<TColor> sourcePixels = source.Lock())
{
Parallel.For(
0,
height,
this.ParallelOptions,
y =>
{
int newX = width - x - 1;
targetPixels[x, y] = sourcePixels[newX, y];
targetPixels[newX, y] = sourcePixels[x, y];
}
});
}
for (int x = 0; x < halfWidth; x++)
{
int newX = width - x - 1;
targetPixels[x, y] = sourcePixels[newX, y];
targetPixels[newX, y] = sourcePixels[x, y];
}
});
}
source.SetPixels(width, height, target);
source.SwapPixelsBuffers(targetPixels);
}
}
}
}

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

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

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

@ -42,29 +42,30 @@ namespace ImageSharp.Processing.Processors
int height = this.CanvasRectangle.Height;
int width = this.CanvasRectangle.Width;
Matrix3x2 matrix = this.GetCenteredMatrix(source, this.processMatrix);
TColor[] target = PixelPool<TColor>.RentPixels(width * height);
using (PixelAccessor<TColor> sourcePixels = source.Lock())
using (PixelAccessor<TColor> targetPixels = target.Lock(width, height))
using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(width, height))
{
Parallel.For(
0,
height,
this.ParallelOptions,
y =>
{
for (int x = 0; x < width; x++)
using (PixelAccessor<TColor> sourcePixels = source.Lock())
{
Parallel.For(
0,
height,
this.ParallelOptions,
y =>
{
Point transformedPoint = Point.Rotate(new Point(x, y), matrix);
if (source.Bounds.Contains(transformedPoint.X, transformedPoint.Y))
for (int x = 0; x < width; x++)
{
targetPixels[x, y] = sourcePixels[transformedPoint.X, transformedPoint.Y];
Point transformedPoint = Point.Rotate(new Point(x, y), matrix);
if (source.Bounds.Contains(transformedPoint.X, transformedPoint.Y))
{
targetPixels[x, y] = sourcePixels[transformedPoint.X, transformedPoint.Y];
}
}
}
});
}
});
}
source.SetPixels(width, height, target);
source.SwapPixelsBuffers(targetPixels);
}
}
/// <inheritdoc/>
@ -124,28 +125,29 @@ namespace ImageSharp.Processing.Processors
{
int width = source.Width;
int height = source.Height;
TColor[] target = PixelPool<TColor>.RentPixels(width * height);
using (PixelAccessor<TColor> sourcePixels = source.Lock())
using (PixelAccessor<TColor> targetPixels = target.Lock(height, width))
using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(height, width))
{
Parallel.For(
0,
height,
this.ParallelOptions,
y =>
{
for (int x = 0; x < width; x++)
using (PixelAccessor<TColor> sourcePixels = source.Lock())
{
Parallel.For(
0,
height,
this.ParallelOptions,
y =>
{
int newX = height - y - 1;
newX = height - newX - 1;
int newY = width - x - 1;
targetPixels[newX, newY] = sourcePixels[x, y];
}
});
}
for (int x = 0; x < width; x++)
{
int newX = height - y - 1;
newX = height - newX - 1;
int newY = width - x - 1;
targetPixels[newX, newY] = sourcePixels[x, y];
}
});
}
source.SetPixels(height, width, target);
source.SwapPixelsBuffers(targetPixels);
}
}
/// <summary>
@ -156,27 +158,28 @@ namespace ImageSharp.Processing.Processors
{
int width = source.Width;
int height = source.Height;
TColor[] target = PixelPool<TColor>.RentPixels(width * height);
using (PixelAccessor<TColor> sourcePixels = source.Lock())
using (PixelAccessor<TColor> targetPixels = target.Lock(width, height))
using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(width, height))
{
Parallel.For(
0,
height,
this.ParallelOptions,
y =>
{
for (int x = 0; x < width; x++)
using (PixelAccessor<TColor> sourcePixels = source.Lock())
{
Parallel.For(
0,
height,
this.ParallelOptions,
y =>
{
int newX = width - x - 1;
int newY = height - y - 1;
targetPixels[newX, newY] = sourcePixels[x, y];
}
});
}
for (int x = 0; x < width; x++)
{
int newX = width - x - 1;
int newY = height - y - 1;
targetPixels[newX, newY] = sourcePixels[x, y];
}
});
}
source.SetPixels(width, height, target);
source.SwapPixelsBuffers(targetPixels);
}
}
/// <summary>
@ -187,26 +190,27 @@ namespace ImageSharp.Processing.Processors
{
int width = source.Width;
int height = source.Height;
TColor[] target = PixelPool<TColor>.RentPixels(width * height);
using (PixelAccessor<TColor> sourcePixels = source.Lock())
using (PixelAccessor<TColor> targetPixels = target.Lock(height, width))
using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(height, width))
{
Parallel.For(
0,
height,
this.ParallelOptions,
y =>
{
for (int x = 0; x < width; x++)
using (PixelAccessor<TColor> sourcePixels = source.Lock())
{
Parallel.For(
0,
height,
this.ParallelOptions,
y =>
{
int newX = height - y - 1;
targetPixels[newX, x] = sourcePixels[x, y];
}
});
}
for (int x = 0; x < width; x++)
{
int newX = height - y - 1;
targetPixels[newX, x] = sourcePixels[x, y];
}
});
}
source.SetPixels(height, width, target);
source.SwapPixelsBuffers(targetPixels);
}
}
}
}

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

@ -42,29 +42,30 @@ namespace ImageSharp.Processing.Processors
int height = this.CanvasRectangle.Height;
int width = this.CanvasRectangle.Width;
Matrix3x2 matrix = this.GetCenteredMatrix(source, this.processMatrix);
TColor[] target = PixelPool<TColor>.RentPixels(width * height);
using (PixelAccessor<TColor> sourcePixels = source.Lock())
using (PixelAccessor<TColor> targetPixels = target.Lock(width, height))
using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(width, height))
{
Parallel.For(
0,
height,
this.ParallelOptions,
y =>
{
for (int x = 0; x < width; x++)
using (PixelAccessor<TColor> sourcePixels = source.Lock())
{
Parallel.For(
0,
height,
this.ParallelOptions,
y =>
{
Point transformedPoint = Point.Skew(new Point(x, y), matrix);
if (source.Bounds.Contains(transformedPoint.X, transformedPoint.Y))
for (int x = 0; x < width; x++)
{
targetPixels[x, y] = sourcePixels[transformedPoint.X, transformedPoint.Y];
Point transformedPoint = Point.Skew(new Point(x, y), matrix);
if (source.Bounds.Contains(transformedPoint.X, transformedPoint.Y))
{
targetPixels[x, y] = sourcePixels[transformedPoint.X, transformedPoint.Y];
}
}
}
});
}
});
}
source.SetPixels(width, height, target);
source.SwapPixelsBuffers(targetPixels);
}
}
/// <inheritdoc/>

3
src/ImageSharp.Processing/project.json

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

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

@ -31,35 +31,6 @@ namespace ImageSharp
/// </exception>
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>
/// Locks the image providing access to the pixels.
/// <remarks>

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

@ -146,49 +146,28 @@ namespace ImageSharp
}
/// <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}.");
}
this.Width = width;
this.Height = height;
this.ReturnPixels();
this.pixelBuffer = pixels;
return new PixelAccessor<TColor>(this);
}
/// <inheritdoc/>
public void ClonePixels(int width, int height, TColor[] pixels)
/// <summary>
/// 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>
/// <param name="pixelSource">The pixel source.</param>
internal void SwapPixelsBuffers(PixelAccessor<TColor> pixelSource)
{
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}.");
}
this.Width = width;
this.Height = height;
Guard.NotNull(pixelSource, nameof(pixelSource));
Guard.IsTrue(pixelSource.PooledMemory, nameof(pixelSource.PooledMemory), "pixelSource must be using pooled memory");
// Copy the pixels. TODO: use Unsafe.Copy.
this.ReturnPixels();
this.RentPixels();
Array.Copy(pixels, this.pixelBuffer, width * height);
}
int newWidth = pixelSource.Width;
int newHeight = pixelSource.Height;
/// <inheritdoc/>
public virtual PixelAccessor<TColor> Lock()
{
return new PixelAccessor<TColor>(this);
// push my memory into the accessor (which in turn unpins the old puffer ready for the images use)
TColor[] newPixels = pixelSource.ReturnCurrentPixelsAndReplaceThemInternally(this.Width, this.Height, this.pixelBuffer, true);
this.Width = newWidth;
this.Height = newHeight;
this.pixelBuffer = newPixels;
}
/// <summary>

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

@ -44,6 +44,11 @@ namespace ImageSharp
/// </remarks>
private bool isDisposed;
/// <summary>
/// The pixels data
/// </summary>
private TColor[] pixels;
/// <summary>
/// Initializes a new instance of the <see cref="PixelAccessor{TColor}"/> class.
/// </summary>
@ -54,13 +59,7 @@ namespace ImageSharp
Guard.MustBeGreaterThan(image.Width, 0, "image width");
Guard.MustBeGreaterThan(image.Height, 0, "image height");
this.Width = image.Width;
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.SetPixelBufferUnsafe(image.Width, image.Height, image.Pixels, false);
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="pixels">The pixel buffer.</param>
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.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}.");
}
this.Width = width;
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.SetPixelBufferUnsafe(width, height, pixels, pooledMemory);
this.ParallelOptions = Configuration.Default.ParallelOptions;
}
@ -99,6 +115,14 @@ namespace ImageSharp
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>
/// Gets the pointer to the pixel buffer.
/// </summary>
@ -107,22 +131,22 @@ namespace ImageSharp
/// <summary>
/// Gets the size of a single pixel in the number of bytes.
/// </summary>
public int PixelSize { get; }
public int PixelSize { get; private set; }
/// <summary>
/// Gets the width of one row in the number of bytes.
/// </summary>
public int RowStride { get; }
public int RowStride { get; private set; }
/// <summary>
/// Gets the width of the image.
/// </summary>
public int Width { get; }
public int Width { get; private set; }
/// <summary>
/// Gets the height of the image.
/// </summary>
public int Height { get; }
public int Height { get; private set; }
/// <summary>
/// Gets the global parallel options for processing tasks in parallel.
@ -221,13 +245,7 @@ namespace ImageSharp
return;
}
if (this.pixelsHandle.IsAllocated)
{
this.pixelsHandle.Free();
}
this.dataPointer = IntPtr.Zero;
this.pixelsBase = null;
this.UnPinPixels();
// Note disposing is done.
this.isDisposed = true;
@ -238,6 +256,12 @@ namespace ImageSharp
// and prevent finalization code for this object
// from executing a second time.
GC.SuppressFinalize(this);
if (this.PooledMemory)
{
PixelPool<TColor>.ReturnPixels(this.pixels);
this.pixels = null;
}
}
/// <summary>
@ -248,6 +272,22 @@ namespace ImageSharp
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>
/// Copies the pixels to another <see cref="PixelAccessor{TColor}"/> of the same size.
/// </summary>
@ -472,6 +512,54 @@ namespace ImageSharp
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>
/// Copy an area of pixels to the image.
/// </summary>

30
src/ImageSharp/Quantizers/Quantize.cs

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

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

@ -71,30 +71,34 @@ namespace ImageSharp.Tests
foreach (TestFile file in Files)
{
using (Image image = file.CreateImage())
using (Image srcImage = file.CreateImage())
{
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 (Image image = new Image(srcImage))
{
image.Quantize(Quantization.Octree)
.Save(output, image.CurrentImageFormat);
using (FileStream output = File.OpenWrite($"{path}/Octree-{file.FileName}"))
{
image.Quantize(Quantization.Octree)
.Save(output, image.CurrentImageFormat);
}
}
image.SetPixels(image.Width, image.Height, pixels);
using (FileStream output = File.OpenWrite($"{path}/Wu-{file.FileName}"))
using (Image image = new Image(srcImage))
{
image.Quantize(Quantization.Wu)
.Save(output, image.CurrentImageFormat);
using (FileStream output = File.OpenWrite($"{path}/Wu-{file.FileName}"))
{
image.Quantize(Quantization.Wu)
.Save(output, image.CurrentImageFormat);
}
}
image.SetPixels(image.Width, image.Height, pixels);
using (FileStream output = File.OpenWrite($"{path}/Palette-{file.FileName}"))
using (Image image = new Image(srcImage))
{
image.Quantize(Quantization.Palette)
.Save(output, image.CurrentImageFormat);
using (FileStream output = File.OpenWrite($"{path}/Wu-{file.FileName}"))
{
image.Quantize(Quantization.Palette)
.Save(output, image.CurrentImageFormat);
}
}
}
}

Loading…
Cancel
Save