mirror of
https://github.com/gohugoio/hugo.git
synced 2025-08-22 21:42:50 +02:00
Add $image.Process
Which supports all the existing actions: resize, crop, fit, fill. But it also allows plain format conversions: ``` {{ $img = $img.Process "webp" }} ``` Which will be a simple re-encoding of the source image. Fixes #11483
This commit is contained in:
@@ -31,6 +31,7 @@ import (
|
||||
|
||||
color_extractor "github.com/marekm4/color-extractor"
|
||||
|
||||
"github.com/gohugoio/hugo/common/hstrings"
|
||||
"github.com/gohugoio/hugo/common/paths"
|
||||
"github.com/gohugoio/hugo/identity"
|
||||
|
||||
@@ -198,75 +199,49 @@ func (i *imageResource) cloneWithUpdates(u *transformationUpdate) (baseResource,
|
||||
}, nil
|
||||
}
|
||||
|
||||
var imageActions = []string{images.ActionResize, images.ActionCrop, images.ActionFit, images.ActionFill}
|
||||
|
||||
// Process processes the image with the given spec.
|
||||
// The spec can contain an optional action, one of "resize", "crop", "fit" or "fill".
|
||||
// This makes this method a more flexible version that covers all of Resize, Crop, Fit and Fill,
|
||||
// but it also supports e.g. format conversions without any resize action.
|
||||
func (i *imageResource) Process(spec string) (images.ImageResource, error) {
|
||||
var action string
|
||||
options := strings.Fields(spec)
|
||||
for i, p := range options {
|
||||
if hstrings.InSlicEqualFold(imageActions, p) {
|
||||
action = p
|
||||
options = append(options[:i], options[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
return i.processActionOptions(action, options)
|
||||
}
|
||||
|
||||
// Resize resizes the image to the specified width and height using the specified resampling
|
||||
// filter and returns the transformed image. If one of width or height is 0, the image aspect
|
||||
// ratio is preserved.
|
||||
func (i *imageResource) Resize(spec string) (images.ImageResource, error) {
|
||||
conf, err := i.decodeImageConfig("resize", spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return i.doWithImageConfig(conf, func(src image.Image) (image.Image, error) {
|
||||
return i.Proc.ApplyFiltersFromConfig(src, conf)
|
||||
})
|
||||
return i.processActionSpec(images.ActionResize, spec)
|
||||
}
|
||||
|
||||
// Crop the image to the specified dimensions without resizing using the given anchor point.
|
||||
// Space delimited config, e.g. `200x300 TopLeft`.
|
||||
func (i *imageResource) Crop(spec string) (images.ImageResource, 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)
|
||||
})
|
||||
return i.processActionSpec(images.ActionCrop, spec)
|
||||
}
|
||||
|
||||
// Fit scales down the image using the specified resample filter to fit the specified
|
||||
// maximum width and height.
|
||||
func (i *imageResource) Fit(spec string) (images.ImageResource, error) {
|
||||
conf, err := i.decodeImageConfig("fit", spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return i.doWithImageConfig(conf, func(src image.Image) (image.Image, error) {
|
||||
return i.Proc.ApplyFiltersFromConfig(src, conf)
|
||||
})
|
||||
return i.processActionSpec(images.ActionFit, spec)
|
||||
}
|
||||
|
||||
// Fill scales the image to the smallest possible size that will cover the specified dimensions,
|
||||
// crops the resized image to the specified dimensions using the given anchor point.
|
||||
// Space delimited config, e.g. `200x300 TopLeft`.
|
||||
func (i *imageResource) Fill(spec string) (images.ImageResource, error) {
|
||||
conf, err := i.decodeImageConfig("fill", spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
img, err := i.doWithImageConfig(conf, func(src image.Image) (image.Image, error) {
|
||||
return i.Proc.ApplyFiltersFromConfig(src, conf)
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if conf.Anchor == 0 && img.Width() == 0 || img.Height() == 0 {
|
||||
// See https://github.com/gohugoio/hugo/issues/7955
|
||||
// Smartcrop fails silently in some rare cases.
|
||||
// Fall back to a center fill.
|
||||
conf.Anchor = gift.CenterAnchor
|
||||
conf.AnchorStr = "center"
|
||||
return i.doWithImageConfig(conf, func(src image.Image) (image.Image, error) {
|
||||
return i.Proc.ApplyFiltersFromConfig(src, conf)
|
||||
})
|
||||
}
|
||||
|
||||
return img, err
|
||||
return i.processActionSpec(images.ActionFill, spec)
|
||||
}
|
||||
|
||||
func (i *imageResource) Filter(filters ...any) (images.ImageResource, error) {
|
||||
@@ -286,6 +261,39 @@ func (i *imageResource) Filter(filters ...any) (images.ImageResource, error) {
|
||||
})
|
||||
}
|
||||
|
||||
func (i *imageResource) processActionSpec(action, spec string) (images.ImageResource, error) {
|
||||
return i.processActionOptions(action, strings.Fields(spec))
|
||||
}
|
||||
|
||||
func (i *imageResource) processActionOptions(action string, options []string) (images.ImageResource, error) {
|
||||
conf, err := images.DecodeImageConfig(action, options, i.Proc.Cfg, i.Format)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
img, err := i.doWithImageConfig(conf, func(src image.Image) (image.Image, error) {
|
||||
return i.Proc.ApplyFiltersFromConfig(src, conf)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if action == images.ActionFill {
|
||||
if conf.Anchor == 0 && img.Width() == 0 || img.Height() == 0 {
|
||||
// See https://github.com/gohugoio/hugo/issues/7955
|
||||
// Smartcrop fails silently in some rare cases.
|
||||
// Fall back to a center fill.
|
||||
conf.Anchor = gift.CenterAnchor
|
||||
conf.AnchorStr = "center"
|
||||
return i.doWithImageConfig(conf, func(src image.Image) (image.Image, error) {
|
||||
return i.Proc.ApplyFiltersFromConfig(src, conf)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
||||
// Serialize image processing. The imaging library spins up its own set of Go routines,
|
||||
// so there is not much to gain from adding more load to the mix. That
|
||||
// can even have negative effect in low resource scenarios.
|
||||
@@ -362,7 +370,8 @@ func (i *imageResource) doWithImageConfig(conf images.ImageConfig, f func(src im
|
||||
}
|
||||
|
||||
func (i *imageResource) decodeImageConfig(action, spec string) (images.ImageConfig, error) {
|
||||
conf, err := images.DecodeImageConfig(action, spec, i.Proc.Cfg, i.Format)
|
||||
options := strings.Fields(spec)
|
||||
conf, err := images.DecodeImageConfig(action, options, i.Proc.Cfg, i.Format)
|
||||
if err != nil {
|
||||
return conf, err
|
||||
}
|
||||
|
Reference in New Issue
Block a user