/// <inheritdoc/> protected override void OnFrameApply(ImageFrame <TPixel> source) { TPixel upperColor = this.definition.UpperColor.ToPixel <TPixel>(); TPixel lowerColor = this.definition.LowerColor.ToPixel <TPixel>(); IErrorDiffuser diffuser = this.definition.Diffuser; byte threshold = (byte)MathF.Round(this.definition.Threshold * 255F); bool isAlphaOnly = typeof(TPixel) == typeof(A8); var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); int startY = interest.Y; int endY = interest.Bottom; int startX = interest.X; int endX = interest.Right; // Collect the values before looping so we can reduce our calculation count for identical sibling pixels TPixel sourcePixel = source[startX, startY]; TPixel previousPixel = sourcePixel; Rgba32 rgba = default; sourcePixel.ToRgba32(ref rgba); // Convert to grayscale using ITU-R Recommendation BT.709 if required byte luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); for (int y = startY; y < endY; y++) { Span <TPixel> row = source.GetPixelRowSpan(y); for (int x = startX; x < endX; x++) { sourcePixel = row[x]; // Check if this is the same as the last pixel. If so use that value // rather than calculating it again. This is an inexpensive optimization. if (!previousPixel.Equals(sourcePixel)) { sourcePixel.ToRgba32(ref rgba); luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); // Setup the previous pointer previousPixel = sourcePixel; } TPixel transformedPixel = luminance >= threshold ? upperColor : lowerColor; diffuser.Dither(source, sourcePixel, transformedPixel, x, y, startX, endX, endY); } } }