/// <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);
Example #2
0
        /// <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);
                    }
Example #4
0
        /// <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);
        }
Example #5
0
        /// <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);
            }
        }
Example #6
0
        /// <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);
            }
        }
Example #8
0
        /// <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);
            }
        }