// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // namespace ImageSharp.Processing.Processors { using System; using System.Buffers; using ImageSharp.Dithering; /// /// An that dithers an image using error diffusion. /// /// The pixel format. public class OrderedDitherProcessor : ImageProcessor where TColor : struct, IPackedPixel, IEquatable { /// /// Initializes a new instance of the class. /// /// The ordered ditherer. /// The component index to test the threshold against. Must range from 0 to 3. public OrderedDitherProcessor(IOrderedDither dither, int index) { Guard.NotNull(dither, nameof(dither)); Guard.MustBeBetweenOrEqualTo(index, 0, 3, nameof(index)); // Alpha8 only stores the pixel data in the alpha channel. if (typeof(TColor) == typeof(Alpha8)) { index = 3; } this.Dither = dither; this.Index = index; // Default to white/black for upper/lower. this.UpperColor = NamedColors.White; this.LowerColor = NamedColors.Black; } /// /// Gets the ditherer. /// public IOrderedDither Dither { get; } /// /// Gets the component index to test the threshold against. /// public int Index { get; } /// /// Gets or sets the color to use for pixels that are above the threshold. /// public TColor UpperColor { get; set; } /// /// Gets or sets the color to use for pixels that fall below the threshold. /// public TColor LowerColor { get; set; } /// protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) { new GrayscaleBt709Processor().Apply(source, sourceRectangle); } /// protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; int startX = sourceRectangle.X; int endX = sourceRectangle.Right; // 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; } using (PixelAccessor sourcePixels = source.Lock()) { for (int y = minY; y < maxY; y++) { int offsetY = y - startY; byte[] bytes = ArrayPool.Shared.Rent(4); for (int x = minX; x < maxX; x++) { int offsetX = x - startX; TColor sourceColor = sourcePixels[offsetX, offsetY]; this.Dither.Dither(sourcePixels, sourceColor, this.UpperColor, this.LowerColor, bytes, this.Index, offsetX, offsetY, maxX, maxY); } ArrayPool.Shared.Return(bytes); } } } } }