func makeThumbnail(img image.Image, dimensions int) (thumbImg image.Image) { // Calculate the largest square bounds for a thumbnail to preserve aspect ratio b := img.Bounds() srcBounds := b dx, dy := b.Dx(), b.Dy() if dx > dy { offs := (dx - dy) / 2 srcBounds.Min.X += offs srcBounds.Max.X -= offs } else if dy > dx { offs := (dy - dx) / 2 srcBounds.Min.Y += offs srcBounds.Max.Y -= offs } else { // Already square. } //log.Printf("'%s': resize %v to %v\n", filename, b, srcBounds) // Cut out the center square to a new image: boximg := imaging.SubImageKind(img, srcBounds) img = nil // Apply resizing algorithm: thumbImg = imaging.Resize(boximg, dimensions, dimensions, imaging.Lanczos) boximg = nil return }
// Crops an image and outputs a new image file: func cropImage(image_path string, left, top, right, bottom int) (tmp_output string, err error) { cropBounds := image.Rect(left, top, right, bottom) // Open the image for reading: imf, err := os.Open(image_path) if err != nil { return "", err } defer imf.Close() // Figure out what kind of image it is: _, imageKind, err := image.DecodeConfig(imf) if err != nil { return "", err } imf.Seek(0, 0) // Crop images: switch imageKind { case "gif": // Decode all GIF frames and crop them: // FIXME(jsd): This approach clearly does not work. An integrated decoder-encoder needs to be written // for animated GIFs so as to preserve as much of the encoding details as possible and crop the // transparent animation subframes over the full image. var g *gif.GIF g, err = gif.DecodeAll(imf) if err != nil { return "", err } // Crop all the frames: for i, img := range g.Image { if !cropBounds.In(img.Bounds()) { return "", fmt.Errorf("Crop boundaries are not contained within image boundaries") } g.Image[i] = imaging.CloneKind(imaging.SubImageKind(img, cropBounds)).(*image.Paletted) } // Write the cropped images to a new GIF: tmpf, err := TempFile(tmp_folder(), "crop-", ".gif") if err != nil { return "", err } defer tmpf.Close() err = gif.EncodeAll(tmpf, g) if err != nil { return "", err } g.Image = nil g.Delay = nil g = nil return tmpf.Name(), nil case "jpeg": img, err := jpeg.Decode(imf) if err != nil { return "", err } if !cropBounds.In(img.Bounds()) { return "", fmt.Errorf("Crop boundaries are not contained within image boundaries") } tmpf, err := TempFile(tmp_folder(), "crop-", ".jpg") if err != nil { return "", err } defer tmpf.Close() img = imaging.CloneKind(imaging.SubImageKind(img, cropBounds)) err = jpeg.Encode(tmpf, img, &jpeg.Options{Quality: 100}) if err != nil { return "", err } return tmpf.Name(), nil case "png": img, err := png.Decode(imf) if err != nil { return "", err } if !cropBounds.In(img.Bounds()) { return "", fmt.Errorf("Crop boundaries are not contained within image boundaries") } tmpf, err := TempFile(tmp_folder(), "crop-", ".png") if err != nil { return "", err } defer tmpf.Close() img = imaging.SubImageKind(img, cropBounds) err = png.Encode(tmpf, img) if err != nil { return "", err } return tmpf.Name(), nil default: return "", fmt.Errorf("Unrecognized image kind '%s'", imageKind) } }