func decodeFirstImage(local_path string) (firstImage image.Image, imageKind string, err error) { imf, err := os.Open(local_path) if err != nil { return nil, "", err } defer imf.Close() _, imageKind, err = image.DecodeConfig(imf) if err != nil { return nil, "", err } imf.Seek(0, 0) switch imageKind { case "gif": // Decode GIF frames until we reach the first fully opaque image: var g *gif.GIF g, err = gif.DecodeAll(imf) if err != nil { return nil, "", err } firstFrame := g.Image[0] g.Image = nil g.Delay = nil g = nil return firstFrame, imageKind, nil default: firstImage, imageKind, err = image.Decode(imf) if err != nil { return nil, "", err } 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) } }