images.go (2466B)
1 // Copyright 2019 The Hugo Authors. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13
14 // Package images provides template functions for manipulating images.
15 package images
16
17 import (
18 "image"
19 "sync"
20
21 "errors"
22
23 "github.com/gohugoio/hugo/resources/images"
24
25 // Importing image codecs for image.DecodeConfig
26 _ "image/gif"
27 _ "image/jpeg"
28 _ "image/png"
29
30 // Import webp codec
31 _ "golang.org/x/image/webp"
32
33 "github.com/gohugoio/hugo/deps"
34 "github.com/spf13/cast"
35 )
36
37 // New returns a new instance of the images-namespaced template functions.
38 func New(deps *deps.Deps) *Namespace {
39 return &Namespace{
40 Filters: &images.Filters{},
41 cache: map[string]image.Config{},
42 deps: deps,
43 }
44 }
45
46 // Namespace provides template functions for the "images" namespace.
47 type Namespace struct {
48 *images.Filters
49 cacheMu sync.RWMutex
50 cache map[string]image.Config
51
52 deps *deps.Deps
53 }
54
55 // Config returns the image.Config for the specified path relative to the
56 // working directory.
57 func (ns *Namespace) Config(path any) (image.Config, error) {
58 filename, err := cast.ToStringE(path)
59 if err != nil {
60 return image.Config{}, err
61 }
62
63 if filename == "" {
64 return image.Config{}, errors.New("config needs a filename")
65 }
66
67 // Check cache for image config.
68 ns.cacheMu.RLock()
69 config, ok := ns.cache[filename]
70 ns.cacheMu.RUnlock()
71
72 if ok {
73 return config, nil
74 }
75
76 f, err := ns.deps.Fs.WorkingDirReadOnly.Open(filename)
77 if err != nil {
78 return image.Config{}, err
79 }
80 defer f.Close()
81
82 config, _, err = image.DecodeConfig(f)
83 if err != nil {
84 return config, err
85 }
86
87 ns.cacheMu.Lock()
88 ns.cache[filename] = config
89 ns.cacheMu.Unlock()
90
91 return config, nil
92 }
93
94 func (ns *Namespace) Filter(args ...any) (images.ImageResource, error) {
95 if len(args) < 2 {
96 return nil, errors.New("must provide an image and one or more filters")
97 }
98
99 img := args[len(args)-1].(images.ImageResource)
100 filtersv := args[:len(args)-1]
101
102 return img.Filter(filtersv...)
103 }