diff --git a/src/ImageSharp.Processing/Processors/Convolution/Convolution2DProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/Convolution2DProcessor.cs index f77f1f439..cdea43e85 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/Convolution2DProcessor.cs +++ b/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.RentPixels(source.Width * source.Height); - using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor targetPixels = target.Lock(source.Width, source.Height)) + using (PixelAccessor targetPixels = new PixelAccessor(source.Width, source.Height)) { - Parallel.For( - startY, - endY, - this.ParallelOptions, - y => + using (PixelAccessor 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); + } } } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Processors/Convolution/Convolution2PassProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/Convolution2PassProcessor.cs index ca343b868..71b806261 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/Convolution2PassProcessor.cs +++ b/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.RentPixels(width * height); - TColor[] firstPass = PixelPool.RentPixels(width * height); - - try + using (PixelAccessor targetPixels = new PixelAccessor(width, height)) { - this.ApplyConvolution(width, height, firstPass, source.Pixels, sourceRectangle, kernelX); - this.ApplyConvolution(width, height, target, firstPass, sourceRectangle, kernelY); + using (PixelAccessor firstPassPixels = new PixelAccessor(width, height)) + using (PixelAccessor 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.ReturnPixels(firstPass); + source.SwapPixelsBuffers(targetPixels); } } @@ -67,13 +64,13 @@ namespace ImageSharp.Processing.Processors /// /// The image width. /// The image height. - /// The target pixels to apply the process to. - /// The source pixels. Cannot be null. + /// The target pixels to apply the process to. + /// The source pixels. Cannot be null. /// /// The structure that specifies the portion of the image object to draw. /// /// The kernel operator. - private void ApplyConvolution(int width, int height, TColor[] target, TColor[] source, Rectangle sourceRectangle, float[][] kernel) + private void ApplyConvolution(int width, int height, PixelAccessor targetPixels, PixelAccessor 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 sourcePixels = source.Lock(width, height)) - using (PixelAccessor 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; + } + }); } } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Processors/Convolution/ConvolutionProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/ConvolutionProcessor.cs index 17d7e2918..aa4940192 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/ConvolutionProcessor.cs +++ b/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 sourcePixels = source.Lock()) - using (PixelAccessor targetPixels = target.Lock(source.Width, source.Height)) + using (PixelAccessor targetPixels = new PixelAccessor(source.Width, source.Height)) { - Parallel.For( - startY, - endY, - this.ParallelOptions, - y => + using (PixelAccessor 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); + } } } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs index 5a1487761..1a88dbe34 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs +++ b/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 target = new Image(source.Width, source.Height); - target.ClonePixels(source.Width, source.Height, source.Pixels); - new ConvolutionProcessor(kernels[0]).Apply(target, sourceRectangle); - - if (kernels.Length == 1) + // we need a clean copy for each pass to start from + using (ImageBase cleanCopy = new Image(source)) { - return; - } + new ConvolutionProcessor(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 pass = new Image(source.Width, source.Height); - pass.ClonePixels(source.Width, source.Height, source.Pixels); + // Reset offset if necessary. + if (minX > 0) + { + shiftX = 0; + } - new ConvolutionProcessor(kernels[i]).Apply(pass, sourceRectangle); + if (minY > 0) + { + shiftY = 0; + } - using (PixelAccessor passPixels = pass.Lock()) - using (PixelAccessor 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 pass = new Image(cleanCopy)) + { + new ConvolutionProcessor(kernels[i]).Apply(pass, sourceRectangle); + + using (PixelAccessor passPixels = pass.Lock()) + using (PixelAccessor 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); } /// diff --git a/src/ImageSharp.Processing/Processors/Effects/OilPaintingProcessor.cs b/src/ImageSharp.Processing/Processors/Effects/OilPaintingProcessor.cs index 8d2340256..5c16af2f7 100644 --- a/src/ImageSharp.Processing/Processors/Effects/OilPaintingProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Effects/OilPaintingProcessor.cs @@ -67,91 +67,92 @@ namespace ImageSharp.Processing.Processors startX = 0; } - TColor[] target = PixelPool.RentPixels(source.Width * source.Height); - using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor targetPixels = target.Lock(source.Width, source.Height)) + using (PixelAccessor targetPixels = new PixelAccessor(source.Width, source.Height)) { - Parallel.For( - minY, - maxY, - this.ParallelOptions, - y => - { - for (int x = startX; x < endX; x++) + using (PixelAccessor 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); + } } } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Processors/Effects/PixelateProcessor.cs b/src/ImageSharp.Processing/Processors/Effects/PixelateProcessor.cs index d44858061..c197ce356 100644 --- a/src/ImageSharp.Processing/Processors/Effects/PixelateProcessor.cs +++ b/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 range = EnumerableExtensions.SteppedRange(minY, i => i < maxY, size); - TColor[] target = PixelPool.RentPixels(source.Width * source.Height); - using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor targetPixels = target.Lock(source.Width, source.Height)) + using (PixelAccessor targetPixels = new PixelAccessor(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 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); + } } } } diff --git a/src/ImageSharp.Processing/Processors/Transforms/CompandingResizeProcessor.cs b/src/ImageSharp.Processing/Processors/Transforms/CompandingResizeProcessor.cs index d9e4f6675..ac8a52321 100644 --- a/src/ImageSharp.Processing/Processors/Transforms/CompandingResizeProcessor.cs +++ b/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.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 targetPixels = new PixelAccessor(width, height)) + { using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor 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.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 targetPixels = new PixelAccessor(width, height)) + { using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor firstPassPixels = firstPass.Lock(width, source.Height)) - using (PixelAccessor targetPixels = target.Lock(width, height)) + using (PixelAccessor firstPassPixels = new PixelAccessor(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.ReturnPixels(firstPass); + source.SwapPixelsBuffers(targetPixels); } } } diff --git a/src/ImageSharp.Processing/Processors/Transforms/CropProcessor.cs b/src/ImageSharp.Processing/Processors/Transforms/CropProcessor.cs index 31bd08090..bdfbc496c 100644 --- a/src/ImageSharp.Processing/Processors/Transforms/CropProcessor.cs +++ b/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.RentPixels(this.CropRectangle.Width * this.CropRectangle.Height); - - using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor targetPixels = target.Lock(this.CropRectangle.Width, this.CropRectangle.Height)) + using (PixelAccessor targetPixels = new PixelAccessor(this.CropRectangle.Width, this.CropRectangle.Height)) { - Parallel.For( - minY, - maxY, - this.ParallelOptions, - y => - { - for (int x = minX; x < maxX; x++) + using (PixelAccessor 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); + } } } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Processors/Transforms/EntropyCropProcessor.cs b/src/ImageSharp.Processing/Processors/Transforms/EntropyCropProcessor.cs index e0c6e9b92..98297eed9 100644 --- a/src/ImageSharp.Processing/Processors/Transforms/EntropyCropProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Transforms/EntropyCropProcessor.cs @@ -36,24 +36,24 @@ namespace ImageSharp.Processing.Processors /// protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { - ImageBase temp = new Image(source.Width, source.Height); - temp.ClonePixels(source.Width, source.Height, source.Pixels); + using (ImageBase temp = new Image(source)) + { + // Detect the edges. + new SobelProcessor().Apply(temp, sourceRectangle); - // Detect the edges. - new SobelProcessor().Apply(temp, sourceRectangle); + // Apply threshold binarization filter. + new BinaryThresholdProcessor(this.Value).Apply(temp, sourceRectangle); - // Apply threshold binarization filter. - new BinaryThresholdProcessor(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(rectangle).Apply(source, sourceRectangle); } - - new CropProcessor(rectangle).Apply(source, sourceRectangle); } } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Processors/Transforms/FlipProcessor.cs b/src/ImageSharp.Processing/Processors/Transforms/FlipProcessor.cs index 374d54fa2..ad375ce0f 100644 --- a/src/ImageSharp.Processing/Processors/Transforms/FlipProcessor.cs +++ b/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.RentPixels(width * height); - - using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor targetPixels = target.Lock(width, height)) + using (PixelAccessor targetPixels = new PixelAccessor(width, height)) { - Parallel.For( - 0, - halfHeight, - this.ParallelOptions, - y => - { - for (int x = 0; x < width; x++) + using (PixelAccessor 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); + } } /// @@ -89,27 +89,27 @@ namespace ImageSharp.Processing.Processors int height = source.Height; int halfWidth = (int)Math.Ceiling(width * .5F); - TColor[] target = PixelPool.RentPixels(width * height); - - using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor targetPixels = target.Lock(width, height)) + using (PixelAccessor targetPixels = new PixelAccessor(width, height)) { - Parallel.For( - 0, - height, - this.ParallelOptions, - y => - { - for (int x = 0; x < halfWidth; x++) + using (PixelAccessor 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); + } } } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Processors/Transforms/ResizeProcessor.cs b/src/ImageSharp.Processing/Processors/Transforms/ResizeProcessor.cs index 108391713..f5d630808 100644 --- a/src/ImageSharp.Processing/Processors/Transforms/ResizeProcessor.cs +++ b/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.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 targetPixels = new PixelAccessor(width, height)) + { using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor 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.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 targetPixels = new PixelAccessor(width, height)) + { using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor firstPassPixels = firstPass.Lock(width, source.Height)) - using (PixelAccessor targetPixels = target.Lock(width, height)) + using (PixelAccessor firstPassPixels = new PixelAccessor(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.ReturnPixels(firstPass); + source.SwapPixelsBuffers(targetPixels); } } } diff --git a/src/ImageSharp.Processing/Processors/Transforms/RotateProcessor.cs b/src/ImageSharp.Processing/Processors/Transforms/RotateProcessor.cs index 313542adc..a5a762b91 100644 --- a/src/ImageSharp.Processing/Processors/Transforms/RotateProcessor.cs +++ b/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.RentPixels(width * height); - using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor targetPixels = target.Lock(width, height)) + using (PixelAccessor targetPixels = new PixelAccessor(width, height)) { - Parallel.For( - 0, - height, - this.ParallelOptions, - y => - { - for (int x = 0; x < width; x++) + using (PixelAccessor 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); + } } /// @@ -124,28 +125,29 @@ namespace ImageSharp.Processing.Processors { int width = source.Width; int height = source.Height; - TColor[] target = PixelPool.RentPixels(width * height); - using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor targetPixels = target.Lock(height, width)) + using (PixelAccessor targetPixels = new PixelAccessor(height, width)) { - Parallel.For( - 0, - height, - this.ParallelOptions, - y => - { - for (int x = 0; x < width; x++) + using (PixelAccessor 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); + } } /// @@ -156,27 +158,28 @@ namespace ImageSharp.Processing.Processors { int width = source.Width; int height = source.Height; - TColor[] target = PixelPool.RentPixels(width * height); - using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor targetPixels = target.Lock(width, height)) + using (PixelAccessor targetPixels = new PixelAccessor(width, height)) { - Parallel.For( - 0, - height, - this.ParallelOptions, - y => - { - for (int x = 0; x < width; x++) + using (PixelAccessor 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); + } } /// @@ -187,26 +190,27 @@ namespace ImageSharp.Processing.Processors { int width = source.Width; int height = source.Height; - TColor[] target = PixelPool.RentPixels(width * height); - using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor targetPixels = target.Lock(height, width)) + using (PixelAccessor targetPixels = new PixelAccessor(height, width)) { - Parallel.For( - 0, - height, - this.ParallelOptions, - y => - { - for (int x = 0; x < width; x++) + using (PixelAccessor 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); + } } } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Processors/Transforms/SkewProcessor.cs b/src/ImageSharp.Processing/Processors/Transforms/SkewProcessor.cs index d2d2a129d..4daa46491 100644 --- a/src/ImageSharp.Processing/Processors/Transforms/SkewProcessor.cs +++ b/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.RentPixels(width * height); - using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor targetPixels = target.Lock(width, height)) + using (PixelAccessor targetPixels = new PixelAccessor(width, height)) { - Parallel.For( - 0, - height, - this.ParallelOptions, - y => - { - for (int x = 0; x < width; x++) + using (PixelAccessor 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); + } } /// diff --git a/src/ImageSharp.Processing/project.json b/src/ImageSharp.Processing/project.json index 2ff224fa4..b375ca3a0 100644 --- a/src/ImageSharp.Processing/project.json +++ b/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", diff --git a/src/ImageSharp/Image/IImageBase{TColor}.cs b/src/ImageSharp/Image/IImageBase{TColor}.cs index 39f3fba67..66746c993 100644 --- a/src/ImageSharp/Image/IImageBase{TColor}.cs +++ b/src/ImageSharp/Image/IImageBase{TColor}.cs @@ -31,35 +31,6 @@ namespace ImageSharp /// void InitPixels(int width, int height); - /// - /// Sets the pixel array of the image to the given value. - /// - /// The new width of the image. Must be greater than zero. - /// The new height of the image. Must be greater than zero. - /// The array with pixels. Must be a multiple of the width and height. - /// - /// Thrown if either or are less than or equal to 0. - /// - /// - /// Thrown if the length is not equal to Width * Height. - /// - void SetPixels(int width, int height, TColor[] pixels); - - /// - /// Sets the pixel array of the image to the given value, creating a copy of - /// the original pixels. - /// - /// The new width of the image. Must be greater than zero. - /// The new height of the image. Must be greater than zero. - /// The array with pixels. Must be a multiple of four times the width and height. - /// - /// Thrown if either or are less than or equal to 0. - /// - /// - /// Thrown if the length is not equal to Width * Height. - /// - void ClonePixels(int width, int height, TColor[] pixels); - /// /// Locks the image providing access to the pixels. /// diff --git a/src/ImageSharp/Image/ImageBase{TColor}.cs b/src/ImageSharp/Image/ImageBase{TColor}.cs index 02bab249b..0cedb7aaa 100644 --- a/src/ImageSharp/Image/ImageBase{TColor}.cs +++ b/src/ImageSharp/Image/ImageBase{TColor}.cs @@ -146,49 +146,28 @@ namespace ImageSharp } /// - public void SetPixels(int width, int height, TColor[] pixels) + public virtual PixelAccessor 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(this); } - /// - 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. + /// + /// The pixel source. + internal void SwapPixelsBuffers(PixelAccessor 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; - /// - public virtual PixelAccessor Lock() - { - return new PixelAccessor(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; } /// diff --git a/src/ImageSharp/Image/PixelAccessor{TColor}.cs b/src/ImageSharp/Image/PixelAccessor{TColor}.cs index 60bf8de78..58cff55c8 100644 --- a/src/ImageSharp/Image/PixelAccessor{TColor}.cs +++ b/src/ImageSharp/Image/PixelAccessor{TColor}.cs @@ -44,6 +44,11 @@ namespace ImageSharp /// private bool isDisposed; + /// + /// The pixels data + /// + private TColor[] pixels; + /// /// Initializes a new instance of the class. /// @@ -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(); - 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 /// The height of the image represented by the pixel buffer. /// The pixel buffer. public PixelAccessor(int width, int height, TColor[] pixels) + : this(width, height, pixels, false) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Gets the width of the image represented by the pixel buffer. + /// The height of the image represented by the pixel buffer. + public PixelAccessor(int width, int height) + : this(width, height, PixelPool.RentPixels(width * height), true) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Gets the width of the image represented by the pixel buffer. + /// The height of the image represented by the pixel buffer. + /// The pixel buffer. + /// if set to true then the TColor[] is from the PixelPool{TColor} thus should be returned once disposed. + 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(); - 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(); } + /// + /// Gets a value indicating whether [pooled memory]. + /// + /// + /// true if [pooled memory]; otherwise, false. + /// + public bool PooledMemory { get; private set; } + /// /// Gets the pointer to the pixel buffer. /// @@ -107,22 +131,22 @@ namespace ImageSharp /// /// Gets the size of a single pixel in the number of bytes. /// - public int PixelSize { get; } + public int PixelSize { get; private set; } /// /// Gets the width of one row in the number of bytes. /// - public int RowStride { get; } + public int RowStride { get; private set; } /// /// Gets the width of the image. /// - public int Width { get; } + public int Width { get; private set; } /// /// Gets the height of the image. /// - public int Height { get; } + public int Height { get; private set; } /// /// 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.ReturnPixels(this.pixels); + this.pixels = null; + } } /// @@ -248,6 +272,22 @@ namespace ImageSharp Unsafe.InitBlock(this.pixelsBase, 0, (uint)(this.RowStride * this.Height)); } + /// + /// Sets the pixel buffer in an unsafe manor this should not be used unless you know what its doing!!! + /// + /// The width. + /// The height. + /// The pixels. + /// if set to true [pooled memory]. + /// Returns the old pixel data thats has gust been replaced. + /// If PixelAccessor.PooledMemory is true then caller is responsible for ensuring PixelPool.ReturnPixels() is called. + internal TColor[] ReturnCurrentPixelsAndReplaceThemInternally(int width, int height, TColor[] pixels, bool pooledMemory) + { + TColor[] oldPixels = this.pixels; + this.SetPixelBufferUnsafe(width, height, pixels, pooledMemory); + return oldPixels; + } + /// /// Copies the pixels to another of the same size. /// @@ -472,6 +512,54 @@ namespace ImageSharp return this.pixelsBase + (((y * this.Width) + x) * Unsafe.SizeOf()); } + /// + /// Sets the pixel buffer in an unsafe manor this should not be used unless you know what its doing!!! + /// + /// The width. + /// The height. + /// The pixels. + /// if set to true [pooled memory]. + 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(); + this.RowStride = this.Width * this.PixelSize; + } + + /// + /// Pins the pixels data. + /// + 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(); + } + + /// + /// Unpins pixels data. + /// + private void UnPinPixels() + { + if (this.pixelsBase != null) + { + if (this.pixelsHandle.IsAllocated) + { + this.pixelsHandle.Free(); + } + + this.dataPointer = IntPtr.Zero; + this.pixelsBase = null; + } + } + /// /// Copy an area of pixels to the image. /// diff --git a/src/ImageSharp/Quantizers/Quantize.cs b/src/ImageSharp/Quantizers/Quantize.cs index 7a42090c7..a03833b25 100644 --- a/src/ImageSharp/Quantizers/Quantize.cs +++ b/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 pixels = new PixelAccessor(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; + } } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs index fc74fe928..70163187c 100644 --- a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs +++ b/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); + } } } }