icns

Easily create .icns files (Mac Icons) with this Go library or the included CLI.
Log | Files | Refs | LICENSE

resample.go (2911B)


      1 // Package resample scales images for the icon formats in this module.
      2 package resample
      3 
      4 import (
      5 	"image"
      6 	"math"
      7 
      8 	"golang.org/x/image/draw"
      9 )
     10 
     11 // Function is the algorithm used to resize an image.
     12 type Function int
     13 
     14 // Function constants, ordered from fastest to highest quality.
     15 const (
     16 	// Nearest-neighbor interpolation
     17 	NearestNeighbor Function = iota
     18 	// Bilinear interpolation
     19 	Bilinear
     20 	// Bicubic interpolation (with cubic hermite spline)
     21 	Bicubic
     22 	// Mitchell-Netravali interpolation
     23 	MitchellNetravali
     24 	// Lanczos interpolation (a=2)
     25 	Lanczos2
     26 	// Lanczos interpolation (a=3)
     27 	Lanczos3
     28 )
     29 
     30 // scaler returns the resampler implementing f. Values outside the
     31 // enumeration fall back to MitchellNetravali, the default.
     32 func (f Function) scaler() draw.Interpolator {
     33 	switch f {
     34 	case NearestNeighbor:
     35 		return draw.NearestNeighbor
     36 	case Bilinear:
     37 		return draw.BiLinear
     38 	case Bicubic:
     39 		// Catmull-Rom is the cubic hermite spline interpolant.
     40 		return draw.CatmullRom
     41 	case Lanczos2:
     42 		return lanczos2
     43 	case Lanczos3:
     44 		return lanczos3
     45 	default:
     46 		return mitchellNetravali
     47 	}
     48 }
     49 
     50 // BiggestSide returns the larger of img's two dimensions.
     51 func BiggestSide(img image.Image) uint {
     52 	b := img.Bounds()
     53 	return uint(max(b.Dx(), b.Dy(), 0))
     54 }
     55 
     56 // Square scales img into a size by size square, ignoring the source aspect
     57 // ratio. An image already that size is returned as it is.
     58 //
     59 // Scaling happens in alpha-premultiplied space, so colour does not bleed out
     60 // of fully transparent pixels into the icon's edges.
     61 func Square(img image.Image, size uint, f Function) image.Image {
     62 	bounds := img.Bounds()
     63 	if bounds.Dx() == int(size) && bounds.Dy() == int(size) {
     64 		return img
     65 	}
     66 	dst := image.NewRGBA(image.Rect(0, 0, int(size), int(size)))
     67 	f.scaler().Scale(dst, dst.Bounds(), img, bounds, draw.Src, nil)
     68 	return dst
     69 }
     70 
     71 // Kernels receive the distance from the sample as a non-negative value, and
     72 // return the weight to give the pixel at that distance.
     73 
     74 // mitchellNetravali is the Mitchell-Netravali filter with B = C = 1/3, the
     75 // parameters its authors found to be the best compromise between blurring
     76 // and ringing.
     77 var mitchellNetravali = &draw.Kernel{Support: 2, At: func(t float64) float64 {
     78 	const b, c = 1.0 / 3.0, 1.0 / 3.0
     79 	switch {
     80 	case t < 1:
     81 		return ((12-9*b-6*c)*t*t*t + (-18+12*b+6*c)*t*t + (6 - 2*b)) / 6
     82 	case t < 2:
     83 		return ((-b-6*c)*t*t*t + (6*b+30*c)*t*t + (-12*b-48*c)*t + (8*b + 24*c)) / 6
     84 	default:
     85 		return 0
     86 	}
     87 }}
     88 
     89 var (
     90 	lanczos2 = lanczos(2)
     91 	lanczos3 = lanczos(3)
     92 )
     93 
     94 // lanczos returns the Lanczos filter of order a: a sinc windowed by another
     95 // sinc stretched across the kernel's whole support.
     96 func lanczos(a float64) *draw.Kernel {
     97 	return &draw.Kernel{Support: a, At: func(t float64) float64 {
     98 		switch {
     99 		case t == 0:
    100 			return 1
    101 		case t < a:
    102 			return a * math.Sin(math.Pi*t) * math.Sin(math.Pi*t/a) / (math.Pi * math.Pi * t * t)
    103 		default:
    104 			return 0
    105 		}
    106 	}}
    107 }