diff --git a/main.go b/main.go index 1f648dd..8130549 100644 --- a/main.go +++ b/main.go @@ -53,6 +53,10 @@ func main() { new = apply(i, simpleErrorDiffusion()) case "floydsteinberg": new = apply(i, floydSteinberg()) + case "jjn": + new = apply(i, jarvisJudiceNinke()) + case "atkinson": + new = apply(i, atkinson()) default: fmt.Printf("unknown ditherer option: %s\n", ditherer) os.Exit(2) diff --git a/quantizer.go b/quantizer.go index b7ceaa6..4d84e0d 100644 --- a/quantizer.go +++ b/quantizer.go @@ -180,6 +180,66 @@ func floydSteinberg() quantizerFunction { } } +func jarvisJudiceNinke() quantizerFunction { + errMap := make(map[coord]float64) + d := diffusion{ + divisor: 48.0, + matrix: map[coord]float64{ + {x: 1, y: 0}: 7.0, + {x: 2, y: 0}: 5.0, + + {x: -2, y: 1}: 3.0, + {x: -1, y: 1}: 5.0, + {x: 0, y: 1}: 7.0, + {x: 1, y: 1}: 5.0, + {x: 2, y: 1}: 3.0, + + {x: -2, y: 2}: 1.0, + {x: -1, y: 2}: 3.0, + {x: 0, y: 2}: 5.0, + {x: 1, y: 2}: 3.0, + {x: 2, y: 2}: 1.0, + }, + } + return func(x int, y int, c color.Color) color.Color { + p := coord{x: x, y: y} + l := luminence(c) + errMap[p] + delete(errMap, p) // don't let the error map grow too big + if l > 0.5 { + applyError(d, l-1.0, p, errMap) + return color.White + } + applyError(d, l, p, errMap) + return color.Black + } +} + +func atkinson() quantizerFunction { + errMap := make(map[coord]float64) + d := diffusion{ + divisor: 8.0, + matrix: map[coord]float64{ + {x: 1, y: 0}: 1.0, + {x: 2, y: 0}: 1.0, + {x: -1, y: 1}: 1.0, + {x: 0, y: 1}: 1.0, + {x: 1, y: 1}: 1.0, + {x: 0, y: 2}: 1.0, + }, + } + return func(x int, y int, c color.Color) color.Color { + p := coord{x: x, y: y} + l := luminence(c) + errMap[p] + delete(errMap, p) // don't let the error map grow too big + if l > 0.5 { + applyError(d, l-1.0, p, errMap) + return color.White + } + applyError(d, l, p, errMap) + return color.Black + } +} + // That is, "relative luminance": https://en.wikipedia.org/wiki/Relative_luminance. // go's color library doesn't give any information on what "color space" the RGBA is derived from, // so we convert to Y'CbCr, which returns luminence directly as the Y component.