diff --git a/config/example.json b/config/example.json index 03a5161..d688687 100644 --- a/config/example.json +++ b/config/example.json @@ -12,6 +12,13 @@ "height": 720, "upscale": false } + }, + { + "name": "rotate image", + "type": 0, + "options": { + "angle": 90.0 + } } ], "output": { diff --git a/pipelines/executable_step.go b/pipelines/executable_step.go index dd2da7d..878f8c2 100644 --- a/pipelines/executable_step.go +++ b/pipelines/executable_step.go @@ -24,3 +24,17 @@ func (s ResizeImageStep) Execute(src image.Image) (image.Image, error) { src = imaging.Resize(src, s.Options.Width, s.Options.Height, imaging.Linear) return src, nil } + +// Rotate image + +type RotateImageStep struct { + Step + Options struct { + Angle float64 `json:"angle"` + } `json:"options"` +} + +func (s RotateImageStep) Execute(src image.Image) (image.Image, error) { + src = imaging.Rotate(src, s.Options.Angle, image.Black) + return src, nil +} diff --git a/pipelines/pipeline_test.go b/pipelines/pipeline_test.go index 87f00fc..919f2d6 100644 --- a/pipelines/pipeline_test.go +++ b/pipelines/pipeline_test.go @@ -9,6 +9,8 @@ import ( "github.com/stretchr/testify/assert" ) +// pipeline deserialization + func TestImagePipelineDeserialization(t *testing.T) { const Payload string = `{ "name": "example pipeline", @@ -61,6 +63,8 @@ func TestVideoPipelineDeserialization(t *testing.T) { }) } +// image pipeline steps + func TestResizeImage(t *testing.T) { const Payload string = `{ "name": "example pipeline", @@ -100,6 +104,45 @@ func TestResizeImage(t *testing.T) { }) } +func TestRotateImage(t *testing.T) { + const Payload string = `{ + "name": "example pipeline", + "type": 1, + "removeMetadata": false, + "steps": [ + { + "name": "rotate image", + "type": 1, + "options": { + "angle": 90.0 + } + } + ] + }` + const Bucket string = "pipeline_test_03" + + t.Run("Image rotating is successful", func(t *testing.T) { + wd, _ := os.Getwd() + pipe := DeserializePipelines([][]byte{[]byte(Payload)})[0] + + storageProvider := storage.GetFileSystemStorageProvider("test", "..") + + _, err := storageProvider.StoreExisting(Bucket, "source.jpg", filepath.Join(wd, "../tests/files/900x900.jpg")) + assert.Nil(t, err, "Test file should be readable") + assert.FileExists(t, storageProvider.GetPath(Bucket, "source.jpg")) + + dest, err := pipe.Run("source.jpg", Bucket, storageProvider) + assert.Nil(t, err) + assert.FileExists(t, storageProvider.GetPath(Bucket, dest)) + + // clean up + os.Remove(storageProvider.GetPath(Bucket, "source.jpg")) + os.Remove(storageProvider.GetPath(Bucket, dest)) + }) +} + +// output options + func TestEncodeImageWithJpegQuality(t *testing.T) { const Payload string = `{ "name": "example pipeline", @@ -122,7 +165,7 @@ func TestEncodeImageWithJpegQuality(t *testing.T) { }` const Bucket string = "pipeline_test_02" - t.Run("Image resizing is successful", func(t *testing.T) { + t.Run("Image encoding with jpeg quality is successful", func(t *testing.T) { wd, _ := os.Getwd() pipe := DeserializePipelines([][]byte{[]byte(Payload)})[0] diff --git a/pipelines/step.go b/pipelines/step.go index 6e2b0f1..8aae1af 100644 --- a/pipelines/step.go +++ b/pipelines/step.go @@ -9,6 +9,7 @@ type StepType int const ( TypeResizeImageStep StepType = iota + TypeRotateImageStep ) type Step struct { @@ -19,12 +20,20 @@ type Step struct { func (s Step) GetExecutable() (IExecutableStep, error) { switch s.GetType() { + case TypeResizeImageStep: step := ResizeImageStep{} if err := json.Unmarshal(s.Options, &step.Options); err != nil { return nil, err } return step, nil + + case TypeRotateImageStep: + step := RotateImageStep{} + if err := json.Unmarshal(s.Options, &step.Options); err != nil { + return nil, err + } + return step, nil } return nil, errors.New("invalid type")