Created
October 15, 2014 22:41
-
-
Save configurator/cd0cb436e43001b9cab4 to your computer and use it in GitHub Desktop.
ErrorDiffusion dithering using the Sierra Matrix
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <summary> | |
/// See http://en.wikipedia.org/wiki/Floyd-Steinberg_dithering for algorithm details. | |
/// The matrix used is Sierra [/32]: | |
/// x 5 3 | |
/// 2 4 5 4 2 | |
/// 2 3 2 | |
/// </summary> | |
protected static Bitmap ErrorDiffusion(System.Drawing.Image bitmap) { | |
const double limit = 0.5; | |
var errors = new ErrorArray(bitmap.Width, bitmap.Height); | |
var result = new Bitmap(bitmap); | |
for (var y = 0; y < bitmap.Height; y++) { | |
for (var x = 0; x < bitmap.Width; x++) { | |
var originalValue = result.GetPixel(x, y).GetBrightness(); | |
var originalError = errors[x, y]; | |
var value = originalValue + originalError; | |
var white = value > limit; | |
result.SetPixel(x, y, white ? Color.White : Color.Black); | |
var residual = value - (white ? 1 : 0); | |
residual /= 32; | |
errors.AddRow(x - 2, y + 0, residual, 0, 0, 0, 5, 3); | |
errors.AddRow(x - 2, y + 1, residual, 2, 4, 5, 4, 2); | |
errors.AddRow(x - 2, y + 2, residual, 0, 2, 3, 2, 0); | |
} | |
} | |
return result; | |
} | |
private struct ErrorArray { | |
private readonly int width; | |
private readonly int height; | |
private readonly double[,] array; | |
public ErrorArray(int width, int height) { | |
this.width = width; | |
this.height = height; | |
this.array = new double[width, height]; | |
} | |
public double this[int x, int y] { | |
get { | |
if (x < 0 || x >= width || y < 0 || y >= height) { | |
return 0; | |
} | |
return array[x, y]; | |
} | |
} | |
public void Add(int x, int y, double value) { | |
if (x < 0 || x >= width || y < 0 || y >= height) { | |
return; | |
} | |
array[x, y] += value; | |
} | |
public void AddRow(int x, int y, double residual, params int[] multipliers) { | |
for (var i = 0; i < multipliers.Length; i++) { | |
Add(x + i, y, residual * multipliers[i]); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This works great for small images, but I haven't tried it on larger once. If it's too slow or memory hungry, this code could be easily transformed so the operation is done in place with only additional O(image.Width) memory requirements, by:
Changing the doubles to floats would also probably have no noticeable impact.