// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; using System.Numerics; using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing.Processors; using SixLabors.Memory; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// /// Defines a processor that uses a 2 dimensional matrix to perform convolution against an image. /// /// The pixel format. internal class ConvolutionProcessor : ImageProcessor where TPixel : struct, IPixel { /// /// Initializes a new instance of the class. /// /// The 2d gradient operator. public ConvolutionProcessor(DenseMatrix kernelXY) { this.KernelXY = kernelXY; } /// /// Gets the 2d gradient operator. /// public DenseMatrix KernelXY { get; } /// protected override void OnFrameApply(ImageFrame 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 targetPixels = configuration.MemoryAllocator.Allocate2D(source.Size())) { source.CopyTo(targetPixels); Parallel.For( startY, endY, configuration.ParallelOptions, y => { Span sourceRow = source.GetPixelRowSpan(y); Span 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 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.SwapOrCopyContent(source.PixelBuffer, targetPixels); } } } }