dither/color_quant.go

75 lines
2.2 KiB
Go
Raw Normal View History

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 {
return func(_, _ int, c color.Color) color.Color {
// the randomization here is tuned for the sixteen-color palette for now.
// 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.
noise := rand.Intn(64) - 32
if noise < 0 {
noise = 0
}
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)]
}
}