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
This commit is contained in:
John Elliott
2022-02-22 16:50:23 +00:00
committed by Bjørn Erik Pedersen
parent aebde49b88
commit 7732da9f93
9 changed files with 75 additions and 8 deletions

View File

@@ -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)
}

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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
}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)
}