コード例 #1
0
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> source)
        {
            MemoryAllocator memoryAllocator = this.Configuration.MemoryAllocator;
            int             numberOfPixels  = source.Width * source.Height;
            var             workingRect     = new Rectangle(0, 0, source.Width, source.Height);

            using (IMemoryOwner <int> histogramBuffer = memoryAllocator.Allocate <int>(this.LuminanceLevels, AllocationOptions.Clean))
                using (IMemoryOwner <int> cdfBuffer = memoryAllocator.Allocate <int>(this.LuminanceLevels, AllocationOptions.Clean))
                {
                    // Build the histogram of the grayscale levels.
                    ParallelHelper.IterateRows(
                        workingRect,
                        this.Configuration,
                        rows =>
                    {
                        ref int histogramBase = ref MemoryMarshal.GetReference(histogramBuffer.GetSpan());
                        for (int y = rows.Min; y < rows.Max; y++)
                        {
                            ref TPixel pixelBase = ref MemoryMarshal.GetReference(source.GetPixelRowSpan(y));

                            for (int x = 0; x < workingRect.Width; x++)
                            {
                                int luminance = GetLuminance(Unsafe.Add(ref pixelBase, x), this.LuminanceLevels);
                                Unsafe.Add(ref histogramBase, luminance)++;
                            }
                        }
                    });
コード例 #2
0
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixelDst> source, Rectangle sourceRectangle, Configuration configuration)
        {
            Image <TPixelSrc>        targetImage = this.Image;
            PixelBlender <TPixelDst> blender     = this.Blender;
            int locationY = this.Location.Y;

            // Align start/end positions.
            Rectangle bounds = targetImage.Bounds();

            int minX    = Math.Max(this.Location.X, sourceRectangle.X);
            int maxX    = Math.Min(this.Location.X + bounds.Width, sourceRectangle.Width);
            int targetX = minX - this.Location.X;

            int minY = Math.Max(this.Location.Y, sourceRectangle.Y);
            int maxY = Math.Min(this.Location.Y + bounds.Height, sourceRectangle.Bottom);

            int width = maxX - minX;

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

            ParallelHelper.IterateRows(
                workingRect,
                configuration,
                rows =>
            {
                for (int y = rows.Min; y < rows.Max; y++)
                {
                    Span <TPixelDst> background = source.GetPixelRowSpan(y).Slice(minX, width);
                    Span <TPixelSrc> foreground =
                        targetImage.GetPixelRowSpan(y - locationY).Slice(targetX, width);
                    blender.Blend <TPixelSrc>(configuration, background, background, foreground, this.Opacity);
                }
            });
        }
コード例 #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
        public void IterateRows_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.IterateRows(
                rectangle,
                parallelSettings,
                rows =>
            {
                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);
        }
コード例 #5
0
        /// <summary>
        /// Rotates the image 90 degrees clockwise at the centre point.
        /// </summary>
        /// <param name="source">The source image.</param>
        /// <param name="destination">The destination image.</param>
        /// <param name="configuration">The configuration.</param>
        private void Rotate90(ImageFrame <TPixel> source, ImageFrame <TPixel> destination, Configuration configuration)
        {
            int       width             = source.Width;
            int       height            = source.Height;
            Rectangle destinationBounds = destination.Bounds();

            ParallelHelper.IterateRows(
                source.Bounds(),
                configuration,
                rows =>
            {
                for (int y = rows.Min; y < rows.Max; y++)
                {
                    Span <TPixel> sourceRow = source.GetPixelRowSpan(y);
                    int newX = height - y - 1;
                    for (int x = 0; x < width; x++)
                    {
                        // TODO: Optimize this:
                        if (destinationBounds.Contains(newX, x))
                        {
                            destination[newX, x] = sourceRow[x];
                        }
                    }
                }
            });
        }
コード例 #6
0
        public void IterateRows_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.IterateRows(
                rectangle,
                parallelSettings,
                rows =>
            {
                for (int y = rows.Min; y < rows.Max; y++)
                {
                    actualData[y] = y;
                }
            });

            Assert.Equal(expectedData, actualData);
        }
コード例 #7
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);
                }
            });
        }
コード例 #8
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));
            }

            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);
コード例 #9
0
        public void IterateRowsRequiresValidRectangle(int width, int height)
        {
            var parallelSettings = default(ParallelExecutionSettings);

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

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

            Assert.Contains(width <= 0 ? "Width" : "Height", ex.Message);
        }
コード例 #10
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)
 {
     ParallelHelper.IterateRows(
         source.Bounds(),
         configuration,
         rows =>
     {
         for (int y = rows.Min; y < rows.Max; y++)
         {
             source.GetPixelRowSpan(y).Reverse();
         }
     });
 }
コード例 #11
0
        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);

                    ParallelHelper.IterateRows(
                        rect,
                        settings,
                        rows =>
                    {
                        this.output.WriteLine(rows.ToString());
                        for (int y = rows.Min; y < rows.Max; y++)
                        {
                            FillRow(y, actual);
                        }
                    });

                    // Assert:
                    TestImageExtensions.CompareBuffers(expected.GetSpan(), actual.GetSpan());
                }
        }
コード例 #12
0
        /// <inheritdoc/>
        protected override void OnFrameApply(
            ImageFrame <TPixelBg> source,
            Rectangle sourceRectangle,
            Configuration configuration)
        {
            Image <TPixelFg>        targetImage = this.Image;
            PixelBlender <TPixelBg> blender     = this.Blender;
            int locationY = this.Location.Y;

            // Align start/end positions.
            Rectangle bounds = targetImage.Bounds();

            int minX    = Math.Max(this.Location.X, sourceRectangle.X);
            int maxX    = Math.Min(this.Location.X + bounds.Width, sourceRectangle.Right);
            int targetX = minX - this.Location.X;

            int minY = Math.Max(this.Location.Y, sourceRectangle.Y);
            int maxY = Math.Min(this.Location.Y + bounds.Height, sourceRectangle.Bottom);

            int width = maxX - minX;

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

            // not a valid operation because rectangle does not overlap with this image.
            if (workingRect.Width <= 0 || workingRect.Height <= 0)
            {
                throw new ImageProcessingException(
                          "Cannot draw image because the source image does not overlap the target image.");
            }

            ParallelHelper.IterateRows(
                workingRect,
                configuration,
                rows =>
            {
                for (int y = rows.Min; y < rows.Max; y++)
                {
                    Span <TPixelBg> background = source.GetPixelRowSpan(y).Slice(minX, width);
                    Span <TPixelFg> foreground =
                        targetImage.GetPixelRowSpan(y - locationY).Slice(targetX, width);
                    blender.Blend <TPixelFg>(configuration, background, background, foreground, this.Opacity);
                }
            });
        }
コード例 #13
0
        /// <inheritdoc/>
        protected override void OnFrameApply(
            ImageFrame <TPixel> source,
            Rectangle sourceRectangle,
            Configuration configuration)
        {
            float  threshold = this.Threshold * 255F;
            TPixel upper     = this.UpperColor;
            TPixel lower     = this.LowerColor;

            var interest = Rectangle.Intersect(sourceRectangle, source.Bounds());
            int startY   = interest.Y;
            int endY     = interest.Bottom;
            int startX   = interest.X;
            int endX     = interest.Right;

            bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8);

            var workingRect = Rectangle.FromLTRB(startX, startY, endX, endY);

            ParallelHelper.IterateRows(
                workingRect,
                configuration,
                rows =>
            {
                for (int y = rows.Min; y < rows.Max; y++)
                {
                    Span <TPixel> row = source.GetPixelRowSpan(y);
                    Rgba32 rgba       = default;

                    for (int x = startX; x < endX; x++)
                    {
                        ref TPixel color = ref row[x];
                        color.ToRgba32(ref rgba);

                        // Convert to grayscale using ITU-R Recommendation BT.709 if required
                        float luminance = isAlphaOnly
                                                      ? rgba.A
                                                      : (.2126F * rgba.R) + (.7152F * rgba.G) + (.0722F * rgba.B);
                        color = luminance >= threshold ? upper : lower;
                    }
                }
            });
        }
コード例 #14
0
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> source)
        {
            byte   threshold = (byte)MathF.Round(this.definition.Threshold * 255F);
            TPixel upper     = this.definition.UpperColor.ToPixel <TPixel>();
            TPixel lower     = this.definition.LowerColor.ToPixel <TPixel>();

            Rectangle     sourceRectangle = this.SourceRectangle;
            Configuration configuration   = this.Configuration;

            var interest = Rectangle.Intersect(sourceRectangle, source.Bounds());
            int startY   = interest.Y;
            int endY     = interest.Bottom;
            int startX   = interest.X;
            int endX     = interest.Right;

            bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8);

            var workingRect = Rectangle.FromLTRB(startX, startY, endX, endY);

            ParallelHelper.IterateRows(
                workingRect,
                configuration,
                rows =>
            {
                Rgba32 rgba = default;
                for (int y = rows.Min; y < rows.Max; y++)
                {
                    Span <TPixel> row = source.GetPixelRowSpan(y);

                    for (int x = startX; x < endX; x++)
                    {
                        ref TPixel color = ref row[x];
                        color.ToRgba32(ref rgba);

                        // Convert to grayscale using ITU-R Recommendation BT.709 if required
                        byte luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B);
                        color          = luminance >= threshold ? upper : lower;
                    }
                }
            });
        }
コード例 #15
0
        /// <summary>
        /// Rotates the image 180 degrees clockwise at the centre point.
        /// </summary>
        /// <param name="source">The source image.</param>
        /// <param name="destination">The destination image.</param>
        /// <param name="configuration">The configuration.</param>
        private void Rotate180(ImageFrame <TPixel> source, ImageFrame <TPixel> destination, Configuration configuration)
        {
            int width  = source.Width;
            int height = source.Height;

            ParallelHelper.IterateRows(
                source.Bounds(),
                configuration,
                rows =>
            {
                for (int y = rows.Min; y < rows.Max; y++)
                {
                    Span <TPixel> sourceRow = source.GetPixelRowSpan(y);
                    Span <TPixel> targetRow = destination.GetPixelRowSpan(height - y - 1);

                    for (int x = 0; x < width; x++)
                    {
                        targetRow[width - x - 1] = sourceRow[x];
                    }
                }
            });
        }
コード例 #16
0
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> source, Rectangle sourceRectangle, Configuration configuration)
        {
            var interest = Rectangle.Intersect(sourceRectangle, source.Bounds());

            Matrix4x4 matrix = this.Matrix;

            ParallelHelper.IterateRows(
                interest,
                configuration,
                rows =>
            {
                for (int y = rows.Min; y < rows.Max; y++)
                {
                    Span <TPixel> row = source.GetPixelRowSpan(y);

                    for (int x = interest.X; x < interest.Right; x++)
                    {
                        ref TPixel pixel = ref row[x];
                        var vector       = Vector4.Transform(pixel.ToVector4(), matrix);
                        pixel.PackFromVector4(vector);
                    }
                }
            });
        }
コード例 #17
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);
                    }
                });
            }
        }
コード例 #18
0
        /// <inheritdoc/>
        protected override void OnFrameApply(
            ImageFrame <TPixel> source,
            ImageFrame <TPixel> destination,
            Rectangle sourceRectangle,
            Configuration configuration)
        {
            int height = this.TargetDimensions.Height;
            int width  = this.TargetDimensions.Width;

            Rectangle sourceBounds = source.Bounds();
            var       targetBounds = new Rectangle(0, 0, width, height);

            // Since could potentially be resizing the canvas we might need to re-calculate the matrix
            Matrix3x2 matrix = this.GetProcessingMatrix(sourceBounds, targetBounds);

            // Convert from screen to world space.
            Matrix3x2.Invert(matrix, out matrix);

            if (this.Sampler is NearestNeighborResampler)
            {
                ParallelHelper.IterateRows(
                    targetBounds,
                    configuration,
                    rows =>
                {
                    for (int y = rows.Min; y < rows.Max; y++)
                    {
                        Span <TPixel> destRow = destination.GetPixelRowSpan(y);

                        for (int x = 0; x < width; x++)
                        {
                            var point = Point.Transform(new Point(x, y), matrix);
                            if (sourceBounds.Contains(point.X, point.Y))
                            {
                                destRow[x] = source[point.X, point.Y];
                            }
                        }
                    }
                });

                return;
            }

            int maxSourceX = source.Width - 1;
            int maxSourceY = source.Height - 1;

            (float radius, float scale, float ratio)xRadiusScale = this.GetSamplingRadius(source.Width, destination.Width);
            (float radius, float scale, float ratio)yRadiusScale = this.GetSamplingRadius(source.Height, destination.Height);
            float      xScale    = xRadiusScale.scale;
            float      yScale    = yRadiusScale.scale;
            var        radius    = new Vector2(xRadiusScale.radius, yRadiusScale.radius);
            IResampler sampler   = this.Sampler;
            var        maxSource = new Vector4(maxSourceX, maxSourceY, maxSourceX, maxSourceY);
            int        xLength   = (int)MathF.Ceiling((radius.X * 2) + 2);
            int        yLength   = (int)MathF.Ceiling((radius.Y * 2) + 2);

            MemoryAllocator memoryAllocator = configuration.MemoryAllocator;

            using (Buffer2D <float> yBuffer = memoryAllocator.Allocate2D <float>(yLength, height))
                using (Buffer2D <float> xBuffer = memoryAllocator.Allocate2D <float>(xLength, height))
                {
                    ParallelHelper.IterateRows(
                        targetBounds,
                        configuration,
                        rows =>
                    {
                        for (int y = rows.Min; y < rows.Max; y++)
                        {
                            ref TPixel destRowRef = ref MemoryMarshal.GetReference(destination.GetPixelRowSpan(y));
                            ref float ySpanRef    = ref MemoryMarshal.GetReference(yBuffer.GetRowSpan(y));
                            ref float xSpanRef    = ref MemoryMarshal.GetReference(xBuffer.GetRowSpan(y));

                            for (int x = 0; x < width; x++)
                            {
                                // Use the single precision position to calculate correct bounding pixels
                                // otherwise we get rogue pixels outside of the bounds.
                                var point = Vector2.Transform(new Vector2(x, y), matrix);

                                // Clamp sampling pixel radial extents to the source image edges
                                Vector2 maxXY = point + radius;
                                Vector2 minXY = point - radius;

                                // max, maxY, minX, minY
                                var extents = new Vector4(
                                    MathF.Floor(maxXY.X + .5F),
                                    MathF.Floor(maxXY.Y + .5F),
                                    MathF.Ceiling(minXY.X - .5F),
                                    MathF.Ceiling(minXY.Y - .5F));

                                int right  = (int)extents.X;
                                int bottom = (int)extents.Y;
                                int left   = (int)extents.Z;
                                int top    = (int)extents.W;

                                extents = Vector4.Clamp(extents, Vector4.Zero, maxSource);

                                int maxX = (int)extents.X;
                                int maxY = (int)extents.Y;
                                int minX = (int)extents.Z;
                                int minY = (int)extents.W;

                                if (minX == maxX || minY == maxY)
                                {
                                    continue;
                                }

                                // It appears these have to be calculated on-the-fly.
                                // Precalculating transformed weights would require prior knowledge of every transformed pixel location
                                // since they can be at sub-pixel positions on both axis.
                                // I've optimized where I can but am always open to suggestions.
                                if (yScale > 1 && xScale > 1)
                                {
                                    CalculateWeightsDown(
                                        top,
                                        bottom,
                                        minY,
                                        maxY,
                                        point.Y,
                                        sampler,
                                        yScale,
                                        ref ySpanRef,
                                        yLength);

                                    CalculateWeightsDown(
                                        left,
                                        right,
                                        minX,
                                        maxX,
                                        point.X,
                                        sampler,
                                        xScale,
                                        ref xSpanRef,
                                        xLength);
                                }
                                else
                                {
                                    CalculateWeightsScaleUp(minY, maxY, point.Y, sampler, ref ySpanRef);
                                    CalculateWeightsScaleUp(minX, maxX, point.X, sampler, ref xSpanRef);
                                }

                                // Now multiply the results against the offsets
                                Vector4 sum = Vector4.Zero;
                                for (int yy = 0, j = minY; j <= maxY; j++, yy++)
                                {
                                    float yWeight = Unsafe.Add(ref ySpanRef, yy);

                                    for (int xx = 0, i = minX; i <= maxX; i++, xx++)
                                    {
                                        float xWeight = Unsafe.Add(ref xSpanRef, xx);

                                        // Values are first premultiplied to prevent darkening of edge pixels
                                        var current = source[i, j].ToVector4();
                                        Vector4Utils.Premultiply(ref current);
                                        sum += current * xWeight * yWeight;
                                    }
                                }

                                ref TPixel dest = ref Unsafe.Add(ref destRowRef, x);

                                // Reverse the premultiplication
                                Vector4Utils.UnPremultiply(ref sum);
                                dest.FromVector4(sum);
                            }
コード例 #19
0
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> source)
        {
            TPixel          color           = this.definition.Color.ToPixel <TPixel>();
            GraphicsOptions graphicsOptions = this.definition.GraphicsOptions;

            int startY = this.SourceRectangle.Y;
            int endY   = this.SourceRectangle.Bottom;
            int startX = this.SourceRectangle.X;
            int endX   = this.SourceRectangle.Right;

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

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

            using (IMemoryOwner <TPixel> colors = source.MemoryAllocator.Allocate <TPixel>(width))
                using (IMemoryOwner <float> amount = source.MemoryAllocator.Allocate <float>(width))
                {
                    // Be careful! Do not capture colorSpan & amountSpan in the lambda below!
                    Span <TPixel> colorSpan  = colors.GetSpan();
                    Span <float>  amountSpan = amount.GetSpan();

                    colorSpan.Fill(color);
                    amountSpan.Fill(graphicsOptions.BlendPercentage);

                    PixelBlender <TPixel> blender = PixelOperations <TPixel> .Instance.GetPixelBlender(graphicsOptions);

                    ParallelHelper.IterateRows(
                        workingRect,
                        this.Configuration,
                        rows =>
                    {
                        for (int y = rows.Min; y < rows.Max; y++)
                        {
                            Span <TPixel> destination =
                                source.GetPixelRowSpan(y - startY).Slice(minX - startX, width);

                            // This switched color & destination in the 2nd and 3rd places because we are applying the target color under the current one
                            blender.Blend(
                                source.Configuration,
                                destination,
                                colors.GetSpan(),
                                destination,
                                amount.GetSpan());
                        }
                    });
                }
        }
コード例 #20
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);
            }
        }
コード例 #21
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);
                            }
                        });
                    }
            }
        }
コード例 #22
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);

                var workingRect = Rectangle.FromLTRB(startX, startY, endX, endY);

                ParallelHelper.IterateRows(
                    workingRect,
                    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 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);
            }
        }
コード例 #23
0
        /// <inheritdoc />
        protected override void OnFrameApply(ImageFrame <TPixel> source)
        {
            DenseMatrix <float>[] kernels = this.Kernels.Flatten();

            int startY = this.SourceRectangle.Y;
            int endY   = this.SourceRectangle.Bottom;
            int startX = this.SourceRectangle.X;
            int endX   = this.SourceRectangle.Right;

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

            // we need a clean copy for each pass to start from
            using (ImageFrame <TPixel> cleanCopy = source.Clone())
            {
                using (var processor = new ConvolutionProcessor <TPixel>(this.Configuration, kernels[0], true, this.Source, this.SourceRectangle))
                {
                    processor.Apply(source);
                }

                if (kernels.Length == 1)
                {
                    return;
                }

                int shiftY = startY;
                int shiftX = startX;

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

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

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

                // Additional runs.
                // ReSharper disable once ForCanBeConvertedToForeach
                for (int i = 1; i < kernels.Length; i++)
                {
                    using (ImageFrame <TPixel> pass = cleanCopy.Clone())
                    {
                        using (var processor = new ConvolutionProcessor <TPixel>(this.Configuration, kernels[i], true, this.Source, this.SourceRectangle))
                        {
                            processor.Apply(pass);
                        }

                        Buffer2D <TPixel> passPixels   = pass.PixelBuffer;
                        Buffer2D <TPixel> targetPixels = source.PixelBuffer;

                        ParallelHelper.IterateRows(
                            workingRect,
                            this.Configuration,
                            rows =>
                        {
                            for (int y = rows.Min; y < rows.Max; y++)
                            {
                                int offsetY = y - shiftY;

                                ref TPixel passPixelsBase   = ref MemoryMarshal.GetReference(passPixels.GetRowSpan(offsetY));
                                ref TPixel targetPixelsBase = ref MemoryMarshal.GetReference(targetPixels.GetRowSpan(offsetY));

                                for (int x = minX; x < maxX; x++)
                                {
                                    int offsetX = x - shiftX;

                                    // Grab the max components of the two pixels
                                    ref TPixel currentPassPixel   = ref Unsafe.Add(ref passPixelsBase, offsetX);
                                    ref TPixel currentTargetPixel = ref Unsafe.Add(ref targetPixelsBase, offsetX);

                                    var pixelValue = Vector4.Max(
                                        currentPassPixel.ToVector4(),
                                        currentTargetPixel.ToVector4());

                                    currentTargetPixel.FromVector4(pixelValue);
                                }
                            }
                        });
コード例 #24
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  glowColor   = this.GlowColor;
                Vector2 centre      = Rectangle.Center(sourceRectangle);
                float   maxDistance = this.Radius > 0
                                        ? Math.Min(this.Radius, sourceRectangle.Width * .5F)
                                        : 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;

                using (IMemoryOwner <TPixel> rowColors = Configuration.Default.MemoryAllocator.Allocate <TPixel>(width))
                {
                    Buffer2D <TPixel> sourcePixels = source.PixelBuffer;
                    rowColors.GetSpan().Fill(glowColor);

                    var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY);
                    ParallelHelper.IterateRows(
                        workingRect,
                        configuration,
                        rows =>
                    {
                        for (int y = rows.Min; y < rows.Max; y++)
                        {
                            int offsetY = y - startY;

                            for (int x = minX; x < maxX; x++)
                            {
                                int offsetX         = x - startX;
                                float distance      = Vector2.Distance(centre, new Vector2(offsetX, offsetY));
                                Vector4 sourceColor = sourcePixels[offsetX, offsetY].ToVector4();
                                TPixel packed       = default;
                                packed.FromVector4(
                                    PremultipliedLerp(
                                        sourceColor,
                                        glowColor.ToVector4(),
                                        1 - (.95F * (distance / maxDistance))));
                                sourcePixels[offsetX, offsetY] = packed;
                            }
                        }
                    });
                }
            }
コード例 #25
0
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> source, ImageFrame <TPixel> destination)
        {
            Rectangle     sourceRectangle = this.SourceRectangle;
            Configuration configuration   = this.Configuration;

            // Handle resize dimensions identical to the original
            if (source.Width == destination.Width && source.Height == destination.Height && sourceRectangle == this.targetRectangle)
            {
                // The cloned will be blank here copy all the pixel data over
                source.GetPixelSpan().CopyTo(destination.GetPixelSpan());
                return;
            }

            int width   = this.targetWidth;
            int height  = this.targetHeight;
            int sourceX = sourceRectangle.X;
            int sourceY = sourceRectangle.Y;
            int startY  = this.targetRectangle.Y;
            int startX  = this.targetRectangle.X;

            var targetWorkingRect = Rectangle.Intersect(
                this.targetRectangle,
                new Rectangle(0, 0, width, height));

            if (this.resampler is NearestNeighborResampler)
            {
                // Scaling factors
                float widthFactor  = sourceRectangle.Width / (float)this.targetRectangle.Width;
                float heightFactor = sourceRectangle.Height / (float)this.targetRectangle.Height;

                ParallelHelper.IterateRows(
                    targetWorkingRect,
                    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 = targetWorkingRect.Left; x < targetWorkingRect.Right; x++)
                        {
                            // X coordinates of source points
                            targetRow[x] = sourceRow[(int)(((x - startX) * widthFactor) + sourceX)];
                        }
                    }
                });

                return;
            }

            PixelConversionModifiers conversionModifiers =
                PixelConversionModifiers.Premultiply.ApplyCompanding(this.compand);

            BufferArea <TPixel> sourceArea = source.PixelBuffer.GetArea(sourceRectangle);

            // To reintroduce parallel processing, we to launch multiple workers
            // for different row intervals of the image.
            using (var worker = new ResizeWorker <TPixel>(
                       configuration,
                       sourceArea,
                       conversionModifiers,
                       this.horizontalKernelMap,
                       this.verticalKernelMap,
                       width,
                       targetWorkingRect,
                       this.targetRectangle.Location))
            {
                worker.Initialize();

                var workingInterval = new RowInterval(targetWorkingRect.Top, targetWorkingRect.Bottom);
                worker.FillDestinationPixels(workingInterval, destination.PixelBuffer);
            }
        }
コード例 #26
0
        /// <inheritdoc/>
        protected override void OnFrameApply(
            ImageFrame <TPixel> source,
            Rectangle sourceRectangle,
            Configuration configuration)
        {
            int brushSize = this.definition.BrushSize;

            if (brushSize <= 0 || brushSize > source.Height || brushSize > source.Width)
            {
                throw new ArgumentOutOfRangeException(nameof(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 = brushSize >> 1;
            int levels = this.definition.Levels;

            using (Buffer2D <TPixel> targetPixels = configuration.MemoryAllocator.Allocate2D <TPixel>(source.Size()))
            {
                source.CopyTo(targetPixels);

                var workingRect = Rectangle.FromLTRB(startX, startY, endX, endY);
                ParallelHelper.IterateRows(
                    workingRect,
                    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++)
                        {
                            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.FromVector4(
                                    new Vector4(red, green, blue, sourceRow[x].ToVector4().W));
                            }
                        }
                    }
                });

                Buffer2D <TPixel> .SwapOrCopyContent(source.PixelBuffer, targetPixels);
            }
        }
コード例 #27
0
        /// <summary>
        /// Applies the process to the specified portion of the specified <see cref="ImageFrame{TPixel}"/> at the specified location
        /// and with the specified size.
        /// </summary>
        /// <param name="targetPixels">The target pixels to apply the process to.</param>
        /// <param name="sourcePixels">The source pixels. Cannot be null.</param>
        /// <param name="sourceRectangle">
        /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
        /// </param>
        /// <param name="kernel">The kernel operator.</param>
        /// <param name="configuration">The <see cref="Configuration"/></param>
        private void ApplyConvolution(
            Buffer2D <TPixel> targetPixels,
            Buffer2D <TPixel> sourcePixels,
            Rectangle sourceRectangle,
            DenseMatrix <float> kernel, // TODO: Can't use 'in' as pass by ref to lambda expression.
            Configuration configuration)
        {
            int kernelHeight = kernel.Rows;
            int kernelWidth  = kernel.Columns;
            int radiusY      = kernelHeight >> 1;
            int radiusX      = kernelWidth >> 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;

            var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY);

            ParallelHelper.IterateRows(
                workingRectangle,
                configuration,
                rows =>
            {
                for (int y = rows.Min; y < rows.Max; y++)
                {
                    Span <TPixel> targetRow = targetPixels.GetRowSpan(y);

                    for (int x = startX; x < endX; x++)
                    {
                        Vector4 destination = default;

                        // Apply each matrix multiplier to the color components for each pixel.
                        for (int fy = 0; fy < kernelHeight; fy++)
                        {
                            int fyr     = fy - radiusY;
                            int offsetY = y + fyr;

                            offsetY           = offsetY.Clamp(0, maxY);
                            Span <TPixel> row = sourcePixels.GetRowSpan(offsetY);

                            for (int fx = 0; fx < kernelWidth; fx++)
                            {
                                int fxr     = fx - radiusX;
                                int offsetX = x + fxr;

                                offsetX = offsetX.Clamp(0, maxX);

                                Vector4 currentColor = row[offsetX].ToVector4().Premultiply();
                                destination         += kernel[fy, fx] * currentColor;
                            }
                        }

                        ref TPixel pixel = ref targetRow[x];
                        pixel.PackFromVector4(destination.UnPremultiply());
                    }
                }
            });
        }