Skip to content

Commit

Permalink
feat: add gifsicle backend (#134)
Browse files Browse the repository at this point in the history
  • Loading branch information
yansal authored Sep 10, 2020
1 parent 07c576d commit d8484bb
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 9 deletions.
112 changes: 112 additions & 0 deletions engine/backend/gifsicle.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package backend

import (
"bytes"
"errors"
"fmt"
"image/gif"
"os/exec"

"github.com/thoas/picfit/image"
)

// Gifsicle is the gifsicle backend.
type Gifsicle struct {
Path string
}

func (b *Gifsicle) String() string {
return "gifsicle"
}

// Fit implements Backend.
func (b *Gifsicle) Fit(*image.ImageFile, *Options) ([]byte, error) {
return nil, MethodNotImplementedError
}

// Flat implements Backend.
func (b *Gifsicle) Flat(*image.ImageFile, *Options) ([]byte, error) {
return nil, MethodNotImplementedError
}

// Flip implements Backend.
func (b *Gifsicle) Flip(*image.ImageFile, *Options) ([]byte, error) {
return nil, MethodNotImplementedError
}

// Resize implements Backend.
func (b *Gifsicle) Resize(imgfile *image.ImageFile, opts *Options) ([]byte, error) {
cmd := exec.Command(b.Path,
"--resize", fmt.Sprintf("%dx%d", opts.Width, opts.Height),
)
cmd.Stdin = bytes.NewReader(imgfile.Source)
stdout := new(bytes.Buffer)
cmd.Stdout = stdout
stderr := new(bytes.Buffer)
cmd.Stderr = stderr

var target *exec.ExitError
if err := cmd.Run(); errors.As(err, &target) && target.Exited() {
return nil, errors.New(stderr.String())
} else if err != nil {
return nil, err
}
return stdout.Bytes(), nil
}

// Rotate implements Backend.
func (b *Gifsicle) Rotate(*image.ImageFile, *Options) ([]byte, error) {
return nil, MethodNotImplementedError
}

// Thumbnail implements Backend.
func (b *Gifsicle) Thumbnail(imgfile *image.ImageFile, opts *Options) ([]byte, error) {
img, err := gif.Decode(bytes.NewReader(imgfile.Source))
if err != nil {
return nil, err
}
bounds := img.Bounds()
left, top, cropw, croph := computecrop(bounds.Dx(), bounds.Dy(), opts.Width, opts.Height)

cmd := exec.Command(b.Path,
"--crop", fmt.Sprintf("%d,%d+%dx%d", left, top, cropw, croph),
"--resize", fmt.Sprintf("%dx%d", opts.Width, opts.Height),
)
cmd.Stdin = bytes.NewReader(imgfile.Source)
stdout := new(bytes.Buffer)
cmd.Stdout = stdout
stderr := new(bytes.Buffer)
cmd.Stderr = stderr

var target *exec.ExitError
if err := cmd.Run(); errors.As(err, &target) && target.Exited() {
return nil, errors.New(stderr.String())
} else if err != nil {
return nil, err
}
return stdout.Bytes(), nil
}

func computecrop(srcw, srch, destw, desth int) (left, top, cropw, croph int) {
srcratio := float64(srcw) / float64(srch)
destratio := float64(destw) / float64(desth)

if srcratio > destratio {
cropw = int((destratio * float64(srch)) + 0.5)
croph = srch
} else {
croph = int((float64(srcw) / destratio) + 0.5)
cropw = srcw
}

left = int(float64(srcw-cropw) * 0.5)
if left < 0 {
left = 0
}

top = int(float64(srch-croph) * 0.5)
if top < 0 {
top = 0
}
return
}
11 changes: 9 additions & 2 deletions engine/config/config.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
package config

type Backends struct {
Lilliput *Backend `mapstructure:"lilliput"`
GoImage *Backend `mapstructure:"goimage"`
Gifsicle *CommandBackend `mapstructure:"gifsicle"`
GoImage *Backend `mapstructure:"goimage"`
Lilliput *Backend `mapstructure:"lilliput"`
}

type Backend struct {
Weight int
Mimetypes []string
}

type CommandBackend struct {
Path string
Mimetypes []string
Weight int
}

// Config is the engine config
type Config struct {
Backends *Backends `mapstructure:"backends"`
Expand Down
28 changes: 21 additions & 7 deletions engine/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package engine

import (
"fmt"
"os/exec"
"sort"
"strings"

Expand Down Expand Up @@ -34,21 +35,34 @@ func New(cfg config.Config) *Engine {
mimetypes: MimeTypes,
})
} else {
if cfg.Backends.Lilliput != nil {
b = append(b, Backend{
Backend: backend.NewLilliput(cfg),
mimetypes: cfg.Backends.Lilliput.Mimetypes,
weight: cfg.Backends.Lilliput.Weight,
})
}
if cfg.Backends.Gifsicle != nil {
path := cfg.Backends.Gifsicle.Path
if path == "" {
path = "gifsicle"
}

if _, err := exec.LookPath(path); err == nil {
b = append(b, Backend{
Backend: &backend.Gifsicle{Path: path},
mimetypes: cfg.Backends.Gifsicle.Mimetypes,
weight: cfg.Backends.Gifsicle.Weight,
})
}
}
if cfg.Backends.GoImage != nil {
b = append(b, Backend{
Backend: &backend.GoImage{},
mimetypes: cfg.Backends.GoImage.Mimetypes,
weight: cfg.Backends.GoImage.Weight,
})
}
if cfg.Backends.Lilliput != nil {
b = append(b, Backend{
Backend: backend.NewLilliput(cfg),
mimetypes: cfg.Backends.Lilliput.Mimetypes,
weight: cfg.Backends.Lilliput.Weight,
})
}
}

sort.Slice(b, func(i, j int) bool {
Expand Down

0 comments on commit d8484bb

Please sign in to comment.