From 356f94698f59e0c5801b4597e186aa9ffa0a7bda Mon Sep 17 00:00:00 2001 From: Noah Petherbridge Date: Sat, 22 Jul 2023 11:51:58 -0700 Subject: [PATCH] Some bugfixes around profile picture cropping --- pkg/controller/photo/upload.go | 2 +- pkg/photo/upload.go | 63 ++++++++++++++++++++++++------ web/templates/account/profile.html | 2 +- 3 files changed, 52 insertions(+), 15 deletions(-) diff --git a/pkg/controller/photo/upload.go b/pkg/controller/photo/upload.go index 31a76fc..024c137 100644 --- a/pkg/controller/photo/upload.go +++ b/pkg/controller/photo/upload.go @@ -143,7 +143,7 @@ func Upload() http.HandlerFunc { } // Are we uploading a profile pic? If so, set the user's pic now. - if vars["Intent"] == "profile_pic" { + if vars["Intent"] == "profile_pic" && cropFilename != "" { log.Info("User %s is setting their profile picture", user.Username) user.ProfilePhoto = *p user.Save() diff --git a/pkg/photo/upload.go b/pkg/photo/upload.go index 0e1d1a5..0d893a7 100644 --- a/pkg/photo/upload.go +++ b/pkg/photo/upload.go @@ -40,12 +40,9 @@ func UploadPhoto(cfg UploadConfig) (string, string, error) { dbExtension = extension ) switch extension { - case ".jpg": - fallthrough - case ".jpe": - fallthrough - case ".jpeg": + case ".jpg", ".jpe", ".jpeg": extension = ".jpg" + dbExtension = ".jpg" case ".png": extension = ".png" dbExtension = ".jpg" @@ -114,12 +111,18 @@ func UploadPhoto(cfg UploadConfig) (string, string, error) { w = cfg.Crop[2] h = cfg.Crop[3] ) - croppedImg := Crop(origImage, x, y, w, h) + croppedImg, err := Crop(origImage, x, y, w, h) + if err != nil { + // Error during the crop: return it and just the original image filename + log.Error("Couldn't crop new profile photo: %s", err) + return filename, "", nil + } // Write that to disk, too. log.Debug("Writing cropped image to disk: %s", cropFilename) if err := ToDisk(cropFilename, extension, croppedImg, &cfg); err != nil { - return filename, "", err + log.Error("Couldn't write cropped photo to disk: %s", err) + return filename, "", nil } // Return both filenames! @@ -148,11 +151,35 @@ func Scale(src image.Image, rect image.Rectangle, scale draw.Scaler) image.Image // Crop an image, returning the new image. Example: // // cropped := Crop() -func Crop(src image.Image, x, y, w, h int) image.Image { +func Crop(src image.Image, x, y, w, h int) (image.Image, error) { + // Sanity check the crop constraints, e.g. sometimes the front-end might send "203 -1 738 738" with a negative x/y value + if x < 0 { + log.Debug("Crop(%d, %d, %d, %d): x value %d too low, cap to zero", x, y, w, h, x) + x = 0 + } + if y < 0 { + log.Debug("Crop(%d, %d, %d, %d): y value %d too low, cap to zero", x, y, w, h, y) + y = 0 + } + if x+w > src.Bounds().Dx() { + log.Debug("Crop(%d, %d, %d, %d): width is too wide", x, y, w, h) + w = src.Bounds().Dx() - x + } + if y+h > src.Bounds().Dy() { + log.Debug("Crop(%d, %d, %d, %d): height is too tall", x, y, w, h) + h = src.Bounds().Dy() - y + } + + // If they are trying to crop a 0x0 image, return an error. + if w == 0 || h == 0 { + return nil, errors.New("can't crop to a 0x0 resolution image") + } + + log.Debug("Crop(): running draw.Copy with dimensions %d, %d, %d, %d", x, y, w, h) dst := image.NewRGBA(image.Rect(0, 0, w, h)) srcrect := image.Rect(x, y, x+w, y+h) - draw.Copy(dst, image.ZP, src, srcrect, draw.Over, nil) - return dst + draw.Copy(dst, image.Point{}, src, srcrect, draw.Over, nil) + return dst, nil } // ReCrop an image, loading the original image from disk. Returns the newly created filename. @@ -170,7 +197,8 @@ func ReCrop(filename string, x, y, w, h int) (string, error) { // Decode the image. var img image.Image switch ext { - case ".jpg": + case ".jpg", ".jpeg", ".jpe": + // NOTE: new uploads enforce .jpg extension, some legacy pics may have slipped thru img, err = jpeg.Decode(fh) if err != nil { return "", err @@ -185,7 +213,10 @@ func ReCrop(filename string, x, y, w, h int) (string, error) { } // Crop it. - croppedImg := Crop(img, x, y, w, h) + croppedImg, err := Crop(img, x, y, w, h) + if err != nil { + return "", err + } // Write it. err = ToDisk(cropFilename, ext, croppedImg, nil) @@ -209,6 +240,11 @@ func ParseCropCoords(coords string) []int { } } } + + // If either the width or height would be zero, the coords aren't valid. + if coords[2] == 0 || coords[3] == 0 { + return nil + } return crop } @@ -237,7 +273,8 @@ func ToDisk(filename string, extension string, img image.Image, cfg *UploadConfi defer fh.Close() switch extension { - case ".jpg", ".png": + case ".jpg", ".jpe", ".jpeg", ".png": + // NOTE: new uploads enforce .jpg extension always, some legacy pics (.png too) may have slipped thru jpeg.Encode(fh, img, &jpeg.Options{ Quality: config.JpegQuality, }) diff --git a/web/templates/account/profile.html b/web/templates/account/profile.html index 16cf3ed..5cd571d 100644 --- a/web/templates/account/profile.html +++ b/web/templates/account/profile.html @@ -45,7 +45,7 @@ {{if or (not .LoggedIn) .IsPrivate}}

is on {{PrettyTitle}}, a social network for nudists & exhibitionists.

- {{PrettyTitle}} is a new social network for real nudists and exhibionists. + {{PrettyTitle}} is a new social network for real nudists and exhibitionists. Join {{.User.Username}} and the others on this site by creating an account and sending them a friend request! Please see the home page for all the details.