2016-10-13 09:25:11 +00:00
|
|
|
package thumbnail
|
|
|
|
|
|
|
|
import (
|
2016-12-05 08:48:48 +00:00
|
|
|
"bytes"
|
2016-10-13 09:25:11 +00:00
|
|
|
"image"
|
2016-12-05 08:48:48 +00:00
|
|
|
"image/jpeg"
|
2018-06-09 04:25:16 +00:00
|
|
|
"image/png"
|
|
|
|
|
|
|
|
"golang.org/x/image/bmp"
|
2018-06-09 05:17:57 +00:00
|
|
|
"golang.org/x/image/draw"
|
2016-12-05 06:05:59 +00:00
|
|
|
)
|
2016-10-13 09:25:11 +00:00
|
|
|
|
2018-06-09 05:17:57 +00:00
|
|
|
func (this *DirectThumbnailer) scaleImage(src image.Image) (image.Image, error) {
|
2016-10-13 09:25:11 +00:00
|
|
|
srcW := src.Bounds().Max.X
|
|
|
|
srcH := src.Bounds().Max.Y
|
2018-06-09 05:17:57 +00:00
|
|
|
var srcCopyPosition, destCopyPosition image.Rectangle
|
2016-10-13 09:25:11 +00:00
|
|
|
|
2018-06-09 05:17:57 +00:00
|
|
|
switch this.cfg.Aspect {
|
|
|
|
case FitInside:
|
2016-10-13 09:25:11 +00:00
|
|
|
|
2018-06-09 05:17:57 +00:00
|
|
|
var destW, destH int
|
2016-10-13 09:25:11 +00:00
|
|
|
|
2018-06-09 05:17:57 +00:00
|
|
|
if srcW > srcH {
|
|
|
|
destW = this.cfg.Width
|
|
|
|
destH = this.cfg.Height * srcH / srcW
|
|
|
|
} else {
|
|
|
|
destW = this.cfg.Width * srcW / srcH
|
|
|
|
destH = this.cfg.Height
|
|
|
|
}
|
2016-10-13 09:25:11 +00:00
|
|
|
|
2018-06-09 05:17:57 +00:00
|
|
|
offsetX := (this.cfg.Width - destW) / 2
|
|
|
|
offsetY := (this.cfg.Height - destH) / 2
|
|
|
|
|
|
|
|
srcCopyPosition = src.Bounds()
|
|
|
|
destCopyPosition = image.Rect(offsetX, offsetY, destW+offsetX, destH+offsetY)
|
|
|
|
|
|
|
|
case FitOutside:
|
|
|
|
|
|
|
|
var srcSmallestDim, offsetX, offsetY int
|
|
|
|
if srcW > srcH {
|
|
|
|
// Landscape
|
|
|
|
srcSmallestDim = srcH
|
|
|
|
offsetY = 0
|
|
|
|
offsetX = (srcW - srcH) / 2
|
|
|
|
} else {
|
|
|
|
// Portrait (or square)
|
|
|
|
srcSmallestDim = srcW
|
|
|
|
offsetY = (srcH - srcW) / 2
|
|
|
|
offsetX = 0
|
|
|
|
}
|
2016-10-13 09:25:11 +00:00
|
|
|
|
2018-06-09 05:17:57 +00:00
|
|
|
srcCopyPosition = image.Rect(offsetX, offsetY, srcSmallestDim+offsetX, srcSmallestDim+offsetY)
|
|
|
|
destCopyPosition = image.Rect(0, 0, this.cfg.Width, this.cfg.Height)
|
2016-12-05 08:48:48 +00:00
|
|
|
|
2018-06-09 05:17:57 +00:00
|
|
|
default:
|
|
|
|
return nil, ErrInvalidOption
|
|
|
|
}
|
2016-12-05 08:48:48 +00:00
|
|
|
|
2018-06-09 05:17:57 +00:00
|
|
|
//
|
|
|
|
|
|
|
|
dest := image.NewRGBA(image.Rect(0, 0, this.cfg.Width, this.cfg.Height))
|
|
|
|
|
|
|
|
// For a transparent destination, Op.Src is faster than Op.Over
|
2016-12-05 08:48:48 +00:00
|
|
|
|
2018-06-09 05:17:57 +00:00
|
|
|
switch this.cfg.Scale {
|
2018-06-09 04:39:41 +00:00
|
|
|
case NearestNeighbour:
|
2018-06-09 05:17:57 +00:00
|
|
|
draw.NearestNeighbor.Scale(dest, destCopyPosition, src, srcCopyPosition, draw.Src, nil)
|
2016-12-05 08:48:48 +00:00
|
|
|
|
2018-06-09 05:17:57 +00:00
|
|
|
case BilinearFast:
|
|
|
|
draw.ApproxBiLinear.Scale(dest, destCopyPosition, src, srcCopyPosition, draw.Src, nil)
|
|
|
|
|
|
|
|
case BilinearAccurate:
|
|
|
|
draw.BiLinear.Scale(dest, destCopyPosition, src, srcCopyPosition, draw.Src, nil)
|
2016-10-13 09:25:11 +00:00
|
|
|
|
2018-06-09 05:17:57 +00:00
|
|
|
case Bicubic:
|
|
|
|
draw.CatmullRom.Scale(dest, destCopyPosition, src, srcCopyPosition, draw.Src, nil)
|
2016-12-05 08:48:48 +00:00
|
|
|
}
|
|
|
|
|
2018-06-09 05:17:57 +00:00
|
|
|
return dest, nil
|
2018-06-09 04:39:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (this *DirectThumbnailer) encode(dest image.Image) ([]byte, error) {
|
|
|
|
|
2018-06-09 04:25:16 +00:00
|
|
|
switch this.cfg.Output {
|
2018-06-09 04:39:41 +00:00
|
|
|
case Png:
|
|
|
|
buff := bytes.Buffer{}
|
|
|
|
err := png.Encode(&buff, dest)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return buff.Bytes(), nil
|
|
|
|
|
|
|
|
case PngCrush:
|
2018-06-04 05:41:07 +00:00
|
|
|
return crushFast(dest)
|
2016-10-13 09:25:11 +00:00
|
|
|
|
2018-06-09 04:39:41 +00:00
|
|
|
case Jpeg:
|
2016-12-05 08:48:48 +00:00
|
|
|
buff := bytes.Buffer{}
|
|
|
|
err := jpeg.Encode(&buff, dest, &jpeg.Options{Quality: jpeg.DefaultQuality})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2016-10-13 09:25:11 +00:00
|
|
|
}
|
2016-12-05 08:48:48 +00:00
|
|
|
|
|
|
|
return buff.Bytes(), nil
|
|
|
|
|
2018-06-09 04:39:41 +00:00
|
|
|
case Bmp:
|
|
|
|
buff := bytes.Buffer{}
|
|
|
|
err := bmp.Encode(&buff, dest)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return buff.Bytes(), nil
|
|
|
|
|
2016-12-05 08:48:48 +00:00
|
|
|
default:
|
|
|
|
return nil, ErrInvalidOption
|
|
|
|
|
2016-10-13 09:25:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|