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  }