Skip to content

mcraiha/CSharp-Dithering

Repository files navigation

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

Nuget

Projects using this library

Dithery-cli Dithery

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

Original


Floyd-Steinberg

Floyd-Steinberg


Jarvis-Judice-Ninke

Jarvis-Judice-Ninke


Stucki

Stucki


Atkinson

Atkinson


Burkes

Burkes


Sierra

Sierra


Sierra Two Row

Sierra Two Row


Sierra Lite

Sierra Lite


None

No dithering, just color reduction