A simple tool I use to "jankify" my textures. It performs a series of transforms that gives everything a consistent pixelated 80s look.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
jankifier/filters/posterize.go

126 lines
2.7 KiB

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)
}
}
}
}