package main import ( "errors" "flag" "fmt" "image" "image/gif" "image/jpeg" "image/png" "os" "strings" "golang.org/x/image/bmp" "golang.org/x/image/draw" "golang.org/x/image/tiff" _ "golang.org/x/image/webp" ) var ( x = flag.Int("x", 0, "new image width") y = flag.Int("y", 0, "new image height") stretch = flag.Bool("stretch", false, "if resizing, whether to stretch the image rather than cropping (the default)") format = flag.String("format", "", "output image format, default is to use the file extension") ) func load(filename string) (image.Image, error) { file := os.Stdin if filename != "-" { var err error file, err = os.Open(filename) if err != nil { return nil, err } defer file.Close() } img, _, err := image.Decode(file) if err != nil { return nil, fmt.Errorf("decode %q: %s", filename, err) } return img, nil } func divRound(n, d int) int { return (n + d/2) / d } func resize(img image.Image) image.Image { x, y := *x, *y if x == 0 { x = divRound(y*img.Bounds().Dx(), img.Bounds().Dy()) } if y == 0 { y = divRound(x*img.Bounds().Dy(), img.Bounds().Dx()) } output := image.NewRGBA64(image.Rect(0, 0, x, y)) srcRect := img.Bounds() if !*stretch { if img.Bounds().Dx()*y >= img.Bounds().Dy()*x { scaledX := divRound(img.Bounds().Dy()*x, y) xOffset := (img.Bounds().Dx() - scaledX) / 2 srcRect.Min.X += xOffset srcRect.Max.X = srcRect.Min.X + scaledX } else { scaledY := divRound(img.Bounds().Dx()*y, x) yOffset := (img.Bounds().Dy() - scaledY) / 2 srcRect.Min.Y += yOffset srcRect.Max.Y = srcRect.Min.Y + scaledY } } draw.CatmullRom.Scale(output, output.Bounds(), img, srcRect, draw.Src, nil) return output } func run() error { args := flag.Args() if len(args) != 2 { return errors.New("usage: convert input-file output-file") } inputFileName, outputFileName := args[0], args[1] ext := *format if ext == "" { n := strings.LastIndex(outputFileName, ".") if n < 0 { return errors.New("unknown output file extension") } ext = outputFileName[n+1:] } switch ext { case "gif", "jpeg", "jpg", "png", "bmp", "tiff": default: return fmt.Errorf("unknown output file extension %q", ext) } input, err := load(inputFileName) if err != nil { return err } if *x != 0 || *y != 0 { input = resize(input) } outputFile, err := os.Create(outputFileName) if err != nil { return err } defer outputFile.Close() switch ext { case "gif": err = gif.Encode(outputFile, input, nil) case "jpeg", "jpg": err = jpeg.Encode(outputFile, input, &jpeg.Options{Quality: 100}) case "png": err = png.Encode(outputFile, input) case "bmp": err = bmp.Encode(outputFile, input) case "tiff": err = tiff.Encode(outputFile, input, nil) } if err != nil { return err } return outputFile.Close() } func main() { flag.Usage = func() { fmt.Fprint(os.Stderr, `Usage: convert input-file output-file Supported formats: png jpeg bmp tiff webp (input only) `) } flag.Parse() if err := run(); err != nil { fmt.Fprintf(os.Stderr, "convert: %s\n", err) os.Exit(1) } }