mirror of
https://github.com/filebrowser/filebrowser.git
synced 2024-06-07 23:00:43 +00:00
173 lines
3.1 KiB
Go
173 lines
3.1 KiB
Go
//go:generate go-enum --sql --marshal --file $GOFILE
|
|
package img
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"io"
|
|
"path/filepath"
|
|
|
|
"github.com/disintegration/imaging"
|
|
"github.com/marusama/semaphore/v2"
|
|
"github.com/spf13/afero"
|
|
)
|
|
|
|
// ErrUnsupportedFormat means the given image format is not supported.
|
|
var ErrUnsupportedFormat = errors.New("unsupported image format")
|
|
|
|
// Service
|
|
type Service struct {
|
|
lowPrioritySem semaphore.Semaphore
|
|
highPrioritySem semaphore.Semaphore
|
|
}
|
|
|
|
func New(workers int) *Service {
|
|
return &Service{
|
|
lowPrioritySem: semaphore.New(workers),
|
|
highPrioritySem: semaphore.New(workers),
|
|
}
|
|
}
|
|
|
|
// Format is an image file format.
|
|
/*
|
|
ENUM(
|
|
jpeg
|
|
png
|
|
gif
|
|
tiff
|
|
bmp
|
|
)
|
|
*/
|
|
type Format int
|
|
|
|
func (x Format) toImaging() imaging.Format {
|
|
switch x {
|
|
case FormatJpeg:
|
|
return imaging.JPEG
|
|
case FormatPng:
|
|
return imaging.PNG
|
|
case FormatGif:
|
|
return imaging.GIF
|
|
case FormatTiff:
|
|
return imaging.TIFF
|
|
case FormatBmp:
|
|
return imaging.BMP
|
|
default:
|
|
return imaging.JPEG
|
|
}
|
|
}
|
|
|
|
/*
|
|
ENUM(
|
|
high
|
|
medium
|
|
low
|
|
)
|
|
*/
|
|
type Quality int
|
|
|
|
func (x Quality) resampleFilter() imaging.ResampleFilter {
|
|
switch x {
|
|
case QualityHigh:
|
|
return imaging.Lanczos
|
|
case QualityMedium:
|
|
return imaging.Box
|
|
case QualityLow:
|
|
return imaging.NearestNeighbor
|
|
default:
|
|
return imaging.Linear
|
|
}
|
|
}
|
|
|
|
/*
|
|
ENUM(
|
|
fit
|
|
fill
|
|
)
|
|
*/
|
|
type ResizeMode int
|
|
|
|
func (s *Service) FormatFromExtension(ext string) (Format, error) {
|
|
format, err := imaging.FormatFromExtension(ext)
|
|
if err != nil {
|
|
return -1, ErrUnsupportedFormat
|
|
}
|
|
switch format {
|
|
case imaging.JPEG:
|
|
return FormatJpeg, nil
|
|
case imaging.PNG:
|
|
return FormatPng, nil
|
|
case imaging.GIF:
|
|
return FormatGif, nil
|
|
case imaging.TIFF:
|
|
return FormatTiff, nil
|
|
case imaging.BMP:
|
|
return FormatBmp, nil
|
|
}
|
|
return -1, ErrUnsupportedFormat
|
|
}
|
|
|
|
type resizeConfig struct {
|
|
prioritized bool
|
|
resizeMode ResizeMode
|
|
quality Quality
|
|
}
|
|
|
|
type Option func(*resizeConfig)
|
|
|
|
func WithMode(mode ResizeMode) Option {
|
|
return func(config *resizeConfig) {
|
|
config.resizeMode = mode
|
|
}
|
|
}
|
|
|
|
func WithQuality(quality Quality) Option {
|
|
return func(config *resizeConfig) {
|
|
config.quality = quality
|
|
}
|
|
}
|
|
|
|
func WithHighPriority() Option {
|
|
return func(config *resizeConfig) {
|
|
config.prioritized = true
|
|
}
|
|
}
|
|
|
|
func (s *Service) Resize(ctx context.Context, file afero.File, width, height int, out io.Writer, options ...Option) error {
|
|
config := resizeConfig{
|
|
resizeMode: ResizeModeFit,
|
|
quality: QualityMedium,
|
|
}
|
|
for _, option := range options {
|
|
option(&config)
|
|
}
|
|
|
|
sem := s.lowPrioritySem
|
|
if config.prioritized {
|
|
sem = s.highPrioritySem
|
|
}
|
|
|
|
if err := sem.Acquire(ctx, 1); err != nil {
|
|
return err
|
|
}
|
|
defer sem.Release(1)
|
|
|
|
format, err := s.FormatFromExtension(filepath.Ext(file.Name()))
|
|
if err != nil {
|
|
return ErrUnsupportedFormat
|
|
}
|
|
img, err := imaging.Decode(file, imaging.AutoOrientation(true))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
switch config.resizeMode {
|
|
case ResizeModeFill:
|
|
img = imaging.Fill(img, width, height, imaging.Center, config.quality.resampleFilter())
|
|
default:
|
|
img = imaging.Fit(img, width, height, config.quality.resampleFilter())
|
|
}
|
|
|
|
return imaging.Encode(out, img, format.toImaging())
|
|
}
|