2021-01-13 01:19:48 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"image/color"
|
|
|
|
"math/rand"
|
|
|
|
)
|
|
|
|
|
|
|
|
var bwpallette = color.Palette{
|
|
|
|
color.Black,
|
|
|
|
color.White,
|
|
|
|
}
|
|
|
|
|
|
|
|
var sixteencolors = color.Palette{
|
|
|
|
color.RGBA{0, 0, 0, 255}, // black
|
|
|
|
color.RGBA{0, 0, 127, 255}, // navy
|
|
|
|
color.RGBA{0, 0, 255, 255}, // blue
|
|
|
|
color.RGBA{0, 127, 0, 255}, // green
|
|
|
|
color.RGBA{0, 255, 0, 255}, // lime
|
|
|
|
color.RGBA{127, 0, 0, 255}, // maroon
|
|
|
|
color.RGBA{255, 0, 0, 255}, // red
|
|
|
|
color.RGBA{0, 127, 127, 255}, // teal
|
|
|
|
color.RGBA{127, 0, 127, 255}, // purple
|
|
|
|
color.RGBA{127, 127, 0, 255}, // olive
|
|
|
|
color.RGBA{0, 255, 255, 255}, // aqua
|
|
|
|
color.RGBA{255, 0, 255, 255}, // fuchsia
|
|
|
|
color.RGBA{255, 255, 0, 255}, // yellow
|
|
|
|
color.RGBA{127, 127, 127, 255}, // gray
|
|
|
|
color.RGBA{192, 192, 192, 255}, // silver
|
|
|
|
color.RGBA{255, 255, 255, 255}, // white
|
|
|
|
}
|
|
|
|
|
|
|
|
func permuteColor(c color.Color, i uint8) color.Color {
|
|
|
|
r, g, b, a := c.RGBA()
|
|
|
|
return color.RGBA{
|
|
|
|
uint8(r>>8) + i,
|
|
|
|
uint8(g>>8) + i,
|
|
|
|
uint8(b>>8) + i,
|
|
|
|
uint8(a >> 8),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// naivePalette smashes each pixel to its closest color in the pallette.
|
|
|
|
func naivePalette(p color.Palette) quantizerFunction {
|
|
|
|
return func(_, _ int, c color.Color) color.Color {
|
|
|
|
return p[p.Index(c)]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// randomNoisePalette injects random noise into the quantization step
|
|
|
|
func randomNoisePalette(p color.Palette) quantizerFunction {
|
2021-01-13 01:27:42 +00:00
|
|
|
r := len(p)
|
2021-01-13 01:19:48 +00:00
|
|
|
return func(_, _ int, c color.Color) color.Color {
|
|
|
|
// I think the proper theory here is probably "only try and randomize within one palette swatch in either direction".
|
|
|
|
// it might be possible to instead permute the color selected _from the palette_ (i.e. modify the result of p.Index(c))...
|
|
|
|
// ...but I think for that to work you'd need a proper "ordering" for the colors.
|
2021-01-13 01:27:42 +00:00
|
|
|
noise := rand.Intn(256) / r
|
2021-01-13 01:19:48 +00:00
|
|
|
rc := permuteColor(c, uint8(noise))
|
|
|
|
return p[p.Index(rc)]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// color permutation algo: https://en.wikipedia.org/wiki/Ordered_dithering#Algorithm
|
|
|
|
func colorBayer(level int, p color.Palette) quantizerFunction {
|
|
|
|
b := newBayer(level)
|
|
|
|
r := len(p)
|
|
|
|
return func(x, y int, c color.Color) color.Color {
|
|
|
|
v := float64(r) * (b.valueAt(x, y) - 0.5)
|
|
|
|
rc := permuteColor(c, uint8(v))
|
|
|
|
return p[p.Index(rc)]
|
|
|
|
}
|
|
|
|
}
|
2021-01-17 17:24:45 +00:00
|
|
|
|
|
|
|
func colorError(errMap map[coord]float64, d diffusion, palette color.Palette) quantizerFunction {
|
|
|
|
r := float64(len(palette))
|
|
|
|
return func(x int, y int, c color.Color) color.Color {
|
|
|
|
p := coord{x: x, y: y}
|
|
|
|
rc := permuteColor(c, uint8(r*errMap[p]))
|
|
|
|
delete(errMap, p) // don't let the error map grow too big
|
|
|
|
nc := palette[palette.Index(rc)]
|
|
|
|
l := luminence(c) - luminence(nc)
|
|
|
|
applyError(d, l, p, errMap)
|
|
|
|
return nc
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func simpleColorErrorDiffusion() quantizerFunction {
|
|
|
|
errMap := make(map[coord]float64)
|
|
|
|
d := diffusion{
|
|
|
|
divisor: 2.0,
|
|
|
|
matrix: map[coord]float64{
|
|
|
|
{x: 1, y: 0}: 1.0,
|
|
|
|
{x: 0, y: 1}: 1.0,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
return colorError(errMap, d, sixteencolors)
|
|
|
|
}
|
|
|
|
|
|
|
|
func colorJarvisJudiceNinke() 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 colorError(errMap, d, sixteencolors)
|
|
|
|
}
|