thumbnail/Thumbnailer.go

160 lines
3.3 KiB
Go
Raw Normal View History

2016-11-18 06:44:27 +00:00
package thumbnail
import (
"errors"
2018-06-09 00:25:27 +00:00
"image"
2016-12-05 06:08:13 +00:00
"image/gif"
2016-11-18 06:44:27 +00:00
"image/jpeg"
"image/png"
2018-06-09 00:25:27 +00:00
"io"
2017-11-17 23:52:24 +00:00
"mime"
2016-11-18 06:44:27 +00:00
"os"
2016-12-05 06:03:05 +00:00
"path/filepath"
2016-11-18 06:44:27 +00:00
"strings"
2016-11-18 07:02:28 +00:00
lru "github.com/hashicorp/golang-lru"
2018-06-09 00:25:27 +00:00
"golang.org/x/image/bmp"
"golang.org/x/image/webp"
2016-11-18 06:44:27 +00:00
)
type OutputFormat uint8
type AspectFormat uint8
type ScaleFormat uint8
const (
OUTPUT_PNG_CRUSH OutputFormat = 2
OUTPUT_JPG OutputFormat = 3
OUTPUT__DEFAULT = OUTPUT_PNG_CRUSH
ASPECT_PAD_TO_DIMENSIONS AspectFormat = 80
ASPECT_RESPECT_MAX_DIMENSION_ONLY AspectFormat = 81
ASPECT_CROP_TO_DIMENSIONS AspectFormat = 82
ASPECT__DEFAULT = ASPECT_PAD_TO_DIMENSIONS
SCALEFMT_NN ScaleFormat = 120
SCALEFMT_BILINEAR ScaleFormat = 121
SCALEFMT__DEFAULT = SCALEFMT_BILINEAR
)
var (
ErrInvalidOption error = errors.New("Invalid format parameter")
ErrUnsupportedFiletype error = errors.New("Unsupported filetype")
)
2016-11-18 06:44:27 +00:00
type Thumbnailer struct {
width int
height int
ofmt OutputFormat
afmt AspectFormat
sfmt ScaleFormat
2016-11-18 06:44:27 +00:00
thumbCache *lru.Cache // threadsafe, might be nil
2016-11-18 06:44:27 +00:00
}
func NewThumbnailer(Width, Height int, MaxCacheSize uint) *Thumbnailer {
return NewThumbnailerEx(Width, Height, MaxCacheSize, OUTPUT__DEFAULT, ASPECT__DEFAULT, SCALEFMT__DEFAULT)
}
func NewThumbnailerEx(Width, Height int, MaxCacheSize uint, of OutputFormat, af AspectFormat, sf ScaleFormat) *Thumbnailer {
2016-11-18 07:02:28 +00:00
ret := &Thumbnailer{
width: Width,
height: Height,
ofmt: of,
afmt: af,
sfmt: sf,
thumbCache: nil,
}
if MaxCacheSize > 0 {
thumbCache, err := lru.New(int(MaxCacheSize))
if err != nil {
panic(err)
}
ret.thumbCache = thumbCache
2016-11-18 06:44:27 +00:00
}
return ret
2016-11-18 06:44:27 +00:00
}
2017-11-18 00:26:07 +00:00
func (this *Thumbnailer) Width() int {
return this.width
}
func (this *Thumbnailer) Height() int {
return this.height
}
2016-11-18 06:44:27 +00:00
func (this *Thumbnailer) RenderFile(absPath string) ([]byte, error) {
if this.thumbCache != nil {
thumb, ok := this.thumbCache.Get(absPath)
if ok {
return thumb.([]byte), nil
}
2016-11-18 06:44:27 +00:00
}
// Add to cache
2016-11-18 07:02:28 +00:00
thumb, err := this.RenderFile_NoCache(absPath)
2016-11-18 06:44:27 +00:00
if err != nil {
return nil, err
}
if this.thumbCache != nil {
this.thumbCache.Add(absPath, thumb)
}
return thumb, nil
2016-11-18 06:44:27 +00:00
}
2016-12-05 06:47:24 +00:00
func FiletypeSupported(ext string) bool {
switch strings.ToLower(ext) {
2018-06-09 00:25:27 +00:00
case
".jpg", ".jpeg", ".png", ".gif",
".avi", ".mkv", ".mp4", ".ogm", ".wmv", ".flv", ".rm", ".rmvb",
".bmp", ".webp":
2016-12-05 06:47:24 +00:00
return true
default:
return false
}
}
2016-11-18 06:44:27 +00:00
func (this *Thumbnailer) RenderFile_NoCache(absPath string) ([]byte, error) {
return this.RenderFile_NoCache_MimeType(absPath, mime.TypeByExtension(filepath.Ext(absPath)))
}
func (this *Thumbnailer) RenderFile_NoCache_MimeType(absPath, mimeType string) ([]byte, error) {
2016-11-18 06:44:27 +00:00
fh, err := os.OpenFile(absPath, os.O_RDONLY, 0400)
if err != nil {
return nil, err
}
defer fh.Close()
2018-06-09 00:25:27 +00:00
type imageDecoder func(io.Reader) (image.Image, error)
imageDecoders := map[string]imageDecoder{
`image/jpeg`: jpeg.Decode,
`image/png`: png.Decode,
`image/gif`: gif.Decode,
`image/webp`: webp.Decode,
`image/bmp`: bmp.Decode,
}
2018-06-09 00:25:27 +00:00
if fn, ok := imageDecoders[mimeType]; ok {
src, err := fn(fh)
2016-12-05 06:08:13 +00:00
if err != nil {
return nil, err
}
return this.RenderScaledImage(src)
} else if strings.HasPrefix(mimeType, `video/`) {
return this.RenderScaledFfmpeg(absPath)
} else {
return nil, ErrUnsupportedFiletype
2016-11-18 06:44:27 +00:00
}
}