116 lines
2.4 KiB
Go
116 lines
2.4 KiB
Go
package thumbnail
|
|
|
|
import (
|
|
"bytes"
|
|
"image"
|
|
"image/gif"
|
|
"image/jpeg"
|
|
"image/png"
|
|
"io"
|
|
"os"
|
|
|
|
"golang.org/x/image/bmp"
|
|
"golang.org/x/image/webp"
|
|
)
|
|
|
|
type imageDecoder func(io.Reader) (image.Image, error)
|
|
|
|
var imageDecoders map[string]imageDecoder = map[string]imageDecoder{
|
|
`image/jpeg`: jpeg.Decode,
|
|
`image/png`: png.Decode,
|
|
`image/gif`: gif.Decode,
|
|
`image/webp`: webp.Decode,
|
|
`image/bmp`: bmp.Decode,
|
|
}
|
|
|
|
func (this *DirectThumbnailer) renderImageFile(absPath, mimeType string) ([]byte, error) {
|
|
|
|
fh, err := os.OpenFile(absPath, os.O_RDONLY, 0400)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer fh.Close()
|
|
|
|
fn, ok := imageDecoders[mimeType]
|
|
if !ok {
|
|
return nil, ErrUnsupportedFiletype
|
|
}
|
|
|
|
src, err := fn(fh)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return this.RenderScaledImage(src)
|
|
}
|
|
|
|
func (this *DirectThumbnailer) RenderScaledImage(src image.Image) ([]byte, error) {
|
|
srcW := src.Bounds().Max.X
|
|
srcH := src.Bounds().Max.Y
|
|
destW := 0
|
|
destH := 0
|
|
|
|
if srcW > srcH {
|
|
destW = this.cfg.Width
|
|
destH = this.cfg.Height * srcH / srcW
|
|
} else {
|
|
destW = this.cfg.Width * srcW / srcH
|
|
destH = this.cfg.Height
|
|
}
|
|
|
|
offsetX := (this.cfg.Width - destW) / 2
|
|
offsetY := (this.cfg.Height - destH) / 2
|
|
|
|
scaleW := float64(srcW) / float64(destW)
|
|
scaleH := float64(srcH) / float64(destH)
|
|
|
|
dest := image.NewRGBA(image.Rectangle{Max: image.Point{X: this.cfg.Width, Y: this.cfg.Height}})
|
|
|
|
switch this.cfg.Scale {
|
|
|
|
case SCALEFMT_BILINEAR:
|
|
|
|
for y := 0; y < destH; y += 1 {
|
|
for x := 0; x < destW; x += 1 {
|
|
c00 := src.At(int(float64(x)*scaleW), int(float64(y)*scaleH))
|
|
c01 := src.At(int((float64(x)+0.5)*scaleW), int(float64(y)*scaleH))
|
|
c10 := src.At(int(float64(x)*scaleW), int((float64(y)+0.5)*scaleH))
|
|
c11 := src.At(int((float64(x)+0.5)*scaleW), int((float64(y)+0.5)*scaleH))
|
|
cBlend := Blend(Blend(c00, c01), Blend(c10, c11))
|
|
dest.Set(x+offsetX, y+offsetY, cBlend)
|
|
|
|
}
|
|
}
|
|
|
|
case SCALEFMT_NN:
|
|
|
|
for y := 0; y < destH; y += 1 {
|
|
for x := 0; x < destW; x += 1 {
|
|
mapx := int(float64(x) * scaleW)
|
|
mapy := int(float64(y) * scaleH)
|
|
dest.Set(x+offsetX, y+offsetY, src.At(mapx, mapy))
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
switch this.cfg.Output {
|
|
case OUTPUT_PNG_CRUSH:
|
|
return crushFast(dest)
|
|
|
|
case OUTPUT_JPG:
|
|
buff := bytes.Buffer{}
|
|
err := jpeg.Encode(&buff, dest, &jpeg.Options{Quality: jpeg.DefaultQuality})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return buff.Bytes(), nil
|
|
|
|
default:
|
|
return nil, ErrInvalidOption
|
|
|
|
}
|
|
|
|
}
|