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 }