package main import ( "os" "image" "log" "image/png" "github.com/disintegration/gift" "flag" "math" "lcthw.dev/go/jankifier/filters" ) func LoadImage(filename string) image.Image { reader, err := os.Open(filename) if err != nil { log.Fatal(err) } defer reader.Close() img, _, err := image.Decode(reader) if err != nil { log.Fatal(err) } return img } func SaveImage(filename string, img image.Image) { out, err := os.Create(filename) if err != nil { log.Fatalf("can't write file %s: %v", filename, err) } defer out.Close() err = png.Encode(out, img) if err != nil { log.Fatalf("can't png encode %s: %v", filename, err) } } type Opts struct { InFile string OutFile string PixelWidth int ColorDepth int DitherType int } func ParseOpts() Opts { var opts Opts flag.StringVar(&opts.InFile, "input", "", "input file.png") flag.StringVar(&opts.OutFile, "output", "", "output file.png") flag.IntVar(&opts.PixelWidth, "pixel-width", 4, "pixel width") flag.IntVar(&opts.ColorDepth, "color-depth", 16, "number of colors in the palette") flag.IntVar(&opts.DitherType, "dither", 0, "0=none, 1=floyd, 2=atkinson") flag.Parse() if opts.ColorDepth > math.MaxUint8 { log.Fatalf("color-depth can't be greater than %d", math.MaxUint8); } return opts } func main() { opts := ParseOpts() src := LoadImage(opts.InFile) bounds := src.Bounds() resize := gift.Resize(bounds.Max.X / opts.PixelWidth, 0, gift.NearestNeighborResampling) posterize := filters.Posterize(uint16(opts.ColorDepth), opts.DitherType) upscale := filters.Upscale(bounds, opts.PixelWidth) sharpen := gift.UnsharpMask(1, 1, 0) g := gift.New(resize, posterize, upscale, sharpen) smaller := image.NewNRGBA(g.Bounds(bounds)) g.Draw(smaller, src) SaveImage(opts.OutFile, smaller) }