16 Commits

Author SHA1 Message Date
bd09f93af1 make_archive.sh 2018-06-04 18:22:11 +12:00
f76b46a0d1 hgignore 2018-06-04 18:22:07 +12:00
6b2240f56e doc: update README 2018-06-04 17:56:56 +12:00
9a1d999cbd Removed tag release-1.0 2018-06-04 17:55:40 +12:00
db7e21a63c Added tag v0.1.0 for changeset efd7b4071770 2018-06-04 17:55:29 +12:00
5603cdd393 add compiletime build tag 'disableimagecrush' to remove cgo dependency 2018-06-04 17:41:07 +12:00
f0fe3685c2 Added tag v0.2.0 for changeset 292439a79182 2018-06-04 17:40:24 +12:00
394427d1b8 doc: README 2017-11-18 14:33:11 +13:00
9e7eca86b7 add height/width accessor methods 2017-11-18 13:26:07 +13:00
3378948eb2 doc: README 2017-11-18 13:10:52 +13:00
fb04e02c99 move files into top-level directory 2017-11-18 13:03:21 +13:00
05744a728e update tags 2017-11-17 12:01:43 +00:00
2791bc2cd7 thumbnailer: allow passing 0 as cache size 2017-11-18 12:52:53 +13:00
f89726198b thumbnailer: rename (unused) ASPECT_ constants 2017-11-18 12:52:42 +13:00
5b7b22bd95 thumbnailer: imports for previous 2017-11-18 12:52:24 +13:00
3905686363 thumbnailer: option to set custom mime type for file decoding 2017-11-18 12:52:17 +13:00
12 changed files with 102 additions and 30 deletions

2
.hgignore Normal file
View File

@@ -0,0 +1,2 @@
_dist/

5
.hgtags Normal file
View 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

View File

@@ -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,34 +44,50 @@ 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) {
thumb, ok := this.thumbCache.Get(absPath) if this.thumbCache != nil {
if ok { thumb, ok := this.thumbCache.Get(absPath)
return thumb.([]byte), nil if ok {
return thumb.([]byte), nil
}
} }
// Add to cache // Add to cache
@@ -78,8 +96,11 @@ func (this *Thumbnailer) RenderFile(absPath string) ([]byte, error) {
return nil, err return nil, err
} }
this.thumbCache.Add(absPath, thumb) if this.thumbCache != nil {
return thumb.([]byte), nil this.thumbCache.Add(absPath, thumb)
}
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
View 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`)

View File

@@ -5,7 +5,7 @@ import (
"fmt" "fmt"
"os" "os"
"code.ivysaur.me/webdir/thumbnail" "code.ivysaur.me/thumbnail"
) )
func main() { func main() {

View File

@@ -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{}

View File

@@ -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
View 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
View File

@@ -0,0 +1 @@
hg archive out.zip