/// <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 center = Rectangle.Center(sourceRectangle); float finalRadius = this.Radius.Calculate(source.Size()); float maxDistance = finalRadius > 0 ? MathF.Min(finalRadius, 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 = source.MemoryAllocator.Allocate <TPixel>(width)) { // Be careful! Do not capture rowColorsSpan in the lambda below! Span <TPixel> rowColorsSpan = rowColors.GetSpan(); for (int i = 0; i < width; i++) { rowColorsSpan[i] = glowColor; } ParallelFor.WithTemporaryBuffer <float>( minY, maxY, configuration, width, (y, amounts) => { Span <float> amountsSpan = amounts.GetSpan(); int offsetY = y - startY; int offsetX = minX - startX; for (int i = 0; i < width; i++) { float distance = Vector2.Distance(center, new Vector2(i + offsetX, offsetY)); amountsSpan[i] = (this.GraphicsOptions.BlendPercentage * (1 - (.95F * (distance / maxDistance)))).Clamp(0, 1); } Span <TPixel> destination = source.GetPixelRowSpan(offsetY).Slice(offsetX, width); this.blender.Blend(source.MemoryAllocator, destination, destination, rowColors.GetSpan(), amountsSpan); }); } }