package main import ( "fmt" "image" "image/color" _ "image/jpeg" "image/png" "math/rand" "os" "time" ) type coord struct { x, y int } // provided x, y, and color at location, return a color type quantizerFunction func(int, int, color.Color) color.Color // apply sequentially applies a quantizing function to an image and returns the result func apply(i image.Image, f quantizerFunction) image.Image { out := image.NewRGBA(image.Rect(0, 0, i.Bounds().Max.X, i.Bounds().Max.Y)) b := out.Bounds() for y := b.Min.Y; y < b.Max.Y; y++ { for x := b.Min.X; x < b.Max.X; x++ { out.Set(x, y, f(x, y, i.At(x, y))) } } return out } func main() { if len(os.Args) == 1 || len(os.Args) > 4 || os.Args[1] == "help" { fmt.Printf(`usage: %s Supported dither options are: noop; naive; randomnoise; bayer{0,1}; bayer{0,1}n Supported input image formats are jpg, png `, os.Args[0]) os.Exit(0) } filename := os.Args[1] ditherer := os.Args[2] outfile := os.Args[3] i, codex, err := loadImage(filename) if err != nil { fmt.Printf("error loading %s; %v\n", filename, err) os.Exit(255) } fmt.Printf("loaded %s using %s\n", filename, codex) t := time.Now() var new image.Image switch ditherer { case "noop": new = apply(i, noOp) case "naive": new = apply(i, naiveBW) case "palette": new = apply(i, naivePalette(sixteencolors)) case "randomnoise": rand.Seed(time.Now().UnixNano()) new = apply(i, randomNoise) case "noisepalette": rand.Seed(time.Now().UnixNano()) new = apply(i, randomNoisePalette(sixteencolors)) case "bayer0": new = apply(i, bayerDithering(0, false)) case "bayer0p": new = apply(i, colorBayer(0, sixteencolors)) case "bayer0n": new = apply(i, bayerDithering(0, true)) case "bayer1": new = apply(i, bayerDithering(1, false)) case "bayer1n": new = apply(i, bayerDithering(1, true)) case "bayer1p": new = apply(i, colorBayer(1, sixteencolors)) case "simpleerror": new = apply(i, simpleErrorDiffusion()) case "floydsteinberg": new = apply(i, floydSteinberg()) case "jjn": new = apply(i, jarvisJudiceNinke()) case "atkinson": new = apply(i, atkinson()) default: fmt.Printf("unknown ditherer option: %s\n", ditherer) os.Exit(2) } fmt.Printf("dithering took %s\n", time.Since(t)) err = saveImage(outfile, new) if err != nil { fmt.Printf("error saving %s; %v\n", outfile, err) os.Exit(255) } fmt.Printf("saved output to %s\n", outfile) } func loadImage(filename string) (image.Image, string, error) { r, err := os.Open(filename) if err != nil { return nil, "", err } return image.Decode(r) } func saveImage(filename string, img image.Image) error { f, err := os.Create(filename) if err != nil { return err } if err := png.Encode(f, img); err != nil { f.Close() return err } if err := f.Close(); err != nil { return err } return nil }