hugo

Fork of github.com/gohugoio/hugo with reverse pagination support

git clone git://git.shimmy1996.com/hugo.git

filters.go (7489B)

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