diff --git a/vendor/manifest b/vendor/manifest
index 6e6e24640..edd1c12b0 100644
--- a/vendor/manifest
+++ b/vendor/manifest
@@ -114,6 +114,12 @@
 			"branch": "master",
 			"path": "/pbutil"
 		},
+		{
+			"importpath": "github.com/nfnt/resize",
+			"repository": "https://github.com/nfnt/resize",
+			"revision": "891127d8d1b52734debe1b3c3d7e747502b6c366",
+			"branch": "master"
+		},
 		{
 			"importpath": "github.com/pierrec/lz4",
 			"repository": "https://github.com/pierrec/lz4",
diff --git a/vendor/src/github.com/nfnt/resize/LICENSE b/vendor/src/github.com/nfnt/resize/LICENSE
new file mode 100644
index 000000000..7836cad5f
--- /dev/null
+++ b/vendor/src/github.com/nfnt/resize/LICENSE
@@ -0,0 +1,13 @@
+Copyright (c) 2012, Jan Schlicht <jan.schlicht@gmail.com>
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
diff --git a/vendor/src/github.com/nfnt/resize/README.md b/vendor/src/github.com/nfnt/resize/README.md
new file mode 100644
index 000000000..2aefa75c9
--- /dev/null
+++ b/vendor/src/github.com/nfnt/resize/README.md
@@ -0,0 +1,149 @@
+Resize
+======
+
+Image resizing for the [Go programming language](http://golang.org) with common interpolation methods.
+
+[![Build Status](https://travis-ci.org/nfnt/resize.svg)](https://travis-ci.org/nfnt/resize)
+
+Installation
+------------
+
+```bash
+$ go get github.com/nfnt/resize
+```
+
+It's that easy!
+
+Usage
+-----
+
+This package needs at least Go 1.1. Import package with
+
+```go
+import "github.com/nfnt/resize"
+```
+
+The resize package provides 2 functions:
+
+* `resize.Resize` creates a scaled image with new dimensions (`width`, `height`) using the interpolation function `interp`.
+  If either `width` or `height` is set to 0, it will be set to an aspect ratio preserving value.
+* `resize.Thumbnail` downscales an image preserving its aspect ratio to the maximum dimensions (`maxWidth`, `maxHeight`).
+  It will return the original image if original sizes are smaller than the provided dimensions.
+
+```go
+resize.Resize(width, height uint, img image.Image, interp resize.InterpolationFunction) image.Image
+resize.Thumbnail(maxWidth, maxHeight uint, img image.Image, interp resize.InterpolationFunction) image.Image
+```
+
+The provided interpolation functions are (from fast to slow execution time)
+
+- `NearestNeighbor`: [Nearest-neighbor interpolation](http://en.wikipedia.org/wiki/Nearest-neighbor_interpolation)
+- `Bilinear`: [Bilinear interpolation](http://en.wikipedia.org/wiki/Bilinear_interpolation)
+- `Bicubic`: [Bicubic interpolation](http://en.wikipedia.org/wiki/Bicubic_interpolation)
+- `MitchellNetravali`: [Mitchell-Netravali interpolation](http://dl.acm.org/citation.cfm?id=378514)
+- `Lanczos2`: [Lanczos resampling](http://en.wikipedia.org/wiki/Lanczos_resampling) with a=2
+- `Lanczos3`: [Lanczos resampling](http://en.wikipedia.org/wiki/Lanczos_resampling) with a=3
+
+Which of these methods gives the best results depends on your use case.
+
+Sample usage:
+
+```go
+package main
+
+import (
+	"github.com/nfnt/resize"
+	"image/jpeg"
+	"log"
+	"os"
+)
+
+func main() {
+	// open "test.jpg"
+	file, err := os.Open("test.jpg")
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	// decode jpeg into image.Image
+	img, err := jpeg.Decode(file)
+	if err != nil {
+		log.Fatal(err)
+	}
+	file.Close()
+
+	// resize to width 1000 using Lanczos resampling
+	// and preserve aspect ratio
+	m := resize.Resize(1000, 0, img, resize.Lanczos3)
+
+	out, err := os.Create("test_resized.jpg")
+	if err != nil {
+		log.Fatal(err)
+	}
+	defer out.Close()
+
+	// write new image to file
+	jpeg.Encode(out, m, nil)
+}
+```
+
+Caveats
+-------
+
+* Optimized access routines are used for `image.RGBA`, `image.NRGBA`, `image.RGBA64`, `image.NRGBA64`, `image.YCbCr`, `image.Gray`, and `image.Gray16` types. All other image types are accessed in a generic way that will result in slow processing speed.
+* JPEG images are stored in `image.YCbCr`. This image format stores data in a way that will decrease processing speed. A resize may be up to 2 times slower than with `image.RGBA`. 
+
+
+Downsizing Samples
+-------
+
+Downsizing is not as simple as it might look like. Images have to be filtered before they are scaled down, otherwise aliasing might occur.
+Filtering is highly subjective: Applying too much will blur the whole image, too little will make aliasing become apparent.
+Resize tries to provide sane defaults that should suffice in most cases.
+
+### Artificial sample
+
+Original image
+![Rings](http://nfnt.github.com/img/rings_lg_orig.png)
+
+<table>
+<tr>
+<th><img src="http://nfnt.github.com/img/rings_300_NearestNeighbor.png" /><br>Nearest-Neighbor</th>
+<th><img src="http://nfnt.github.com/img/rings_300_Bilinear.png" /><br>Bilinear</th>
+</tr>
+<tr>
+<th><img src="http://nfnt.github.com/img/rings_300_Bicubic.png" /><br>Bicubic</th>
+<th><img src="http://nfnt.github.com/img/rings_300_MitchellNetravali.png" /><br>Mitchell-Netravali</th>
+</tr>
+<tr>
+<th><img src="http://nfnt.github.com/img/rings_300_Lanczos2.png" /><br>Lanczos2</th>
+<th><img src="http://nfnt.github.com/img/rings_300_Lanczos3.png" /><br>Lanczos3</th>
+</tr>
+</table>
+
+### Real-Life sample
+
+Original image  
+![Original](http://nfnt.github.com/img/IMG_3694_720.jpg)
+
+<table>
+<tr>
+<th><img src="http://nfnt.github.com/img/IMG_3694_300_NearestNeighbor.png" /><br>Nearest-Neighbor</th>
+<th><img src="http://nfnt.github.com/img/IMG_3694_300_Bilinear.png" /><br>Bilinear</th>
+</tr>
+<tr>
+<th><img src="http://nfnt.github.com/img/IMG_3694_300_Bicubic.png" /><br>Bicubic</th>
+<th><img src="http://nfnt.github.com/img/IMG_3694_300_MitchellNetravali.png" /><br>Mitchell-Netravali</th>
+</tr>
+<tr>
+<th><img src="http://nfnt.github.com/img/IMG_3694_300_Lanczos2.png" /><br>Lanczos2</th>
+<th><img src="http://nfnt.github.com/img/IMG_3694_300_Lanczos3.png" /><br>Lanczos3</th>
+</tr>
+</table>
+
+
+License
+-------
+
+Copyright (c) 2012 Jan Schlicht <janschlicht@gmail.com>
+Resize is released under a MIT style license.
diff --git a/vendor/src/github.com/nfnt/resize/converter.go b/vendor/src/github.com/nfnt/resize/converter.go
new file mode 100644
index 000000000..f9c520d09
--- /dev/null
+++ b/vendor/src/github.com/nfnt/resize/converter.go
@@ -0,0 +1,438 @@
+/*
+Copyright (c) 2012, Jan Schlicht <jan.schlicht@gmail.com>
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+*/
+
+package resize
+
+import "image"
+
+// Keep value in [0,255] range.
+func clampUint8(in int32) uint8 {
+	// casting a negative int to an uint will result in an overflown
+	// large uint. this behavior will be exploited here and in other functions
+	// to achieve a higher performance.
+	if uint32(in) < 256 {
+		return uint8(in)
+	}
+	if in > 255 {
+		return 255
+	}
+	return 0
+}
+
+// Keep value in [0,65535] range.
+func clampUint16(in int64) uint16 {
+	if uint64(in) < 65536 {
+		return uint16(in)
+	}
+	if in > 65535 {
+		return 65535
+	}
+	return 0
+}
+
+func resizeGeneric(in image.Image, out *image.RGBA64, scale float64, coeffs []int32, offset []int, filterLength int) {
+	newBounds := out.Bounds()
+	maxX := in.Bounds().Dx() - 1
+
+	for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
+		for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
+			var rgba [4]int64
+			var sum int64
+			start := offset[y]
+			ci := y * filterLength
+			for i := 0; i < filterLength; i++ {
+				coeff := coeffs[ci+i]
+				if coeff != 0 {
+					xi := start + i
+					switch {
+					case xi < 0:
+						xi = 0
+					case xi >= maxX:
+						xi = maxX
+					}
+
+					r, g, b, a := in.At(xi+in.Bounds().Min.X, x+in.Bounds().Min.Y).RGBA()
+
+					rgba[0] += int64(coeff) * int64(r)
+					rgba[1] += int64(coeff) * int64(g)
+					rgba[2] += int64(coeff) * int64(b)
+					rgba[3] += int64(coeff) * int64(a)
+					sum += int64(coeff)
+				}
+			}
+
+			offset := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*8
+
+			value := clampUint16(rgba[0] / sum)
+			out.Pix[offset+0] = uint8(value >> 8)
+			out.Pix[offset+1] = uint8(value)
+			value = clampUint16(rgba[1] / sum)
+			out.Pix[offset+2] = uint8(value >> 8)
+			out.Pix[offset+3] = uint8(value)
+			value = clampUint16(rgba[2] / sum)
+			out.Pix[offset+4] = uint8(value >> 8)
+			out.Pix[offset+5] = uint8(value)
+			value = clampUint16(rgba[3] / sum)
+			out.Pix[offset+6] = uint8(value >> 8)
+			out.Pix[offset+7] = uint8(value)
+		}
+	}
+}
+
+func resizeRGBA(in *image.RGBA, out *image.RGBA, scale float64, coeffs []int16, offset []int, filterLength int) {
+	newBounds := out.Bounds()
+	maxX := in.Bounds().Dx() - 1
+
+	for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
+		row := in.Pix[x*in.Stride:]
+		for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
+			var rgba [4]int32
+			var sum int32
+			start := offset[y]
+			ci := y * filterLength
+			for i := 0; i < filterLength; i++ {
+				coeff := coeffs[ci+i]
+				if coeff != 0 {
+					xi := start + i
+					switch {
+					case uint(xi) < uint(maxX):
+						xi *= 4
+					case xi >= maxX:
+						xi = 4 * maxX
+					default:
+						xi = 0
+					}
+
+					rgba[0] += int32(coeff) * int32(row[xi+0])
+					rgba[1] += int32(coeff) * int32(row[xi+1])
+					rgba[2] += int32(coeff) * int32(row[xi+2])
+					rgba[3] += int32(coeff) * int32(row[xi+3])
+					sum += int32(coeff)
+				}
+			}
+
+			xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*4
+
+			out.Pix[xo+0] = clampUint8(rgba[0] / sum)
+			out.Pix[xo+1] = clampUint8(rgba[1] / sum)
+			out.Pix[xo+2] = clampUint8(rgba[2] / sum)
+			out.Pix[xo+3] = clampUint8(rgba[3] / sum)
+		}
+	}
+}
+
+func resizeNRGBA(in *image.NRGBA, out *image.RGBA, scale float64, coeffs []int16, offset []int, filterLength int) {
+	newBounds := out.Bounds()
+	maxX := in.Bounds().Dx() - 1
+
+	for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
+		row := in.Pix[x*in.Stride:]
+		for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
+			var rgba [4]int32
+			var sum int32
+			start := offset[y]
+			ci := y * filterLength
+			for i := 0; i < filterLength; i++ {
+				coeff := coeffs[ci+i]
+				if coeff != 0 {
+					xi := start + i
+					switch {
+					case uint(xi) < uint(maxX):
+						xi *= 4
+					case xi >= maxX:
+						xi = 4 * maxX
+					default:
+						xi = 0
+					}
+
+					// Forward alpha-premultiplication
+					a := int32(row[xi+3])
+					r := int32(row[xi+0]) * a
+					r /= 0xff
+					g := int32(row[xi+1]) * a
+					g /= 0xff
+					b := int32(row[xi+2]) * a
+					b /= 0xff
+
+					rgba[0] += int32(coeff) * r
+					rgba[1] += int32(coeff) * g
+					rgba[2] += int32(coeff) * b
+					rgba[3] += int32(coeff) * a
+					sum += int32(coeff)
+				}
+			}
+
+			xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*4
+
+			out.Pix[xo+0] = clampUint8(rgba[0] / sum)
+			out.Pix[xo+1] = clampUint8(rgba[1] / sum)
+			out.Pix[xo+2] = clampUint8(rgba[2] / sum)
+			out.Pix[xo+3] = clampUint8(rgba[3] / sum)
+		}
+	}
+}
+
+func resizeRGBA64(in *image.RGBA64, out *image.RGBA64, scale float64, coeffs []int32, offset []int, filterLength int) {
+	newBounds := out.Bounds()
+	maxX := in.Bounds().Dx() - 1
+
+	for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
+		row := in.Pix[x*in.Stride:]
+		for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
+			var rgba [4]int64
+			var sum int64
+			start := offset[y]
+			ci := y * filterLength
+			for i := 0; i < filterLength; i++ {
+				coeff := coeffs[ci+i]
+				if coeff != 0 {
+					xi := start + i
+					switch {
+					case uint(xi) < uint(maxX):
+						xi *= 8
+					case xi >= maxX:
+						xi = 8 * maxX
+					default:
+						xi = 0
+					}
+
+					rgba[0] += int64(coeff) * (int64(row[xi+0])<<8 | int64(row[xi+1]))
+					rgba[1] += int64(coeff) * (int64(row[xi+2])<<8 | int64(row[xi+3]))
+					rgba[2] += int64(coeff) * (int64(row[xi+4])<<8 | int64(row[xi+5]))
+					rgba[3] += int64(coeff) * (int64(row[xi+6])<<8 | int64(row[xi+7]))
+					sum += int64(coeff)
+				}
+			}
+
+			xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*8
+
+			value := clampUint16(rgba[0] / sum)
+			out.Pix[xo+0] = uint8(value >> 8)
+			out.Pix[xo+1] = uint8(value)
+			value = clampUint16(rgba[1] / sum)
+			out.Pix[xo+2] = uint8(value >> 8)
+			out.Pix[xo+3] = uint8(value)
+			value = clampUint16(rgba[2] / sum)
+			out.Pix[xo+4] = uint8(value >> 8)
+			out.Pix[xo+5] = uint8(value)
+			value = clampUint16(rgba[3] / sum)
+			out.Pix[xo+6] = uint8(value >> 8)
+			out.Pix[xo+7] = uint8(value)
+		}
+	}
+}
+
+func resizeNRGBA64(in *image.NRGBA64, out *image.RGBA64, scale float64, coeffs []int32, offset []int, filterLength int) {
+	newBounds := out.Bounds()
+	maxX := in.Bounds().Dx() - 1
+
+	for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
+		row := in.Pix[x*in.Stride:]
+		for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
+			var rgba [4]int64
+			var sum int64
+			start := offset[y]
+			ci := y * filterLength
+			for i := 0; i < filterLength; i++ {
+				coeff := coeffs[ci+i]
+				if coeff != 0 {
+					xi := start + i
+					switch {
+					case uint(xi) < uint(maxX):
+						xi *= 8
+					case xi >= maxX:
+						xi = 8 * maxX
+					default:
+						xi = 0
+					}
+
+					// Forward alpha-premultiplication
+					a := int64(uint16(row[xi+6])<<8 | uint16(row[xi+7]))
+					r := int64(uint16(row[xi+0])<<8|uint16(row[xi+1])) * a
+					r /= 0xffff
+					g := int64(uint16(row[xi+2])<<8|uint16(row[xi+3])) * a
+					g /= 0xffff
+					b := int64(uint16(row[xi+4])<<8|uint16(row[xi+5])) * a
+					b /= 0xffff
+
+					rgba[0] += int64(coeff) * r
+					rgba[1] += int64(coeff) * g
+					rgba[2] += int64(coeff) * b
+					rgba[3] += int64(coeff) * a
+					sum += int64(coeff)
+				}
+			}
+
+			xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*8
+
+			value := clampUint16(rgba[0] / sum)
+			out.Pix[xo+0] = uint8(value >> 8)
+			out.Pix[xo+1] = uint8(value)
+			value = clampUint16(rgba[1] / sum)
+			out.Pix[xo+2] = uint8(value >> 8)
+			out.Pix[xo+3] = uint8(value)
+			value = clampUint16(rgba[2] / sum)
+			out.Pix[xo+4] = uint8(value >> 8)
+			out.Pix[xo+5] = uint8(value)
+			value = clampUint16(rgba[3] / sum)
+			out.Pix[xo+6] = uint8(value >> 8)
+			out.Pix[xo+7] = uint8(value)
+		}
+	}
+}
+
+func resizeGray(in *image.Gray, out *image.Gray, scale float64, coeffs []int16, offset []int, filterLength int) {
+	newBounds := out.Bounds()
+	maxX := in.Bounds().Dx() - 1
+
+	for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
+		row := in.Pix[(x-newBounds.Min.X)*in.Stride:]
+		for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
+			var gray int32
+			var sum int32
+			start := offset[y]
+			ci := y * filterLength
+			for i := 0; i < filterLength; i++ {
+				coeff := coeffs[ci+i]
+				if coeff != 0 {
+					xi := start + i
+					switch {
+					case xi < 0:
+						xi = 0
+					case xi >= maxX:
+						xi = maxX
+					}
+					gray += int32(coeff) * int32(row[xi])
+					sum += int32(coeff)
+				}
+			}
+
+			offset := (y-newBounds.Min.Y)*out.Stride + (x - newBounds.Min.X)
+			out.Pix[offset] = clampUint8(gray / sum)
+		}
+	}
+}
+
+func resizeGray16(in *image.Gray16, out *image.Gray16, scale float64, coeffs []int32, offset []int, filterLength int) {
+	newBounds := out.Bounds()
+	maxX := in.Bounds().Dx() - 1
+
+	for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
+		row := in.Pix[x*in.Stride:]
+		for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
+			var gray int64
+			var sum int64
+			start := offset[y]
+			ci := y * filterLength
+			for i := 0; i < filterLength; i++ {
+				coeff := coeffs[ci+i]
+				if coeff != 0 {
+					xi := start + i
+					switch {
+					case uint(xi) < uint(maxX):
+						xi *= 2
+					case xi >= maxX:
+						xi = 2 * maxX
+					default:
+						xi = 0
+					}
+					gray += int64(coeff) * int64(uint16(row[xi+0])<<8|uint16(row[xi+1]))
+					sum += int64(coeff)
+				}
+			}
+
+			offset := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*2
+			value := clampUint16(gray / sum)
+			out.Pix[offset+0] = uint8(value >> 8)
+			out.Pix[offset+1] = uint8(value)
+		}
+	}
+}
+
+func resizeYCbCr(in *ycc, out *ycc, scale float64, coeffs []int16, offset []int, filterLength int) {
+	newBounds := out.Bounds()
+	maxX := in.Bounds().Dx() - 1
+
+	for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
+		row := in.Pix[x*in.Stride:]
+		for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
+			var p [3]int32
+			var sum int32
+			start := offset[y]
+			ci := y * filterLength
+			for i := 0; i < filterLength; i++ {
+				coeff := coeffs[ci+i]
+				if coeff != 0 {
+					xi := start + i
+					switch {
+					case uint(xi) < uint(maxX):
+						xi *= 3
+					case xi >= maxX:
+						xi = 3 * maxX
+					default:
+						xi = 0
+					}
+					p[0] += int32(coeff) * int32(row[xi+0])
+					p[1] += int32(coeff) * int32(row[xi+1])
+					p[2] += int32(coeff) * int32(row[xi+2])
+					sum += int32(coeff)
+				}
+			}
+
+			xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*3
+			out.Pix[xo+0] = clampUint8(p[0] / sum)
+			out.Pix[xo+1] = clampUint8(p[1] / sum)
+			out.Pix[xo+2] = clampUint8(p[2] / sum)
+		}
+	}
+}
+
+func nearestYCbCr(in *ycc, out *ycc, scale float64, coeffs []bool, offset []int, filterLength int) {
+	newBounds := out.Bounds()
+	maxX := in.Bounds().Dx() - 1
+
+	for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
+		row := in.Pix[x*in.Stride:]
+		for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
+			var p [3]float32
+			var sum float32
+			start := offset[y]
+			ci := y * filterLength
+			for i := 0; i < filterLength; i++ {
+				if coeffs[ci+i] {
+					xi := start + i
+					switch {
+					case uint(xi) < uint(maxX):
+						xi *= 3
+					case xi >= maxX:
+						xi = 3 * maxX
+					default:
+						xi = 0
+					}
+					p[0] += float32(row[xi+0])
+					p[1] += float32(row[xi+1])
+					p[2] += float32(row[xi+2])
+					sum++
+				}
+			}
+
+			xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*3
+			out.Pix[xo+0] = floatToUint8(p[0] / sum)
+			out.Pix[xo+1] = floatToUint8(p[1] / sum)
+			out.Pix[xo+2] = floatToUint8(p[2] / sum)
+		}
+	}
+}
diff --git a/vendor/src/github.com/nfnt/resize/converter_test.go b/vendor/src/github.com/nfnt/resize/converter_test.go
new file mode 100644
index 000000000..85639efc2
--- /dev/null
+++ b/vendor/src/github.com/nfnt/resize/converter_test.go
@@ -0,0 +1,43 @@
+package resize
+
+import (
+	"testing"
+)
+
+func Test_ClampUint8(t *testing.T) {
+	var testData = []struct {
+		in       int32
+		expected uint8
+	}{
+		{0, 0},
+		{255, 255},
+		{128, 128},
+		{-2, 0},
+		{256, 255},
+	}
+	for _, test := range testData {
+		actual := clampUint8(test.in)
+		if actual != test.expected {
+			t.Fail()
+		}
+	}
+}
+
+func Test_ClampUint16(t *testing.T) {
+	var testData = []struct {
+		in       int64
+		expected uint16
+	}{
+		{0, 0},
+		{65535, 65535},
+		{128, 128},
+		{-2, 0},
+		{65536, 65535},
+	}
+	for _, test := range testData {
+		actual := clampUint16(test.in)
+		if actual != test.expected {
+			t.Fail()
+		}
+	}
+}
diff --git a/vendor/src/github.com/nfnt/resize/filters.go b/vendor/src/github.com/nfnt/resize/filters.go
new file mode 100644
index 000000000..4ce04e389
--- /dev/null
+++ b/vendor/src/github.com/nfnt/resize/filters.go
@@ -0,0 +1,143 @@
+/*
+Copyright (c) 2012, Jan Schlicht <jan.schlicht@gmail.com>
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+*/
+
+package resize
+
+import (
+	"math"
+)
+
+func nearest(in float64) float64 {
+	if in >= -0.5 && in < 0.5 {
+		return 1
+	}
+	return 0
+}
+
+func linear(in float64) float64 {
+	in = math.Abs(in)
+	if in <= 1 {
+		return 1 - in
+	}
+	return 0
+}
+
+func cubic(in float64) float64 {
+	in = math.Abs(in)
+	if in <= 1 {
+		return in*in*(1.5*in-2.5) + 1.0
+	}
+	if in <= 2 {
+		return in*(in*(2.5-0.5*in)-4.0) + 2.0
+	}
+	return 0
+}
+
+func mitchellnetravali(in float64) float64 {
+	in = math.Abs(in)
+	if in <= 1 {
+		return (7.0*in*in*in - 12.0*in*in + 5.33333333333) * 0.16666666666
+	}
+	if in <= 2 {
+		return (-2.33333333333*in*in*in + 12.0*in*in - 20.0*in + 10.6666666667) * 0.16666666666
+	}
+	return 0
+}
+
+func sinc(x float64) float64 {
+	x = math.Abs(x) * math.Pi
+	if x >= 1.220703e-4 {
+		return math.Sin(x) / x
+	}
+	return 1
+}
+
+func lanczos2(in float64) float64 {
+	if in > -2 && in < 2 {
+		return sinc(in) * sinc(in*0.5)
+	}
+	return 0
+}
+
+func lanczos3(in float64) float64 {
+	if in > -3 && in < 3 {
+		return sinc(in) * sinc(in*0.3333333333333333)
+	}
+	return 0
+}
+
+// range [-256,256]
+func createWeights8(dy, filterLength int, blur, scale float64, kernel func(float64) float64) ([]int16, []int, int) {
+	filterLength = filterLength * int(math.Max(math.Ceil(blur*scale), 1))
+	filterFactor := math.Min(1./(blur*scale), 1)
+
+	coeffs := make([]int16, dy*filterLength)
+	start := make([]int, dy)
+	for y := 0; y < dy; y++ {
+		interpX := scale*(float64(y)+0.5) - 0.5
+		start[y] = int(interpX) - filterLength/2 + 1
+		interpX -= float64(start[y])
+		for i := 0; i < filterLength; i++ {
+			in := (interpX - float64(i)) * filterFactor
+			coeffs[y*filterLength+i] = int16(kernel(in) * 256)
+		}
+	}
+
+	return coeffs, start, filterLength
+}
+
+// range [-65536,65536]
+func createWeights16(dy, filterLength int, blur, scale float64, kernel func(float64) float64) ([]int32, []int, int) {
+	filterLength = filterLength * int(math.Max(math.Ceil(blur*scale), 1))
+	filterFactor := math.Min(1./(blur*scale), 1)
+
+	coeffs := make([]int32, dy*filterLength)
+	start := make([]int, dy)
+	for y := 0; y < dy; y++ {
+		interpX := scale*(float64(y)+0.5) - 0.5
+		start[y] = int(interpX) - filterLength/2 + 1
+		interpX -= float64(start[y])
+		for i := 0; i < filterLength; i++ {
+			in := (interpX - float64(i)) * filterFactor
+			coeffs[y*filterLength+i] = int32(kernel(in) * 65536)
+		}
+	}
+
+	return coeffs, start, filterLength
+}
+
+func createWeightsNearest(dy, filterLength int, blur, scale float64) ([]bool, []int, int) {
+	filterLength = filterLength * int(math.Max(math.Ceil(blur*scale), 1))
+	filterFactor := math.Min(1./(blur*scale), 1)
+
+	coeffs := make([]bool, dy*filterLength)
+	start := make([]int, dy)
+	for y := 0; y < dy; y++ {
+		interpX := scale*(float64(y)+0.5) - 0.5
+		start[y] = int(interpX) - filterLength/2 + 1
+		interpX -= float64(start[y])
+		for i := 0; i < filterLength; i++ {
+			in := (interpX - float64(i)) * filterFactor
+			if in >= -0.5 && in < 0.5 {
+				coeffs[y*filterLength+i] = true
+			} else {
+				coeffs[y*filterLength+i] = false
+			}
+		}
+	}
+
+	return coeffs, start, filterLength
+}
diff --git a/vendor/src/github.com/nfnt/resize/nearest.go b/vendor/src/github.com/nfnt/resize/nearest.go
new file mode 100644
index 000000000..888039d85
--- /dev/null
+++ b/vendor/src/github.com/nfnt/resize/nearest.go
@@ -0,0 +1,318 @@
+/*
+Copyright (c) 2014, Charlie Vieth <charlie.vieth@gmail.com>
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+*/
+
+package resize
+
+import "image"
+
+func floatToUint8(x float32) uint8 {
+	// Nearest-neighbor values are always
+	// positive no need to check lower-bound.
+	if x > 0xfe {
+		return 0xff
+	}
+	return uint8(x)
+}
+
+func floatToUint16(x float32) uint16 {
+	if x > 0xfffe {
+		return 0xffff
+	}
+	return uint16(x)
+}
+
+func nearestGeneric(in image.Image, out *image.RGBA64, scale float64, coeffs []bool, offset []int, filterLength int) {
+	newBounds := out.Bounds()
+	maxX := in.Bounds().Dx() - 1
+
+	for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
+		for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
+			var rgba [4]float32
+			var sum float32
+			start := offset[y]
+			ci := y * filterLength
+			for i := 0; i < filterLength; i++ {
+				if coeffs[ci+i] {
+					xi := start + i
+					switch {
+					case xi < 0:
+						xi = 0
+					case xi >= maxX:
+						xi = maxX
+					}
+					r, g, b, a := in.At(xi+in.Bounds().Min.X, x+in.Bounds().Min.Y).RGBA()
+					rgba[0] += float32(r)
+					rgba[1] += float32(g)
+					rgba[2] += float32(b)
+					rgba[3] += float32(a)
+					sum++
+				}
+			}
+
+			offset := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*8
+			value := floatToUint16(rgba[0] / sum)
+			out.Pix[offset+0] = uint8(value >> 8)
+			out.Pix[offset+1] = uint8(value)
+			value = floatToUint16(rgba[1] / sum)
+			out.Pix[offset+2] = uint8(value >> 8)
+			out.Pix[offset+3] = uint8(value)
+			value = floatToUint16(rgba[2] / sum)
+			out.Pix[offset+4] = uint8(value >> 8)
+			out.Pix[offset+5] = uint8(value)
+			value = floatToUint16(rgba[3] / sum)
+			out.Pix[offset+6] = uint8(value >> 8)
+			out.Pix[offset+7] = uint8(value)
+		}
+	}
+}
+
+func nearestRGBA(in *image.RGBA, out *image.RGBA, scale float64, coeffs []bool, offset []int, filterLength int) {
+	newBounds := out.Bounds()
+	maxX := in.Bounds().Dx() - 1
+
+	for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
+		row := in.Pix[x*in.Stride:]
+		for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
+			var rgba [4]float32
+			var sum float32
+			start := offset[y]
+			ci := y * filterLength
+			for i := 0; i < filterLength; i++ {
+				if coeffs[ci+i] {
+					xi := start + i
+					switch {
+					case uint(xi) < uint(maxX):
+						xi *= 4
+					case xi >= maxX:
+						xi = 4 * maxX
+					default:
+						xi = 0
+					}
+					rgba[0] += float32(row[xi+0])
+					rgba[1] += float32(row[xi+1])
+					rgba[2] += float32(row[xi+2])
+					rgba[3] += float32(row[xi+3])
+					sum++
+				}
+			}
+
+			xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*4
+			out.Pix[xo+0] = floatToUint8(rgba[0] / sum)
+			out.Pix[xo+1] = floatToUint8(rgba[1] / sum)
+			out.Pix[xo+2] = floatToUint8(rgba[2] / sum)
+			out.Pix[xo+3] = floatToUint8(rgba[3] / sum)
+		}
+	}
+}
+
+func nearestNRGBA(in *image.NRGBA, out *image.NRGBA, scale float64, coeffs []bool, offset []int, filterLength int) {
+	newBounds := out.Bounds()
+	maxX := in.Bounds().Dx() - 1
+
+	for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
+		row := in.Pix[x*in.Stride:]
+		for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
+			var rgba [4]float32
+			var sum float32
+			start := offset[y]
+			ci := y * filterLength
+			for i := 0; i < filterLength; i++ {
+				if coeffs[ci+i] {
+					xi := start + i
+					switch {
+					case uint(xi) < uint(maxX):
+						xi *= 4
+					case xi >= maxX:
+						xi = 4 * maxX
+					default:
+						xi = 0
+					}
+					rgba[0] += float32(row[xi+0])
+					rgba[1] += float32(row[xi+1])
+					rgba[2] += float32(row[xi+2])
+					rgba[3] += float32(row[xi+3])
+					sum++
+				}
+			}
+
+			xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*4
+			out.Pix[xo+0] = floatToUint8(rgba[0] / sum)
+			out.Pix[xo+1] = floatToUint8(rgba[1] / sum)
+			out.Pix[xo+2] = floatToUint8(rgba[2] / sum)
+			out.Pix[xo+3] = floatToUint8(rgba[3] / sum)
+		}
+	}
+}
+
+func nearestRGBA64(in *image.RGBA64, out *image.RGBA64, scale float64, coeffs []bool, offset []int, filterLength int) {
+	newBounds := out.Bounds()
+	maxX := in.Bounds().Dx() - 1
+
+	for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
+		row := in.Pix[x*in.Stride:]
+		for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
+			var rgba [4]float32
+			var sum float32
+			start := offset[y]
+			ci := y * filterLength
+			for i := 0; i < filterLength; i++ {
+				if coeffs[ci+i] {
+					xi := start + i
+					switch {
+					case uint(xi) < uint(maxX):
+						xi *= 8
+					case xi >= maxX:
+						xi = 8 * maxX
+					default:
+						xi = 0
+					}
+					rgba[0] += float32(uint16(row[xi+0])<<8 | uint16(row[xi+1]))
+					rgba[1] += float32(uint16(row[xi+2])<<8 | uint16(row[xi+3]))
+					rgba[2] += float32(uint16(row[xi+4])<<8 | uint16(row[xi+5]))
+					rgba[3] += float32(uint16(row[xi+6])<<8 | uint16(row[xi+7]))
+					sum++
+				}
+			}
+
+			xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*8
+			value := floatToUint16(rgba[0] / sum)
+			out.Pix[xo+0] = uint8(value >> 8)
+			out.Pix[xo+1] = uint8(value)
+			value = floatToUint16(rgba[1] / sum)
+			out.Pix[xo+2] = uint8(value >> 8)
+			out.Pix[xo+3] = uint8(value)
+			value = floatToUint16(rgba[2] / sum)
+			out.Pix[xo+4] = uint8(value >> 8)
+			out.Pix[xo+5] = uint8(value)
+			value = floatToUint16(rgba[3] / sum)
+			out.Pix[xo+6] = uint8(value >> 8)
+			out.Pix[xo+7] = uint8(value)
+		}
+	}
+}
+
+func nearestNRGBA64(in *image.NRGBA64, out *image.NRGBA64, scale float64, coeffs []bool, offset []int, filterLength int) {
+	newBounds := out.Bounds()
+	maxX := in.Bounds().Dx() - 1
+
+	for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
+		row := in.Pix[x*in.Stride:]
+		for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
+			var rgba [4]float32
+			var sum float32
+			start := offset[y]
+			ci := y * filterLength
+			for i := 0; i < filterLength; i++ {
+				if coeffs[ci+i] {
+					xi := start + i
+					switch {
+					case uint(xi) < uint(maxX):
+						xi *= 8
+					case xi >= maxX:
+						xi = 8 * maxX
+					default:
+						xi = 0
+					}
+					rgba[0] += float32(uint16(row[xi+0])<<8 | uint16(row[xi+1]))
+					rgba[1] += float32(uint16(row[xi+2])<<8 | uint16(row[xi+3]))
+					rgba[2] += float32(uint16(row[xi+4])<<8 | uint16(row[xi+5]))
+					rgba[3] += float32(uint16(row[xi+6])<<8 | uint16(row[xi+7]))
+					sum++
+				}
+			}
+
+			xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*8
+			value := floatToUint16(rgba[0] / sum)
+			out.Pix[xo+0] = uint8(value >> 8)
+			out.Pix[xo+1] = uint8(value)
+			value = floatToUint16(rgba[1] / sum)
+			out.Pix[xo+2] = uint8(value >> 8)
+			out.Pix[xo+3] = uint8(value)
+			value = floatToUint16(rgba[2] / sum)
+			out.Pix[xo+4] = uint8(value >> 8)
+			out.Pix[xo+5] = uint8(value)
+			value = floatToUint16(rgba[3] / sum)
+			out.Pix[xo+6] = uint8(value >> 8)
+			out.Pix[xo+7] = uint8(value)
+		}
+	}
+}
+
+func nearestGray(in *image.Gray, out *image.Gray, scale float64, coeffs []bool, offset []int, filterLength int) {
+	newBounds := out.Bounds()
+	maxX := in.Bounds().Dx() - 1
+
+	for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
+		row := in.Pix[x*in.Stride:]
+		for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
+			var gray float32
+			var sum float32
+			start := offset[y]
+			ci := y * filterLength
+			for i := 0; i < filterLength; i++ {
+				if coeffs[ci+i] {
+					xi := start + i
+					switch {
+					case xi < 0:
+						xi = 0
+					case xi >= maxX:
+						xi = maxX
+					}
+					gray += float32(row[xi])
+					sum++
+				}
+			}
+
+			offset := (y-newBounds.Min.Y)*out.Stride + (x - newBounds.Min.X)
+			out.Pix[offset] = floatToUint8(gray / sum)
+		}
+	}
+}
+
+func nearestGray16(in *image.Gray16, out *image.Gray16, scale float64, coeffs []bool, offset []int, filterLength int) {
+	newBounds := out.Bounds()
+	maxX := in.Bounds().Dx() - 1
+
+	for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
+		row := in.Pix[x*in.Stride:]
+		for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
+			var gray float32
+			var sum float32
+			start := offset[y]
+			ci := y * filterLength
+			for i := 0; i < filterLength; i++ {
+				if coeffs[ci+i] {
+					xi := start + i
+					switch {
+					case uint(xi) < uint(maxX):
+						xi *= 2
+					case xi >= maxX:
+						xi = 2 * maxX
+					default:
+						xi = 0
+					}
+					gray += float32(uint16(row[xi+0])<<8 | uint16(row[xi+1]))
+					sum++
+				}
+			}
+
+			offset := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*2
+			value := floatToUint16(gray / sum)
+			out.Pix[offset+0] = uint8(value >> 8)
+			out.Pix[offset+1] = uint8(value)
+		}
+	}
+}
diff --git a/vendor/src/github.com/nfnt/resize/nearest_test.go b/vendor/src/github.com/nfnt/resize/nearest_test.go
new file mode 100644
index 000000000..d4a76dda5
--- /dev/null
+++ b/vendor/src/github.com/nfnt/resize/nearest_test.go
@@ -0,0 +1,57 @@
+/*
+Copyright (c) 2014, Charlie Vieth <charlie.vieth@gmail.com>
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+*/
+
+package resize
+
+import "testing"
+
+func Test_FloatToUint8(t *testing.T) {
+	var testData = []struct {
+		in       float32
+		expected uint8
+	}{
+		{0, 0},
+		{255, 255},
+		{128, 128},
+		{1, 1},
+		{256, 255},
+	}
+	for _, test := range testData {
+		actual := floatToUint8(test.in)
+		if actual != test.expected {
+			t.Fail()
+		}
+	}
+}
+
+func Test_FloatToUint16(t *testing.T) {
+	var testData = []struct {
+		in       float32
+		expected uint16
+	}{
+		{0, 0},
+		{65535, 65535},
+		{128, 128},
+		{1, 1},
+		{65536, 65535},
+	}
+	for _, test := range testData {
+		actual := floatToUint16(test.in)
+		if actual != test.expected {
+			t.Fail()
+		}
+	}
+}
diff --git a/vendor/src/github.com/nfnt/resize/resize.go b/vendor/src/github.com/nfnt/resize/resize.go
new file mode 100644
index 000000000..57bd1fcd0
--- /dev/null
+++ b/vendor/src/github.com/nfnt/resize/resize.go
@@ -0,0 +1,614 @@
+/*
+Copyright (c) 2012, Jan Schlicht <jan.schlicht@gmail.com>
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+*/
+
+// Package resize implements various image resizing methods.
+//
+// The package works with the Image interface described in the image package.
+// Various interpolation methods are provided and multiple processors may be
+// utilized in the computations.
+//
+// Example:
+//     imgResized := resize.Resize(1000, 0, imgOld, resize.MitchellNetravali)
+package resize
+
+import (
+	"image"
+	"runtime"
+	"sync"
+)
+
+// An InterpolationFunction provides the parameters that describe an
+// interpolation kernel. It returns the number of samples to take
+// and the kernel function to use for sampling.
+type InterpolationFunction int
+
+// InterpolationFunction constants
+const (
+	// Nearest-neighbor interpolation
+	NearestNeighbor InterpolationFunction = iota
+	// Bilinear interpolation
+	Bilinear
+	// Bicubic interpolation (with cubic hermite spline)
+	Bicubic
+	// Mitchell-Netravali interpolation
+	MitchellNetravali
+	// Lanczos interpolation (a=2)
+	Lanczos2
+	// Lanczos interpolation (a=3)
+	Lanczos3
+)
+
+// kernal, returns an InterpolationFunctions taps and kernel.
+func (i InterpolationFunction) kernel() (int, func(float64) float64) {
+	switch i {
+	case Bilinear:
+		return 2, linear
+	case Bicubic:
+		return 4, cubic
+	case MitchellNetravali:
+		return 4, mitchellnetravali
+	case Lanczos2:
+		return 4, lanczos2
+	case Lanczos3:
+		return 6, lanczos3
+	default:
+		// Default to NearestNeighbor.
+		return 2, nearest
+	}
+}
+
+// values <1 will sharpen the image
+var blur = 1.0
+
+// Resize scales an image to new width and height using the interpolation function interp.
+// A new image with the given dimensions will be returned.
+// If one of the parameters width or height is set to 0, its size will be calculated so that
+// the aspect ratio is that of the originating image.
+// The resizing algorithm uses channels for parallel computation.
+func Resize(width, height uint, img image.Image, interp InterpolationFunction) image.Image {
+	scaleX, scaleY := calcFactors(width, height, float64(img.Bounds().Dx()), float64(img.Bounds().Dy()))
+	if width == 0 {
+		width = uint(0.7 + float64(img.Bounds().Dx())/scaleX)
+	}
+	if height == 0 {
+		height = uint(0.7 + float64(img.Bounds().Dy())/scaleY)
+	}
+
+	// Trivial case: return input image
+	if int(width) == img.Bounds().Dx() && int(height) == img.Bounds().Dy() {
+		return img
+	}
+
+	if interp == NearestNeighbor {
+		return resizeNearest(width, height, scaleX, scaleY, img, interp)
+	}
+
+	taps, kernel := interp.kernel()
+	cpus := runtime.GOMAXPROCS(0)
+	wg := sync.WaitGroup{}
+
+	// Generic access to image.Image is slow in tight loops.
+	// The optimal access has to be determined from the concrete image type.
+	switch input := img.(type) {
+	case *image.RGBA:
+		// 8-bit precision
+		temp := image.NewRGBA(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
+		result := image.NewRGBA(image.Rect(0, 0, int(width), int(height)))
+
+		// horizontal filter, results in transposed temporary image
+		coeffs, offset, filterLength := createWeights8(temp.Bounds().Dy(), taps, blur, scaleX, kernel)
+		wg.Add(cpus)
+		for i := 0; i < cpus; i++ {
+			slice := makeSlice(temp, i, cpus).(*image.RGBA)
+			go func() {
+				defer wg.Done()
+				resizeRGBA(input, slice, scaleX, coeffs, offset, filterLength)
+			}()
+		}
+		wg.Wait()
+
+		// horizontal filter on transposed image, result is not transposed
+		coeffs, offset, filterLength = createWeights8(result.Bounds().Dy(), taps, blur, scaleY, kernel)
+		wg.Add(cpus)
+		for i := 0; i < cpus; i++ {
+			slice := makeSlice(result, i, cpus).(*image.RGBA)
+			go func() {
+				defer wg.Done()
+				resizeRGBA(temp, slice, scaleY, coeffs, offset, filterLength)
+			}()
+		}
+		wg.Wait()
+		return result
+	case *image.NRGBA:
+		// 8-bit precision
+		temp := image.NewRGBA(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
+		result := image.NewRGBA(image.Rect(0, 0, int(width), int(height)))
+
+		// horizontal filter, results in transposed temporary image
+		coeffs, offset, filterLength := createWeights8(temp.Bounds().Dy(), taps, blur, scaleX, kernel)
+		wg.Add(cpus)
+		for i := 0; i < cpus; i++ {
+			slice := makeSlice(temp, i, cpus).(*image.RGBA)
+			go func() {
+				defer wg.Done()
+				resizeNRGBA(input, slice, scaleX, coeffs, offset, filterLength)
+			}()
+		}
+		wg.Wait()
+
+		// horizontal filter on transposed image, result is not transposed
+		coeffs, offset, filterLength = createWeights8(result.Bounds().Dy(), taps, blur, scaleY, kernel)
+		wg.Add(cpus)
+		for i := 0; i < cpus; i++ {
+			slice := makeSlice(result, i, cpus).(*image.RGBA)
+			go func() {
+				defer wg.Done()
+				resizeRGBA(temp, slice, scaleY, coeffs, offset, filterLength)
+			}()
+		}
+		wg.Wait()
+		return result
+
+	case *image.YCbCr:
+		// 8-bit precision
+		// accessing the YCbCr arrays in a tight loop is slow.
+		// converting the image to ycc increases performance by 2x.
+		temp := newYCC(image.Rect(0, 0, input.Bounds().Dy(), int(width)), input.SubsampleRatio)
+		result := newYCC(image.Rect(0, 0, int(width), int(height)), image.YCbCrSubsampleRatio444)
+
+		coeffs, offset, filterLength := createWeights8(temp.Bounds().Dy(), taps, blur, scaleX, kernel)
+		in := imageYCbCrToYCC(input)
+		wg.Add(cpus)
+		for i := 0; i < cpus; i++ {
+			slice := makeSlice(temp, i, cpus).(*ycc)
+			go func() {
+				defer wg.Done()
+				resizeYCbCr(in, slice, scaleX, coeffs, offset, filterLength)
+			}()
+		}
+		wg.Wait()
+
+		coeffs, offset, filterLength = createWeights8(result.Bounds().Dy(), taps, blur, scaleY, kernel)
+		wg.Add(cpus)
+		for i := 0; i < cpus; i++ {
+			slice := makeSlice(result, i, cpus).(*ycc)
+			go func() {
+				defer wg.Done()
+				resizeYCbCr(temp, slice, scaleY, coeffs, offset, filterLength)
+			}()
+		}
+		wg.Wait()
+		return result.YCbCr()
+	case *image.RGBA64:
+		// 16-bit precision
+		temp := image.NewRGBA64(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
+		result := image.NewRGBA64(image.Rect(0, 0, int(width), int(height)))
+
+		// horizontal filter, results in transposed temporary image
+		coeffs, offset, filterLength := createWeights16(temp.Bounds().Dy(), taps, blur, scaleX, kernel)
+		wg.Add(cpus)
+		for i := 0; i < cpus; i++ {
+			slice := makeSlice(temp, i, cpus).(*image.RGBA64)
+			go func() {
+				defer wg.Done()
+				resizeRGBA64(input, slice, scaleX, coeffs, offset, filterLength)
+			}()
+		}
+		wg.Wait()
+
+		// horizontal filter on transposed image, result is not transposed
+		coeffs, offset, filterLength = createWeights16(result.Bounds().Dy(), taps, blur, scaleY, kernel)
+		wg.Add(cpus)
+		for i := 0; i < cpus; i++ {
+			slice := makeSlice(result, i, cpus).(*image.RGBA64)
+			go func() {
+				defer wg.Done()
+				resizeRGBA64(temp, slice, scaleY, coeffs, offset, filterLength)
+			}()
+		}
+		wg.Wait()
+		return result
+	case *image.NRGBA64:
+		// 16-bit precision
+		temp := image.NewRGBA64(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
+		result := image.NewRGBA64(image.Rect(0, 0, int(width), int(height)))
+
+		// horizontal filter, results in transposed temporary image
+		coeffs, offset, filterLength := createWeights16(temp.Bounds().Dy(), taps, blur, scaleX, kernel)
+		wg.Add(cpus)
+		for i := 0; i < cpus; i++ {
+			slice := makeSlice(temp, i, cpus).(*image.RGBA64)
+			go func() {
+				defer wg.Done()
+				resizeNRGBA64(input, slice, scaleX, coeffs, offset, filterLength)
+			}()
+		}
+		wg.Wait()
+
+		// horizontal filter on transposed image, result is not transposed
+		coeffs, offset, filterLength = createWeights16(result.Bounds().Dy(), taps, blur, scaleY, kernel)
+		wg.Add(cpus)
+		for i := 0; i < cpus; i++ {
+			slice := makeSlice(result, i, cpus).(*image.RGBA64)
+			go func() {
+				defer wg.Done()
+				resizeRGBA64(temp, slice, scaleY, coeffs, offset, filterLength)
+			}()
+		}
+		wg.Wait()
+		return result
+	case *image.Gray:
+		// 8-bit precision
+		temp := image.NewGray(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
+		result := image.NewGray(image.Rect(0, 0, int(width), int(height)))
+
+		// horizontal filter, results in transposed temporary image
+		coeffs, offset, filterLength := createWeights8(temp.Bounds().Dy(), taps, blur, scaleX, kernel)
+		wg.Add(cpus)
+		for i := 0; i < cpus; i++ {
+			slice := makeSlice(temp, i, cpus).(*image.Gray)
+			go func() {
+				defer wg.Done()
+				resizeGray(input, slice, scaleX, coeffs, offset, filterLength)
+			}()
+		}
+		wg.Wait()
+
+		// horizontal filter on transposed image, result is not transposed
+		coeffs, offset, filterLength = createWeights8(result.Bounds().Dy(), taps, blur, scaleY, kernel)
+		wg.Add(cpus)
+		for i := 0; i < cpus; i++ {
+			slice := makeSlice(result, i, cpus).(*image.Gray)
+			go func() {
+				defer wg.Done()
+				resizeGray(temp, slice, scaleY, coeffs, offset, filterLength)
+			}()
+		}
+		wg.Wait()
+		return result
+	case *image.Gray16:
+		// 16-bit precision
+		temp := image.NewGray16(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
+		result := image.NewGray16(image.Rect(0, 0, int(width), int(height)))
+
+		// horizontal filter, results in transposed temporary image
+		coeffs, offset, filterLength := createWeights16(temp.Bounds().Dy(), taps, blur, scaleX, kernel)
+		wg.Add(cpus)
+		for i := 0; i < cpus; i++ {
+			slice := makeSlice(temp, i, cpus).(*image.Gray16)
+			go func() {
+				defer wg.Done()
+				resizeGray16(input, slice, scaleX, coeffs, offset, filterLength)
+			}()
+		}
+		wg.Wait()
+
+		// horizontal filter on transposed image, result is not transposed
+		coeffs, offset, filterLength = createWeights16(result.Bounds().Dy(), taps, blur, scaleY, kernel)
+		wg.Add(cpus)
+		for i := 0; i < cpus; i++ {
+			slice := makeSlice(result, i, cpus).(*image.Gray16)
+			go func() {
+				defer wg.Done()
+				resizeGray16(temp, slice, scaleY, coeffs, offset, filterLength)
+			}()
+		}
+		wg.Wait()
+		return result
+	default:
+		// 16-bit precision
+		temp := image.NewRGBA64(image.Rect(0, 0, img.Bounds().Dy(), int(width)))
+		result := image.NewRGBA64(image.Rect(0, 0, int(width), int(height)))
+
+		// horizontal filter, results in transposed temporary image
+		coeffs, offset, filterLength := createWeights16(temp.Bounds().Dy(), taps, blur, scaleX, kernel)
+		wg.Add(cpus)
+		for i := 0; i < cpus; i++ {
+			slice := makeSlice(temp, i, cpus).(*image.RGBA64)
+			go func() {
+				defer wg.Done()
+				resizeGeneric(img, slice, scaleX, coeffs, offset, filterLength)
+			}()
+		}
+		wg.Wait()
+
+		// horizontal filter on transposed image, result is not transposed
+		coeffs, offset, filterLength = createWeights16(result.Bounds().Dy(), taps, blur, scaleY, kernel)
+		wg.Add(cpus)
+		for i := 0; i < cpus; i++ {
+			slice := makeSlice(result, i, cpus).(*image.RGBA64)
+			go func() {
+				defer wg.Done()
+				resizeRGBA64(temp, slice, scaleY, coeffs, offset, filterLength)
+			}()
+		}
+		wg.Wait()
+		return result
+	}
+}
+
+func resizeNearest(width, height uint, scaleX, scaleY float64, img image.Image, interp InterpolationFunction) image.Image {
+	taps, _ := interp.kernel()
+	cpus := runtime.GOMAXPROCS(0)
+	wg := sync.WaitGroup{}
+
+	switch input := img.(type) {
+	case *image.RGBA:
+		// 8-bit precision
+		temp := image.NewRGBA(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
+		result := image.NewRGBA(image.Rect(0, 0, int(width), int(height)))
+
+		// horizontal filter, results in transposed temporary image
+		coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX)
+		wg.Add(cpus)
+		for i := 0; i < cpus; i++ {
+			slice := makeSlice(temp, i, cpus).(*image.RGBA)
+			go func() {
+				defer wg.Done()
+				nearestRGBA(input, slice, scaleX, coeffs, offset, filterLength)
+			}()
+		}
+		wg.Wait()
+
+		// horizontal filter on transposed image, result is not transposed
+		coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY)
+		wg.Add(cpus)
+		for i := 0; i < cpus; i++ {
+			slice := makeSlice(result, i, cpus).(*image.RGBA)
+			go func() {
+				defer wg.Done()
+				nearestRGBA(temp, slice, scaleY, coeffs, offset, filterLength)
+			}()
+		}
+		wg.Wait()
+		return result
+	case *image.NRGBA:
+		// 8-bit precision
+		temp := image.NewNRGBA(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
+		result := image.NewNRGBA(image.Rect(0, 0, int(width), int(height)))
+
+		// horizontal filter, results in transposed temporary image
+		coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX)
+		wg.Add(cpus)
+		for i := 0; i < cpus; i++ {
+			slice := makeSlice(temp, i, cpus).(*image.NRGBA)
+			go func() {
+				defer wg.Done()
+				nearestNRGBA(input, slice, scaleX, coeffs, offset, filterLength)
+			}()
+		}
+		wg.Wait()
+
+		// horizontal filter on transposed image, result is not transposed
+		coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY)
+		wg.Add(cpus)
+		for i := 0; i < cpus; i++ {
+			slice := makeSlice(result, i, cpus).(*image.NRGBA)
+			go func() {
+				defer wg.Done()
+				nearestNRGBA(temp, slice, scaleY, coeffs, offset, filterLength)
+			}()
+		}
+		wg.Wait()
+		return result
+	case *image.YCbCr:
+		// 8-bit precision
+		// accessing the YCbCr arrays in a tight loop is slow.
+		// converting the image to ycc increases performance by 2x.
+		temp := newYCC(image.Rect(0, 0, input.Bounds().Dy(), int(width)), input.SubsampleRatio)
+		result := newYCC(image.Rect(0, 0, int(width), int(height)), image.YCbCrSubsampleRatio444)
+
+		coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX)
+		in := imageYCbCrToYCC(input)
+		wg.Add(cpus)
+		for i := 0; i < cpus; i++ {
+			slice := makeSlice(temp, i, cpus).(*ycc)
+			go func() {
+				defer wg.Done()
+				nearestYCbCr(in, slice, scaleX, coeffs, offset, filterLength)
+			}()
+		}
+		wg.Wait()
+
+		coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY)
+		wg.Add(cpus)
+		for i := 0; i < cpus; i++ {
+			slice := makeSlice(result, i, cpus).(*ycc)
+			go func() {
+				defer wg.Done()
+				nearestYCbCr(temp, slice, scaleY, coeffs, offset, filterLength)
+			}()
+		}
+		wg.Wait()
+		return result.YCbCr()
+	case *image.RGBA64:
+		// 16-bit precision
+		temp := image.NewRGBA64(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
+		result := image.NewRGBA64(image.Rect(0, 0, int(width), int(height)))
+
+		// horizontal filter, results in transposed temporary image
+		coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX)
+		wg.Add(cpus)
+		for i := 0; i < cpus; i++ {
+			slice := makeSlice(temp, i, cpus).(*image.RGBA64)
+			go func() {
+				defer wg.Done()
+				nearestRGBA64(input, slice, scaleX, coeffs, offset, filterLength)
+			}()
+		}
+		wg.Wait()
+
+		// horizontal filter on transposed image, result is not transposed
+		coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY)
+		wg.Add(cpus)
+		for i := 0; i < cpus; i++ {
+			slice := makeSlice(result, i, cpus).(*image.RGBA64)
+			go func() {
+				defer wg.Done()
+				nearestRGBA64(temp, slice, scaleY, coeffs, offset, filterLength)
+			}()
+		}
+		wg.Wait()
+		return result
+	case *image.NRGBA64:
+		// 16-bit precision
+		temp := image.NewNRGBA64(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
+		result := image.NewNRGBA64(image.Rect(0, 0, int(width), int(height)))
+
+		// horizontal filter, results in transposed temporary image
+		coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX)
+		wg.Add(cpus)
+		for i := 0; i < cpus; i++ {
+			slice := makeSlice(temp, i, cpus).(*image.NRGBA64)
+			go func() {
+				defer wg.Done()
+				nearestNRGBA64(input, slice, scaleX, coeffs, offset, filterLength)
+			}()
+		}
+		wg.Wait()
+
+		// horizontal filter on transposed image, result is not transposed
+		coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY)
+		wg.Add(cpus)
+		for i := 0; i < cpus; i++ {
+			slice := makeSlice(result, i, cpus).(*image.NRGBA64)
+			go func() {
+				defer wg.Done()
+				nearestNRGBA64(temp, slice, scaleY, coeffs, offset, filterLength)
+			}()
+		}
+		wg.Wait()
+		return result
+	case *image.Gray:
+		// 8-bit precision
+		temp := image.NewGray(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
+		result := image.NewGray(image.Rect(0, 0, int(width), int(height)))
+
+		// horizontal filter, results in transposed temporary image
+		coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX)
+		wg.Add(cpus)
+		for i := 0; i < cpus; i++ {
+			slice := makeSlice(temp, i, cpus).(*image.Gray)
+			go func() {
+				defer wg.Done()
+				nearestGray(input, slice, scaleX, coeffs, offset, filterLength)
+			}()
+		}
+		wg.Wait()
+
+		// horizontal filter on transposed image, result is not transposed
+		coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY)
+		wg.Add(cpus)
+		for i := 0; i < cpus; i++ {
+			slice := makeSlice(result, i, cpus).(*image.Gray)
+			go func() {
+				defer wg.Done()
+				nearestGray(temp, slice, scaleY, coeffs, offset, filterLength)
+			}()
+		}
+		wg.Wait()
+		return result
+	case *image.Gray16:
+		// 16-bit precision
+		temp := image.NewGray16(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
+		result := image.NewGray16(image.Rect(0, 0, int(width), int(height)))
+
+		// horizontal filter, results in transposed temporary image
+		coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX)
+		wg.Add(cpus)
+		for i := 0; i < cpus; i++ {
+			slice := makeSlice(temp, i, cpus).(*image.Gray16)
+			go func() {
+				defer wg.Done()
+				nearestGray16(input, slice, scaleX, coeffs, offset, filterLength)
+			}()
+		}
+		wg.Wait()
+
+		// horizontal filter on transposed image, result is not transposed
+		coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY)
+		wg.Add(cpus)
+		for i := 0; i < cpus; i++ {
+			slice := makeSlice(result, i, cpus).(*image.Gray16)
+			go func() {
+				defer wg.Done()
+				nearestGray16(temp, slice, scaleY, coeffs, offset, filterLength)
+			}()
+		}
+		wg.Wait()
+		return result
+	default:
+		// 16-bit precision
+		temp := image.NewRGBA64(image.Rect(0, 0, img.Bounds().Dy(), int(width)))
+		result := image.NewRGBA64(image.Rect(0, 0, int(width), int(height)))
+
+		// horizontal filter, results in transposed temporary image
+		coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX)
+		wg.Add(cpus)
+		for i := 0; i < cpus; i++ {
+			slice := makeSlice(temp, i, cpus).(*image.RGBA64)
+			go func() {
+				defer wg.Done()
+				nearestGeneric(img, slice, scaleX, coeffs, offset, filterLength)
+			}()
+		}
+		wg.Wait()
+
+		// horizontal filter on transposed image, result is not transposed
+		coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY)
+		wg.Add(cpus)
+		for i := 0; i < cpus; i++ {
+			slice := makeSlice(result, i, cpus).(*image.RGBA64)
+			go func() {
+				defer wg.Done()
+				nearestRGBA64(temp, slice, scaleY, coeffs, offset, filterLength)
+			}()
+		}
+		wg.Wait()
+		return result
+	}
+
+}
+
+// Calculates scaling factors using old and new image dimensions.
+func calcFactors(width, height uint, oldWidth, oldHeight float64) (scaleX, scaleY float64) {
+	if width == 0 {
+		if height == 0 {
+			scaleX = 1.0
+			scaleY = 1.0
+		} else {
+			scaleY = oldHeight / float64(height)
+			scaleX = scaleY
+		}
+	} else {
+		scaleX = oldWidth / float64(width)
+		if height == 0 {
+			scaleY = scaleX
+		} else {
+			scaleY = oldHeight / float64(height)
+		}
+	}
+	return
+}
+
+type imageWithSubImage interface {
+	image.Image
+	SubImage(image.Rectangle) image.Image
+}
+
+func makeSlice(img imageWithSubImage, i, n int) image.Image {
+	return img.SubImage(image.Rect(img.Bounds().Min.X, img.Bounds().Min.Y+i*img.Bounds().Dy()/n, img.Bounds().Max.X, img.Bounds().Min.Y+(i+1)*img.Bounds().Dy()/n))
+}
diff --git a/vendor/src/github.com/nfnt/resize/resize_test.go b/vendor/src/github.com/nfnt/resize/resize_test.go
new file mode 100644
index 000000000..d4b80bee5
--- /dev/null
+++ b/vendor/src/github.com/nfnt/resize/resize_test.go
@@ -0,0 +1,330 @@
+package resize
+
+import (
+	"image"
+	"image/color"
+	"runtime"
+	"testing"
+)
+
+var img = image.NewGray16(image.Rect(0, 0, 3, 3))
+
+func init() {
+	runtime.GOMAXPROCS(runtime.NumCPU())
+	img.Set(1, 1, color.White)
+}
+
+func Test_Param1(t *testing.T) {
+	m := Resize(0, 0, img, NearestNeighbor)
+	if m.Bounds() != img.Bounds() {
+		t.Fail()
+	}
+}
+
+func Test_Param2(t *testing.T) {
+	m := Resize(100, 0, img, NearestNeighbor)
+	if m.Bounds() != image.Rect(0, 0, 100, 100) {
+		t.Fail()
+	}
+}
+
+func Test_ZeroImg(t *testing.T) {
+	zeroImg := image.NewGray16(image.Rect(0, 0, 0, 0))
+
+	m := Resize(0, 0, zeroImg, NearestNeighbor)
+	if m.Bounds() != zeroImg.Bounds() {
+		t.Fail()
+	}
+}
+
+func Test_CorrectResize(t *testing.T) {
+	zeroImg := image.NewGray16(image.Rect(0, 0, 256, 256))
+
+	m := Resize(60, 0, zeroImg, NearestNeighbor)
+	if m.Bounds() != image.Rect(0, 0, 60, 60) {
+		t.Fail()
+	}
+}
+
+func Test_SameColorWithRGBA(t *testing.T) {
+	img := image.NewRGBA(image.Rect(0, 0, 20, 20))
+	for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ {
+		for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ {
+			img.SetRGBA(x, y, color.RGBA{0x80, 0x80, 0x80, 0xFF})
+		}
+	}
+	out := Resize(10, 10, img, Lanczos3)
+	for y := out.Bounds().Min.Y; y < out.Bounds().Max.Y; y++ {
+		for x := out.Bounds().Min.X; x < out.Bounds().Max.X; x++ {
+			color := out.At(x, y).(color.RGBA)
+			if color.R != 0x80 || color.G != 0x80 || color.B != 0x80 || color.A != 0xFF {
+				t.Errorf("%+v", color)
+			}
+		}
+	}
+}
+
+func Test_SameColorWithNRGBA(t *testing.T) {
+	img := image.NewNRGBA(image.Rect(0, 0, 20, 20))
+	for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ {
+		for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ {
+			img.SetNRGBA(x, y, color.NRGBA{0x80, 0x80, 0x80, 0xFF})
+		}
+	}
+	out := Resize(10, 10, img, Lanczos3)
+	for y := out.Bounds().Min.Y; y < out.Bounds().Max.Y; y++ {
+		for x := out.Bounds().Min.X; x < out.Bounds().Max.X; x++ {
+			color := out.At(x, y).(color.RGBA)
+			if color.R != 0x80 || color.G != 0x80 || color.B != 0x80 || color.A != 0xFF {
+				t.Errorf("%+v", color)
+			}
+		}
+	}
+}
+
+func Test_SameColorWithRGBA64(t *testing.T) {
+	img := image.NewRGBA64(image.Rect(0, 0, 20, 20))
+	for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ {
+		for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ {
+			img.SetRGBA64(x, y, color.RGBA64{0x8000, 0x8000, 0x8000, 0xFFFF})
+		}
+	}
+	out := Resize(10, 10, img, Lanczos3)
+	for y := out.Bounds().Min.Y; y < out.Bounds().Max.Y; y++ {
+		for x := out.Bounds().Min.X; x < out.Bounds().Max.X; x++ {
+			color := out.At(x, y).(color.RGBA64)
+			if color.R != 0x8000 || color.G != 0x8000 || color.B != 0x8000 || color.A != 0xFFFF {
+				t.Errorf("%+v", color)
+			}
+		}
+	}
+}
+
+func Test_SameColorWithNRGBA64(t *testing.T) {
+	img := image.NewNRGBA64(image.Rect(0, 0, 20, 20))
+	for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ {
+		for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ {
+			img.SetNRGBA64(x, y, color.NRGBA64{0x8000, 0x8000, 0x8000, 0xFFFF})
+		}
+	}
+	out := Resize(10, 10, img, Lanczos3)
+	for y := out.Bounds().Min.Y; y < out.Bounds().Max.Y; y++ {
+		for x := out.Bounds().Min.X; x < out.Bounds().Max.X; x++ {
+			color := out.At(x, y).(color.RGBA64)
+			if color.R != 0x8000 || color.G != 0x8000 || color.B != 0x8000 || color.A != 0xFFFF {
+				t.Errorf("%+v", color)
+			}
+		}
+	}
+}
+
+func Test_SameColorWithGray(t *testing.T) {
+	img := image.NewGray(image.Rect(0, 0, 20, 20))
+	for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ {
+		for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ {
+			img.SetGray(x, y, color.Gray{0x80})
+		}
+	}
+	out := Resize(10, 10, img, Lanczos3)
+	for y := out.Bounds().Min.Y; y < out.Bounds().Max.Y; y++ {
+		for x := out.Bounds().Min.X; x < out.Bounds().Max.X; x++ {
+			color := out.At(x, y).(color.Gray)
+			if color.Y != 0x80 {
+				t.Errorf("%+v", color)
+			}
+		}
+	}
+}
+
+func Test_SameColorWithGray16(t *testing.T) {
+	img := image.NewGray16(image.Rect(0, 0, 20, 20))
+	for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ {
+		for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ {
+			img.SetGray16(x, y, color.Gray16{0x8000})
+		}
+	}
+	out := Resize(10, 10, img, Lanczos3)
+	for y := out.Bounds().Min.Y; y < out.Bounds().Max.Y; y++ {
+		for x := out.Bounds().Min.X; x < out.Bounds().Max.X; x++ {
+			color := out.At(x, y).(color.Gray16)
+			if color.Y != 0x8000 {
+				t.Errorf("%+v", color)
+			}
+		}
+	}
+}
+
+func Test_Bounds(t *testing.T) {
+	img := image.NewRGBA(image.Rect(20, 10, 200, 99))
+	out := Resize(80, 80, img, Lanczos2)
+	out.At(0, 0)
+}
+
+func Test_SameSizeReturnsOriginal(t *testing.T) {
+	img := image.NewRGBA(image.Rect(0, 0, 10, 10))
+	out := Resize(0, 0, img, Lanczos2)
+
+	if img != out {
+		t.Fail()
+	}
+
+	out = Resize(10, 10, img, Lanczos2)
+
+	if img != out {
+		t.Fail()
+	}
+}
+
+func Test_PixelCoordinates(t *testing.T) {
+	checkers := image.NewGray(image.Rect(0, 0, 4, 4))
+	checkers.Pix = []uint8{
+		255, 0, 255, 0,
+		0, 255, 0, 255,
+		255, 0, 255, 0,
+		0, 255, 0, 255,
+	}
+
+	resized := Resize(12, 12, checkers, NearestNeighbor).(*image.Gray)
+
+	if resized.Pix[0] != 255 || resized.Pix[1] != 255 || resized.Pix[2] != 255 {
+		t.Fail()
+	}
+
+	if resized.Pix[3] != 0 || resized.Pix[4] != 0 || resized.Pix[5] != 0 {
+		t.Fail()
+	}
+}
+
+func Test_ResizeWithPremultipliedAlpha(t *testing.T) {
+	img := image.NewRGBA(image.Rect(0, 0, 1, 4))
+	for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ {
+		// 0x80 = 0.5 * 0xFF.
+		img.SetRGBA(0, y, color.RGBA{0x80, 0x80, 0x80, 0x80})
+	}
+
+	out := Resize(1, 2, img, MitchellNetravali)
+
+	outputColor := out.At(0, 0).(color.RGBA)
+	if outputColor.R != 0x80 {
+		t.Fail()
+	}
+}
+
+func Test_ResizeWithTranslucentColor(t *testing.T) {
+	img := image.NewNRGBA(image.Rect(0, 0, 1, 2))
+
+	// Set the pixel colors to an "invisible green" and white.
+	// After resizing, the green shouldn't be visible.
+	img.SetNRGBA(0, 0, color.NRGBA{0x00, 0xFF, 0x00, 0x00})
+	img.SetNRGBA(0, 1, color.NRGBA{0x00, 0x00, 0x00, 0xFF})
+
+	out := Resize(1, 1, img, Bilinear)
+
+	_, g, _, _ := out.At(0, 0).RGBA()
+	if g != 0x00 {
+		t.Errorf("%+v", g)
+	}
+}
+
+const (
+	// Use a small image size for benchmarks. We don't want memory performance
+	// to affect the benchmark results.
+	benchMaxX = 250
+	benchMaxY = 250
+
+	// Resize values near the original size require increase the amount of time
+	// resize spends converting the image.
+	benchWidth  = 200
+	benchHeight = 200
+)
+
+func benchRGBA(b *testing.B, interp InterpolationFunction) {
+	m := image.NewRGBA(image.Rect(0, 0, benchMaxX, benchMaxY))
+	// Initialize m's pixels to create a non-uniform image.
+	for y := m.Rect.Min.Y; y < m.Rect.Max.Y; y++ {
+		for x := m.Rect.Min.X; x < m.Rect.Max.X; x++ {
+			i := m.PixOffset(x, y)
+			m.Pix[i+0] = uint8(y + 4*x)
+			m.Pix[i+1] = uint8(y + 4*x)
+			m.Pix[i+2] = uint8(y + 4*x)
+			m.Pix[i+3] = uint8(4*y + x)
+		}
+	}
+
+	var out image.Image
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		out = Resize(benchWidth, benchHeight, m, interp)
+	}
+	out.At(0, 0)
+}
+
+// The names of some interpolation functions are truncated so that the columns
+// of 'go test -bench' line up.
+func Benchmark_Nearest_RGBA(b *testing.B) {
+	benchRGBA(b, NearestNeighbor)
+}
+
+func Benchmark_Bilinear_RGBA(b *testing.B) {
+	benchRGBA(b, Bilinear)
+}
+
+func Benchmark_Bicubic_RGBA(b *testing.B) {
+	benchRGBA(b, Bicubic)
+}
+
+func Benchmark_Mitchell_RGBA(b *testing.B) {
+	benchRGBA(b, MitchellNetravali)
+}
+
+func Benchmark_Lanczos2_RGBA(b *testing.B) {
+	benchRGBA(b, Lanczos2)
+}
+
+func Benchmark_Lanczos3_RGBA(b *testing.B) {
+	benchRGBA(b, Lanczos3)
+}
+
+func benchYCbCr(b *testing.B, interp InterpolationFunction) {
+	m := image.NewYCbCr(image.Rect(0, 0, benchMaxX, benchMaxY), image.YCbCrSubsampleRatio422)
+	// Initialize m's pixels to create a non-uniform image.
+	for y := m.Rect.Min.Y; y < m.Rect.Max.Y; y++ {
+		for x := m.Rect.Min.X; x < m.Rect.Max.X; x++ {
+			yi := m.YOffset(x, y)
+			ci := m.COffset(x, y)
+			m.Y[yi] = uint8(16*y + x)
+			m.Cb[ci] = uint8(y + 16*x)
+			m.Cr[ci] = uint8(y + 16*x)
+		}
+	}
+	var out image.Image
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		out = Resize(benchWidth, benchHeight, m, interp)
+	}
+	out.At(0, 0)
+}
+
+func Benchmark_Nearest_YCC(b *testing.B) {
+	benchYCbCr(b, NearestNeighbor)
+}
+
+func Benchmark_Bilinear_YCC(b *testing.B) {
+	benchYCbCr(b, Bilinear)
+}
+
+func Benchmark_Bicubic_YCC(b *testing.B) {
+	benchYCbCr(b, Bicubic)
+}
+
+func Benchmark_Mitchell_YCC(b *testing.B) {
+	benchYCbCr(b, MitchellNetravali)
+}
+
+func Benchmark_Lanczos2_YCC(b *testing.B) {
+	benchYCbCr(b, Lanczos2)
+}
+
+func Benchmark_Lanczos3_YCC(b *testing.B) {
+	benchYCbCr(b, Lanczos3)
+}
diff --git a/vendor/src/github.com/nfnt/resize/thumbnail.go b/vendor/src/github.com/nfnt/resize/thumbnail.go
new file mode 100644
index 000000000..9efc246be
--- /dev/null
+++ b/vendor/src/github.com/nfnt/resize/thumbnail.go
@@ -0,0 +1,55 @@
+/*
+Copyright (c) 2012, Jan Schlicht <jan.schlicht@gmail.com>
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+*/
+
+package resize
+
+import (
+	"image"
+)
+
+// Thumbnail will downscale provided image to max width and height preserving
+// original aspect ratio and using the interpolation function interp.
+// It will return original image, without processing it, if original sizes
+// are already smaller than provided constraints.
+func Thumbnail(maxWidth, maxHeight uint, img image.Image, interp InterpolationFunction) image.Image {
+	origBounds := img.Bounds()
+	origWidth := uint(origBounds.Dx())
+	origHeight := uint(origBounds.Dy())
+	newWidth, newHeight := origWidth, origHeight
+
+	// Return original image if it have same or smaller size as constraints
+	if maxWidth >= origWidth && maxHeight >= origHeight {
+		return img
+	}
+
+	// Preserve aspect ratio
+	if origWidth > maxWidth {
+		newHeight = uint(origHeight * maxWidth / origWidth)
+		if newHeight < 1 {
+			newHeight = 1
+		}
+		newWidth = maxWidth
+	}
+
+	if newHeight > maxHeight {
+		newWidth = uint(newWidth * maxHeight / newHeight)
+		if newWidth < 1 {
+			newWidth = 1
+		}
+		newHeight = maxHeight
+	}
+	return Resize(newWidth, newHeight, img, interp)
+}
diff --git a/vendor/src/github.com/nfnt/resize/thumbnail_test.go b/vendor/src/github.com/nfnt/resize/thumbnail_test.go
new file mode 100644
index 000000000..bd9875b2b
--- /dev/null
+++ b/vendor/src/github.com/nfnt/resize/thumbnail_test.go
@@ -0,0 +1,47 @@
+package resize
+
+import (
+	"image"
+	"runtime"
+	"testing"
+)
+
+func init() {
+	runtime.GOMAXPROCS(runtime.NumCPU())
+}
+
+var thumbnailTests = []struct {
+	origWidth      int
+	origHeight     int
+	maxWidth       uint
+	maxHeight      uint
+	expectedWidth  uint
+	expectedHeight uint
+}{
+	{5, 5, 10, 10, 5, 5},
+	{10, 10, 5, 5, 5, 5},
+	{10, 50, 10, 10, 2, 10},
+	{50, 10, 10, 10, 10, 2},
+	{50, 100, 60, 90, 45, 90},
+	{120, 100, 60, 90, 60, 50},
+	{200, 250, 200, 150, 120, 150},
+}
+
+func TestThumbnail(t *testing.T) {
+	for i, tt := range thumbnailTests {
+		img := image.NewGray16(image.Rect(0, 0, tt.origWidth, tt.origHeight))
+
+		outImg := Thumbnail(tt.maxWidth, tt.maxHeight, img, NearestNeighbor)
+
+		newWidth := uint(outImg.Bounds().Dx())
+		newHeight := uint(outImg.Bounds().Dy())
+		if newWidth != tt.expectedWidth ||
+			newHeight != tt.expectedHeight {
+			t.Errorf("%d. Thumbnail(%v, %v, img, NearestNeighbor) => "+
+				"width: %v, height: %v, want width: %v, height: %v",
+				i, tt.maxWidth, tt.maxHeight,
+				newWidth, newHeight, tt.expectedWidth, tt.expectedHeight,
+			)
+		}
+	}
+}
diff --git a/vendor/src/github.com/nfnt/resize/ycc.go b/vendor/src/github.com/nfnt/resize/ycc.go
new file mode 100644
index 000000000..104159955
--- /dev/null
+++ b/vendor/src/github.com/nfnt/resize/ycc.go
@@ -0,0 +1,227 @@
+/*
+Copyright (c) 2014, Charlie Vieth <charlie.vieth@gmail.com>
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+*/
+
+package resize
+
+import (
+	"image"
+	"image/color"
+)
+
+// ycc is an in memory YCbCr image.  The Y, Cb and Cr samples are held in a
+// single slice to increase resizing performance.
+type ycc struct {
+	// Pix holds the image's pixels, in Y, Cb, Cr order. The pixel at
+	// (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*3].
+	Pix []uint8
+	// Stride is the Pix stride (in bytes) between vertically adjacent pixels.
+	Stride int
+	// Rect is the image's bounds.
+	Rect image.Rectangle
+	// SubsampleRatio is the subsample ratio of the original YCbCr image.
+	SubsampleRatio image.YCbCrSubsampleRatio
+}
+
+// PixOffset returns the index of the first element of Pix that corresponds to
+// the pixel at (x, y).
+func (p *ycc) PixOffset(x, y int) int {
+	return (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*3
+}
+
+func (p *ycc) Bounds() image.Rectangle {
+	return p.Rect
+}
+
+func (p *ycc) ColorModel() color.Model {
+	return color.YCbCrModel
+}
+
+func (p *ycc) At(x, y int) color.Color {
+	if !(image.Point{x, y}.In(p.Rect)) {
+		return color.YCbCr{}
+	}
+	i := p.PixOffset(x, y)
+	return color.YCbCr{
+		p.Pix[i+0],
+		p.Pix[i+1],
+		p.Pix[i+2],
+	}
+}
+
+func (p *ycc) Opaque() bool {
+	return true
+}
+
+// SubImage returns an image representing the portion of the image p visible
+// through r. The returned value shares pixels with the original image.
+func (p *ycc) SubImage(r image.Rectangle) image.Image {
+	r = r.Intersect(p.Rect)
+	if r.Empty() {
+		return &ycc{SubsampleRatio: p.SubsampleRatio}
+	}
+	i := p.PixOffset(r.Min.X, r.Min.Y)
+	return &ycc{
+		Pix:            p.Pix[i:],
+		Stride:         p.Stride,
+		Rect:           r,
+		SubsampleRatio: p.SubsampleRatio,
+	}
+}
+
+// newYCC returns a new ycc with the given bounds and subsample ratio.
+func newYCC(r image.Rectangle, s image.YCbCrSubsampleRatio) *ycc {
+	w, h := r.Dx(), r.Dy()
+	buf := make([]uint8, 3*w*h)
+	return &ycc{Pix: buf, Stride: 3 * w, Rect: r, SubsampleRatio: s}
+}
+
+// YCbCr converts ycc to a YCbCr image with the same subsample ratio
+// as the YCbCr image that ycc was generated from.
+func (p *ycc) YCbCr() *image.YCbCr {
+	ycbcr := image.NewYCbCr(p.Rect, p.SubsampleRatio)
+	var off int
+
+	switch ycbcr.SubsampleRatio {
+	case image.YCbCrSubsampleRatio422:
+		for y := ycbcr.Rect.Min.Y; y < ycbcr.Rect.Max.Y; y++ {
+			yy := (y - ycbcr.Rect.Min.Y) * ycbcr.YStride
+			cy := (y - ycbcr.Rect.Min.Y) * ycbcr.CStride
+			for x := ycbcr.Rect.Min.X; x < ycbcr.Rect.Max.X; x++ {
+				xx := (x - ycbcr.Rect.Min.X)
+				yi := yy + xx
+				ci := cy + xx/2
+				ycbcr.Y[yi] = p.Pix[off+0]
+				ycbcr.Cb[ci] = p.Pix[off+1]
+				ycbcr.Cr[ci] = p.Pix[off+2]
+				off += 3
+			}
+		}
+	case image.YCbCrSubsampleRatio420:
+		for y := ycbcr.Rect.Min.Y; y < ycbcr.Rect.Max.Y; y++ {
+			yy := (y - ycbcr.Rect.Min.Y) * ycbcr.YStride
+			cy := (y/2 - ycbcr.Rect.Min.Y/2) * ycbcr.CStride
+			for x := ycbcr.Rect.Min.X; x < ycbcr.Rect.Max.X; x++ {
+				xx := (x - ycbcr.Rect.Min.X)
+				yi := yy + xx
+				ci := cy + xx/2
+				ycbcr.Y[yi] = p.Pix[off+0]
+				ycbcr.Cb[ci] = p.Pix[off+1]
+				ycbcr.Cr[ci] = p.Pix[off+2]
+				off += 3
+			}
+		}
+	case image.YCbCrSubsampleRatio440:
+		for y := ycbcr.Rect.Min.Y; y < ycbcr.Rect.Max.Y; y++ {
+			yy := (y - ycbcr.Rect.Min.Y) * ycbcr.YStride
+			cy := (y/2 - ycbcr.Rect.Min.Y/2) * ycbcr.CStride
+			for x := ycbcr.Rect.Min.X; x < ycbcr.Rect.Max.X; x++ {
+				xx := (x - ycbcr.Rect.Min.X)
+				yi := yy + xx
+				ci := cy + xx
+				ycbcr.Y[yi] = p.Pix[off+0]
+				ycbcr.Cb[ci] = p.Pix[off+1]
+				ycbcr.Cr[ci] = p.Pix[off+2]
+				off += 3
+			}
+		}
+	default:
+		// Default to 4:4:4 subsampling.
+		for y := ycbcr.Rect.Min.Y; y < ycbcr.Rect.Max.Y; y++ {
+			yy := (y - ycbcr.Rect.Min.Y) * ycbcr.YStride
+			cy := (y - ycbcr.Rect.Min.Y) * ycbcr.CStride
+			for x := ycbcr.Rect.Min.X; x < ycbcr.Rect.Max.X; x++ {
+				xx := (x - ycbcr.Rect.Min.X)
+				yi := yy + xx
+				ci := cy + xx
+				ycbcr.Y[yi] = p.Pix[off+0]
+				ycbcr.Cb[ci] = p.Pix[off+1]
+				ycbcr.Cr[ci] = p.Pix[off+2]
+				off += 3
+			}
+		}
+	}
+	return ycbcr
+}
+
+// imageYCbCrToYCC converts a YCbCr image to a ycc image for resizing.
+func imageYCbCrToYCC(in *image.YCbCr) *ycc {
+	w, h := in.Rect.Dx(), in.Rect.Dy()
+	r := image.Rect(0, 0, w, h)
+	buf := make([]uint8, 3*w*h)
+	p := ycc{Pix: buf, Stride: 3 * w, Rect: r, SubsampleRatio: in.SubsampleRatio}
+	var off int
+
+	switch in.SubsampleRatio {
+	case image.YCbCrSubsampleRatio422:
+		for y := in.Rect.Min.Y; y < in.Rect.Max.Y; y++ {
+			yy := (y - in.Rect.Min.Y) * in.YStride
+			cy := (y - in.Rect.Min.Y) * in.CStride
+			for x := in.Rect.Min.X; x < in.Rect.Max.X; x++ {
+				xx := (x - in.Rect.Min.X)
+				yi := yy + xx
+				ci := cy + xx/2
+				p.Pix[off+0] = in.Y[yi]
+				p.Pix[off+1] = in.Cb[ci]
+				p.Pix[off+2] = in.Cr[ci]
+				off += 3
+			}
+		}
+	case image.YCbCrSubsampleRatio420:
+		for y := in.Rect.Min.Y; y < in.Rect.Max.Y; y++ {
+			yy := (y - in.Rect.Min.Y) * in.YStride
+			cy := (y/2 - in.Rect.Min.Y/2) * in.CStride
+			for x := in.Rect.Min.X; x < in.Rect.Max.X; x++ {
+				xx := (x - in.Rect.Min.X)
+				yi := yy + xx
+				ci := cy + xx/2
+				p.Pix[off+0] = in.Y[yi]
+				p.Pix[off+1] = in.Cb[ci]
+				p.Pix[off+2] = in.Cr[ci]
+				off += 3
+			}
+		}
+	case image.YCbCrSubsampleRatio440:
+		for y := in.Rect.Min.Y; y < in.Rect.Max.Y; y++ {
+			yy := (y - in.Rect.Min.Y) * in.YStride
+			cy := (y/2 - in.Rect.Min.Y/2) * in.CStride
+			for x := in.Rect.Min.X; x < in.Rect.Max.X; x++ {
+				xx := (x - in.Rect.Min.X)
+				yi := yy + xx
+				ci := cy + xx
+				p.Pix[off+0] = in.Y[yi]
+				p.Pix[off+1] = in.Cb[ci]
+				p.Pix[off+2] = in.Cr[ci]
+				off += 3
+			}
+		}
+	default:
+		// Default to 4:4:4 subsampling.
+		for y := in.Rect.Min.Y; y < in.Rect.Max.Y; y++ {
+			yy := (y - in.Rect.Min.Y) * in.YStride
+			cy := (y - in.Rect.Min.Y) * in.CStride
+			for x := in.Rect.Min.X; x < in.Rect.Max.X; x++ {
+				xx := (x - in.Rect.Min.X)
+				yi := yy + xx
+				ci := cy + xx
+				p.Pix[off+0] = in.Y[yi]
+				p.Pix[off+1] = in.Cb[ci]
+				p.Pix[off+2] = in.Cr[ci]
+				off += 3
+			}
+		}
+	}
+	return &p
+}
diff --git a/vendor/src/github.com/nfnt/resize/ycc_test.go b/vendor/src/github.com/nfnt/resize/ycc_test.go
new file mode 100644
index 000000000..54d53d157
--- /dev/null
+++ b/vendor/src/github.com/nfnt/resize/ycc_test.go
@@ -0,0 +1,214 @@
+/*
+Copyright (c) 2014, Charlie Vieth <charlie.vieth@gmail.com>
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+*/
+
+package resize
+
+import (
+	"image"
+	"image/color"
+	"testing"
+)
+
+type Image interface {
+	image.Image
+	SubImage(image.Rectangle) image.Image
+}
+
+func TestImage(t *testing.T) {
+	testImage := []Image{
+		newYCC(image.Rect(0, 0, 10, 10), image.YCbCrSubsampleRatio420),
+		newYCC(image.Rect(0, 0, 10, 10), image.YCbCrSubsampleRatio422),
+		newYCC(image.Rect(0, 0, 10, 10), image.YCbCrSubsampleRatio440),
+		newYCC(image.Rect(0, 0, 10, 10), image.YCbCrSubsampleRatio444),
+	}
+	for _, m := range testImage {
+		if !image.Rect(0, 0, 10, 10).Eq(m.Bounds()) {
+			t.Errorf("%T: want bounds %v, got %v",
+				m, image.Rect(0, 0, 10, 10), m.Bounds())
+			continue
+		}
+		m = m.SubImage(image.Rect(3, 2, 9, 8)).(Image)
+		if !image.Rect(3, 2, 9, 8).Eq(m.Bounds()) {
+			t.Errorf("%T: sub-image want bounds %v, got %v",
+				m, image.Rect(3, 2, 9, 8), m.Bounds())
+			continue
+		}
+		// Test that taking an empty sub-image starting at a corner does not panic.
+		m.SubImage(image.Rect(0, 0, 0, 0))
+		m.SubImage(image.Rect(10, 0, 10, 0))
+		m.SubImage(image.Rect(0, 10, 0, 10))
+		m.SubImage(image.Rect(10, 10, 10, 10))
+	}
+}
+
+func TestConvertYCbCr(t *testing.T) {
+	testImage := []Image{
+		image.NewYCbCr(image.Rect(0, 0, 50, 50), image.YCbCrSubsampleRatio420),
+		image.NewYCbCr(image.Rect(0, 0, 50, 50), image.YCbCrSubsampleRatio422),
+		image.NewYCbCr(image.Rect(0, 0, 50, 50), image.YCbCrSubsampleRatio440),
+		image.NewYCbCr(image.Rect(0, 0, 50, 50), image.YCbCrSubsampleRatio444),
+	}
+
+	for _, img := range testImage {
+		m := img.(*image.YCbCr)
+		for y := m.Rect.Min.Y; y < m.Rect.Max.Y; y++ {
+			for x := m.Rect.Min.X; x < m.Rect.Max.X; x++ {
+				yi := m.YOffset(x, y)
+				ci := m.COffset(x, y)
+				m.Y[yi] = uint8(16*y + x)
+				m.Cb[ci] = uint8(y + 16*x)
+				m.Cr[ci] = uint8(y + 16*x)
+			}
+		}
+
+		// test conversion from YCbCr to ycc
+		yc := imageYCbCrToYCC(m)
+		for y := m.Rect.Min.Y; y < m.Rect.Max.Y; y++ {
+			for x := m.Rect.Min.X; x < m.Rect.Max.X; x++ {
+				ystride := 3 * (m.Rect.Max.X - m.Rect.Min.X)
+				xstride := 3
+				yi := m.YOffset(x, y)
+				ci := m.COffset(x, y)
+				si := (y * ystride) + (x * xstride)
+				if m.Y[yi] != yc.Pix[si] {
+					t.Errorf("Err Y - found: %d expected: %d x: %d y: %d yi: %d si: %d",
+						m.Y[yi], yc.Pix[si], x, y, yi, si)
+				}
+				if m.Cb[ci] != yc.Pix[si+1] {
+					t.Errorf("Err Cb - found: %d expected: %d x: %d y: %d ci: %d si: %d",
+						m.Cb[ci], yc.Pix[si+1], x, y, ci, si+1)
+				}
+				if m.Cr[ci] != yc.Pix[si+2] {
+					t.Errorf("Err Cr - found: %d expected: %d x: %d y: %d ci: %d si: %d",
+						m.Cr[ci], yc.Pix[si+2], x, y, ci, si+2)
+				}
+			}
+		}
+
+		// test conversion from ycc back to YCbCr
+		ym := yc.YCbCr()
+		for y := m.Rect.Min.Y; y < m.Rect.Max.Y; y++ {
+			for x := m.Rect.Min.X; x < m.Rect.Max.X; x++ {
+				yi := m.YOffset(x, y)
+				ci := m.COffset(x, y)
+				if m.Y[yi] != ym.Y[yi] {
+					t.Errorf("Err Y - found: %d expected: %d x: %d y: %d yi: %d",
+						m.Y[yi], ym.Y[yi], x, y, yi)
+				}
+				if m.Cb[ci] != ym.Cb[ci] {
+					t.Errorf("Err Cb - found: %d expected: %d x: %d y: %d ci: %d",
+						m.Cb[ci], ym.Cb[ci], x, y, ci)
+				}
+				if m.Cr[ci] != ym.Cr[ci] {
+					t.Errorf("Err Cr - found: %d expected: %d x: %d y: %d ci: %d",
+						m.Cr[ci], ym.Cr[ci], x, y, ci)
+				}
+			}
+		}
+	}
+}
+
+func TestYCbCr(t *testing.T) {
+	rects := []image.Rectangle{
+		image.Rect(0, 0, 16, 16),
+		image.Rect(1, 0, 16, 16),
+		image.Rect(0, 1, 16, 16),
+		image.Rect(1, 1, 16, 16),
+		image.Rect(1, 1, 15, 16),
+		image.Rect(1, 1, 16, 15),
+		image.Rect(1, 1, 15, 15),
+		image.Rect(2, 3, 14, 15),
+		image.Rect(7, 0, 7, 16),
+		image.Rect(0, 8, 16, 8),
+		image.Rect(0, 0, 10, 11),
+		image.Rect(5, 6, 16, 16),
+		image.Rect(7, 7, 8, 8),
+		image.Rect(7, 8, 8, 9),
+		image.Rect(8, 7, 9, 8),
+		image.Rect(8, 8, 9, 9),
+		image.Rect(7, 7, 17, 17),
+		image.Rect(8, 8, 17, 17),
+		image.Rect(9, 9, 17, 17),
+		image.Rect(10, 10, 17, 17),
+	}
+	subsampleRatios := []image.YCbCrSubsampleRatio{
+		image.YCbCrSubsampleRatio444,
+		image.YCbCrSubsampleRatio422,
+		image.YCbCrSubsampleRatio420,
+		image.YCbCrSubsampleRatio440,
+	}
+	deltas := []image.Point{
+		image.Pt(0, 0),
+		image.Pt(1000, 1001),
+		image.Pt(5001, -400),
+		image.Pt(-701, -801),
+	}
+	for _, r := range rects {
+		for _, subsampleRatio := range subsampleRatios {
+			for _, delta := range deltas {
+				testYCbCr(t, r, subsampleRatio, delta)
+			}
+		}
+		if testing.Short() {
+			break
+		}
+	}
+}
+
+func testYCbCr(t *testing.T, r image.Rectangle, subsampleRatio image.YCbCrSubsampleRatio, delta image.Point) {
+	// Create a YCbCr image m, whose bounds are r translated by (delta.X, delta.Y).
+	r1 := r.Add(delta)
+	img := image.NewYCbCr(r1, subsampleRatio)
+
+	// Initialize img's pixels. For 422 and 420 subsampling, some of the Cb and Cr elements
+	// will be set multiple times. That's OK. We just want to avoid a uniform image.
+	for y := r1.Min.Y; y < r1.Max.Y; y++ {
+		for x := r1.Min.X; x < r1.Max.X; x++ {
+			yi := img.YOffset(x, y)
+			ci := img.COffset(x, y)
+			img.Y[yi] = uint8(16*y + x)
+			img.Cb[ci] = uint8(y + 16*x)
+			img.Cr[ci] = uint8(y + 16*x)
+		}
+	}
+
+	m := imageYCbCrToYCC(img)
+
+	// Make various sub-images of m.
+	for y0 := delta.Y + 3; y0 < delta.Y+7; y0++ {
+		for y1 := delta.Y + 8; y1 < delta.Y+13; y1++ {
+			for x0 := delta.X + 3; x0 < delta.X+7; x0++ {
+				for x1 := delta.X + 8; x1 < delta.X+13; x1++ {
+					subRect := image.Rect(x0, y0, x1, y1)
+					sub := m.SubImage(subRect).(*ycc)
+
+					// For each point in the sub-image's bounds, check that m.At(x, y) equals sub.At(x, y).
+					for y := sub.Rect.Min.Y; y < sub.Rect.Max.Y; y++ {
+						for x := sub.Rect.Min.X; x < sub.Rect.Max.X; x++ {
+							color0 := m.At(x, y).(color.YCbCr)
+							color1 := sub.At(x, y).(color.YCbCr)
+							if color0 != color1 {
+								t.Errorf("r=%v, subsampleRatio=%v, delta=%v, x=%d, y=%d, color0=%v, color1=%v",
+									r, subsampleRatio, delta, x, y, color0, color1)
+								return
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+}