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"}
)
"github.com/gohugoio/hugo/helpers"
"github.com/gohugoio/hugo/resources/images"
- // Blind import for image.Decode
-
// Blind import for image.Decode
_ "golang.org/x/image/webp"
)
}
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.
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)
ci := i.clone(converted)
ci.setBasePath(conf)
+ ci.Format = conf.TargetFormat
+ ci.setMediaType(conf.TargetFormat.MediaType())
return ci, converted, nil
})
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
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())
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)
} else {
return c, errors.New("invalid image dimensions")
}
-
+ } else if f, ok := ImageFormatFromExt("." + part); ok {
+ c.TargetFormat = f
}
}
// 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.
"io"
"sync"
+ "github.com/gohugoio/hugo/media"
"github.com/gohugoio/hugo/resources/images/exif"
"github.com/disintegration/gift"
}
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
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
return l.mediaType
}
+func (l *genericResource) setMediaType(mediaType media.Type) {
+ l.mediaType = mediaType
+}
+
func (l *genericResource) Name() string {
return l.name
}
"strconv"
"github.com/gohugoio/hugo/hugofs/glob"
+ "github.com/gohugoio/hugo/media"
"github.com/gohugoio/hugo/resources/resource"
"github.com/pkg/errors"
type metaAssigner interface {
setTitle(title string)
setName(name string)
+ setMediaType(mediaType media.Type)
updateParams(params map[string]interface{})
}