diff options
Diffstat (limited to 'resources/images/image.go')
-rw-r--r-- | resources/images/image.go | 90 |
1 files changed, 77 insertions, 13 deletions
diff --git a/resources/images/image.go b/resources/images/image.go index b39e84972..d04c1e93d 100644 --- a/resources/images/image.go +++ b/resources/images/image.go @@ -15,16 +15,22 @@ package images import ( "image" + "image/color" + "image/gif" "image/jpeg" + "image/png" "io" "sync" - "github.com/disintegration/imaging" + "github.com/disintegration/gift" + "golang.org/x/image/bmp" + "golang.org/x/image/tiff" + "github.com/gohugoio/hugo/common/hugio" "github.com/pkg/errors" ) -func NewImage(f imaging.Format, proc *ImageProcessor, img image.Image, s Spec) *Image { +func NewImage(f Format, proc *ImageProcessor, img image.Image, s Spec) *Image { if img != nil { return &Image{ Format: f, @@ -40,7 +46,7 @@ func NewImage(f imaging.Format, proc *ImageProcessor, img image.Image, s Spec) * } type Image struct { - Format imaging.Format + Format Format Proc *ImageProcessor @@ -51,7 +57,7 @@ type Image struct { func (i *Image) EncodeTo(conf ImageConfig, img image.Image, w io.Writer) error { switch i.Format { - case imaging.JPEG: + case JPEG: var rgba *image.RGBA quality := conf.Quality @@ -69,9 +75,23 @@ func (i *Image) EncodeTo(conf ImageConfig, img image.Image, w io.Writer) error { return jpeg.Encode(w, rgba, &jpeg.Options{Quality: quality}) } return jpeg.Encode(w, img, &jpeg.Options{Quality: quality}) + case PNG: + encoder := png.Encoder{CompressionLevel: png.DefaultCompression} + return encoder.Encode(w, img) + + case GIF: + return gif.Encode(w, img, &gif.Options{ + NumColors: 256, + }) + case TIFF: + return tiff.Encode(w, img, &tiff.Options{Compression: tiff.Deflate, Predictor: true}) + + case BMP: + return bmp.Encode(w, img) default: - return imaging.Encode(w, img, i.Format) + return errors.New("format not supported") } + } // Height returns i's height. @@ -138,19 +158,52 @@ type ImageProcessor struct { Cfg Imaging } -func (p *ImageProcessor) Fill(src image.Image, conf ImageConfig) (image.Image, error) { - if conf.AnchorStr == SmartCropIdentifier { - return smartCrop(src, conf.Width, conf.Height, conf.Anchor, conf.Filter) +func (p *ImageProcessor) ApplyFiltersFromConfig(src image.Image, conf ImageConfig) (image.Image, error) { + var filters []gift.Filter + + if conf.Rotate != 0 { + // Apply any rotation before any resize. + filters = append(filters, gift.Rotate(float32(conf.Rotate), color.Transparent, gift.NearestNeighborInterpolation)) } - return imaging.Fill(src, conf.Width, conf.Height, conf.Anchor, conf.Filter), nil + + switch conf.Action { + case "resize": + filters = append(filters, gift.Resize(conf.Width, conf.Height, conf.Filter)) + case "fill": + if conf.AnchorStr == smartCropIdentifier { + bounds, err := p.smartCrop(src, conf.Width, conf.Height, conf.Filter) + if err != nil { + return nil, err + } + + // First crop it, then resize it. + filters = append(filters, gift.Crop(bounds)) + filters = append(filters, gift.Resize(conf.Width, conf.Height, conf.Filter)) + + } else { + filters = append(filters, gift.ResizeToFill(conf.Width, conf.Height, conf.Filter, conf.Anchor)) + } + case "fit": + filters = append(filters, gift.ResizeToFit(conf.Width, conf.Height, conf.Filter)) + default: + return nil, errors.Errorf("unsupported action: %q", conf.Action) + } + + return p.Filter(src, filters...) } -func (p *ImageProcessor) Fit(src image.Image, conf ImageConfig) (image.Image, error) { - return imaging.Fit(src, conf.Width, conf.Height, conf.Filter), nil +func (p *ImageProcessor) Filter(src image.Image, filters ...gift.Filter) (image.Image, error) { + g := gift.New(filters...) + dst := image.NewRGBA(g.Bounds(src.Bounds())) + g.Draw(dst, src) + return dst, nil } -func (p *ImageProcessor) Resize(src image.Image, conf ImageConfig) (image.Image, error) { - return imaging.Resize(src, conf.Width, conf.Height, conf.Filter), nil +func (p *ImageProcessor) GetDefaultImageConfig(action string) ImageConfig { + return ImageConfig{ + Action: action, + Quality: p.Cfg.Quality, + } } type Spec interface { @@ -158,6 +211,17 @@ type Spec interface { ReadSeekCloser() (hugio.ReadSeekCloser, error) } +// Format is an image file format. +type Format int + +const ( + JPEG Format = iota + 1 + PNG + GIF + TIFF + BMP +) + type imageConfig struct { config image.Config configInit sync.Once |