diff --git a/halfshell/image.go b/halfshell/image.go index eb86c55..8b69100 100644 --- a/halfshell/image.go +++ b/halfshell/image.go @@ -25,6 +25,7 @@ import ( "io" "io/ioutil" "os" + "strconv" "strings" "github.com/rafikk/imagick/imagick" @@ -32,6 +33,7 @@ import ( var EmptyImageDimensions = ImageDimensions{} var EmptyResizeDimensions = ResizeDimensions{} +var DefaultFocalPoint = Focalpoint{0.5, 0.5} type Image struct { Wand *imagick.MagickWand @@ -109,3 +111,31 @@ type ResizeDimensions struct { Scale ImageDimensions Crop ImageDimensions } + +// Focalpoint is a float pair representing the location of the image subject. +// (0.5, 0.5) is the middle. (1, 1) is the bottom right. (0, 0) is the top left. +type Focalpoint struct { + X float64 + Y float64 +} + +// NewFocalpointFromString splits the given string into a Focalpoint struct. The +// string format should be: "X,Y". For example: "0.1,0.1". +func NewFocalpointFromString(s string) (fp Focalpoint) { + pair := strings.Split(s, ",") + if len(pair) != 2 { + return DefaultFocalPoint + } + + x, err := strconv.ParseFloat(pair[0], 64) + if err != nil { + return DefaultFocalPoint + } + + y, err := strconv.ParseFloat(pair[1], 64) + if err != nil { + return DefaultFocalPoint + } + + return Focalpoint{x, y} +} diff --git a/halfshell/image_processor.go b/halfshell/image_processor.go index 42170ad..8e53895 100644 --- a/halfshell/image_processor.go +++ b/halfshell/image_processor.go @@ -48,6 +48,7 @@ type ImageProcessorOptions struct { Dimensions ImageDimensions BlurRadius float64 ScaleMode uint + Focalpoint Focalpoint } type imageProcessor struct { @@ -153,7 +154,7 @@ func (ip *imageProcessor) resize(img *Image, req *ImageProcessorOptions) error { } if resize.Crop != EmptyImageDimensions { - err = ip.cropApply(img, resize.Crop) + err = ip.cropApply(img, resize.Crop, req.Focalpoint) if err != nil { return err } @@ -292,17 +293,13 @@ func (ip *imageProcessor) resizeApply(img *Image, dimensions ImageDimensions) er return nil } -func (ip *imageProcessor) cropApply(img *Image, reqDimensions ImageDimensions) error { - if reqDimensions == EmptyImageDimensions { - return nil - } +func (ip *imageProcessor) cropApply(img *Image, reqDimensions ImageDimensions, focalpoint Focalpoint) error { oldDimensions := img.GetDimensions() - return img.Wand.CropImage( - reqDimensions.Width, - reqDimensions.Height, - int((oldDimensions.Width-reqDimensions.Width)/2), - int((oldDimensions.Height-reqDimensions.Height)/2), - ) + x := int(focalpoint.X * (float64(oldDimensions.Width) - float64(reqDimensions.Width))) + y := int(focalpoint.Y * (float64(oldDimensions.Height) - float64(reqDimensions.Height))) + w := reqDimensions.Width + h := reqDimensions.Height + return img.Wand.CropImage(w, h, x, y) } func (ip *imageProcessor) blur(image *Image, request *ImageProcessorOptions) error { diff --git a/halfshell/route.go b/halfshell/route.go index c54ed60..12a529d 100644 --- a/halfshell/route.go +++ b/halfshell/route.go @@ -69,6 +69,7 @@ func (p *Route) SourceAndProcessorOptionsForRequest(r *http.Request) ( width, _ := strconv.ParseUint(r.FormValue("w"), 10, 32) height, _ := strconv.ParseUint(r.FormValue("h"), 10, 32) blurRadius, _ := strconv.ParseFloat(r.FormValue("blur"), 64) + focalpoint := r.FormValue("focalpoint") scaleModeName := r.FormValue("scale_mode") scaleMode, _ := ScaleModes[scaleModeName] @@ -77,5 +78,6 @@ func (p *Route) SourceAndProcessorOptionsForRequest(r *http.Request) ( Dimensions: ImageDimensions{uint(width), uint(height)}, BlurRadius: blurRadius, ScaleMode: uint(scaleMode), + Focalpoint: NewFocalpointFromString(focalpoint), } }