public void IterateRows_OverMinimumPixelsLimit_ShouldVisitAllRows(
            int maxDegreeOfParallelism,
            int minY,
            int maxY,
            int expectedStepLength,
            int expectedLastStepLength,
            int expectedNumberOfSteps)
        {
            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];

            void RowAction(RowInterval rows)
            {
                for (int y = rows.Min; y < rows.Max; y++)
                {
                    actualData[y] = y;
                }
            }

            var operation = new TestRowIntervalOperation(RowAction);

            ParallelRowIterator.IterateRowIntervals(
                rectangle,
                in parallelSettings,
                in operation);

            Assert.Equal(expectedData, actualData);
        }
예제 #2
0
        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);
        }
예제 #3
0
        public void IterateRows_OverMinimumPixelsLimit_IntervalsAreCorrect(
            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 actualNumberOfSteps = 0;

            ParallelHelper.IterateRows(
                rectangle,
                parallelSettings,
                rows =>
            {
                Assert.True(rows.Min >= minY);
                Assert.True(rows.Max <= maxY);

                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);
        }
예제 #4
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.CropRectangle)
            {
                // the cloned will be blank here copy all the pixel data over
                source.GetPixelSpan().CopyTo(destination.GetPixelSpan());
                return;
            }

            var rect = Rectangle.Intersect(this.CropRectangle, sourceRectangle);

            // Copying is cheap, we should process more pixels per task:
            ParallelExecutionSettings parallelSettings = configuration.GetParallelSettings().MultiplyMinimumPixelsPerTask(4);

            ParallelHelper.IterateRows(
                rect,
                parallelSettings,
                rows =>
            {
                for (int y = rows.Min; y < rows.Max; y++)
                {
                    Span <TPixel> sourceRow = source.GetPixelRowSpan(y).Slice(rect.Left);
                    Span <TPixel> targetRow = destination.GetPixelRowSpan(y - rect.Top);
                    sourceRow.Slice(0, rect.Width).CopyTo(targetRow);
                }
            });
        }
예제 #5
0
        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);
        }
예제 #6
0
        public void IterateRowsWithTempBufferRequiresValidRectangle(int width, int height)
        {
            var parallelSettings = new 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);
        }
        public void IterateRectangularBuffer(
            int maxDegreeOfParallelism,
            int bufferWidth,
            int bufferHeight,
            int rectX,
            int rectY,
            int rectWidth,
            int rectHeight)
        {
            MemoryAllocator memoryAllocator = Configuration.Default.MemoryAllocator;

            using (Buffer2D <Point> expected = memoryAllocator.Allocate2D <Point>(bufferWidth, bufferHeight, AllocationOptions.Clean))
                using (Buffer2D <Point> actual = memoryAllocator.Allocate2D <Point>(bufferWidth, bufferHeight, AllocationOptions.Clean))
                {
                    var rect = new Rectangle(rectX, rectY, rectWidth, rectHeight);

                    void FillRow(int y, Buffer2D <Point> buffer)
                    {
                        for (int x = rect.Left; x < rect.Right; x++)
                        {
                            buffer[x, y] = new Point(x, y);
                        }
                    }

                    // Fill Expected data:
                    for (int y = rectY; y < rect.Bottom; y++)
                    {
                        FillRow(y, expected);
                    }

                    // Fill actual data using IterateRows:
                    var settings = new ParallelExecutionSettings(maxDegreeOfParallelism, memoryAllocator);

                    void RowAction(RowInterval rows)
                    {
                        this.output.WriteLine(rows.ToString());
                        for (int y = rows.Min; y < rows.Max; y++)
                        {
                            FillRow(y, actual);
                        }
                    }

                    var operation = new TestRowIntervalOperation(RowAction);

                    ParallelRowIterator.IterateRowIntervals(
                        rect,
                        settings,
                        in operation);

                    // Assert:
                    TestImageExtensions.CompareBuffers(expected.GetSingleSpan(), actual.GetSingleSpan());
                }
        }
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> source)
        {
            var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());

            if (interest.Width == 0 || interest.Height == 0)
            {
                return;
            }

            Configuration   configuration = this.Configuration;
            IBrush          brush         = this.definition.Brush;
            GraphicsOptions options       = this.definition.Options;

            // If there's no reason for blending, then avoid it.
            if (this.IsSolidBrushWithoutBlending(out SolidBrush solidBrush))
            {
                ParallelExecutionSettings parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration)
                                                             .MultiplyMinimumPixelsPerTask(4);

                TPixel colorPixel = solidBrush.Color.ToPixel <TPixel>();

                var solidOperation = new SolidBrushRowIntervalOperation(interest, source, colorPixel);
                ParallelRowIterator.IterateRowIntervals(
                    interest,
                    parallelSettings,
                    in solidOperation);

                return;
            }

            using IMemoryOwner <float> amount         = configuration.MemoryAllocator.Allocate <float>(interest.Width);
            using BrushApplicator <TPixel> applicator = brush.CreateApplicator(
                      configuration,
                      options,
                      source,
                      interest);

            amount.Memory.Span.Fill(1F);

            var operation = new RowIntervalOperation(interest, applicator, amount.Memory);

            ParallelRowIterator.IterateRowIntervals(
                configuration,
                interest,
                in operation);
        }
예제 #9
0
 public void Constructor_MaxDegreeOfParallelism_CompatibleWith_ParallelOptions(int maxDegreeOfParallelism, bool throws)
 {
     if (throws)
     {
         Assert.Throws <ArgumentOutOfRangeException>(
             () =>
         {
             _ = new ParallelExecutionSettings(
                 maxDegreeOfParallelism,
                 Configuration.Default.MemoryAllocator);
         });
     }
     else
     {
         var parallelSettings = new ParallelExecutionSettings(
             maxDegreeOfParallelism,
             Configuration.Default.MemoryAllocator);
         Assert.Equal(maxDegreeOfParallelism, parallelSettings.MaxDegreeOfParallelism);
     }
 }
예제 #10
0
        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);
        }
예제 #11
0
        public void IterateRowsWithTempBuffer_OverMinimumPixelsLimit(
            int maxDegreeOfParallelism,
            int minY,
            int maxY,
            int expectedStepLength,
            int expectedLastStepLength,
            int expectedNumberOfSteps)
        {
            var parallelSettings = new ParallelExecutionSettings(
                maxDegreeOfParallelism,
                1,
                Configuration.Default.MemoryAllocator);

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

            int actualNumberOfSteps = 0;

            void RowAction(RowInterval rows, Span <Vector4> buffer)
            {
                Assert.True(rows.Min >= minY);
                Assert.True(rows.Max <= maxY);

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

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

            var operation = new TestRowIntervalOperation <Vector4>(RowAction);

            ParallelRowIterator.IterateRowIntervals <TestRowIntervalOperation <Vector4>, Vector4>(
                rectangle,
                in parallelSettings,
                in operation);

            Assert.Equal(expectedNumberOfSteps, actualNumberOfSteps);
        }
예제 #12
0
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> source)
        {
            Rectangle     sourceRectangle = this.SourceRectangle;
            Configuration configuration   = this.Configuration;
            int           startX          = sourceRectangle.X;
            int           endX            = sourceRectangle.Right;
            int           startY          = sourceRectangle.Y;
            int           endY            = sourceRectangle.Bottom;

            // 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);

            int width = maxX - minX;

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

            IBrush          brush   = this.definition.Brush;
            GraphicsOptions options = this.definition.Options;

            // If there's no reason for blending, then avoid it.
            if (this.IsSolidBrushWithoutBlending(out SolidBrush solidBrush))
            {
                ParallelExecutionSettings parallelSettings = configuration.GetParallelSettings().MultiplyMinimumPixelsPerTask(4);

                TPixel colorPixel = solidBrush.Color.ToPixel <TPixel>();

                ParallelHelper.IterateRows(
                    workingRect,
                    parallelSettings,
                    rows =>
                {
                    for (int y = rows.Min; y < rows.Max; y++)
                    {
                        source.GetPixelRowSpan(y).Slice(minX, width).Fill(colorPixel);
                    }
                });
            }
            else
            {
                // Reset offset if necessary.
                if (minX > 0)
                {
                    startX = 0;
                }

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

                using (IMemoryOwner <float> amount = source.MemoryAllocator.Allocate <float>(width))
                    using (BrushApplicator <TPixel> applicator = brush.CreateApplicator(
                               source,
                               sourceRectangle,
                               options))
                    {
                        amount.GetSpan().Fill(1f);

                        ParallelHelper.IterateRows(
                            workingRect,
                            configuration,
                            rows =>
                        {
                            for (int y = rows.Min; y < rows.Max; y++)
                            {
                                int offsetY = y - startY;
                                int offsetX = minX - startX;

                                applicator.Apply(amount.GetSpan(), offsetX, offsetY);
                            }
                        });
                    }
            }
        }