/// <inheritdoc/> protected override void OnFrameApply(ImageFrame <TPixel> source) { byte threshold = (byte)MathF.Round(this.Definition.Threshold * 255F); bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8); 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; PixelPair <TPixel> pair = this.GetClosestPixelPair(ref 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)) { pair = this.GetClosestPixelPair(ref sourcePixel); // No error to spread, exact match. if (sourcePixel.Equals(pair.First)) { continue; } 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 ? pair.Second : pair.First; this.Definition.Diffuser.Dither(source, sourcePixel, transformedPixel, x, y, startX, startY, endX, endY); } } }
/// <inheritdoc/> protected override void OnFrameApply(ImageFrame <TPixel> source, Rectangle sourceRectangle, Configuration configuration) { Rgba32 rgba = default; bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8); var interest = Rectangle.Intersect(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; PixelPair <TPixel> pair = this.GetClosestPixelPair(ref sourcePixel); sourcePixel.ToRgba32(ref rgba); // Convert to grayscale using ITU-R Recommendation BT.709 if required float luminance = isAlphaOnly ? rgba.A : (.2126F * rgba.R) + (.7152F * rgba.G) + (.0722F * 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)) { pair = this.GetClosestPixelPair(ref sourcePixel); // No error to spread, exact match. if (sourcePixel.Equals(pair.First)) { continue; } sourcePixel.ToRgba32(ref rgba); luminance = isAlphaOnly ? rgba.A : (.2126F * rgba.R) + (.7152F * rgba.G) + (.0722F * rgba.B); // Setup the previous pointer previousPixel = sourcePixel; } this.Dither.Dither(source, sourcePixel, pair.Second, pair.First, luminance, x, y); } } }
/// <inheritdoc/> public bool Equals(PixelPair <TPixel> other) => this.First.Equals(other.First) && this.Second.Equals(other.Second);