…lines 1–15 unchanged (15 lines)
16import (
17 "encoding/json"
18 "fmt"
19 "image"
20 "image/color"
21 "image/draw"
22 "image/gif"
23 _ "image/png"
24 "io"
25 "os"
26 "strings"
27 "sync"
28
29 color_extractor "github.com/marekm4/color-extractor"
30
31 "github.com/gohugoio/hugo/cache/filecache"
32 "github.com/gohugoio/hugo/common/hashing"
33 "github.com/gohugoio/hugo/common/paths"
34
35 "github.com/disintegration/gift"
36
37 "github.com/gohugoio/hugo/resources/images/exif"
38 "github.com/gohugoio/hugo/resources/internal"
39
40 "github.com/gohugoio/hugo/resources/resource"
41
42 "github.com/gohugoio/hugo/resources/images"
43
44 // Blind import for image.Decode
45 _ "golang.org/x/image/webp"
46)
47
48var (
49 _ images.ImageResource = (*imageResource)(nil)
50 _ resource.Source = (*imageResource)(nil)
51 _ resource.Cloner = (*imageResource)(nil)
…lines 52–111 unchanged (60 lines)
112 i.metaInitErr = err
113 return
114 }
115 defer f.Close()
116
117 filename := i.getResourcePaths().Path()
118 x, err := i.getSpec().imaging.DecodeExif(filename, mf, f)
119 if err != nil {
120 i.getSpec().Logger.Warnf("Unable to decode Exif metadata from image: %s", i.Key())
121 return nil
122 }
123
124 i.meta = &imageMeta{Exif: x}
…lines 125–212 unchanged (88 lines)
213}
214
215// Resize resizes the image to the specified width and height using the specified resampling
216// filter and returns the transformed image. If one of width or height is 0, the image aspect
217// ratio is preserved.
218func (i *imageResource) Resize(spec string) (images.ImageResource, error) {
219 return i.processActionSpec(images.ActionResize, spec)
220}
221
222// Crop the image to the specified dimensions without resizing using the given anchor point.
223// Space delimited config, e.g. `200x300 TopLeft`.
224func (i *imageResource) Crop(spec string) (images.ImageResource, error) {
225 return i.processActionSpec(images.ActionCrop, spec)
…lines 226–379 unchanged (154 lines)
380 draw.FloydSteinberg.Draw(tmp, tmp.Bounds(), converted, converted.Bounds().Min)
381 converted = tmp
382 }
383 }
384
385 ci := i.clone(converted)
386 targetPath := i.relTargetPathFromConfig(conf, i.getSpec().imaging.Cfg.SourceHash)
387 ci.setTargetPath(targetPath)
388 ci.Format = conf.TargetFormat
389 ci.setMediaType(conf.TargetFormat.MediaType())
390
391 return ci, converted, nil
392 })
393 if err != nil {
394 return nil, err
395 }
396 return img, nil
397}
398
399type giphy struct {
400 image.Image
401 gif *gif.GIF
402}
403
404func (g *giphy) GIF() *gif.GIF {
405 return g.gif
406}
407
408// DecodeImage decodes the image source into an Image.
409// This for internal use only.
410func (i *imageResource) DecodeImage() (image.Image, error) {
411 f, err := i.ReadSeekCloser()
412 if err != nil {
413 return nil, fmt.Errorf("failed to open image for decode: %w", err)
414 }
415 defer f.Close()
416
417 if i.Format == images.GIF {
418 g, err := gif.DecodeAll(f)
419 if err != nil {
420 return nil, fmt.Errorf("failed to decode gif: %w", err)
421 }
422 return &giphy{gif: g, Image: g.Image[0]}, nil
423 }
424 img, _, err := image.Decode(f)
425 return img, err
426}
427
428func (i *imageResource) clone(img image.Image) *imageResource {
429 spec := i.baseResource.Clone().(baseResource)
430
431 var image *images.Image
…lines 432–443 unchanged (12 lines)
444
445func (i *imageResource) getImageMetaCacheTargetPath() string {
446 // Increment to invalidate the meta cache
447 // Last increment: v0.130.0 when change to the new imagemeta library for Exif.
448 const imageMetaVersionNumber = 2
449
450 cfgHash := i.getSpec().imaging.Cfg.SourceHash
451 df := i.getResourcePaths()
452 p1, _ := paths.FileAndExt(df.File)
453 h := i.hash()
454 idStr := hashing.HashStringHex(h, i.size(), imageMetaVersionNumber, cfgHash)
455 df.File = fmt.Sprintf("%s_%s.json", p1, idStr)
456 return df.TargetPath()
…lines 457–481 unchanged (25 lines)