package filters import ( "image" "image/draw" "image/color" "math" "github.com/disintegration/gift" ) const ( NoDither=0 FloydSteinDither=1 AtkinsonDither=2 ) const ( BIN_SIZE=math.MaxUint16+1 ) type PosterizeFilter struct { Depth uint16 Bins [BIN_SIZE]uint16 DitherType int } func Posterize(depth uint16, dither_type int) gift.Filter { var i uint16 var bins [BIN_SIZE]uint16 chunk := uint16((BIN_SIZE) / int(depth)) // BUG: this was fine with uint8, now it's dumb as hell for i = 1; i < math.MaxUint16; i++ { bins[i] = (i / chunk) * chunk } return PosterizeFilter{depth, bins, dither_type} } func (filter PosterizeFilter) Bounds(src image.Rectangle) (dst_bounds image.Rectangle) { return src } func (filter PosterizeFilter) Quantize(c color.NRGBA64) (color.NRGBA64, color.NRGBA64) { newpixel := color.NRGBA64{ filter.Bins[c.R], filter.Bins[c.G], filter.Bins[c.B], c.A, } err := color.NRGBA64{ c.R - newpixel.R, c.G - newpixel.G, c.B - newpixel.B, c.A, } return newpixel, err } func DistError(old color.Color, quant_err color.NRGBA64, term float32) color.Color { dumb := old.(color.NRGBA64) c := color.NRGBA64{ dumb.R + uint16(float32(quant_err.R) * term), dumb.G + uint16(float32(quant_err.G) * term), dumb.B + uint16(float32(quant_err.B) * term), dumb.A, } return c } func (filter PosterizeFilter) FloydStein(x int, y int, dst draw.Image, quant_err color.NRGBA64) { dst.Set(x+1, y, DistError(dst.At(x+1, y), quant_err, 7.0/16.0)) dst.Set(x-1, y+1, DistError(dst.At(x-1, y+1), quant_err, 3.0/16.0)) dst.Set(x, y+1, DistError(dst.At(x, y+1), quant_err, 5.0/16.0)) dst.Set(x+1, y+1, DistError(dst.At(x+1, y+1), quant_err, 1.0/16.0)) } func (filter PosterizeFilter) Atkinson(x int, y int, dst draw.Image, quant_err color.NRGBA64) { delta := [6]image.Point{ {x+1, y+0}, {x+2, y+0}, {x-1, y+1}, {x+0, y+1}, {x+1, y+1}, {x+0, y+2}, } bounds := dst.Bounds() for _, d := range(delta) { if d.In(bounds) { dst.Set(d.X, d.Y, DistError(dst.At(d.X, d.Y), quant_err, 1.0/8.0)) } } } func (filter PosterizeFilter) Draw(dst draw.Image, src image.Image, _ *gift.Options) { bounds := src.Bounds() gift.New().Draw(dst, src) for y := bounds.Min.Y; y < bounds.Max.Y; y++ { for x := bounds.Min.X; x < bounds.Max.X; x++ { from := dst.At(x, y) c := from.(color.NRGBA64) newpixel, quant_err := filter.Quantize(c) dst.Set(x, y, newpixel) switch filter.DitherType { case FloydSteinDither: filter.FloydStein(x, y, dst, quant_err) case AtkinsonDither: filter.Atkinson(x, y, dst, quant_err) } } } }