diff --git a/vendor/manifest b/vendor/manifest
index b5725ca36..6e6e24640 100644
--- a/vendor/manifest
+++ b/vendor/manifest
@@ -179,6 +179,12 @@
"revision": "61e43dc76f7ee59a82bdf3d71033dc12bea4c77d",
"branch": "master"
},
+ {
+ "importpath": "github.com/tj/go-debug",
+ "repository": "https://github.com/tj/go-debug",
+ "revision": "ff4a55a20a86994118644bbddc6a216da193cc13",
+ "branch": "master"
+ },
{
"importpath": "golang.org/x/crypto/bcrypt",
"repository": "https://go.googlesource.com/crypto",
@@ -225,6 +231,12 @@
"revision": "bfee1239d796830ca346767650cce5ba90d58c57",
"branch": "master"
},
+ {
+ "importpath": "gopkg.in/h2non/bimg.v1",
+ "repository": "https://gopkg.in/h2non/bimg.v1",
+ "revision": "45f8993550e71ee7b8001d40c681c6c9fa822357",
+ "branch": "master"
+ },
{
"importpath": "gopkg.in/yaml.v2",
"repository": "https://gopkg.in/yaml.v2",
diff --git a/vendor/src/github.com/tj/go-debug/History.md b/vendor/src/github.com/tj/go-debug/History.md
new file mode 100644
index 000000000..318ceb4d3
--- /dev/null
+++ b/vendor/src/github.com/tj/go-debug/History.md
@@ -0,0 +1,21 @@
+
+v2.0.0 / 2014-10-22
+==================
+
+ * remove live toggling feature. Closes #10
+
+1.1.1 / 2014-07-07
+==================
+
+ * fix: dispose socket. Closes #1
+
+1.1.0 / 2014-06-29
+==================
+
+ * add unix domain socket live debugging support
+ * add support for enabling/disabling at runtime
+
+0.1.0 / 2014-05-24
+==================
+
+ * add global and debug relative deltas
diff --git a/vendor/src/github.com/tj/go-debug/Makefile b/vendor/src/github.com/tj/go-debug/Makefile
new file mode 100644
index 000000000..16bc6d36f
--- /dev/null
+++ b/vendor/src/github.com/tj/go-debug/Makefile
@@ -0,0 +1,8 @@
+
+test:
+ @go test
+
+bench:
+ @go test -bench=.
+
+.PHONY: bench test
\ No newline at end of file
diff --git a/vendor/src/github.com/tj/go-debug/Readme.md b/vendor/src/github.com/tj/go-debug/Readme.md
new file mode 100644
index 000000000..6560af8a7
--- /dev/null
+++ b/vendor/src/github.com/tj/go-debug/Readme.md
@@ -0,0 +1,75 @@
+
+# go-debug
+
+ Conditional debug logging for Go libraries.
+
+ View the [docs](http://godoc.org/github.com/tj/go-debug).
+
+## Installation
+
+```
+$ go get github.com/tj/go-debug
+```
+
+## Example
+
+```go
+package main
+
+import . "github.com/tj/go-debug"
+import "time"
+
+var debug = Debug("single")
+
+func main() {
+ for {
+ debug("sending mail")
+ debug("send email to %s", "tobi@segment.io")
+ debug("send email to %s", "loki@segment.io")
+ debug("send email to %s", "jane@segment.io")
+ time.Sleep(500 * time.Millisecond)
+ }
+}
+```
+
+If you run the program with the `DEBUG=*` environment variable you will see:
+
+```
+15:58:15.115 34us 33us single - sending mail
+15:58:15.116 3us 3us single - send email to tobi@segment.io
+15:58:15.116 1us 1us single - send email to loki@segment.io
+15:58:15.116 1us 1us single - send email to jane@segment.io
+15:58:15.620 504ms 504ms single - sending mail
+15:58:15.620 6us 6us single - send email to tobi@segment.io
+15:58:15.620 4us 4us single - send email to loki@segment.io
+15:58:15.620 4us 4us single - send email to jane@segment.io
+15:58:16.123 503ms 503ms single - sending mail
+15:58:16.123 7us 7us single - send email to tobi@segment.io
+15:58:16.123 4us 4us single - send email to loki@segment.io
+15:58:16.123 4us 4us single - send email to jane@segment.io
+15:58:16.625 501ms 501ms single - sending mail
+15:58:16.625 4us 4us single - send email to tobi@segment.io
+15:58:16.625 4us 4us single - send email to loki@segment.io
+15:58:16.625 5us 5us single - send email to jane@segment.io
+```
+
+A timestamp and two deltas are displayed. The timestamp consists of hour, minute, second and microseconds. The left-most delta is relative to the previous debug call of any name, followed by a delta specific to that debug function. These may be useful to identify timing issues and potential bottlenecks.
+
+## The DEBUG environment variable
+
+ Executables often support `--verbose` flags for conditional logging, however
+ libraries typically either require altering your code to enable logging,
+ or simply omit logging all together. go-debug allows conditional logging
+ to be enabled via the __DEBUG__ environment variable, where one or more
+ patterns may be specified.
+
+ For example suppose your application has several models and you want
+ to output logs for users only, you might use `DEBUG=models:user`. In contrast
+ if you wanted to see what all database activity was you might use `DEBUG=models:*`,
+ or if you're love being swamped with logs: `DEBUG=*`. You may also specify a list of names delimited by a comma, for example `DEBUG=mongo,redis:*`.
+
+ The name given _should_ be the package name, however you can use whatever you like.
+
+# License
+
+MIT
\ No newline at end of file
diff --git a/vendor/src/github.com/tj/go-debug/debug.go b/vendor/src/github.com/tj/go-debug/debug.go
new file mode 100644
index 000000000..016ca4698
--- /dev/null
+++ b/vendor/src/github.com/tj/go-debug/debug.go
@@ -0,0 +1,128 @@
+package debug
+
+import (
+ "fmt"
+ "io"
+ "math/rand"
+ "os"
+ "regexp"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+)
+
+var (
+ writer io.Writer = os.Stderr
+ reg *regexp.Regexp
+ m sync.Mutex
+ enabled = false
+)
+
+// Debugger function.
+type DebugFunction func(string, ...interface{})
+
+// Terminal colors used at random.
+var colors []string = []string{
+ "31",
+ "32",
+ "33",
+ "34",
+ "35",
+ "36",
+}
+
+// Initialize with DEBUG environment variable.
+func init() {
+ env := os.Getenv("DEBUG")
+
+ if "" != env {
+ Enable(env)
+ }
+}
+
+// SetWriter replaces the default of os.Stderr with `w`.
+func SetWriter(w io.Writer) {
+ m.Lock()
+ defer m.Unlock()
+ writer = w
+}
+
+// Disable all pattern matching. This function is thread-safe.
+func Disable() {
+ m.Lock()
+ defer m.Unlock()
+ enabled = false
+}
+
+// Enable the given debug `pattern`. Patterns take a glob-like form,
+// for example if you wanted to enable everything, just use "*", or
+// if you had a library named mongodb you could use "mongodb:connection",
+// or "mongodb:*". Multiple matches can be made with a comma, for
+// example "mongo*,redis*".
+//
+// This function is thread-safe.
+func Enable(pattern string) {
+ m.Lock()
+ defer m.Unlock()
+ pattern = regexp.QuoteMeta(pattern)
+ pattern = strings.Replace(pattern, "\\*", ".*?", -1)
+ pattern = strings.Replace(pattern, ",", "|", -1)
+ pattern = "^(" + pattern + ")$"
+ reg = regexp.MustCompile(pattern)
+ enabled = true
+}
+
+// Debug creates a debug function for `name` which you call
+// with printf-style arguments in your application or library.
+func Debug(name string) DebugFunction {
+ prevGlobal := time.Now()
+ color := colors[rand.Intn(len(colors))]
+ prev := time.Now()
+
+ return func(format string, args ...interface{}) {
+ if !enabled {
+ return
+ }
+
+ if !reg.MatchString(name) {
+ return
+ }
+
+ d := deltas(prevGlobal, prev, color)
+ fmt.Fprintf(writer, d+" \033["+color+"m"+name+"\033[0m - "+format+"\n", args...)
+ prevGlobal = time.Now()
+ prev = time.Now()
+ }
+}
+
+// Return formatting for deltas.
+func deltas(prevGlobal, prev time.Time, color string) string {
+ now := time.Now()
+ global := now.Sub(prevGlobal).Nanoseconds()
+ delta := now.Sub(prev).Nanoseconds()
+ ts := now.UTC().Format("15:04:05.000")
+ deltas := fmt.Sprintf("%s %-6s \033["+color+"m%-6s", ts, humanizeNano(global), humanizeNano(delta))
+ return deltas
+}
+
+// Humanize nanoseconds to a string.
+func humanizeNano(n int64) string {
+ var suffix string
+
+ switch {
+ case n > 1e9:
+ n /= 1e9
+ suffix = "s"
+ case n > 1e6:
+ n /= 1e6
+ suffix = "ms"
+ case n > 1e3:
+ n /= 1e3
+ suffix = "us"
+ default:
+ suffix = "ns"
+ }
+
+ return strconv.Itoa(int(n)) + suffix
+}
diff --git a/vendor/src/github.com/tj/go-debug/debug_test.go b/vendor/src/github.com/tj/go-debug/debug_test.go
new file mode 100644
index 000000000..7ce2764c1
--- /dev/null
+++ b/vendor/src/github.com/tj/go-debug/debug_test.go
@@ -0,0 +1,152 @@
+package debug
+
+import "testing"
+import "strings"
+import "bytes"
+import "time"
+
+func assertContains(t *testing.T, str, substr string) {
+ if !strings.Contains(str, substr) {
+ t.Fatalf("expected %q to contain %q", str, substr)
+ }
+}
+
+func assertNotContains(t *testing.T, str, substr string) {
+ if strings.Contains(str, substr) {
+ t.Fatalf("expected %q to not contain %q", str, substr)
+ }
+}
+
+func TestDefault(t *testing.T) {
+ var b []byte
+ buf := bytes.NewBuffer(b)
+ SetWriter(buf)
+
+ debug := Debug("foo")
+ debug("something")
+ debug("here")
+ debug("whoop")
+
+ if buf.Len() != 0 {
+ t.Fatalf("buffer should be empty")
+ }
+}
+
+func TestEnable(t *testing.T) {
+ var b []byte
+ buf := bytes.NewBuffer(b)
+ SetWriter(buf)
+
+ Enable("foo")
+
+ debug := Debug("foo")
+ debug("something")
+ debug("here")
+ debug("whoop")
+
+ if buf.Len() == 0 {
+ t.Fatalf("buffer should have output")
+ }
+
+ str := string(buf.Bytes())
+ assertContains(t, str, "something")
+ assertContains(t, str, "here")
+ assertContains(t, str, "whoop")
+}
+
+func TestMultipleOneEnabled(t *testing.T) {
+ var b []byte
+ buf := bytes.NewBuffer(b)
+ SetWriter(buf)
+
+ Enable("foo")
+
+ foo := Debug("foo")
+ foo("foo")
+
+ bar := Debug("bar")
+ bar("bar")
+
+ if buf.Len() == 0 {
+ t.Fatalf("buffer should have output")
+ }
+
+ str := string(buf.Bytes())
+ assertContains(t, str, "foo")
+ assertNotContains(t, str, "bar")
+}
+
+func TestMultipleEnabled(t *testing.T) {
+ var b []byte
+ buf := bytes.NewBuffer(b)
+ SetWriter(buf)
+
+ Enable("foo,bar")
+
+ foo := Debug("foo")
+ foo("foo")
+
+ bar := Debug("bar")
+ bar("bar")
+
+ if buf.Len() == 0 {
+ t.Fatalf("buffer should have output")
+ }
+
+ str := string(buf.Bytes())
+ assertContains(t, str, "foo")
+ assertContains(t, str, "bar")
+}
+
+func TestEnableDisable(t *testing.T) {
+ var b []byte
+ buf := bytes.NewBuffer(b)
+ SetWriter(buf)
+
+ Enable("foo,bar")
+ Disable()
+
+ foo := Debug("foo")
+ foo("foo")
+
+ bar := Debug("bar")
+ bar("bar")
+
+ if buf.Len() != 0 {
+ t.Fatalf("buffer should not have output")
+ }
+}
+
+func ExampleEnable() {
+ Enable("mongo:connection")
+ Enable("mongo:*")
+ Enable("foo,bar,baz")
+ Enable("*")
+}
+
+func ExampleDebug() {
+ var debug = Debug("single")
+
+ for {
+ debug("sending mail")
+ debug("send email to %s", "tobi@segment.io")
+ debug("send email to %s", "loki@segment.io")
+ debug("send email to %s", "jane@segment.io")
+ time.Sleep(500 * time.Millisecond)
+ }
+}
+
+func BenchmarkDisabled(b *testing.B) {
+ debug := Debug("something")
+ for i := 0; i < b.N; i++ {
+ debug("stuff")
+ }
+}
+
+func BenchmarkNonMatch(b *testing.B) {
+ debug := Debug("something")
+ Enable("nonmatch")
+ for i := 0; i < b.N; i++ {
+ debug("stuff")
+ }
+}
diff --git a/vendor/src/github.com/tj/go-debug/example/multiple.go b/vendor/src/github.com/tj/go-debug/example/multiple.go
new file mode 100644
index 000000000..81c330807
--- /dev/null
+++ b/vendor/src/github.com/tj/go-debug/example/multiple.go
@@ -0,0 +1,25 @@
+package main
+
+import . "github.com/visionmedia/go-debug"
+import "time"
+
+var a = Debug("multiple:a")
+var b = Debug("multiple:b")
+var c = Debug("multiple:c")
+
+func work(debug DebugFunction, delay time.Duration) {
+ for {
+ debug("doing stuff")
+ time.Sleep(delay)
+ }
+}
+
+func main() {
+ q := make(chan bool)
+
+ go work(a, 1000*time.Millisecond)
+ go work(b, 250*time.Millisecond)
+ go work(c, 100*time.Millisecond)
+
+ <-q
+}
diff --git a/vendor/src/github.com/tj/go-debug/example/single.go b/vendor/src/github.com/tj/go-debug/example/single.go
new file mode 100644
index 000000000..fccfe33f2
--- /dev/null
+++ b/vendor/src/github.com/tj/go-debug/example/single.go
@@ -0,0 +1,16 @@
+package main
+
+import . "github.com/visionmedia/go-debug"
+import "time"
+
+var debug = Debug("single")
+
+func main() {
+ for {
+ debug("sending mail")
+ debug("send email to %s", "tobi@segment.io")
+ debug("send email to %s", "loki@segment.io")
+ debug("send email to %s", "jane@segment.io")
+ time.Sleep(500 * time.Millisecond)
+ }
+}
diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/History.md b/vendor/src/gopkg.in/h2non/bimg.v1/History.md
new file mode 100644
index 000000000..57cdfc541
--- /dev/null
+++ b/vendor/src/gopkg.in/h2non/bimg.v1/History.md
@@ -0,0 +1,85 @@
+
+## v1.0.9 / 2017-05-25
+
+ * Merge pull request #156 from Dynom/SmartCropToGravity
+ * Adding a test, verifying both ways of enabling SmartCrop work
+ * Merge pull request #149 from waldophotos/master
+ * Replacing SmartCrop with a Gravity option
+ * refactor(docs): v8.4
+ * Change for older LIBVIPS versions. `vips_bandjoin_const1` is added in libvips 8.2.
+ * Second try, watermarking memory issue fix
+
+## v1.0.8 / 2017-05-18
+
+ * Merge pull request #145 from greut/smartcrop
+ * Merge pull request #155 from greut/libvips8.5.5
+ * Update libvips to 8.5.5.
+ * Adding basic smartcrop support.
+ * Merge pull request #153 from abracadaber/master
+ * Added Linux Mint 17.3+ distro names
+ * feat(docs): add new maintainer notice (thanks to @kirillDanshin)
+ * Merge pull request #152 from greut/libvips85
+ * Download latest version of libvips from github.
+ * Merge pull request #147 from h2non/revert-143-master
+ * Revert "Fix for memory issue when watermarking images"
+ * Merge pull request #146 from greut/minor-major
+ * Merge pull request #143 from waldophotos/master
+ * Merge pull request #144 from greut/go18
+ * Fix tests where minor/major were mixed up
+ * Enabled go 1.8 builds.
+ * Fix the unref of images, when image isn't transparent
+ * Fix for memory issue when watermarking images
+ * feat(docs): add maintainers sections
+ * Merge pull request #132 from jaume-pinyol/WATERMARK_SUPPORT
+ * Add support for image watermarks
+ * Merge pull request #131 from greut/versions
+ * Running tests on more specific versions.
+ * refactor(preinstall.sh): remove deprecation notice
+ * Update preinstall.sh
+ * fix(requirements): required libvips 7.42
+ * fix(History): typo
+ * chore(History): add breaking change note
+
+## v1.0.7 / 13-01-2017
+
+- fix(#128): crop image calculation for missing width or height axis.
+- feat: add TIFF save output format (**note**: this introduces a minor interface breaking change in `bimg.IsImageTypeSupportedByVips` auxiliary function).
+
+## v1.0.6 / 12-11-2016
+
+- feat(#118): handle 16-bit PNGs.
+- feat(#119): adds JPEG2000 file for the type tests.
+- feat(#121): test bimg against multiple libvips versions.
+
+## v1.0.5 / 01-10-2016
+
+- feat(#92): support Extend param with optional background.
+- fix(#106): allow image area extraction without explicit x/y axis.
+- feat(api): add Extend type with `libvips` enum alias.
+
+## v1.0.4 / 29-09-2016
+
+- fix(#111): safe check of magick image type support.
+
+## v1.0.3 / 28-09-2016
+
+- fix(#95): better image type inference and support check.
+- fix(background): pass proper background RGB color for PNG image conversion.
+- feat(types): validate supported image types by current `libvips` compilation.
+- feat(types): consistent SVG image checking.
+- feat(api): add public functions `VipsIsTypeSupported()`, `IsImageTypeSupportedByVips()` and `IsSVGImage()`.
+
+## v1.0.2 / 27-09-2016
+
+- feat(#95): support GIF, SVG and PDF formats.
+- fix(#108): auto-width and height calculations now round instead of floor.
+
+## v1.0.1 / 22-06-2016
+
+- fix(#90): Do not not dereference the original image a second time.
+
+## v1.0.0 / 21-04-2016
+
+- refactor(api): breaking changes: normalize public members to follow Go naming idioms.
+- feat(version): bump to major version. API contract won't be compromised in `v1`.
+- feat(docs): add missing inline godoc documentation.
diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/LICENSE b/vendor/src/gopkg.in/h2non/bimg.v1/LICENSE
new file mode 100644
index 000000000..b28d546c6
--- /dev/null
+++ b/vendor/src/gopkg.in/h2non/bimg.v1/LICENSE
@@ -0,0 +1,24 @@
+The MIT License
+
+Copyright (c) Tomas Aparicio and contributors
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/README.md b/vendor/src/gopkg.in/h2non/bimg.v1/README.md
new file mode 100644
index 000000000..022f6540c
--- /dev/null
+++ b/vendor/src/gopkg.in/h2non/bimg.v1/README.md
@@ -0,0 +1,347 @@
+# bimg [](https://travis-ci.org/h2non/bimg) [](https://godoc.org/github.com/h2non/bimg) [](http://goreportcard.com/report/h2non/bimg) [](https://coveralls.io/github/h2non/bimg?branch=master) 
+
+Small [Go](http://golang.org) package for fast high-level image processing using [libvips](https://github.com/jcupitt/libvips) via C bindings, providing a simple, elegant and fluent [programmatic API](#examples).
+
+bimg was designed to be a small and efficient library supporting a common set of [image operations](#supported-image-operations) such as crop, resize, rotate, zoom or watermark. It can read JPEG, PNG, WEBP natively, and optionally TIFF, PDF, GIF and SVG formats if `libvips@8.3+` is compiled with proper library bindings.
+
+bimg is able to output images as JPEG, PNG and WEBP formats, including transparent conversion across them.
+
+bimg uses internally libvips, a powerful library written in C for image processing which requires a [low memory footprint](http://www.vips.ecs.soton.ac.uk/index.php?title=Speed_and_Memory_Use)
+and it's typically 4x faster than using the quickest ImageMagick and GraphicsMagick settings or Go native `image` package, and in some cases it's even 8x faster processing JPEG images.
+
+If you're looking for an HTTP based image processing solution, see [imaginary](https://github.com/h2non/imaginary).
+
+bimg was heavily inspired in [sharp](https://github.com/lovell/sharp), its homologous package built for [node.js](http://nodejs.org). bimg is used in production environments processing thousands of images per day.
+
+**v1 notice**: `bimg` introduces some minor breaking changes in `v1` release.
+If you're using `gopkg.in`, you can still rely in the `v0` without worrying about API breaking changes.
+
+`bimg` is currently maintained by [Kirill Danshin](https://github.com/kirillDanshin).
+
+## Contents
+
+- [Supported image operations](#supported-image-operations)
+- [Prerequisites](#prerequisites)
+- [Installation](#installation)
+- [Performance](#performance)
+- [Benchmark](#benchmark)
+- [Examples](#examples)
+- [Debugging](#debugging)
+- [API](#api)
+- [Authors](#authors)
+- [Credits](#credits)
+
+## Supported image operations
+
+- Resize
+- Enlarge
+- Crop (including smart crop support)
+- Rotate (with auto-rotate based on EXIF orientation)
+- Flip (with auto-flip based on EXIF metadata)
+- Flop
+- Zoom
+- Thumbnail
+- Extract area
+- Watermark (text only)
+- Gaussian blur effect
+- Custom output color space (RGB, grayscale...)
+- Format conversion (with additional quality/compression settings)
+- EXIF metadata (size, alpha channel, profile, orientation...)
+
+## Prerequisites
+
+- [libvips](https://github.com/jcupitt/libvips) 7.42+ or 8+ (8.4+ recommended)
+- C compatible compiler such as gcc 4.6+ or clang 3.0+
+- Go 1.3+
+
+**Note**: `libvips` v8.3+ is required for GIF, PDF and SVG support.
+
+## Installation
+
+```bash
+go get -u gopkg.in/h2non/bimg.v1
+```
+
+### libvips
+
+Run the following script as `sudo` (supports OSX, Debian/Ubuntu, Redhat, Fedora, Amazon Linux):
+```bash
+curl -s https://raw.githubusercontent.com/h2non/bimg/master/preinstall.sh | sudo bash -
+```
+
+If you wanna take the advantage of [OpenSlide](http://openslide.org/), simply add `--with-openslide` to enable it:
+```bash
+curl -s https://raw.githubusercontent.com/h2non/bimg/master/preinstall.sh | sudo bash -s --with-openslide
+```
+
+The [install script](https://github.com/h2non/bimg/blob/master/preinstall.sh) requires `curl` and `pkg-config`.
+
+## Performance
+
+libvips is probably the faster open source solution for image processing.
+Here you can see some performance test comparisons for multiple scenarios:
+
+- [libvips speed and memory usage](http://www.vips.ecs.soton.ac.uk/index.php?title=Speed_and_Memory_Use)
+
+## Benchmark
+
+Tested using Go 1.5.1 and libvips-7.42.3 in OSX i7 2.7Ghz
+```
+BenchmarkRotateJpeg-8 20 64686945 ns/op
+BenchmarkResizeLargeJpeg-8 20 63390416 ns/op
+BenchmarkResizePng-8 100 18147294 ns/op
+BenchmarkResizeWebP-8 100 20836741 ns/op
+BenchmarkConvertToJpeg-8 100 12831812 ns/op
+BenchmarkConvertToPng-8 10 128901422 ns/op
+BenchmarkConvertToWebp-8 10 204027990 ns/op
+BenchmarkCropJpeg-8 30 59068572 ns/op
+BenchmarkCropPng-8 10 117303259 ns/op
+BenchmarkCropWebP-8 10 107060659 ns/op
+BenchmarkExtractJpeg-8 50 30708919 ns/op
+BenchmarkExtractPng-8 3000 595546 ns/op
+BenchmarkExtractWebp-8 3000 386379 ns/op
+BenchmarkZoomJpeg-8 10 160005424 ns/op
+BenchmarkZoomPng-8 30 44561047 ns/op
+BenchmarkZoomWebp-8 10 126732678 ns/op
+BenchmarkWatermarkJpeg-8 20 79006133 ns/op
+BenchmarkWatermarPng-8 200 8197291 ns/op
+BenchmarkWatermarWebp-8 30 49360369 ns/op
+```
+
+## Examples
+
+```go
+import (
+ "fmt"
+ "os"
+ "gopkg.in/h2non/bimg.v1"
+)
+```
+
+#### Resize
+
+```go
+buffer, err := bimg.Read("image.jpg")
+if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+}
+
+newImage, err := bimg.NewImage(buffer).Resize(800, 600)
+if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+}
+
+size, err := bimg.NewImage(newImage).Size()
+if size.Width == 400 && size.Height == 300 {
+ fmt.Println("The image size is valid")
+}
+
+bimg.Write("new.jpg", newImage)
+```
+
+#### Rotate
+
+```go
+buffer, err := bimg.Read("image.jpg")
+if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+}
+
+newImage, err := bimg.NewImage(buffer).Rotate(90)
+if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+}
+
+bimg.Write("new.jpg", newImage)
+```
+
+#### Convert
+
+```go
+buffer, err := bimg.Read("image.jpg")
+if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+}
+
+newImage, err := bimg.NewImage(buffer).Convert(bimg.PNG)
+if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+}
+
+if bimg.NewImage(newImage).Type() == "png" {
+ fmt.Fprintln(os.Stderr, "The image was converted into png")
+}
+```
+
+#### Force resize
+
+Force resize operation without perserving the aspect ratio:
+
+```go
+buffer, err := bimg.Read("image.jpg")
+if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+}
+
+newImage, err := bimg.NewImage(buffer).ForceResize(1000, 500)
+if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+}
+
+size := bimg.Size(newImage)
+if size.Width != 1000 || size.Height != 500 {
+ fmt.Fprintln(os.Stderr, "Incorrect image size")
+}
+```
+
+#### Custom colour space (black & white)
+
+```go
+buffer, err := bimg.Read("image.jpg")
+if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+}
+
+newImage, err := bimg.NewImage(buffer).Colourspace(bimg.INTERPRETATION_B_W)
+if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+}
+
+colourSpace, _ := bimg.ImageInterpretation(newImage)
+if colourSpace != bimg.INTERPRETATION_B_W {
+ fmt.Fprintln(os.Stderr, "Invalid colour space")
+}
+```
+
+#### Custom options
+
+See [Options](https://godoc.org/github.com/h2non/bimg#Options) struct to discover all the available fields
+
+```go
+options := bimg.Options{
+ Width: 800,
+ Height: 600,
+ Crop: true,
+ Quality: 95,
+ Rotate: 180,
+ Interlace: true,
+}
+
+buffer, err := bimg.Read("image.jpg")
+if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+}
+
+newImage, err := bimg.NewImage(buffer).Process(options)
+if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+}
+
+bimg.Write("new.jpg", newImage)
+```
+
+#### Watermark
+
+```go
+buffer, err := bimg.Read("image.jpg")
+if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+}
+
+watermark := bimg.Watermark{
+ Text: "Chuck Norris (c) 2315",
+ Opacity: 0.25,
+ Width: 200,
+ DPI: 100,
+ Margin: 150,
+ Font: "sans bold 12",
+ Background: bimg.Color{255, 255, 255},
+}
+
+newImage, err := bimg.NewImage(buffer).Watermark(watermark)
+if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+}
+
+bimg.Write("new.jpg", newImage)
+```
+
+#### Fluent interface
+
+```go
+buffer, err := bimg.Read("image.jpg")
+if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+}
+
+image := bimg.NewImage(buffer)
+
+// first crop image
+_, err := image.CropByWidth(300)
+if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+}
+
+// then flip it
+newImage, err := image.Flip()
+if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+}
+
+// save the cropped and flipped image
+bimg.Write("new.jpg", newImage)
+```
+
+## Debugging
+
+Run the process passing the `DEBUG` environment variable
+```
+DEBUG=bimg ./app
+```
+
+Enable libvips traces (note that a lot of data will be written in stdout):
+```
+VIPS_TRACE=1 ./app
+```
+
+You can also dump a core on failure, as [John Cuppit](https://github.com/jcupitt) said:
+```c
+g_log_set_always_fatal(
+ G_LOG_FLAG_RECURSION |
+ G_LOG_FLAG_FATAL |
+ G_LOG_LEVEL_ERROR |
+ G_LOG_LEVEL_CRITICAL |
+ G_LOG_LEVEL_WARNING );
+```
+
+Or set the G_DEBUG environment variable:
+```
+export G_DEBUG=fatal-warnings,fatal-criticals
+```
+
+## API
+
+See [godoc reference](https://godoc.org/github.com/h2non/bimg) for detailed API documentation.
+
+## Authors
+
+- [Tomás Aparicio](https://github.com/h2non) - Original author and architect.
+- [Kirill Danshin](https://github.com/kirillDanshin) - Maintainer since April 2017.
+
+## Credits
+
+People who recurrently contributed to improve `bimg` in some way.
+
+- [John Cupitt](https://github.com/jcupitt)
+- [Yoan Blanc](https://github.com/greut)
+- [Christophe Eblé](https://github.com/chreble)
+- [Brant Fitzsimmons](https://github.com/bfitzsimmons)
+- [Thomas Meson](https://github.com/zllak)
+
+Thank you!
+
+## License
+
+MIT - Tomas Aparicio
+
+[](https://sourcegraph.com/github.com/h2non/bimg)
diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/file.go b/vendor/src/gopkg.in/h2non/bimg.v1/file.go
new file mode 100644
index 000000000..0cbf82aa6
--- /dev/null
+++ b/vendor/src/gopkg.in/h2non/bimg.v1/file.go
@@ -0,0 +1,15 @@
+package bimg
+
+import "io/ioutil"
+
+// Read reads all the content of the given file path
+// and returns it as byte buffer.
+func Read(path string) ([]byte, error) {
+ return ioutil.ReadFile(path)
+}
+
+// Write writes the given byte buffer into disk
+// to the given file path.
+func Write(path string, buf []byte) error {
+ return ioutil.WriteFile(path, buf, 0644)
+}
diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/file_test.go b/vendor/src/gopkg.in/h2non/bimg.v1/file_test.go
new file mode 100644
index 000000000..2144669af
--- /dev/null
+++ b/vendor/src/gopkg.in/h2non/bimg.v1/file_test.go
@@ -0,0 +1,38 @@
+package bimg
+
+import (
+ "testing"
+)
+
+func TestRead(t *testing.T) {
+ buf, err := Read("fixtures/test.jpg")
+
+ if err != nil {
+ t.Errorf("Cannot read the image: %#v", err)
+ }
+
+ if len(buf) == 0 {
+ t.Fatal("Empty buffer")
+ }
+
+ if DetermineImageType(buf) != JPEG {
+ t.Fatal("Image is not jpeg")
+ }
+}
+
+func TestWrite(t *testing.T) {
+ buf, err := Read("fixtures/test.jpg")
+
+ if err != nil {
+ t.Errorf("Cannot read the image: %#v", err)
+ }
+
+ if len(buf) == 0 {
+ t.Fatal("Empty buffer")
+ }
+
+ err = Write("fixtures/test_write_out.jpg", buf)
+ if err != nil {
+ t.Fatalf("Cannot write the file: %#v", err)
+ }
+}
diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/corrupt.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/corrupt.jpg
new file mode 100644
index 000000000..e59922201
Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/corrupt.jpg differ
diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/northern_cardinal_bird.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/northern_cardinal_bird.jpg
new file mode 100644
index 000000000..1bf53853d
Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/northern_cardinal_bird.jpg differ
diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/test.gif b/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/test.gif
new file mode 100644
index 000000000..7bf290acc
Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/test.gif differ
diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/test.jp2 b/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/test.jp2
new file mode 100644
index 000000000..940778fff
Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/test.jp2 differ
diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/test.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/test.jpg
new file mode 100644
index 000000000..f17d2f189
Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/test.jpg differ
diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/test.pdf b/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/test.pdf
new file mode 100644
index 000000000..c14cc561f
Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/test.pdf differ
diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/test.png b/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/test.png
new file mode 100644
index 000000000..d2f059120
Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/test.png differ
diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/test.svg b/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/test.svg
new file mode 100644
index 000000000..679edec2e
--- /dev/null
+++ b/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/test.svg
@@ -0,0 +1,725 @@
+
+
diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/test.webp b/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/test.webp
new file mode 100644
index 000000000..122741b60
Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/test.webp differ
diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/test_gif.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/test_gif.jpg
new file mode 100644
index 000000000..e69de29bb
diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/test_icc_prophoto.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/test_icc_prophoto.jpg
new file mode 100644
index 000000000..ebf7f02b3
Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/test_icc_prophoto.jpg differ
diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/test_issue.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/test_issue.jpg
new file mode 100644
index 000000000..8348e3878
Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/test_issue.jpg differ
diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/test_pdf.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/test_pdf.jpg
new file mode 100644
index 000000000..e69de29bb
diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/test_square.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/test_square.jpg
new file mode 100644
index 000000000..c69aab42b
Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/test_square.jpg differ
diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/test_svg.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/test_svg.jpg
new file mode 100644
index 000000000..e69de29bb
diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/transparent.png b/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/transparent.png
new file mode 100644
index 000000000..c82d01517
Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/transparent.png differ
diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/vertical.jpg b/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/vertical.jpg
new file mode 100644
index 000000000..3e12e65e5
Binary files /dev/null and b/vendor/src/gopkg.in/h2non/bimg.v1/fixtures/vertical.jpg differ
diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/image.go b/vendor/src/gopkg.in/h2non/bimg.v1/image.go
new file mode 100644
index 000000000..efaffcb73
--- /dev/null
+++ b/vendor/src/gopkg.in/h2non/bimg.v1/image.go
@@ -0,0 +1,223 @@
+package bimg
+
+// Image provides a simple method DSL to transform a given image as byte buffer.
+type Image struct {
+ buffer []byte
+}
+
+// NewImage creates a new Image struct with method DSL.
+func NewImage(buf []byte) *Image {
+ return &Image{buf}
+}
+
+// Resize resizes the image to fixed width and height.
+func (i *Image) Resize(width, height int) ([]byte, error) {
+ options := Options{
+ Width: width,
+ Height: height,
+ Embed: true,
+ }
+ return i.Process(options)
+}
+
+// ForceResize resizes with custom size (aspect ratio won't be maintained).
+func (i *Image) ForceResize(width, height int) ([]byte, error) {
+ options := Options{
+ Width: width,
+ Height: height,
+ Force: true,
+ }
+ return i.Process(options)
+}
+
+// ResizeAndCrop resizes the image to fixed width and height with additional crop transformation.
+func (i *Image) ResizeAndCrop(width, height int) ([]byte, error) {
+ options := Options{
+ Width: width,
+ Height: height,
+ Embed: true,
+ Crop: true,
+ }
+ return i.Process(options)
+}
+
+// SmartCrop produces a thumbnail aiming at focus on the interesting part.
+func (i *Image) SmartCrop(width, height int) ([]byte, error) {
+ options := Options{
+ Width: width,
+ Height: height,
+ Crop: true,
+ Gravity: GravitySmart,
+ }
+ return i.Process(options)
+}
+
+// Extract area from the by X/Y axis in the current image.
+func (i *Image) Extract(top, left, width, height int) ([]byte, error) {
+ options := Options{
+ Top: top,
+ Left: left,
+ AreaWidth: width,
+ AreaHeight: height,
+ }
+
+ if top == 0 && left == 0 {
+ options.Top = -1
+ }
+
+ return i.Process(options)
+}
+
+// Enlarge enlarges the image by width and height. Aspect ratio is maintained.
+func (i *Image) Enlarge(width, height int) ([]byte, error) {
+ options := Options{
+ Width: width,
+ Height: height,
+ Enlarge: true,
+ }
+ return i.Process(options)
+}
+
+// EnlargeAndCrop enlarges the image by width and height with additional crop transformation.
+func (i *Image) EnlargeAndCrop(width, height int) ([]byte, error) {
+ options := Options{
+ Width: width,
+ Height: height,
+ Enlarge: true,
+ Crop: true,
+ }
+ return i.Process(options)
+}
+
+// Crop crops the image to the exact size specified.
+func (i *Image) Crop(width, height int, gravity Gravity) ([]byte, error) {
+ options := Options{
+ Width: width,
+ Height: height,
+ Gravity: gravity,
+ Crop: true,
+ }
+ return i.Process(options)
+}
+
+// CropByWidth crops an image by width only param (auto height).
+func (i *Image) CropByWidth(width int) ([]byte, error) {
+ options := Options{
+ Width: width,
+ Crop: true,
+ }
+ return i.Process(options)
+}
+
+// CropByHeight crops an image by height (auto width).
+func (i *Image) CropByHeight(height int) ([]byte, error) {
+ options := Options{
+ Height: height,
+ Crop: true,
+ }
+ return i.Process(options)
+}
+
+// Thumbnail creates a thumbnail of the image by the a given width by aspect ratio 4:4.
+func (i *Image) Thumbnail(pixels int) ([]byte, error) {
+ options := Options{
+ Width: pixels,
+ Height: pixels,
+ Crop: true,
+ Quality: 95,
+ }
+ return i.Process(options)
+}
+
+// Watermark adds text as watermark on the given image.
+func (i *Image) Watermark(w Watermark) ([]byte, error) {
+ options := Options{Watermark: w}
+ return i.Process(options)
+}
+
+// WatermarkImage adds image as watermark on the given image.
+func (i *Image) WatermarkImage(w WatermarkImage) ([]byte, error) {
+ options := Options{WatermarkImage: w}
+ return i.Process(options)
+}
+
+// Zoom zooms the image by the given factor.
+// You should probably call Extract() before.
+func (i *Image) Zoom(factor int) ([]byte, error) {
+ options := Options{Zoom: factor}
+ return i.Process(options)
+}
+
+// Rotate rotates the image by given angle degrees (0, 90, 180 or 270).
+func (i *Image) Rotate(a Angle) ([]byte, error) {
+ options := Options{Rotate: a}
+ return i.Process(options)
+}
+
+// Flip flips the image about the vertical Y axis.
+func (i *Image) Flip() ([]byte, error) {
+ options := Options{Flip: true}
+ return i.Process(options)
+}
+
+// Flop flops the image about the horizontal X axis.
+func (i *Image) Flop() ([]byte, error) {
+ options := Options{Flop: true}
+ return i.Process(options)
+}
+
+// Convert converts image to another format.
+func (i *Image) Convert(t ImageType) ([]byte, error) {
+ options := Options{Type: t}
+ return i.Process(options)
+}
+
+// Colourspace performs a color space conversion bsaed on the given interpretation.
+func (i *Image) Colourspace(c Interpretation) ([]byte, error) {
+ options := Options{Interpretation: c}
+ return i.Process(options)
+}
+
+// Process processes the image based on the given transformation options,
+// talking with libvips bindings accordingly and returning the resultant
+// image buffer.
+func (i *Image) Process(o Options) ([]byte, error) {
+ image, err := Resize(i.buffer, o)
+ if err != nil {
+ return nil, err
+ }
+ i.buffer = image
+ return image, nil
+}
+
+// Metadata returns the image metadata (size, alpha channel, profile, EXIF rotation).
+func (i *Image) Metadata() (ImageMetadata, error) {
+ return Metadata(i.buffer)
+}
+
+// Interpretation gets the image interpretation type.
+// See: http://www.vips.ecs.soton.ac.uk/supported/current/doc/html/libvips/VipsImage.html#VipsInterpretation
+func (i *Image) Interpretation() (Interpretation, error) {
+ return ImageInterpretation(i.buffer)
+}
+
+// ColourspaceIsSupported checks if the current image
+// color space is supported.
+func (i *Image) ColourspaceIsSupported() (bool, error) {
+ return ColourspaceIsSupported(i.buffer)
+}
+
+// Type returns the image type format (jpeg, png, webp, tiff).
+func (i *Image) Type() string {
+ return DetermineImageTypeName(i.buffer)
+}
+
+// Size returns the image size as form of width and height pixels.
+func (i *Image) Size() (ImageSize, error) {
+ return Size(i.buffer)
+}
+
+// Image returns the current resultant image image buffer.
+func (i *Image) Image() []byte {
+ return i.buffer
+}
diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/image_test.go b/vendor/src/gopkg.in/h2non/bimg.v1/image_test.go
new file mode 100644
index 000000000..96fc4ad9f
--- /dev/null
+++ b/vendor/src/gopkg.in/h2non/bimg.v1/image_test.go
@@ -0,0 +1,496 @@
+package bimg
+
+import (
+ "fmt"
+ "path"
+ "testing"
+)
+
+func TestImageResize(t *testing.T) {
+ buf, err := initImage("test.jpg").Resize(300, 240)
+ if err != nil {
+ t.Errorf("Cannot process the image: %#v", err)
+ }
+
+ err = assertSize(buf, 300, 240)
+ if err != nil {
+ t.Error(err)
+ }
+
+ Write("fixtures/test_resize_out.jpg", buf)
+}
+
+func TestImageGifResize(t *testing.T) {
+ _, err := initImage("test.gif").Resize(300, 240)
+ if err == nil {
+ t.Errorf("GIF shouldn't be saved within VIPS")
+ }
+}
+
+func TestImagePdfResize(t *testing.T) {
+ _, err := initImage("test.pdf").Resize(300, 240)
+ if err == nil {
+ t.Errorf("PDF cannot be saved within VIPS")
+ }
+}
+
+func TestImageSvgResize(t *testing.T) {
+ _, err := initImage("test.svg").Resize(300, 240)
+ if err == nil {
+ t.Errorf("SVG cannot be saved within VIPS")
+ }
+}
+
+func TestImageGifToJpeg(t *testing.T) {
+ if VipsMajorVersion >= 8 && VipsMinorVersion > 2 {
+ i := initImage("test.gif")
+ options := Options{
+ Type: JPEG,
+ }
+ buf, err := i.Process(options)
+ if err != nil {
+ t.Errorf("Cannot process the image: %#v", err)
+ }
+
+ Write("fixtures/test_gif.jpg", buf)
+ }
+}
+
+func TestImagePdfToJpeg(t *testing.T) {
+ if VipsMajorVersion >= 8 && VipsMinorVersion > 2 {
+ i := initImage("test.pdf")
+ options := Options{
+ Type: JPEG,
+ }
+ buf, err := i.Process(options)
+ if err != nil {
+ t.Errorf("Cannot process the image: %#v", err)
+ }
+
+ Write("fixtures/test_pdf.jpg", buf)
+ }
+}
+
+func TestImageSvgToJpeg(t *testing.T) {
+ if VipsMajorVersion >= 8 && VipsMinorVersion > 2 {
+ i := initImage("test.svg")
+ options := Options{
+ Type: JPEG,
+ }
+ buf, err := i.Process(options)
+ if err != nil {
+ t.Errorf("Cannot process the image: %#v", err)
+ }
+
+ Write("fixtures/test_svg.jpg", buf)
+ }
+}
+
+func TestImageResizeAndCrop(t *testing.T) {
+ buf, err := initImage("test.jpg").ResizeAndCrop(300, 200)
+ if err != nil {
+ t.Errorf("Cannot process the image: %#v", err)
+ }
+
+ err = assertSize(buf, 300, 200)
+ if err != nil {
+ t.Error(err)
+ }
+
+ Write("fixtures/test_resize_crop_out.jpg", buf)
+}
+
+func TestImageExtract(t *testing.T) {
+ buf, err := initImage("test.jpg").Extract(100, 100, 300, 200)
+ if err != nil {
+ t.Errorf("Cannot process the image: %s", err)
+ }
+
+ err = assertSize(buf, 300, 200)
+ if err != nil {
+ t.Error(err)
+ }
+
+ Write("fixtures/test_extract_out.jpg", buf)
+}
+
+func TestImageExtractZero(t *testing.T) {
+ buf, err := initImage("test.jpg").Extract(0, 0, 300, 200)
+ if err != nil {
+ t.Errorf("Cannot process the image: %s", err)
+ }
+
+ err = assertSize(buf, 300, 200)
+ if err != nil {
+ t.Error(err)
+ }
+
+ Write("fixtures/test_extract_zero_out.jpg", buf)
+}
+
+func TestImageEnlarge(t *testing.T) {
+ buf, err := initImage("test.png").Enlarge(500, 375)
+ if err != nil {
+ t.Errorf("Cannot process the image: %#v", err)
+ }
+
+ err = assertSize(buf, 500, 375)
+ if err != nil {
+ t.Error(err)
+ }
+
+ Write("fixtures/test_enlarge_out.jpg", buf)
+}
+
+func TestImageEnlargeAndCrop(t *testing.T) {
+ buf, err := initImage("test.png").EnlargeAndCrop(800, 480)
+ if err != nil {
+ t.Errorf("Cannot process the image: %#v", err)
+ }
+
+ err = assertSize(buf, 800, 480)
+ if err != nil {
+ t.Error(err)
+ }
+
+ Write("fixtures/test_enlarge_crop_out.jpg", buf)
+}
+
+func TestImageCrop(t *testing.T) {
+ buf, err := initImage("test.jpg").Crop(800, 600, GravityNorth)
+ if err != nil {
+ t.Errorf("Cannot process the image: %s", err)
+ }
+
+ err = assertSize(buf, 800, 600)
+ if err != nil {
+ t.Error(err)
+ }
+
+ Write("fixtures/test_crop_out.jpg", buf)
+}
+
+func TestImageCropByWidth(t *testing.T) {
+ buf, err := initImage("test.jpg").CropByWidth(600)
+ if err != nil {
+ t.Errorf("Cannot process the image: %s", err)
+ }
+
+ err = assertSize(buf, 600, 1050)
+ if err != nil {
+ t.Error(err)
+ }
+
+ Write("fixtures/test_crop_width_out.jpg", buf)
+}
+
+func TestImageCropByHeight(t *testing.T) {
+ buf, err := initImage("test.jpg").CropByHeight(300)
+ if err != nil {
+ t.Errorf("Cannot process the image: %s", err)
+ }
+
+ err = assertSize(buf, 1680, 300)
+ if err != nil {
+ t.Error(err)
+ }
+
+ Write("fixtures/test_crop_height_out.jpg", buf)
+}
+
+func TestImageThumbnail(t *testing.T) {
+ buf, err := initImage("test.jpg").Thumbnail(100)
+ if err != nil {
+ t.Errorf("Cannot process the image: %s", err)
+ }
+
+ err = assertSize(buf, 100, 100)
+ if err != nil {
+ t.Error(err)
+ }
+
+ Write("fixtures/test_thumbnail_out.jpg", buf)
+}
+
+func TestImageWatermark(t *testing.T) {
+ image := initImage("test.jpg")
+ _, err := image.Crop(800, 600, GravityNorth)
+ if err != nil {
+ t.Errorf("Cannot process the image: %#v", err)
+ }
+
+ buf, err := image.Watermark(Watermark{
+ Text: "Copy me if you can",
+ Opacity: 0.5,
+ Width: 200,
+ DPI: 100,
+ Background: Color{255, 255, 255},
+ })
+ if err != nil {
+ t.Error(err)
+ }
+
+ err = assertSize(buf, 800, 600)
+ if err != nil {
+ t.Error(err)
+ }
+
+ if DetermineImageType(buf) != JPEG {
+ t.Fatal("Image is not jpeg")
+ }
+
+ Write("fixtures/test_watermark_text_out.jpg", buf)
+}
+
+func TestImageWatermarkWithImage(t *testing.T) {
+ image := initImage("test.jpg")
+ watermark, _ := imageBuf("transparent.png")
+
+ _, err := image.Crop(800, 600, GravityNorth)
+ if err != nil {
+ t.Errorf("Cannot process the image: %#v", err)
+ }
+
+ buf, err := image.WatermarkImage(WatermarkImage{Left: 100, Top: 100, Buf: watermark})
+
+ if err != nil {
+ t.Error(err)
+ }
+
+ err = assertSize(buf, 800, 600)
+ if err != nil {
+ t.Error(err)
+ }
+
+ if DetermineImageType(buf) != JPEG {
+ t.Fatal("Image is not jpeg")
+ }
+
+ Write("fixtures/test_watermark_image_out.jpg", buf)
+}
+
+func TestImageWatermarkNoReplicate(t *testing.T) {
+ image := initImage("test.jpg")
+ _, err := image.Crop(800, 600, GravityNorth)
+ if err != nil {
+ t.Errorf("Cannot process the image: %s", err)
+ }
+
+ buf, err := image.Watermark(Watermark{
+ Text: "Copy me if you can",
+ Opacity: 0.5,
+ Width: 200,
+ DPI: 100,
+ NoReplicate: true,
+ Background: Color{255, 255, 255},
+ })
+ if err != nil {
+ t.Error(err)
+ }
+
+ err = assertSize(buf, 800, 600)
+ if err != nil {
+ t.Error(err)
+ }
+
+ if DetermineImageType(buf) != JPEG {
+ t.Fatal("Image is not jpeg")
+ }
+
+ Write("fixtures/test_watermark_replicate_out.jpg", buf)
+}
+
+func TestImageZoom(t *testing.T) {
+ image := initImage("test.jpg")
+
+ _, err := image.Extract(100, 100, 400, 300)
+ if err != nil {
+ t.Errorf("Cannot extract the image: %s", err)
+ }
+
+ buf, err := image.Zoom(1)
+ if err != nil {
+ t.Errorf("Cannot process the image: %s", err)
+ }
+
+ err = assertSize(buf, 800, 600)
+ if err != nil {
+ t.Error(err)
+ }
+
+ Write("fixtures/test_zoom_out.jpg", buf)
+}
+
+func TestImageFlip(t *testing.T) {
+ buf, err := initImage("test.jpg").Flip()
+ if err != nil {
+ t.Errorf("Cannot process the image: %#v", err)
+ }
+ Write("fixtures/test_flip_out.jpg", buf)
+}
+
+func TestImageFlop(t *testing.T) {
+ buf, err := initImage("test.jpg").Flop()
+ if err != nil {
+ t.Errorf("Cannot process the image: %#v", err)
+ }
+ Write("fixtures/test_flop_out.jpg", buf)
+}
+
+func TestImageRotate(t *testing.T) {
+ buf, err := initImage("test_flip_out.jpg").Rotate(90)
+ if err != nil {
+ t.Errorf("Cannot process the image: %#v", err)
+ }
+ Write("fixtures/test_image_rotate_out.jpg", buf)
+}
+
+func TestImageConvert(t *testing.T) {
+ buf, err := initImage("test.jpg").Convert(PNG)
+ if err != nil {
+ t.Errorf("Cannot process the image: %#v", err)
+ }
+ Write("fixtures/test_image_convert_out.png", buf)
+}
+
+func TestTransparentImageConvert(t *testing.T) {
+ image := initImage("transparent.png")
+ options := Options{
+ Type: JPEG,
+ Background: Color{255, 255, 255},
+ }
+ buf, err := image.Process(options)
+ if err != nil {
+ t.Errorf("Cannot process the image: %#v", err)
+ }
+ Write("fixtures/test_transparent_image_convert_out.jpg", buf)
+}
+
+func TestImageMetadata(t *testing.T) {
+ data, err := initImage("test.png").Metadata()
+ if err != nil {
+ t.Errorf("Cannot process the image: %#v", err)
+ }
+ if data.Alpha != true {
+ t.Fatal("Invalid alpha channel")
+ }
+ if data.Size.Width != 400 {
+ t.Fatal("Invalid width size")
+ }
+ if data.Type != "png" {
+ t.Fatal("Invalid image type")
+ }
+}
+
+func TestInterpretation(t *testing.T) {
+ interpretation, err := initImage("test.jpg").Interpretation()
+ if err != nil {
+ t.Errorf("Cannot process the image: %#v", err)
+ }
+ if interpretation != InterpretationSRGB {
+ t.Errorf("Invalid interpretation: %d", interpretation)
+ }
+}
+
+func TestImageColourspace(t *testing.T) {
+ tests := []struct {
+ file string
+ interpretation Interpretation
+ }{
+ {"test.jpg", InterpretationSRGB},
+ {"test.jpg", InterpretationBW},
+ }
+
+ for _, test := range tests {
+ buf, err := initImage(test.file).Colourspace(test.interpretation)
+ if err != nil {
+ t.Errorf("Cannot process the image: %#v", err)
+ }
+
+ interpretation, err := ImageInterpretation(buf)
+ if interpretation != test.interpretation {
+ t.Errorf("Invalid colourspace")
+ }
+ }
+}
+
+func TestImageColourspaceIsSupported(t *testing.T) {
+ supported, err := initImage("test.jpg").ColourspaceIsSupported()
+ if err != nil {
+ t.Errorf("Cannot process the image: %#v", err)
+ }
+ if supported != true {
+ t.Errorf("Non-supported colourspace")
+ }
+}
+
+func TestFluentInterface(t *testing.T) {
+ image := initImage("test.jpg")
+ _, err := image.CropByWidth(300)
+ if err != nil {
+ t.Errorf("Cannot process the image: %#v", err)
+ }
+
+ _, err = image.Flip()
+ if err != nil {
+ t.Errorf("Cannot process the image: %#v", err)
+ }
+
+ _, err = image.Convert(PNG)
+ if err != nil {
+ t.Errorf("Cannot process the image: %#v", err)
+ }
+
+ data, _ := image.Metadata()
+ if data.Alpha != false {
+ t.Fatal("Invalid alpha channel")
+ }
+ if data.Size.Width != 300 {
+ t.Fatal("Invalid width size")
+ }
+ if data.Type != "png" {
+ t.Fatal("Invalid image type")
+ }
+
+ Write("fixtures/test_image_fluent_out.png", image.Image())
+}
+
+func TestImageSmartCrop(t *testing.T) {
+
+ if !(VipsMajorVersion >= 8 && VipsMinorVersion > 4) {
+ t.Skipf("Skipping this test, libvips doesn't meet version requirement %s > 8.4", VipsVersion)
+ }
+
+ i := initImage("northern_cardinal_bird.jpg")
+ buf, err := i.SmartCrop(300, 300)
+ if err != nil {
+ t.Errorf("Cannot process the image: %#v", err)
+ }
+
+ err = assertSize(buf, 300, 300)
+ if err != nil {
+ t.Error(err)
+ }
+
+ Write("fixtures/test_smart_crop.jpg", buf)
+}
+
+func initImage(file string) *Image {
+ buf, _ := imageBuf(file)
+ return NewImage(buf)
+}
+
+func imageBuf(file string) ([]byte, error) {
+ return Read(path.Join("fixtures", file))
+}
+
+func assertSize(buf []byte, width, height int) error {
+ size, err := NewImage(buf).Size()
+ if err != nil {
+ return err
+ }
+ if size.Width != width || size.Height != height {
+ return fmt.Errorf("Invalid image size: %dx%d", size.Width, size.Height)
+ }
+ return nil
+}
diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/metadata.go b/vendor/src/gopkg.in/h2non/bimg.v1/metadata.go
new file mode 100644
index 000000000..77eac8cd9
--- /dev/null
+++ b/vendor/src/gopkg.in/h2non/bimg.v1/metadata.go
@@ -0,0 +1,77 @@
+package bimg
+
+/*
+#cgo pkg-config: vips
+#include "vips/vips.h"
+*/
+import "C"
+
+// ImageSize represents the image width and height values
+type ImageSize struct {
+ Width int
+ Height int
+}
+
+// ImageMetadata represents the basic metadata fields
+type ImageMetadata struct {
+ Orientation int
+ Channels int
+ Alpha bool
+ Profile bool
+ Type string
+ Space string
+ Colourspace string
+ Size ImageSize
+}
+
+// Size returns the image size by width and height pixels.
+func Size(buf []byte) (ImageSize, error) {
+ metadata, err := Metadata(buf)
+ if err != nil {
+ return ImageSize{}, err
+ }
+
+ return ImageSize{
+ Width: int(metadata.Size.Width),
+ Height: int(metadata.Size.Height),
+ }, nil
+}
+
+// ColourspaceIsSupported checks if the image colourspace is supported by libvips.
+func ColourspaceIsSupported(buf []byte) (bool, error) {
+ return vipsColourspaceIsSupportedBuffer(buf)
+}
+
+// ImageInterpretation returns the image interpretation type.
+// See: http://www.vips.ecs.soton.ac.uk/supported/current/doc/html/libvips/VipsImage.html#VipsInterpretation
+func ImageInterpretation(buf []byte) (Interpretation, error) {
+ return vipsInterpretationBuffer(buf)
+}
+
+// Metadata returns the image metadata (size, type, alpha channel, profile, EXIF orientation...).
+func Metadata(buf []byte) (ImageMetadata, error) {
+ defer C.vips_thread_shutdown()
+
+ image, imageType, err := vipsRead(buf)
+ if err != nil {
+ return ImageMetadata{}, err
+ }
+ defer C.g_object_unref(C.gpointer(image))
+
+ size := ImageSize{
+ Width: int(image.Xsize),
+ Height: int(image.Ysize),
+ }
+
+ metadata := ImageMetadata{
+ Size: size,
+ Channels: int(image.Bands),
+ Orientation: vipsExifOrientation(image),
+ Alpha: vipsHasAlpha(image),
+ Profile: vipsHasProfile(image),
+ Space: vipsSpace(image),
+ Type: ImageTypeName(imageType),
+ }
+
+ return metadata, nil
+}
diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/metadata_test.go b/vendor/src/gopkg.in/h2non/bimg.v1/metadata_test.go
new file mode 100644
index 000000000..663ec5641
--- /dev/null
+++ b/vendor/src/gopkg.in/h2non/bimg.v1/metadata_test.go
@@ -0,0 +1,124 @@
+package bimg
+
+import (
+ "io/ioutil"
+ "os"
+ "path"
+ "testing"
+)
+
+func TestSize(t *testing.T) {
+ files := []struct {
+ name string
+ width int
+ height int
+ }{
+ {"test.jpg", 1680, 1050},
+ {"test.png", 400, 300},
+ {"test.webp", 550, 368},
+ }
+ for _, file := range files {
+ size, err := Size(readFile(file.name))
+ if err != nil {
+ t.Fatalf("Cannot read the image: %#v", err)
+ }
+
+ if size.Width != file.width || size.Height != file.height {
+ t.Fatalf("Unexpected image size: %dx%d", size.Width, size.Height)
+ }
+ }
+}
+
+func TestMetadata(t *testing.T) {
+ files := []struct {
+ name string
+ format string
+ orientation int
+ alpha bool
+ profile bool
+ space string
+ }{
+ {"test.jpg", "jpeg", 0, false, false, "srgb"},
+ {"test_icc_prophoto.jpg", "jpeg", 0, false, true, "srgb"},
+ {"test.png", "png", 0, true, false, "srgb"},
+ {"test.webp", "webp", 0, false, false, "srgb"},
+ }
+
+ for _, file := range files {
+ metadata, err := Metadata(readFile(file.name))
+ if err != nil {
+ t.Fatalf("Cannot read the image: %s -> %s", file.name, err)
+ }
+
+ if metadata.Type != file.format {
+ t.Fatalf("Unexpected image format: %s", file.format)
+ }
+ if metadata.Orientation != file.orientation {
+ t.Fatalf("Unexpected image orientation: %d != %d", metadata.Orientation, file.orientation)
+ }
+ if metadata.Alpha != file.alpha {
+ t.Fatalf("Unexpected image alpha: %t != %t", metadata.Alpha, file.alpha)
+ }
+ if metadata.Profile != file.profile {
+ t.Fatalf("Unexpected image profile: %t != %t", metadata.Profile, file.profile)
+ }
+ if metadata.Space != file.space {
+ t.Fatalf("Unexpected image profile: %t != %t", metadata.Profile, file.profile)
+ }
+ }
+}
+
+func TestImageInterpretation(t *testing.T) {
+ files := []struct {
+ name string
+ interpretation Interpretation
+ }{
+ {"test.jpg", InterpretationSRGB},
+ {"test.png", InterpretationSRGB},
+ {"test.webp", InterpretationSRGB},
+ }
+
+ for _, file := range files {
+ interpretation, err := ImageInterpretation(readFile(file.name))
+ if err != nil {
+ t.Fatalf("Cannot read the image: %s -> %s", file.name, err)
+ }
+ if interpretation != file.interpretation {
+ t.Fatalf("Unexpected image interpretation")
+ }
+ }
+}
+
+func TestColourspaceIsSupported(t *testing.T) {
+ files := []struct {
+ name string
+ }{
+ {"test.jpg"},
+ {"test.png"},
+ {"test.webp"},
+ }
+
+ for _, file := range files {
+ supported, err := ColourspaceIsSupported(readFile(file.name))
+ if err != nil {
+ t.Fatalf("Cannot read the image: %s -> %s", file.name, err)
+ }
+ if supported != true {
+ t.Fatalf("Unsupported image colourspace")
+ }
+ }
+
+ supported, err := initImage("test.jpg").ColourspaceIsSupported()
+ if err != nil {
+ t.Errorf("Cannot process the image: %#v", err)
+ }
+ if supported != true {
+ t.Errorf("Non-supported colourspace")
+ }
+}
+
+func readFile(file string) []byte {
+ data, _ := os.Open(path.Join("fixtures", file))
+ buf, _ := ioutil.ReadAll(data)
+ return buf
+}
diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/options.go b/vendor/src/gopkg.in/h2non/bimg.v1/options.go
new file mode 100644
index 000000000..f6ebec2c0
--- /dev/null
+++ b/vendor/src/gopkg.in/h2non/bimg.v1/options.go
@@ -0,0 +1,218 @@
+package bimg
+
+/*
+#cgo pkg-config: vips
+#include "vips/vips.h"
+*/
+import "C"
+
+const (
+ // Quality defines the default JPEG quality to be used.
+ Quality = 80
+ // MaxSize defines the maximum pixels width or height supported.
+ MaxSize = 16383
+)
+
+// Gravity represents the image gravity value.
+type Gravity int
+
+const (
+ // GravityCentre represents the centre value used for image gravity orientation.
+ GravityCentre Gravity = iota
+ // GravityNorth represents the north value used for image gravity orientation.
+ GravityNorth
+ // GravityEast represents the east value used for image gravity orientation.
+ GravityEast
+ // GravitySouth represents the south value used for image gravity orientation.
+ GravitySouth
+ // GravityWest represents the west value used for image gravity orientation.
+ GravityWest
+ // GravitySmart enables libvips Smart Crop algorithm for image gravity orientation.
+ GravitySmart
+)
+
+// Interpolator represents the image interpolation value.
+type Interpolator int
+
+const (
+ // Bicubic interpolation value.
+ Bicubic Interpolator = iota
+ // Bilinear interpolation value.
+ Bilinear
+ // Nohalo interpolation value.
+ Nohalo
+)
+
+var interpolations = map[Interpolator]string{
+ Bicubic: "bicubic",
+ Bilinear: "bilinear",
+ Nohalo: "nohalo",
+}
+
+func (i Interpolator) String() string {
+ return interpolations[i]
+}
+
+// Angle represents the image rotation angle value.
+type Angle int
+
+const (
+ // D0 represents the rotation angle 0 degrees.
+ D0 Angle = 0
+ // D45 represents the rotation angle 90 degrees.
+ D45 Angle = 45
+ // D90 represents the rotation angle 90 degrees.
+ D90 Angle = 90
+ // D135 represents the rotation angle 90 degrees.
+ D135 Angle = 135
+ // D180 represents the rotation angle 180 degrees.
+ D180 Angle = 180
+ // D235 represents the rotation angle 235 degrees.
+ D235 Angle = 235
+ // D270 represents the rotation angle 270 degrees.
+ D270 Angle = 270
+ // D315 represents the rotation angle 180 degrees.
+ D315 Angle = 315
+)
+
+// Direction represents the image direction value.
+type Direction int
+
+const (
+ // Horizontal represents the orizontal image direction value.
+ Horizontal Direction = C.VIPS_DIRECTION_HORIZONTAL
+ // Vertical represents the vertical image direction value.
+ Vertical Direction = C.VIPS_DIRECTION_VERTICAL
+)
+
+// Interpretation represents the image interpretation type.
+// See: http://www.vips.ecs.soton.ac.uk/supported/current/doc/html/libvips/VipsImage.html#VipsInterpretation
+type Interpretation int
+
+const (
+ // InterpretationError points to the libvips interpretation error type.
+ InterpretationError Interpretation = C.VIPS_INTERPRETATION_ERROR
+ // InterpretationMultiband points to its libvips interpretation equivalent type.
+ InterpretationMultiband Interpretation = C.VIPS_INTERPRETATION_MULTIBAND
+ // InterpretationBW points to its libvips interpretation equivalent type.
+ InterpretationBW Interpretation = C.VIPS_INTERPRETATION_B_W
+ // InterpretationCMYK points to its libvips interpretation equivalent type.
+ InterpretationCMYK Interpretation = C.VIPS_INTERPRETATION_CMYK
+ // InterpretationRGB points to its libvips interpretation equivalent type.
+ InterpretationRGB Interpretation = C.VIPS_INTERPRETATION_RGB
+ // InterpretationSRGB points to its libvips interpretation equivalent type.
+ InterpretationSRGB Interpretation = C.VIPS_INTERPRETATION_sRGB
+ // InterpretationRGB16 points to its libvips interpretation equivalent type.
+ InterpretationRGB16 Interpretation = C.VIPS_INTERPRETATION_RGB16
+ // InterpretationGREY16 points to its libvips interpretation equivalent type.
+ InterpretationGREY16 Interpretation = C.VIPS_INTERPRETATION_GREY16
+ // InterpretationScRGB points to its libvips interpretation equivalent type.
+ InterpretationScRGB Interpretation = C.VIPS_INTERPRETATION_scRGB
+ // InterpretationLAB points to its libvips interpretation equivalent type.
+ InterpretationLAB Interpretation = C.VIPS_INTERPRETATION_LAB
+ // InterpretationXYZ points to its libvips interpretation equivalent type.
+ InterpretationXYZ Interpretation = C.VIPS_INTERPRETATION_XYZ
+)
+
+// Extend represents the image extend mode, used when the edges
+// of an image are extended, you can specify how you want the extension done.
+// See: http://www.vips.ecs.soton.ac.uk/supported/8.4/doc/html/libvips/libvips-conversion.html#VIPS-EXTEND-BACKGROUND:CAPS
+type Extend int
+
+const (
+ // ExtendBlack extend with black (all 0) pixels mode.
+ ExtendBlack Extend = C.VIPS_EXTEND_BLACK
+ // ExtendCopy copy the image edges.
+ ExtendCopy Extend = C.VIPS_EXTEND_COPY
+ // ExtendRepeat repeat the whole image.
+ ExtendRepeat Extend = C.VIPS_EXTEND_REPEAT
+ // ExtendMirror mirror the whole image.
+ ExtendMirror Extend = C.VIPS_EXTEND_MIRROR
+ // ExtendWhite extend with white (all bits set) pixels.
+ ExtendWhite Extend = C.VIPS_EXTEND_WHITE
+ // ExtendBackground with colour from the background property.
+ ExtendBackground Extend = C.VIPS_EXTEND_BACKGROUND
+ // ExtendLast extend with last pixel.
+ ExtendLast Extend = C.VIPS_EXTEND_LAST
+)
+
+// WatermarkFont defines the default watermark font to be used.
+var WatermarkFont = "sans 10"
+
+// Color represents a traditional RGB color scheme.
+type Color struct {
+ R, G, B uint8
+}
+
+// ColorBlack is a shortcut to black RGB color representation.
+var ColorBlack = Color{0, 0, 0}
+
+// Watermark represents the text-based watermark supported options.
+type Watermark struct {
+ Width int
+ DPI int
+ Margin int
+ Opacity float32
+ NoReplicate bool
+ Text string
+ Font string
+ Background Color
+}
+
+// WatermarkImage represents the image-based watermark supported options.
+type WatermarkImage struct {
+ Left int
+ Top int
+ Buf []byte
+ Opacity float32
+}
+
+// GaussianBlur represents the gaussian image transformation values.
+type GaussianBlur struct {
+ Sigma float64
+ MinAmpl float64
+}
+
+// Sharpen represents the image sharp transformation options.
+type Sharpen struct {
+ Radius int
+ X1 float64
+ Y2 float64
+ Y3 float64
+ M1 float64
+ M2 float64
+}
+
+// Options represents the supported image transformation options.
+type Options struct {
+ Height int
+ Width int
+ AreaHeight int
+ AreaWidth int
+ Top int
+ Left int
+ Quality int
+ Compression int
+ Zoom int
+ Crop bool
+ SmartCrop bool // Deprecated
+ Enlarge bool
+ Embed bool
+ Flip bool
+ Flop bool
+ Force bool
+ NoAutoRotate bool
+ NoProfile bool
+ Interlace bool
+ Extend Extend
+ Rotate Angle
+ Background Color
+ Gravity Gravity
+ Watermark Watermark
+ WatermarkImage WatermarkImage
+ Type ImageType
+ Interpolator Interpolator
+ Interpretation Interpretation
+ GaussianBlur GaussianBlur
+ Sharpen Sharpen
+}
diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/preinstall.sh b/vendor/src/gopkg.in/h2non/bimg.v1/preinstall.sh
new file mode 100644
index 000000000..47fa24c52
--- /dev/null
+++ b/vendor/src/gopkg.in/h2non/bimg.v1/preinstall.sh
@@ -0,0 +1,302 @@
+#!/bin/bash
+
+vips_version_minimum=8.4.2
+vips_version_latest_major_minor=8.4
+vips_version_latest_patch=2
+
+openslide_version_minimum=3.4.0
+openslide_version_latest_major_minor=3.4
+openslide_version_latest_patch=1
+
+install_libvips_from_source() {
+ echo "Compiling libvips $vips_version_latest_major_minor.$vips_version_latest_patch from source"
+ curl -O http://www.vips.ecs.soton.ac.uk/supported/$vips_version_latest_major_minor/vips-$vips_version_latest_major_minor.$vips_version_latest_patch.tar.gz
+ tar zvxf vips-$vips_version_latest_major_minor.$vips_version_latest_patch.tar.gz
+ cd vips-$vips_version_latest_major_minor.$vips_version_latest_patch
+ CXXFLAGS="-D_GLIBCXX_USE_CXX11_ABI=0" ./configure --disable-debug --disable-docs --disable-static --disable-introspection --disable-dependency-tracking --enable-cxx=yes --without-python --without-orc --without-fftw $1
+ make
+ make install
+ cd ..
+ rm -rf vips-$vips_version_latest_major_minor.$vips_version_latest_patch
+ rm vips-$vips_version_latest_major_minor.$vips_version_latest_patch.tar.gz
+ ldconfig
+ echo "Installed libvips $(PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig:/usr/lib/pkgconfig pkg-config --modversion vips)"
+}
+
+install_libopenslide_from_source() {
+ echo "Compiling openslide $openslide_version_latest_major_minor.$openslide_version_latest_patch from source"
+ curl -O -L https://github.com/openslide/openslide/releases/download/v$openslide_version_latest_major_minor.$openslide_version_latest_patch/openslide-$openslide_version_latest_major_minor.$openslide_version_latest_patch.tar.gz
+ tar xzvf openslide-$openslide_version_latest_major_minor.$openslide_version_latest_patch.tar.gz
+ cd openslide-$openslide_version_latest_major_minor.$openslide_version_latest_patch
+ PKG_CONFIG_PATH=$pkg_config_path ./configure $1
+ make
+ make install
+ cd ..
+ rm -rf openslide-$openslide_version_latest_major_minor.$openslide_version_latest_patch
+ rm openslide-$openslide_version_latest_major_minor.$openslide_version_latest_patch.tar.gz
+ ldconfig
+ echo "Installed libopenslide $openslide_version_latest_major_minor.$openslide_version_latest_patch"
+}
+
+sorry() {
+ echo "Sorry, I don't yet know how to install lib$1 on $2"
+ exit 1
+}
+
+pkg_config_path="$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig:/usr/lib/pkgconfig"
+
+check_if_library_exists() {
+ PKG_CONFIG_PATH=$pkg_config_path pkg-config --exists $1
+ if [ $? -eq 0 ]; then
+ version_found=$(PKG_CONFIG_PATH=$pkg_config_path pkg-config --modversion $1)
+ PKG_CONFIG_PATH=$pkg_config_path pkg-config --atleast-version=$2 $1
+ if [ $? -eq 0 ]; then
+ # Found suitable version of libvips
+ echo "Found lib$1 $version_found"
+ return 1
+ fi
+ echo "Found lib$1 $version_found but require $2"
+ else
+ echo "Could not find lib$1 using a PKG_CONFIG_PATH of '$pkg_config_path'"
+ fi
+ return 0
+}
+
+enable_openslide=0
+# Is libvips already installed, and is it at least the minimum required version?
+if [ $# -eq 1 ]; then
+ if [ "$1" = "--with-openslide" ]; then
+ echo "Installing vips with openslide support"
+ enable_openslide=1
+ else
+ echo "Sorry, $1 is not supported. Did you mean --with-openslide?"
+ exit 1
+ fi
+fi
+
+if ! type pkg-config >/dev/null; then
+ sorry "vips" "a system without pkg-config"
+fi
+
+openslide_exists=0
+if [ $enable_openslide -eq 1 ]; then
+ check_if_library_exists "openslide" "$openslide_version_minimum"
+ openslide_exists=$?
+fi
+
+check_if_library_exists "vips" "$vips_version_minimum"
+vips_exists=$?
+if [ $vips_exists -eq 1 ] && [ $enable_openslide -eq 1 ]; then
+ if [ $openslide_exists -eq 1 ]; then
+ # Check if vips compiled with openslide support
+ vips_with_openslide=`vips list classes | grep -i opensli`
+ if [ -z $vips_with_openslide ]; then
+ echo "Vips compiled without openslide support."
+ else
+ exit 0
+ fi
+ fi
+elif [ $vips_exists -eq 1 ] && [ $enable_openslide -eq 0 ]; then
+ exit 0
+fi
+
+# Verify root/sudo access
+if [ "$(id -u)" -ne "0" ]; then
+ echo "Sorry, I need root/sudo access to continue"
+ exit 1
+fi
+
+# Deprecation warning
+if [ "$(arch)" == "x86_64" ]; then
+ echo "This script is no longer required on most 64-bit Linux systems when using sharp v0.12.0+"
+fi
+
+# OS-specific installations of libopenslide follows
+# Either openslide does not exist, or vips is installed without openslide support
+if [ $enable_openslide -eq 1 ] && [ -z $vips_with_openslide ] && [ $openslide_exists -eq 0 ]; then
+ if [ -f /etc/debian_version ]; then
+ # Debian Linux
+ DISTRO=$(lsb_release -c -s)
+ echo "Detected Debian Linux '$DISTRO'"
+ case "$DISTRO" in
+ jessie|vivid|wily|xenial)
+ # Debian 8, Ubuntu 15
+ echo "Installing libopenslide via apt-get"
+ apt-get install -y libopenslide-dev
+ ;;
+ trusty|utopic|qiana|rebecca|rafaela|freya|rosa|sarah|serena)
+ # Ubuntu 14, Mint 17+
+ echo "Installing libopenslide dependencies via apt-get"
+ apt-get install -y automake build-essential curl zlib1g-dev libopenjpeg-dev libpng12-dev libjpeg-dev libtiff5-dev libgdk-pixbuf2.0-dev libxml2-dev libsqlite3-dev libcairo2-dev libglib2.0-dev sqlite3 libsqlite3-dev
+ install_libopenslide_from_source
+ ;;
+ precise|wheezy|maya)
+ # Debian 7, Ubuntu 12.04, Mint 13
+ echo "Installing libopenslide dependencies via apt-get"
+ apt-get install -y automake build-essential curl zlib1g-dev libopenjpeg-dev libpng12-dev libjpeg-dev libtiff5-dev libgdk-pixbuf2.0-dev libxml2-dev libsqlite3-dev libcairo2-dev libglib2.0-dev sqlite3 libsqlite3-dev
+ install_libopenslide_from_source
+ ;;
+ *)
+ # Unsupported Debian-based OS
+ sorry "openslide" "Debian-based $DISTRO"
+ ;;
+ esac
+ elif [ -f /etc/redhat-release ]; then
+ # Red Hat Linux
+ RELEASE=$(cat /etc/redhat-release)
+ echo "Detected Red Hat Linux '$RELEASE'"
+ case $RELEASE in
+ "Red Hat Enterprise Linux release 7."*|"CentOS Linux release 7."*|"Scientific Linux release 7."*)
+ # RHEL/CentOS 7
+ echo "Installing libopenslide dependencies via yum"
+ yum groupinstall -y "Development Tools"
+ yum install -y tar curl libpng-devel libjpeg-devel libxml2-devel zlib-devel openjpeg-devel libtiff-devel gdk-pixbuf2-devel sqlite-devel cairo-devel glib2-devel
+ install_libopenslide_from_source "--prefix=/usr"
+ ;;
+ "Red Hat Enterprise Linux release 6."*|"CentOS release 6."*|"Scientific Linux release 6."*)
+ # RHEL/CentOS 6
+ echo "Installing libopenslide dependencies via yum"
+ yum groupinstall -y "Development Tools"
+ yum install -y tar curl libpng-devel libjpeg-devel libxml2-devel zlib-devel openjpeg-devel libtiff-devel gdk-pixbuf2-devel sqlite-devel cairo-devel glib2-devel
+ install_libopenslide_from_source "--prefix=/usr"
+ ;;
+ "Fedora release 21 "*|"Fedora release 22 "*)
+ # Fedora 21, 22
+ echo "Installing libopenslide via yum"
+ yum install -y openslide-devel
+ ;;
+ *)
+ # Unsupported RHEL-based OS
+ sorry "openslide" "$RELEASE"
+ ;;
+ esac
+ elif [ -f /etc/os-release ]; then
+ RELEASE=$(cat /etc/os-release | grep VERSION)
+ echo "Detected OpenSuse Linux '$RELEASE'"
+ case $RELEASE in
+ *"13.2"*)
+ echo "Installing libopenslide via zypper"
+ zypper --gpg-auto-import-keys install -y libopenslide-devel
+ ;;
+ esac
+ elif [ -f /etc/SuSE-brand ]; then
+ RELEASE=$(cat /etc/SuSE-brand | grep VERSION)
+ echo "Detected OpenSuse Linux '$RELEASE'"
+ case $RELEASE in
+ *"13.1")
+ echo "Installing libopenslide dependencies via zypper"
+ zypper --gpg-auto-import-keys install -y --type pattern devel_basis
+ zypper --gpg-auto-import-keys install -y tar curl libpng16-devel libjpeg-turbo libjpeg8-devel libxml2-devel zlib-devel openjpeg-devel libtiff-devel libgdk_pixbuf-2_0-0 sqlite3-devel cairo-devel glib2-devel
+ install_libopenslide_from_source
+ ;;
+ esac
+ else
+ # Unsupported OS
+ sorry "openslide" "$(uname -a)"
+ fi
+fi
+
+# OS-specific installations of libvips follows
+
+if [ -f /etc/debian_version ]; then
+ # Debian Linux
+ DISTRO=$(lsb_release -c -s)
+ echo "Detected Debian Linux '$DISTRO'"
+ case "$DISTRO" in
+ jessie|trusty|utopic|vivid|wily|xenial|qiana|rebecca|rafaela|freya|rosa|sarah|serena)
+ # Debian 8, Ubuntu 14.04+, Mint 17+
+ echo "Installing libvips dependencies via apt-get"
+ apt-get install -y automake build-essential gobject-introspection gtk-doc-tools libglib2.0-dev libjpeg-dev libpng12-dev libwebp-dev libtiff5-dev libexif-dev libgsf-1-dev liblcms2-dev libxml2-dev swig libmagickcore-dev curl
+ install_libvips_from_source
+ ;;
+ precise|wheezy|maya)
+ # Debian 7, Ubuntu 12.04, Mint 13
+ echo "Installing libvips dependencies via apt-get"
+ add-apt-repository -y ppa:lyrasis/precise-backports
+ apt-get update
+ apt-get install -y automake build-essential gobject-introspection gtk-doc-tools libglib2.0-dev libjpeg-dev libpng12-dev libwebp-dev libtiff4-dev libexif-dev libgsf-1-dev liblcms2-dev libxml2-dev swig libmagickcore-dev curl
+ install_libvips_from_source
+ ;;
+ *)
+ # Unsupported Debian-based OS
+ sorry "vips" "Debian-based $DISTRO"
+ ;;
+ esac
+elif [ -f /etc/redhat-release ]; then
+ # Red Hat Linux
+ RELEASE=$(cat /etc/redhat-release)
+ echo "Detected Red Hat Linux '$RELEASE'"
+ case $RELEASE in
+ "Red Hat Enterprise Linux release 7."*|"CentOS Linux release 7."*|"Scientific Linux release 7."*)
+ # RHEL/CentOS 7
+ echo "Installing libvips dependencies via yum"
+ yum groupinstall -y "Development Tools"
+ yum install -y tar curl gtk-doc libxml2-devel libjpeg-turbo-devel libpng-devel libtiff-devel libexif-devel libgsf-devel lcms2-devel ImageMagick-devel gobject-introspection-devel libwebp-devel
+ install_libvips_from_source "--prefix=/usr"
+ ;;
+ "Red Hat Enterprise Linux release 6."*|"CentOS release 6."*|"Scientific Linux release 6."*)
+ # RHEL/CentOS 6
+ echo "Installing libvips dependencies via yum"
+ yum groupinstall -y "Development Tools"
+ yum install -y tar curl gtk-doc libxml2-devel libjpeg-turbo-devel libpng-devel libtiff-devel libexif-devel libgsf-devel lcms-devel ImageMagick-devel
+ yum install -y http://li.nux.ro/download/nux/dextop/el6/x86_64/nux-dextop-release-0-2.el6.nux.noarch.rpm
+ yum install -y --enablerepo=nux-dextop gobject-introspection-devel
+ yum install -y http://rpms.famillecollet.com/enterprise/remi-release-6.rpm
+ yum install -y --enablerepo=remi libwebp-devel
+ install_libvips_from_source "--prefix=/usr"
+ ;;
+ "Fedora"*)
+ # Fedora 21, 22, 23
+ echo "Installing libvips dependencies via yum"
+ yum groupinstall -y "Development Tools"
+ yum install -y gcc-c++ gtk-doc libxml2-devel libjpeg-turbo-devel libpng-devel libtiff-devel libexif-devel lcms-devel ImageMagick-devel gobject-introspection-devel libwebp-devel curl
+ install_libvips_from_source "--prefix=/usr"
+ ;;
+ *)
+ # Unsupported RHEL-based OS
+ sorry "vips" "$RELEASE"
+ ;;
+ esac
+elif [ -f /etc/system-release ]; then
+ # Probably Amazon Linux
+ RELEASE=$(cat /etc/system-release)
+ case $RELEASE in
+ "Amazon Linux AMI release 2015.03"|"Amazon Linux AMI release 2015.09")
+ # Amazon Linux
+ echo "Detected '$RELEASE'"
+ echo "Installing libvips dependencies via yum"
+ yum groupinstall -y "Development Tools"
+ yum install -y gtk-doc libxml2-devel libjpeg-turbo-devel libpng-devel libtiff-devel libexif-devel libgsf-devel lcms2-devel ImageMagick-devel gobject-introspection-devel libwebp-devel curl
+ install_libvips_from_source "--prefix=/usr"
+ ;;
+ *)
+ # Unsupported Amazon Linux version
+ sorry "vips" "$RELEASE"
+ ;;
+ esac
+elif [ -f /etc/os-release ]; then
+ RELEASE=$(cat /etc/os-release | grep VERSION)
+ echo "Detected OpenSuse Linux '$RELEASE'"
+ case $RELEASE in
+ *"13.2"*)
+ echo "Installing libvips dependencies via zypper"
+ zypper --gpg-auto-import-keys install -y --type pattern devel_basis
+ zypper --gpg-auto-import-keys install -y tar curl gtk-doc libxml2-devel libjpeg-turbo libjpeg8-devel libpng16-devel libtiff-devel libexif-devel liblcms2-devel ImageMagick-devel gobject-introspection-devel libwebp-devel
+ install_libvips_from_source
+ ;;
+ esac
+elif [ -f /etc/SuSE-brand ]; then
+ RELEASE=$(cat /etc/SuSE-brand | grep VERSION)
+ echo "Detected OpenSuse Linux '$RELEASE'"
+ case $RELEASE in
+ *"13.1")
+ echo "Installing libvips dependencies via zypper"
+ zypper --gpg-auto-import-keys install -y --type pattern devel_basis
+ zypper --gpg-auto-import-keys install -y tar curl gtk-doc libxml2-devel libjpeg-turbo libjpeg8-devel libpng16-devel libtiff-devel libexif-devel liblcms2-devel ImageMagick-devel gobject-introspection-devel libwebp-devel
+ install_libvips_from_source
+ ;;
+ esac
+else
+ # Unsupported OS
+ sorry "vips" "$(uname -a)"
+fi
diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/resize.go b/vendor/src/gopkg.in/h2non/bimg.v1/resize.go
new file mode 100644
index 000000000..93d624805
--- /dev/null
+++ b/vendor/src/gopkg.in/h2non/bimg.v1/resize.go
@@ -0,0 +1,561 @@
+package bimg
+
+/*
+#cgo pkg-config: vips
+#include "vips/vips.h"
+*/
+import "C"
+
+import (
+ "errors"
+ "math"
+)
+
+// Resize is used to transform a given image as byte buffer
+// with the passed options.
+func Resize(buf []byte, o Options) ([]byte, error) {
+ defer C.vips_thread_shutdown()
+
+ image, imageType, err := loadImage(buf)
+
+ if err != nil {
+ return nil, err
+ }
+
+ // Clone and define default options
+ o = applyDefaults(o, imageType)
+
+ if !IsTypeSupported(o.Type) {
+ return nil, errors.New("Unsupported image output type")
+ }
+
+ debug("Options: %#v", o)
+
+ // Auto rotate image based on EXIF orientation header
+ image, rotated, err := rotateAndFlipImage(image, o)
+ if err != nil {
+ return nil, err
+ }
+
+ // If JPEG image, retrieve the buffer
+ if rotated && imageType == JPEG && !o.NoAutoRotate {
+ buf, err = getImageBuffer(image)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ inWidth := int(image.Xsize)
+ inHeight := int(image.Ysize)
+
+ // Infer the required operation based on the in/out image sizes for a coherent transformation
+ normalizeOperation(&o, inWidth, inHeight)
+
+ // image calculations
+ factor := imageCalculations(&o, inWidth, inHeight)
+ shrink := calculateShrink(factor, o.Interpolator)
+ residual := calculateResidual(factor, shrink)
+
+ // Do not enlarge the output if the input width or height
+ // are already less than the required dimensions
+ if !o.Enlarge && !o.Force {
+ if inWidth < o.Width && inHeight < o.Height {
+ factor = 1.0
+ shrink = 1
+ residual = 0
+ o.Width = inWidth
+ o.Height = inHeight
+ }
+ }
+
+ // Try to use libjpeg shrink-on-load
+ if imageType == JPEG && shrink >= 2 {
+ tmpImage, factor, err := shrinkJpegImage(buf, image, factor, shrink)
+ if err != nil {
+ return nil, err
+ }
+
+ image = tmpImage
+ factor = math.Max(factor, 1.0)
+ shrink = int(math.Floor(factor))
+ residual = float64(shrink) / factor
+ }
+
+ // Zoom image, if necessary
+ image, err = zoomImage(image, o.Zoom)
+ if err != nil {
+ return nil, err
+ }
+
+ // Transform image, if necessary
+ if shouldTransformImage(o, inWidth, inHeight) {
+ image, err = transformImage(image, o, shrink, residual)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ // Apply effects, if necessary
+ if shouldApplyEffects(o) {
+ image, err = applyEffects(image, o)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ // Add watermark, if necessary
+ image, err = watermarkImageWithText(image, o.Watermark)
+ if err != nil {
+ return nil, err
+ }
+
+ // Add watermark, if necessary
+ image, err = watermarkImageWithAnotherImage(image, o.WatermarkImage)
+ if err != nil {
+ return nil, err
+ }
+
+ // Flatten image on a background, if necessary
+ image, err = imageFlatten(image, imageType, o)
+ if err != nil {
+ return nil, err
+ }
+
+ return saveImage(image, o)
+}
+
+func loadImage(buf []byte) (*C.VipsImage, ImageType, error) {
+ if len(buf) == 0 {
+ return nil, JPEG, errors.New("Image buffer is empty")
+ }
+
+ image, imageType, err := vipsRead(buf)
+ if err != nil {
+ return nil, JPEG, err
+ }
+
+ return image, imageType, nil
+}
+
+func applyDefaults(o Options, imageType ImageType) Options {
+ if o.Quality == 0 {
+ o.Quality = Quality
+ }
+ if o.Compression == 0 {
+ o.Compression = 6
+ }
+ if o.Type == 0 {
+ o.Type = imageType
+ }
+ if o.Interpretation == 0 {
+ o.Interpretation = InterpretationSRGB
+ }
+ return o
+}
+
+func saveImage(image *C.VipsImage, o Options) ([]byte, error) {
+ saveOptions := vipsSaveOptions{
+ Quality: o.Quality,
+ Type: o.Type,
+ Compression: o.Compression,
+ Interlace: o.Interlace,
+ NoProfile: o.NoProfile,
+ Interpretation: o.Interpretation,
+ }
+ // Finally get the resultant buffer
+ return vipsSave(image, saveOptions)
+}
+
+func normalizeOperation(o *Options, inWidth, inHeight int) {
+ if !o.Force && !o.Crop && !o.Embed && !o.Enlarge && o.Rotate == 0 && (o.Width > 0 || o.Height > 0) {
+ o.Force = true
+ }
+}
+
+func shouldTransformImage(o Options, inWidth, inHeight int) bool {
+ return o.Force || (o.Width > 0 && o.Width != inWidth) ||
+ (o.Height > 0 && o.Height != inHeight) || o.AreaWidth > 0 || o.AreaHeight > 0
+}
+
+func shouldApplyEffects(o Options) bool {
+ return o.GaussianBlur.Sigma > 0 || o.GaussianBlur.MinAmpl > 0 || o.Sharpen.Radius > 0 && o.Sharpen.Y2 > 0 || o.Sharpen.Y3 > 0
+}
+
+func transformImage(image *C.VipsImage, o Options, shrink int, residual float64) (*C.VipsImage, error) {
+ var err error
+ // Use vips_shrink with the integral reduction
+ if shrink > 1 {
+ image, residual, err = shrinkImage(image, o, residual, shrink)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ residualx, residualy := residual, residual
+ if o.Force {
+ residualx = float64(o.Width) / float64(image.Xsize)
+ residualy = float64(o.Height) / float64(image.Ysize)
+ }
+
+ if o.Force || residual != 0 {
+ image, err = vipsAffine(image, residualx, residualy, o.Interpolator)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ if o.Force {
+ o.Crop = false
+ o.Embed = false
+ }
+
+ image, err = extractOrEmbedImage(image, o)
+ if err != nil {
+ return nil, err
+ }
+
+ debug("Transform: shrink=%v, residual=%v, interpolator=%v",
+ shrink, residual, o.Interpolator.String())
+
+ return image, nil
+}
+
+func applyEffects(image *C.VipsImage, o Options) (*C.VipsImage, error) {
+ var err error
+
+ if o.GaussianBlur.Sigma > 0 || o.GaussianBlur.MinAmpl > 0 {
+ image, err = vipsGaussianBlur(image, o.GaussianBlur)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ if o.Sharpen.Radius > 0 && o.Sharpen.Y2 > 0 || o.Sharpen.Y3 > 0 {
+ image, err = vipsSharpen(image, o.Sharpen)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ debug("Effects: gaussSigma=%v, gaussMinAmpl=%v, sharpenRadius=%v",
+ o.GaussianBlur.Sigma, o.GaussianBlur.MinAmpl, o.Sharpen.Radius)
+
+ return image, nil
+}
+
+func extractOrEmbedImage(image *C.VipsImage, o Options) (*C.VipsImage, error) {
+ var err error
+ inWidth := int(image.Xsize)
+ inHeight := int(image.Ysize)
+
+ switch {
+ case o.Gravity == GravitySmart, o.SmartCrop:
+ image, err = vipsSmartCrop(image, o.Width, o.Height)
+ break
+ case o.Crop:
+ width := int(math.Min(float64(inWidth), float64(o.Width)))
+ height := int(math.Min(float64(inHeight), float64(o.Height)))
+ left, top := calculateCrop(inWidth, inHeight, o.Width, o.Height, o.Gravity)
+ left, top = int(math.Max(float64(left), 0)), int(math.Max(float64(top), 0))
+ image, err = vipsExtract(image, left, top, width, height)
+ break
+ case o.Embed:
+ left, top := (o.Width-inWidth)/2, (o.Height-inHeight)/2
+ image, err = vipsEmbed(image, left, top, o.Width, o.Height, o.Extend, o.Background)
+ break
+
+ case o.Top != 0 || o.Left != 0 || o.AreaWidth != 0 || o.AreaHeight != 0:
+ if o.AreaWidth == 0 {
+ o.AreaHeight = o.Width
+ }
+ if o.AreaHeight == 0 {
+ o.AreaHeight = o.Height
+ }
+ if o.AreaWidth == 0 || o.AreaHeight == 0 {
+ return nil, errors.New("Extract area width/height params are required")
+ }
+ image, err = vipsExtract(image, o.Left, o.Top, o.AreaWidth, o.AreaHeight)
+ break
+ }
+
+ return image, err
+}
+
+func rotateAndFlipImage(image *C.VipsImage, o Options) (*C.VipsImage, bool, error) {
+ var err error
+ var rotated bool
+ var direction Direction = -1
+
+ if o.NoAutoRotate == false {
+ rotation, flip := calculateRotationAndFlip(image, o.Rotate)
+ if flip {
+ o.Flip = flip
+ }
+ if rotation > 0 && o.Rotate == 0 {
+ o.Rotate = rotation
+ }
+ }
+
+ if o.Rotate > 0 {
+ rotated = true
+ image, err = vipsRotate(image, getAngle(o.Rotate))
+ }
+
+ if o.Flip {
+ direction = Horizontal
+ } else if o.Flop {
+ direction = Vertical
+ }
+
+ if direction != -1 {
+ rotated = true
+ image, err = vipsFlip(image, direction)
+ }
+
+ return image, rotated, err
+}
+
+func watermarkImageWithText(image *C.VipsImage, w Watermark) (*C.VipsImage, error) {
+ if w.Text == "" {
+ return image, nil
+ }
+
+ // Defaults
+ if w.Font == "" {
+ w.Font = WatermarkFont
+ }
+ if w.Width == 0 {
+ w.Width = int(math.Floor(float64(image.Xsize / 6)))
+ }
+ if w.DPI == 0 {
+ w.DPI = 150
+ }
+ if w.Margin == 0 {
+ w.Margin = w.Width
+ }
+ if w.Opacity == 0 {
+ w.Opacity = 0.25
+ } else if w.Opacity > 1 {
+ w.Opacity = 1
+ }
+
+ image, err := vipsWatermark(image, w)
+ if err != nil {
+ return nil, err
+ }
+
+ return image, nil
+}
+
+func watermarkImageWithAnotherImage(image *C.VipsImage, w WatermarkImage) (*C.VipsImage, error) {
+
+ if len(w.Buf) == 0 {
+ return image, nil
+ }
+
+ if w.Opacity == 0.0 {
+ w.Opacity = 1.0
+ }
+
+ image, err := vipsDrawWatermark(image, w)
+
+ if err != nil {
+ return nil, err
+ }
+
+ return image, nil
+}
+
+func imageFlatten(image *C.VipsImage, imageType ImageType, o Options) (*C.VipsImage, error) {
+ // Only PNG images are supported for now
+ if imageType != PNG || o.Background == ColorBlack {
+ return image, nil
+ }
+ return vipsFlattenBackground(image, o.Background)
+}
+
+func zoomImage(image *C.VipsImage, zoom int) (*C.VipsImage, error) {
+ if zoom == 0 {
+ return image, nil
+ }
+ return vipsZoom(image, zoom+1)
+}
+
+func shrinkImage(image *C.VipsImage, o Options, residual float64, shrink int) (*C.VipsImage, float64, error) {
+ // Use vips_shrink with the integral reduction
+ image, err := vipsShrink(image, shrink)
+ if err != nil {
+ return nil, 0, err
+ }
+
+ // Recalculate residual float based on dimensions of required vs shrunk images
+ residualx := float64(o.Width) / float64(image.Xsize)
+ residualy := float64(o.Height) / float64(image.Ysize)
+
+ if o.Crop {
+ residual = math.Max(residualx, residualy)
+ } else {
+ residual = math.Min(residualx, residualy)
+ }
+
+ return image, residual, nil
+}
+
+func shrinkJpegImage(buf []byte, input *C.VipsImage, factor float64, shrink int) (*C.VipsImage, float64, error) {
+ var image *C.VipsImage
+ var err error
+ shrinkOnLoad := 1
+
+ // Recalculate integral shrink and double residual
+ switch {
+ case shrink >= 8:
+ factor = factor / 8
+ shrinkOnLoad = 8
+ case shrink >= 4:
+ factor = factor / 4
+ shrinkOnLoad = 4
+ case shrink >= 2:
+ factor = factor / 2
+ shrinkOnLoad = 2
+ }
+
+ // Reload input using shrink-on-load
+ if shrinkOnLoad > 1 {
+ image, err = vipsShrinkJpeg(buf, input, shrinkOnLoad)
+ }
+
+ return image, factor, err
+}
+
+func imageCalculations(o *Options, inWidth, inHeight int) float64 {
+ factor := 1.0
+ xfactor := float64(inWidth) / float64(o.Width)
+ yfactor := float64(inHeight) / float64(o.Height)
+
+ switch {
+ // Fixed width and height
+ case o.Width > 0 && o.Height > 0:
+ if o.Crop {
+ factor = math.Min(xfactor, yfactor)
+ } else {
+ factor = math.Max(xfactor, yfactor)
+ }
+ // Fixed width, auto height
+ case o.Width > 0:
+ if o.Crop {
+ o.Height = inHeight
+ } else {
+ factor = xfactor
+ o.Height = roundFloat(float64(inHeight) / factor)
+ }
+ // Fixed height, auto width
+ case o.Height > 0:
+ if o.Crop {
+ o.Width = inWidth
+ } else {
+ factor = yfactor
+ o.Width = roundFloat(float64(inWidth) / factor)
+ }
+ // Identity transform
+ default:
+ o.Width = inWidth
+ o.Height = inHeight
+ break
+ }
+
+ return factor
+}
+
+func roundFloat(f float64) int {
+ if f < 0 {
+ return int(math.Ceil(f - 0.5))
+ }
+ return int(math.Floor(f + 0.5))
+}
+
+func calculateCrop(inWidth, inHeight, outWidth, outHeight int, gravity Gravity) (int, int) {
+ left, top := 0, 0
+
+ switch gravity {
+ case GravityNorth:
+ left = (inWidth - outWidth + 1) / 2
+ case GravityEast:
+ left = inWidth - outWidth
+ top = (inHeight - outHeight + 1) / 2
+ case GravitySouth:
+ left = (inWidth - outWidth + 1) / 2
+ top = inHeight - outHeight
+ case GravityWest:
+ top = (inHeight - outHeight + 1) / 2
+ default:
+ left = (inWidth - outWidth + 1) / 2
+ top = (inHeight - outHeight + 1) / 2
+ }
+
+ return left, top
+}
+
+func calculateRotationAndFlip(image *C.VipsImage, angle Angle) (Angle, bool) {
+ rotate := D0
+ flip := false
+
+ if angle > 0 {
+ return rotate, flip
+ }
+
+ switch vipsExifOrientation(image) {
+ case 6:
+ rotate = D90
+ break
+ case 3:
+ rotate = D180
+ break
+ case 8:
+ rotate = D270
+ break
+ case 2:
+ flip = true
+ break // flip 1
+ case 7:
+ flip = true
+ rotate = D90
+ break // flip 6
+ case 4:
+ flip = true
+ rotate = D180
+ break // flip 3
+ case 5:
+ flip = true
+ rotate = D270
+ break // flip 8
+ }
+
+ return rotate, flip
+}
+
+func calculateShrink(factor float64, i Interpolator) int {
+ var shrink float64
+
+ // Calculate integral box shrink
+ windowSize := vipsWindowSize(i.String())
+ if factor >= 2 && windowSize > 3 {
+ // Shrink less, affine more with interpolators that use at least 4x4 pixel window, e.g. bicubic
+ shrink = float64(math.Floor(factor * 3.0 / windowSize))
+ } else {
+ shrink = math.Floor(factor)
+ }
+
+ return int(math.Max(shrink, 1))
+}
+
+func calculateResidual(factor float64, shrink int) float64 {
+ return float64(shrink) / factor
+}
+
+func getAngle(angle Angle) Angle {
+ divisor := angle % 90
+ if divisor != 0 {
+ angle = angle - divisor
+ }
+ return Angle(math.Min(float64(angle), 270))
+}
diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/resize_test.go b/vendor/src/gopkg.in/h2non/bimg.v1/resize_test.go
new file mode 100644
index 000000000..28e310954
--- /dev/null
+++ b/vendor/src/gopkg.in/h2non/bimg.v1/resize_test.go
@@ -0,0 +1,644 @@
+package bimg
+
+import (
+ "bytes"
+ "crypto/md5"
+ "image"
+ "image/jpeg"
+ "io/ioutil"
+ "os"
+ "path"
+ "strconv"
+ "testing"
+)
+
+func TestResize(t *testing.T) {
+ options := Options{Width: 800, Height: 600}
+ buf, _ := Read("fixtures/test.jpg")
+
+ newImg, err := Resize(buf, options)
+ if err != nil {
+ t.Errorf("Resize(imgData, %#v) error: %#v", options, err)
+ }
+
+ if DetermineImageType(newImg) != JPEG {
+ t.Fatal("Image is not jpeg")
+ }
+
+ size, _ := Size(newImg)
+ if size.Height != options.Height || size.Width != options.Width {
+ t.Fatalf("Invalid image size: %dx%d", size.Width, size.Height)
+ }
+
+ Write("fixtures/test_out.jpg", newImg)
+}
+
+func TestResizeVerticalImage(t *testing.T) {
+ tests := []struct {
+ format ImageType
+ options Options
+ }{
+ {JPEG, Options{Width: 800, Height: 600}},
+ {JPEG, Options{Width: 1000, Height: 1000}},
+ {JPEG, Options{Width: 1000, Height: 1500}},
+ {JPEG, Options{Width: 1000}},
+ {JPEG, Options{Height: 1500}},
+ {JPEG, Options{Width: 100, Height: 50}},
+ {JPEG, Options{Width: 2000, Height: 2000}},
+ {JPEG, Options{Width: 500, Height: 1000}},
+ {JPEG, Options{Width: 500}},
+ {JPEG, Options{Height: 500}},
+ {JPEG, Options{Crop: true, Width: 500, Height: 1000}},
+ {JPEG, Options{Crop: true, Enlarge: true, Width: 2000, Height: 1400}},
+ {JPEG, Options{Enlarge: true, Force: true, Width: 2000, Height: 2000}},
+ {JPEG, Options{Force: true, Width: 2000, Height: 2000}},
+ }
+
+ buf, _ := Read("fixtures/vertical.jpg")
+ for _, test := range tests {
+ image, err := Resize(buf, test.options)
+ if err != nil {
+ t.Errorf("Resize(imgData, %#v) error: %#v", test.options, err)
+ }
+
+ if DetermineImageType(image) != test.format {
+ t.Fatalf("Image format is invalid. Expected: %#v", test.format)
+ }
+
+ size, _ := Size(image)
+ if test.options.Height > 0 && size.Height != test.options.Height {
+ t.Fatalf("Invalid height: %d", size.Height)
+ }
+ if test.options.Width > 0 && size.Width != test.options.Width {
+ t.Fatalf("Invalid width: %d", size.Width)
+ }
+
+ Write("fixtures/test_vertical_"+strconv.Itoa(test.options.Width)+"x"+strconv.Itoa(test.options.Height)+"_out.jpg", image)
+ }
+}
+
+func TestResizeCustomSizes(t *testing.T) {
+ tests := []struct {
+ format ImageType
+ options Options
+ }{
+ {JPEG, Options{Width: 800, Height: 600}},
+ {JPEG, Options{Width: 1000, Height: 1000}},
+ {JPEG, Options{Width: 100, Height: 50}},
+ {JPEG, Options{Width: 2000, Height: 2000}},
+ {JPEG, Options{Width: 500, Height: 1000}},
+ {JPEG, Options{Width: 500}},
+ {JPEG, Options{Height: 500}},
+ {JPEG, Options{Crop: true, Width: 500, Height: 1000}},
+ {JPEG, Options{Crop: true, Enlarge: true, Width: 2000, Height: 1400}},
+ {JPEG, Options{Enlarge: true, Force: true, Width: 2000, Height: 2000}},
+ {JPEG, Options{Force: true, Width: 2000, Height: 2000}},
+ }
+
+ buf, _ := Read("fixtures/test.jpg")
+ for _, test := range tests {
+ image, err := Resize(buf, test.options)
+ if err != nil {
+ t.Errorf("Resize(imgData, %#v) error: %#v", test.options, err)
+ }
+
+ if DetermineImageType(image) != test.format {
+ t.Fatalf("Image format is invalid. Expected: %#v", test.format)
+ }
+
+ size, _ := Size(image)
+ if test.options.Height > 0 && size.Height != test.options.Height {
+ t.Fatalf("Invalid height: %d", size.Height)
+ }
+ if test.options.Width > 0 && size.Width != test.options.Width {
+ t.Fatalf("Invalid width: %d", size.Width)
+ }
+ }
+}
+
+func TestResizePrecision(t *testing.T) {
+ // see https://github.com/h2non/bimg/issues/99
+ img := image.NewGray16(image.Rect(0, 0, 1920, 1080))
+ input := &bytes.Buffer{}
+ jpeg.Encode(input, img, nil)
+
+ opts := Options{Width: 300}
+ newImg, err := Resize(input.Bytes(), opts)
+ if err != nil {
+ t.Fatalf("Resize(imgData, %#v) error: %#v", opts, err)
+ }
+
+ size, _ := Size(newImg)
+ if size.Width != opts.Width {
+ t.Fatalf("Invalid width: %d", size.Width)
+ }
+}
+
+func TestRotate(t *testing.T) {
+ options := Options{Width: 800, Height: 600, Rotate: 270, Crop: true}
+ buf, _ := Read("fixtures/test.jpg")
+
+ newImg, err := Resize(buf, options)
+ if err != nil {
+ t.Errorf("Resize(imgData, %#v) error: %#v", options, err)
+ }
+
+ if DetermineImageType(newImg) != JPEG {
+ t.Error("Image is not jpeg")
+ }
+
+ size, _ := Size(newImg)
+ if size.Width != options.Width || size.Height != options.Height {
+ t.Errorf("Invalid image size: %dx%d", size.Width, size.Height)
+ }
+
+ Write("fixtures/test_rotate_out.jpg", newImg)
+}
+
+func TestInvalidRotateDegrees(t *testing.T) {
+ options := Options{Width: 800, Height: 600, Rotate: 111, Crop: true}
+ buf, _ := Read("fixtures/test.jpg")
+
+ newImg, err := Resize(buf, options)
+ if err != nil {
+ t.Errorf("Resize(imgData, %#v) error: %#v", options, err)
+ }
+
+ if DetermineImageType(newImg) != JPEG {
+ t.Errorf("Image is not jpeg")
+ }
+
+ size, _ := Size(newImg)
+ if size.Width != options.Width || size.Height != options.Height {
+ t.Errorf("Invalid image size: %dx%d", size.Width, size.Height)
+ }
+
+ Write("fixtures/test_rotate_invalid_out.jpg", newImg)
+}
+
+func TestCorruptedImage(t *testing.T) {
+ options := Options{Width: 800, Height: 600}
+ buf, _ := Read("fixtures/corrupt.jpg")
+
+ newImg, err := Resize(buf, options)
+ if err != nil {
+ t.Errorf("Resize(imgData, %#v) error: %#v", options, err)
+ }
+
+ if DetermineImageType(newImg) != JPEG {
+ t.Fatal("Image is not jpeg")
+ }
+
+ size, _ := Size(newImg)
+ if size.Height != options.Height || size.Width != options.Width {
+ t.Fatalf("Invalid image size: %dx%d", size.Width, size.Height)
+ }
+
+ Write("fixtures/test_corrupt_out.jpg", newImg)
+}
+
+func TestNoColorProfile(t *testing.T) {
+ options := Options{Width: 800, Height: 600, NoProfile: true}
+ buf, _ := Read("fixtures/test.jpg")
+
+ newImg, err := Resize(buf, options)
+ if err != nil {
+ t.Errorf("Resize(imgData, %#v) error: %#v", options, err)
+ }
+
+ metadata, err := Metadata(newImg)
+ if metadata.Profile == true {
+ t.Fatal("Invalid profile data")
+ }
+
+ size, _ := Size(newImg)
+ if size.Height != options.Height || size.Width != options.Width {
+ t.Fatalf("Invalid image size: %dx%d", size.Width, size.Height)
+ }
+}
+
+func TestEmbedExtendColor(t *testing.T) {
+ options := Options{Width: 400, Height: 600, Crop: false, Embed: true, Extend: ExtendWhite, Background: Color{255, 20, 10}}
+ buf, _ := Read("fixtures/test_issue.jpg")
+
+ newImg, err := Resize(buf, options)
+ if err != nil {
+ t.Errorf("Resize(imgData, %#v) error: %#v", options, err)
+ }
+
+ size, _ := Size(newImg)
+ if size.Height != options.Height || size.Width != options.Width {
+ t.Fatalf("Invalid image size: %dx%d", size.Width, size.Height)
+ }
+
+ Write("fixtures/test_extend_white_out.jpg", newImg)
+}
+
+func TestEmbedExtendWithCustomColor(t *testing.T) {
+ options := Options{Width: 400, Height: 600, Crop: false, Embed: true, Extend: 5, Background: Color{255, 20, 10}}
+ buf, _ := Read("fixtures/test_issue.jpg")
+
+ newImg, err := Resize(buf, options)
+ if err != nil {
+ t.Errorf("Resize(imgData, %#v) error: %#v", options, err)
+ }
+
+ size, _ := Size(newImg)
+ if size.Height != options.Height || size.Width != options.Width {
+ t.Fatalf("Invalid image size: %dx%d", size.Width, size.Height)
+ }
+
+ Write("fixtures/test_extend_background_out.jpg", newImg)
+}
+
+func TestGaussianBlur(t *testing.T) {
+ options := Options{Width: 800, Height: 600, GaussianBlur: GaussianBlur{Sigma: 5}}
+ buf, _ := Read("fixtures/test.jpg")
+
+ newImg, err := Resize(buf, options)
+ if err != nil {
+ t.Errorf("Resize(imgData, %#v) error: %#v", options, err)
+ }
+
+ size, _ := Size(newImg)
+ if size.Height != options.Height || size.Width != options.Width {
+ t.Fatalf("Invalid image size: %dx%d", size.Width, size.Height)
+ }
+
+ Write("fixtures/test_gaussian_out.jpg", newImg)
+}
+
+func TestSharpen(t *testing.T) {
+ options := Options{Width: 800, Height: 600, Sharpen: Sharpen{Radius: 1, X1: 1.5, Y2: 20, Y3: 50, M1: 1, M2: 2}}
+ buf, _ := Read("fixtures/test.jpg")
+
+ newImg, err := Resize(buf, options)
+ if err != nil {
+ t.Errorf("Resize(imgData, %#v) error: %#v", options, err)
+ }
+
+ size, _ := Size(newImg)
+ if size.Height != options.Height || size.Width != options.Width {
+ t.Fatalf("Invalid image size: %dx%d", size.Width, size.Height)
+ }
+
+ Write("fixtures/test_sharpen_out.jpg", newImg)
+}
+
+func TestExtractWithDefaultAxis(t *testing.T) {
+ options := Options{AreaWidth: 200, AreaHeight: 200}
+ buf, _ := Read("fixtures/test.jpg")
+
+ newImg, err := Resize(buf, options)
+ if err != nil {
+ t.Errorf("Resize(imgData, %#v) error: %#v", options, err)
+ }
+
+ size, _ := Size(newImg)
+ if size.Height != options.AreaHeight || size.Width != options.AreaWidth {
+ t.Fatalf("Invalid image size: %dx%d", size.Width, size.Height)
+ }
+
+ Write("fixtures/test_extract_defaults_out.jpg", newImg)
+}
+
+func TestExtractCustomAxis(t *testing.T) {
+ options := Options{Top: 100, Left: 100, AreaWidth: 200, AreaHeight: 200}
+ buf, _ := Read("fixtures/test.jpg")
+
+ newImg, err := Resize(buf, options)
+ if err != nil {
+ t.Errorf("Resize(imgData, %#v) error: %#v", options, err)
+ }
+
+ size, _ := Size(newImg)
+ if size.Height != options.AreaHeight || size.Width != options.AreaWidth {
+ t.Fatalf("Invalid image size: %dx%d", size.Width, size.Height)
+ }
+
+ Write("fixtures/test_extract_custom_axis_out.jpg", newImg)
+}
+
+func TestConvert(t *testing.T) {
+ width, height := 300, 240
+ formats := [3]ImageType{PNG, WEBP, JPEG}
+
+ files := []string{
+ "test.jpg",
+ "test.png",
+ "test.webp",
+ }
+
+ for _, file := range files {
+ img, err := os.Open("fixtures/" + file)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ buf, err := ioutil.ReadAll(img)
+ if err != nil {
+ t.Fatal(err)
+ }
+ img.Close()
+
+ for _, format := range formats {
+ options := Options{Width: width, Height: height, Crop: true, Type: format}
+
+ newImg, err := Resize(buf, options)
+ if err != nil {
+ t.Errorf("Resize(imgData, %#v) error: %#v", options, err)
+ }
+
+ if DetermineImageType(newImg) != format {
+ t.Fatal("Image is not png")
+ }
+
+ size, _ := Size(newImg)
+ if size.Height != height || size.Width != width {
+ t.Fatalf("Invalid image size: %dx%d", size.Width, size.Height)
+ }
+ }
+ }
+}
+
+func TestResizePngWithTransparency(t *testing.T) {
+ width, height := 300, 240
+
+ options := Options{Width: width, Height: height, Crop: true}
+ img, err := os.Open("fixtures/transparent.png")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer img.Close()
+
+ buf, err := ioutil.ReadAll(img)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ newImg, err := Resize(buf, options)
+ if err != nil {
+ t.Errorf("Resize(imgData, %#v) error: %#v", options, err)
+ }
+
+ if DetermineImageType(newImg) != PNG {
+ t.Fatal("Image is not png")
+ }
+
+ size, _ := Size(newImg)
+ if size.Height != height || size.Width != width {
+ t.Fatal("Invalid image size")
+ }
+
+ Write("fixtures/transparent_out.png", newImg)
+}
+
+func TestIfBothSmartCropOptionsAreIdentical(t *testing.T) {
+ if !(VipsMajorVersion >= 8 && VipsMinorVersion > 4) {
+ t.Skipf("Skipping this test, libvips doesn't meet version requirement %s > 8.4", VipsVersion)
+ }
+
+ benchmarkOptions := Options{Width: 100, Height: 100, Crop: true}
+ smartCropOptions := Options{Width: 100, Height: 100, Crop: true, SmartCrop: true}
+ gravityOptions := Options{Width: 100, Height: 100, Crop: true, Gravity: GravitySmart}
+
+ testImg, err := os.Open("fixtures/northern_cardinal_bird.jpg")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer testImg.Close()
+
+ testImgByte, err := ioutil.ReadAll(testImg)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ scImg, err := Resize(testImgByte, smartCropOptions)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ gImg, err := Resize(testImgByte, gravityOptions)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ benchmarkImg, err := Resize(testImgByte, benchmarkOptions)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ sch, gh, bh := md5.Sum(scImg), md5.Sum(gImg), md5.Sum(benchmarkImg)
+ if gh == bh || sch == bh {
+ t.Error("Expected both options produce a different result from a standard crop.")
+ }
+
+ if sch != gh {
+ t.Errorf("Expected both options to result in the same output, %x != %x", sch, gh)
+ }
+}
+
+func runBenchmarkResize(file string, o Options, b *testing.B) {
+ buf, _ := Read(path.Join("fixtures", file))
+
+ for n := 0; n < b.N; n++ {
+ Resize(buf, o)
+ }
+}
+
+func BenchmarkRotateJpeg(b *testing.B) {
+ options := Options{Rotate: 180}
+ runBenchmarkResize("test.jpg", options, b)
+}
+
+func BenchmarkResizeLargeJpeg(b *testing.B) {
+ options := Options{
+ Width: 800,
+ Height: 600,
+ }
+ runBenchmarkResize("test.jpg", options, b)
+}
+
+func BenchmarkResizePng(b *testing.B) {
+ options := Options{
+ Width: 200,
+ Height: 200,
+ }
+ runBenchmarkResize("test.png", options, b)
+}
+
+func BenchmarkResizeWebP(b *testing.B) {
+ options := Options{
+ Width: 200,
+ Height: 200,
+ }
+ runBenchmarkResize("test.webp", options, b)
+}
+
+func BenchmarkConvertToJpeg(b *testing.B) {
+ options := Options{Type: JPEG}
+ runBenchmarkResize("test.png", options, b)
+}
+
+func BenchmarkConvertToPng(b *testing.B) {
+ options := Options{Type: PNG}
+ runBenchmarkResize("test.jpg", options, b)
+}
+
+func BenchmarkConvertToWebp(b *testing.B) {
+ options := Options{Type: WEBP}
+ runBenchmarkResize("test.jpg", options, b)
+}
+
+func BenchmarkCropJpeg(b *testing.B) {
+ options := Options{
+ Width: 800,
+ Height: 600,
+ }
+ runBenchmarkResize("test.jpg", options, b)
+}
+
+func BenchmarkCropPng(b *testing.B) {
+ options := Options{
+ Width: 800,
+ Height: 600,
+ }
+ runBenchmarkResize("test.png", options, b)
+}
+
+func BenchmarkCropWebP(b *testing.B) {
+ options := Options{
+ Width: 800,
+ Height: 600,
+ }
+ runBenchmarkResize("test.webp", options, b)
+}
+
+func BenchmarkExtractJpeg(b *testing.B) {
+ options := Options{
+ Top: 100,
+ Left: 50,
+ AreaWidth: 600,
+ AreaHeight: 480,
+ }
+ runBenchmarkResize("test.jpg", options, b)
+}
+
+func BenchmarkExtractPng(b *testing.B) {
+ options := Options{
+ Top: 100,
+ Left: 50,
+ AreaWidth: 600,
+ AreaHeight: 480,
+ }
+ runBenchmarkResize("test.png", options, b)
+}
+
+func BenchmarkExtractWebp(b *testing.B) {
+ options := Options{
+ Top: 100,
+ Left: 50,
+ AreaWidth: 600,
+ AreaHeight: 480,
+ }
+ runBenchmarkResize("test.webp", options, b)
+}
+
+func BenchmarkZoomJpeg(b *testing.B) {
+ options := Options{Zoom: 1}
+ runBenchmarkResize("test.jpg", options, b)
+}
+
+func BenchmarkZoomPng(b *testing.B) {
+ options := Options{Zoom: 1}
+ runBenchmarkResize("test.png", options, b)
+}
+
+func BenchmarkZoomWebp(b *testing.B) {
+ options := Options{Zoom: 1}
+ runBenchmarkResize("test.webp", options, b)
+}
+
+func BenchmarkWatermarkJpeg(b *testing.B) {
+ options := Options{
+ Watermark: Watermark{
+ Text: "Chuck Norris (c) 2315",
+ Opacity: 0.25,
+ Width: 200,
+ DPI: 100,
+ Margin: 150,
+ Font: "sans bold 12",
+ Background: Color{255, 255, 255},
+ },
+ }
+ runBenchmarkResize("test.jpg", options, b)
+}
+
+func BenchmarkWatermarPng(b *testing.B) {
+ options := Options{
+ Watermark: Watermark{
+ Text: "Chuck Norris (c) 2315",
+ Opacity: 0.25,
+ Width: 200,
+ DPI: 100,
+ Margin: 150,
+ Font: "sans bold 12",
+ Background: Color{255, 255, 255},
+ },
+ }
+ runBenchmarkResize("test.png", options, b)
+}
+
+func BenchmarkWatermarWebp(b *testing.B) {
+ options := Options{
+ Watermark: Watermark{
+ Text: "Chuck Norris (c) 2315",
+ Opacity: 0.25,
+ Width: 200,
+ DPI: 100,
+ Margin: 150,
+ Font: "sans bold 12",
+ Background: Color{255, 255, 255},
+ },
+ }
+ runBenchmarkResize("test.webp", options, b)
+}
+
+func BenchmarkWatermarkImageJpeg(b *testing.B) {
+ watermark := readFile("transparent.png")
+ options := Options{
+ WatermarkImage: WatermarkImage{
+ Buf: watermark,
+ Opacity: 0.25,
+ Left: 100,
+ Top: 100,
+ },
+ }
+ runBenchmarkResize("test.jpg", options, b)
+}
+
+func BenchmarkWatermarImagePng(b *testing.B) {
+ watermark := readFile("transparent.png")
+ options := Options{
+ WatermarkImage: WatermarkImage{
+ Buf: watermark,
+ Opacity: 0.25,
+ Left: 100,
+ Top: 100,
+ },
+ }
+ runBenchmarkResize("test.png", options, b)
+}
+
+func BenchmarkWatermarImageWebp(b *testing.B) {
+ watermark := readFile("transparent.png")
+ options := Options{
+ WatermarkImage: WatermarkImage{
+ Buf: watermark,
+ Opacity: 0.25,
+ Left: 100,
+ Top: 100,
+ },
+ }
+ runBenchmarkResize("test.webp", options, b)
+}
diff --git a/vendor/src/gopkg.in/h2non/bimg.v1/type.go b/vendor/src/gopkg.in/h2non/bimg.v1/type.go
new file mode 100644
index 000000000..260adbf81
--- /dev/null
+++ b/vendor/src/gopkg.in/h2non/bimg.v1/type.go
@@ -0,0 +1,172 @@
+package bimg
+
+import (
+ "regexp"
+ "sync"
+ "unicode/utf8"
+)
+
+const (
+ // UNKNOWN represents an unknow image type value.
+ UNKNOWN ImageType = iota
+ // JPEG represents the JPEG image type.
+ JPEG
+ // WEBP represents the WEBP image type.
+ WEBP
+ // PNG represents the PNG image type.
+ PNG
+ // TIFF represents the TIFF image type.
+ TIFF
+ // GIF represents the GIF image type.
+ GIF
+ // PDF represents the PDF type.
+ PDF
+ // SVG represents the SVG image type.
+ SVG
+ // MAGICK represents the libmagick compatible genetic image type.
+ MAGICK
+)
+
+// ImageType represents an image type value.
+type ImageType int
+
+var (
+ htmlCommentRegex = regexp.MustCompile("(?i)")
+ svgRegex = regexp.MustCompile(`(?i)^\s*(?:<\?xml[^>]*>\s*)?(?:]*>\s*)?