github.com/kovansky/hugo@v0.92.3-0.20220224232819-63076e4ff19f/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  
    34  // Overlay creates a filter that overlays src at position x y.
    35  func (*Filters) Overlay(src ImageSource, x, y interface{}) 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 ...interface{}) 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 interface{}) 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 interface{}) 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 interface{}) 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 interface{}) 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 interface{}) 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 interface{}) 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 interface{}) 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 interface{}) 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 interface{}) 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 interface{}) 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 interface{}) 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 interface{}) 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    interface{}
   229  }
   230  
   231  func newFilterOpts(vals ...interface{}) filterOpts {
   232  	return filterOpts{
   233  		Version: filterAPIVersion,
   234  		Vals:    vals,
   235  	}
   236  }