diff options
-rw-r--r-- | media/mediaType.go | 7 | ||||
-rw-r--r-- | resources/image.go | 24 | ||||
-rw-r--r-- | resources/image_test.go | 40 | ||||
-rw-r--r-- | resources/images/config.go | 6 | ||||
-rw-r--r-- | resources/images/image.go | 32 | ||||
-rw-r--r-- | resources/resource.go | 4 | ||||
-rw-r--r-- | resources/resource_metadata.go | 2 |
7 files changed, 100 insertions, 15 deletions
diff --git a/media/mediaType.go b/media/mediaType.go index 434672c43..baf7b7e27 100644 --- a/media/mediaType.go +++ b/media/mediaType.go @@ -140,8 +140,11 @@ var ( YAMLType = Type{MainType: "application", SubType: "yaml", Suffixes: []string{"yaml", "yml"}, Delimiter: defaultDelimiter} // Common image types - PNGType = Type{MainType: "image", SubType: "png", Suffixes: []string{"png"}, Delimiter: defaultDelimiter} - JPGType = Type{MainType: "image", SubType: "jpg", Suffixes: []string{"jpg", "jpeg"}, Delimiter: defaultDelimiter} + PNGType = Type{MainType: "image", SubType: "png", Suffixes: []string{"png"}, Delimiter: defaultDelimiter} + JPGType = Type{MainType: "image", SubType: "jpg", Suffixes: []string{"jpg", "jpeg"}, Delimiter: defaultDelimiter} + GIFType = Type{MainType: "image", SubType: "gif", Suffixes: []string{"gif"}, Delimiter: defaultDelimiter} + TIFFType = Type{MainType: "image", SubType: "tiff", Suffixes: []string{"tif", "tiff"}, Delimiter: defaultDelimiter} + BMPType = Type{MainType: "image", SubType: "bmp", Suffixes: []string{"bmp"}, Delimiter: defaultDelimiter} OctetType = Type{MainType: "application", SubType: "octet-stream"} ) diff --git a/resources/image.go b/resources/image.go index 26b9b8710..bb9c987a5 100644 --- a/resources/image.go +++ b/resources/image.go @@ -43,8 +43,6 @@ import ( "github.com/gohugoio/hugo/resources/images" // Blind import for image.Decode - - // Blind import for image.Decode _ "golang.org/x/image/webp" ) @@ -220,17 +218,13 @@ func (i *imageResource) Filter(filters ...interface{}) (resource.Image, error) { } conf.Key = internal.HashString(gfilters) + conf.TargetFormat = i.Format return i.doWithImageConfig(conf, func(src image.Image) (image.Image, error) { return i.Proc.Filter(src, gfilters...) }) } -func (i *imageResource) isJPEG() bool { - name := strings.ToLower(i.getResourcePaths().relTargetDirFile.file) - return strings.HasSuffix(name, ".jpg") || strings.HasSuffix(name, ".jpeg") -} - // 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. @@ -260,7 +254,7 @@ func (i *imageResource) doWithImageConfig(conf images.ImageConfig, f func(src im return nil, nil, &os.PathError{Op: errOp, Path: errPath, Err: err} } - if i.Format == images.PNG { + if conf.TargetFormat == images.PNG { // Apply the colour palette from the source if paletted, ok := src.(*image.Paletted); ok { tmp := image.NewPaletted(converted.Bounds(), paletted.Palette) @@ -271,6 +265,8 @@ func (i *imageResource) doWithImageConfig(conf images.ImageConfig, f func(src im ci := i.clone(converted) ci.setBasePath(conf) + ci.Format = conf.TargetFormat + ci.setMediaType(conf.TargetFormat.MediaType()) return ci, converted, nil }) @@ -282,11 +278,14 @@ func (i *imageResource) decodeImageConfig(action, spec string) (images.ImageConf return conf, err } - iconf := i.Proc.Cfg + // default to the source format + if conf.TargetFormat == 0 { + conf.TargetFormat = i.Format + } - if conf.Quality <= 0 && i.isJPEG() { + if conf.Quality <= 0 && conf.TargetFormat.RequiresDefaultQuality() { // We need a quality setting for all JPEGs - conf.Quality = iconf.Quality + conf.Quality = i.Proc.Cfg.Quality } return conf, nil @@ -339,6 +338,9 @@ func (i *imageResource) getImageMetaCacheTargetPath() string { func (i *imageResource) relTargetPathFromConfig(conf images.ImageConfig) dirFile { p1, p2 := helpers.FileAndExt(i.getResourcePaths().relTargetDirFile.file) + if conf.TargetFormat != i.Format { + p2 = conf.TargetFormat.DefaultExtension() + } h, _ := i.hash() idStr := fmt.Sprintf("_hu%s_%d", h, i.size()) diff --git a/resources/image_test.go b/resources/image_test.go index 59d6b7c9a..5fa5021c9 100644 --- a/resources/image_test.go +++ b/resources/image_test.go @@ -133,6 +133,46 @@ func TestImageTransformBasic(t *testing.T) { c.Assert(filled, eq, filledAgain) } +func TestImageTransformFormat(t *testing.T) { + c := qt.New(t) + + image := fetchSunset(c) + + fileCache := image.(specProvider).getSpec().FileCaches.ImageCache().Fs + + assertExtWidthHeight := func(img resource.Image, ext string, w, h int) { + c.Helper() + c.Assert(img, qt.Not(qt.IsNil)) + c.Assert(helpers.Ext(img.RelPermalink()), qt.Equals, ext) + c.Assert(img.Width(), qt.Equals, w) + c.Assert(img.Height(), qt.Equals, h) + } + + c.Assert(image.RelPermalink(), qt.Equals, "/a/sunset.jpg") + c.Assert(image.ResourceType(), qt.Equals, "image") + assertExtWidthHeight(image, ".jpg", 900, 562) + + imagePng, err := image.Resize("450x png") + c.Assert(err, qt.IsNil) + c.Assert(imagePng.RelPermalink(), qt.Equals, "/a/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_450x0_resize_linear.png") + c.Assert(imagePng.ResourceType(), qt.Equals, "image") + assertExtWidthHeight(imagePng, ".png", 450, 281) + c.Assert(imagePng.Name(), qt.Equals, "sunset.jpg") + c.Assert(imagePng.MediaType().String(), qt.Equals, "image/png") + + assertFileCache(c, fileCache, path.Base(imagePng.RelPermalink()), 450, 281) + + imageGif, err := image.Resize("225x gif") + c.Assert(err, qt.IsNil) + c.Assert(imageGif.RelPermalink(), qt.Equals, "/a/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_225x0_resize_linear.gif") + c.Assert(imageGif.ResourceType(), qt.Equals, "image") + assertExtWidthHeight(imageGif, ".gif", 225, 141) + c.Assert(imageGif.Name(), qt.Equals, "sunset.jpg") + c.Assert(imageGif.MediaType().String(), qt.Equals, "image/gif") + + assertFileCache(c, fileCache, path.Base(imageGif.RelPermalink()), 225, 141) +} + // https://github.com/gohugoio/hugo/issues/4261 func TestImageTransformLongFilename(t *testing.T) { c := qt.New(t) diff --git a/resources/images/config.go b/resources/images/config.go index a290922ab..6bc701bfe 100644 --- a/resources/images/config.go +++ b/resources/images/config.go @@ -187,7 +187,8 @@ func DecodeImageConfig(action, config string, defaults Imaging) (ImageConfig, er } else { return c, errors.New("invalid image dimensions") } - + } else if f, ok := ImageFormatFromExt("." + part); ok { + c.TargetFormat = f } } @@ -212,6 +213,9 @@ func DecodeImageConfig(action, config string, defaults Imaging) (ImageConfig, er // ImageConfig holds configuration to create a new image from an existing one, resize etc. type ImageConfig struct { + // This defines the output format of the output image. It defaults to the source format + TargetFormat Format + Action string // If set, this will be used as the key in filenames etc. diff --git a/resources/images/image.go b/resources/images/image.go index e72d96837..bd7500c28 100644 --- a/resources/images/image.go +++ b/resources/images/image.go @@ -23,6 +23,7 @@ import ( "io" "sync" + "github.com/gohugoio/hugo/media" "github.com/gohugoio/hugo/resources/images/exif" "github.com/disintegration/gift" @@ -59,7 +60,7 @@ type Image struct { } func (i *Image) EncodeTo(conf ImageConfig, img image.Image, w io.Writer) error { - switch i.Format { + switch conf.TargetFormat { case JPEG: var rgba *image.RGBA @@ -250,6 +251,35 @@ const ( BMP ) +// RequiresDefaultQuality returns if the default quality needs to be applied to images of this format +func (f Format) RequiresDefaultQuality() bool { + return f == JPEG +} + +// DefaultExtension returns the default file extension of this format, starting with a dot. +// For example: .jpg for JPEG +func (f Format) DefaultExtension() string { + return f.MediaType().FullSuffix() +} + +// MediaType returns the media type of this image, e.g. image/jpeg for JPEG +func (f Format) MediaType() media.Type { + switch f { + case JPEG: + return media.JPGType + case PNG: + return media.PNGType + case GIF: + return media.GIFType + case TIFF: + return media.TIFFType + case BMP: + return media.BMPType + default: + panic(fmt.Sprintf("%d is not a valid image format", f)) + } +} + type imageConfig struct { config image.Config configInit sync.Once diff --git a/resources/resource.go b/resources/resource.go index 7f52a7135..acf8e37c0 100644 --- a/resources/resource.go +++ b/resources/resource.go @@ -220,6 +220,10 @@ func (l *genericResource) MediaType() media.Type { return l.mediaType } +func (l *genericResource) setMediaType(mediaType media.Type) { + l.mediaType = mediaType +} + func (l *genericResource) Name() string { return l.name } diff --git a/resources/resource_metadata.go b/resources/resource_metadata.go index 79e61e1a0..ce17df022 100644 --- a/resources/resource_metadata.go +++ b/resources/resource_metadata.go @@ -18,6 +18,7 @@ import ( "strconv" "github.com/gohugoio/hugo/hugofs/glob" + "github.com/gohugoio/hugo/media" "github.com/gohugoio/hugo/resources/resource" "github.com/pkg/errors" @@ -42,6 +43,7 @@ type metaAssignerProvider interface { type metaAssigner interface { setTitle(title string) setName(name string) + setMediaType(mediaType media.Type) updateParams(params map[string]interface{}) } |