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