You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

114 lines
2.1 KiB

package image
import (
"bytes"
stdimage "image"
"image/jpeg"
"os"
"sync"
xdraw "golang.org/x/image/draw"
)
const (
defaultCacheEntries = 500
thumbnailWidth = 160
thumbnailHeight = 90
)
type Cache struct {
mu sync.Mutex
max int
entries map[string][]byte
order []string
}
func NewCache(max int) *Cache {
if max <= 0 {
max = defaultCacheEntries
}
return &Cache{
max: max,
entries: make(map[string][]byte),
}
}
func (c *Cache) Thumbnail(absPath string) ([]byte, error) {
if cached, ok := c.get(absPath); ok {
return cached, nil
}
file, err := os.Open(absPath)
if err != nil {
return nil, err
}
defer file.Close()
src, err := jpeg.Decode(file)
if err != nil {
return nil, err
}
dst := resizeToFit(src, thumbnailWidth, thumbnailHeight)
var out bytes.Buffer
if err := jpeg.Encode(&out, dst, &jpeg.Options{Quality: 75}); err != nil {
return nil, err
}
thumb := out.Bytes()
c.add(absPath, thumb)
return append([]byte(nil), thumb...), nil
}
func (c *Cache) get(key string) ([]byte, bool) {
c.mu.Lock()
defer c.mu.Unlock()
value, ok := c.entries[key]
if !ok {
return nil, false
}
return append([]byte(nil), value...), true
}
func (c *Cache) add(key string, value []byte) {
c.mu.Lock()
defer c.mu.Unlock()
if _, ok := c.entries[key]; !ok {
c.order = append(c.order, key)
}
c.entries[key] = append([]byte(nil), value...)
for len(c.order) > c.max {
oldest := c.order[0]
c.order = c.order[1:]
delete(c.entries, oldest)
}
}
func resizeToFit(src stdimage.Image, maxWidth, maxHeight int) stdimage.Image {
bounds := src.Bounds()
width := bounds.Dx()
height := bounds.Dy()
if width <= 0 || height <= 0 {
return stdimage.NewRGBA(stdimage.Rect(0, 0, 1, 1))
}
targetWidth := maxWidth
targetHeight := height * targetWidth / width
if targetHeight > maxHeight {
targetHeight = maxHeight
targetWidth = width * targetHeight / height
}
if targetWidth < 1 {
targetWidth = 1
}
if targetHeight < 1 {
targetHeight = 1
}
dst := stdimage.NewRGBA(stdimage.Rect(0, 0, targetWidth, targetHeight))
xdraw.BiLinear.Scale(dst, dst.Bounds(), src, bounds, xdraw.Over, nil)
return dst
}