Awesome
CSharp-Dithering
CSharp (C#) versions of certain dithering algorithms. This project is .NET Standard 2.0 and .NET 8 compatible, managed and available as Nuget!
Build status
Nuget
Projects using this library
Introduction to this project
This project contains implementations of different dithering algorithms (C#). They can be used with any graphics/image API.
Introduction to dithering
As Wikipedia says "Dither is an intentionally applied form of noise used to randomize quantization error, preventing large-scale patterns such as color banding in images."
In this case dithering is used help in color reduction (less banding). This reduction of colors + dithering combo can be used e.g. to reduce file sizes, make artsy images and avoid issues when displaying images on displays that have limited color range.
Implementation
Inspiration for this project came from blog post made by Tanner Helland.
DitheringBase.cs contains the abstract base class that every error pushing dithering implmentation should use.
FakeDithering.cs is "fake" dithering since it doesn't do any dithering. It is used to get image with reduced colors.
Other .cs files are used for different dithering algorithms, and the files are named as SomeAlgorithmDithering.cs
Samples folder contains images that are shown in the end of this Readme file
Examples
Use Atkinson dithering with web safe color reduction for 24 bit PNG input with System.Drawing
public void DoAtkinsonDithering()
{
AtkinsonDitheringRGBByte atkinson = new AtkinsonDitheringRGBByte(TrueColorBytesToWebSafeColorBytes);
using(FileStream pngStream = new FileStream("half.png", FileMode.Open, FileAccess.Read))
using(var image = new Bitmap(pngStream))
{
byte[,,] bytes = ReadBitmapToColorBytes(image);
TempByteImageFormat temp = new TempByteImageFormat(bytes);
atkinson.DoDithering(temp);
WriteToBitmap(image, temp.GetPixelChannels);
image.Save("test.png");
}
}
private static void TrueColorBytesToWebSafeColorBytes(in byte[] input, ref byte[] output)
{
for (int i = 0; i < input.Length; i++)
{
output[i] = (byte)(Math.Round(input[i] / 51.0) * 51);
}
}
private static byte[,,] ReadBitmapToColorBytes(Bitmap bitmap)
{
byte[,,] returnValue = new byte[bitmap.Width, bitmap.Height, 3];
for (int x = 0; x < bitmap.Width; x++)
{
for (int y = 0; y < bitmap.Height; y++)
{
Color color = bitmap.GetPixel(x, y);
returnValue[x, y, 0] = color.R;
returnValue[x, y, 1] = color.G;
returnValue[x, y, 2] = color.B;
}
}
return returnValue;
}
private static void WriteToBitmap(Bitmap bitmap, Func<int, int, byte[]> reader)
{
for (int x = 0; x < bitmap.Width; x++)
{
for (int y = 0; y < bitmap.Height; y++)
{
byte[] read = reader(x, y);
Color color = Color.FromArgb(read[0], read[1], read[2]);
bitmap.SetPixel(x, y, color);
}
}
}
Usage
You have to always give color reduction method as parameter for dither constructor. You can dither multiple images with one instance by calling DoDithering again with different input.
Wasn't this .NET Framework project?
Yes, but time moves on...
License
Text in this document and source code files are released into the public domain. See PUBLICDOMAIN file.
Parrot image (half.png) is made from image that comes from Kodak Lossless True Color Image Suite and it doesn't have any specific license.
Samples
I took the famous parrot image and reduced its size. Then I ran the image (which has 64655 different colors) with all dithering methods and using Web safe colors as palette (216 colors).
Original <br><br><br>
Floyd-Steinberg <br><br><br>
Jarvis-Judice-Ninke <br><br><br>
Stucki <br><br><br>
Atkinson <br><br><br>
Burkes <br><br><br>
Sierra <br><br><br>
Sierra Two Row <br><br><br>
Sierra Lite <br><br><br>
No dithering, just color reduction