/// <inheritdoc/> protected override void OnFrameApply(ImageFrame <TPixel> source) { int brushSize = this.definition.BrushSize; if (brushSize <= 0 || brushSize > source.Height || brushSize > source.Width) { throw new ArgumentOutOfRangeException(nameof(brushSize)); } int startY = this.SourceRectangle.Y; int endY = this.SourceRectangle.Bottom; int startX = this.SourceRectangle.X; int endX = this.SourceRectangle.Right; int maxY = endY - 1; int maxX = endX - 1; int radius = brushSize >> 1; int levels = this.definition.Levels; int rowWidth = source.Width; int rectangleWidth = this.SourceRectangle.Width; Configuration configuration = this.Configuration; using Buffer2D <TPixel> targetPixels = this.Configuration.MemoryAllocator.Allocate2D <TPixel>(source.Size()); source.CopyTo(targetPixels); var workingRect = Rectangle.FromLTRB(startX, startY, endX, endY); ParallelHelper.IterateRows( workingRect, this.Configuration, (rows) => { /* Allocate the two temporary Vector4 buffers, one for the source row and one for the target row. * The ParallelHelper.IterateRowsWithTempBuffers overload is not used in this case because * the two allocated buffers have a length equal to the width of the source image, * and not just equal to the width of the target rectangle to process. * Furthermore, there are two buffers being allocated in this case, so using that overload would * have still required the explicit allocation of the secondary buffer. * Similarly, one temporary float buffer is also allocated from the pool, and that is used * to create the target bins for all the color channels being processed. * This buffer is only rented once outside of the main processing loop, and its contents * are cleared for each loop iteration, to avoid the repeated allocation for each processed pixel. */ using (IMemoryOwner <Vector4> sourceRowBuffer = configuration.MemoryAllocator.Allocate <Vector4>(rowWidth)) using (IMemoryOwner <Vector4> targetRowBuffer = configuration.MemoryAllocator.Allocate <Vector4>(rowWidth)) using (IMemoryOwner <float> bins = configuration.MemoryAllocator.Allocate <float>(levels * 4)) { Span <Vector4> sourceRowVector4Span = sourceRowBuffer.Memory.Span; Span <Vector4> sourceRowAreaVector4Span = sourceRowVector4Span.Slice(startX, rectangleWidth); Span <Vector4> targetRowVector4Span = targetRowBuffer.Memory.Span; Span <Vector4> targetRowAreaVector4Span = targetRowVector4Span.Slice(startX, rectangleWidth); ref float binsRef = ref bins.GetReference(); ref int intensityBinRef = ref Unsafe.As <float, int>(ref binsRef); ref float redBinRef = ref Unsafe.Add(ref binsRef, levels);
/// <inheritdoc/> protected override void OnFrameApply(ImageFrame <TPixel> source, Rectangle sourceRectangle, Configuration configuration) { using (Buffer2D <TPixel> firstPassPixels = configuration.MemoryAllocator.Allocate2D <TPixel>(source.Size())) { source.CopyTo(firstPassPixels); var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); this.ApplyConvolution(firstPassPixels, source.PixelBuffer, interest, this.KernelX, configuration); this.ApplyConvolution(source.PixelBuffer, firstPassPixels, interest, this.KernelY, configuration); } }
/// <inheritdoc/> protected override void OnFrameApply( ImageFrame <TPixel> source, Rectangle sourceRectangle, Configuration configuration) { DenseMatrix <float> matrixY = this.KernelY; DenseMatrix <float> matrixX = this.KernelX; var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); int startY = interest.Y; int endY = interest.Bottom; int startX = interest.X; int endX = interest.Right; int maxY = endY - 1; int maxX = endX - 1; using (Buffer2D <TPixel> targetPixels = configuration.MemoryAllocator.Allocate2D <TPixel>(source.Width, source.Height)) { source.CopyTo(targetPixels); var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY); int width = workingRectangle.Width; ParallelHelper.IterateRowsWithTempBuffer <Vector4>( workingRectangle, configuration, (rows, vectorBuffer) => { Span <Vector4> vectorSpan = vectorBuffer.Span; int length = vectorSpan.Length; for (int y = rows.Min; y < rows.Max; y++) { Span <TPixel> targetRowSpan = targetPixels.GetRowSpan(y).Slice(startX); PixelOperations <TPixel> .Instance.ToVector4(targetRowSpan, vectorSpan, length); for (int x = 0; x < width; x++) { DenseMatrixUtils.Convolve2D(in matrixY, in matrixX, source.PixelBuffer, vectorSpan, y, x, maxY, maxX, startX); } PixelOperations <TPixel> .Instance.PackFromVector4(vectorSpan, targetRowSpan, length); }
/// <inheritdoc/> protected override void OnFrameApply(ImageFrame <TPixel> source) { int brushSize = this.definition.BrushSize; if (brushSize <= 0 || brushSize > source.Height || brushSize > source.Width) { throw new ArgumentOutOfRangeException(nameof(brushSize)); } using Buffer2D <TPixel> targetPixels = this.Configuration.MemoryAllocator.Allocate2D <TPixel>(source.Size()); source.CopyTo(targetPixels); var operation = new RowIntervalOperation(this.SourceRectangle, targetPixels, source, this.Configuration, brushSize >> 1, this.definition.Levels); ParallelRowIterator.IterateRowIntervals( this.Configuration, this.SourceRectangle, in operation); Buffer2D <TPixel> .SwapOrCopyContent(source.PixelBuffer, targetPixels); }
/// <inheritdoc/> protected override void OnFrameApply(ImageFrame <TPixel> source, Rectangle sourceRectangle, Configuration configuration) { if (this.BrushSize <= 0 || this.BrushSize > source.Height || this.BrushSize > source.Width) { throw new ArgumentOutOfRangeException(nameof(this.BrushSize)); } int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; int startX = sourceRectangle.X; int endX = sourceRectangle.Right; int maxY = endY - 1; int maxX = endX - 1; int radius = this.BrushSize >> 1; int levels = this.Levels; using (Buffer2D <TPixel> targetPixels = configuration.MemoryAllocator.Allocate2D <TPixel>(source.Size())) { source.CopyTo(targetPixels); Parallel.For( startY, maxY, configuration.ParallelOptions, y => { Span <TPixel> sourceRow = source.GetPixelRowSpan(y); Span <TPixel> targetRow = targetPixels.GetRowSpan(y); for (int x = startX; x < endX; x++) { 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++) { int fyr = fy - radius; int offsetY = y + fyr; offsetY = offsetY.Clamp(0, maxY); Span <TPixel> sourceOffsetRow = source.GetPixelRowSpan(offsetY); for (int fx = 0; fx <= radius; fx++) { int fxr = fx - radius; int offsetX = x + fxr; offsetX = offsetX.Clamp(0, maxX); var vector = sourceOffsetRow[offsetX].ToVector4(); float sourceRed = vector.X; float sourceBlue = vector.Z; float sourceGreen = vector.Y; int currentIntensity = (int)MathF.Round((sourceBlue + sourceGreen + sourceRed) / 3F * (levels - 1)); intensityBin[currentIntensity]++; blueBin[currentIntensity] += sourceBlue; greenBin[currentIntensity] += sourceGreen; redBin[currentIntensity] += sourceRed; if (intensityBin[currentIntensity] > maxIntensity) { maxIntensity = intensityBin[currentIntensity]; maxIndex = currentIntensity; } } float red = MathF.Abs(redBin[maxIndex] / maxIntensity); float green = MathF.Abs(greenBin[maxIndex] / maxIntensity); float blue = MathF.Abs(blueBin[maxIndex] / maxIntensity); ref TPixel pixel = ref targetRow[x]; pixel.PackFromVector4(new Vector4(red, green, blue, sourceRow[x].ToVector4().W)); } } }); Buffer2D <TPixel> .SwapOrCopyContent(source.PixelBuffer, targetPixels); } }
/// <inheritdoc/> protected override void OnFrameApply(ImageFrame <TPixel> source, Rectangle sourceRectangle, Configuration configuration) { int kernelLength = this.KernelXY.Rows; int radius = kernelLength >> 1; int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; int startX = sourceRectangle.X; int endX = sourceRectangle.Right; int maxY = endY - 1; int maxX = endX - 1; using (Buffer2D <TPixel> targetPixels = configuration.MemoryAllocator.Allocate2D <TPixel>(source.Size())) { source.CopyTo(targetPixels); Parallel.For( startY, endY, configuration.ParallelOptions, y => { Span <TPixel> sourceRow = source.GetPixelRowSpan(y); Span <TPixel> targetRow = targetPixels.GetRowSpan(y); for (int x = startX; x < endX; x++) { float red = 0; float green = 0; float blue = 0; // Apply each matrix multiplier to the color components for each pixel. for (int fy = 0; fy < kernelLength; fy++) { int fyr = fy - radius; int offsetY = y + fyr; offsetY = offsetY.Clamp(0, maxY); Span <TPixel> sourceOffsetRow = source.GetPixelRowSpan(offsetY); for (int fx = 0; fx < kernelLength; fx++) { int fxr = fx - radius; int offsetX = x + fxr; offsetX = offsetX.Clamp(0, maxX); Vector4 currentColor = sourceOffsetRow[offsetX].ToVector4().Premultiply(); currentColor *= this.KernelXY[fy, fx]; red += currentColor.X; green += currentColor.Y; blue += currentColor.Z; } } ref TPixel pixel = ref targetRow[x]; pixel.PackFromVector4(new Vector4(red, green, blue, sourceRow[x].ToVector4().W).UnPremultiply()); } }); Buffer2D <TPixel> .SwapOrCopyContent(source.PixelBuffer, targetPixels); } }
/// <inheritdoc/> protected override void OnApply(ImageFrame <TPixel> source, Rectangle sourceRectangle, Configuration configuration) { int kernelYHeight = this.KernelY.Height; int kernelYWidth = this.KernelY.Width; int kernelXHeight = this.KernelX.Height; int kernelXWidth = this.KernelX.Width; int radiusY = kernelYHeight >> 1; int radiusX = kernelXWidth >> 1; int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; int startX = sourceRectangle.X; int endX = sourceRectangle.Right; int maxY = endY - 1; int maxX = endX - 1; using (var targetPixels = new PixelAccessor <TPixel>(source.Width, source.Height)) { source.CopyTo(targetPixels); Parallel.For( startY, endY, configuration.ParallelOptions, y => { Span <TPixel> sourceRow = source.GetPixelRowSpan(y); Span <TPixel> targetRow = targetPixels.GetRowSpan(y); for (int x = startX; x < endX; x++) { 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 fyr = fy - radiusY; int offsetY = y + fyr; offsetY = offsetY.Clamp(0, maxY); Span <TPixel> sourceOffsetRow = source.GetPixelRowSpan(offsetY); for (int fx = 0; fx < kernelXWidth; fx++) { int fxr = fx - radiusX; int offsetX = x + fxr; offsetX = offsetX.Clamp(0, maxX); var currentColor = sourceOffsetRow[offsetX].ToVector4(); if (fy < kernelXHeight) { Vector4 kx = this.KernelX[fy, fx] * currentColor; rX += kx.X; gX += kx.Y; bX += kx.Z; } if (fx < kernelYWidth) { Vector4 ky = this.KernelY[fy, fx] * currentColor; rY += ky.X; gY += ky.Y; bY += ky.Z; } } } float red = MathF.Sqrt((rX * rX) + (rY * rY)); float green = MathF.Sqrt((gX * gX) + (gY * gY)); float blue = MathF.Sqrt((bX * bX) + (bY * bY)); ref TPixel pixel = ref targetRow[x]; pixel.PackFromVector4(new Vector4(red, green, blue, sourceRow[x].ToVector4().W)); } }); source.SwapPixelsBuffers(targetPixels); } }
/// <inheritdoc/> protected override void OnFrameApply( ImageFrame <TPixel> source, Rectangle sourceRectangle, Configuration configuration) { int kernelYHeight = this.KernelY.Rows; int kernelYWidth = this.KernelY.Columns; int kernelXHeight = this.KernelX.Rows; int kernelXWidth = this.KernelX.Columns; int radiusY = kernelYHeight >> 1; int radiusX = kernelXWidth >> 1; int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; int startX = sourceRectangle.X; int endX = sourceRectangle.Right; int maxY = endY - 1; int maxX = endX - 1; using (Buffer2D <TPixel> targetPixels = configuration.MemoryAllocator.Allocate2D <TPixel>(source.Width, source.Height)) { source.CopyTo(targetPixels); var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY); ParallelHelper.IterateRows( workingRectangle, configuration, rows => { for (int y = rows.Min; y < rows.Max; y++) { Span <TPixel> sourceRow = source.GetPixelRowSpan(y); Span <TPixel> targetRow = targetPixels.GetRowSpan(y); for (int x = startX; x < endX; x++) { 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 fyr = fy - radiusY; int offsetY = y + fyr; offsetY = offsetY.Clamp(0, maxY); Span <TPixel> sourceOffsetRow = source.GetPixelRowSpan(offsetY); for (int fx = 0; fx < kernelXWidth; fx++) { int fxr = fx - radiusX; int offsetX = x + fxr; offsetX = offsetX.Clamp(0, maxX); Vector4 currentColor = sourceOffsetRow[offsetX].ToVector4().Premultiply(); if (fy < kernelXHeight) { Vector4 kx = this.KernelX[fy, fx] * currentColor; rX += kx.X; gX += kx.Y; bX += kx.Z; } if (fx < kernelYWidth) { Vector4 ky = this.KernelY[fy, fx] * currentColor; rY += ky.X; gY += ky.Y; bY += ky.Z; } } } float red = MathF.Sqrt((rX * rX) + (rY * rY)); float green = MathF.Sqrt((gX * gX) + (gY * gY)); float blue = MathF.Sqrt((bX * bX) + (bY * bY)); ref TPixel pixel = ref targetRow[x]; pixel.PackFromVector4( new Vector4(red, green, blue, sourceRow[x].ToVector4().W).UnPremultiply()); } } }); Buffer2D <TPixel> .SwapOrCopyContent(source.PixelBuffer, targetPixels); } }