/// <inheritdoc/> protected override void OnFrameApply(ImageFrame <TPixel> source) { int sourceWidth = source.Width; int sourceHeight = source.Height; int tileWidth = (int)MathF.Ceiling(sourceWidth / (float)this.Tiles); int tileHeight = (int)MathF.Ceiling(sourceHeight / (float)this.Tiles); int tileCount = this.Tiles; int halfTileWidth = tileWidth / 2; int halfTileHeight = tileHeight / 2; int luminanceLevels = this.LuminanceLevels; // The image is split up into tiles. For each tile the cumulative distribution function will be calculated. using (var cdfData = new CdfTileData(this.Configuration, sourceWidth, sourceHeight, this.Tiles, this.Tiles, tileWidth, tileHeight, luminanceLevels)) { cdfData.CalculateLookupTables(source, this); var tileYStartPositions = new List <(int y, int cdfY)>(); int cdfY = 0; int yStart = halfTileHeight; for (int tile = 0; tile < tileCount - 1; tile++) { tileYStartPositions.Add((yStart, cdfY)); cdfY++; yStart += tileHeight; } var operation = new RowIntervalOperation(cdfData, tileYStartPositions, tileWidth, tileHeight, tileCount, halfTileWidth, luminanceLevels, source); ParallelRowIterator.IterateRowIntervals( this.Configuration, new Rectangle(0, 0, sourceWidth, tileYStartPositions.Count), in operation); ref TPixel pixelsBase = ref source.GetPixelReference(0, 0); // Fix left column ProcessBorderColumn(ref pixelsBase, cdfData, 0, sourceWidth, sourceHeight, this.Tiles, tileHeight, xStart: 0, xEnd: halfTileWidth, luminanceLevels); // Fix right column int rightBorderStartX = ((this.Tiles - 1) * tileWidth) + halfTileWidth; ProcessBorderColumn(ref pixelsBase, cdfData, this.Tiles - 1, sourceWidth, sourceHeight, this.Tiles, tileHeight, xStart: rightBorderStartX, xEnd: sourceWidth, luminanceLevels); // Fix top row ProcessBorderRow(ref pixelsBase, cdfData, 0, sourceWidth, this.Tiles, tileWidth, yStart: 0, yEnd: halfTileHeight, luminanceLevels); // Fix bottom row int bottomBorderStartY = ((this.Tiles - 1) * tileHeight) + halfTileHeight; ProcessBorderRow(ref pixelsBase, cdfData, this.Tiles - 1, sourceWidth, this.Tiles, tileWidth, yStart: bottomBorderStartY, yEnd: sourceHeight, luminanceLevels); // Left top corner ProcessCornerTile(ref pixelsBase, cdfData, sourceWidth, 0, 0, xStart: 0, xEnd: halfTileWidth, yStart: 0, yEnd: halfTileHeight, luminanceLevels); // Left bottom corner ProcessCornerTile(ref pixelsBase, cdfData, sourceWidth, 0, this.Tiles - 1, xStart: 0, xEnd: halfTileWidth, yStart: bottomBorderStartY, yEnd: sourceHeight, luminanceLevels); // Right top corner ProcessCornerTile(ref pixelsBase, cdfData, sourceWidth, this.Tiles - 1, 0, xStart: rightBorderStartX, xEnd: sourceWidth, yStart: 0, yEnd: halfTileHeight, luminanceLevels); // Right bottom corner ProcessCornerTile(ref pixelsBase, cdfData, sourceWidth, this.Tiles - 1, this.Tiles - 1, xStart: rightBorderStartX, xEnd: sourceWidth, yStart: bottomBorderStartY, yEnd: sourceHeight, luminanceLevels); }
/// <inheritdoc/> protected override void OnFrameApply(ImageFrame <TPixel> source) { TPixel glowColor = this.definition.GlowColor.ToPixel <TPixel>(); float blendPercent = this.definition.GraphicsOptions.BlendPercentage; var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); Vector2 center = Rectangle.Center(interest); float finalRadius = this.definition.Radius.Calculate(interest.Size); float maxDistance = finalRadius > 0 ? MathF.Min(finalRadius, interest.Width * .5F) : interest.Width * .5F; Configuration configuration = this.Configuration; MemoryAllocator allocator = configuration.MemoryAllocator; using IMemoryOwner <TPixel> rowColors = allocator.Allocate <TPixel>(interest.Width); rowColors.GetSpan().Fill(glowColor); var operation = new RowIntervalOperation(configuration, interest, rowColors, this.blender, center, maxDistance, blendPercent, source); ParallelRowIterator.IterateRows <RowIntervalOperation, float>( configuration, interest, in operation); }
/// <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.GetPixelMemoryGroup().CopyTo(destination.GetPixelMemoryGroup()); return; } int width = this.targetWidth; int height = this.targetHeight; var interest = 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; var operation = new RowIntervalOperation(sourceRectangle, this.targetRectangle, widthFactor, heightFactor, source, destination); ParallelRowIterator.IterateRows( configuration, interest, in operation); 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, interest, this.targetRectangle.Location)) { worker.Initialize(); var workingInterval = new RowInterval(interest.Top, interest.Bottom); worker.FillDestinationPixels(workingInterval, destination.PixelBuffer); } }
/// <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) { var operation = new RowIntervalOperation(source); ParallelRowIterator.IterateRows( configuration, source.Bounds(), in operation); }
/// <inheritdoc/> protected override void OnFrameApply(ImageFrame <TPixel> source) { var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); var operation = new RowIntervalOperation(interest.X, source, this.definition.Matrix, this.Configuration); ParallelRowIterator.IterateRows <RowIntervalOperation, Vector4>( this.Configuration, interest, in operation); }
/// <inheritdoc /> protected override void OnFrameApply(ImageFrame <TPixel> source) { Configuration configuration = this.Configuration; using IFrameQuantizer <TPixel> frameQuantizer = this.quantizer.CreateFrameQuantizer <TPixel>(configuration); using IQuantizedFrame <TPixel> quantized = frameQuantizer.QuantizeFrame(source); var operation = new RowIntervalOperation(this.SourceRectangle, source, quantized); ParallelRowIterator.IterateRows( configuration, this.SourceRectangle, in operation); }
protected override void OnFrameApply(ImageFrame <TPixelBg> source) { var targetBounds = this.TargetImage.Bounds(); var sourceBounds = this.Source.Bounds(); var maxWidth = Math.Min(targetBounds.Width, sourceBounds.Width); var operation = new RowIntervalOperation( source, this.TargetImage, this.Blender, this.Configuration, maxWidth); ParallelRowIterator.IterateRowIntervals(this.Configuration, this.SourceRectangle, in operation); }
/// <inheritdoc /> protected override void OnFrameApply(ImageFrame <TPixel> source) { var interest = Rectangle.Intersect(source.Bounds(), this.SourceRectangle); Configuration configuration = this.Configuration; using IQuantizer <TPixel> frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer <TPixel>(configuration); using IndexedImageFrame <TPixel> quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(source, interest); var operation = new RowIntervalOperation(this.SourceRectangle, source, quantized); ParallelRowIterator.IterateRowIntervals( configuration, interest, in operation); }
/// <inheritdoc/> protected override void OnFrameApply(ImageFrame <TPixel> source) { var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); if (interest.Width == 0 || interest.Height == 0) { return; } Configuration configuration = this.Configuration; 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 = ParallelExecutionSettings.FromConfiguration(configuration) .MultiplyMinimumPixelsPerTask(4); TPixel colorPixel = solidBrush.Color.ToPixel <TPixel>(); var solidOperation = new SolidBrushRowIntervalOperation(interest, source, colorPixel); ParallelRowIterator.IterateRowIntervals( interest, parallelSettings, in solidOperation); return; } using IMemoryOwner <float> amount = configuration.MemoryAllocator.Allocate <float>(interest.Width); using BrushApplicator <TPixel> applicator = brush.CreateApplicator( configuration, options, source, interest); amount.Memory.Span.Fill(1F); var operation = new RowIntervalOperation(interest, applicator, amount.Memory); ParallelRowIterator.IterateRowIntervals( configuration, interest, in operation); }
/// <summary> /// Returns a copy of the image frame in the given pixel format. /// </summary> /// <typeparam name="TPixel2">The pixel format.</typeparam> /// <param name="configuration">The configuration providing initialization code which allows extending the library.</param> /// <returns>The <see cref="ImageFrame{TPixel2}"/></returns> internal ImageFrame <TPixel2> CloneAs <TPixel2>(Configuration configuration) where TPixel2 : unmanaged, IPixel <TPixel2> { if (typeof(TPixel2) == typeof(TPixel)) { return(this.Clone(configuration) as ImageFrame <TPixel2>); } var target = new ImageFrame <TPixel2>(configuration, this.Width, this.Height, this.Metadata.DeepClone()); var operation = new RowIntervalOperation <TPixel2>(this, target, configuration); ParallelRowIterator.IterateRowIntervals( configuration, this.Bounds(), in operation); return(target); }
/// <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()); bool isAlphaOnly = typeof(TPixel) == typeof(A8); var operation = new RowIntervalOperation(interest, source, upper, lower, threshold, isAlphaOnly); ParallelRowIterator.IterateRows( configuration, interest, in operation); }
/// <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); }
/// <inheritdoc/> protected override void OnFrameApply(ImageFrame <TPixelBg> source) { Rectangle sourceRectangle = this.SourceRectangle; Configuration configuration = this.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."); } var operation = new RowIntervalOperation(source, targetImage, blender, configuration, minX, width, locationY, targetX, this.Opacity); ParallelRowIterator.IterateRows( configuration, workingRect, in operation); }
/// <inheritdoc /> protected override void OnFrameApply(ImageFrame <TPixel> source) { DenseMatrix <float>[] kernels = this.Kernels.Flatten(); var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); // 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, interest)) { processor.Apply(source); } if (kernels.Length == 1) { return; } // Additional runs 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, interest)) { processor.Apply(pass); } var operation = new RowIntervalOperation(source.PixelBuffer, pass.PixelBuffer, interest); ParallelRowIterator.IterateRows( this.Configuration, interest, in operation); } }
private static void SecondPass <TFrameQuantizer, TPixel>( ref TFrameQuantizer quantizer, ImageFrame <TPixel> source, Rectangle bounds, Memory <byte> output, ReadOnlyMemory <TPixel> palette) where TFrameQuantizer : struct, IFrameQuantizer <TPixel> where TPixel : unmanaged, IPixel <TPixel> { IDither dither = quantizer.Options.Dither; if (dither is null) { var operation = new RowIntervalOperation <TFrameQuantizer, TPixel>(quantizer, source, output, bounds, palette); ParallelRowIterator.IterateRowIntervals( quantizer.Configuration, bounds, in operation); return; } dither.ApplyQuantizationDither(ref quantizer, palette, source, output, bounds); }