Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 394427d1b8 | |||
| 9e7eca86b7 | |||
| 3378948eb2 | |||
| fb04e02c99 | |||
| 05744a728e | |||
| 2791bc2cd7 | |||
| f89726198b | |||
| 5b7b22bd95 | |||
| 3905686363 |
@@ -5,6 +5,7 @@ import (
|
|||||||
"image/gif"
|
"image/gif"
|
||||||
"image/jpeg"
|
"image/jpeg"
|
||||||
"image/png"
|
"image/png"
|
||||||
|
"mime"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -21,9 +22,10 @@ const (
|
|||||||
OUTPUT_JPG OutputFormat = 3
|
OUTPUT_JPG OutputFormat = 3
|
||||||
OUTPUT__DEFAULT = OUTPUT_PNG_CRUSH
|
OUTPUT__DEFAULT = OUTPUT_PNG_CRUSH
|
||||||
|
|
||||||
ASPECT_PADDED AspectFormat = 80
|
ASPECT_PAD_TO_DIMENSIONS AspectFormat = 80
|
||||||
ASPECT_SVELTE AspectFormat = 81
|
ASPECT_RESPECT_MAX_DIMENSION_ONLY AspectFormat = 81
|
||||||
ASPECT__DEFAULT = ASPECT_PADDED
|
ASPECT_CROP_TO_DIMENSIONS AspectFormat = 82
|
||||||
|
ASPECT__DEFAULT = ASPECT_PAD_TO_DIMENSIONS
|
||||||
|
|
||||||
SCALEFMT_NN ScaleFormat = 120
|
SCALEFMT_NN ScaleFormat = 120
|
||||||
SCALEFMT_BILINEAR ScaleFormat = 121
|
SCALEFMT_BILINEAR ScaleFormat = 121
|
||||||
@@ -42,35 +44,51 @@ type Thumbnailer struct {
|
|||||||
afmt AspectFormat
|
afmt AspectFormat
|
||||||
sfmt ScaleFormat
|
sfmt ScaleFormat
|
||||||
|
|
||||||
thumbCache *lru.Cache // threadsafe
|
thumbCache *lru.Cache // threadsafe, might be nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewThumbnailer(Width, Height, MaxCacheSize int) *Thumbnailer {
|
func NewThumbnailer(Width, Height int, MaxCacheSize uint) *Thumbnailer {
|
||||||
return NewThumbnailerEx(Width, Height, MaxCacheSize, OUTPUT__DEFAULT, ASPECT__DEFAULT, SCALEFMT__DEFAULT)
|
return NewThumbnailerEx(Width, Height, MaxCacheSize, OUTPUT__DEFAULT, ASPECT__DEFAULT, SCALEFMT__DEFAULT)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewThumbnailerEx(Width, Height, MaxCacheSize int, of OutputFormat, af AspectFormat, sf ScaleFormat) *Thumbnailer {
|
func NewThumbnailerEx(Width, Height int, MaxCacheSize uint, of OutputFormat, af AspectFormat, sf ScaleFormat) *Thumbnailer {
|
||||||
thumbCache, err := lru.New(MaxCacheSize)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Thumbnailer{
|
ret := &Thumbnailer{
|
||||||
width: Width,
|
width: Width,
|
||||||
height: Height,
|
height: Height,
|
||||||
ofmt: of,
|
ofmt: of,
|
||||||
afmt: af,
|
afmt: af,
|
||||||
sfmt: sf,
|
sfmt: sf,
|
||||||
thumbCache: thumbCache,
|
thumbCache: nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if MaxCacheSize > 0 {
|
||||||
|
thumbCache, err := lru.New(int(MaxCacheSize))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
ret.thumbCache = thumbCache
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Thumbnailer) Width() int {
|
||||||
|
return this.width
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Thumbnailer) Height() int {
|
||||||
|
return this.height
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *Thumbnailer) RenderFile(absPath string) ([]byte, error) {
|
func (this *Thumbnailer) RenderFile(absPath string) ([]byte, error) {
|
||||||
|
|
||||||
|
if this.thumbCache != nil {
|
||||||
thumb, ok := this.thumbCache.Get(absPath)
|
thumb, ok := this.thumbCache.Get(absPath)
|
||||||
if ok {
|
if ok {
|
||||||
return thumb.([]byte), nil
|
return thumb.([]byte), nil
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Add to cache
|
// Add to cache
|
||||||
thumb, err := this.RenderFile_NoCache(absPath)
|
thumb, err := this.RenderFile_NoCache(absPath)
|
||||||
@@ -78,8 +96,11 @@ func (this *Thumbnailer) RenderFile(absPath string) ([]byte, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if this.thumbCache != nil {
|
||||||
this.thumbCache.Add(absPath, thumb)
|
this.thumbCache.Add(absPath, thumb)
|
||||||
return thumb.([]byte), nil
|
}
|
||||||
|
|
||||||
|
return thumb, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func FiletypeSupported(ext string) bool {
|
func FiletypeSupported(ext string) bool {
|
||||||
@@ -92,18 +113,18 @@ func FiletypeSupported(ext string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *Thumbnailer) RenderFile_NoCache(absPath string) ([]byte, error) {
|
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) {
|
||||||
|
|
||||||
fh, err := os.OpenFile(absPath, os.O_RDONLY, 0400)
|
fh, err := os.OpenFile(absPath, os.O_RDONLY, 0400)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer fh.Close()
|
defer fh.Close()
|
||||||
|
|
||||||
extension := strings.ToLower(filepath.Ext(absPath))
|
if mimeType == `image/jpeg` {
|
||||||
|
|
||||||
switch extension {
|
|
||||||
case ".jpg", ".jpeg":
|
|
||||||
src, err := jpeg.Decode(fh)
|
src, err := jpeg.Decode(fh)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -111,7 +132,7 @@ func (this *Thumbnailer) RenderFile_NoCache(absPath string) ([]byte, error) {
|
|||||||
|
|
||||||
return this.RenderScaledImage(src)
|
return this.RenderScaledImage(src)
|
||||||
|
|
||||||
case ".png":
|
} else if mimeType == `image/png` {
|
||||||
src, err := png.Decode(fh)
|
src, err := png.Decode(fh)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -119,7 +140,7 @@ func (this *Thumbnailer) RenderFile_NoCache(absPath string) ([]byte, error) {
|
|||||||
|
|
||||||
return this.RenderScaledImage(src)
|
return this.RenderScaledImage(src)
|
||||||
|
|
||||||
case ".gif":
|
} else if mimeType == `image/gif` {
|
||||||
src, err := gif.Decode(fh)
|
src, err := gif.Decode(fh)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -127,10 +148,10 @@ func (this *Thumbnailer) RenderFile_NoCache(absPath string) ([]byte, error) {
|
|||||||
|
|
||||||
return this.RenderScaledImage(src)
|
return this.RenderScaledImage(src)
|
||||||
|
|
||||||
case ".avi", ".mkv", ".mp4", ".ogm", ".wmv", ".flv", ".rm", ".rmvb":
|
} else if strings.HasPrefix(mimeType, `video/`) {
|
||||||
return this.RenderScaledFfmpeg(absPath)
|
return this.RenderScaledFfmpeg(absPath)
|
||||||
|
|
||||||
default:
|
} else {
|
||||||
return nil, ErrUnsupportedFiletype
|
return nil, ErrUnsupportedFiletype
|
||||||
|
|
||||||
}
|
}
|
||||||
23
_dist/README.txt
Normal file
23
_dist/README.txt
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
A thumbnailing library for Go.
|
||||||
|
|
||||||
|
Written in Go
|
||||||
|
|
||||||
|
Earlier versions of this project were vendored into `webdir`.
|
||||||
|
|
||||||
|
- Supports jpeg / png / gif files (internally) and video files (requires `ffmpeg` in `$PATH`)
|
||||||
|
- LRU cache of recent thumbnails for performance
|
||||||
|
- Scaling algorithms: Nearest-neighbour or bilinear
|
||||||
|
- Aspect ratio preserving (fit outside with image crop; fit inside with transparent background; fit inside with dimension reduction)
|
||||||
|
- Output formats: JPG (internal) or transparent PNG (via `go-imagequant`)
|
||||||
|
|
||||||
|
A standalone binary `mkthumb` is provided as a sample utility.
|
||||||
|
|
||||||
|
=CHANGELOG=
|
||||||
|
|
||||||
|
2017-11-18 0.2.0
|
||||||
|
- Initial standalone release
|
||||||
|
- Feature: Decode input with specified mime type
|
||||||
|
- Feature: Allow passing zero as thumbnail cache size
|
||||||
|
|
||||||
|
2017-01-03 webdir-1.0
|
||||||
|
- Version of `thumbnail` vendored with `webdir` 1.0
|
||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"code.ivysaur.me/webdir/thumbnail"
|
"code.ivysaur.me/thumbnail"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|||||||
Reference in New Issue
Block a user