// AddImage examines the provided image and compares it to the images already // stored in the map of prototype images. If this image is unique and doesn't // match any of the prototype images, including rotations or mirror copies, then // it is added to the map of prototype images. func (p *ImageSet) AddImage(img image.Image, x, y int) { index := y*p.stride + x images := make([]image.Image, 8) images[0] = img images[1] = imaging.Rotate90(img) images[2] = imaging.Rotate180(img) images[3] = imaging.Rotate270(img) images[4] = imaging.FlipH(img) images[5] = imaging.Rotate90(images[4]) images[6] = imaging.Rotate180(images[4]) images[7] = imaging.Rotate270(images[4]) found := false firstHash := uint64(0) for i := 0; i < 8; i++ { hash := Hash(images[i]) if i == 0 { firstHash = hash } if _, ok := p.protoImages[hash]; ok { found = true p.orientations[index] = byte(i) p.images[index] = hash break } } if !found { p.protoImages[firstHash] = img p.images[index] = firstHash p.orientations[index] = byte(0) } }
//Creating a thumbnail from a JPEG is straightforwards func (p *JpegPhoto) Thumbnail(in io.ReadSeeker, longSide int) (io.ReadSeeker, string, error) { //first we need to read the image. img, _, err := image.Decode(in) if err != nil { return nil, "", err } var w, h int aspect := float64(p.Width) / float64(p.Height) if p.Width > p.Height { w, h = longSide, int(float64(longSide)/aspect) } else { w, h = int(float64(longSide)*aspect), longSide } //we need to do this switch twice. first to check if we need to swap width/height //as we resize before rotation/flip //then after to do the resize/flip. switch p.Orientation { case OrientedNormal90, OrientedMirror90, OrientedNormal270, OrientedMirror270: //flip then rotate 270 w, h = h, w } //now create thumbnail. img = imaging.Thumbnail(img, w, h, imaging.Box) //now we need to rotate/flip it to match the ExifOrientation flag switch p.Orientation { case OrientedNormal: //nothing case OrientedMirror: //flip only img = imaging.FlipH(img) case OrientedNormal90: //rotate 90 img = imaging.Rotate90(img) case OrientedMirror90: //flip and rotate 90 img = imaging.FlipH(imaging.Rotate90(img)) case OrientedNormal180: //rotate 180 img = imaging.Rotate180(img) case OrientedMirror180: //flip then rotate 180 img = imaging.FlipH(imaging.Rotate180(img)) case OrientedNormal270: //rotate 270 (90 anti-clockwise) img = imaging.Rotate270(img) case OrientedMirror270: //flip then rotate 270 img = imaging.FlipH(imaging.Rotate270(img)) } //now re-encode var wr bytes.Buffer err = jpeg.Encode(&wr, img, nil) return bytes.NewReader(wr.Bytes()), "image/jpeg", err }
func MakeThumb(r io.Reader, w, h int, orient int) ([]byte, error) { img, _, err := image.Decode(r) if err != nil { return nil, err } m := resize.Resize(uint(w), uint(h), img, resize.Bicubic) switch orient { case 3, 4: m = imaging.Rotate180(m) case 5, 6: m = imaging.Rotate270(m) case 7, 8: m = imaging.Rotate90(m) } switch orient { case 2, 5, 4, 7: m = imaging.FlipH(m) } var buf bytes.Buffer err = jpeg.Encode(&buf, m, nil) if err != nil { return nil, err } return buf.Bytes(), nil }
func (p *program) editImage(rot Rot, folder string, list []string) error { for _, item := range list { filename := filepath.Join(config.FileRoot, folder, item) fi, err := os.Stat(filename) if err != nil { return err } fullImage, err := imaging.Open(filename) if err != nil { return err } switch rot { case RotLeft: fullImage = imaging.Rotate90(fullImage) case RotRight: fullImage = imaging.Rotate270(fullImage) case RotFlip: fullImage = imaging.Rotate180(fullImage) default: return fmt.Errorf("Unknown rot value: %d", rot) } err = imaging.Save(fullImage, filename) if err != nil { return err } tm := fi.ModTime() err = os.Chtimes(filename, tm, tm) if err != nil { return err } } return p.refreshCache(folder, list) }
// transformImage modifies the image m based on the transformations specified // in opt. func transformImage(m image.Image, opt Options) image.Image { // resize if needed if w, h, resize := resizeParams(m, opt); resize { if opt.Fit { m = imaging.Fit(m, w, h, resampleFilter) } else { if w == 0 || h == 0 { m = imaging.Resize(m, w, h, resampleFilter) } else { m = imaging.Thumbnail(m, w, h, resampleFilter) } } } // flip if opt.FlipVertical { m = imaging.FlipV(m) } if opt.FlipHorizontal { m = imaging.FlipH(m) } // rotate switch opt.Rotate { case 90: m = imaging.Rotate90(m) case 180: m = imaging.Rotate180(m) case 270: m = imaging.Rotate270(m) } return m }
func GetRotatedImage(src io.Reader) (image.Image, string, error) { raw, err := ioutil.ReadAll(src) if err != nil { return nil, "", err } data := bytes.NewReader(raw) image, format, err := image.Decode(data) if err != nil { return nil, "", err } if _, err := data.Seek(0, 0); err != nil { return nil, "", err } angle := needsRotation(data) switch angle { case 90: image = imaging.Rotate90(image) case 180: image = imaging.Rotate180(image) case 270: image = imaging.Rotate270(image) } return image, format, nil }
//Creating a thumbnail from a mp4 is complex so I cheat and use FFMPEG to create a JPEG... //JPEG is straightforwards // but ffmpeg probably can't make a thumbnail from a piped reader, so this only works if our //ReadSeeker is actually an *os.File func (m *Mp4Video) Thumbnail(in io.ReadSeeker, longSide int) (io.ReadSeeker, string, error) { var cmd *exec.Cmd if file, ok := in.(*os.File); ok { //this is the best way as ffmpeg can seek. cmd = exec.Command("ffmpeg", "-i", "/dev/fd/3", "-vframes", "1", "-f", "image2", "-") cmd.ExtraFiles = []*os.File{file} } else { log.Println("mp4thumb: using stdin (will probably fail...)") cmd = exec.Command("ffmpeg", "-i", "-", "-vframes", "1", "-f", "image2", "-") cmd.Stdin = in } stdout, err := cmd.StdoutPipe() //cmd.Stderr = os.Stderr if err != nil { return nil, "", err } if err := cmd.Start(); err != nil { return nil, "", err } img, err := jpeg.Decode(stdout) if err != nil { return nil, "", err } if err := cmd.Wait(); err != nil { return nil, "", err } //now we should have a jpeg to resize! var w, h int aspect := float64(m.Width) / float64(m.Height) if m.Width > m.Height { w, h = longSide, int(float64(longSide)/aspect) } else { w, h = int(float64(longSide)*aspect), longSide } switch m.Orientation { case photo.OrientedNormal90, photo.OrientedNormal270: //flip then rotate 270 w, h = h, w } //now create thumbnail. img = imaging.Thumbnail(img, w, h, imaging.Box) //rotate if needed. switch m.Orientation { case photo.OrientedNormal90: //rotate 90 (270 anticlockwise) img = imaging.Rotate270(img) case photo.OrientedNormal180: //rotate 180 img = imaging.Rotate180(img) case photo.OrientedNormal270: //rotate 270 (90 anti-clockwise) img = imaging.Rotate90(img) } var wr bytes.Buffer err = jpeg.Encode(&wr, img, nil) return bytes.NewReader(wr.Bytes()), "image/jpeg", err }
func rotate(img image.Image, orientation int) image.Image { fmt.Println("orientation:", orientation) // 1 2 3 4 5 6 7 8 // 888888 888888 88 88 8888888888 88 88 8888888888 // 88 88 88 88 88 88 88 88 88 88 88 88 // 8888 8888 8888 8888 88 8888888888 8888888888 88 // 88 88 88 88 // 88 88 888888 888888 // func Rotate180(img image.Image) *image.NRGBA // func Rotate270(img image.Image) *image.NRGBA // func Rotate90(img image.Image) *image.NRGBA // func FlipH(img image.Image) *image.NRGBA // func FlipV(img image.Image) *image.NRGBA var out image.Image switch orientation { case 1: out = img // nothing; case 2: out = imaging.FlipH(img) // flip Horiz L to R case 3: out = imaging.Rotate180(img) // rotate 180 ccw case 4: out = imaging.FlipV(img) // flip Vert T to B case 5: out = imaging.Transpose(img) // transpose case 6: out = imaging.Rotate90(img) // rotate 90 case 7: out = imaging.Transverse(img) // transverse case 8: out = imaging.Rotate270(img) // rotate 270 default: out = img // nothing; } return out }
func handleImages(previewPathList []string, thumbnailPathList []string, fileData [][]byte) { for i, data := range fileData { go func(i int, data []byte) { // Decode image bytes into Image object img, imgType, err := image.Decode(bytes.NewReader(fileData[i])) if err != nil { l4g.Error(utils.T("api.file.handle_images_forget.decode.error"), err) return } width := img.Bounds().Dx() height := img.Bounds().Dy() // Fill in the background of a potentially-transparent png file as white if imgType == "png" { dst := image.NewRGBA(img.Bounds()) draw.Draw(dst, dst.Bounds(), image.NewUniform(color.White), image.Point{}, draw.Src) draw.Draw(dst, dst.Bounds(), img, img.Bounds().Min, draw.Over) img = dst } // Flip the image to be upright orientation, _ := getImageOrientation(fileData[i]) switch orientation { case UprightMirrored: img = imaging.FlipH(img) case UpsideDown: img = imaging.Rotate180(img) case UpsideDownMirrored: img = imaging.FlipV(img) case RotatedCWMirrored: img = imaging.Transpose(img) case RotatedCCW: img = imaging.Rotate270(img) case RotatedCCWMirrored: img = imaging.Transverse(img) case RotatedCW: img = imaging.Rotate90(img) } go generateThumbnailImage(img, thumbnailPathList[i], width, height) go generatePreviewImage(img, previewPathList[i], width) }(i, data) } }
func prepareImage(fileData []byte) (*image.Image, int, int) { // Decode image bytes into Image object img, imgType, err := image.Decode(bytes.NewReader(fileData)) if err != nil { l4g.Error(utils.T("api.file.handle_images_forget.decode.error"), err) return nil, 0, 0 } width := img.Bounds().Dx() height := img.Bounds().Dy() // Fill in the background of a potentially-transparent png file as white if imgType == "png" { dst := image.NewRGBA(img.Bounds()) draw.Draw(dst, dst.Bounds(), image.NewUniform(color.White), image.Point{}, draw.Src) draw.Draw(dst, dst.Bounds(), img, img.Bounds().Min, draw.Over) img = dst } // Flip the image to be upright orientation, _ := getImageOrientation(fileData) switch orientation { case UprightMirrored: img = imaging.FlipH(img) case UpsideDown: img = imaging.Rotate180(img) case UpsideDownMirrored: img = imaging.FlipV(img) case RotatedCWMirrored: img = imaging.Transpose(img) case RotatedCCW: img = imaging.Rotate270(img) case RotatedCCWMirrored: img = imaging.Transverse(img) case RotatedCW: img = imaging.Rotate90(img) } return &img, width, height }
// Transforms image (resize, rotate, flip, brightness, contrast) func (c *Convertor) TransformImage(img image.Image) image.Image { var i image.Image = img if c.Opts.Width > 0 || c.Opts.Height > 0 { if c.Opts.Fit { i = imaging.Fit(i, c.Opts.Width, c.Opts.Height, filters[c.Opts.Filter]) } else { i = imaging.Resize(i, c.Opts.Width, c.Opts.Height, filters[c.Opts.Filter]) } } if c.Opts.Rotate > 0 { switch c.Opts.Rotate { case 90: i = imaging.Rotate90(i) case 180: i = imaging.Rotate180(i) case 270: i = imaging.Rotate270(i) } } if c.Opts.Flip != "none" { switch c.Opts.Flip { case "horizontal": i = imaging.FlipH(i) case "vertical": i = imaging.FlipV(i) } } if c.Opts.Brightness != 0 { i = imaging.AdjustBrightness(i, c.Opts.Brightness) } if c.Opts.Contrast != 0 { i = imaging.AdjustContrast(i, c.Opts.Contrast) } return i }
func thumbnailHandler(w http.ResponseWriter, r *http.Request) { query := r.URL.Query() height, err := strconv.Atoi(query.Get("height")) if err != nil { height = 50 } path := filepath.Join(storage, r.URL.Path) photo, err := imaging.Open(path) if err != nil { fmt.Fprint(w, err) return } switch readRotation(path) { case 8: photo = imaging.Rotate90(photo) case 2: photo = imaging.Rotate180(photo) case 6: photo = imaging.Rotate270(photo) } thumb := imaging.Resize(photo, 0, height, imaging.Box) imaging.Encode(w, thumb, imaging.JPEG) }
func handleImages(filenames []string, fileData [][]byte, teamId, channelId, userId string) { dest := "teams/" + teamId + "/channels/" + channelId + "/users/" + userId + "/" for i, filename := range filenames { name := filename[:strings.LastIndex(filename, ".")] go func() { // Decode image bytes into Image object img, imgType, err := image.Decode(bytes.NewReader(fileData[i])) if err != nil { l4g.Error(utils.T("api.file.handle_images_forget.decode.error"), channelId, userId, filename, err) return } width := img.Bounds().Dx() height := img.Bounds().Dy() // Get the image's orientation and ignore any errors since not all images will have orientation data orientation, _ := getImageOrientation(fileData[i]) if imgType == "png" { dst := image.NewRGBA(img.Bounds()) draw.Draw(dst, dst.Bounds(), image.NewUniform(color.White), image.Point{}, draw.Src) draw.Draw(dst, dst.Bounds(), img, img.Bounds().Min, draw.Over) img = dst } switch orientation { case UprightMirrored: img = imaging.FlipH(img) case UpsideDown: img = imaging.Rotate180(img) case UpsideDownMirrored: img = imaging.FlipV(img) case RotatedCWMirrored: img = imaging.Transpose(img) case RotatedCCW: img = imaging.Rotate270(img) case RotatedCCWMirrored: img = imaging.Transverse(img) case RotatedCW: img = imaging.Rotate90(img) } // Create thumbnail go func() { thumbWidth := float64(utils.Cfg.FileSettings.ThumbnailWidth) thumbHeight := float64(utils.Cfg.FileSettings.ThumbnailHeight) imgWidth := float64(width) imgHeight := float64(height) var thumbnail image.Image if imgHeight < thumbHeight && imgWidth < thumbWidth { thumbnail = img } else if imgHeight/imgWidth < thumbHeight/thumbWidth { thumbnail = imaging.Resize(img, 0, utils.Cfg.FileSettings.ThumbnailHeight, imaging.Lanczos) } else { thumbnail = imaging.Resize(img, utils.Cfg.FileSettings.ThumbnailWidth, 0, imaging.Lanczos) } buf := new(bytes.Buffer) err = jpeg.Encode(buf, thumbnail, &jpeg.Options{Quality: 90}) if err != nil { l4g.Error(utils.T("api.file.handle_images_forget.encode_jpeg.error"), channelId, userId, filename, err) return } if err := WriteFile(buf.Bytes(), dest+name+"_thumb.jpg"); err != nil { l4g.Error(utils.T("api.file.handle_images_forget.upload_thumb.error"), channelId, userId, filename, err) return } }() // Create preview go func() { var preview image.Image if width > int(utils.Cfg.FileSettings.PreviewWidth) { preview = imaging.Resize(img, utils.Cfg.FileSettings.PreviewWidth, utils.Cfg.FileSettings.PreviewHeight, imaging.Lanczos) } else { preview = img } buf := new(bytes.Buffer) err = jpeg.Encode(buf, preview, &jpeg.Options{Quality: 90}) if err != nil { l4g.Error(utils.T("api.file.handle_images_forget.encode_preview.error"), channelId, userId, filename, err) return } if err := WriteFile(buf.Bytes(), dest+name+"_preview.jpg"); err != nil { l4g.Error(utils.T("api.file.handle_images_forget.upload_preview.error"), channelId, userId, filename, err) return } }() }() } }
func storeImage(rw http.ResponseWriter, req *http.Request) { // Appengine var c appengine.Context // Google Cloud Storage authentication var cc gcscontext.Context // Google Cloud Storage bucket name var bucketName string = "" // Google Cloud Storage client var client *storage.Client // Google Cloud Storage bucket var bucketHandle *storage.BucketHandle // User uploaded image file name var fileName string = uuid.New() // Transform user uploaded image to a thumbnail file name var fileNameThumbnail string = uuid.New() // User uploaded image file type var contentType string = "" // User uploaded image file raw data var b []byte // Google Cloud Storage file writer var wc *storage.Writer = nil // Error var err error = nil // Result, 0: success, 1: failed var r int = http.StatusCreated // Set response in the end defer func() { // Return status. WriteHeader() must be called before call to Write if r == http.StatusCreated { // Changing the header after a call to WriteHeader (or Write) has no effect. // rw.Header().Set("Location", req.URL.String()+"/"+cKey.Encode()) rw.Header().Set("Location", "http://"+bucketName+".storage.googleapis.com/"+fileName) rw.Header().Set("X-Thumbnail", "http://"+bucketName+".storage.googleapis.com/"+fileNameThumbnail) rw.WriteHeader(r) } else { http.Error(rw, http.StatusText(r), r) } }() // To log information in Google APP Engine console c = appengine.NewContext(req) // Get data from body b, err = ioutil.ReadAll(req.Body) if err != nil { c.Errorf("%s in reading body", err) r = http.StatusInternalServerError return } c.Infof("Body length %d bytes, read %d bytes", req.ContentLength, len(b)) // Determine filename extension from content type contentType = req.Header["Content-Type"][0] switch contentType { case "image/jpeg": fileName += ".jpg" fileNameThumbnail += ".jpg" default: c.Errorf("Unknown or unsupported content type '%s'. Valid: image/jpeg", contentType) r = http.StatusBadRequest return } c.Infof("Content type %s is received, %s is detected.", contentType, http.DetectContentType(b)) // Prepare Google Cloud Storage authentication cc = gcsappengine.NewContext(req) if client, err = storage.NewClient(cc); err != nil { c.Errorf("%s in initializing a GCS client", err) r = http.StatusInternalServerError return } defer client.Close() // Get default bucket if bucketName, err = gcsfile.DefaultBucketName(cc); err != nil { c.Errorf("%s in getting default GCS bucket name", err) r = http.StatusInternalServerError return } bucketHandle = client.Bucket(bucketName) c.Infof("APP Engine Version: %s", gcsappengine.VersionID(cc)) c.Infof("Using bucket name: %s", bucketName) // Change default object ACLs if err = bucketHandle.DefaultObjectACL().Set(cc, storage.AllUsers, storage.RoleReader); err != nil { c.Errorf("%v in saving default object ACL rule for bucket %q", err, bucketName) r = http.StatusInternalServerError return } // Store rotated image in Google Cloud Storage var in *bytes.Reader = bytes.NewReader(b) var x *exif.Exif = nil var orientation *tiff.Tag = nil var beforeImage image.Image var afterImage *image.NRGBA = nil // Read EXIF if _, err = in.Seek(0, 0); err != nil { c.Errorf("%s in moving the reader offset to the beginning in order to read EXIF", err) return } if x, err = exif.Decode(in); err != nil { c.Errorf("%s in decoding JPEG image", err) return } // Get Orientation if orientation, err = x.Get(exif.Orientation); err != nil { c.Warningf("%s in getting orientation from EXIF", err) return } c.Debugf("Orientation %s", orientation.String()) // Open image if _, err = in.Seek(0, 0); err != nil { c.Errorf("%s in moving the reader offset to the beginning in order to read EXIF", err) return } if beforeImage, err = imaging.Decode(in); err != nil { c.Errorf("%s in opening image %s", err) return } switch orientation.String() { case "1": afterImage = beforeImage.(*image.NRGBA) case "2": afterImage = imaging.FlipH(beforeImage) case "3": afterImage = imaging.Rotate180(beforeImage) case "4": afterImage = imaging.FlipV(beforeImage) case "5": afterImage = imaging.Transverse(beforeImage) case "6": afterImage = imaging.Rotate270(beforeImage) case "7": afterImage = imaging.Transpose(beforeImage) case "8": afterImage = imaging.Rotate90(beforeImage) } // Save rotated image wc = bucketHandle.Object(fileName).NewWriter(cc) wc.ContentType = contentType if err = imaging.Encode(wc, afterImage, imaging.JPEG); err != nil { c.Errorf("%s in saving rotated image", err) return } if err = wc.Close(); err != nil { c.Errorf("CreateFile: unable to close bucket %q, file %q: %v", bucketName, fileName, err) r = 1 return } wc = nil // Make thumbnail if afterImage.Rect.Dx() > afterImage.Rect.Dy() { afterImage = imaging.Resize(afterImage, 1920, 0, imaging.Lanczos) } else { afterImage = imaging.Resize(afterImage, 0, 1920, imaging.Lanczos) } // Save thumbnail wc = bucketHandle.Object(fileNameThumbnail).NewWriter(cc) wc.ContentType = contentType if imaging.Encode(wc, afterImage, imaging.JPEG); err != nil { c.Errorf("%s in saving image thumbnail", err) return } if err = wc.Close(); err != nil { c.Errorf("CreateFileThumbnail: unable to close bucket %q, file %q: %v", bucketName, fileNameThumbnail, err) r = 1 return } c.Infof("/%v/%v, /%v/%v created", bucketName, fileName, bucketName, fileNameThumbnail) }
// transformImage modifies the image m based on the transformations specified // in opt. func transformImage(m image.Image, opt Options) image.Image { // convert percentage width and height values to absolute values imgW := m.Bounds().Max.X - m.Bounds().Min.X imgH := m.Bounds().Max.Y - m.Bounds().Min.Y var w, h int if 0 < opt.Width && opt.Width < 1 { w = int(float64(imgW) * opt.Width) } else if opt.Width < 0 { w = 0 } else { w = int(opt.Width) } if 0 < opt.Height && opt.Height < 1 { h = int(float64(imgH) * opt.Height) } else if opt.Height < 0 { h = 0 } else { h = int(opt.Height) } // never resize larger than the original image if !opt.ScaleUp { if w > imgW { w = imgW } if h > imgH { h = imgH } } // resize if w != 0 || h != 0 { if opt.Fit { m = imaging.Fit(m, w, h, resampleFilter) } else { if w == 0 || h == 0 { m = imaging.Resize(m, w, h, resampleFilter) } else { m = imaging.Thumbnail(m, w, h, resampleFilter) } } } // flip if opt.FlipVertical { m = imaging.FlipV(m) } if opt.FlipHorizontal { m = imaging.FlipH(m) } // rotate switch opt.Rotate { case 90: m = imaging.Rotate90(m) case 180: m = imaging.Rotate180(m) case 270: m = imaging.Rotate270(m) } return m }
func (i imagingFlipper) FlipDiagonal(tile image.Image) image.Image { return imaging.FlipH(imaging.Rotate270(tile)) }
func main() { fmt.Println("Hello, world") fname := "P1070332.JPG" rname := "P1070332_rotate.JPG" sname := "P1070332_small.JPG" f, err := os.Open(fname) if err != nil { log.Fatal(err) } defer f.Close() // Optionally register camera makenote data parsing - currently Nikon and // Canon are supported. exif.RegisterParsers(mknote.All...) x, err := exif.Decode(f) if err != nil { log.Fatal(err) } // Get Orientation orientation, err := x.Get(exif.Orientation) if err != nil { fmt.Println(exif.Model, " not fround") return } fmt.Println("Orientation", orientation.String()) // Rotate var rotateImage *image.NRGBA openImage, err := imaging.Open(fname) if err != nil { fmt.Println(err) return } switch orientation.String() { case "1": // Do nothing case "2": rotateImage = imaging.FlipH(openImage) case "3": rotateImage = imaging.Rotate180(openImage) case "4": rotateImage = imaging.FlipV(openImage) case "5": rotateImage = imaging.Transverse(openImage) case "6": rotateImage = imaging.Rotate270(openImage) case "7": rotateImage = imaging.Transpose(openImage) case "8": rotateImage = imaging.Rotate90(openImage) } err = imaging.Save(rotateImage, rname) if err != nil { fmt.Println(err) return } fmt.Println(rname, " saved") // Small var smallImage *image.NRGBA if rotateImage.Rect.Dx() > rotateImage.Rect.Dy() { smallImage = imaging.Resize(rotateImage, 1920, 0, imaging.Lanczos) } else { smallImage = imaging.Resize(rotateImage, 0, 1920, imaging.Lanczos) } err = imaging.Save(smallImage, sname) if err != nil { fmt.Println(err) return } fmt.Println(sname, " saved") // Use jpeg.Encode() to write to a file // https://github.com/disintegration/imaging/blob/master/helpers.go#L79 // func Encode(w io.Writer, m image.Image, o *Options) error // https://golang.org/pkg/image/jpeg/ }
func handleServeImage(ctx context.Context, w http.ResponseWriter, r *http.Request) { img, err := ImageByID(sq.DB(ctx), web.Args(ctx).ByIndex(0)) switch err { case nil: // all good case sq.ErrNotFound: web.StdJSONResp(w, http.StatusNotFound) return default: log.Error("cannot get object", "object", web.Args(ctx).ByIndex(0), "error", err.Error()) web.StdJSONResp(w, http.StatusInternalServerError) return } if web.CheckLastModified(w, r, img.Created) { return } fs := FileStore(ctx) fd, err := fs.Read(img.Created.Year(), img.ImageID) if err != nil { log.Error("cannot read image file", "image", img.ImageID, "error", err.Error()) web.StdJSONResp(w, http.StatusInternalServerError) return } defer fd.Close() w.Header().Set("X-Image-ID", img.ImageID) w.Header().Set("X-Image-Width", fmt.Sprint(img.Width)) w.Header().Set("X-Image-Height", fmt.Sprint(img.Height)) w.Header().Set("X-Image-Created", img.Created.Format(time.RFC3339)) w.Header().Set("Content-Type", "image/jpeg") if r.URL.Query().Get("resize") == "" { io.Copy(w, fd) return } image, err := jpeg.Decode(fd) if err != nil { log.Error("cannot read image file", "image", img.ImageID, "error", err.Error()) web.StdJSONResp(w, http.StatusInternalServerError) return } var width, height int if _, err := fmt.Sscanf(r.URL.Query().Get("resize"), "%dx%d", &width, &height); err != nil { log.Error("cannot resize image", "image", img.ImageID, "error", err.Error()) } else { switch img.Orientation { case 1: // all good case 3: image = imaging.Rotate180(image) case 8: image = imaging.Rotate90(image) case 6: image = imaging.Rotate270(image) default: log.Debug("unknown image orientation", "decoder", "EXIF", "image", img.ImageID, "value", fmt.Sprint(img.Orientation)) } image = imaging.Fill(image, width, height, imaging.Center, imaging.Linear) } imaging.Encode(w, image, imaging.JPEG) }