refactor(thumbnails): separate out 'kovidgoyal/imaging' dependencies

Move code using the 'kovidgoyal/imaging' package to separate files
to make it easier to create alternative implementations based on build
tags.
This commit is contained in:
Ralf Haferkamp
2024-10-14 15:49:06 +02:00
committed by Ralf Haferkamp
parent d0ce57dfc2
commit 358adc15dc
8 changed files with 137 additions and 97 deletions

View File

@@ -27,18 +27,6 @@ type FileConverter interface {
Convert(r io.Reader) (interface{}, error)
}
// ImageDecoder is a converter for the image file
type ImageDecoder struct{}
// Convert reads the image file and returns the thumbnail image
func (i ImageDecoder) Convert(r io.Reader) (interface{}, error) {
img, err := imaging.Decode(r, imaging.AutoOrientation(true))
if err != nil {
return nil, errors.Wrap(err, `could not decode the image`)
}
return img, nil
}
// GifDecoder is a converter for the gif file
type GifDecoder struct{}

View File

@@ -0,0 +1,20 @@
package preprocessor
import (
"io"
"github.com/kovidgoyal/imaging"
"github.com/pkg/errors"
)
// ImageDecoder is a converter for the image file
type ImageDecoder struct{}
// Convert reads the image file and returns the thumbnail image
func (i ImageDecoder) Convert(r io.Reader) (interface{}, error) {
img, err := imaging.Decode(r, imaging.AutoOrientation(true))
if err != nil {
return nil, errors.Wrap(err, `could not decode the image`)
}
return img, nil
}

View File

@@ -1,10 +1,7 @@
package thumbnail
import (
"image"
"image/gif"
"image/jpeg"
"image/png"
"io"
"strings"
@@ -29,50 +26,6 @@ type Encoder interface {
MimeType() string
}
// PngEncoder encodes to png
type PngEncoder struct{}
// Encode encodes to png format
func (e PngEncoder) Encode(w io.Writer, img interface{}) error {
m, ok := img.(image.Image)
if !ok {
return errors.ErrInvalidType
}
return png.Encode(w, m)
}
// Types returns the png suffix
func (e PngEncoder) Types() []string {
return []string{typePng}
}
// MimeType returns the mimetype for png files.
func (e PngEncoder) MimeType() string {
return "image/png"
}
// JpegEncoder encodes to jpg
type JpegEncoder struct{}
// Encode encodes to jpg
func (e JpegEncoder) Encode(w io.Writer, img interface{}) error {
m, ok := img.(image.Image)
if !ok {
return errors.ErrInvalidType
}
return jpeg.Encode(w, m, nil)
}
// Types returns the jpg suffixes.
func (e JpegEncoder) Types() []string {
return []string{typeJpeg, typeJpg}
}
// MimeType returns the mimetype for jpg files.
func (e JpegEncoder) MimeType() string {
return "image/jpeg"
}
// GifEncoder encodes to gif
type GifEncoder struct{}

View File

@@ -0,0 +1,54 @@
package thumbnail
import (
"image"
"image/jpeg"
"image/png"
"io"
"github.com/owncloud/ocis/v2/services/thumbnails/pkg/errors"
)
// PngEncoder encodes to png
type PngEncoder struct{}
// Encode encodes to png format
func (e PngEncoder) Encode(w io.Writer, img interface{}) error {
m, ok := img.(image.Image)
if !ok {
return errors.ErrInvalidType
}
return png.Encode(w, m)
}
// Types returns the png suffix
func (e PngEncoder) Types() []string {
return []string{typePng}
}
// MimeType returns the mimetype for png files.
func (e PngEncoder) MimeType() string {
return "image/png"
}
// JpegEncoder encodes to jpg
type JpegEncoder struct{}
// Encode encodes to jpg
func (e JpegEncoder) Encode(w io.Writer, img interface{}) error {
m, ok := img.(image.Image)
if !ok {
return errors.ErrInvalidType
}
return jpeg.Encode(w, m, nil)
}
// Types returns the jpg suffixes.
func (e JpegEncoder) Types() []string {
return []string{typeJpeg, typeJpg}
}
// MimeType returns the mimetype for jpg files.
func (e JpegEncoder) MimeType() string {
return "image/jpeg"
}

View File

@@ -14,44 +14,17 @@ import (
// Generator generates a web friendly file version.
type Generator interface {
Generate(size image.Rectangle, img interface{}) (interface{}, error)
Dimensions(img interface{}) (image.Rectangle, error)
ProcessorID() string
}
// SimpleGenerator is the default image generator and is used for all image types expect gif.
type SimpleGenerator struct {
processor Processor
}
func NewSimpleGenerator(filetype, process string) (SimpleGenerator, error) {
processor, err := ProcessorFor(filetype, process)
if err != nil {
return SimpleGenerator{}, err
}
return SimpleGenerator{processor: processor}, nil
}
// ProcessorID returns the processor identification.
func (g SimpleGenerator) ProcessorID() string {
return g.processor.ID()
}
// Generate generates a alternative image version.
func (g SimpleGenerator) Generate(size image.Rectangle, img interface{}) (interface{}, error) {
m, ok := img.(image.Image)
if !ok {
return nil, errors.ErrInvalidType
}
return g.processor.Process(m, size.Dx(), size.Dy(), imaging.Lanczos), nil
}
// GifGenerator is used to create a web friendly version of the provided gif image.
type GifGenerator struct {
processor Processor
}
func NewGifGenerator(filetype, process string) (GifGenerator, error) {
processor, err := ProcessorFor(filetype, process)
processor, err := ProcessorFor(process, filetype)
if err != nil {
return GifGenerator{}, err
}
@@ -97,6 +70,14 @@ func (g GifGenerator) Generate(size image.Rectangle, img interface{}) (interface
return m, nil
}
func (g GifGenerator) Dimensions(img interface{}) (image.Rectangle, error) {
m, ok := img.(*gif.GIF)
if !ok {
return image.Rectangle{}, errors.ErrInvalidType
}
return m.Image[0].Bounds(), nil
}
func (g GifGenerator) imageToPaletted(img image.Image, p color.Palette) *image.Paletted {
b := img.Bounds()
pm := image.NewPaletted(b, p)

View File

@@ -0,0 +1,44 @@
package thumbnail
import (
"image"
"github.com/kovidgoyal/imaging"
"github.com/owncloud/ocis/v2/services/thumbnails/pkg/errors"
)
// SimpleGenerator is the default image generator and is used for all image types expect gif.
type SimpleGenerator struct {
processor Processor
}
func NewSimpleGenerator(filetype, process string) (SimpleGenerator, error) {
processor, err := ProcessorFor(process, filetype)
if err != nil {
return SimpleGenerator{}, err
}
return SimpleGenerator{processor: processor}, nil
}
// ProcessorID returns the processor identification.
func (g SimpleGenerator) ProcessorID() string {
return g.processor.ID()
}
// Generate generates a alternative image version.
func (g SimpleGenerator) Generate(size image.Rectangle, img interface{}) (interface{}, error) {
m, ok := img.(image.Image)
if !ok {
return nil, errors.ErrInvalidType
}
return g.processor.Process(m, size.Dx(), size.Dy(), imaging.Lanczos), nil
}
func (g SimpleGenerator) Dimensions(img interface{}) (image.Rectangle, error) {
m, ok := img.(image.Image)
if !ok {
return image.Rectangle{}, errors.ErrInvalidType
}
return m.Bounds(), nil
}

View File

@@ -3,7 +3,6 @@ package thumbnail
import (
"bytes"
"image"
"image/gif"
"mime"
"github.com/owncloud/ocis/v2/ocis-pkg/log"
@@ -70,15 +69,12 @@ type SimpleManager struct {
// Generate creates a thumbnail and stores it
func (s SimpleManager) Generate(r Request, img interface{}) (string, error) {
var match image.Rectangle
var inputDimensions image.Rectangle
switch m := img.(type) {
case *gif.GIF:
match = s.resolutions.ClosestMatch(r.Resolution, m.Image[0].Bounds())
inputDimensions = m.Image[0].Bounds()
case image.Image:
match = s.resolutions.ClosestMatch(r.Resolution, m.Bounds())
inputDimensions = m.Bounds()
inputDimensions, err := r.Generator.Dimensions(img)
if err != nil {
return "", err
}
match = s.resolutions.ClosestMatch(r.Resolution, inputDimensions)
// validate max input image dimensions - 6016x4000
if inputDimensions.Size().X > s.maxDimension.X || inputDimensions.Size().Y > s.maxDimension.Y {

View File

@@ -183,6 +183,10 @@ func TestPreviewGenerationTooBigImage(t *testing.T) {
ext := path.Ext(tt.fileName)
req.Encoder, _ = EncoderForType(ext)
req.Generator, err = GeneratorFor(ext, "fit")
if err != nil {
return
}
generate, err := sut.Generate(req, convert)
if err != nil {
return