// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // namespace ImageSharp.Processing.Processors { using System; using System.Numerics; using System.Threading.Tasks; /// /// Defines a sampler that uses two one-dimensional matrices to perform convolution against an image. /// /// The pixel format. public class Convolution2DProcessor : ImageProcessor where TColor : struct, IPackedPixel, IEquatable { /// /// Initializes a new instance of the class. /// /// The horizontal gradient operator. /// The vertical gradient operator. public Convolution2DProcessor(Fast2DArray kernelX, Fast2DArray kernelY) { this.KernelX = kernelX; this.KernelY = kernelY; } /// /// Gets the horizontal gradient operator. /// public Fast2DArray KernelX { get; } /// /// Gets the vertical gradient operator. /// public Fast2DArray KernelY { get; } /// protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { int kernelYHeight = this.KernelY.Height; int kernelYWidth = this.KernelY.Width; int kernelXHeight = this.KernelX.Height; int kernelXWidth = this.KernelX.Width; 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 (PixelAccessor targetPixels = new PixelAccessor(source.Width, source.Height)) { using (PixelAccessor sourcePixels = source.Lock()) { Parallel.For( startY, endY, this.ParallelOptions, 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); for (int fx = 0; fx < kernelXWidth; fx++) { int fxr = fx - radiusX; int offsetX = x + fxr; offsetX = offsetX.Clamp(0, maxX); Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4(); 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 = (float)Math.Sqrt((rX * rX) + (rY * rY)); float green = (float)Math.Sqrt((gX * gX) + (gY * gY)); float blue = (float)Math.Sqrt((bX * bX) + (bY * bY)); TColor packed = default(TColor); packed.PackFromVector4(new Vector4(red, green, blue, sourcePixels[x, y].ToVector4().W)); targetPixels[x, y] = packed; } }); } source.SwapPixelsBuffers(targetPixels); } } } }