From b2561b56c4c8981cb62d27f8b23423a95f6fd738 Mon Sep 17 00:00:00 2001 From: Vivien Cormier Date: Fri, 8 Mar 2024 09:52:28 +0100 Subject: [PATCH] feat: Add blur (#189) --- README.rst | 13 ++++++++++++- constants/constants.go | 6 ++++++ engine/backend/backend.go | 4 +++- engine/backend/exif.go | 2 +- engine/backend/gifsicle.go | 5 +++++ engine/backend/goimage.go | 21 ++++++++++++++++++++- engine/backend/goimage_flat.go | 2 +- engine/engine.go | 7 +++++-- engine/operations.go | 2 ++ go.mod | 7 ++++--- go.sum | 6 ++++++ parameters.go | 34 +++++++++++++++++----------------- processor_test.go | 2 +- 13 files changed, 83 insertions(+), 28 deletions(-) diff --git a/README.rst b/README.rst index b0fd960e..4b33b3cb 100644 --- a/README.rst +++ b/README.rst @@ -376,7 +376,7 @@ Parameters to call the picfit service are: .. code-block:: html - `_. +Effect +------ + +Add effect to the given image. + +- **filter** - The desired effect : ``blur`` + +You have to pass the ``effect`` value to the ``op`` parameter +to use this operation. + Methods ======= diff --git a/constants/constants.go b/constants/constants.go index 075bd731..9dbaff72 100644 --- a/constants/constants.go +++ b/constants/constants.go @@ -40,6 +40,12 @@ var StickPositions = []string{ TopRight, } +const FilterBlur = "blur" + +var Filters = []string{ + FilterBlur, +} + const ModifiedTimeFormat = time.RFC1123 const RequestIDCtx = "request-id" diff --git a/engine/backend/backend.go b/engine/backend/backend.go index 313d31c6..560b4aff 100644 --- a/engine/backend/backend.go +++ b/engine/backend/backend.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - "github.com/disintegration/imaging" + "github.com/go-spectest/imaging" "github.com/pkg/errors" "github.com/thoas/picfit/image" ) @@ -16,6 +16,7 @@ var MethodNotImplementedError = errors.New("Not implemented") type Options struct { Color string Degree int + Filter string Format imaging.Format Height int Images []image.ImageFile @@ -33,6 +34,7 @@ func (o Options) String() string { // Engine is an interface to define an image engine type Backend interface { + Effect(ctx context.Context, img *image.ImageFile, options *Options) ([]byte, error) Fit(ctx context.Context, img *image.ImageFile, options *Options) ([]byte, error) Flat(ctx context.Context, background *image.ImageFile, options *Options) ([]byte, error) Flip(ctx context.Context, img *image.ImageFile, options *Options) ([]byte, error) diff --git a/engine/backend/exif.go b/engine/backend/exif.go index dfc8093b..dd6c7702 100644 --- a/engine/backend/exif.go +++ b/engine/backend/exif.go @@ -4,7 +4,7 @@ import ( "image" "io" - "github.com/disintegration/imaging" + "github.com/go-spectest/imaging" "github.com/rwcarlsen/goexif/exif" _ "golang.org/x/image/webp" ) diff --git a/engine/backend/gifsicle.go b/engine/backend/gifsicle.go index 3fba6238..1fa54e45 100644 --- a/engine/backend/gifsicle.go +++ b/engine/backend/gifsicle.go @@ -92,6 +92,11 @@ func (b *Gifsicle) Fit(ctx context.Context, img *image.ImageFile, options *Optio return nil, MethodNotImplementedError } +// Effect implements Backend. +func (b *Gifsicle) Effect(ctx context.Context, img *image.ImageFile, options *Options) ([]byte, error) { + return nil, MethodNotImplementedError +} + // Flat implements Backend. func (b *Gifsicle) Flat(ctx context.Context, img *image.ImageFile, options *Options) ([]byte, error) { return nil, MethodNotImplementedError diff --git a/engine/backend/goimage.go b/engine/backend/goimage.go index df37456a..a6f3d09f 100644 --- a/engine/backend/goimage.go +++ b/engine/backend/goimage.go @@ -4,6 +4,8 @@ import ( "bytes" "context" "fmt" + "github.com/thoas/go-funk" + "github.com/thoas/picfit/constants" "image" "image/color/palette" "image/draw" @@ -13,7 +15,7 @@ import ( "io" "math" - "github.com/disintegration/imaging" + "github.com/go-spectest/imaging" imagefile "github.com/thoas/picfit/image" @@ -102,6 +104,23 @@ func (e *GoImage) Fit(ctx context.Context, img *imagefile.ImageFile, options *Op return e.transform(image, options, imaging.Fit) } +func (e *GoImage) Effect(ctx context.Context, img *imagefile.ImageFile, options *Options) ([]byte, error) { + image, err := e.source(img) + if err != nil { + return nil, err + } + width, height := imageSize(image) + size := funk.MaxInt([]int{width, height}) + sigma := size / 20 + + switch options.Filter { + case constants.FilterBlur: + return e.toBytes(imaging.Blur(image, float64(sigma)), options.Format, options.Quality) + } + + return nil, MethodNotImplementedError +} + func (e *GoImage) toBytes(img image.Image, format imaging.Format, quality int) ([]byte, error) { var buf bytes.Buffer diff --git a/engine/backend/goimage_flat.go b/engine/backend/goimage_flat.go index 01347398..ed4821e2 100644 --- a/engine/backend/goimage_flat.go +++ b/engine/backend/goimage_flat.go @@ -10,7 +10,7 @@ import ( "strconv" "strings" - "github.com/disintegration/imaging" + "github.com/go-spectest/imaging" colorful "github.com/lucasb-eyer/go-colorful" "github.com/thoas/picfit/constants" diff --git a/engine/engine.go b/engine/engine.go index 4caf2792..50d77a48 100644 --- a/engine/engine.go +++ b/engine/engine.go @@ -3,6 +3,7 @@ package engine import ( "context" "fmt" + "github.com/pkg/errors" "github.com/thoas/picfit/engine/backend" "github.com/thoas/picfit/engine/config" "github.com/thoas/picfit/image" @@ -124,7 +125,7 @@ func (e Engine) Transform(ctx context.Context, output *image.ImageFile, operatio output.Source = processed break } - if err != backend.MethodNotImplementedError { + if !errors.Is(err, backend.MethodNotImplementedError) { return nil, err } } @@ -152,7 +153,9 @@ func operate(ctx context.Context, b backend.Backend, img *image.ImageFile, opera return b.Fit(ctx, img, options) case Flat: return b.Flat(ctx, img, options) + case Effect: + return b.Effect(ctx, img, options) default: - return nil, fmt.Errorf("Operation not found for %s", operation) + return nil, fmt.Errorf("operation not found for %s", operation) } } diff --git a/engine/operations.go b/engine/operations.go index 3eb786f0..31bcca52 100644 --- a/engine/operations.go +++ b/engine/operations.go @@ -9,6 +9,7 @@ func (o Operation) String() string { } const ( + Effect = Operation("effect") Fit = Operation("fit") Flat = Operation("flat") Flip = Operation("flip") @@ -19,6 +20,7 @@ const ( ) var Operations = map[string]Operation{ + Effect.String(): Effect, Fit.String(): Fit, Flat.String(): Flat, Flip.String(): Flip, diff --git a/go.mod b/go.mod index 94b92c55..9a71999b 100644 --- a/go.mod +++ b/go.mod @@ -4,11 +4,11 @@ require ( github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/cstockton/go-conv v0.0.0-20170524002450-66a2b2ba36e1 github.com/davecgh/go-spew v1.1.1 - github.com/disintegration/imaging v1.6.2 github.com/getsentry/sentry-go v0.19.0 github.com/gin-gonic/contrib v0.0.0-20201101042839-6a891bf89f19 github.com/gin-gonic/gin v1.9.1 github.com/go-playground/validator/v10 v10.15.4 // indirect + github.com/go-spectest/imaging v1.0.6 github.com/k0kubun/pp v3.0.1+incompatible github.com/lucasb-eyer/go-colorful v0.0.0-20180709185858-c7842319cf3a github.com/mattn/go-isatty v0.0.19 // indirect @@ -25,16 +25,17 @@ require ( github.com/ulule/gokvstores v0.1.1-0.20221229151109-3bd12fb72ebe github.com/ulule/gostorages v0.2.5-0.20230920134537-c63293fd790c github.com/urfave/cli v1.22.10 - golang.org/x/image v0.10.0 + golang.org/x/image v0.13.0 google.golang.org/protobuf v1.31.0 // indirect gopkg.in/DATA-DOG/go-sqlmock.v1 v1.3.0 // indirect gopkg.in/fukata/golang-stats-api-handler.v1 v1.0.0 ) require ( + github.com/go-spectest/imaging v1.0.6 github.com/google/uuid v1.3.0 github.com/prometheus/client_golang v1.14.0 - golang.org/x/sync v0.1.0 + golang.org/x/sync v0.4.0 ) require ( diff --git a/go.sum b/go.sum index 8318c9b3..852f05c4 100644 --- a/go.sum +++ b/go.sum @@ -151,6 +151,8 @@ github.com/go-playground/validator/v10 v10.15.4 h1:zMXza4EpOdooxPel5xDqXEdXG5r+W github.com/go-playground/validator/v10 v10.15.4/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= +github.com/go-spectest/imaging v1.0.6 h1:b7qzcXLs1p9uZMjAKO+oe+a6EGo5JbwPDkcWafmTnX8= +github.com/go-spectest/imaging v1.0.6/go.mod h1:/AyhBt5HEFGDPy2AxP+hcV5NH8QQpftJP9g3yGOVtvs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= @@ -507,6 +509,8 @@ golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+o golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.10.0 h1:gXjUUtwtx5yOE0VKWq1CH4IJAClq4UGgUA3i+rpON9M= golang.org/x/image v0.10.0/go.mod h1:jtrku+n79PfroUbvDdeUWMAI+heR786BofxrbiSF+J0= +golang.org/x/image v0.13.0 h1:3cge/F/QTkNLauhf2QoE9zp+7sr+ZcL4HnoZmdwg9sg= +golang.org/x/image v0.13.0/go.mod h1:6mmbMOeV28HuMTgA6OSRkdXKYw/t5W9Uwn2Yv1r3Yxk= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -610,6 +614,8 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= +golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= diff --git a/parameters.go b/parameters.go index 84c4211e..a15911e0 100644 --- a/parameters.go +++ b/parameters.go @@ -3,10 +3,11 @@ package picfit import ( "context" "fmt" + "slices" "strconv" "strings" - "github.com/disintegration/imaging" + "github.com/go-spectest/imaging" "github.com/pkg/errors" "github.com/ulule/gostorages" @@ -197,17 +198,10 @@ func (p Processor) newBackendOptionsFromParameters(operation engine.Operation, q return nil, fmt.Errorf("Parameter \"pos\" not found in query string") } - stick, _ := qs["stick"].(string) - if stick != "" { - var exists bool - for i := range constants.StickPositions { - if stick == constants.StickPositions[i] { - exists = true - break - } - } - if !exists { - return nil, fmt.Errorf("Parameter \"stick\" has wrong value. Available values are: %v", constants.StickPositions) + stick, ok := qs["stick"].(string) + if ok { + if !slices.Contains(constants.StickPositions, stick) { + return nil, fmt.Errorf("parameter \"stick\" has wrong value. Available values are: %v", constants.StickPositions) } } @@ -241,14 +235,20 @@ func (p Processor) newBackendOptionsFromParameters(operation engine.Operation, q } } + filter, ok := qs["filter"].(string) + if ok && !slices.Contains(constants.Filters, filter) { + return nil, fmt.Errorf("parameter \"filter\" has wrong value. Available values are: %v", constants.Filters) + } + return &backend.Options{ - Width: width, + Color: color, + Degree: degree, + Filter: filter, Height: height, - Upscale: upscale, Position: position, - Stick: stick, Quality: quality, - Degree: degree, - Color: color, + Stick: stick, + Upscale: upscale, + Width: width, }, nil } diff --git a/processor_test.go b/processor_test.go index 109d49aa..9c71e05e 100644 --- a/processor_test.go +++ b/processor_test.go @@ -20,7 +20,7 @@ import ( "github.com/cstockton/go-conv" - "github.com/disintegration/imaging" + "github.com/go-spectest/imaging" "github.com/stretchr/testify/assert"