Example #1
0
        /// <summary>
        /// Swaps the image at the Y-axis, which goes vertically through the middle at half of the width of the image.
        /// </summary>
        /// <param name="source">The source image to apply the process to.</param>
        /// <param name="configuration">The configuration.</param>
        private void FlipY(ImageFrame <TPixel> source, Configuration configuration)
        {
            int width     = source.Width;
            int height    = source.Height;
            int halfWidth = (int)Math.Ceiling(width * .5F);

            using (Buffer2D <TPixel> targetPixels = configuration.MemoryAllocator.Allocate2D <TPixel>(source.Size()))
            {
                ParallelFor.WithConfiguration(
                    0,
                    height,
                    configuration,
                    y =>
                {
                    Span <TPixel> sourceRow = source.GetPixelRowSpan(y);
                    Span <TPixel> targetRow = targetPixels.GetRowSpan(y);

                    for (int x = 0; x < halfWidth; x++)
                    {
                        int newX        = width - x - 1;
                        targetRow[x]    = sourceRow[newX];
                        targetRow[newX] = sourceRow[x];
                    }
                });

                Buffer2D <TPixel> .SwapOrCopyContent(source.PixelBuffer, targetPixels);
            }
        }
Example #2
0
        /// <summary>
        /// Swaps the image at the X-axis, which goes horizontally through the middle at half the height of the image.
        /// </summary>
        /// <param name="source">The source image to apply the process to.</param>
        /// <param name="configuration">The configuration.</param>
        private void FlipX(ImageFrame <TPixel> source, Configuration configuration)
        {
            int height     = source.Height;
            int halfHeight = (int)Math.Ceiling(source.Height * .5F);

            using (Buffer2D <TPixel> targetPixels = configuration.MemoryAllocator.Allocate2D <TPixel>(source.Size()))
            {
                ParallelFor.WithConfiguration(
                    0,
                    halfHeight,
                    configuration,
                    y =>
                {
                    int newY = height - y - 1;
                    Span <TPixel> sourceRow    = source.GetPixelRowSpan(y);
                    Span <TPixel> altSourceRow = source.GetPixelRowSpan(newY);
                    Span <TPixel> targetRow    = targetPixels.GetRowSpan(y);
                    Span <TPixel> altTargetRow = targetPixels.GetRowSpan(newY);

                    sourceRow.CopyTo(altTargetRow);
                    altSourceRow.CopyTo(targetRow);
                });

                Buffer2D <TPixel> .SwapOrCopyContent(source.PixelBuffer, targetPixels);
            }
        }
Example #3
0
        /// <summary>
        /// Switches the buffers used by the image and the pixelSource meaning that the Image will "own" the buffer from the pixelSource and the pixelSource will now own the Images buffer.
        /// </summary>
        /// <param name="pixelSource">The pixel source.</param>
        internal void SwapOrCopyPixelsBufferFrom(ImageFrame <TPixel> pixelSource)
        {
            Guard.NotNull(pixelSource, nameof(pixelSource));

            Buffer2D <TPixel> .SwapOrCopyContent(this.PixelBuffer, pixelSource.PixelBuffer);

            this.UpdateSize(this.PixelBuffer.Size());
        }
Example #4
0
            public void WhenDestIsNotMemoryOwner_DifferentSize_Throws(bool sourceIsOwner)
            {
                var data  = new Rgba32[21];
                var color = new Rgba32(1, 2, 3, 4);

                using var destOwner = new TestMemoryManager <Rgba32>(data);
                using var dest      = new Buffer2D <Rgba32>(MemoryGroup <Rgba32> .Wrap(destOwner.Memory), 21, 1);

                using Buffer2D <Rgba32> source = this.MemoryAllocator.Allocate2D <Rgba32>(22, 1);

                source.FastMemoryGroup[0].Span[10] = color;

                // Act:
                Assert.ThrowsAny <InvalidOperationException>(() => Buffer2D <Rgba32> .SwapOrCopyContent(dest, source));

                Assert.Equal(color, source.MemoryGroup[0].Span[10]);
                Assert.NotEqual(color, dest.MemoryGroup[0].Span[10]);
            }
Example #5
0
            public void WhenDestIsNotAllocated_SameSize_ShouldCopy(bool sourceIsAllocated)
            {
                var data  = new Rgba32[21];
                var color = new Rgba32(1, 2, 3, 4);

                using var destOwner = new TestMemoryManager <Rgba32>(data);
                using var dest      = new Buffer2D <Rgba32>(MemoryGroup <Rgba32> .Wrap(destOwner.Memory), 21, 1);

                using Buffer2D <Rgba32> source = this.MemoryAllocator.Allocate2D <Rgba32>(21, 1);

                source.FastMemoryGroup[0].Span[10] = color;

                // Act:
                bool swap = Buffer2D <Rgba32> .SwapOrCopyContent(dest, source);

                // Assert:
                Assert.False(swap);
                Assert.Equal(color, dest.MemoryGroup[0].Span[10]);
                Assert.NotEqual(source.FastMemoryGroup[0], dest.FastMemoryGroup[0]);
            }
Example #6
0
            public void WhenBothAreMemoryOwners_ShouldSwap()
            {
                this.MemoryAllocator.BufferCapacityInBytes = sizeof(int) * 50;
                using Buffer2D <int> a = this.MemoryAllocator.Allocate2D <int>(48, 2);
                using Buffer2D <int> b = this.MemoryAllocator.Allocate2D <int>(50, 2);

                Memory <int> a0 = a.FastMemoryGroup[0];
                Memory <int> a1 = a.FastMemoryGroup[1];
                Memory <int> b0 = b.FastMemoryGroup[0];
                Memory <int> b1 = b.FastMemoryGroup[1];

                bool swap = Buffer2D <int> .SwapOrCopyContent(a, b);

                Assert.True(swap);

                Assert.Equal(b0, a.FastMemoryGroup[0]);
                Assert.Equal(b1, a.FastMemoryGroup[1]);
                Assert.Equal(a0, b.FastMemoryGroup[0]);
                Assert.Equal(a1, b.FastMemoryGroup[1]);
                Assert.NotEqual(a.FastMemoryGroup[0], b.FastMemoryGroup[0]);
            }
Example #7
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 #8
0
            public void WhenBothAreMemoryOwners_ShouldReplaceViews()
            {
                using Buffer2D <int> a = this.MemoryAllocator.Allocate2D <int>(100, 1);
                using Buffer2D <int> b = this.MemoryAllocator.Allocate2D <int>(100, 2);

                a.FastMemoryGroup[0].Span[42] = 1;
                b.FastMemoryGroup[0].Span[33] = 2;
                MemoryGroupView <int> aView0 = (MemoryGroupView <int>)a.MemoryGroup;
                MemoryGroupView <int> bView0 = (MemoryGroupView <int>)b.MemoryGroup;

                Buffer2D <int> .SwapOrCopyContent(a, b);

                Assert.False(aView0.IsValid);
                Assert.False(bView0.IsValid);
                Assert.ThrowsAny <InvalidOperationException>(() => _ = aView0[0].Span);
                Assert.ThrowsAny <InvalidOperationException>(() => _ = bView0[0].Span);

                Assert.True(a.MemoryGroup.IsValid);
                Assert.True(b.MemoryGroup.IsValid);
                Assert.Equal(2, a.MemoryGroup[0].Span[33]);
                Assert.Equal(1, b.MemoryGroup[0].Span[42]);
            }
Example #9
0
            public void SwapOrCopyContent_WhenBothAllocated()
            {
                using (Buffer2D <int> a = this.MemoryAllocator.Allocate2D <int>(10, 5, AllocationOptions.Clean))
                    using (Buffer2D <int> b = this.MemoryAllocator.Allocate2D <int>(3, 7, AllocationOptions.Clean))
                    {
                        a[1, 3] = 666;
                        b[1, 3] = 444;

                        Memory <int> aa = a.FastMemoryGroup.Single();
                        Memory <int> bb = b.FastMemoryGroup.Single();

                        Buffer2D <int> .SwapOrCopyContent(a, b);

                        Assert.Equal(bb, a.FastMemoryGroup.Single());
                        Assert.Equal(aa, b.FastMemoryGroup.Single());

                        Assert.Equal(new Size(3, 7), a.Size());
                        Assert.Equal(new Size(10, 5), b.Size());

                        Assert.Equal(666, b[1, 3]);
                        Assert.Equal(444, a[1, 3]);
                    }
            }
Example #10
0
            public void SwapOrCopyContent_WhenDestinationIsOwned_ShouldNotSwapInDisposedSourceBuffer()
            {
                using var destData = MemoryGroup <int> .Wrap(new int[100]);

                using var dest = new Buffer2D <int>(destData, 10, 10);

                using (Buffer2D <int> source = this.MemoryAllocator.Allocate2D <int>(10, 10, AllocationOptions.Clean))
                {
                    source[0, 0] = 1;
                    dest[0, 0]   = 2;

                    Buffer2D <int> .SwapOrCopyContent(dest, source);
                }

                int actual1 = dest.DangerousGetRowSpan(0)[0];
                int actual2 = dest.DangerousGetRowSpan(0)[0];
                int actual3 = dest.GetSafeRowMemory(0).Span[0];
                int actual5 = dest[0, 0];

                Assert.Equal(1, actual1);
                Assert.Equal(1, actual2);
                Assert.Equal(1, actual3);
                Assert.Equal(1, actual5);
            }
Example #11
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 #12
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);
            }
        }
Example #13
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);
            }
        }
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> source)
        {
            MemoryAllocator memoryAllocator = this.Configuration.MemoryAllocator;

            var parallelOptions = new ParallelOptions {
                MaxDegreeOfParallelism = this.Configuration.MaxDegreeOfParallelism
            };
            int tileWidth          = source.Width / this.Tiles;
            int tileHeight         = tileWidth;
            int pixelInTile        = tileWidth * tileHeight;
            int halfTileHeight     = tileHeight / 2;
            int halfTileWidth      = halfTileHeight;
            var slidingWindowInfos = new SlidingWindowInfos(tileWidth, tileHeight, halfTileWidth, halfTileHeight, pixelInTile);

            using (Buffer2D <TPixel> targetPixels = this.Configuration.MemoryAllocator.Allocate2D <TPixel>(source.Width, source.Height))
            {
                // Process the inner tiles, which do not require to check the borders.
                Parallel.For(
                    halfTileWidth,
                    source.Width - halfTileWidth,
                    parallelOptions,
                    this.ProcessSlidingWindow(
                        source,
                        memoryAllocator,
                        targetPixels,
                        slidingWindowInfos,
                        yStart: halfTileHeight,
                        yEnd: source.Height - halfTileHeight,
                        useFastPath: true,
                        this.Configuration));

                // Process the left border of the image.
                Parallel.For(
                    0,
                    halfTileWidth,
                    parallelOptions,
                    this.ProcessSlidingWindow(
                        source,
                        memoryAllocator,
                        targetPixels,
                        slidingWindowInfos,
                        yStart: 0,
                        yEnd: source.Height,
                        useFastPath: false,
                        this.Configuration));

                // Process the right border of the image.
                Parallel.For(
                    source.Width - halfTileWidth,
                    source.Width,
                    parallelOptions,
                    this.ProcessSlidingWindow(
                        source,
                        memoryAllocator,
                        targetPixels,
                        slidingWindowInfos,
                        yStart: 0,
                        yEnd: source.Height,
                        useFastPath: false,
                        this.Configuration));

                // Process the top border of the image.
                Parallel.For(
                    halfTileWidth,
                    source.Width - halfTileWidth,
                    parallelOptions,
                    this.ProcessSlidingWindow(
                        source,
                        memoryAllocator,
                        targetPixels,
                        slidingWindowInfos,
                        yStart: 0,
                        yEnd: halfTileHeight,
                        useFastPath: false,
                        this.Configuration));

                // Process the bottom border of the image.
                Parallel.For(
                    halfTileWidth,
                    source.Width - halfTileWidth,
                    parallelOptions,
                    this.ProcessSlidingWindow(
                        source,
                        memoryAllocator,
                        targetPixels,
                        slidingWindowInfos,
                        yStart: source.Height - halfTileHeight,
                        yEnd: source.Height,
                        useFastPath: false,
                        this.Configuration));

                Buffer2D <TPixel> .SwapOrCopyContent(source.PixelBuffer, targetPixels);
            }
        }
Example #15
0
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> source)
        {
            MemoryAllocator memoryAllocator = this.Configuration.MemoryAllocator;

            var parallelOptions = new ParallelOptions {
                MaxDegreeOfParallelism = this.Configuration.MaxDegreeOfParallelism
            };
            int tileWidth          = source.Width / this.Tiles;
            int tileHeight         = tileWidth;
            int pixelInTile        = tileWidth * tileHeight;
            int halfTileHeight     = tileHeight / 2;
            int halfTileWidth      = halfTileHeight;
            var slidingWindowInfos = new SlidingWindowInfos(tileWidth, tileHeight, halfTileWidth, halfTileHeight, pixelInTile);

            // TODO: If the process was able to be switched to operate in parallel rows instead of columns
            // then we could take advantage of batching and allocate per-row buffers only once per batch.
            using Buffer2D <TPixel> targetPixels = this.Configuration.MemoryAllocator.Allocate2D <TPixel>(source.Width, source.Height);

            // Process the inner tiles, which do not require to check the borders.
            var innerOperation = new SlidingWindowOperation(
                this.Configuration,
                this,
                source,
                memoryAllocator,
                targetPixels,
                slidingWindowInfos,
                yStart: halfTileHeight,
                yEnd: source.Height - halfTileHeight,
                useFastPath: true);

            Parallel.For(
                halfTileWidth,
                source.Width - halfTileWidth,
                parallelOptions,
                innerOperation.Invoke);

            // Process the left border of the image.
            var leftBorderOperation = new SlidingWindowOperation(
                this.Configuration,
                this,
                source,
                memoryAllocator,
                targetPixels,
                slidingWindowInfos,
                yStart: 0,
                yEnd: source.Height,
                useFastPath: false);

            Parallel.For(
                0,
                halfTileWidth,
                parallelOptions,
                leftBorderOperation.Invoke);

            // Process the right border of the image.
            var rightBorderOperation = new SlidingWindowOperation(
                this.Configuration,
                this,
                source,
                memoryAllocator,
                targetPixels,
                slidingWindowInfos,
                yStart: 0,
                yEnd: source.Height,
                useFastPath: false);

            Parallel.For(
                source.Width - halfTileWidth,
                source.Width,
                parallelOptions,
                rightBorderOperation.Invoke);

            // Process the top border of the image.
            var topBorderOperation = new SlidingWindowOperation(
                this.Configuration,
                this,
                source,
                memoryAllocator,
                targetPixels,
                slidingWindowInfos,
                yStart: 0,
                yEnd: halfTileHeight,
                useFastPath: false);

            Parallel.For(
                halfTileWidth,
                source.Width - halfTileWidth,
                parallelOptions,
                topBorderOperation.Invoke);

            // Process the bottom border of the image.
            var bottomBorderOperation = new SlidingWindowOperation(
                this.Configuration,
                this,
                source,
                memoryAllocator,
                targetPixels,
                slidingWindowInfos,
                yStart: source.Height - halfTileHeight,
                yEnd: source.Height,
                useFastPath: false);

            Parallel.For(
                halfTileWidth,
                source.Width - halfTileWidth,
                parallelOptions,
                bottomBorderOperation.Invoke);

            Buffer2D <TPixel> .SwapOrCopyContent(source.PixelBuffer, targetPixels);
        }