Skip to content

Commit

Permalink
feat(#315, #309): autorotate / gracefully fallback failed image type …
Browse files Browse the repository at this point in the history
…encoding
  • Loading branch information
h2non committed Aug 4, 2020
1 parent c68335d commit 3fc9db7
Show file tree
Hide file tree
Showing 9 changed files with 70 additions and 8 deletions.
3 changes: 1 addition & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ services:
dist: focal

go:
- "1.12"
- "1.13"
# - "1.14"
- "1.14"

env:
global:
Expand Down
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
ARG GOLANG_VERSION=1.13
ARG GOLANG_VERSION=1.14
FROM golang:${GOLANG_VERSION} as builder

ARG IMAGINARY_VERSION=dev
ARG LIBVIPS_VERSION=8.9.2
ARG GOLANGCILINT_VERSION=1.23.3
ARG GOLANGCILINT_VERSION=1.29.0

# Installs libvips + required libraries
RUN DEBIAN_FRONTEND=noninteractive \
Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ To get started, take a look the [installation](#installation) steps, [usage](#co
- Crop
- SmartCrop (based on [libvips built-in algorithm](https://github.com/jcupitt/libvips/blob/master/libvips/conversion/smartcrop.c))
- Rotate (with auto-rotate based on EXIF orientation)
- AutoRotate with further image transformations (based on EXIF metadata orientation)
- Flip (with auto-flip based on EXIF metadata)
- Flop
- Zoom
Expand Down Expand Up @@ -883,6 +884,14 @@ The width and height specify a maximum bounding box for the image.
#### GET | POST /rotate
Accepts: `image/*, multipart/form-data`. Content-Type: `image/*`


#### GET | POST /autorotate
Accepts: `image/*, multipart/form-data`. Content-Type: `image/*`

Automatically rotate the image with no further image transformations based on EXIF orientation metadata.

Returns a new image with the same size and format as the input image.

##### Allowed params

- rotate `int` `required`
Expand Down Expand Up @@ -1036,6 +1045,7 @@ Self-documented JSON operation schema:
- **enlarge** - Same as [`/enlarge`](#get--post-enlarge) endpoint.
- **extract** - Same as [`/extract`](#get--post-extract) endpoint.
- **rotate** - Same as [`/rotate`](#get--post-rotate) endpoint.
- **autorotate** - Same as [`/autorotate`](#get--post-autorotate) endpoint.
- **flip** - Same as [`/flip`](#get--post-flip) endpoint.
- **flop** - Same as [`/flop`](#get--post-flop) endpoint.
- **thumbnail** - Same as [`/thumbnail`](#get--post-thumbnail) endpoint.
Expand Down
1 change: 1 addition & 0 deletions controllers.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ func formController(w http.ResponseWriter, r *http.Request) {
{"Extract", "extract", "top=100&left=100&areawidth=300&areaheight=150"},
{"Enlarge", "enlarge", "width=1440&height=900&quality=95"},
{"Rotate", "rotate", "rotate=180"},
{"AutoRotate", "autorotate", "quality=90"},
{"Flip", "flip", ""},
{"Flop", "flop", ""},
{"Thumbnail", "thumbnail", "width=100"},
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ require (
github.com/garyburd/redigo v1.6.0 // indirect
github.com/hashicorp/golang-lru v0.0.0-20160813221303-0a025b7e63ad // indirect
github.com/rs/cors v0.0.0-20170727213201-7af7a1e09ba3
github.com/h2non/bimg v1.1.2
github.com/h2non/bimg v1.1.4
github.com/h2non/filetype v1.1.0
gopkg.in/throttled/throttled.v2 v2.0.3
)
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
github.com/garyburd/redigo v1.6.0/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
github.com/h2non/bimg v1.1.2 h1:J75W2eM5FT0KjcwsL2aiy1Ilu0Xy0ENb0sU+HHUJAvw=
github.com/h2non/bimg v1.1.2/go.mod h1:R3+UiYwkK4rQl6KVFTOFJHitgLbZXBZNFh2cv3AEbp8=
github.com/h2non/bimg v1.1.4 h1:6qf7qDo3d9axbNUOcSoQmzleBCMTcQ1PwF3FgGhX4O0=
github.com/h2non/bimg v1.1.4/go.mod h1:R3+UiYwkK4rQl6KVFTOFJHitgLbZXBZNFh2cv3AEbp8=
github.com/h2non/filetype v1.1.0 h1:Or/gjocJrJRNK/Cri/TDEKFjAR+cfG6eK65NGYB6gBA=
github.com/h2non/filetype v1.1.0/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
github.com/hashicorp/golang-lru v0.0.0-20160813221303-0a025b7e63ad h1:eMxs9EL0PvIGS9TTtxg4R+JxuPGav82J8rA+GFnY7po=
Expand Down
41 changes: 38 additions & 3 deletions image.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"io"
"strings"
"io/ioutil"
"math"
"net/http"
Expand Down Expand Up @@ -221,6 +222,31 @@ func Rotate(buf []byte, o ImageOptions) (Image, error) {
return Process(buf, opts)
}

func AutoRotate(buf []byte, o ImageOptions) (out Image, err error) {
defer func() {
if r := recover(); r != nil {
switch value := r.(type) {
case error:
err = value
case string:
err = errors.New(value)
default:
err = errors.New("libvips internal error")
}
out = Image{}
}
}()

// Resize image via bimg
ibuf, err := bimg.NewImage(buf).AutoRotate()
if err != nil {
return Image{}, err
}

mime := GetImageMimeType(bimg.DetermineImageType(ibuf))
return Image{Body: ibuf, Mime: mime}, nil
}

func Flip(buf []byte, o ImageOptions) (Image, error) {
opts := BimgOptions(o)
opts.Flip = true
Expand Down Expand Up @@ -406,11 +432,20 @@ func Process(buf []byte, opts bimg.Options) (out Image, err error) {
}
}()

buf, err = bimg.Resize(buf, opts)
// Resize image via bimg
ibuf, err := bimg.Resize(buf, opts)

// Handle specific type encode errors gracefully
if err != nil && strings.Contains(err.Error(), "encode") && (opts.Type == bimg.WEBP || opts.Type == bimg.HEIF) {
// Always fallback to JPEG
opts.Type = bimg.JPEG
ibuf, err = bimg.Resize(buf, opts)
}

if err != nil {
return Image{}, err
}

mime := GetImageMimeType(bimg.DetermineImageType(buf))
return Image{Body: buf, Mime: mime}, nil
mime := GetImageMimeType(bimg.DetermineImageType(ibuf))
return Image{Body: ibuf, Mime: mime}, nil
}
14 changes: 14 additions & 0 deletions image_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,20 @@ func TestImageFit(t *testing.T) {
}
}

func TestImageAutoRotate(t *testing.T) {
buf, _ := ioutil.ReadAll(readFile("imaginary.jpg"))
img, err := AutoRotate(buf, ImageOptions{})
if err != nil {
t.Errorf("Cannot process image: %s", err)
}
if img.Mime != "image/jpeg" {
t.Error("Invalid image MIME type")
}
if assertSize(img.Body, 550, 740) != nil {
t.Errorf("Invalid image size, expected: %dx%d", 550, 740)
}
}

func TestImagePipelineOperations(t *testing.T) {
width, height := 300, 260

Expand Down
1 change: 1 addition & 0 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ func NewServerMux(o ServerOptions) http.Handler {
mux.Handle(join(o, "/crop"), image(Crop))
mux.Handle(join(o, "/smartcrop"), image(SmartCrop))
mux.Handle(join(o, "/rotate"), image(Rotate))
mux.Handle(join(o, "/autorotate"), image(AutoRotate))
mux.Handle(join(o, "/flip"), image(Flip))
mux.Handle(join(o, "/flop"), image(Flop))
mux.Handle(join(o, "/thumbnail"), image(Thumbnail))
Expand Down

0 comments on commit 3fc9db7

Please sign in to comment.