github.com/anakojm/hugo-katex@v0.0.0-20231023141351-42d6f5de9c0b/resources/images/filters.go (about) 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 "fmt" 19 20 "github.com/gohugoio/hugo/common/hugio" 21 "github.com/gohugoio/hugo/common/maps" 22 "github.com/gohugoio/hugo/resources/resource" 23 24 "github.com/disintegration/gift" 25 "github.com/spf13/cast" 26 ) 27 28 // Increment for re-generation of images using these filters. 29 const filterAPIVersion = 0 30 31 type Filters struct{} 32 33 func (*Filters) Process(spec any) gift.Filter { 34 return filter{ 35 Options: newFilterOpts(spec), 36 Filter: processFilter{ 37 spec: cast.ToString(spec), 38 }, 39 } 40 } 41 42 // Overlay creates a filter that overlays src at position x y. 43 func (*Filters) Overlay(src ImageSource, x, y any) gift.Filter { 44 return filter{ 45 Options: newFilterOpts(src.Key(), x, y), 46 Filter: overlayFilter{src: src, x: cast.ToInt(x), y: cast.ToInt(y)}, 47 } 48 } 49 50 // Opacity creates a filter that changes the opacity of an image. 51 // The opacity parameter must be in range (0, 1). 52 func (*Filters) Opacity(opacity any) gift.Filter { 53 return filter{ 54 Options: newFilterOpts(opacity), 55 Filter: opacityFilter{opacity: cast.ToFloat32(opacity)}, 56 } 57 } 58 59 // Text creates a filter that draws text with the given options. 60 func (*Filters) Text(text string, options ...any) gift.Filter { 61 tf := textFilter{ 62 text: text, 63 color: "#ffffff", 64 size: 20, 65 x: 10, 66 y: 10, 67 linespacing: 2, 68 } 69 70 var opt maps.Params 71 if len(options) > 0 { 72 opt = maps.MustToParamsAndPrepare(options[0]) 73 for option, v := range opt { 74 switch option { 75 case "color": 76 tf.color = cast.ToString(v) 77 case "size": 78 tf.size = cast.ToFloat64(v) 79 case "x": 80 tf.x = cast.ToInt(v) 81 case "y": 82 tf.y = cast.ToInt(v) 83 case "linespacing": 84 tf.linespacing = cast.ToInt(v) 85 case "font": 86 if err, ok := v.(error); ok { 87 panic(fmt.Sprintf("invalid font source: %s", err)) 88 } 89 fontSource, ok1 := v.(hugio.ReadSeekCloserProvider) 90 identifier, ok2 := v.(resource.Identifier) 91 92 if !(ok1 && ok2) { 93 panic(fmt.Sprintf("invalid text font source: %T", v)) 94 } 95 96 tf.fontSource = fontSource 97 98 // The input value isn't hashable and will not make a stable key. 99 // Replace it with a string in the map used as basis for the 100 // hash string. 101 opt["font"] = identifier.Key() 102 103 } 104 } 105 } 106 107 return filter{ 108 Options: newFilterOpts(text, opt), 109 Filter: tf, 110 } 111 } 112 113 // Brightness creates a filter that changes the brightness of an image. 114 // The percentage parameter must be in range (-100, 100). 115 func (*Filters) Brightness(percentage any) gift.Filter { 116 return filter{ 117 Options: newFilterOpts(percentage), 118 Filter: gift.Brightness(cast.ToFloat32(percentage)), 119 } 120 } 121 122 // ColorBalance creates a filter that changes the color balance of an image. 123 // The percentage parameters for each color channel (red, green, blue) must be in range (-100, 500). 124 func (*Filters) ColorBalance(percentageRed, percentageGreen, percentageBlue any) gift.Filter { 125 return filter{ 126 Options: newFilterOpts(percentageRed, percentageGreen, percentageBlue), 127 Filter: gift.ColorBalance(cast.ToFloat32(percentageRed), cast.ToFloat32(percentageGreen), cast.ToFloat32(percentageBlue)), 128 } 129 } 130 131 // Colorize creates a filter that produces a colorized version of an image. 132 // The hue parameter is the angle on the color wheel, typically in range (0, 360). 133 // The saturation parameter must be in range (0, 100). 134 // The percentage parameter specifies the strength of the effect, it must be in range (0, 100). 135 func (*Filters) Colorize(hue, saturation, percentage any) gift.Filter { 136 return filter{ 137 Options: newFilterOpts(hue, saturation, percentage), 138 Filter: gift.Colorize(cast.ToFloat32(hue), cast.ToFloat32(saturation), cast.ToFloat32(percentage)), 139 } 140 } 141 142 // Contrast creates a filter that changes the contrast of an image. 143 // The percentage parameter must be in range (-100, 100). 144 func (*Filters) Contrast(percentage any) gift.Filter { 145 return filter{ 146 Options: newFilterOpts(percentage), 147 Filter: gift.Contrast(cast.ToFloat32(percentage)), 148 } 149 } 150 151 // Gamma creates a filter that performs a gamma correction on an image. 152 // The gamma parameter must be positive. Gamma = 1 gives the original image. 153 // Gamma less than 1 darkens the image and gamma greater than 1 lightens it. 154 func (*Filters) Gamma(gamma any) gift.Filter { 155 return filter{ 156 Options: newFilterOpts(gamma), 157 Filter: gift.Gamma(cast.ToFloat32(gamma)), 158 } 159 } 160 161 // GaussianBlur creates a filter that applies a gaussian blur to an image. 162 func (*Filters) GaussianBlur(sigma any) gift.Filter { 163 return filter{ 164 Options: newFilterOpts(sigma), 165 Filter: gift.GaussianBlur(cast.ToFloat32(sigma)), 166 } 167 } 168 169 // Grayscale creates a filter that produces a grayscale version of an image. 170 func (*Filters) Grayscale() gift.Filter { 171 return filter{ 172 Filter: gift.Grayscale(), 173 } 174 } 175 176 // Hue creates a filter that rotates the hue of an image. 177 // The hue angle shift is typically in range -180 to 180. 178 func (*Filters) Hue(shift any) gift.Filter { 179 return filter{ 180 Options: newFilterOpts(shift), 181 Filter: gift.Hue(cast.ToFloat32(shift)), 182 } 183 } 184 185 // Invert creates a filter that negates the colors of an image. 186 func (*Filters) Invert() gift.Filter { 187 return filter{ 188 Filter: gift.Invert(), 189 } 190 } 191 192 // Pixelate creates a filter that applies a pixelation effect to an image. 193 func (*Filters) Pixelate(size any) gift.Filter { 194 return filter{ 195 Options: newFilterOpts(size), 196 Filter: gift.Pixelate(cast.ToInt(size)), 197 } 198 } 199 200 // Saturation creates a filter that changes the saturation of an image. 201 func (*Filters) Saturation(percentage any) gift.Filter { 202 return filter{ 203 Options: newFilterOpts(percentage), 204 Filter: gift.Saturation(cast.ToFloat32(percentage)), 205 } 206 } 207 208 // Sepia creates a filter that produces a sepia-toned version of an image. 209 func (*Filters) Sepia(percentage any) gift.Filter { 210 return filter{ 211 Options: newFilterOpts(percentage), 212 Filter: gift.Sepia(cast.ToFloat32(percentage)), 213 } 214 } 215 216 // Sigmoid creates a filter that changes the contrast of an image using a sigmoidal function and returns the adjusted image. 217 // It's a non-linear contrast change useful for photo adjustments as it preserves highlight and shadow detail. 218 func (*Filters) Sigmoid(midpoint, factor any) gift.Filter { 219 return filter{ 220 Options: newFilterOpts(midpoint, factor), 221 Filter: gift.Sigmoid(cast.ToFloat32(midpoint), cast.ToFloat32(factor)), 222 } 223 } 224 225 // UnsharpMask creates a filter that sharpens an image. 226 // The sigma parameter is used in a gaussian function and affects the radius of effect. 227 // Sigma must be positive. Sharpen radius roughly equals 3 * sigma. 228 // The amount parameter controls how much darker and how much lighter the edge borders become. Typically between 0.5 and 1.5. 229 // The threshold parameter controls the minimum brightness change that will be sharpened. Typically between 0 and 0.05. 230 func (*Filters) UnsharpMask(sigma, amount, threshold any) gift.Filter { 231 return filter{ 232 Options: newFilterOpts(sigma, amount, threshold), 233 Filter: gift.UnsharpMask(cast.ToFloat32(sigma), cast.ToFloat32(amount), cast.ToFloat32(threshold)), 234 } 235 } 236 237 type filter struct { 238 Options filterOpts 239 gift.Filter 240 } 241 242 // For cache-busting. 243 type filterOpts struct { 244 Version int 245 Vals any 246 } 247 248 func newFilterOpts(vals ...any) filterOpts { 249 return filterOpts{ 250 Version: filterAPIVersion, 251 Vals: vals, 252 } 253 }