Floyd–Steinberg dithering

Gradient fill with error diffusion.
See also en.wikipedia.org/wiki/Floyd%E2%80%93Steinberg_dithering
This implementation zig-zags to reduce some artifact patterns.

I couldn't solid-fill the drawing area. Even at 0.1 step size there were gaps.

#dither #pixel #gradient

Log in to post a comment.

// Floyd-Steinberg dithering.  Implemented by @imakerobots
// https://turtletoy.net/turtle/d47e2bad0c

Canvas.setpenopacity(1);

// Global code will be evaluated once.
const turtle = new Turtle();
const level_of_detail=2;  // min=1.5, max=20, step=0.5

turtle.penup();
turtle.goto(-100,-100);
const stepSize = level_of_detail/10.0;
const stepsPerLine = Math.floor(200.0/stepSize);
stepsRemaining=stepsPerLine*stepsPerLine;
dx=1;

error1 = new Array(stepsPerLine);
error2 = new Array(stepsPerLine);
for(i=0;i<error1.length;++i) {
    error1[i] = error2[i] = 0;
}


function newLine() {
    for(var i=0;i<stepsPerLine;++i) {
        error1[i] = error2[i];
        error2[i] = 0;
    }
}

function get_pixel(x,y) {
    return error1[x] + y/stepsPerLine;
}

function find_closest_palette_color(oldpixel) {
    return oldpixel>0.5?1.0:0.0;
}

function walk(i) {
    var x=turtle.x()/stepSize+stepsPerLine/2;
    var y=turtle.y()/stepSize+stepsPerLine/2;
    if(x<0) x=0;
    if(x>=stepsPerLine) x=stepsPerLine-1;
    x=Math.floor(x);
    
    var oldpixel = get_pixel(x,y);
    var newpixel = find_closest_palette_color(oldpixel);
    var quant_error = oldpixel - newpixel;
    var xP1 = x+dx;
    var xM1 = x-dx;
    if(xP1>=0 && xP1<stepsPerLine) error1[xP1] += quant_error * 7.0/16.0;
    if(xM1>=0 && xM1<stepsPerLine) error2[xM1] += quant_error * 3.0/16.0;
                                   error2[x  ] += quant_error * 5.0/16.0;
    if(xP1>=0 && xP1<stepsPerLine) error2[xP1] += quant_error * 1.0/16.0;
    
    if(newpixel>0.5) {
        turtle.penup();
    } else {
        turtle.pendown();
    }
    
    turtle.forward(stepSize);
    if(turtle.x()>=100) {
        turtle.right(90);
        turtle.forward(stepSize);
        turtle.right(90);
        dx=-dx;
        newLine();
    } else if(turtle.x()<=-100) {
        turtle.left(90);
        turtle.forward(stepSize);
        turtle.left(90);
        dx=-dx;
        newLine();
    }
    
    return turtle.y()<100 && turtle.y()>=-100;//stepsRemaining-- > 0;
}