summaryrefslogtreecommitdiffstats
path: root/resources
diff options
context:
space:
mode:
authorJohn Elliott <johnsvenn@googlemail.com>2022-02-22 16:50:23 +0000
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2022-02-23 10:01:50 +0100
commit7732da9f93503c1a723d6ac5bb77da206cb0fa0e (patch)
treea205fe894c5413a7dcaa6c6ad8ce64c93e61f874 /resources
parentaebde49b884c3b5ef73d8e1a01fca8a1354ac5b9 (diff)
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.go4
-rw-r--r--resources/image.go13
-rw-r--r--resources/image_test.go16
-rw-r--r--resources/images/config.go2
-rw-r--r--resources/images/image.go15
-rw-r--r--resources/resource/resourcetypes.go1
-rw-r--r--resources/transform.go4
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)
}