/// <inheritdoc /> protected override void OnFrameApply(ImageFrame <TPixel> source, Rectangle sourceRectangle, Configuration configuration) { QuantizedFrame <TPixel> quantized = this.Quantizer.Quantize(source, this.MaxColors); int paletteCount = quantized.Palette.Length - 1; using (Buffer2D <TPixel> pixels = source.MemoryManager.Allocate2D <TPixel>(quantized.Width, quantized.Height)) { Parallel.For( 0, pixels.Height, configuration.ParallelOptions, y => { Span <TPixel> row = pixels.GetRowSpan(y); int yy = y * pixels.Width; for (int x = 0; x < pixels.Width; x++) { int i = x + yy; TPixel color = quantized.Palette[Math.Min(paletteCount, quantized.Pixels[i])]; row[x] = color; } }); Buffer2D <TPixel> .SwapContents(source.PixelBuffer, pixels); } }
/// <summary> /// Applies quantization to the image. /// </summary> /// <typeparam name="TPixel">The pixel format.</typeparam> /// <param name="source">The image this method extends.</param> /// <param name="quantizer">The quantizer to apply to perform the operation.</param> /// <param name="maxColors">The maximum number of colors to return.</param> /// <returns>The <see cref="Image{TPixel}"/>.</returns> public static IImageProcessingContext <TPixel> Quantize <TPixel>(this IImageProcessingContext <TPixel> source, IQuantizer <TPixel> quantizer, int maxColors) where TPixel : struct, IPixel <TPixel> { return(source.Apply(img => { // TODO : move helper logic into the processor QuantizedImage <TPixel> quantized = quantizer.Quantize(img.Frames.RootFrame, maxColors); int palleteCount = quantized.Palette.Length - 1; using (Buffer2D <TPixel> pixels = source.MemoryManager.Allocate2D <TPixel>(quantized.Width, quantized.Height)) { Parallel.For( 0, pixels.Height, img.GetConfiguration().ParallelOptions, y => { Span <TPixel> row = pixels.GetRowSpan(y); int yy = y * pixels.Width; for (int x = 0; x < pixels.Width; x++) { int i = x + yy; TPixel color = quantized.Palette[Math.Min(palleteCount, quantized.Pixels[i])]; row[x] = color; } }); Buffer2D <TPixel> .SwapContents(img.Frames[0].PixelBuffer, pixels); } })); }
/// <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.MemoryManager.Allocate2D <TPixel>(source.Size())) { Parallel.For( 0, height, configuration.ParallelOptions, 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> .SwapContents(source.PixelBuffer, targetPixels); } }
/// <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.MemoryManager.Allocate2D <TPixel>(source.Size())) { Parallel.For( 0, halfHeight, configuration.ParallelOptions, 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> .SwapContents(source.PixelBuffer, targetPixels); } }
/// <inheritdoc/> protected override void OnApply(ImageFrame <TPixel> source, Rectangle sourceRectangle, Configuration configuration) { if (this.CropRectangle == sourceRectangle) { return; } int minY = Math.Max(this.CropRectangle.Y, sourceRectangle.Y); int maxY = Math.Min(this.CropRectangle.Bottom, sourceRectangle.Bottom); int minX = Math.Max(this.CropRectangle.X, sourceRectangle.X); int maxX = Math.Min(this.CropRectangle.Right, sourceRectangle.Right); using (Buffer2D <TPixel> targetPixels = configuration.MemoryManager.Allocate2D <TPixel>(this.CropRectangle.Size)) { Parallel.For( minY, maxY, configuration.ParallelOptions, y => { Span <TPixel> sourceRow = source.GetPixelRowSpan(y).Slice(minX); Span <TPixel> targetRow = targetPixels.GetRowSpan(y - minY); SpanHelper.Copy(sourceRow, targetRow, maxX - minX); }); Buffer2D <TPixel> .SwapContents(source.PixelBuffer, targetPixels); } }
/// <inheritdoc/> protected override void OnApply(ImageFrame <TPixel> source, Rectangle sourceRectangle, Configuration configuration) { 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 (Buffer2D <TPixel> targetPixels = configuration.MemoryManager.Allocate2D <TPixel>(source.Width, source.Height)) { 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 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> .SwapContents(source.PixelBuffer, targetPixels); } }
/// <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.MemoryManager.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> .SwapContents(source.PixelBuffer, targetPixels); } }
/// <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.MemoryManager.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] += 1; 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> .SwapContents(source.PixelBuffer, targetPixels); } }