Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bd09f93af1 | |||
| f76b46a0d1 | |||
| 6b2240f56e | |||
| 9a1d999cbd | |||
| db7e21a63c | |||
| 5603cdd393 | |||
| f0fe3685c2 | |||
| 394427d1b8 | |||
| 9e7eca86b7 | |||
| 3378948eb2 | |||
| fb04e02c99 | |||
| 05744a728e | |||
| 2791bc2cd7 | |||
| f89726198b | |||
| 5b7b22bd95 | |||
| 3905686363 |
5
.hgtags
Normal file
5
.hgtags
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
efd7b407177086c57e8c086605c2c8d1cee23840 release-1.0
|
||||||
|
292439a79182796c2f6277ef13ca179379b0fb88 v0.2.0
|
||||||
|
efd7b407177086c57e8c086605c2c8d1cee23840 v0.1.0
|
||||||
|
efd7b407177086c57e8c086605c2c8d1cee23840 release-1.0
|
||||||
|
0000000000000000000000000000000000000000 release-1.0
|
||||||
@@ -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
|
||||||
|
|
||||||
}
|
}
|
||||||
27
_dist/README.txt
Normal file
27
_dist/README.txt
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
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=
|
||||||
|
|
||||||
|
2018-05-04 0.2.1
|
||||||
|
- Add `disableimagecrush` build tag option to exclude cgo library dependency
|
||||||
|
|
||||||
|
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 0.1.0
|
||||||
|
- Version of `thumbnail` vendored with `webdir` 1.0 (previously tagged as `release-1.0`)
|
||||||
|
|
||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"code.ivysaur.me/webdir/thumbnail"
|
"code.ivysaur.me/thumbnail"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"image"
|
"image"
|
||||||
"image/jpeg"
|
"image/jpeg"
|
||||||
|
|
||||||
"code.ivysaur.me/imagequant"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (this *Thumbnailer) RenderScaledImage(src image.Image) ([]byte, error) {
|
func (this *Thumbnailer) RenderScaledImage(src image.Image) ([]byte, error) {
|
||||||
@@ -60,7 +58,7 @@ func (this *Thumbnailer) RenderScaledImage(src image.Image) ([]byte, error) {
|
|||||||
|
|
||||||
switch this.ofmt {
|
switch this.ofmt {
|
||||||
case OUTPUT_PNG_CRUSH:
|
case OUTPUT_PNG_CRUSH:
|
||||||
return crush(dest, imagequant.SPEED_FASTEST)
|
return crushFast(dest)
|
||||||
|
|
||||||
case OUTPUT_JPG:
|
case OUTPUT_JPG:
|
||||||
buff := bytes.Buffer{}
|
buff := bytes.Buffer{}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//+build !disableimagecrush
|
||||||
|
|
||||||
package thumbnail
|
package thumbnail
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -51,6 +53,10 @@ func rgb8PaletteToGoImage(w, h int, rgb8data []byte, pal color.Palette) image.Im
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func crushFast(img image.Image) ([]byte, error) {
|
||||||
|
return crush(img, imagequant.SPEED_FASTEST)
|
||||||
|
}
|
||||||
|
|
||||||
func crush(img image.Image, speed int) ([]byte, error) {
|
func crush(img image.Image, speed int) ([]byte, error) {
|
||||||
|
|
||||||
width := img.Bounds().Max.X
|
width := img.Bounds().Max.X
|
||||||
12
imagecrush_disabled.go
Normal file
12
imagecrush_disabled.go
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
// +build disableimagecrush
|
||||||
|
|
||||||
|
package thumbnail
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"image"
|
||||||
|
)
|
||||||
|
|
||||||
|
func crushFast(img image.Image) ([]byte, error) {
|
||||||
|
return nil, errors.New("Pngquant not compiled in")
|
||||||
|
}
|
||||||
1
make_archive.sh
Executable file
1
make_archive.sh
Executable file
@@ -0,0 +1 @@
|
|||||||
|
hg archive out.zip
|
||||||
Reference in New Issue
Block a user