diff options
author | John Elliott <[email protected]> | 2022-02-22 16:50:23 +0000 |
---|---|---|
committer | Bjørn Erik Pedersen <[email protected]> | 2022-02-23 10:01:50 +0100 |
commit | 7732da9f93503c1a723d6ac5bb77da206cb0fa0e (patch) | |
tree | a205fe894c5413a7dcaa6c6ad8ce64c93e61f874 /resources | |
parent | aebde49b884c3b5ef73d8e1a01fca8a1354ac5b9 (diff) | |
download | hugo-7732da9f93503c1a723d6ac5bb77da206cb0fa0e.tar.gz hugo-7732da9f93503c1a723d6ac5bb77da206cb0fa0e.zip |
Allow images to be cropped without being resized
Introduces the Crop method for image processing which implements gift.CropToSize. Also allows a smartCrop without resizing, and updates the documentation.
Fixes #9499
Diffstat (limited to 'resources')
-rw-r--r-- | resources/errorResource.go | 4 | ||||
-rw-r--r-- | resources/image.go | 13 | ||||
-rw-r--r-- | resources/image_test.go | 16 | ||||
-rw-r--r-- | resources/images/config.go | 2 | ||||
-rw-r--r-- | resources/images/image.go | 15 | ||||
-rw-r--r-- | resources/resource/resourcetypes.go | 1 | ||||
-rw-r--r-- | resources/transform.go | 4 |
7 files changed, 54 insertions, 1 deletions
diff --git a/resources/errorResource.go b/resources/errorResource.go index 705547d4c..a7f99c2b7 100644 --- a/resources/errorResource.go +++ b/resources/errorResource.go @@ -100,6 +100,10 @@ func (e *errorResource) Width() int { panic(e.error) } +func (e *errorResource) Crop(spec string) (resource.Image, error) { + panic(e.error) +} + func (e *errorResource) Fill(spec string) (resource.Image, error) { panic(e.error) } diff --git a/resources/image.go b/resources/image.go index 1eedbad91..3a790a217 100644 --- a/resources/image.go +++ b/resources/image.go @@ -181,6 +181,19 @@ func (i *imageResource) Resize(spec string) (resource.Image, error) { }) } +// Crop the image to the specified dimensions without resizing using the given anchor point. +// Space delimited config: 200x300 TopLeft +func (i *imageResource) Crop(spec string) (resource.Image, error) { + conf, err := i.decodeImageConfig("crop", spec) + if err != nil { + return nil, err + } + + return i.doWithImageConfig(conf, func(src image.Image) (image.Image, error) { + return i.Proc.ApplyFiltersFromConfig(src, conf) + }) +} + // Fit scales down the image using the specified resample filter to fit the specified // maximum width and height. func (i *imageResource) Fit(spec string) (resource.Image, error) { diff --git a/resources/image_test.go b/resources/image_test.go index ad8c42bd7..e85fe9b9a 100644 --- a/resources/image_test.go +++ b/resources/image_test.go @@ -137,6 +137,22 @@ func TestImageTransformBasic(t *testing.T) { filledAgain, err := image.Fill("200x100 bottomLeft") c.Assert(err, qt.IsNil) c.Assert(filled, eq, filledAgain) + + cropped, err := image.Crop("300x300 topRight") + c.Assert(err, qt.IsNil) + c.Assert(cropped.RelPermalink(), qt.Equals, "/a/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_300x300_crop_q68_linear_topright.jpg") + assertWidthHeight(cropped, 300, 300) + + smartcropped, err := image.Crop("200x200 smart") + c.Assert(err, qt.IsNil) + c.Assert(smartcropped.RelPermalink(), qt.Equals, fmt.Sprintf("/a/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_200x200_crop_q68_linear_smart%d.jpg", 1)) + assertWidthHeight(smartcropped, 200, 200) + + // Check cache + croppedAgain, err := image.Crop("300x300 topRight") + c.Assert(err, qt.IsNil) + c.Assert(cropped, eq, croppedAgain) + } func TestImageTransformFormat(t *testing.T) { diff --git a/resources/images/config.go b/resources/images/config.go index a8b5412d6..f9a3fa09d 100644 --- a/resources/images/config.go +++ b/resources/images/config.go @@ -364,7 +364,7 @@ func (i ImageConfig) GetKey(format Format) string { k += "_" + i.FilterStr - if strings.EqualFold(i.Action, "fill") { + if strings.EqualFold(i.Action, "fill") || strings.EqualFold(i.Action, "crop") { k += "_" + anchor } diff --git a/resources/images/image.go b/resources/images/image.go index 4cbdc895f..66ee9dda1 100644 --- a/resources/images/image.go +++ b/resources/images/image.go @@ -207,6 +207,21 @@ func (p *ImageProcessor) ApplyFiltersFromConfig(src image.Image, conf ImageConfi switch conf.Action { case "resize": filters = append(filters, gift.Resize(conf.Width, conf.Height, conf.Filter)) + case "crop": + if conf.AnchorStr == smartCropIdentifier { + bounds, err := p.smartCrop(src, conf.Width, conf.Height, conf.Filter) + if err != nil { + return nil, err + } + + // First crop using the bounds returned by smartCrop. + filters = append(filters, gift.Crop(bounds)) + // Then center crop the image to get an image the desired size without resizing. + filters = append(filters, gift.CropToSize(conf.Width, conf.Height, gift.CenterAnchor)) + + } else { + filters = append(filters, gift.CropToSize(conf.Width, conf.Height, conf.Anchor)) + } case "fill": if conf.AnchorStr == smartCropIdentifier { bounds, err := p.smartCrop(src, conf.Width, conf.Height, conf.Filter) diff --git a/resources/resource/resourcetypes.go b/resources/resource/resourcetypes.go index c96f3d495..987c230a8 100644 --- a/resources/resource/resourcetypes.go +++ b/resources/resource/resourcetypes.go @@ -62,6 +62,7 @@ type Image interface { type ImageOps interface { Height() int Width() int + Crop(spec string) (Image, error) Fill(spec string) (Image, error) Fit(spec string) (Image, error) Resize(spec string) (Image, error) diff --git a/resources/transform.go b/resources/transform.go index 0569fb35e..67f5c1461 100644 --- a/resources/transform.go +++ b/resources/transform.go @@ -176,6 +176,10 @@ func (r *resourceAdapter) Data() interface{} { return r.target.Data() } +func (r *resourceAdapter) Crop(spec string) (resource.Image, error) { + return r.getImageOps().Crop(spec) +} + func (r *resourceAdapter) Fill(spec string) (resource.Image, error) { return r.getImageOps().Fill(spec) } |