public void IterateRowsWithTempBuffer_WithEffectiveMinimumPixelsLimit(
            int maxDegreeOfParallelism,
            int minimumPixelsProcessedPerTask,
            int width,
            int height,
            int expectedNumberOfSteps,
            int expectedStepLength,
            int expectedLastStepLength)
        {
            var parallelSettings = new ParallelExecutionSettings(
                maxDegreeOfParallelism,
                minimumPixelsProcessedPerTask,
                Configuration.Default.MemoryAllocator);

            var rectangle = new Rectangle(0, 0, width, height);

            int actualNumberOfSteps = 0;

            ParallelHelper.IterateRowsWithTempBuffer(
                rectangle,
                parallelSettings,
                (RowInterval rows, Memory <Vector4> buffer) =>
            {
                Assert.True(rows.Min >= 0);
                Assert.True(rows.Max <= height);

                int step     = rows.Max - rows.Min;
                int expected = rows.Max < height ? expectedStepLength : expectedLastStepLength;

                Interlocked.Increment(ref actualNumberOfSteps);
                Assert.Equal(expected, step);
            });

            Assert.Equal(expectedNumberOfSteps, actualNumberOfSteps);
        }
Example #2
0
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> source, Rectangle sourceRectangle, Configuration configuration)
        {
            var interest = Rectangle.Intersect(sourceRectangle, source.Bounds());
            int startX   = interest.X;

            ColorMatrix matrix = this.definition.Matrix;

            ParallelHelper.IterateRowsWithTempBuffer <Vector4>(
                interest,
                configuration,
                (rows, vectorBuffer) =>
            {
                for (int y = rows.Min; y < rows.Max; y++)
                {
                    Span <Vector4> vectorSpan = vectorBuffer.Span;
                    int length            = vectorSpan.Length;
                    Span <TPixel> rowSpan = source.GetPixelRowSpan(y).Slice(startX, length);
                    PixelOperations <TPixel> .Instance.ToVector4(configuration, rowSpan, vectorSpan);

                    Vector4Utils.Transform(vectorSpan, ref matrix);

                    PixelOperations <TPixel> .Instance.FromVector4Destructive(configuration, vectorSpan, rowSpan);
                }
            });
        }
        public void IterateRowsWithTempBuffer_OverMinimumPixelsLimit_ShouldVisitAllRows(
            int maxDegreeOfParallelism,
            int minY,
            int maxY,
            int expectedStepLength,
            int expectedLastStepLength)
        {
            var parallelSettings = new ParallelExecutionSettings(
                maxDegreeOfParallelism,
                1,
                Configuration.Default.MemoryAllocator);

            var rectangle = new Rectangle(0, minY, 10, maxY - minY);

            int[] expectedData = Enumerable.Repeat(0, minY).Concat(Enumerable.Range(minY, maxY - minY)).ToArray();
            var   actualData   = new int[maxY];

            ParallelHelper.IterateRowsWithTempBuffer(
                rectangle,
                parallelSettings,
                (RowInterval rows, Memory <Vector4> buffer) =>
            {
                for (int y = rows.Min; y < rows.Max; y++)
                {
                    actualData[y] = y;
                }
            });

            Assert.Equal(expectedData, actualData);
        }
Example #4
0
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> source)
        {
            var                      interest      = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
            int                      startX        = interest.X;
            Configuration            configuration = this.Configuration;
            PixelConversionModifiers modifiers     = this.modifiers;

            ParallelHelper.IterateRowsWithTempBuffer <Vector4>(
                interest,
                this.Configuration,
                (rows, vectorBuffer) =>
            {
                for (int y = rows.Min; y < rows.Max; y++)
                {
                    Span <Vector4> vectorSpan = vectorBuffer.Span;
                    int length            = vectorSpan.Length;
                    Span <TPixel> rowSpan = source.GetPixelRowSpan(y).Slice(startX, length);
                    PixelOperations <TPixel> .Instance.ToVector4(configuration, rowSpan, vectorSpan, modifiers);

                    // Run the user defined pixel shader on the current row of pixels
                    this.ApplyPixelShader(vectorSpan, new Point(startX, y));

                    PixelOperations <TPixel> .Instance.FromVector4Destructive(configuration, vectorSpan, rowSpan, modifiers);
                }
            });
        }
        public void IterateRowsWithTempBufferRequiresValidRectangle(int width, int height)
        {
            var parallelSettings = default(ParallelExecutionSettings);

            var rect = new Rectangle(0, 0, width, height);

            ArgumentOutOfRangeException ex = Assert.Throws <ArgumentOutOfRangeException>(
                () => ParallelHelper.IterateRowsWithTempBuffer <Rgba32>(rect, parallelSettings, (rows, memory) => { }));

            Assert.Contains(width <= 0 ? "Width" : "Height", ex.Message);
        }
        /// <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);
                    }
        public void IterateRowsWithTempBuffer_OverMinimumPixelsLimit(
            int maxDegreeOfParallelism,
            int minY,
            int maxY,
            int expectedStepLength,
            int expectedLastStepLength)
        {
            var parallelSettings = new ParallelExecutionSettings(
                maxDegreeOfParallelism,
                1,
                Configuration.Default.MemoryAllocator);

            var rectangle = new Rectangle(0, minY, 10, maxY - minY);

            var bufferHashes = new ConcurrentBag <int>();

            int actualNumberOfSteps = 0;

            ParallelHelper.IterateRowsWithTempBuffer(
                rectangle,
                parallelSettings,
                (RowInterval rows, Memory <Vector4> buffer) =>
            {
                Assert.True(rows.Min >= minY);
                Assert.True(rows.Max <= maxY);

                bufferHashes.Add(buffer.GetHashCode());

                int step     = rows.Max - rows.Min;
                int expected = rows.Max < maxY ? expectedStepLength : expectedLastStepLength;

                Interlocked.Increment(ref actualNumberOfSteps);
                Assert.Equal(expected, step);
            });

            Assert.Equal(maxDegreeOfParallelism, actualNumberOfSteps);

            int numberOfDifferentBuffers = bufferHashes.Distinct().Count();

            Assert.Equal(actualNumberOfSteps, numberOfDifferentBuffers);
        }
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> source)
        {
            // TODO: can we simplify the rectangle calculation?
            int     startY    = this.SourceRectangle.Y;
            int     endY      = this.SourceRectangle.Bottom;
            int     startX    = this.SourceRectangle.X;
            int     endX      = this.SourceRectangle.Right;
            TPixel  glowColor = this.definition.GlowColor.ToPixel <TPixel>();
            Vector2 center    = Rectangle.Center(this.SourceRectangle);

            float finalRadius = this.definition.Radius.Calculate(source.Size());

            float maxDistance = finalRadius > 0
                                    ? MathF.Min(finalRadius, this.SourceRectangle.Width * .5F)
                                    : this.SourceRectangle.Width * .5F;

            // Align start/end positions.
            int minX = Math.Max(0, startX);
            int maxX = Math.Min(source.Width, endX);
            int minY = Math.Max(0, startY);
            int maxY = Math.Min(source.Height, endY);

            // Reset offset if necessary.
            if (minX > 0)
            {
                startX = 0;
            }

            if (minY > 0)
            {
                startY = 0;
            }

            int width   = maxX - minX;
            int offsetX = minX - startX;

            var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY);

            float           blendPercentage = this.definition.GraphicsOptions.BlendPercentage;
            Configuration   configuration   = this.Configuration;
            MemoryAllocator memoryAllocator = configuration.MemoryAllocator;

            using (IMemoryOwner <TPixel> rowColors = memoryAllocator.Allocate <TPixel>(width))
            {
                rowColors.GetSpan().Fill(glowColor);

                ParallelHelper.IterateRowsWithTempBuffer <float>(
                    workingRect,
                    configuration,
                    (rows, amounts) =>
                {
                    Span <float> amountsSpan = amounts.Span;

                    for (int y = rows.Min; y < rows.Max; y++)
                    {
                        int offsetY = y - startY;

                        for (int i = 0; i < width; i++)
                        {
                            float distance = Vector2.Distance(center, new Vector2(i + offsetX, offsetY));
                            amountsSpan[i] =
                                (blendPercentage * (1 - (.95F * (distance / maxDistance)))).Clamp(0, 1);
                        }

                        Span <TPixel> destination = source.GetPixelRowSpan(offsetY).Slice(offsetX, width);

                        this.blender.Blend(
                            configuration,
                            destination,
                            destination,
                            rowColors.GetSpan(),
                            amountsSpan);
                    }
                });
            }
        }
Example #9
0
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> source, ImageFrame <TPixel> destination, Rectangle sourceRectangle, Configuration configuration)
        {
            // Handle resize dimensions identical to the original
            if (source.Width == destination.Width && source.Height == destination.Height && sourceRectangle == this.ResizeRectangle)
            {
                // The cloned will be blank here copy all the pixel data over
                source.GetPixelSpan().CopyTo(destination.GetPixelSpan());
                return;
            }

            int width   = this.Width;
            int height  = this.Height;
            int sourceX = sourceRectangle.X;
            int sourceY = sourceRectangle.Y;
            int startY  = this.ResizeRectangle.Y;
            int endY    = this.ResizeRectangle.Bottom;
            int startX  = this.ResizeRectangle.X;
            int endX    = this.ResizeRectangle.Right;

            int minX = Math.Max(0, startX);
            int maxX = Math.Min(width, endX);
            int minY = Math.Max(0, startY);
            int maxY = Math.Min(height, endY);

            if (this.Sampler is NearestNeighborResampler)
            {
                var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY);

                // Scaling factors
                float widthFactor  = sourceRectangle.Width / (float)this.ResizeRectangle.Width;
                float heightFactor = sourceRectangle.Height / (float)this.ResizeRectangle.Height;

                ParallelHelper.IterateRows(
                    workingRect,
                    configuration,
                    rows =>
                {
                    for (int y = rows.Min; y < rows.Max; y++)
                    {
                        // Y coordinates of source points
                        Span <TPixel> sourceRow =
                            source.GetPixelRowSpan((int)(((y - startY) * heightFactor) + sourceY));
                        Span <TPixel> targetRow = destination.GetPixelRowSpan(y);

                        for (int x = minX; x < maxX; x++)
                        {
                            // X coordinates of source points
                            targetRow[x] = sourceRow[(int)(((x - startX) * widthFactor) + sourceX)];
                        }
                    }
                });

                return;
            }

            int sourceHeight = source.Height;

            PixelConversionModifiers conversionModifiers = PixelConversionModifiers.Premultiply;

            if (this.Compand)
            {
                conversionModifiers |= PixelConversionModifiers.Scale | PixelConversionModifiers.SRgbCompand;
            }

            // 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 (Buffer2D <Vector4> firstPassPixelsTransposed = source.MemoryAllocator.Allocate2D <Vector4>(sourceHeight, width))
            {
                firstPassPixelsTransposed.MemorySource.Clear();

                var processColsRect = new Rectangle(0, 0, source.Width, sourceRectangle.Bottom);

                ParallelHelper.IterateRowsWithTempBuffer <Vector4>(
                    processColsRect,
                    configuration,
                    (rows, tempRowBuffer) =>
                {
                    for (int y = rows.Min; y < rows.Max; y++)
                    {
                        Span <TPixel> sourceRow    = source.GetPixelRowSpan(y).Slice(sourceX);
                        Span <Vector4> tempRowSpan = tempRowBuffer.Span.Slice(sourceX);

                        PixelOperations <TPixel> .Instance.ToVector4(configuration, sourceRow, tempRowSpan, conversionModifiers);

                        ref Vector4 firstPassBaseRef = ref firstPassPixelsTransposed.Span[y];

                        for (int x = minX; x < maxX; x++)
                        {
                            ResizeKernel kernel = this.horizontalKernelMap.GetKernel(x - startX);
                            Unsafe.Add(ref firstPassBaseRef, x * sourceHeight) = kernel.Convolve(tempRowSpan);
                        }
                    }
                });

                var processRowsRect = Rectangle.FromLTRB(0, minY, width, maxY);

                // Now process the rows.
                ParallelHelper.IterateRowsWithTempBuffer <Vector4>(
                    processRowsRect,
                    configuration,
                    (rows, tempRowBuffer) =>
                {
                    Span <Vector4> tempRowSpan = tempRowBuffer.Span;

                    for (int y = rows.Min; y < rows.Max; y++)
                    {
                        // Ensure offsets are normalized for cropping and padding.
                        ResizeKernel kernel = this.verticalKernelMap.GetKernel(y - startY);

                        ref Vector4 tempRowBase = ref MemoryMarshal.GetReference(tempRowSpan);

                        for (int x = 0; x < width; x++)
                        {
                            Span <Vector4> firstPassColumn = firstPassPixelsTransposed.GetRowSpan(x).Slice(sourceY);

                            // Destination color components
                            Unsafe.Add(ref tempRowBase, x) = kernel.Convolve(firstPassColumn);
                        }

                        Span <TPixel> targetRowSpan = destination.GetPixelRowSpan(y);

                        PixelOperations <TPixel> .Instance.FromVector4Destructive(configuration, tempRowSpan, targetRowSpan, conversionModifiers);
                    }
                });
            }
        }
Example #10
0
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> source, Rectangle sourceRectangle, Configuration configuration)
        {
            int     startY        = sourceRectangle.Y;
            int     endY          = sourceRectangle.Bottom;
            int     startX        = sourceRectangle.X;
            int     endX          = sourceRectangle.Right;
            TPixel  vignetteColor = this.VignetteColor;
            Vector2 centre        = Rectangle.Center(sourceRectangle);

            Size  sourceSize   = source.Size();
            float finalRadiusX = this.RadiusX.Calculate(sourceSize);
            float finalRadiusY = this.RadiusY.Calculate(sourceSize);
            float rX           = finalRadiusX > 0 ? MathF.Min(finalRadiusX, sourceRectangle.Width * .5F) : sourceRectangle.Width * .5F;
            float rY           = finalRadiusY > 0 ? MathF.Min(finalRadiusY, sourceRectangle.Height * .5F) : sourceRectangle.Height * .5F;
            float maxDistance  = MathF.Sqrt((rX * rX) + (rY * rY));

            // Align start/end positions.
            int minX = Math.Max(0, startX);
            int maxX = Math.Min(source.Width, endX);
            int minY = Math.Max(0, startY);
            int maxY = Math.Min(source.Height, endY);

            // Reset offset if necessary.
            if (minX > 0)
            {
                startX = 0;
            }

            if (minY > 0)
            {
                startY = 0;
            }

            int width   = maxX - minX;
            int offsetX = minX - startX;

            var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY);

            using (IMemoryOwner <TPixel> rowColors = source.MemoryAllocator.Allocate <TPixel>(width))
            {
                rowColors.GetSpan().Fill(vignetteColor);

                ParallelHelper.IterateRowsWithTempBuffer <float>(
                    workingRect,
                    configuration,
                    (rows, amounts) =>
                {
                    Span <float> amountsSpan = amounts.Span;

                    for (int y = rows.Min; y < rows.Max; y++)
                    {
                        int offsetY = y - startY;

                        for (int i = 0; i < width; i++)
                        {
                            float distance = Vector2.Distance(centre, new Vector2(i + offsetX, offsetY));
                            amountsSpan[i] =
                                (this.GraphicsOptions.BlendPercentage * (.9F * (distance / maxDistance))).Clamp(
                                    0,
                                    1);
                        }

                        Span <TPixel> destination = source.GetPixelRowSpan(offsetY).Slice(offsetX, width);

                        this.blender.Blend(
                            source.Configuration,
                            destination,
                            destination,
                            rowColors.GetSpan(),
                            amountsSpan);
                    }
                });
            }
        }