summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md13
-rw-r--r--Godeps/Godeps.json17
-rw-r--r--Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/Makefile15
-rw-r--r--Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/affine.go174
-rw-r--r--Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/blur.go68
-rw-r--r--Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/blur_test.go207
-rw-r--r--Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/convolve/Makefile11
-rw-r--r--Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/convolve/convolve.go274
-rw-r--r--Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/convolve/convolve_test.go78
-rw-r--r--Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/detect/Makefile15
-rw-r--r--Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/detect/detect.go133
-rw-r--r--Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/detect/detect_test.go77
-rw-r--r--Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/detect/doc.go31
-rw-r--r--Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/detect/integral.go93
-rw-r--r--Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/detect/integral_test.go156
-rw-r--r--Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/detect/opencv_parser.go125
-rw-r--r--Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/detect/opencv_parser_test.go75
-rw-r--r--Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/detect/projector.go55
-rw-r--r--Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/detect/projector_test.go49
-rw-r--r--Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/graphicstest/Makefile11
-rw-r--r--Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/graphicstest/graphicstest.go112
-rw-r--r--Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/interp/Makefile13
-rw-r--r--Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/interp/bilinear.go206
-rw-r--r--Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/interp/bilinear_test.go143
-rw-r--r--Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/interp/doc.go25
-rw-r--r--Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/interp/interp.go29
-rw-r--r--Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/rotate.go35
-rw-r--r--Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/rotate_test.go169
-rw-r--r--Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/scale.go31
-rw-r--r--Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/scale_test.go153
-rw-r--r--Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/shared_test.go69
-rw-r--r--Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/thumbnail.go41
-rw-r--r--Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/thumbnail_test.go53
-rw-r--r--Godeps/_workspace/src/github.com/disintegration/imaging/LICENSE21
-rw-r--r--Godeps/_workspace/src/github.com/disintegration/imaging/README.md160
-rw-r--r--Godeps/_workspace/src/github.com/disintegration/imaging/adjust.go200
-rw-r--r--Godeps/_workspace/src/github.com/disintegration/imaging/effects.go187
-rw-r--r--Godeps/_workspace/src/github.com/disintegration/imaging/helpers.go392
-rw-r--r--Godeps/_workspace/src/github.com/disintegration/imaging/resize.go564
-rw-r--r--Godeps/_workspace/src/github.com/disintegration/imaging/tools.go182
-rw-r--r--Godeps/_workspace/src/github.com/disintegration/imaging/transform.go201
-rw-r--r--Godeps/_workspace/src/github.com/disintegration/imaging/utils.go77
-rw-r--r--Godeps/_workspace/src/github.com/nfnt/resize/.travis.yml7
-rw-r--r--Godeps/_workspace/src/github.com/nfnt/resize/LICENSE13
-rw-r--r--Godeps/_workspace/src/github.com/nfnt/resize/README.md149
-rw-r--r--Godeps/_workspace/src/github.com/nfnt/resize/converter.go452
-rw-r--r--Godeps/_workspace/src/github.com/nfnt/resize/converter_test.go43
-rw-r--r--Godeps/_workspace/src/github.com/nfnt/resize/filters.go143
-rw-r--r--Godeps/_workspace/src/github.com/nfnt/resize/nearest.go318
-rw-r--r--Godeps/_workspace/src/github.com/nfnt/resize/nearest_test.go57
-rw-r--r--Godeps/_workspace/src/github.com/nfnt/resize/resize.go614
-rw-r--r--Godeps/_workspace/src/github.com/nfnt/resize/resize_test.go314
-rw-r--r--Godeps/_workspace/src/github.com/nfnt/resize/thumbnail.go55
-rw-r--r--Godeps/_workspace/src/github.com/nfnt/resize/thumbnail_test.go47
-rw-r--r--Godeps/_workspace/src/github.com/nfnt/resize/ycc.go227
-rw-r--r--Godeps/_workspace/src/github.com/nfnt/resize/ycc_test.go214
-rw-r--r--Godeps/_workspace/src/golang.org/x/image/tiff/buffer.go69
-rw-r--r--Godeps/_workspace/src/golang.org/x/image/tiff/compress.go58
-rw-r--r--Godeps/_workspace/src/golang.org/x/image/tiff/consts.go133
-rw-r--r--Godeps/_workspace/src/golang.org/x/image/tiff/lzw/reader.go277
-rw-r--r--Godeps/_workspace/src/golang.org/x/image/tiff/reader.go681
-rw-r--r--Godeps/_workspace/src/golang.org/x/image/tiff/writer.go438
-rw-r--r--api/file.go67
-rw-r--r--api/user.go21
-rw-r--r--doc/README.md4
-rw-r--r--doc/install/single-container-install.md8
-rw-r--r--doc/integrations/webhook/incoming.md57
-rw-r--r--model/config.go12
-rw-r--r--web/react/components/command_list.jsx2
-rw-r--r--web/react/components/popover_list_members.jsx26
-rw-r--r--web/react/components/post_body.jsx122
-rw-r--r--web/react/components/post_info.jsx2
-rw-r--r--web/react/components/post_list.jsx14
-rw-r--r--web/react/components/sidebar_right.jsx25
-rw-r--r--web/react/components/user_settings/import_theme_modal.jsx2
-rw-r--r--web/react/utils/utils.jsx169
-rw-r--r--web/sass-files/sass/partials/_command-box.scss17
77 files changed, 3960 insertions, 5637 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2ee47ec35..64cc57ab8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,6 @@
# Mattermost Changelog
-## UNDER DEVELOPMENT - Release v1.0.0
+## UNDER DEVELOPMENT - Release v1.0.0-RC1
The "UNDER DEVELOPMENT" section of the Mattermost changelog appears in the product's `master` branch to note key changes committed to master and are on their way to the next stable release. When a stable release is pushed the "UNDER DEVELOPMENT" heading is removed from the final changelog of the release.
@@ -9,7 +9,10 @@ The "UNDER DEVELOPMENT" section of the Mattermost changelog appears in the produ
### Release Highlights
-- [See Product Roadmap for anticipated features](https://mattermost.atlassian.net/issues/?filter=10002)
+- System Console - UI for configuring deployments, managing teams, resetting user passwords and other admin features
+- Markdown support in messages, comments and channel descriptions - Including font formatting, emoticons, headings and tables
+- Preset themes and detailed theme color options, plus ability to import themes from Slack
+- Numerous performance improvements and optimizations
### New Features
@@ -17,12 +20,17 @@ Messaging, Comments and Notifications
- Support for emoji codes rendering to image files
- Full markdown support in messages, comments, and channel description
+
+Files and Images
+
- Added ability to play video and audio files
System Console
- UI to change config.json settings
- Ability to view log files from console
+- Ability to reset user passwords
+- Ability for IT admin to manage members across multiple teams from single interface
User Interface
@@ -65,6 +73,7 @@ UI
Many thanks to our external contributors. In no particular order:
+- [jdeng](https://github.com/jdeng)
- [Trozz](https://github.com/Trozz)
- [LAndres](https://github.com/LAndreas)
- [JessBot](https://github.com/JessBot)
diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json
index 9bd02d455..7c80558c4 100644
--- a/Godeps/Godeps.json
+++ b/Godeps/Godeps.json
@@ -13,11 +13,6 @@
"Rev": "69e2a90ed92d03812364aeb947b7068dc42e561e"
},
{
- "ImportPath": "code.google.com/p/graphics-go/graphics",
- "Comment": "null-25",
- "Rev": "f843bfcd8ac420072c7f6804599995b0a229070b"
- },
- {
"ImportPath": "code.google.com/p/log4go",
"Comment": "go.weekly.2012-02-22-1",
"Rev": "c3294304d93f48a37d3bed1d382882a9c2989f99"
@@ -32,6 +27,10 @@
"Rev": "5280e250f2795914acbeb2bf3b55dd5a2d1fba52"
},
{
+ "ImportPath": "github.com/disintegration/imaging",
+ "Rev": "493653de80c32beeae336f3a3a3a125e7603459b"
+ },
+ {
"ImportPath": "github.com/garyburd/redigo/internal",
"Rev": "a47585eaae68b1d14b02940d2af1b9194f3caa9c"
},
@@ -89,10 +88,6 @@
"Rev": "a163d6a569f1cd264d2f8b2bf3c5d04ace5995eb"
},
{
- "ImportPath": "github.com/nfnt/resize",
- "Rev": "dc93e1b98c579d90ee2fa15c1fd6dac34f6e7899"
- },
- {
"ImportPath": "github.com/rwcarlsen/goexif/exif",
"Rev": "709fab3d192d7c62f86043caff1e7e3fb0f42bd8"
},
@@ -137,6 +132,10 @@
"Rev": "baddd3465a05d84a6d8d3507547a91cb188c81ea"
},
{
+ "ImportPath": "golang.org/x/image/tiff",
+ "Rev": "baddd3465a05d84a6d8d3507547a91cb188c81ea"
+ },
+ {
"ImportPath": "gopkg.in/fsnotify.v1",
"Comment": "v1.2.0",
"Rev": "96c060f6a6b7e0d6f75fddd10efeaca3e5d1bcb0"
diff --git a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/Makefile b/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/Makefile
deleted file mode 100644
index 28a06f0e8..000000000
--- a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/Makefile
+++ /dev/null
@@ -1,15 +0,0 @@
-# Copyright 2011 The Graphics-Go Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file.
-
-include $(GOROOT)/src/Make.inc
-
-TARG=code.google.com/p/graphics-go/graphics
-GOFILES=\
- affine.go\
- blur.go\
- rotate.go\
- scale.go\
- thumbnail.go\
-
-include $(GOROOT)/src/Make.pkg
diff --git a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/affine.go b/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/affine.go
deleted file mode 100644
index 0ac2ec9da..000000000
--- a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/affine.go
+++ /dev/null
@@ -1,174 +0,0 @@
-// Copyright 2011 The Graphics-Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package graphics
-
-import (
- "code.google.com/p/graphics-go/graphics/interp"
- "errors"
- "image"
- "image/draw"
- "math"
-)
-
-// I is the identity Affine transform matrix.
-var I = Affine{
- 1, 0, 0,
- 0, 1, 0,
- 0, 0, 1,
-}
-
-// Affine is a 3x3 2D affine transform matrix.
-// M(i,j) is Affine[i*3+j].
-type Affine [9]float64
-
-// Mul returns the multiplication of two affine transform matrices.
-func (a Affine) Mul(b Affine) Affine {
- return Affine{
- a[0]*b[0] + a[1]*b[3] + a[2]*b[6],
- a[0]*b[1] + a[1]*b[4] + a[2]*b[7],
- a[0]*b[2] + a[1]*b[5] + a[2]*b[8],
- a[3]*b[0] + a[4]*b[3] + a[5]*b[6],
- a[3]*b[1] + a[4]*b[4] + a[5]*b[7],
- a[3]*b[2] + a[4]*b[5] + a[5]*b[8],
- a[6]*b[0] + a[7]*b[3] + a[8]*b[6],
- a[6]*b[1] + a[7]*b[4] + a[8]*b[7],
- a[6]*b[2] + a[7]*b[5] + a[8]*b[8],
- }
-}
-
-func (a Affine) transformRGBA(dst *image.RGBA, src *image.RGBA, i interp.RGBA) error {
- srcb := src.Bounds()
- b := dst.Bounds()
- for y := b.Min.Y; y < b.Max.Y; y++ {
- for x := b.Min.X; x < b.Max.X; x++ {
- sx, sy := a.pt(x, y)
- if inBounds(srcb, sx, sy) {
- c := i.RGBA(src, sx, sy)
- off := (y-dst.Rect.Min.Y)*dst.Stride + (x-dst.Rect.Min.X)*4
- dst.Pix[off+0] = c.R
- dst.Pix[off+1] = c.G
- dst.Pix[off+2] = c.B
- dst.Pix[off+3] = c.A
- }
- }
- }
- return nil
-}
-
-// Transform applies the affine transform to src and produces dst.
-func (a Affine) Transform(dst draw.Image, src image.Image, i interp.Interp) error {
- if dst == nil {
- return errors.New("graphics: dst is nil")
- }
- if src == nil {
- return errors.New("graphics: src is nil")
- }
-
- // RGBA fast path.
- dstRGBA, dstOk := dst.(*image.RGBA)
- srcRGBA, srcOk := src.(*image.RGBA)
- interpRGBA, interpOk := i.(interp.RGBA)
- if dstOk && srcOk && interpOk {
- return a.transformRGBA(dstRGBA, srcRGBA, interpRGBA)
- }
-
- srcb := src.Bounds()
- b := dst.Bounds()
- for y := b.Min.Y; y < b.Max.Y; y++ {
- for x := b.Min.X; x < b.Max.X; x++ {
- sx, sy := a.pt(x, y)
- if inBounds(srcb, sx, sy) {
- dst.Set(x, y, i.Interp(src, sx, sy))
- }
- }
- }
- return nil
-}
-
-func inBounds(b image.Rectangle, x, y float64) bool {
- if x < float64(b.Min.X) || x >= float64(b.Max.X) {
- return false
- }
- if y < float64(b.Min.Y) || y >= float64(b.Max.Y) {
- return false
- }
- return true
-}
-
-func (a Affine) pt(x0, y0 int) (x1, y1 float64) {
- fx := float64(x0) + 0.5
- fy := float64(y0) + 0.5
- x1 = fx*a[0] + fy*a[1] + a[2]
- y1 = fx*a[3] + fy*a[4] + a[5]
- return x1, y1
-}
-
-// TransformCenter applies the affine transform to src and produces dst.
-// Equivalent to
-// a.CenterFit(dst, src).Transform(dst, src, i).
-func (a Affine) TransformCenter(dst draw.Image, src image.Image, i interp.Interp) error {
- if dst == nil {
- return errors.New("graphics: dst is nil")
- }
- if src == nil {
- return errors.New("graphics: src is nil")
- }
-
- return a.CenterFit(dst.Bounds(), src.Bounds()).Transform(dst, src, i)
-}
-
-// Scale produces a scaling transform of factors x and y.
-func (a Affine) Scale(x, y float64) Affine {
- return a.Mul(Affine{
- 1 / x, 0, 0,
- 0, 1 / y, 0,
- 0, 0, 1,
- })
-}
-
-// Rotate produces a clockwise rotation transform of angle, in radians.
-func (a Affine) Rotate(angle float64) Affine {
- s, c := math.Sincos(angle)
- return a.Mul(Affine{
- +c, +s, +0,
- -s, +c, +0,
- +0, +0, +1,
- })
-}
-
-// Shear produces a shear transform by the slopes x and y.
-func (a Affine) Shear(x, y float64) Affine {
- d := 1 - x*y
- return a.Mul(Affine{
- +1 / d, -x / d, 0,
- -y / d, +1 / d, 0,
- 0, 0, 1,
- })
-}
-
-// Translate produces a translation transform with pixel distances x and y.
-func (a Affine) Translate(x, y float64) Affine {
- return a.Mul(Affine{
- 1, 0, -x,
- 0, 1, -y,
- 0, 0, +1,
- })
-}
-
-// Center produces the affine transform, centered around the provided point.
-func (a Affine) Center(x, y float64) Affine {
- return I.Translate(-x, -y).Mul(a).Translate(x, y)
-}
-
-// CenterFit produces the affine transform, centered around the rectangles.
-// It is equivalent to
-// I.Translate(-<center of src>).Mul(a).Translate(<center of dst>)
-func (a Affine) CenterFit(dst, src image.Rectangle) Affine {
- dx := float64(dst.Min.X) + float64(dst.Dx())/2
- dy := float64(dst.Min.Y) + float64(dst.Dy())/2
- sx := float64(src.Min.X) + float64(src.Dx())/2
- sy := float64(src.Min.Y) + float64(src.Dy())/2
- return I.Translate(-sx, -sy).Mul(a).Translate(dx, dy)
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/blur.go b/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/blur.go
deleted file mode 100644
index 9a54d5ad5..000000000
--- a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/blur.go
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2011 The Graphics-Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package graphics
-
-import (
- "code.google.com/p/graphics-go/graphics/convolve"
- "errors"
- "image"
- "image/draw"
- "math"
-)
-
-// DefaultStdDev is the default blurring parameter.
-var DefaultStdDev = 0.5
-
-// BlurOptions are the blurring parameters.
-// StdDev is the standard deviation of the normal, higher is blurrier.
-// Size is the size of the kernel. If zero, it is set to Ceil(6 * StdDev).
-type BlurOptions struct {
- StdDev float64
- Size int
-}
-
-// Blur produces a blurred version of the image, using a Gaussian blur.
-func Blur(dst draw.Image, src image.Image, opt *BlurOptions) error {
- if dst == nil {
- return errors.New("graphics: dst is nil")
- }
- if src == nil {
- return errors.New("graphics: src is nil")
- }
-
- sd := DefaultStdDev
- size := 0
-
- if opt != nil {
- sd = opt.StdDev
- size = opt.Size
- }
-
- if size < 1 {
- size = int(math.Ceil(sd * 6))
- }
-
- kernel := make([]float64, 2*size+1)
- for i := 0; i <= size; i++ {
- x := float64(i) / sd
- x = math.Pow(1/math.SqrtE, x*x)
- kernel[size-i] = x
- kernel[size+i] = x
- }
-
- // Normalize the weights to sum to 1.0.
- kSum := 0.0
- for _, k := range kernel {
- kSum += k
- }
- for i, k := range kernel {
- kernel[i] = k / kSum
- }
-
- return convolve.Convolve(dst, src, &convolve.SeparableKernel{
- X: kernel,
- Y: kernel,
- })
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/blur_test.go b/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/blur_test.go
deleted file mode 100644
index 1d84fa604..000000000
--- a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/blur_test.go
+++ /dev/null
@@ -1,207 +0,0 @@
-// Copyright 2011 The Graphics-Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package graphics
-
-import (
- "code.google.com/p/graphics-go/graphics/graphicstest"
- "image"
- "image/color"
- "testing"
-
- _ "image/png"
-)
-
-var blurOneColorTests = []transformOneColorTest{
- {
- "1x1-blank", 1, 1, 1, 1,
- &BlurOptions{0.83, 1},
- []uint8{0xff},
- []uint8{0xff},
- },
- {
- "1x1-spreadblank", 1, 1, 1, 1,
- &BlurOptions{0.83, 2},
- []uint8{0xff},
- []uint8{0xff},
- },
- {
- "3x3-blank", 3, 3, 3, 3,
- &BlurOptions{0.83, 2},
- []uint8{
- 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff,
- },
- []uint8{
- 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff,
- },
- },
- {
- "3x3-dot", 3, 3, 3, 3,
- &BlurOptions{0.34, 1},
- []uint8{
- 0x00, 0x00, 0x00,
- 0x00, 0xff, 0x00,
- 0x00, 0x00, 0x00,
- },
- []uint8{
- 0x00, 0x03, 0x00,
- 0x03, 0xf2, 0x03,
- 0x00, 0x03, 0x00,
- },
- },
- {
- "5x5-dot", 5, 5, 5, 5,
- &BlurOptions{0.34, 1},
- []uint8{
- 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0xff, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00,
- },
- []uint8{
- 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x03, 0x00, 0x00,
- 0x00, 0x03, 0xf2, 0x03, 0x00,
- 0x00, 0x00, 0x03, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00,
- },
- },
- {
- "5x5-dot-spread", 5, 5, 5, 5,
- &BlurOptions{0.85, 1},
- []uint8{
- 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0xff, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00,
- },
- []uint8{
- 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x10, 0x20, 0x10, 0x00,
- 0x00, 0x20, 0x40, 0x20, 0x00,
- 0x00, 0x10, 0x20, 0x10, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00,
- },
- },
- {
- "4x4-box", 4, 4, 4, 4,
- &BlurOptions{0.34, 1},
- []uint8{
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0xff, 0xff, 0x00,
- 0x00, 0xff, 0xff, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- },
- []uint8{
- 0x00, 0x03, 0x03, 0x00,
- 0x03, 0xf8, 0xf8, 0x03,
- 0x03, 0xf8, 0xf8, 0x03,
- 0x00, 0x03, 0x03, 0x00,
- },
- },
- {
- "5x5-twodots", 5, 5, 5, 5,
- &BlurOptions{0.34, 1},
- []uint8{
- 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x96, 0x00, 0x96, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00,
- },
- []uint8{
- 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x02, 0x00, 0x02, 0x00,
- 0x02, 0x8e, 0x04, 0x8e, 0x02,
- 0x00, 0x02, 0x00, 0x02, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00,
- },
- },
-}
-
-func TestBlurOneColor(t *testing.T) {
- for _, oc := range blurOneColorTests {
- dst := oc.newDst()
- src := oc.newSrc()
- opt := oc.opt.(*BlurOptions)
- if err := Blur(dst, src, opt); err != nil {
- t.Fatal(err)
- }
-
- if !checkTransformTest(t, &oc, dst) {
- continue
- }
- }
-}
-
-func TestBlurEmpty(t *testing.T) {
- empty := image.NewRGBA(image.Rect(0, 0, 0, 0))
- if err := Blur(empty, empty, nil); err != nil {
- t.Fatal(err)
- }
-}
-
-func TestBlurGopher(t *testing.T) {
- src, err := graphicstest.LoadImage("../testdata/gopher.png")
- if err != nil {
- t.Fatal(err)
- }
-
- dst := image.NewRGBA(src.Bounds())
- if err = Blur(dst, src, &BlurOptions{StdDev: 1.1}); err != nil {
- t.Fatal(err)
- }
-
- cmp, err := graphicstest.LoadImage("../testdata/gopher-blur.png")
- if err != nil {
- t.Fatal(err)
- }
- err = graphicstest.ImageWithinTolerance(dst, cmp, 0x101)
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func benchBlur(b *testing.B, bounds image.Rectangle) {
- b.StopTimer()
-
- // Construct a fuzzy image.
- src := image.NewRGBA(bounds)
- for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
- for x := bounds.Min.X; x < bounds.Max.X; x++ {
- src.SetRGBA(x, y, color.RGBA{
- uint8(5 * x % 0x100),
- uint8(7 * y % 0x100),
- uint8((7*x + 5*y) % 0x100),
- 0xff,
- })
- }
- }
- dst := image.NewRGBA(bounds)
-
- b.StartTimer()
- for i := 0; i < b.N; i++ {
- Blur(dst, src, &BlurOptions{0.84, 3})
- }
-}
-
-func BenchmarkBlur400x400x3(b *testing.B) {
- benchBlur(b, image.Rect(0, 0, 400, 400))
-}
-
-// Exactly twice the pixel count of 400x400.
-func BenchmarkBlur400x800x3(b *testing.B) {
- benchBlur(b, image.Rect(0, 0, 400, 800))
-}
-
-// Exactly twice the pixel count of 400x800
-func BenchmarkBlur400x1600x3(b *testing.B) {
- benchBlur(b, image.Rect(0, 0, 400, 1600))
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/convolve/Makefile b/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/convolve/Makefile
deleted file mode 100644
index a5691fa30..000000000
--- a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/convolve/Makefile
+++ /dev/null
@@ -1,11 +0,0 @@
-# Copyright 2011 The Graphics-Go Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file.
-
-include $(GOROOT)/src/Make.inc
-
-TARG=code.google.com/p/graphics-go/graphics/convolve
-GOFILES=\
- convolve.go\
-
-include $(GOROOT)/src/Make.pkg
diff --git a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/convolve/convolve.go b/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/convolve/convolve.go
deleted file mode 100644
index da69496d0..000000000
--- a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/convolve/convolve.go
+++ /dev/null
@@ -1,274 +0,0 @@
-// Copyright 2011 The Graphics-Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package convolve
-
-import (
- "errors"
- "fmt"
- "image"
- "image/draw"
- "math"
-)
-
-// clamp clamps x to the range [x0, x1].
-func clamp(x, x0, x1 float64) float64 {
- if x < x0 {
- return x0
- }
- if x > x1 {
- return x1
- }
- return x
-}
-
-// Kernel is a square matrix that defines a convolution.
-type Kernel interface {
- // Weights returns the square matrix of weights in row major order.
- Weights() []float64
-}
-
-// SeparableKernel is a linearly separable, square convolution kernel.
-// X and Y are the per-axis weights. Each slice must be the same length, and
-// have an odd length. The middle element of each slice is the weight for the
-// central pixel. For example, the horizontal Sobel kernel is:
-// sobelX := &SeparableKernel{
-// X: []float64{-1, 0, +1},
-// Y: []float64{1, 2, 1},
-// }
-type SeparableKernel struct {
- X, Y []float64
-}
-
-func (k *SeparableKernel) Weights() []float64 {
- n := len(k.X)
- w := make([]float64, n*n)
- for y := 0; y < n; y++ {
- for x := 0; x < n; x++ {
- w[y*n+x] = k.X[x] * k.Y[y]
- }
- }
- return w
-}
-
-// fullKernel is a square convolution kernel.
-type fullKernel []float64
-
-func (k fullKernel) Weights() []float64 { return k }
-
-func kernelSize(w []float64) (size int, err error) {
- size = int(math.Sqrt(float64(len(w))))
- if size*size != len(w) {
- return 0, errors.New("graphics: kernel is not square")
- }
- if size%2 != 1 {
- return 0, errors.New("graphics: kernel size is not odd")
- }
- return size, nil
-}
-
-// NewKernel returns a square convolution kernel.
-func NewKernel(w []float64) (Kernel, error) {
- if _, err := kernelSize(w); err != nil {
- return nil, err
- }
- return fullKernel(w), nil
-}
-
-func convolveRGBASep(dst *image.RGBA, src image.Image, k *SeparableKernel) error {
- if len(k.X) != len(k.Y) {
- return fmt.Errorf("graphics: kernel not square (x %d, y %d)", len(k.X), len(k.Y))
- }
- if len(k.X)%2 != 1 {
- return fmt.Errorf("graphics: kernel length (%d) not odd", len(k.X))
- }
- radius := (len(k.X) - 1) / 2
-
- // buf holds the result of vertically blurring src.
- bounds := dst.Bounds()
- width, height := bounds.Dx(), bounds.Dy()
- buf := make([]float64, width*height*4)
- for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
- for x := bounds.Min.X; x < bounds.Max.X; x++ {
- var r, g, b, a float64
- // k0 is the kernel weight for the center pixel. This may be greater
- // than kernel[0], near the boundary of the source image, to avoid
- // vignetting.
- k0 := k.X[radius]
-
- // Add the pixels from above.
- for i := 1; i <= radius; i++ {
- f := k.Y[radius-i]
- if y-i < bounds.Min.Y {
- k0 += f
- } else {
- or, og, ob, oa := src.At(x, y-i).RGBA()
- r += float64(or>>8) * f
- g += float64(og>>8) * f
- b += float64(ob>>8) * f
- a += float64(oa>>8) * f
- }
- }
-
- // Add the pixels from below.
- for i := 1; i <= radius; i++ {
- f := k.Y[radius+i]
- if y+i >= bounds.Max.Y {
- k0 += f
- } else {
- or, og, ob, oa := src.At(x, y+i).RGBA()
- r += float64(or>>8) * f
- g += float64(og>>8) * f
- b += float64(ob>>8) * f
- a += float64(oa>>8) * f
- }
- }
-
- // Add the central pixel.
- or, og, ob, oa := src.At(x, y).RGBA()
- r += float64(or>>8) * k0
- g += float64(og>>8) * k0
- b += float64(ob>>8) * k0
- a += float64(oa>>8) * k0
-
- // Write to buf.
- o := (y-bounds.Min.Y)*width*4 + (x-bounds.Min.X)*4
- buf[o+0] = r
- buf[o+1] = g
- buf[o+2] = b
- buf[o+3] = a
- }
- }
-
- // dst holds the result of horizontally blurring buf.
- for y := 0; y < height; y++ {
- for x := 0; x < width; x++ {
- var r, g, b, a float64
- k0, off := k.X[radius], y*width*4+x*4
-
- // Add the pixels from the left.
- for i := 1; i <= radius; i++ {
- f := k.X[radius-i]
- if x-i < 0 {
- k0 += f
- } else {
- o := off - i*4
- r += buf[o+0] * f
- g += buf[o+1] * f
- b += buf[o+2] * f
- a += buf[o+3] * f
- }
- }
-
- // Add the pixels from the right.
- for i := 1; i <= radius; i++ {
- f := k.X[radius+i]
- if x+i >= width {
- k0 += f
- } else {
- o := off + i*4
- r += buf[o+0] * f
- g += buf[o+1] * f
- b += buf[o+2] * f
- a += buf[o+3] * f
- }
- }
-
- // Add the central pixel.
- r += buf[off+0] * k0
- g += buf[off+1] * k0
- b += buf[off+2] * k0
- a += buf[off+3] * k0
-
- // Write to dst, clamping to the range [0, 255].
- dstOff := (y-dst.Rect.Min.Y)*dst.Stride + (x-dst.Rect.Min.X)*4
- dst.Pix[dstOff+0] = uint8(clamp(r+0.5, 0, 255))
- dst.Pix[dstOff+1] = uint8(clamp(g+0.5, 0, 255))
- dst.Pix[dstOff+2] = uint8(clamp(b+0.5, 0, 255))
- dst.Pix[dstOff+3] = uint8(clamp(a+0.5, 0, 255))
- }
- }
-
- return nil
-}
-
-func convolveRGBA(dst *image.RGBA, src image.Image, k Kernel) error {
- b := dst.Bounds()
- bs := src.Bounds()
- w := k.Weights()
- size, err := kernelSize(w)
- if err != nil {
- return err
- }
- radius := (size - 1) / 2
-
- for y := b.Min.Y; y < b.Max.Y; y++ {
- for x := b.Min.X; x < b.Max.X; x++ {
- if !image.Pt(x, y).In(bs) {
- continue
- }
-
- var r, g, b, a, adj float64
- for cy := y - radius; cy <= y+radius; cy++ {
- for cx := x - radius; cx <= x+radius; cx++ {
- factor := w[(cy-y+radius)*size+cx-x+radius]
- if !image.Pt(cx, cy).In(bs) {
- adj += factor
- } else {
- sr, sg, sb, sa := src.At(cx, cy).RGBA()
- r += float64(sr>>8) * factor
- g += float64(sg>>8) * factor
- b += float64(sb>>8) * factor
- a += float64(sa>>8) * factor
- }
- }
- }
-
- if adj != 0 {
- sr, sg, sb, sa := src.At(x, y).RGBA()
- r += float64(sr>>8) * adj
- g += float64(sg>>8) * adj
- b += float64(sb>>8) * adj
- a += float64(sa>>8) * adj
- }
-
- off := (y-dst.Rect.Min.Y)*dst.Stride + (x-dst.Rect.Min.X)*4
- dst.Pix[off+0] = uint8(clamp(r+0.5, 0, 0xff))
- dst.Pix[off+1] = uint8(clamp(g+0.5, 0, 0xff))
- dst.Pix[off+2] = uint8(clamp(b+0.5, 0, 0xff))
- dst.Pix[off+3] = uint8(clamp(a+0.5, 0, 0xff))
- }
- }
-
- return nil
-}
-
-// Convolve produces dst by applying the convolution kernel k to src.
-func Convolve(dst draw.Image, src image.Image, k Kernel) (err error) {
- if dst == nil || src == nil || k == nil {
- return nil
- }
-
- b := dst.Bounds()
- dstRgba, ok := dst.(*image.RGBA)
- if !ok {
- dstRgba = image.NewRGBA(b)
- }
-
- switch k := k.(type) {
- case *SeparableKernel:
- err = convolveRGBASep(dstRgba, src, k)
- default:
- err = convolveRGBA(dstRgba, src, k)
- }
-
- if err != nil {
- return err
- }
-
- if !ok {
- draw.Draw(dst, b, dstRgba, b.Min, draw.Src)
- }
- return nil
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/convolve/convolve_test.go b/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/convolve/convolve_test.go
deleted file mode 100644
index f34d7afc8..000000000
--- a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/convolve/convolve_test.go
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright 2011 The Graphics-Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package convolve
-
-import (
- "code.google.com/p/graphics-go/graphics/graphicstest"
- "image"
- "reflect"
- "testing"
-
- _ "image/png"
-)
-
-func TestSeparableWeights(t *testing.T) {
- sobelXFull := []float64{
- -1, 0, 1,
- -2, 0, 2,
- -1, 0, 1,
- }
- sobelXSep := &SeparableKernel{
- X: []float64{-1, 0, +1},
- Y: []float64{1, 2, 1},
- }
- w := sobelXSep.Weights()
- if !reflect.DeepEqual(w, sobelXFull) {
- t.Errorf("got %v want %v", w, sobelXFull)
- }
-}
-
-func TestConvolve(t *testing.T) {
- kernFull, err := NewKernel([]float64{
- 0, 0, 0,
- 1, 1, 1,
- 0, 0, 0,
- })
- if err != nil {
- t.Fatal(err)
- }
-
- kernSep := &SeparableKernel{
- X: []float64{1, 1, 1},
- Y: []float64{0, 1, 0},
- }
-
- src, err := graphicstest.LoadImage("../../testdata/gopher.png")
- if err != nil {
- t.Fatal(err)
- }
- b := src.Bounds()
-
- sep := image.NewRGBA(b)
- if err = Convolve(sep, src, kernSep); err != nil {
- t.Fatal(err)
- }
-
- full := image.NewRGBA(b)
- Convolve(full, src, kernFull)
-
- err = graphicstest.ImageWithinTolerance(sep, full, 0x101)
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func TestConvolveNil(t *testing.T) {
- if err := Convolve(nil, nil, nil); err != nil {
- t.Fatal(err)
- }
-}
-
-func TestConvolveEmpty(t *testing.T) {
- empty := image.NewRGBA(image.Rect(0, 0, 0, 0))
- if err := Convolve(empty, empty, nil); err != nil {
- t.Fatal(err)
- }
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/detect/Makefile b/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/detect/Makefile
deleted file mode 100644
index 0b1c6cb3e..000000000
--- a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/detect/Makefile
+++ /dev/null
@@ -1,15 +0,0 @@
-# Copyright 2011 The Graphics-Go Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file.
-
-include $(GOROOT)/src/Make.inc
-
-TARG=code.google.com/p/graphics-go/graphics
-GOFILES=\
- detect.go\
- doc.go\
- integral.go\
- opencv_parser.go\
- projector.go\
-
-include $(GOROOT)/src/Make.pkg
diff --git a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/detect/detect.go b/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/detect/detect.go
deleted file mode 100644
index dde941cbe..000000000
--- a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/detect/detect.go
+++ /dev/null
@@ -1,133 +0,0 @@
-// Copyright 2011 The Graphics-Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package detect
-
-import (
- "image"
- "math"
-)
-
-// Feature is a Haar-like feature.
-type Feature struct {
- Rect image.Rectangle
- Weight float64
-}
-
-// Classifier is a set of features with a threshold.
-type Classifier struct {
- Feature []Feature
- Threshold float64
- Left float64
- Right float64
-}
-
-// CascadeStage is a cascade of classifiers.
-type CascadeStage struct {
- Classifier []Classifier
- Threshold float64
-}
-
-// Cascade is a degenerate tree of Haar-like classifiers.
-type Cascade struct {
- Stage []CascadeStage
- Size image.Point
-}
-
-// Match returns true if the full image is classified as an object.
-func (c *Cascade) Match(m image.Image) bool {
- return c.classify(newWindow(m))
-}
-
-// Find returns a set of areas of m that match the feature cascade c.
-func (c *Cascade) Find(m image.Image) []image.Rectangle {
- // TODO(crawshaw): Consider de-duping strategies.
- matches := []image.Rectangle{}
- w := newWindow(m)
-
- b := m.Bounds()
- origScale := c.Size
- for s := origScale; s.X < b.Dx() && s.Y < b.Dy(); s = s.Add(s.Div(10)) {
- // translate region and classify
- tx := image.Pt(s.X/10, 0)
- ty := image.Pt(0, s.Y/10)
- for r := image.Rect(0, 0, s.X, s.Y).Add(b.Min); r.In(b); r = r.Add(ty) {
- for r1 := r; r1.In(b); r1 = r1.Add(tx) {
- if c.classify(w.subWindow(r1)) {
- matches = append(matches, r1)
- }
- }
- }
- }
- return matches
-}
-
-type window struct {
- mi *integral
- miSq *integral
- rect image.Rectangle
- invArea float64
- stdDev float64
-}
-
-func (w *window) init() {
- w.invArea = 1 / float64(w.rect.Dx()*w.rect.Dy())
- mean := float64(w.mi.sum(w.rect)) * w.invArea
- vr := float64(w.miSq.sum(w.rect))*w.invArea - mean*mean
- if vr < 0 {
- vr = 1
- }
- w.stdDev = math.Sqrt(vr)
-}
-
-func newWindow(m image.Image) *window {
- mi, miSq := newIntegrals(m)
- res := &window{
- mi: mi,
- miSq: miSq,
- rect: m.Bounds(),
- }
- res.init()
- return res
-}
-
-func (w *window) subWindow(r image.Rectangle) *window {
- res := &window{
- mi: w.mi,
- miSq: w.miSq,
- rect: r,
- }
- res.init()
- return res
-}
-
-func (c *Classifier) classify(w *window, pr *projector) float64 {
- s := 0.0
- for _, f := range c.Feature {
- s += float64(w.mi.sum(pr.rect(f.Rect))) * f.Weight
- }
- s *= w.invArea // normalize to maintain scale invariance
- if s < c.Threshold*w.stdDev {
- return c.Left
- }
- return c.Right
-}
-
-func (s *CascadeStage) classify(w *window, pr *projector) bool {
- sum := 0.0
- for _, c := range s.Classifier {
- sum += c.classify(w, pr)
- }
- return sum >= s.Threshold
-}
-
-func (c *Cascade) classify(w *window) bool {
- pr := newProjector(w.rect, image.Rectangle{image.Pt(0, 0), c.Size})
- for _, s := range c.Stage {
- if !s.classify(w, pr) {
- return false
- }
- }
- return true
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/detect/detect_test.go b/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/detect/detect_test.go
deleted file mode 100644
index 8a2df113d..000000000
--- a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/detect/detect_test.go
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2011 The Graphics-Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package detect
-
-import (
- "image"
- "image/draw"
- "testing"
-)
-
-var (
- c0 = Classifier{
- Feature: []Feature{
- Feature{Rect: image.Rect(0, 0, 3, 4), Weight: 4.0},
- },
- Threshold: 0.2,
- Left: 0.8,
- Right: 0.2,
- }
- c1 = Classifier{
- Feature: []Feature{
- Feature{Rect: image.Rect(3, 4, 4, 5), Weight: 4.0},
- },
- Threshold: 0.2,
- Left: 0.8,
- Right: 0.2,
- }
- c2 = Classifier{
- Feature: []Feature{
- Feature{Rect: image.Rect(0, 0, 1, 1), Weight: +4.0},
- Feature{Rect: image.Rect(0, 0, 2, 2), Weight: -1.0},
- },
- Threshold: 0.2,
- Left: 0.8,
- Right: 0.2,
- }
-)
-
-func TestClassifier(t *testing.T) {
- m := image.NewGray(image.Rect(0, 0, 20, 20))
- b := m.Bounds()
- draw.Draw(m, image.Rect(0, 0, 20, 20), image.White, image.ZP, draw.Src)
- draw.Draw(m, image.Rect(3, 4, 4, 5), image.Black, image.ZP, draw.Src)
- w := newWindow(m)
- pr := newProjector(b, b)
-
- if res := c0.classify(w, pr); res != c0.Right {
- t.Errorf("c0 got %f want %f", res, c0.Right)
- }
- if res := c1.classify(w, pr); res != c1.Left {
- t.Errorf("c1 got %f want %f", res, c1.Left)
- }
- if res := c2.classify(w, pr); res != c1.Left {
- t.Errorf("c2 got %f want %f", res, c1.Left)
- }
-}
-
-func TestClassifierScale(t *testing.T) {
- m := image.NewGray(image.Rect(0, 0, 50, 50))
- b := m.Bounds()
- draw.Draw(m, image.Rect(0, 0, 8, 10), image.White, b.Min, draw.Src)
- draw.Draw(m, image.Rect(8, 10, 10, 13), image.Black, b.Min, draw.Src)
- w := newWindow(m)
- pr := newProjector(b, image.Rect(0, 0, 20, 20))
-
- if res := c0.classify(w, pr); res != c0.Right {
- t.Errorf("scaled c0 got %f want %f", res, c0.Right)
- }
- if res := c1.classify(w, pr); res != c1.Left {
- t.Errorf("scaled c1 got %f want %f", res, c1.Left)
- }
- if res := c2.classify(w, pr); res != c1.Left {
- t.Errorf("scaled c2 got %f want %f", res, c1.Left)
- }
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/detect/doc.go b/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/detect/doc.go
deleted file mode 100644
index a0f4e94cd..000000000
--- a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/detect/doc.go
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2011 The Graphics-Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-/*
-Package detect implements an object detector cascade.
-
-The technique used is a degenerate tree of Haar-like classifiers, commonly
-used for face detection. It is described in
-
- P. Viola, M. Jones.
- Rapid Object Detection using a Boosted Cascade of Simple Features, 2001
- IEEE Conference on Computer Vision and Pattern Recognition
-
-A Cascade can be constructed manually from a set of Classifiers in stages,
-or can be loaded from an XML file in the OpenCV format with
-
- classifier, _, err := detect.ParseOpenCV(r)
-
-The classifier can be used to determine if a full image is detected as an
-object using Detect
-
- if classifier.Match(m) {
- // m is an image of a face.
- }
-
-It is also possible to search an image for occurrences of an object
-
- objs := classifier.Find(m)
-*/
-package detect
diff --git a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/detect/integral.go b/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/detect/integral.go
deleted file mode 100644
index 814ced590..000000000
--- a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/detect/integral.go
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright 2011 The Graphics-Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package detect
-
-import (
- "image"
- "image/draw"
-)
-
-// integral is an image.Image-like structure that stores the cumulative
-// sum of the preceding pixels. This allows for O(1) summation of any
-// rectangular region within the image.
-type integral struct {
- // pix holds the cumulative sum of the image's pixels. The pixel at
- // (x, y) starts at pix[(y-rect.Min.Y)*stride + (x-rect.Min.X)*1].
- pix []uint64
- stride int
- rect image.Rectangle
-}
-
-func (p *integral) at(x, y int) uint64 {
- return p.pix[(y-p.rect.Min.Y)*p.stride+(x-p.rect.Min.X)]
-}
-
-func (p *integral) sum(b image.Rectangle) uint64 {
- c := p.at(b.Max.X-1, b.Max.Y-1)
- inY := b.Min.Y > p.rect.Min.Y
- inX := b.Min.X > p.rect.Min.X
- if inY && inX {
- c += p.at(b.Min.X-1, b.Min.Y-1)
- }
- if inY {
- c -= p.at(b.Max.X-1, b.Min.Y-1)
- }
- if inX {
- c -= p.at(b.Min.X-1, b.Max.Y-1)
- }
- return c
-}
-
-func (m *integral) integrate() {
- b := m.rect
- for y := b.Min.Y; y < b.Max.Y; y++ {
- for x := b.Min.X; x < b.Max.X; x++ {
- c := uint64(0)
- if y > b.Min.Y && x > b.Min.X {
- c += m.at(x-1, y)
- c += m.at(x, y-1)
- c -= m.at(x-1, y-1)
- } else if y > b.Min.Y {
- c += m.at(b.Min.X, y-1)
- } else if x > b.Min.X {
- c += m.at(x-1, b.Min.Y)
- }
- m.pix[(y-m.rect.Min.Y)*m.stride+(x-m.rect.Min.X)] += c
- }
- }
-}
-
-// newIntegrals returns the integral and the squared integral.
-func newIntegrals(src image.Image) (*integral, *integral) {
- b := src.Bounds()
- srcg, ok := src.(*image.Gray)
- if !ok {
- srcg = image.NewGray(b)
- draw.Draw(srcg, b, src, b.Min, draw.Src)
- }
-
- m := integral{
- pix: make([]uint64, b.Max.Y*b.Max.X),
- stride: b.Max.X,
- rect: b,
- }
- mSq := integral{
- pix: make([]uint64, b.Max.Y*b.Max.X),
- stride: b.Max.X,
- rect: b,
- }
- for y := b.Min.Y; y < b.Max.Y; y++ {
- for x := b.Min.X; x < b.Max.X; x++ {
- os := (y-b.Min.Y)*srcg.Stride + x - b.Min.X
- om := (y-b.Min.Y)*m.stride + x - b.Min.X
- c := uint64(srcg.Pix[os])
- m.pix[om] = c
- mSq.pix[om] = c * c
- }
- }
- m.integrate()
- mSq.integrate()
- return &m, &mSq
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/detect/integral_test.go b/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/detect/integral_test.go
deleted file mode 100644
index 0bc321a4d..000000000
--- a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/detect/integral_test.go
+++ /dev/null
@@ -1,156 +0,0 @@
-// Copyright 2011 The Graphics-Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package detect
-
-import (
- "bytes"
- "fmt"
- "image"
- "testing"
-)
-
-type integralTest struct {
- x int
- y int
- src []uint8
- res []uint8
-}
-
-var integralTests = []integralTest{
- {
- 1, 1,
- []uint8{0x01},
- []uint8{0x01},
- },
- {
- 2, 2,
- []uint8{
- 0x01, 0x02,
- 0x03, 0x04,
- },
- []uint8{
- 0x01, 0x03,
- 0x04, 0x0a,
- },
- },
- {
- 4, 4,
- []uint8{
- 0x02, 0x03, 0x00, 0x01,
- 0x01, 0x02, 0x01, 0x05,
- 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01,
- },
- []uint8{
- 0x02, 0x05, 0x05, 0x06,
- 0x03, 0x08, 0x09, 0x0f,
- 0x04, 0x0a, 0x0c, 0x13,
- 0x05, 0x0c, 0x0f, 0x17,
- },
- },
-}
-
-func sprintBox(box []byte, width, height int) string {
- buf := bytes.NewBuffer(nil)
- i := 0
- for y := 0; y < height; y++ {
- for x := 0; x < width; x++ {
- fmt.Fprintf(buf, " 0x%02x,", box[i])
- i++
- }
- buf.WriteByte('\n')
- }
- return buf.String()
-}
-
-func TestIntegral(t *testing.T) {
- for i, oc := range integralTests {
- src := &image.Gray{
- Pix: oc.src,
- Stride: oc.x,
- Rect: image.Rect(0, 0, oc.x, oc.y),
- }
- dst, _ := newIntegrals(src)
- res := make([]byte, len(dst.pix))
- for i, p := range dst.pix {
- res[i] = byte(p)
- }
-
- if !bytes.Equal(res, oc.res) {
- got := sprintBox(res, oc.x, oc.y)
- want := sprintBox(oc.res, oc.x, oc.y)
- t.Errorf("%d: got\n%s\n want\n%s", i, got, want)
- }
- }
-}
-
-func TestIntegralSum(t *testing.T) {
- src := &image.Gray{
- Pix: []uint8{
- 0x02, 0x03, 0x00, 0x01, 0x03,
- 0x01, 0x02, 0x01, 0x05, 0x05,
- 0x01, 0x01, 0x01, 0x01, 0x02,
- 0x01, 0x01, 0x01, 0x01, 0x07,
- 0x02, 0x01, 0x00, 0x03, 0x01,
- },
- Stride: 5,
- Rect: image.Rect(0, 0, 5, 5),
- }
- img, _ := newIntegrals(src)
-
- type sumTest struct {
- rect image.Rectangle
- sum uint64
- }
-
- var sumTests = []sumTest{
- {image.Rect(0, 0, 1, 1), 2},
- {image.Rect(0, 0, 2, 1), 5},
- {image.Rect(0, 0, 1, 3), 4},
- {image.Rect(1, 1, 3, 3), 5},
- {image.Rect(2, 2, 4, 4), 4},
- {image.Rect(4, 3, 5, 5), 8},
- {image.Rect(2, 4, 3, 5), 0},
- }
-
- for _, st := range sumTests {
- s := img.sum(st.rect)
- if s != st.sum {
- t.Errorf("%v: got %d want %d", st.rect, s, st.sum)
- return
- }
- }
-}
-
-func TestIntegralSubImage(t *testing.T) {
- m0 := &image.Gray{
- Pix: []uint8{
- 0x02, 0x03, 0x00, 0x01, 0x03,
- 0x01, 0x02, 0x01, 0x05, 0x05,
- 0x01, 0x04, 0x01, 0x01, 0x02,
- 0x01, 0x02, 0x01, 0x01, 0x07,
- 0x02, 0x01, 0x09, 0x03, 0x01,
- },
- Stride: 5,
- Rect: image.Rect(0, 0, 5, 5),
- }
- b := image.Rect(1, 1, 4, 4)
- m1 := m0.SubImage(b)
- mi0, _ := newIntegrals(m0)
- mi1, _ := newIntegrals(m1)
-
- sum0 := mi0.sum(b)
- sum1 := mi1.sum(b)
- if sum0 != sum1 {
- t.Errorf("b got %d want %d", sum0, sum1)
- }
-
- r0 := image.Rect(2, 2, 4, 4)
- sum0 = mi0.sum(r0)
- sum1 = mi1.sum(r0)
- if sum0 != sum1 {
- t.Errorf("r0 got %d want %d", sum1, sum0)
- }
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/detect/opencv_parser.go b/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/detect/opencv_parser.go
deleted file mode 100644
index 51ded1a1c..000000000
--- a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/detect/opencv_parser.go
+++ /dev/null
@@ -1,125 +0,0 @@
-// Copyright 2011 The Graphics-Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package detect
-
-import (
- "bytes"
- "encoding/xml"
- "errors"
- "fmt"
- "image"
- "io"
- "io/ioutil"
- "strconv"
- "strings"
-)
-
-type xmlFeature struct {
- Rects []string `xml:"grp>feature>rects>grp"`
- Tilted int `xml:"grp>feature>tilted"`
- Threshold float64 `xml:"grp>threshold"`
- Left float64 `xml:"grp>left_val"`
- Right float64 `xml:"grp>right_val"`
-}
-
-type xmlStages struct {
- Trees []xmlFeature `xml:"trees>grp"`
- Stage_threshold float64 `xml:"stage_threshold"`
- Parent int `xml:"parent"`
- Next int `xml:"next"`
-}
-
-type opencv_storage struct {
- Any struct {
- XMLName xml.Name
- Type string `xml:"type_id,attr"`
- Size string `xml:"size"`
- Stages []xmlStages `xml:"stages>grp"`
- } `xml:",any"`
-}
-
-func buildFeature(r string) (f Feature, err error) {
- var x, y, w, h int
- var weight float64
- _, err = fmt.Sscanf(r, "%d %d %d %d %f", &x, &y, &w, &h, &weight)
- if err != nil {
- return
- }
- f.Rect = image.Rect(x, y, x+w, y+h)
- f.Weight = weight
- return
-}
-
-func buildCascade(s *opencv_storage) (c *Cascade, name string, err error) {
- if s.Any.Type != "opencv-haar-classifier" {
- err = fmt.Errorf("got %s want opencv-haar-classifier", s.Any.Type)
- return
- }
- name = s.Any.XMLName.Local
-
- c = &Cascade{}
- sizes := strings.Split(s.Any.Size, " ")
- w, err := strconv.Atoi(sizes[0])
- if err != nil {
- return nil, "", err
- }
- h, err := strconv.Atoi(sizes[1])
- if err != nil {
- return nil, "", err
- }
- c.Size = image.Pt(w, h)
- c.Stage = []CascadeStage{}
-
- for _, stage := range s.Any.Stages {
- cs := CascadeStage{
- Classifier: []Classifier{},
- Threshold: stage.Stage_threshold,
- }
- for _, tree := range stage.Trees {
- if tree.Tilted != 0 {
- err = errors.New("Cascade does not support tilted features")
- return
- }
-
- cls := Classifier{
- Feature: []Feature{},
- Threshold: tree.Threshold,
- Left: tree.Left,
- Right: tree.Right,
- }
-
- for _, rect := range tree.Rects {
- f, err := buildFeature(rect)
- if err != nil {
- return nil, "", err
- }
- cls.Feature = append(cls.Feature, f)
- }
-
- cs.Classifier = append(cs.Classifier, cls)
- }
- c.Stage = append(c.Stage, cs)
- }
-
- return
-}
-
-// ParseOpenCV produces a detection Cascade from an OpenCV XML file.
-func ParseOpenCV(r io.Reader) (cascade *Cascade, name string, err error) {
- // BUG(crawshaw): tag-based parsing doesn't seem to work with <_>
- buf, err := ioutil.ReadAll(r)
- if err != nil {
- return
- }
- buf = bytes.Replace(buf, []byte("<_>"), []byte("<grp>"), -1)
- buf = bytes.Replace(buf, []byte("</_>"), []byte("</grp>"), -1)
-
- s := &opencv_storage{}
- err = xml.Unmarshal(buf, s)
- if err != nil {
- return
- }
- return buildCascade(s)
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/detect/opencv_parser_test.go b/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/detect/opencv_parser_test.go
deleted file mode 100644
index 343390499..000000000
--- a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/detect/opencv_parser_test.go
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2011 The Graphics-Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package detect
-
-import (
- "image"
- "os"
- "reflect"
- "testing"
-)
-
-var (
- classifier0 = Classifier{
- Feature: []Feature{
- Feature{Rect: image.Rect(0, 0, 3, 4), Weight: -1},
- Feature{Rect: image.Rect(3, 4, 5, 6), Weight: 3.1},
- },
- Threshold: 0.03,
- Left: 0.01,
- Right: 0.8,
- }
- classifier1 = Classifier{
- Feature: []Feature{
- Feature{Rect: image.Rect(3, 7, 17, 11), Weight: -3.2},
- Feature{Rect: image.Rect(3, 9, 17, 11), Weight: 2.},
- },
- Threshold: 0.11,
- Left: 0.03,
- Right: 0.83,
- }
- classifier2 = Classifier{
- Feature: []Feature{
- Feature{Rect: image.Rect(1, 1, 3, 3), Weight: -1.},
- Feature{Rect: image.Rect(3, 3, 5, 5), Weight: 2.5},
- },
- Threshold: 0.07,
- Left: 0.2,
- Right: 0.4,
- }
- cascade = Cascade{
- Stage: []CascadeStage{
- CascadeStage{
- Classifier: []Classifier{classifier0, classifier1},
- Threshold: 0.82,
- },
- CascadeStage{
- Classifier: []Classifier{classifier2},
- Threshold: 0.22,
- },
- },
- Size: image.Pt(20, 20),
- }
-)
-
-func TestParseOpenCV(t *testing.T) {
- file, err := os.Open("../../testdata/opencv.xml")
- if err != nil {
- t.Fatal(err)
- }
- defer file.Close()
-
- cascadeFile, name, err := ParseOpenCV(file)
- if err != nil {
- t.Fatal(err)
- }
- if name != "name_of_cascade" {
- t.Fatalf("name: got %s want name_of_cascade", name)
- }
-
- if !reflect.DeepEqual(cascade, *cascadeFile) {
- t.Errorf("got\n %v want\n %v", *cascadeFile, cascade)
- }
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/detect/projector.go b/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/detect/projector.go
deleted file mode 100644
index 1ebd6db59..000000000
--- a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/detect/projector.go
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2011 The Graphics-Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package detect
-
-import (
- "image"
-)
-
-// projector allows projecting from a source Rectangle onto a target Rectangle.
-type projector struct {
- // rx, ry is the scaling factor.
- rx, ry float64
- // dx, dy is the translation factor.
- dx, dy float64
- // r is the clipping region of the target.
- r image.Rectangle
-}
-
-// newProjector creates a Projector with source src and target dst.
-func newProjector(dst image.Rectangle, src image.Rectangle) *projector {
- return &projector{
- rx: float64(dst.Dx()) / float64(src.Dx()),
- ry: float64(dst.Dy()) / float64(src.Dy()),
- dx: float64(dst.Min.X - src.Min.X),
- dy: float64(dst.Min.Y - src.Min.Y),
- r: dst,
- }
-}
-
-// pt projects p from the source rectangle onto the target rectangle.
-func (s *projector) pt(p image.Point) image.Point {
- return image.Point{
- clamp(s.rx*float64(p.X)+s.dx, s.r.Min.X, s.r.Max.X),
- clamp(s.ry*float64(p.Y)+s.dy, s.r.Min.Y, s.r.Max.Y),
- }
-}
-
-// rect projects r from the source rectangle onto the target rectangle.
-func (s *projector) rect(r image.Rectangle) image.Rectangle {
- return image.Rectangle{s.pt(r.Min), s.pt(r.Max)}
-}
-
-// clamp rounds and clamps o to the integer range [x0, x1].
-func clamp(o float64, x0, x1 int) int {
- x := int(o + 0.5)
- if x < x0 {
- return x0
- }
- if x > x1 {
- return x1
- }
- return x
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/detect/projector_test.go b/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/detect/projector_test.go
deleted file mode 100644
index c6d0b0cd5..000000000
--- a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/detect/projector_test.go
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2011 The Graphics-Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package detect
-
-import (
- "image"
- "reflect"
- "testing"
-)
-
-type projectorTest struct {
- dst image.Rectangle
- src image.Rectangle
- pdst image.Rectangle
- psrc image.Rectangle
-}
-
-var projectorTests = []projectorTest{
- {
- image.Rect(0, 0, 6, 6),
- image.Rect(0, 0, 2, 2),
- image.Rect(0, 0, 6, 6),
- image.Rect(0, 0, 2, 2),
- },
- {
- image.Rect(0, 0, 6, 6),
- image.Rect(0, 0, 2, 2),
- image.Rect(3, 3, 6, 6),
- image.Rect(1, 1, 2, 2),
- },
- {
- image.Rect(30, 30, 40, 40),
- image.Rect(10, 10, 20, 20),
- image.Rect(32, 33, 34, 37),
- image.Rect(12, 13, 14, 17),
- },
-}
-
-func TestProjector(t *testing.T) {
- for i, tt := range projectorTests {
- pr := newProjector(tt.dst, tt.src)
- res := pr.rect(tt.psrc)
- if !reflect.DeepEqual(res, tt.pdst) {
- t.Errorf("%d: got %v want %v", i, res, tt.pdst)
- }
- }
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/graphicstest/Makefile b/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/graphicstest/Makefile
deleted file mode 100644
index 7bfdf22d8..000000000
--- a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/graphicstest/Makefile
+++ /dev/null
@@ -1,11 +0,0 @@
-# Copyright 2011 The Graphics-Go Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file.
-
-include $(GOROOT)/src/Make.inc
-
-TARG=code.google.com/p/graphics-go/graphics/graphicstest
-GOFILES=\
- graphicstest.go\
-
-include $(GOROOT)/src/Make.pkg
diff --git a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/graphicstest/graphicstest.go b/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/graphicstest/graphicstest.go
deleted file mode 100644
index ceb3a974d..000000000
--- a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/graphicstest/graphicstest.go
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright 2011 The Graphics-Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package graphicstest
-
-import (
- "bytes"
- "errors"
- "fmt"
- "image"
- "image/color"
- "os"
-)
-
-// LoadImage decodes an image from a file.
-func LoadImage(path string) (img image.Image, err error) {
- file, err := os.Open(path)
- if err != nil {
- return
- }
- defer file.Close()
- img, _, err = image.Decode(file)
- return
-}
-
-func delta(u0, u1 uint32) int {
- d := int(u0) - int(u1)
- if d < 0 {
- return -d
- }
- return d
-}
-
-func withinTolerance(c0, c1 color.Color, tol int) bool {
- r0, g0, b0, a0 := c0.RGBA()
- r1, g1, b1, a1 := c1.RGBA()
- r := delta(r0, r1)
- g := delta(g0, g1)
- b := delta(b0, b1)
- a := delta(a0, a1)
- return r <= tol && g <= tol && b <= tol && a <= tol
-}
-
-// ImageWithinTolerance checks that each pixel varies by no more than tol.
-func ImageWithinTolerance(m0, m1 image.Image, tol int) error {
- b0 := m0.Bounds()
- b1 := m1.Bounds()
- if !b0.Eq(b1) {
- return errors.New(fmt.Sprintf("got bounds %v want %v", b0, b1))
- }
-
- for y := b0.Min.Y; y < b0.Max.Y; y++ {
- for x := b0.Min.X; x < b0.Max.X; x++ {
- c0 := m0.At(x, y)
- c1 := m1.At(x, y)
- if !withinTolerance(c0, c1, tol) {
- e := fmt.Sprintf("got %v want %v at (%d, %d)", c0, c1, x, y)
- return errors.New(e)
- }
- }
- }
- return nil
-}
-
-// SprintBox pretty prints the array as a hexidecimal matrix.
-func SprintBox(box []byte, width, height int) string {
- buf := bytes.NewBuffer(nil)
- i := 0
- for y := 0; y < height; y++ {
- for x := 0; x < width; x++ {
- fmt.Fprintf(buf, " 0x%02x,", box[i])
- i++
- }
- buf.WriteByte('\n')
- }
- return buf.String()
-}
-
-// SprintImageR pretty prints the red channel of src. It looks like SprintBox.
-func SprintImageR(src *image.RGBA) string {
- w, h := src.Rect.Dx(), src.Rect.Dy()
- i := 0
- box := make([]byte, w*h)
- for y := src.Rect.Min.Y; y < src.Rect.Max.Y; y++ {
- for x := src.Rect.Min.X; x < src.Rect.Max.X; x++ {
- off := (y-src.Rect.Min.Y)*src.Stride + (x-src.Rect.Min.X)*4
- box[i] = src.Pix[off]
- i++
- }
- }
- return SprintBox(box, w, h)
-}
-
-// MakeRGBA returns an image with R, G, B taken from src.
-func MakeRGBA(src []uint8, width int) *image.RGBA {
- b := image.Rect(0, 0, width, len(src)/width)
- ret := image.NewRGBA(b)
- i := 0
- for y := b.Min.Y; y < b.Max.Y; y++ {
- for x := b.Min.X; x < b.Max.X; x++ {
- ret.SetRGBA(x, y, color.RGBA{
- R: src[i],
- G: src[i],
- B: src[i],
- A: 0xff,
- })
- i++
- }
- }
- return ret
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/interp/Makefile b/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/interp/Makefile
deleted file mode 100644
index 4d8f524fb..000000000
--- a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/interp/Makefile
+++ /dev/null
@@ -1,13 +0,0 @@
-# Copyright 2012 The Graphics-Go Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file.
-
-include $(GOROOT)/src/Make.inc
-
-TARG=code.google.com/p/graphics-go/graphics/interp
-GOFILES=\
- bilinear.go\
- doc.go\
- interp.go\
-
-include $(GOROOT)/src/Make.pkg
diff --git a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/interp/bilinear.go b/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/interp/bilinear.go
deleted file mode 100644
index e18321a15..000000000
--- a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/interp/bilinear.go
+++ /dev/null
@@ -1,206 +0,0 @@
-// Copyright 2012 The Graphics-Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package interp
-
-import (
- "image"
- "image/color"
- "math"
-)
-
-// Bilinear implements bilinear interpolation.
-var Bilinear Interp = bilinear{}
-
-type bilinear struct{}
-
-func (i bilinear) Interp(src image.Image, x, y float64) color.Color {
- if src, ok := src.(*image.RGBA); ok {
- return i.RGBA(src, x, y)
- }
- return bilinearGeneral(src, x, y)
-}
-
-func bilinearGeneral(src image.Image, x, y float64) color.Color {
- p := findLinearSrc(src.Bounds(), x, y)
- var fr, fg, fb, fa float64
- var r, g, b, a uint32
-
- r, g, b, a = src.At(p.low.X, p.low.Y).RGBA()
- fr += float64(r) * p.frac00
- fg += float64(g) * p.frac00
- fb += float64(b) * p.frac00
- fa += float64(a) * p.frac00
-
- r, g, b, a = src.At(p.high.X, p.low.Y).RGBA()
- fr += float64(r) * p.frac01
- fg += float64(g) * p.frac01
- fb += float64(b) * p.frac01
- fa += float64(a) * p.frac01
-
- r, g, b, a = src.At(p.low.X, p.high.Y).RGBA()
- fr += float64(r) * p.frac10
- fg += float64(g) * p.frac10
- fb += float64(b) * p.frac10
- fa += float64(a) * p.frac10
-
- r, g, b, a = src.At(p.high.X, p.high.Y).RGBA()
- fr += float64(r) * p.frac11
- fg += float64(g) * p.frac11
- fb += float64(b) * p.frac11
- fa += float64(a) * p.frac11
-
- var c color.RGBA64
- c.R = uint16(fr + 0.5)
- c.G = uint16(fg + 0.5)
- c.B = uint16(fb + 0.5)
- c.A = uint16(fa + 0.5)
- return c
-}
-
-func (bilinear) RGBA(src *image.RGBA, x, y float64) color.RGBA {
- p := findLinearSrc(src.Bounds(), x, y)
-
- // Array offsets for the surrounding pixels.
- off00 := offRGBA(src, p.low.X, p.low.Y)
- off01 := offRGBA(src, p.high.X, p.low.Y)
- off10 := offRGBA(src, p.low.X, p.high.Y)
- off11 := offRGBA(src, p.high.X, p.high.Y)
-
- var fr, fg, fb, fa float64
-
- fr += float64(src.Pix[off00+0]) * p.frac00
- fg += float64(src.Pix[off00+1]) * p.frac00
- fb += float64(src.Pix[off00+2]) * p.frac00
- fa += float64(src.Pix[off00+3]) * p.frac00
-
- fr += float64(src.Pix[off01+0]) * p.frac01
- fg += float64(src.Pix[off01+1]) * p.frac01
- fb += float64(src.Pix[off01+2]) * p.frac01
- fa += float64(src.Pix[off01+3]) * p.frac01
-
- fr += float64(src.Pix[off10+0]) * p.frac10
- fg += float64(src.Pix[off10+1]) * p.frac10
- fb += float64(src.Pix[off10+2]) * p.frac10
- fa += float64(src.Pix[off10+3]) * p.frac10
-
- fr += float64(src.Pix[off11+0]) * p.frac11
- fg += float64(src.Pix[off11+1]) * p.frac11
- fb += float64(src.Pix[off11+2]) * p.frac11
- fa += float64(src.Pix[off11+3]) * p.frac11
-
- var c color.RGBA
- c.R = uint8(fr + 0.5)
- c.G = uint8(fg + 0.5)
- c.B = uint8(fb + 0.5)
- c.A = uint8(fa + 0.5)
- return c
-}
-
-func (bilinear) Gray(src *image.Gray, x, y float64) color.Gray {
- p := findLinearSrc(src.Bounds(), x, y)
-
- // Array offsets for the surrounding pixels.
- off00 := offGray(src, p.low.X, p.low.Y)
- off01 := offGray(src, p.high.X, p.low.Y)
- off10 := offGray(src, p.low.X, p.high.Y)
- off11 := offGray(src, p.high.X, p.high.Y)
-
- var fc float64
- fc += float64(src.Pix[off00]) * p.frac00
- fc += float64(src.Pix[off01]) * p.frac01
- fc += float64(src.Pix[off10]) * p.frac10
- fc += float64(src.Pix[off11]) * p.frac11
-
- var c color.Gray
- c.Y = uint8(fc + 0.5)
- return c
-}
-
-type bilinearSrc struct {
- // Top-left and bottom-right interpolation sources
- low, high image.Point
- // Fraction of each pixel to take. The 0 suffix indicates
- // top/left, and the 1 suffix indicates bottom/right.
- frac00, frac01, frac10, frac11 float64
-}
-
-func findLinearSrc(b image.Rectangle, sx, sy float64) bilinearSrc {
- maxX := float64(b.Max.X)
- maxY := float64(b.Max.Y)
- minX := float64(b.Min.X)
- minY := float64(b.Min.Y)
- lowX := math.Floor(sx - 0.5)
- lowY := math.Floor(sy - 0.5)
- if lowX < minX {
- lowX = minX
- }
- if lowY < minY {
- lowY = minY
- }
-
- highX := math.Ceil(sx - 0.5)
- highY := math.Ceil(sy - 0.5)
- if highX >= maxX {
- highX = maxX - 1
- }
- if highY >= maxY {
- highY = maxY - 1
- }
-
- // In the variables below, the 0 suffix indicates top/left, and the
- // 1 suffix indicates bottom/right.
-
- // Center of each surrounding pixel.
- x00 := lowX + 0.5
- y00 := lowY + 0.5
- x01 := highX + 0.5
- y01 := lowY + 0.5
- x10 := lowX + 0.5
- y10 := highY + 0.5
- x11 := highX + 0.5
- y11 := highY + 0.5
-
- p := bilinearSrc{
- low: image.Pt(int(lowX), int(lowY)),
- high: image.Pt(int(highX), int(highY)),
- }
-
- // Literally, edge cases. If we are close enough to the edge of
- // the image, curtail the interpolation sources.
- if lowX == highX && lowY == highY {
- p.frac00 = 1.0
- } else if sy-minY <= 0.5 && sx-minX <= 0.5 {
- p.frac00 = 1.0
- } else if maxY-sy <= 0.5 && maxX-sx <= 0.5 {
- p.frac11 = 1.0
- } else if sy-minY <= 0.5 || lowY == highY {
- p.frac00 = x01 - sx
- p.frac01 = sx - x00
- } else if sx-minX <= 0.5 || lowX == highX {
- p.frac00 = y10 - sy
- p.frac10 = sy - y00
- } else if maxY-sy <= 0.5 {
- p.frac10 = x11 - sx
- p.frac11 = sx - x10
- } else if maxX-sx <= 0.5 {
- p.frac01 = y11 - sy
- p.frac11 = sy - y01
- } else {
- p.frac00 = (x01 - sx) * (y10 - sy)
- p.frac01 = (sx - x00) * (y11 - sy)
- p.frac10 = (x11 - sx) * (sy - y00)
- p.frac11 = (sx - x10) * (sy - y01)
- }
-
- return p
-}
-
-// TODO(crawshaw): When we have inlining, consider func (p *RGBA) Off(x, y) int
-func offRGBA(src *image.RGBA, x, y int) int {
- return (y-src.Rect.Min.Y)*src.Stride + (x-src.Rect.Min.X)*4
-}
-func offGray(src *image.Gray, x, y int) int {
- return (y-src.Rect.Min.Y)*src.Stride + (x - src.Rect.Min.X)
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/interp/bilinear_test.go b/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/interp/bilinear_test.go
deleted file mode 100644
index 242d70546..000000000
--- a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/interp/bilinear_test.go
+++ /dev/null
@@ -1,143 +0,0 @@
-// Copyright 2012 The Graphics-Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package interp
-
-import (
- "image"
- "image/color"
- "testing"
-)
-
-type interpTest struct {
- desc string
- src []uint8
- srcWidth int
- x, y float64
- expect uint8
-}
-
-func (p *interpTest) newSrc() *image.RGBA {
- b := image.Rect(0, 0, p.srcWidth, len(p.src)/p.srcWidth)
- src := image.NewRGBA(b)
- i := 0
- for y := b.Min.Y; y < b.Max.Y; y++ {
- for x := b.Min.X; x < b.Max.X; x++ {
- src.SetRGBA(x, y, color.RGBA{
- R: p.src[i],
- G: p.src[i],
- B: p.src[i],
- A: 0xff,
- })
- i++
- }
- }
- return src
-}
-
-var interpTests = []interpTest{
- {
- desc: "center of a single white pixel should match that pixel",
- src: []uint8{0x00},
- srcWidth: 1,
- x: 0.5,
- y: 0.5,
- expect: 0x00,
- },
- {
- desc: "middle of a square is equally weighted",
- src: []uint8{
- 0x00, 0xff,
- 0xff, 0x00,
- },
- srcWidth: 2,
- x: 1.0,
- y: 1.0,
- expect: 0x80,
- },
- {
- desc: "center of a pixel is just that pixel",
- src: []uint8{
- 0x00, 0xff,
- 0xff, 0x00,
- },
- srcWidth: 2,
- x: 1.5,
- y: 0.5,
- expect: 0xff,
- },
- {
- desc: "asymmetry abounds",
- src: []uint8{
- 0xaa, 0x11, 0x55,
- 0xff, 0x95, 0xdd,
- },
- srcWidth: 3,
- x: 2.0,
- y: 1.0,
- expect: 0x76, // (0x11 + 0x55 + 0x95 + 0xdd) / 4
- },
-}
-
-func TestBilinearRGBA(t *testing.T) {
- for _, p := range interpTests {
- src := p.newSrc()
-
- // Fast path.
- c := Bilinear.(RGBA).RGBA(src, p.x, p.y)
- if c.R != c.G || c.R != c.B || c.A != 0xff {
- t.Errorf("expect channels to match, got %v", c)
- continue
- }
- if c.R != p.expect {
- t.Errorf("%s: got 0x%02x want 0x%02x", p.desc, c.R, p.expect)
- continue
- }
-
- // Standard Interp should use the fast path.
- cStd := Bilinear.Interp(src, p.x, p.y)
- if cStd != c {
- t.Errorf("%s: standard mismatch got %v want %v", p.desc, cStd, c)
- continue
- }
-
- // General case should match the fast path.
- cGen := color.RGBAModel.Convert(bilinearGeneral(src, p.x, p.y))
- r0, g0, b0, a0 := c.RGBA()
- r1, g1, b1, a1 := cGen.RGBA()
- if r0 != r1 || g0 != g1 || b0 != b1 || a0 != a1 {
- t.Errorf("%s: general case mismatch got %v want %v", p.desc, c, cGen)
- continue
- }
- }
-}
-
-func TestBilinearSubImage(t *testing.T) {
- b0 := image.Rect(0, 0, 4, 4)
- src0 := image.NewRGBA(b0)
- b1 := image.Rect(1, 1, 3, 3)
- src1 := src0.SubImage(b1).(*image.RGBA)
- src1.Set(1, 1, color.RGBA{0x11, 0, 0, 0xff})
- src1.Set(2, 1, color.RGBA{0x22, 0, 0, 0xff})
- src1.Set(1, 2, color.RGBA{0x33, 0, 0, 0xff})
- src1.Set(2, 2, color.RGBA{0x44, 0, 0, 0xff})
-
- tests := []struct {
- x, y float64
- want uint8
- }{
- {1, 1, 0x11},
- {3, 1, 0x22},
- {1, 3, 0x33},
- {3, 3, 0x44},
- {2, 2, 0x2b},
- }
-
- for _, p := range tests {
- c := Bilinear.(RGBA).RGBA(src1, p.x, p.y)
- if c.R != p.want {
- t.Errorf("(%.0f, %.0f): got 0x%02x want 0x%02x", p.x, p.y, c.R, p.want)
- }
- }
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/interp/doc.go b/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/interp/doc.go
deleted file mode 100644
index b115534cc..000000000
--- a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/interp/doc.go
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2012 The Graphics-Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-/*
-Package interp implements image interpolation.
-
-An interpolator provides the Interp interface, which can be used
-to interpolate a pixel:
-
- c := interp.Bilinear.Interp(src, 1.2, 1.8)
-
-To interpolate a large number of RGBA or Gray pixels, an implementation
-may provide a fast-path by implementing the RGBA or Gray interfaces.
-
- i1, ok := i.(interp.RGBA)
- if ok {
- c := i1.RGBA(src, 1.2, 1.8)
- // use c.R, c.G, etc
- return
- }
- c := i.Interp(src, 1.2, 1.8)
- // use generic color.Color
-*/
-package interp
diff --git a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/interp/interp.go b/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/interp/interp.go
deleted file mode 100644
index 560637d4a..000000000
--- a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/interp/interp.go
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2012 The Graphics-Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package interp
-
-import (
- "image"
- "image/color"
-)
-
-// Interp interpolates an image's color at fractional co-ordinates.
-type Interp interface {
- // Interp interpolates (x, y).
- Interp(src image.Image, x, y float64) color.Color
-}
-
-// RGBA is a fast-path interpolation implementation for image.RGBA.
-// It is common for an Interp to also implement RGBA.
-type RGBA interface {
- // RGBA interpolates (x, y).
- RGBA(src *image.RGBA, x, y float64) color.RGBA
-}
-
-// Gray is a fast-path interpolation implementation for image.Gray.
-type Gray interface {
- // Gray interpolates (x, y).
- Gray(src *image.Gray, x, y float64) color.Gray
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/rotate.go b/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/rotate.go
deleted file mode 100644
index 62bde1a08..000000000
--- a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/rotate.go
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2011 The Graphics-Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package graphics
-
-import (
- "code.google.com/p/graphics-go/graphics/interp"
- "errors"
- "image"
- "image/draw"
-)
-
-// RotateOptions are the rotation parameters.
-// Angle is the angle, in radians, to rotate the image clockwise.
-type RotateOptions struct {
- Angle float64
-}
-
-// Rotate produces a rotated version of src, drawn onto dst.
-func Rotate(dst draw.Image, src image.Image, opt *RotateOptions) error {
- if dst == nil {
- return errors.New("graphics: dst is nil")
- }
- if src == nil {
- return errors.New("graphics: src is nil")
- }
-
- angle := 0.0
- if opt != nil {
- angle = opt.Angle
- }
-
- return I.Rotate(angle).TransformCenter(dst, src, interp.Bilinear)
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/rotate_test.go b/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/rotate_test.go
deleted file mode 100644
index bfc532a0a..000000000
--- a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/rotate_test.go
+++ /dev/null
@@ -1,169 +0,0 @@
-// Copyright 2011 The Graphics-Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package graphics
-
-import (
- "code.google.com/p/graphics-go/graphics/graphicstest"
- "image"
- "math"
- "testing"
-
- _ "image/png"
-)
-
-var rotateOneColorTests = []transformOneColorTest{
- {
- "onepixel-onequarter", 1, 1, 1, 1,
- &RotateOptions{math.Pi / 2},
- []uint8{0xff},
- []uint8{0xff},
- },
- {
- "onepixel-partial", 1, 1, 1, 1,
- &RotateOptions{math.Pi * 2.0 / 3.0},
- []uint8{0xff},
- []uint8{0xff},
- },
- {
- "onepixel-complete", 1, 1, 1, 1,
- &RotateOptions{2 * math.Pi},
- []uint8{0xff},
- []uint8{0xff},
- },
- {
- "even-onequarter", 2, 2, 2, 2,
- &RotateOptions{math.Pi / 2.0},
- []uint8{
- 0xff, 0x00,
- 0x00, 0xff,
- },
- []uint8{
- 0x00, 0xff,
- 0xff, 0x00,
- },
- },
- {
- "even-complete", 2, 2, 2, 2,
- &RotateOptions{2.0 * math.Pi},
- []uint8{
- 0xff, 0x00,
- 0x00, 0xff,
- },
- []uint8{
- 0xff, 0x00,
- 0x00, 0xff,
- },
- },
- {
- "line-partial", 3, 3, 3, 3,
- &RotateOptions{math.Pi * 1.0 / 3.0},
- []uint8{
- 0x00, 0x00, 0x00,
- 0xff, 0xff, 0xff,
- 0x00, 0x00, 0x00,
- },
- []uint8{
- 0xa2, 0x80, 0x00,
- 0x22, 0xff, 0x22,
- 0x00, 0x80, 0xa2,
- },
- },
- {
- "line-offset-partial", 3, 3, 3, 3,
- &RotateOptions{math.Pi * 3 / 2},
- []uint8{
- 0x00, 0x00, 0x00,
- 0x00, 0xff, 0xff,
- 0x00, 0x00, 0x00,
- },
- []uint8{
- 0x00, 0xff, 0x00,
- 0x00, 0xff, 0x00,
- 0x00, 0x00, 0x00,
- },
- },
- {
- "dot-partial", 4, 4, 4, 4,
- &RotateOptions{math.Pi},
- []uint8{
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0xff, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- },
- []uint8{
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0xff, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- },
- },
-}
-
-func TestRotateOneColor(t *testing.T) {
- for _, oc := range rotateOneColorTests {
- src := oc.newSrc()
- dst := oc.newDst()
-
- if err := Rotate(dst, src, oc.opt.(*RotateOptions)); err != nil {
- t.Errorf("rotate %s: %v", oc.desc, err)
- continue
- }
- if !checkTransformTest(t, &oc, dst) {
- continue
- }
- }
-}
-
-func TestRotateEmpty(t *testing.T) {
- empty := image.NewRGBA(image.Rect(0, 0, 0, 0))
- if err := Rotate(empty, empty, nil); err != nil {
- t.Fatal(err)
- }
-}
-
-func TestRotateGopherSide(t *testing.T) {
- src, err := graphicstest.LoadImage("../testdata/gopher.png")
- if err != nil {
- t.Fatal(err)
- }
-
- srcb := src.Bounds()
- dst := image.NewRGBA(image.Rect(0, 0, srcb.Dy(), srcb.Dx()))
- if err := Rotate(dst, src, &RotateOptions{math.Pi / 2.0}); err != nil {
- t.Fatal(err)
- }
-
- cmp, err := graphicstest.LoadImage("../testdata/gopher-rotate-side.png")
- if err != nil {
- t.Fatal(err)
- }
- err = graphicstest.ImageWithinTolerance(dst, cmp, 0x101)
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func TestRotateGopherPartial(t *testing.T) {
- src, err := graphicstest.LoadImage("../testdata/gopher.png")
- if err != nil {
- t.Fatal(err)
- }
-
- srcb := src.Bounds()
- dst := image.NewRGBA(image.Rect(0, 0, srcb.Dx(), srcb.Dy()))
- if err := Rotate(dst, src, &RotateOptions{math.Pi / 3.0}); err != nil {
- t.Fatal(err)
- }
-
- cmp, err := graphicstest.LoadImage("../testdata/gopher-rotate-partial.png")
- if err != nil {
- t.Fatal(err)
- }
- err = graphicstest.ImageWithinTolerance(dst, cmp, 0x101)
- if err != nil {
- t.Fatal(err)
- }
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/scale.go b/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/scale.go
deleted file mode 100644
index 7a7fe9696..000000000
--- a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/scale.go
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2011 The Graphics-Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package graphics
-
-import (
- "code.google.com/p/graphics-go/graphics/interp"
- "errors"
- "image"
- "image/draw"
-)
-
-// Scale produces a scaled version of the image using bilinear interpolation.
-func Scale(dst draw.Image, src image.Image) error {
- if dst == nil {
- return errors.New("graphics: dst is nil")
- }
- if src == nil {
- return errors.New("graphics: src is nil")
- }
-
- b := dst.Bounds()
- srcb := src.Bounds()
- if b.Empty() || srcb.Empty() {
- return nil
- }
- sx := float64(b.Dx()) / float64(srcb.Dx())
- sy := float64(b.Dy()) / float64(srcb.Dy())
- return I.Scale(sx, sy).Transform(dst, src, interp.Bilinear)
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/scale_test.go b/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/scale_test.go
deleted file mode 100644
index 9c2468f11..000000000
--- a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/scale_test.go
+++ /dev/null
@@ -1,153 +0,0 @@
-// Copyright 2011 The Graphics-Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package graphics
-
-import (
- "code.google.com/p/graphics-go/graphics/graphicstest"
- "image"
- "testing"
-
- _ "image/png"
-)
-
-var scaleOneColorTests = []transformOneColorTest{
- {
- "down-half",
- 1, 1,
- 2, 2,
- nil,
- []uint8{
- 0x80, 0x00,
- 0x00, 0x80,
- },
- []uint8{
- 0x40,
- },
- },
- {
- "up-double",
- 4, 4,
- 2, 2,
- nil,
- []uint8{
- 0x80, 0x00,
- 0x00, 0x80,
- },
- []uint8{
- 0x80, 0x60, 0x20, 0x00,
- 0x60, 0x50, 0x30, 0x20,
- 0x20, 0x30, 0x50, 0x60,
- 0x00, 0x20, 0x60, 0x80,
- },
- },
- {
- "up-doublewidth",
- 4, 2,
- 2, 2,
- nil,
- []uint8{
- 0x80, 0x00,
- 0x00, 0x80,
- },
- []uint8{
- 0x80, 0x60, 0x20, 0x00,
- 0x00, 0x20, 0x60, 0x80,
- },
- },
- {
- "up-doubleheight",
- 2, 4,
- 2, 2,
- nil,
- []uint8{
- 0x80, 0x00,
- 0x00, 0x80,
- },
- []uint8{
- 0x80, 0x00,
- 0x60, 0x20,
- 0x20, 0x60,
- 0x00, 0x80,
- },
- },
- {
- "up-partial",
- 3, 3,
- 2, 2,
- nil,
- []uint8{
- 0x80, 0x00,
- 0x00, 0x80,
- },
- []uint8{
- 0x80, 0x40, 0x00,
- 0x40, 0x40, 0x40,
- 0x00, 0x40, 0x80,
- },
- },
-}
-
-func TestScaleOneColor(t *testing.T) {
- for _, oc := range scaleOneColorTests {
- dst := oc.newDst()
- src := oc.newSrc()
- if err := Scale(dst, src); err != nil {
- t.Errorf("scale %s: %v", oc.desc, err)
- continue
- }
-
- if !checkTransformTest(t, &oc, dst) {
- continue
- }
- }
-}
-
-func TestScaleEmpty(t *testing.T) {
- empty := image.NewRGBA(image.Rect(0, 0, 0, 0))
- if err := Scale(empty, empty); err != nil {
- t.Fatal(err)
- }
-}
-
-func TestScaleGopher(t *testing.T) {
- dst := image.NewRGBA(image.Rect(0, 0, 100, 150))
-
- src, err := graphicstest.LoadImage("../testdata/gopher.png")
- if err != nil {
- t.Error(err)
- return
- }
-
- // Down-sample.
- if err := Scale(dst, src); err != nil {
- t.Fatal(err)
- }
- cmp, err := graphicstest.LoadImage("../testdata/gopher-100x150.png")
- if err != nil {
- t.Error(err)
- return
- }
- err = graphicstest.ImageWithinTolerance(dst, cmp, 0)
- if err != nil {
- t.Error(err)
- return
- }
-
- // Up-sample.
- dst = image.NewRGBA(image.Rect(0, 0, 500, 750))
- if err := Scale(dst, src); err != nil {
- t.Fatal(err)
- }
- cmp, err = graphicstest.LoadImage("../testdata/gopher-500x750.png")
- if err != nil {
- t.Error(err)
- return
- }
- err = graphicstest.ImageWithinTolerance(dst, cmp, 0)
- if err != nil {
- t.Error(err)
- return
- }
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/shared_test.go b/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/shared_test.go
deleted file mode 100644
index e1cd21fb3..000000000
--- a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/shared_test.go
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright 2011 The Graphics-Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package graphics
-
-import (
- "bytes"
- "code.google.com/p/graphics-go/graphics/graphicstest"
- "image"
- "image/color"
- "testing"
-)
-
-type transformOneColorTest struct {
- desc string
- dstWidth int
- dstHeight int
- srcWidth int
- srcHeight int
- opt interface{}
- src []uint8
- res []uint8
-}
-
-func (oc *transformOneColorTest) newSrc() *image.RGBA {
- b := image.Rect(0, 0, oc.srcWidth, oc.srcHeight)
- src := image.NewRGBA(b)
- i := 0
- for y := b.Min.Y; y < b.Max.Y; y++ {
- for x := b.Min.X; x < b.Max.X; x++ {
- src.SetRGBA(x, y, color.RGBA{
- R: oc.src[i],
- G: oc.src[i],
- B: oc.src[i],
- A: oc.src[i],
- })
- i++
- }
- }
- return src
-}
-
-func (oc *transformOneColorTest) newDst() *image.RGBA {
- return image.NewRGBA(image.Rect(0, 0, oc.dstWidth, oc.dstHeight))
-}
-
-func checkTransformTest(t *testing.T, oc *transformOneColorTest, dst *image.RGBA) bool {
- for ch := 0; ch < 4; ch++ {
- i := 0
- res := make([]byte, len(oc.res))
- for y := 0; y < oc.dstHeight; y++ {
- for x := 0; x < oc.dstWidth; x++ {
- off := (y-dst.Rect.Min.Y)*dst.Stride + (x-dst.Rect.Min.X)*4
- res[i] = dst.Pix[off+ch]
- i++
- }
- }
-
- if !bytes.Equal(res, oc.res) {
- got := graphicstest.SprintBox(res, oc.dstWidth, oc.dstHeight)
- want := graphicstest.SprintBox(oc.res, oc.dstWidth, oc.dstHeight)
- t.Errorf("%s: ch=%d\n got\n%s\n want\n%s", oc.desc, ch, got, want)
- return false
- }
- }
-
- return true
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/thumbnail.go b/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/thumbnail.go
deleted file mode 100644
index d3ad7e8f7..000000000
--- a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/thumbnail.go
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2011 The Graphics-Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package graphics
-
-import (
- "image"
- "image/draw"
-)
-
-// Thumbnail scales and crops src so it fits in dst.
-func Thumbnail(dst draw.Image, src image.Image) error {
- // Scale down src in the dimension that is closer to dst.
- sb := src.Bounds()
- db := dst.Bounds()
- rx := float64(sb.Dx()) / float64(db.Dx())
- ry := float64(sb.Dy()) / float64(db.Dy())
- var b image.Rectangle
- if rx < ry {
- b = image.Rect(0, 0, db.Dx(), int(float64(sb.Dy())/rx))
- } else {
- b = image.Rect(0, 0, int(float64(sb.Dx())/ry), db.Dy())
- }
-
- buf := image.NewRGBA(b)
- if err := Scale(buf, src); err != nil {
- return err
- }
-
- // Crop.
- // TODO(crawshaw): improve on center-alignment.
- var pt image.Point
- if rx < ry {
- pt.Y = (b.Dy() - db.Dy()) / 2
- } else {
- pt.X = (b.Dx() - db.Dx()) / 2
- }
- draw.Draw(dst, db, buf, pt, draw.Src)
- return nil
-}
diff --git a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/thumbnail_test.go b/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/thumbnail_test.go
deleted file mode 100644
index d12659f17..000000000
--- a/Godeps/_workspace/src/code.google.com/p/graphics-go/graphics/thumbnail_test.go
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2011 The Graphics-Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package graphics
-
-import (
- "code.google.com/p/graphics-go/graphics/graphicstest"
- "image"
- "testing"
-
- _ "image/png"
-)
-
-func TestThumbnailGopher(t *testing.T) {
- dst := image.NewRGBA(image.Rect(0, 0, 80, 80))
-
- src, err := graphicstest.LoadImage("../testdata/gopher.png")
- if err != nil {
- t.Fatal(err)
- }
- if err := Thumbnail(dst, src); err != nil {
- t.Fatal(err)
- }
- cmp, err := graphicstest.LoadImage("../testdata/gopher-thumb-80x80.png")
- if err != nil {
- t.Fatal(err)
- }
- err = graphicstest.ImageWithinTolerance(dst, cmp, 0)
- if err != nil {
- t.Error(err)
- }
-}
-
-func TestThumbnailLongGopher(t *testing.T) {
- dst := image.NewRGBA(image.Rect(0, 0, 50, 150))
-
- src, err := graphicstest.LoadImage("../testdata/gopher.png")
- if err != nil {
- t.Fatal(err)
- }
- if err := Thumbnail(dst, src); err != nil {
- t.Fatal(err)
- }
- cmp, err := graphicstest.LoadImage("../testdata/gopher-thumb-50x150.png")
- if err != nil {
- t.Fatal(err)
- }
- err = graphicstest.ImageWithinTolerance(dst, cmp, 0)
- if err != nil {
- t.Error(err)
- }
-}
diff --git a/Godeps/_workspace/src/github.com/disintegration/imaging/LICENSE b/Godeps/_workspace/src/github.com/disintegration/imaging/LICENSE
new file mode 100644
index 000000000..95ae410c3
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/disintegration/imaging/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2012-2014 Grigory Dryapak
+
+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/Godeps/_workspace/src/github.com/disintegration/imaging/README.md b/Godeps/_workspace/src/github.com/disintegration/imaging/README.md
new file mode 100644
index 000000000..16ac8cf6c
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/disintegration/imaging/README.md
@@ -0,0 +1,160 @@
+# Imaging
+
+Package imaging provides basic image manipulation functions (resize, rotate, flip, crop, etc.).
+This package is based on the standard Go image package and works best along with it.
+
+Image manipulation functions provided by the package take any image type
+that implements `image.Image` interface as an input, and return a new image of
+`*image.NRGBA` type (32bit RGBA colors, not premultiplied by alpha).
+
+## Installation
+
+Imaging requires Go version 1.2 or greater.
+
+ go get -u github.com/disintegration/imaging
+
+## Documentation
+
+http://godoc.org/github.com/disintegration/imaging
+
+## Usage examples
+
+A few usage examples can be found below. See the documentation for the full list of supported functions.
+
+### Image resizing
+```go
+// resize srcImage to size = 128x128px using the Lanczos filter
+dstImage128 := imaging.Resize(srcImage, 128, 128, imaging.Lanczos)
+
+// resize srcImage to width = 800px preserving the aspect ratio
+dstImage800 := imaging.Resize(srcImage, 800, 0, imaging.Lanczos)
+
+// scale down srcImage to fit the 800x600px bounding box
+dstImageFit := imaging.Fit(srcImage, 800, 600, imaging.Lanczos)
+
+// resize and crop the srcImage to make a 100x100px thumbnail
+dstImageThumb := imaging.Thumbnail(srcImage, 100, 100, imaging.Lanczos)
+```
+
+Imaging supports image resizing using various resampling filters. The most notable ones:
+- `NearestNeighbor` - Fastest resampling filter, no antialiasing.
+- `Box` - Simple and fast averaging filter appropriate for downscaling. When upscaling it's similar to NearestNeighbor.
+- `Linear` - Bilinear filter, smooth and reasonably fast.
+- `MitchellNetravali` - А smooth bicubic filter.
+- `CatmullRom` - A sharp bicubic filter.
+- `Gaussian` - Blurring filter that uses gaussian function, useful for noise removal.
+- `Lanczos` - High-quality resampling filter for photographic images yielding sharp results, but it's slower than cubic filters.
+
+The full list of supported filters: NearestNeighbor, Box, Linear, Hermite, MitchellNetravali, CatmullRom, BSpline, Gaussian, Lanczos, Hann, Hamming, Blackman, Bartlett, Welch, Cosine. Custom filters can be created using ResampleFilter struct.
+
+**Resampling filters comparison**
+
+Original image. Will be resized from 512x512px to 128x128px.
+
+![srcImage](http://disintegration.github.io/imaging/in_lena_bw_512.png)
+
+Filter | Resize result
+---|---
+`imaging.NearestNeighbor` | ![dstImage](http://disintegration.github.io/imaging/out_resize_down_nearest.png)
+`imaging.Box` | ![dstImage](http://disintegration.github.io/imaging/out_resize_down_box.png)
+`imaging.Linear` | ![dstImage](http://disintegration.github.io/imaging/out_resize_down_linear.png)
+`imaging.MitchellNetravali` | ![dstImage](http://disintegration.github.io/imaging/out_resize_down_mitchell.png)
+`imaging.CatmullRom` | ![dstImage](http://disintegration.github.io/imaging/out_resize_down_catrom.png)
+`imaging.Gaussian` | ![dstImage](http://disintegration.github.io/imaging/out_resize_down_gaussian.png)
+`imaging.Lanczos` | ![dstImage](http://disintegration.github.io/imaging/out_resize_down_lanczos.png)
+
+### Gaussian Blur
+```go
+dstImage := imaging.Blur(srcImage, 0.5)
+```
+
+Sigma parameter allows to control the strength of the blurring effect.
+
+Original image | Sigma = 0.5 | Sigma = 1.5
+---|---|---
+![srcImage](http://disintegration.github.io/imaging/in_lena_bw_128.png) | ![dstImage](http://disintegration.github.io/imaging/out_blur_0.5.png) | ![dstImage](http://disintegration.github.io/imaging/out_blur_1.5.png)
+
+### Sharpening
+```go
+dstImage := imaging.Sharpen(srcImage, 0.5)
+```
+
+Uses gaussian function internally. Sigma parameter allows to control the strength of the sharpening effect.
+
+Original image | Sigma = 0.5 | Sigma = 1.5
+---|---|---
+![srcImage](http://disintegration.github.io/imaging/in_lena_bw_128.png) | ![dstImage](http://disintegration.github.io/imaging/out_sharpen_0.5.png) | ![dstImage](http://disintegration.github.io/imaging/out_sharpen_1.5.png)
+
+### Gamma correction
+```go
+dstImage := imaging.AdjustGamma(srcImage, 0.75)
+```
+
+Original image | Gamma = 0.75 | Gamma = 1.25
+---|---|---
+![srcImage](http://disintegration.github.io/imaging/in_lena_bw_128.png) | ![dstImage](http://disintegration.github.io/imaging/out_gamma_0.75.png) | ![dstImage](http://disintegration.github.io/imaging/out_gamma_1.25.png)
+
+### Contrast adjustment
+```go
+dstImage := imaging.AdjustContrast(srcImage, 20)
+```
+
+Original image | Contrast = 20 | Contrast = -20
+---|---|---
+![srcImage](http://disintegration.github.io/imaging/in_lena_bw_128.png) | ![dstImage](http://disintegration.github.io/imaging/out_contrast_p20.png) | ![dstImage](http://disintegration.github.io/imaging/out_contrast_m20.png)
+
+### Brightness adjustment
+```go
+dstImage := imaging.AdjustBrightness(srcImage, 20)
+```
+
+Original image | Brightness = 20 | Brightness = -20
+---|---|---
+![srcImage](http://disintegration.github.io/imaging/in_lena_bw_128.png) | ![dstImage](http://disintegration.github.io/imaging/out_brightness_p20.png) | ![dstImage](http://disintegration.github.io/imaging/out_brightness_m20.png)
+
+
+### Complete code example
+Here is the code example that loads several images, makes thumbnails of them
+and combines them together side-by-side.
+
+```go
+package main
+
+import (
+ "image"
+ "image/color"
+
+ "github.com/disintegration/imaging"
+)
+
+func main() {
+
+ // input files
+ files := []string{"01.jpg", "02.jpg", "03.jpg"}
+
+ // load images and make 100x100 thumbnails of them
+ var thumbnails []image.Image
+ for _, file := range files {
+ img, err := imaging.Open(file)
+ if err != nil {
+ panic(err)
+ }
+ thumb := imaging.Thumbnail(img, 100, 100, imaging.CatmullRom)
+ thumbnails = append(thumbnails, thumb)
+ }
+
+ // create a new blank image
+ dst := imaging.New(100*len(thumbnails), 100, color.NRGBA{0, 0, 0, 0})
+
+ // paste thumbnails into the new image side by side
+ for i, thumb := range thumbnails {
+ dst = imaging.Paste(dst, thumb, image.Pt(i*100, 0))
+ }
+
+ // save the combined image to file
+ err := imaging.Save(dst, "dst.jpg")
+ if err != nil {
+ panic(err)
+ }
+}
+```
diff --git a/Godeps/_workspace/src/github.com/disintegration/imaging/adjust.go b/Godeps/_workspace/src/github.com/disintegration/imaging/adjust.go
new file mode 100644
index 000000000..9b1b83a4f
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/disintegration/imaging/adjust.go
@@ -0,0 +1,200 @@
+package imaging
+
+import (
+ "image"
+ "image/color"
+ "math"
+)
+
+// AdjustFunc applies the fn function to each pixel of the img image and returns the adjusted image.
+//
+// Example:
+//
+// dstImage = imaging.AdjustFunc(
+// srcImage,
+// func(c color.NRGBA) color.NRGBA {
+// // shift the red channel by 16
+// r := int(c.R) + 16
+// if r > 255 {
+// r = 255
+// }
+// return color.NRGBA{uint8(r), c.G, c.B, c.A}
+// }
+// )
+//
+func AdjustFunc(img image.Image, fn func(c color.NRGBA) color.NRGBA) *image.NRGBA {
+ src := toNRGBA(img)
+ width := src.Bounds().Max.X
+ height := src.Bounds().Max.Y
+ dst := image.NewNRGBA(image.Rect(0, 0, width, height))
+
+ parallel(height, func(partStart, partEnd int) {
+ for y := partStart; y < partEnd; y++ {
+ for x := 0; x < width; x++ {
+ i := y*src.Stride + x*4
+ j := y*dst.Stride + x*4
+
+ r := src.Pix[i+0]
+ g := src.Pix[i+1]
+ b := src.Pix[i+2]
+ a := src.Pix[i+3]
+
+ c := fn(color.NRGBA{r, g, b, a})
+
+ dst.Pix[j+0] = c.R
+ dst.Pix[j+1] = c.G
+ dst.Pix[j+2] = c.B
+ dst.Pix[j+3] = c.A
+ }
+ }
+ })
+
+ return dst
+}
+
+// AdjustGamma performs a gamma correction on the image and returns the adjusted image.
+// Gamma parameter must be positive. Gamma = 1.0 gives the original image.
+// Gamma less than 1.0 darkens the image and gamma greater than 1.0 lightens it.
+//
+// Example:
+//
+// dstImage = imaging.AdjustGamma(srcImage, 0.7)
+//
+func AdjustGamma(img image.Image, gamma float64) *image.NRGBA {
+ e := 1.0 / math.Max(gamma, 0.0001)
+ lut := make([]uint8, 256)
+
+ for i := 0; i < 256; i++ {
+ lut[i] = clamp(math.Pow(float64(i)/255.0, e) * 255.0)
+ }
+
+ fn := func(c color.NRGBA) color.NRGBA {
+ return color.NRGBA{lut[c.R], lut[c.G], lut[c.B], c.A}
+ }
+
+ return AdjustFunc(img, fn)
+}
+
+func sigmoid(a, b, x float64) float64 {
+ return 1 / (1 + math.Exp(b*(a-x)))
+}
+
+// AdjustSigmoid changes the contrast of the image using a sigmoidal function and returns the adjusted image.
+// It's a non-linear contrast change useful for photo adjustments as it preserves highlight and shadow detail.
+// The midpoint parameter is the midpoint of contrast that must be between 0 and 1, typically 0.5.
+// The factor parameter indicates how much to increase or decrease the contrast, typically in range (-10, 10).
+// If the factor parameter is positive the image contrast is increased otherwise the contrast is decreased.
+//
+// Examples:
+//
+// dstImage = imaging.AdjustSigmoid(srcImage, 0.5, 3.0) // increase the contrast
+// dstImage = imaging.AdjustSigmoid(srcImage, 0.5, -3.0) // decrease the contrast
+//
+func AdjustSigmoid(img image.Image, midpoint, factor float64) *image.NRGBA {
+ if factor == 0 {
+ return Clone(img)
+ }
+
+ lut := make([]uint8, 256)
+ a := math.Min(math.Max(midpoint, 0.0), 1.0)
+ b := math.Abs(factor)
+ sig0 := sigmoid(a, b, 0)
+ sig1 := sigmoid(a, b, 1)
+ e := 1.0e-6
+
+ if factor > 0 {
+ for i := 0; i < 256; i++ {
+ x := float64(i) / 255.0
+ sigX := sigmoid(a, b, x)
+ f := (sigX - sig0) / (sig1 - sig0)
+ lut[i] = clamp(f * 255.0)
+ }
+ } else {
+ for i := 0; i < 256; i++ {
+ x := float64(i) / 255.0
+ arg := math.Min(math.Max((sig1-sig0)*x+sig0, e), 1.0-e)
+ f := a - math.Log(1.0/arg-1.0)/b
+ lut[i] = clamp(f * 255.0)
+ }
+ }
+
+ fn := func(c color.NRGBA) color.NRGBA {
+ return color.NRGBA{lut[c.R], lut[c.G], lut[c.B], c.A}
+ }
+
+ return AdjustFunc(img, fn)
+}
+
+// AdjustContrast changes the contrast of the image using the percentage parameter and returns the adjusted image.
+// The percentage must be in range (-100, 100). The percentage = 0 gives the original image.
+// The percentage = -100 gives solid grey image.
+//
+// Examples:
+//
+// dstImage = imaging.AdjustContrast(srcImage, -10) // decrease image contrast by 10%
+// dstImage = imaging.AdjustContrast(srcImage, 20) // increase image contrast by 20%
+//
+func AdjustContrast(img image.Image, percentage float64) *image.NRGBA {
+ percentage = math.Min(math.Max(percentage, -100.0), 100.0)
+ lut := make([]uint8, 256)
+
+ v := (100.0 + percentage) / 100.0
+ for i := 0; i < 256; i++ {
+ if 0 <= v && v <= 1 {
+ lut[i] = clamp((0.5 + (float64(i)/255.0-0.5)*v) * 255.0)
+ } else if 1 < v && v < 2 {
+ lut[i] = clamp((0.5 + (float64(i)/255.0-0.5)*(1/(2.0-v))) * 255.0)
+ } else {
+ lut[i] = uint8(float64(i)/255.0+0.5) * 255
+ }
+ }
+
+ fn := func(c color.NRGBA) color.NRGBA {
+ return color.NRGBA{lut[c.R], lut[c.G], lut[c.B], c.A}
+ }
+
+ return AdjustFunc(img, fn)
+}
+
+// AdjustBrightness changes the brightness of the image using the percentage parameter and returns the adjusted image.
+// The percentage must be in range (-100, 100). The percentage = 0 gives the original image.
+// The percentage = -100 gives solid black image. The percentage = 100 gives solid white image.
+//
+// Examples:
+//
+// dstImage = imaging.AdjustBrightness(srcImage, -15) // decrease image brightness by 15%
+// dstImage = imaging.AdjustBrightness(srcImage, 10) // increase image brightness by 10%
+//
+func AdjustBrightness(img image.Image, percentage float64) *image.NRGBA {
+ percentage = math.Min(math.Max(percentage, -100.0), 100.0)
+ lut := make([]uint8, 256)
+
+ shift := 255.0 * percentage / 100.0
+ for i := 0; i < 256; i++ {
+ lut[i] = clamp(float64(i) + shift)
+ }
+
+ fn := func(c color.NRGBA) color.NRGBA {
+ return color.NRGBA{lut[c.R], lut[c.G], lut[c.B], c.A}
+ }
+
+ return AdjustFunc(img, fn)
+}
+
+// Grayscale produces grayscale version of the image.
+func Grayscale(img image.Image) *image.NRGBA {
+ fn := func(c color.NRGBA) color.NRGBA {
+ f := 0.299*float64(c.R) + 0.587*float64(c.G) + 0.114*float64(c.B)
+ y := uint8(f + 0.5)
+ return color.NRGBA{y, y, y, c.A}
+ }
+ return AdjustFunc(img, fn)
+}
+
+// Invert produces inverted (negated) version of the image.
+func Invert(img image.Image) *image.NRGBA {
+ fn := func(c color.NRGBA) color.NRGBA {
+ return color.NRGBA{255 - c.R, 255 - c.G, 255 - c.B, c.A}
+ }
+ return AdjustFunc(img, fn)
+}
diff --git a/Godeps/_workspace/src/github.com/disintegration/imaging/effects.go b/Godeps/_workspace/src/github.com/disintegration/imaging/effects.go
new file mode 100644
index 000000000..fe92e10a2
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/disintegration/imaging/effects.go
@@ -0,0 +1,187 @@
+package imaging
+
+import (
+ "image"
+ "math"
+)
+
+func gaussianBlurKernel(x, sigma float64) float64 {
+ return math.Exp(-(x*x)/(2*sigma*sigma)) / (sigma * math.Sqrt(2*math.Pi))
+}
+
+// Blur produces a blurred version of the image using a Gaussian function.
+// Sigma parameter must be positive and indicates how much the image will be blurred.
+//
+// Usage example:
+//
+// dstImage := imaging.Blur(srcImage, 3.5)
+//
+func Blur(img image.Image, sigma float64) *image.NRGBA {
+ if sigma <= 0 {
+ // sigma parameter must be positive!
+ return Clone(img)
+ }
+
+ src := toNRGBA(img)
+ radius := int(math.Ceil(sigma * 3.0))
+ kernel := make([]float64, radius+1)
+
+ for i := 0; i <= radius; i++ {
+ kernel[i] = gaussianBlurKernel(float64(i), sigma)
+ }
+
+ var dst *image.NRGBA
+ dst = blurHorizontal(src, kernel)
+ dst = blurVertical(dst, kernel)
+
+ return dst
+}
+
+func blurHorizontal(src *image.NRGBA, kernel []float64) *image.NRGBA {
+ radius := len(kernel) - 1
+ width := src.Bounds().Max.X
+ height := src.Bounds().Max.Y
+
+ dst := image.NewNRGBA(image.Rect(0, 0, width, height))
+
+ parallel(width, func(partStart, partEnd int) {
+ for x := partStart; x < partEnd; x++ {
+ start := x - radius
+ if start < 0 {
+ start = 0
+ }
+
+ end := x + radius
+ if end > width-1 {
+ end = width - 1
+ }
+
+ weightSum := 0.0
+ for ix := start; ix <= end; ix++ {
+ weightSum += kernel[absint(x-ix)]
+ }
+
+ for y := 0; y < height; y++ {
+
+ r, g, b, a := 0.0, 0.0, 0.0, 0.0
+ for ix := start; ix <= end; ix++ {
+ weight := kernel[absint(x-ix)]
+ i := y*src.Stride + ix*4
+ r += float64(src.Pix[i+0]) * weight
+ g += float64(src.Pix[i+1]) * weight
+ b += float64(src.Pix[i+2]) * weight
+ a += float64(src.Pix[i+3]) * weight
+ }
+
+ r = math.Min(math.Max(r/weightSum, 0.0), 255.0)
+ g = math.Min(math.Max(g/weightSum, 0.0), 255.0)
+ b = math.Min(math.Max(b/weightSum, 0.0), 255.0)
+ a = math.Min(math.Max(a/weightSum, 0.0), 255.0)
+
+ j := y*dst.Stride + x*4
+ dst.Pix[j+0] = uint8(r + 0.5)
+ dst.Pix[j+1] = uint8(g + 0.5)
+ dst.Pix[j+2] = uint8(b + 0.5)
+ dst.Pix[j+3] = uint8(a + 0.5)
+
+ }
+ }
+ })
+
+ return dst
+}
+
+func blurVertical(src *image.NRGBA, kernel []float64) *image.NRGBA {
+ radius := len(kernel) - 1
+ width := src.Bounds().Max.X
+ height := src.Bounds().Max.Y
+
+ dst := image.NewNRGBA(image.Rect(0, 0, width, height))
+
+ parallel(height, func(partStart, partEnd int) {
+ for y := partStart; y < partEnd; y++ {
+ start := y - radius
+ if start < 0 {
+ start = 0
+ }
+
+ end := y + radius
+ if end > height-1 {
+ end = height - 1
+ }
+
+ weightSum := 0.0
+ for iy := start; iy <= end; iy++ {
+ weightSum += kernel[absint(y-iy)]
+ }
+
+ for x := 0; x < width; x++ {
+
+ r, g, b, a := 0.0, 0.0, 0.0, 0.0
+ for iy := start; iy <= end; iy++ {
+ weight := kernel[absint(y-iy)]
+ i := iy*src.Stride + x*4
+ r += float64(src.Pix[i+0]) * weight
+ g += float64(src.Pix[i+1]) * weight
+ b += float64(src.Pix[i+2]) * weight
+ a += float64(src.Pix[i+3]) * weight
+ }
+
+ r = math.Min(math.Max(r/weightSum, 0.0), 255.0)
+ g = math.Min(math.Max(g/weightSum, 0.0), 255.0)
+ b = math.Min(math.Max(b/weightSum, 0.0), 255.0)
+ a = math.Min(math.Max(a/weightSum, 0.0), 255.0)
+
+ j := y*dst.Stride + x*4
+ dst.Pix[j+0] = uint8(r + 0.5)
+ dst.Pix[j+1] = uint8(g + 0.5)
+ dst.Pix[j+2] = uint8(b + 0.5)
+ dst.Pix[j+3] = uint8(a + 0.5)
+
+ }
+ }
+ })
+
+ return dst
+}
+
+// Sharpen produces a sharpened version of the image.
+// Sigma parameter must be positive and indicates how much the image will be sharpened.
+//
+// Usage example:
+//
+// dstImage := imaging.Sharpen(srcImage, 3.5)
+//
+func Sharpen(img image.Image, sigma float64) *image.NRGBA {
+ if sigma <= 0 {
+ // sigma parameter must be positive!
+ return Clone(img)
+ }
+
+ src := toNRGBA(img)
+ blurred := Blur(img, sigma)
+
+ width := src.Bounds().Max.X
+ height := src.Bounds().Max.Y
+ dst := image.NewNRGBA(image.Rect(0, 0, width, height))
+
+ parallel(height, func(partStart, partEnd int) {
+ for y := partStart; y < partEnd; y++ {
+ for x := 0; x < width; x++ {
+ i := y*src.Stride + x*4
+ for j := 0; j < 4; j++ {
+ k := i + j
+ val := int(src.Pix[k]) + (int(src.Pix[k]) - int(blurred.Pix[k]))
+ if val < 0 {
+ val = 0
+ } else if val > 255 {
+ val = 255
+ }
+ dst.Pix[k] = uint8(val)
+ }
+ }
+ }
+ })
+
+ return dst
+}
diff --git a/Godeps/_workspace/src/github.com/disintegration/imaging/helpers.go b/Godeps/_workspace/src/github.com/disintegration/imaging/helpers.go
new file mode 100644
index 000000000..983b64d71
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/disintegration/imaging/helpers.go
@@ -0,0 +1,392 @@
+/*
+Package imaging provides basic image manipulation functions (resize, rotate, flip, crop, etc.).
+This package is based on the standard Go image package and works best along with it.
+
+Image manipulation functions provided by the package take any image type
+that implements `image.Image` interface as an input, and return a new image of
+`*image.NRGBA` type (32bit RGBA colors, not premultiplied by alpha).
+*/
+package imaging
+
+import (
+ "errors"
+ "image"
+ "image/color"
+ "image/gif"
+ "image/jpeg"
+ "image/png"
+ "io"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "golang.org/x/image/bmp"
+ "golang.org/x/image/tiff"
+)
+
+type Format int
+
+const (
+ JPEG Format = iota
+ PNG
+ GIF
+ TIFF
+ BMP
+)
+
+func (f Format) String() string {
+ switch f {
+ case JPEG:
+ return "JPEG"
+ case PNG:
+ return "PNG"
+ case GIF:
+ return "GIF"
+ case TIFF:
+ return "TIFF"
+ case BMP:
+ return "BMP"
+ default:
+ return "Unsupported"
+ }
+}
+
+var (
+ ErrUnsupportedFormat = errors.New("imaging: unsupported image format")
+)
+
+// Decode reads an image from r.
+func Decode(r io.Reader) (image.Image, error) {
+ img, _, err := image.Decode(r)
+ if err != nil {
+ return nil, err
+ }
+ return toNRGBA(img), nil
+}
+
+// Open loads an image from file
+func Open(filename string) (image.Image, error) {
+ file, err := os.Open(filename)
+ if err != nil {
+ return nil, err
+ }
+ defer file.Close()
+ img, err := Decode(file)
+ return img, err
+}
+
+// Encode writes the image img to w in the specified format (JPEG, PNG, GIF, TIFF or BMP).
+func Encode(w io.Writer, img image.Image, format Format) error {
+ var err error
+ switch format {
+ case JPEG:
+ var rgba *image.RGBA
+ if nrgba, ok := img.(*image.NRGBA); ok {
+ if nrgba.Opaque() {
+ rgba = &image.RGBA{
+ Pix: nrgba.Pix,
+ Stride: nrgba.Stride,
+ Rect: nrgba.Rect,
+ }
+ }
+ }
+ if rgba != nil {
+ err = jpeg.Encode(w, rgba, &jpeg.Options{Quality: 95})
+ } else {
+ err = jpeg.Encode(w, img, &jpeg.Options{Quality: 95})
+ }
+
+ case PNG:
+ err = png.Encode(w, img)
+ case GIF:
+ err = gif.Encode(w, img, &gif.Options{NumColors: 256})
+ case TIFF:
+ err = tiff.Encode(w, img, &tiff.Options{Compression: tiff.Deflate, Predictor: true})
+ case BMP:
+ err = bmp.Encode(w, img)
+ default:
+ err = ErrUnsupportedFormat
+ }
+ return err
+}
+
+// Save saves the image to file with the specified filename.
+// The format is determined from the filename extension: "jpg" (or "jpeg"), "png", "gif", "tif" (or "tiff") and "bmp" are supported.
+func Save(img image.Image, filename string) (err error) {
+ formats := map[string]Format{
+ ".jpg": JPEG,
+ ".jpeg": JPEG,
+ ".png": PNG,
+ ".tif": TIFF,
+ ".tiff": TIFF,
+ ".bmp": BMP,
+ ".gif": GIF,
+ }
+
+ ext := strings.ToLower(filepath.Ext(filename))
+ f, ok := formats[ext]
+ if !ok {
+ return ErrUnsupportedFormat
+ }
+
+ file, err := os.Create(filename)
+ if err != nil {
+ return err
+ }
+ defer file.Close()
+
+ return Encode(file, img, f)
+}
+
+// New creates a new image with the specified width and height, and fills it with the specified color.
+func New(width, height int, fillColor color.Color) *image.NRGBA {
+ if width <= 0 || height <= 0 {
+ return &image.NRGBA{}
+ }
+
+ dst := image.NewNRGBA(image.Rect(0, 0, width, height))
+ c := color.NRGBAModel.Convert(fillColor).(color.NRGBA)
+
+ if c.R == 0 && c.G == 0 && c.B == 0 && c.A == 0 {
+ return dst
+ }
+
+ cs := []uint8{c.R, c.G, c.B, c.A}
+
+ // fill the first row
+ for x := 0; x < width; x++ {
+ copy(dst.Pix[x*4:(x+1)*4], cs)
+ }
+ // copy the first row to other rows
+ for y := 1; y < height; y++ {
+ copy(dst.Pix[y*dst.Stride:y*dst.Stride+width*4], dst.Pix[0:width*4])
+ }
+
+ return dst
+}
+
+// Clone returns a copy of the given image.
+func Clone(img image.Image) *image.NRGBA {
+ srcBounds := img.Bounds()
+ srcMinX := srcBounds.Min.X
+ srcMinY := srcBounds.Min.Y
+
+ dstBounds := srcBounds.Sub(srcBounds.Min)
+ dstW := dstBounds.Dx()
+ dstH := dstBounds.Dy()
+ dst := image.NewNRGBA(dstBounds)
+
+ switch src := img.(type) {
+
+ case *image.NRGBA:
+ rowSize := srcBounds.Dx() * 4
+ parallel(dstH, func(partStart, partEnd int) {
+ for dstY := partStart; dstY < partEnd; dstY++ {
+ di := dst.PixOffset(0, dstY)
+ si := src.PixOffset(srcMinX, srcMinY+dstY)
+ copy(dst.Pix[di:di+rowSize], src.Pix[si:si+rowSize])
+ }
+ })
+
+ case *image.NRGBA64:
+ parallel(dstH, func(partStart, partEnd int) {
+ for dstY := partStart; dstY < partEnd; dstY++ {
+ di := dst.PixOffset(0, dstY)
+ si := src.PixOffset(srcMinX, srcMinY+dstY)
+ for dstX := 0; dstX < dstW; dstX++ {
+
+ dst.Pix[di+0] = src.Pix[si+0]
+ dst.Pix[di+1] = src.Pix[si+2]
+ dst.Pix[di+2] = src.Pix[si+4]
+ dst.Pix[di+3] = src.Pix[si+6]
+
+ di += 4
+ si += 8
+
+ }
+ }
+ })
+
+ case *image.RGBA:
+ parallel(dstH, func(partStart, partEnd int) {
+ for dstY := partStart; dstY < partEnd; dstY++ {
+ di := dst.PixOffset(0, dstY)
+ si := src.PixOffset(srcMinX, srcMinY+dstY)
+ for dstX := 0; dstX < dstW; dstX++ {
+
+ a := src.Pix[si+3]
+ dst.Pix[di+3] = a
+ switch a {
+ case 0:
+ dst.Pix[di+0] = 0
+ dst.Pix[di+1] = 0
+ dst.Pix[di+2] = 0
+ case 0xff:
+ dst.Pix[di+0] = src.Pix[si+0]
+ dst.Pix[di+1] = src.Pix[si+1]
+ dst.Pix[di+2] = src.Pix[si+2]
+ default:
+ dst.Pix[di+0] = uint8(uint16(src.Pix[si+0]) * 0xff / uint16(a))
+ dst.Pix[di+1] = uint8(uint16(src.Pix[si+1]) * 0xff / uint16(a))
+ dst.Pix[di+2] = uint8(uint16(src.Pix[si+2]) * 0xff / uint16(a))
+ }
+
+ di += 4
+ si += 4
+
+ }
+ }
+ })
+
+ case *image.RGBA64:
+ parallel(dstH, func(partStart, partEnd int) {
+ for dstY := partStart; dstY < partEnd; dstY++ {
+ di := dst.PixOffset(0, dstY)
+ si := src.PixOffset(srcMinX, srcMinY+dstY)
+ for dstX := 0; dstX < dstW; dstX++ {
+
+ a := src.Pix[si+6]
+ dst.Pix[di+3] = a
+ switch a {
+ case 0:
+ dst.Pix[di+0] = 0
+ dst.Pix[di+1] = 0
+ dst.Pix[di+2] = 0
+ case 0xff:
+ dst.Pix[di+0] = src.Pix[si+0]
+ dst.Pix[di+1] = src.Pix[si+2]
+ dst.Pix[di+2] = src.Pix[si+4]
+ default:
+ dst.Pix[di+0] = uint8(uint16(src.Pix[si+0]) * 0xff / uint16(a))
+ dst.Pix[di+1] = uint8(uint16(src.Pix[si+2]) * 0xff / uint16(a))
+ dst.Pix[di+2] = uint8(uint16(src.Pix[si+4]) * 0xff / uint16(a))
+ }
+
+ di += 4
+ si += 8
+
+ }
+ }
+ })
+
+ case *image.Gray:
+ parallel(dstH, func(partStart, partEnd int) {
+ for dstY := partStart; dstY < partEnd; dstY++ {
+ di := dst.PixOffset(0, dstY)
+ si := src.PixOffset(srcMinX, srcMinY+dstY)
+ for dstX := 0; dstX < dstW; dstX++ {
+
+ c := src.Pix[si]
+ dst.Pix[di+0] = c
+ dst.Pix[di+1] = c
+ dst.Pix[di+2] = c
+ dst.Pix[di+3] = 0xff
+
+ di += 4
+ si += 1
+
+ }
+ }
+ })
+
+ case *image.Gray16:
+ parallel(dstH, func(partStart, partEnd int) {
+ for dstY := partStart; dstY < partEnd; dstY++ {
+ di := dst.PixOffset(0, dstY)
+ si := src.PixOffset(srcMinX, srcMinY+dstY)
+ for dstX := 0; dstX < dstW; dstX++ {
+
+ c := src.Pix[si]
+ dst.Pix[di+0] = c
+ dst.Pix[di+1] = c
+ dst.Pix[di+2] = c
+ dst.Pix[di+3] = 0xff
+
+ di += 4
+ si += 2
+
+ }
+ }
+ })
+
+ case *image.YCbCr:
+ parallel(dstH, func(partStart, partEnd int) {
+ for dstY := partStart; dstY < partEnd; dstY++ {
+ di := dst.PixOffset(0, dstY)
+ for dstX := 0; dstX < dstW; dstX++ {
+
+ srcX := srcMinX + dstX
+ srcY := srcMinY + dstY
+ siy := src.YOffset(srcX, srcY)
+ sic := src.COffset(srcX, srcY)
+ r, g, b := color.YCbCrToRGB(src.Y[siy], src.Cb[sic], src.Cr[sic])
+ dst.Pix[di+0] = r
+ dst.Pix[di+1] = g
+ dst.Pix[di+2] = b
+ dst.Pix[di+3] = 0xff
+
+ di += 4
+
+ }
+ }
+ })
+
+ case *image.Paletted:
+ plen := len(src.Palette)
+ pnew := make([]color.NRGBA, plen)
+ for i := 0; i < plen; i++ {
+ pnew[i] = color.NRGBAModel.Convert(src.Palette[i]).(color.NRGBA)
+ }
+
+ parallel(dstH, func(partStart, partEnd int) {
+ for dstY := partStart; dstY < partEnd; dstY++ {
+ di := dst.PixOffset(0, dstY)
+ si := src.PixOffset(srcMinX, srcMinY+dstY)
+ for dstX := 0; dstX < dstW; dstX++ {
+
+ c := pnew[src.Pix[si]]
+ dst.Pix[di+0] = c.R
+ dst.Pix[di+1] = c.G
+ dst.Pix[di+2] = c.B
+ dst.Pix[di+3] = c.A
+
+ di += 4
+ si += 1
+
+ }
+ }
+ })
+
+ default:
+ parallel(dstH, func(partStart, partEnd int) {
+ for dstY := partStart; dstY < partEnd; dstY++ {
+ di := dst.PixOffset(0, dstY)
+ for dstX := 0; dstX < dstW; dstX++ {
+
+ c := color.NRGBAModel.Convert(img.At(srcMinX+dstX, srcMinY+dstY)).(color.NRGBA)
+ dst.Pix[di+0] = c.R
+ dst.Pix[di+1] = c.G
+ dst.Pix[di+2] = c.B
+ dst.Pix[di+3] = c.A
+
+ di += 4
+
+ }
+ }
+ })
+
+ }
+
+ return dst
+}
+
+// This function used internally to convert any image type to NRGBA if needed.
+func toNRGBA(img image.Image) *image.NRGBA {
+ srcBounds := img.Bounds()
+ if srcBounds.Min.X == 0 && srcBounds.Min.Y == 0 {
+ if src0, ok := img.(*image.NRGBA); ok {
+ return src0
+ }
+ }
+ return Clone(img)
+}
diff --git a/Godeps/_workspace/src/github.com/disintegration/imaging/resize.go b/Godeps/_workspace/src/github.com/disintegration/imaging/resize.go
new file mode 100644
index 000000000..d2efd5c83
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/disintegration/imaging/resize.go
@@ -0,0 +1,564 @@
+package imaging
+
+import (
+ "image"
+ "math"
+)
+
+type iwpair struct {
+ i int
+ w int32
+}
+
+type pweights struct {
+ iwpairs []iwpair
+ wsum int32
+}
+
+func precomputeWeights(dstSize, srcSize int, filter ResampleFilter) []pweights {
+ du := float64(srcSize) / float64(dstSize)
+ scale := du
+ if scale < 1.0 {
+ scale = 1.0
+ }
+ ru := math.Ceil(scale * filter.Support)
+
+ out := make([]pweights, dstSize)
+
+ for v := 0; v < dstSize; v++ {
+ fu := (float64(v)+0.5)*du - 0.5
+
+ startu := int(math.Ceil(fu - ru))
+ if startu < 0 {
+ startu = 0
+ }
+ endu := int(math.Floor(fu + ru))
+ if endu > srcSize-1 {
+ endu = srcSize - 1
+ }
+
+ wsum := int32(0)
+ for u := startu; u <= endu; u++ {
+ w := int32(0xff * filter.Kernel((float64(u)-fu)/scale))
+ if w != 0 {
+ wsum += w
+ out[v].iwpairs = append(out[v].iwpairs, iwpair{u, w})
+ }
+ }
+ out[v].wsum = wsum
+ }
+
+ return out
+}
+
+// Resize resizes the image to the specified width and height using the specified resampling
+// filter and returns the transformed image. If one of width or height is 0, the image aspect
+// ratio is preserved.
+//
+// Supported resample filters: NearestNeighbor, Box, Linear, Hermite, MitchellNetravali,
+// CatmullRom, BSpline, Gaussian, Lanczos, Hann, Hamming, Blackman, Bartlett, Welch, Cosine.
+//
+// Usage example:
+//
+// dstImage := imaging.Resize(srcImage, 800, 600, imaging.Lanczos)
+//
+func Resize(img image.Image, width, height int, filter ResampleFilter) *image.NRGBA {
+ dstW, dstH := width, height
+
+ if dstW < 0 || dstH < 0 {
+ return &image.NRGBA{}
+ }
+ if dstW == 0 && dstH == 0 {
+ return &image.NRGBA{}
+ }
+
+ src := toNRGBA(img)
+
+ srcW := src.Bounds().Max.X
+ srcH := src.Bounds().Max.Y
+
+ if srcW <= 0 || srcH <= 0 {
+ return &image.NRGBA{}
+ }
+
+ // if new width or height is 0 then preserve aspect ratio, minimum 1px
+ if dstW == 0 {
+ tmpW := float64(dstH) * float64(srcW) / float64(srcH)
+ dstW = int(math.Max(1.0, math.Floor(tmpW+0.5)))
+ }
+ if dstH == 0 {
+ tmpH := float64(dstW) * float64(srcH) / float64(srcW)
+ dstH = int(math.Max(1.0, math.Floor(tmpH+0.5)))
+ }
+
+ var dst *image.NRGBA
+
+ if filter.Support <= 0.0 {
+ // nearest-neighbor special case
+ dst = resizeNearest(src, dstW, dstH)
+
+ } else {
+ // two-pass resize
+ if srcW != dstW {
+ dst = resizeHorizontal(src, dstW, filter)
+ } else {
+ dst = src
+ }
+
+ if srcH != dstH {
+ dst = resizeVertical(dst, dstH, filter)
+ }
+ }
+
+ return dst
+}
+
+func resizeHorizontal(src *image.NRGBA, width int, filter ResampleFilter) *image.NRGBA {
+ srcBounds := src.Bounds()
+ srcW := srcBounds.Max.X
+ srcH := srcBounds.Max.Y
+
+ dstW := width
+ dstH := srcH
+
+ dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
+
+ weights := precomputeWeights(dstW, srcW, filter)
+
+ parallel(dstH, func(partStart, partEnd int) {
+ for dstY := partStart; dstY < partEnd; dstY++ {
+ for dstX := 0; dstX < dstW; dstX++ {
+ var c [4]int32
+ for _, iw := range weights[dstX].iwpairs {
+ i := dstY*src.Stride + iw.i*4
+ c[0] += int32(src.Pix[i+0]) * iw.w
+ c[1] += int32(src.Pix[i+1]) * iw.w
+ c[2] += int32(src.Pix[i+2]) * iw.w
+ c[3] += int32(src.Pix[i+3]) * iw.w
+ }
+ j := dstY*dst.Stride + dstX*4
+ sum := weights[dstX].wsum
+ dst.Pix[j+0] = clampint32(int32(float32(c[0])/float32(sum) + 0.5))
+ dst.Pix[j+1] = clampint32(int32(float32(c[1])/float32(sum) + 0.5))
+ dst.Pix[j+2] = clampint32(int32(float32(c[2])/float32(sum) + 0.5))
+ dst.Pix[j+3] = clampint32(int32(float32(c[3])/float32(sum) + 0.5))
+ }
+ }
+ })
+
+ return dst
+}
+
+func resizeVertical(src *image.NRGBA, height int, filter ResampleFilter) *image.NRGBA {
+ srcBounds := src.Bounds()
+ srcW := srcBounds.Max.X
+ srcH := srcBounds.Max.Y
+
+ dstW := srcW
+ dstH := height
+
+ dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
+
+ weights := precomputeWeights(dstH, srcH, filter)
+
+ parallel(dstW, func(partStart, partEnd int) {
+
+ for dstX := partStart; dstX < partEnd; dstX++ {
+ for dstY := 0; dstY < dstH; dstY++ {
+ var c [4]int32
+ for _, iw := range weights[dstY].iwpairs {
+ i := iw.i*src.Stride + dstX*4
+ c[0] += int32(src.Pix[i+0]) * iw.w
+ c[1] += int32(src.Pix[i+1]) * iw.w
+ c[2] += int32(src.Pix[i+2]) * iw.w
+ c[3] += int32(src.Pix[i+3]) * iw.w
+ }
+ j := dstY*dst.Stride + dstX*4
+ sum := weights[dstY].wsum
+ dst.Pix[j+0] = clampint32(int32(float32(c[0])/float32(sum) + 0.5))
+ dst.Pix[j+1] = clampint32(int32(float32(c[1])/float32(sum) + 0.5))
+ dst.Pix[j+2] = clampint32(int32(float32(c[2])/float32(sum) + 0.5))
+ dst.Pix[j+3] = clampint32(int32(float32(c[3])/float32(sum) + 0.5))
+ }
+ }
+
+ })
+
+ return dst
+}
+
+// fast nearest-neighbor resize, no filtering
+func resizeNearest(src *image.NRGBA, width, height int) *image.NRGBA {
+ dstW, dstH := width, height
+
+ srcBounds := src.Bounds()
+ srcW := srcBounds.Max.X
+ srcH := srcBounds.Max.Y
+
+ dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
+
+ dx := float64(srcW) / float64(dstW)
+ dy := float64(srcH) / float64(dstH)
+
+ parallel(dstH, func(partStart, partEnd int) {
+
+ for dstY := partStart; dstY < partEnd; dstY++ {
+ fy := (float64(dstY)+0.5)*dy - 0.5
+
+ for dstX := 0; dstX < dstW; dstX++ {
+ fx := (float64(dstX)+0.5)*dx - 0.5
+
+ srcX := int(math.Min(math.Max(math.Floor(fx+0.5), 0.0), float64(srcW)))
+ srcY := int(math.Min(math.Max(math.Floor(fy+0.5), 0.0), float64(srcH)))
+
+ srcOff := srcY*src.Stride + srcX*4
+ dstOff := dstY*dst.Stride + dstX*4
+
+ copy(dst.Pix[dstOff:dstOff+4], src.Pix[srcOff:srcOff+4])
+ }
+ }
+
+ })
+
+ return dst
+}
+
+// Fit scales down the image using the specified resample filter to fit the specified
+// maximum width and height and returns the transformed image.
+//
+// Supported resample filters: NearestNeighbor, Box, Linear, Hermite, MitchellNetravali,
+// CatmullRom, BSpline, Gaussian, Lanczos, Hann, Hamming, Blackman, Bartlett, Welch, Cosine.
+//
+// Usage example:
+//
+// dstImage := imaging.Fit(srcImage, 800, 600, imaging.Lanczos)
+//
+func Fit(img image.Image, width, height int, filter ResampleFilter) *image.NRGBA {
+ maxW, maxH := width, height
+
+ if maxW <= 0 || maxH <= 0 {
+ return &image.NRGBA{}
+ }
+
+ srcBounds := img.Bounds()
+ srcW := srcBounds.Dx()
+ srcH := srcBounds.Dy()
+
+ if srcW <= 0 || srcH <= 0 {
+ return &image.NRGBA{}
+ }
+
+ if srcW <= maxW && srcH <= maxH {
+ return Clone(img)
+ }
+
+ srcAspectRatio := float64(srcW) / float64(srcH)
+ maxAspectRatio := float64(maxW) / float64(maxH)
+
+ var newW, newH int
+ if srcAspectRatio > maxAspectRatio {
+ newW = maxW
+ newH = int(float64(newW) / srcAspectRatio)
+ } else {
+ newH = maxH
+ newW = int(float64(newH) * srcAspectRatio)
+ }
+
+ return Resize(img, newW, newH, filter)
+}
+
+// Thumbnail scales the image up or down using the specified resample filter, crops it
+// to the specified width and hight and returns the transformed image.
+//
+// Supported resample filters: NearestNeighbor, Box, Linear, Hermite, MitchellNetravali,
+// CatmullRom, BSpline, Gaussian, Lanczos, Hann, Hamming, Blackman, Bartlett, Welch, Cosine.
+//
+// Usage example:
+//
+// dstImage := imaging.Thumbnail(srcImage, 100, 100, imaging.Lanczos)
+//
+func Thumbnail(img image.Image, width, height int, filter ResampleFilter) *image.NRGBA {
+ thumbW, thumbH := width, height
+
+ if thumbW <= 0 || thumbH <= 0 {
+ return &image.NRGBA{}
+ }
+
+ srcBounds := img.Bounds()
+ srcW := srcBounds.Dx()
+ srcH := srcBounds.Dy()
+
+ if srcW <= 0 || srcH <= 0 {
+ return &image.NRGBA{}
+ }
+
+ srcAspectRatio := float64(srcW) / float64(srcH)
+ thumbAspectRatio := float64(thumbW) / float64(thumbH)
+
+ var tmp image.Image
+ if srcAspectRatio > thumbAspectRatio {
+ tmp = Resize(img, 0, thumbH, filter)
+ } else {
+ tmp = Resize(img, thumbW, 0, filter)
+ }
+
+ return CropCenter(tmp, thumbW, thumbH)
+}
+
+// Resample filter struct. It can be used to make custom filters.
+//
+// Supported resample filters: NearestNeighbor, Box, Linear, Hermite, MitchellNetravali,
+// CatmullRom, BSpline, Gaussian, Lanczos, Hann, Hamming, Blackman, Bartlett, Welch, Cosine.
+//
+// General filter recommendations:
+//
+// - Lanczos
+// Probably the best resampling filter for photographic images yielding sharp results,
+// but it's slower than cubic filters (see below).
+//
+// - CatmullRom
+// A sharp cubic filter. It's a good filter for both upscaling and downscaling if sharp results are needed.
+//
+// - MitchellNetravali
+// A high quality cubic filter that produces smoother results with less ringing than CatmullRom.
+//
+// - BSpline
+// A good filter if a very smooth output is needed.
+//
+// - Linear
+// Bilinear interpolation filter, produces reasonably good, smooth output. It's faster than cubic filters.
+//
+// - Box
+// Simple and fast resampling filter appropriate for downscaling.
+// When upscaling it's similar to NearestNeighbor.
+//
+// - NearestNeighbor
+// Fastest resample filter, no antialiasing at all. Rarely used.
+//
+type ResampleFilter struct {
+ Support float64
+ Kernel func(float64) float64
+}
+
+// Nearest-neighbor filter, no anti-aliasing.
+var NearestNeighbor ResampleFilter
+
+// Box filter (averaging pixels).
+var Box ResampleFilter
+
+// Linear filter.
+var Linear ResampleFilter
+
+// Hermite cubic spline filter (BC-spline; B=0; C=0).
+var Hermite ResampleFilter
+
+// Mitchell-Netravali cubic filter (BC-spline; B=1/3; C=1/3).
+var MitchellNetravali ResampleFilter
+
+// Catmull-Rom - sharp cubic filter (BC-spline; B=0; C=0.5).
+var CatmullRom ResampleFilter
+
+// Cubic B-spline - smooth cubic filter (BC-spline; B=1; C=0).
+var BSpline ResampleFilter
+
+// Gaussian Blurring Filter.
+var Gaussian ResampleFilter
+
+// Bartlett-windowed sinc filter (3 lobes).
+var Bartlett ResampleFilter
+
+// Lanczos filter (3 lobes).
+var Lanczos ResampleFilter
+
+// Hann-windowed sinc filter (3 lobes).
+var Hann ResampleFilter
+
+// Hamming-windowed sinc filter (3 lobes).
+var Hamming ResampleFilter
+
+// Blackman-windowed sinc filter (3 lobes).
+var Blackman ResampleFilter
+
+// Welch-windowed sinc filter (parabolic window, 3 lobes).
+var Welch ResampleFilter
+
+// Cosine-windowed sinc filter (3 lobes).
+var Cosine ResampleFilter
+
+func bcspline(x, b, c float64) float64 {
+ x = math.Abs(x)
+ if x < 1.0 {
+ return ((12-9*b-6*c)*x*x*x + (-18+12*b+6*c)*x*x + (6 - 2*b)) / 6
+ }
+ if x < 2.0 {
+ return ((-b-6*c)*x*x*x + (6*b+30*c)*x*x + (-12*b-48*c)*x + (8*b + 24*c)) / 6
+ }
+ return 0
+}
+
+func sinc(x float64) float64 {
+ if x == 0 {
+ return 1
+ }
+ return math.Sin(math.Pi*x) / (math.Pi * x)
+}
+
+func init() {
+ NearestNeighbor = ResampleFilter{
+ Support: 0.0, // special case - not applying the filter
+ }
+
+ Box = ResampleFilter{
+ Support: 0.5,
+ Kernel: func(x float64) float64 {
+ x = math.Abs(x)
+ if x <= 0.5 {
+ return 1.0
+ }
+ return 0
+ },
+ }
+
+ Linear = ResampleFilter{
+ Support: 1.0,
+ Kernel: func(x float64) float64 {
+ x = math.Abs(x)
+ if x < 1.0 {
+ return 1.0 - x
+ }
+ return 0
+ },
+ }
+
+ Hermite = ResampleFilter{
+ Support: 1.0,
+ Kernel: func(x float64) float64 {
+ x = math.Abs(x)
+ if x < 1.0 {
+ return bcspline(x, 0.0, 0.0)
+ }
+ return 0
+ },
+ }
+
+ MitchellNetravali = ResampleFilter{
+ Support: 2.0,
+ Kernel: func(x float64) float64 {
+ x = math.Abs(x)
+ if x < 2.0 {
+ return bcspline(x, 1.0/3.0, 1.0/3.0)
+ }
+ return 0
+ },
+ }
+
+ CatmullRom = ResampleFilter{
+ Support: 2.0,
+ Kernel: func(x float64) float64 {
+ x = math.Abs(x)
+ if x < 2.0 {
+ return bcspline(x, 0.0, 0.5)
+ }
+ return 0
+ },
+ }
+
+ BSpline = ResampleFilter{
+ Support: 2.0,
+ Kernel: func(x float64) float64 {
+ x = math.Abs(x)
+ if x < 2.0 {
+ return bcspline(x, 1.0, 0.0)
+ }
+ return 0
+ },
+ }
+
+ Gaussian = ResampleFilter{
+ Support: 2.0,
+ Kernel: func(x float64) float64 {
+ x = math.Abs(x)
+ if x < 2.0 {
+ return math.Exp(-2 * x * x)
+ }
+ return 0
+ },
+ }
+
+ Bartlett = ResampleFilter{
+ Support: 3.0,
+ Kernel: func(x float64) float64 {
+ x = math.Abs(x)
+ if x < 3.0 {
+ return sinc(x) * (3.0 - x) / 3.0
+ }
+ return 0
+ },
+ }
+
+ Lanczos = ResampleFilter{
+ Support: 3.0,
+ Kernel: func(x float64) float64 {
+ x = math.Abs(x)
+ if x < 3.0 {
+ return sinc(x) * sinc(x/3.0)
+ }
+ return 0
+ },
+ }
+
+ Hann = ResampleFilter{
+ Support: 3.0,
+ Kernel: func(x float64) float64 {
+ x = math.Abs(x)
+ if x < 3.0 {
+ return sinc(x) * (0.5 + 0.5*math.Cos(math.Pi*x/3.0))
+ }
+ return 0
+ },
+ }
+
+ Hamming = ResampleFilter{
+ Support: 3.0,
+ Kernel: func(x float64) float64 {
+ x = math.Abs(x)
+ if x < 3.0 {
+ return sinc(x) * (0.54 + 0.46*math.Cos(math.Pi*x/3.0))
+ }
+ return 0
+ },
+ }
+
+ Blackman = ResampleFilter{
+ Support: 3.0,
+ Kernel: func(x float64) float64 {
+ x = math.Abs(x)
+ if x < 3.0 {
+ return sinc(x) * (0.42 - 0.5*math.Cos(math.Pi*x/3.0+math.Pi) + 0.08*math.Cos(2.0*math.Pi*x/3.0))
+ }
+ return 0
+ },
+ }
+
+ Welch = ResampleFilter{
+ Support: 3.0,
+ Kernel: func(x float64) float64 {
+ x = math.Abs(x)
+ if x < 3.0 {
+ return sinc(x) * (1.0 - (x * x / 9.0))
+ }
+ return 0
+ },
+ }
+
+ Cosine = ResampleFilter{
+ Support: 3.0,
+ Kernel: func(x float64) float64 {
+ x = math.Abs(x)
+ if x < 3.0 {
+ return sinc(x) * math.Cos((math.Pi/2.0)*(x/3.0))
+ }
+ return 0
+ },
+ }
+}
diff --git a/Godeps/_workspace/src/github.com/disintegration/imaging/tools.go b/Godeps/_workspace/src/github.com/disintegration/imaging/tools.go
new file mode 100644
index 000000000..2c39c900a
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/disintegration/imaging/tools.go
@@ -0,0 +1,182 @@
+package imaging
+
+import (
+ "image"
+ "math"
+)
+
+// Anchor is the anchor point for image alignment.
+type Anchor int
+
+const (
+ Center Anchor = iota
+ TopLeft
+ Top
+ TopRight
+ Left
+ Right
+ BottomLeft
+ Bottom
+ BottomRight
+)
+
+func anchorPt(b image.Rectangle, w, h int, anchor Anchor) image.Point {
+ var x, y int
+ switch anchor {
+ case TopLeft:
+ x = b.Min.X
+ y = b.Min.Y
+ case Top:
+ x = b.Min.X + (b.Dx()-w)/2
+ y = b.Min.Y
+ case TopRight:
+ x = b.Max.X - w
+ y = b.Min.Y
+ case Left:
+ x = b.Min.X
+ y = b.Min.Y + (b.Dy()-h)/2
+ case Right:
+ x = b.Max.X - w
+ y = b.Min.Y + (b.Dy()-h)/2
+ case BottomLeft:
+ x = b.Min.X
+ y = b.Max.Y - h
+ case Bottom:
+ x = b.Min.X + (b.Dx()-w)/2
+ y = b.Max.Y - h
+ case BottomRight:
+ x = b.Max.X - w
+ y = b.Max.Y - h
+ default:
+ x = b.Min.X + (b.Dx()-w)/2
+ y = b.Min.Y + (b.Dy()-h)/2
+ }
+ return image.Pt(x, y)
+}
+
+// Crop cuts out a rectangular region with the specified bounds
+// from the image and returns the cropped image.
+func Crop(img image.Image, rect image.Rectangle) *image.NRGBA {
+ src := toNRGBA(img)
+ srcRect := rect.Sub(img.Bounds().Min)
+ sub := src.SubImage(srcRect)
+ return Clone(sub) // New image Bounds().Min point will be (0, 0)
+}
+
+// CropAnchor cuts out a rectangular region with the specified size
+// from the image using the specified anchor point and returns the cropped image.
+func CropAnchor(img image.Image, width, height int, anchor Anchor) *image.NRGBA {
+ srcBounds := img.Bounds()
+ pt := anchorPt(srcBounds, width, height, anchor)
+ r := image.Rect(0, 0, width, height).Add(pt)
+ b := srcBounds.Intersect(r)
+ return Crop(img, b)
+}
+
+// CropCenter cuts out a rectangular region with the specified size
+// from the center of the image and returns the cropped image.
+func CropCenter(img image.Image, width, height int) *image.NRGBA {
+ return CropAnchor(img, width, height, Center)
+}
+
+// Paste pastes the img image to the background image at the specified position and returns the combined image.
+func Paste(background, img image.Image, pos image.Point) *image.NRGBA {
+ src := toNRGBA(img)
+ dst := Clone(background) // cloned image bounds start at (0, 0)
+ startPt := pos.Sub(background.Bounds().Min) // so we should translate start point
+ endPt := startPt.Add(src.Bounds().Size())
+ pasteBounds := image.Rectangle{startPt, endPt}
+
+ if dst.Bounds().Overlaps(pasteBounds) {
+ intersectBounds := dst.Bounds().Intersect(pasteBounds)
+
+ rowSize := intersectBounds.Dx() * 4
+ numRows := intersectBounds.Dy()
+
+ srcStartX := intersectBounds.Min.X - pasteBounds.Min.X
+ srcStartY := intersectBounds.Min.Y - pasteBounds.Min.Y
+
+ i0 := dst.PixOffset(intersectBounds.Min.X, intersectBounds.Min.Y)
+ j0 := src.PixOffset(srcStartX, srcStartY)
+
+ di := dst.Stride
+ dj := src.Stride
+
+ for row := 0; row < numRows; row++ {
+ copy(dst.Pix[i0:i0+rowSize], src.Pix[j0:j0+rowSize])
+ i0 += di
+ j0 += dj
+ }
+ }
+
+ return dst
+}
+
+// PasteCenter pastes the img image to the center of the background image and returns the combined image.
+func PasteCenter(background, img image.Image) *image.NRGBA {
+ bgBounds := background.Bounds()
+ bgW := bgBounds.Dx()
+ bgH := bgBounds.Dy()
+ bgMinX := bgBounds.Min.X
+ bgMinY := bgBounds.Min.Y
+
+ centerX := bgMinX + bgW/2
+ centerY := bgMinY + bgH/2
+
+ x0 := centerX - img.Bounds().Dx()/2
+ y0 := centerY - img.Bounds().Dy()/2
+
+ return Paste(background, img, image.Pt(x0, y0))
+}
+
+// Overlay draws the img image over the background image at given position
+// and returns the combined image. Opacity parameter is the opacity of the img
+// image layer, used to compose the images, it must be from 0.0 to 1.0.
+//
+// Usage examples:
+//
+// // draw the sprite over the background at position (50, 50)
+// dstImage := imaging.Overlay(backgroundImage, spriteImage, image.Pt(50, 50), 1.0)
+//
+// // blend two opaque images of the same size
+// dstImage := imaging.Overlay(imageOne, imageTwo, image.Pt(0, 0), 0.5)
+//
+func Overlay(background, img image.Image, pos image.Point, opacity float64) *image.NRGBA {
+ opacity = math.Min(math.Max(opacity, 0.0), 1.0) // check: 0.0 <= opacity <= 1.0
+
+ src := toNRGBA(img)
+ dst := Clone(background) // cloned image bounds start at (0, 0)
+ startPt := pos.Sub(background.Bounds().Min) // so we should translate start point
+ endPt := startPt.Add(src.Bounds().Size())
+ pasteBounds := image.Rectangle{startPt, endPt}
+
+ if dst.Bounds().Overlaps(pasteBounds) {
+ intersectBounds := dst.Bounds().Intersect(pasteBounds)
+
+ for y := intersectBounds.Min.Y; y < intersectBounds.Max.Y; y++ {
+ for x := intersectBounds.Min.X; x < intersectBounds.Max.X; x++ {
+ i := y*dst.Stride + x*4
+
+ srcX := x - pasteBounds.Min.X
+ srcY := y - pasteBounds.Min.Y
+ j := srcY*src.Stride + srcX*4
+
+ a1 := float64(dst.Pix[i+3])
+ a2 := float64(src.Pix[j+3])
+
+ coef2 := opacity * a2 / 255.0
+ coef1 := (1 - coef2) * a1 / 255.0
+ coefSum := coef1 + coef2
+ coef1 /= coefSum
+ coef2 /= coefSum
+
+ dst.Pix[i+0] = uint8(float64(dst.Pix[i+0])*coef1 + float64(src.Pix[j+0])*coef2)
+ dst.Pix[i+1] = uint8(float64(dst.Pix[i+1])*coef1 + float64(src.Pix[j+1])*coef2)
+ dst.Pix[i+2] = uint8(float64(dst.Pix[i+2])*coef1 + float64(src.Pix[j+2])*coef2)
+ dst.Pix[i+3] = uint8(math.Min(a1+a2*opacity*(255.0-a1)/255.0, 255.0))
+ }
+ }
+ }
+
+ return dst
+}
diff --git a/Godeps/_workspace/src/github.com/disintegration/imaging/transform.go b/Godeps/_workspace/src/github.com/disintegration/imaging/transform.go
new file mode 100644
index 000000000..a11601bba
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/disintegration/imaging/transform.go
@@ -0,0 +1,201 @@
+package imaging
+
+import (
+ "image"
+)
+
+// Rotate90 rotates the image 90 degrees counterclockwise and returns the transformed image.
+func Rotate90(img image.Image) *image.NRGBA {
+ src := toNRGBA(img)
+ srcW := src.Bounds().Max.X
+ srcH := src.Bounds().Max.Y
+ dstW := srcH
+ dstH := srcW
+ dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
+
+ parallel(dstH, func(partStart, partEnd int) {
+
+ for dstY := partStart; dstY < partEnd; dstY++ {
+ for dstX := 0; dstX < dstW; dstX++ {
+ srcX := dstH - dstY - 1
+ srcY := dstX
+
+ srcOff := srcY*src.Stride + srcX*4
+ dstOff := dstY*dst.Stride + dstX*4
+
+ copy(dst.Pix[dstOff:dstOff+4], src.Pix[srcOff:srcOff+4])
+ }
+ }
+
+ })
+
+ return dst
+}
+
+// Rotate180 rotates the image 180 degrees counterclockwise and returns the transformed image.
+func Rotate180(img image.Image) *image.NRGBA {
+ src := toNRGBA(img)
+ srcW := src.Bounds().Max.X
+ srcH := src.Bounds().Max.Y
+ dstW := srcW
+ dstH := srcH
+ dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
+
+ parallel(dstH, func(partStart, partEnd int) {
+
+ for dstY := partStart; dstY < partEnd; dstY++ {
+ for dstX := 0; dstX < dstW; dstX++ {
+ srcX := dstW - dstX - 1
+ srcY := dstH - dstY - 1
+
+ srcOff := srcY*src.Stride + srcX*4
+ dstOff := dstY*dst.Stride + dstX*4
+
+ copy(dst.Pix[dstOff:dstOff+4], src.Pix[srcOff:srcOff+4])
+ }
+ }
+
+ })
+
+ return dst
+}
+
+// Rotate270 rotates the image 270 degrees counterclockwise and returns the transformed image.
+func Rotate270(img image.Image) *image.NRGBA {
+ src := toNRGBA(img)
+ srcW := src.Bounds().Max.X
+ srcH := src.Bounds().Max.Y
+ dstW := srcH
+ dstH := srcW
+ dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
+
+ parallel(dstH, func(partStart, partEnd int) {
+
+ for dstY := partStart; dstY < partEnd; dstY++ {
+ for dstX := 0; dstX < dstW; dstX++ {
+ srcX := dstY
+ srcY := dstW - dstX - 1
+
+ srcOff := srcY*src.Stride + srcX*4
+ dstOff := dstY*dst.Stride + dstX*4
+
+ copy(dst.Pix[dstOff:dstOff+4], src.Pix[srcOff:srcOff+4])
+ }
+ }
+
+ })
+
+ return dst
+}
+
+// FlipH flips the image horizontally (from left to right) and returns the transformed image.
+func FlipH(img image.Image) *image.NRGBA {
+ src := toNRGBA(img)
+ srcW := src.Bounds().Max.X
+ srcH := src.Bounds().Max.Y
+ dstW := srcW
+ dstH := srcH
+ dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
+
+ parallel(dstH, func(partStart, partEnd int) {
+
+ for dstY := partStart; dstY < partEnd; dstY++ {
+ for dstX := 0; dstX < dstW; dstX++ {
+ srcX := dstW - dstX - 1
+ srcY := dstY
+
+ srcOff := srcY*src.Stride + srcX*4
+ dstOff := dstY*dst.Stride + dstX*4
+
+ copy(dst.Pix[dstOff:dstOff+4], src.Pix[srcOff:srcOff+4])
+ }
+ }
+
+ })
+
+ return dst
+}
+
+// FlipV flips the image vertically (from top to bottom) and returns the transformed image.
+func FlipV(img image.Image) *image.NRGBA {
+ src := toNRGBA(img)
+ srcW := src.Bounds().Max.X
+ srcH := src.Bounds().Max.Y
+ dstW := srcW
+ dstH := srcH
+ dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
+
+ parallel(dstH, func(partStart, partEnd int) {
+
+ for dstY := partStart; dstY < partEnd; dstY++ {
+ for dstX := 0; dstX < dstW; dstX++ {
+ srcX := dstX
+ srcY := dstH - dstY - 1
+
+ srcOff := srcY*src.Stride + srcX*4
+ dstOff := dstY*dst.Stride + dstX*4
+
+ copy(dst.Pix[dstOff:dstOff+4], src.Pix[srcOff:srcOff+4])
+ }
+ }
+
+ })
+
+ return dst
+}
+
+// Transpose flips the image horizontally and rotates 90 degrees counter-clockwise.
+func Transpose(img image.Image) *image.NRGBA {
+ src := toNRGBA(img)
+ srcW := src.Bounds().Max.X
+ srcH := src.Bounds().Max.Y
+ dstW := srcH
+ dstH := srcW
+ dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
+
+ parallel(dstH, func(partStart, partEnd int) {
+
+ for dstY := partStart; dstY < partEnd; dstY++ {
+ for dstX := 0; dstX < dstW; dstX++ {
+ srcX := dstY
+ srcY := dstX
+
+ srcOff := srcY*src.Stride + srcX*4
+ dstOff := dstY*dst.Stride + dstX*4
+
+ copy(dst.Pix[dstOff:dstOff+4], src.Pix[srcOff:srcOff+4])
+ }
+ }
+
+ })
+
+ return dst
+}
+
+// Transverse flips the image vertically and rotates 90 degrees counter-clockwise.
+func Transverse(img image.Image) *image.NRGBA {
+ src := toNRGBA(img)
+ srcW := src.Bounds().Max.X
+ srcH := src.Bounds().Max.Y
+ dstW := srcH
+ dstH := srcW
+ dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
+
+ parallel(dstH, func(partStart, partEnd int) {
+
+ for dstY := partStart; dstY < partEnd; dstY++ {
+ for dstX := 0; dstX < dstW; dstX++ {
+ srcX := dstH - dstY - 1
+ srcY := dstW - dstX - 1
+
+ srcOff := srcY*src.Stride + srcX*4
+ dstOff := dstY*dst.Stride + dstX*4
+
+ copy(dst.Pix[dstOff:dstOff+4], src.Pix[srcOff:srcOff+4])
+ }
+ }
+
+ })
+
+ return dst
+}
diff --git a/Godeps/_workspace/src/github.com/disintegration/imaging/utils.go b/Godeps/_workspace/src/github.com/disintegration/imaging/utils.go
new file mode 100644
index 000000000..8b1ab8adb
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/disintegration/imaging/utils.go
@@ -0,0 +1,77 @@
+package imaging
+
+import (
+ "math"
+ "runtime"
+ "sync"
+ "sync/atomic"
+)
+
+var parallelizationEnabled = true
+
+// if GOMAXPROCS = 1: no goroutines used
+// if GOMAXPROCS > 1: spawn N=GOMAXPROCS workers in separate goroutines
+func parallel(dataSize int, fn func(partStart, partEnd int)) {
+ numGoroutines := 1
+ partSize := dataSize
+
+ if parallelizationEnabled {
+ numProcs := runtime.GOMAXPROCS(0)
+ if numProcs > 1 {
+ numGoroutines = numProcs
+ partSize = dataSize / (numGoroutines * 10)
+ if partSize < 1 {
+ partSize = 1
+ }
+ }
+ }
+
+ if numGoroutines == 1 {
+ fn(0, dataSize)
+ } else {
+ var wg sync.WaitGroup
+ wg.Add(numGoroutines)
+ idx := uint64(0)
+
+ for p := 0; p < numGoroutines; p++ {
+ go func() {
+ defer wg.Done()
+ for {
+ partStart := int(atomic.AddUint64(&idx, uint64(partSize))) - partSize
+ if partStart >= dataSize {
+ break
+ }
+ partEnd := partStart + partSize
+ if partEnd > dataSize {
+ partEnd = dataSize
+ }
+ fn(partStart, partEnd)
+ }
+ }()
+ }
+
+ wg.Wait()
+ }
+}
+
+func absint(i int) int {
+ if i < 0 {
+ return -i
+ }
+ return i
+}
+
+// clamp & round float64 to uint8 (0..255)
+func clamp(v float64) uint8 {
+ return uint8(math.Min(math.Max(v, 0.0), 255.0) + 0.5)
+}
+
+// clamp int32 to uint8 (0..255)
+func clampint32(v int32) uint8 {
+ if v < 0 {
+ return 0
+ } else if v > 255 {
+ return 255
+ }
+ return uint8(v)
+}
diff --git a/Godeps/_workspace/src/github.com/nfnt/resize/.travis.yml b/Godeps/_workspace/src/github.com/nfnt/resize/.travis.yml
deleted file mode 100644
index 57bd4a76e..000000000
--- a/Godeps/_workspace/src/github.com/nfnt/resize/.travis.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-language: go
-
-go:
- - 1.1
- - 1.2
- - 1.3
- - tip
diff --git a/Godeps/_workspace/src/github.com/nfnt/resize/LICENSE b/Godeps/_workspace/src/github.com/nfnt/resize/LICENSE
deleted file mode 100644
index 7836cad5f..000000000
--- a/Godeps/_workspace/src/github.com/nfnt/resize/LICENSE
+++ /dev/null
@@ -1,13 +0,0 @@
-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/Godeps/_workspace/src/github.com/nfnt/resize/README.md b/Godeps/_workspace/src/github.com/nfnt/resize/README.md
deleted file mode 100644
index 2aefa75c9..000000000
--- a/Godeps/_workspace/src/github.com/nfnt/resize/README.md
+++ /dev/null
@@ -1,149 +0,0 @@
-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/Godeps/_workspace/src/github.com/nfnt/resize/converter.go b/Godeps/_workspace/src/github.com/nfnt/resize/converter.go
deleted file mode 100644
index b3dd06b8d..000000000
--- a/Godeps/_workspace/src/github.com/nfnt/resize/converter.go
+++ /dev/null
@@ -1,452 +0,0 @@
-/*
-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.NRGBA64, 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()
-
- // reverse alpha-premultiplication.
- if a != 0 {
- r *= 0xffff
- r /= a
- g *= 0xffff
- g /= a
- b *= 0xffff
- b /= a
- }
-
- 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.NRGBA, 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
- }
-
- r := uint32(row[xi+0])
- g := uint32(row[xi+1])
- b := uint32(row[xi+2])
- a := uint32(row[xi+3])
-
- // reverse alpha-premultiplication.
- if a != 0 {
- r *= 0xff
- r /= a
- g *= 0xff
- g /= a
- b *= 0xff
- b /= a
- }
-
- rgba[0] += int32(coeff) * int32(r)
- rgba[1] += int32(coeff) * int32(g)
- rgba[2] += int32(coeff) * int32(b)
- rgba[3] += int32(coeff) * int32(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 resizeNRGBA(in *image.NRGBA, out *image.NRGBA, 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 resizeRGBA64(in *image.RGBA64, out *image.NRGBA64, 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
- }
-
- r := uint32(uint16(row[xi+0])<<8 | uint16(row[xi+1]))
- g := uint32(uint16(row[xi+2])<<8 | uint16(row[xi+3]))
- b := uint32(uint16(row[xi+4])<<8 | uint16(row[xi+5]))
- a := uint32(uint16(row[xi+6])<<8 | uint16(row[xi+7]))
-
- // reverse alpha-premultiplication.
- if a != 0 {
- r *= 0xffff
- r /= a
- g *= 0xffff
- g /= a
- b *= 0xffff
- b /= a
- }
-
- 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)
- }
- }
-
- 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.NRGBA64, 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(uint16(row[xi+0])<<8|uint16(row[xi+1]))
- rgba[1] += int64(coeff) * int64(uint16(row[xi+2])<<8|uint16(row[xi+3]))
- rgba[2] += int64(coeff) * int64(uint16(row[xi+4])<<8|uint16(row[xi+5]))
- rgba[3] += int64(coeff) * int64(uint16(row[xi+6])<<8|uint16(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 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/Godeps/_workspace/src/github.com/nfnt/resize/converter_test.go b/Godeps/_workspace/src/github.com/nfnt/resize/converter_test.go
deleted file mode 100644
index 85639efc2..000000000
--- a/Godeps/_workspace/src/github.com/nfnt/resize/converter_test.go
+++ /dev/null
@@ -1,43 +0,0 @@
-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/Godeps/_workspace/src/github.com/nfnt/resize/filters.go b/Godeps/_workspace/src/github.com/nfnt/resize/filters.go
deleted file mode 100644
index 4ce04e389..000000000
--- a/Godeps/_workspace/src/github.com/nfnt/resize/filters.go
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
-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/Godeps/_workspace/src/github.com/nfnt/resize/nearest.go b/Godeps/_workspace/src/github.com/nfnt/resize/nearest.go
deleted file mode 100644
index 888039d85..000000000
--- a/Godeps/_workspace/src/github.com/nfnt/resize/nearest.go
+++ /dev/null
@@ -1,318 +0,0 @@
-/*
-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/Godeps/_workspace/src/github.com/nfnt/resize/nearest_test.go b/Godeps/_workspace/src/github.com/nfnt/resize/nearest_test.go
deleted file mode 100644
index d4a76dda5..000000000
--- a/Godeps/_workspace/src/github.com/nfnt/resize/nearest_test.go
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
-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/Godeps/_workspace/src/github.com/nfnt/resize/resize.go b/Godeps/_workspace/src/github.com/nfnt/resize/resize.go
deleted file mode 100644
index c1672432e..000000000
--- a/Godeps/_workspace/src/github.com/nfnt/resize/resize.go
+++ /dev/null
@@ -1,614 +0,0 @@
-/*
-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.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 := createWeights8(temp.Bounds().Dy(), taps, blur, scaleX, kernel)
- wg.Add(cpus)
- for i := 0; i < cpus; i++ {
- slice := makeSlice(temp, i, cpus).(*image.NRGBA)
- 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.NRGBA)
- go func() {
- defer wg.Done()
- resizeNRGBA(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 := createWeights8(temp.Bounds().Dy(), taps, blur, scaleX, kernel)
- wg.Add(cpus)
- for i := 0; i < cpus; i++ {
- slice := makeSlice(temp, i, cpus).(*image.NRGBA)
- 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.NRGBA)
- go func() {
- defer wg.Done()
- resizeNRGBA(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.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 := createWeights16(temp.Bounds().Dy(), taps, blur, scaleX, kernel)
- wg.Add(cpus)
- for i := 0; i < cpus; i++ {
- slice := makeSlice(temp, i, cpus).(*image.NRGBA64)
- 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.NRGBA64)
- go func() {
- defer wg.Done()
- resizeNRGBA64(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 := createWeights16(temp.Bounds().Dy(), taps, blur, scaleX, kernel)
- wg.Add(cpus)
- for i := 0; i < cpus; i++ {
- slice := makeSlice(temp, i, cpus).(*image.NRGBA64)
- 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.NRGBA64)
- go func() {
- defer wg.Done()
- resizeNRGBA64(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.NewNRGBA64(image.Rect(0, 0, img.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 := createWeights16(temp.Bounds().Dy(), taps, blur, scaleX, kernel)
- wg.Add(cpus)
- for i := 0; i < cpus; i++ {
- slice := makeSlice(temp, i, cpus).(*image.NRGBA64)
- 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.NRGBA64)
- go func() {
- defer wg.Done()
- resizeNRGBA64(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/Godeps/_workspace/src/github.com/nfnt/resize/resize_test.go b/Godeps/_workspace/src/github.com/nfnt/resize/resize_test.go
deleted file mode 100644
index 6f6911316..000000000
--- a/Godeps/_workspace/src/github.com/nfnt/resize/resize_test.go
+++ /dev/null
@@ -1,314 +0,0 @@
-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.NRGBA)
- 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.NRGBA)
- 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.NRGBA64)
- 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.NRGBA64)
- 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.NRGBA)
- if outputColor.R != 0xFF {
- t.Fail()
- }
-}
-
-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/Godeps/_workspace/src/github.com/nfnt/resize/thumbnail.go b/Godeps/_workspace/src/github.com/nfnt/resize/thumbnail.go
deleted file mode 100644
index 9efc246be..000000000
--- a/Godeps/_workspace/src/github.com/nfnt/resize/thumbnail.go
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
-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/Godeps/_workspace/src/github.com/nfnt/resize/thumbnail_test.go b/Godeps/_workspace/src/github.com/nfnt/resize/thumbnail_test.go
deleted file mode 100644
index bd9875b2b..000000000
--- a/Godeps/_workspace/src/github.com/nfnt/resize/thumbnail_test.go
+++ /dev/null
@@ -1,47 +0,0 @@
-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/Godeps/_workspace/src/github.com/nfnt/resize/ycc.go b/Godeps/_workspace/src/github.com/nfnt/resize/ycc.go
deleted file mode 100644
index 104159955..000000000
--- a/Godeps/_workspace/src/github.com/nfnt/resize/ycc.go
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
-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/Godeps/_workspace/src/github.com/nfnt/resize/ycc_test.go b/Godeps/_workspace/src/github.com/nfnt/resize/ycc_test.go
deleted file mode 100644
index 54d53d157..000000000
--- a/Godeps/_workspace/src/github.com/nfnt/resize/ycc_test.go
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
-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
- }
- }
- }
- }
- }
- }
- }
-}
diff --git a/Godeps/_workspace/src/golang.org/x/image/tiff/buffer.go b/Godeps/_workspace/src/golang.org/x/image/tiff/buffer.go
new file mode 100644
index 000000000..d1801be48
--- /dev/null
+++ b/Godeps/_workspace/src/golang.org/x/image/tiff/buffer.go
@@ -0,0 +1,69 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package tiff
+
+import "io"
+
+// buffer buffers an io.Reader to satisfy io.ReaderAt.
+type buffer struct {
+ r io.Reader
+ buf []byte
+}
+
+// fill reads data from b.r until the buffer contains at least end bytes.
+func (b *buffer) fill(end int) error {
+ m := len(b.buf)
+ if end > m {
+ if end > cap(b.buf) {
+ newcap := 1024
+ for newcap < end {
+ newcap *= 2
+ }
+ newbuf := make([]byte, end, newcap)
+ copy(newbuf, b.buf)
+ b.buf = newbuf
+ } else {
+ b.buf = b.buf[:end]
+ }
+ if n, err := io.ReadFull(b.r, b.buf[m:end]); err != nil {
+ end = m + n
+ b.buf = b.buf[:end]
+ return err
+ }
+ }
+ return nil
+}
+
+func (b *buffer) ReadAt(p []byte, off int64) (int, error) {
+ o := int(off)
+ end := o + len(p)
+ if int64(end) != off+int64(len(p)) {
+ return 0, io.ErrUnexpectedEOF
+ }
+
+ err := b.fill(end)
+ return copy(p, b.buf[o:end]), err
+}
+
+// Slice returns a slice of the underlying buffer. The slice contains
+// n bytes starting at offset off.
+func (b *buffer) Slice(off, n int) ([]byte, error) {
+ end := off + n
+ if err := b.fill(end); err != nil {
+ return nil, err
+ }
+ return b.buf[off:end], nil
+}
+
+// newReaderAt converts an io.Reader into an io.ReaderAt.
+func newReaderAt(r io.Reader) io.ReaderAt {
+ if ra, ok := r.(io.ReaderAt); ok {
+ return ra
+ }
+ return &buffer{
+ r: r,
+ buf: make([]byte, 0, 1024),
+ }
+}
diff --git a/Godeps/_workspace/src/golang.org/x/image/tiff/compress.go b/Godeps/_workspace/src/golang.org/x/image/tiff/compress.go
new file mode 100644
index 000000000..3f176f00a
--- /dev/null
+++ b/Godeps/_workspace/src/golang.org/x/image/tiff/compress.go
@@ -0,0 +1,58 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package tiff
+
+import (
+ "bufio"
+ "io"
+)
+
+type byteReader interface {
+ io.Reader
+ io.ByteReader
+}
+
+// unpackBits decodes the PackBits-compressed data in src and returns the
+// uncompressed data.
+//
+// The PackBits compression format is described in section 9 (p. 42)
+// of the TIFF spec.
+func unpackBits(r io.Reader) ([]byte, error) {
+ buf := make([]byte, 128)
+ dst := make([]byte, 0, 1024)
+ br, ok := r.(byteReader)
+ if !ok {
+ br = bufio.NewReader(r)
+ }
+
+ for {
+ b, err := br.ReadByte()
+ if err != nil {
+ if err == io.EOF {
+ return dst, nil
+ }
+ return nil, err
+ }
+ code := int(int8(b))
+ switch {
+ case code >= 0:
+ n, err := io.ReadFull(br, buf[:code+1])
+ if err != nil {
+ return nil, err
+ }
+ dst = append(dst, buf[:n]...)
+ case code == -128:
+ // No-op.
+ default:
+ if b, err = br.ReadByte(); err != nil {
+ return nil, err
+ }
+ for j := 0; j < 1-code; j++ {
+ buf[j] = b
+ }
+ dst = append(dst, buf[:1-code]...)
+ }
+ }
+}
diff --git a/Godeps/_workspace/src/golang.org/x/image/tiff/consts.go b/Godeps/_workspace/src/golang.org/x/image/tiff/consts.go
new file mode 100644
index 000000000..3c51a70be
--- /dev/null
+++ b/Godeps/_workspace/src/golang.org/x/image/tiff/consts.go
@@ -0,0 +1,133 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package tiff
+
+// A tiff image file contains one or more images. The metadata
+// of each image is contained in an Image File Directory (IFD),
+// which contains entries of 12 bytes each and is described
+// on page 14-16 of the specification. An IFD entry consists of
+//
+// - a tag, which describes the signification of the entry,
+// - the data type and length of the entry,
+// - the data itself or a pointer to it if it is more than 4 bytes.
+//
+// The presence of a length means that each IFD is effectively an array.
+
+const (
+ leHeader = "II\x2A\x00" // Header for little-endian files.
+ beHeader = "MM\x00\x2A" // Header for big-endian files.
+
+ ifdLen = 12 // Length of an IFD entry in bytes.
+)
+
+// Data types (p. 14-16 of the spec).
+const (
+ dtByte = 1
+ dtASCII = 2
+ dtShort = 3
+ dtLong = 4
+ dtRational = 5
+)
+
+// The length of one instance of each data type in bytes.
+var lengths = [...]uint32{0, 1, 1, 2, 4, 8}
+
+// Tags (see p. 28-41 of the spec).
+const (
+ tImageWidth = 256
+ tImageLength = 257
+ tBitsPerSample = 258
+ tCompression = 259
+ tPhotometricInterpretation = 262
+
+ tStripOffsets = 273
+ tSamplesPerPixel = 277
+ tRowsPerStrip = 278
+ tStripByteCounts = 279
+
+ tTileWidth = 322
+ tTileLength = 323
+ tTileOffsets = 324
+ tTileByteCounts = 325
+
+ tXResolution = 282
+ tYResolution = 283
+ tResolutionUnit = 296
+
+ tPredictor = 317
+ tColorMap = 320
+ tExtraSamples = 338
+ tSampleFormat = 339
+)
+
+// Compression types (defined in various places in the spec and supplements).
+const (
+ cNone = 1
+ cCCITT = 2
+ cG3 = 3 // Group 3 Fax.
+ cG4 = 4 // Group 4 Fax.
+ cLZW = 5
+ cJPEGOld = 6 // Superseded by cJPEG.
+ cJPEG = 7
+ cDeflate = 8 // zlib compression.
+ cPackBits = 32773
+ cDeflateOld = 32946 // Superseded by cDeflate.
+)
+
+// Photometric interpretation values (see p. 37 of the spec).
+const (
+ pWhiteIsZero = 0
+ pBlackIsZero = 1
+ pRGB = 2
+ pPaletted = 3
+ pTransMask = 4 // transparency mask
+ pCMYK = 5
+ pYCbCr = 6
+ pCIELab = 8
+)
+
+// Values for the tPredictor tag (page 64-65 of the spec).
+const (
+ prNone = 1
+ prHorizontal = 2
+)
+
+// Values for the tResolutionUnit tag (page 18).
+const (
+ resNone = 1
+ resPerInch = 2 // Dots per inch.
+ resPerCM = 3 // Dots per centimeter.
+)
+
+// imageMode represents the mode of the image.
+type imageMode int
+
+const (
+ mBilevel imageMode = iota
+ mPaletted
+ mGray
+ mGrayInvert
+ mRGB
+ mRGBA
+ mNRGBA
+)
+
+// CompressionType describes the type of compression used in Options.
+type CompressionType int
+
+const (
+ Uncompressed CompressionType = iota
+ Deflate
+)
+
+// specValue returns the compression type constant from the TIFF spec that
+// is equivalent to c.
+func (c CompressionType) specValue() uint32 {
+ switch c {
+ case Deflate:
+ return cDeflate
+ }
+ return cNone
+}
diff --git a/Godeps/_workspace/src/golang.org/x/image/tiff/lzw/reader.go b/Godeps/_workspace/src/golang.org/x/image/tiff/lzw/reader.go
new file mode 100644
index 000000000..dc9f7dd1d
--- /dev/null
+++ b/Godeps/_workspace/src/golang.org/x/image/tiff/lzw/reader.go
@@ -0,0 +1,277 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package lzw implements the Lempel-Ziv-Welch compressed data format,
+// described in T. A. Welch, ``A Technique for High-Performance Data
+// Compression'', Computer, 17(6) (June 1984), pp 8-19.
+//
+// In particular, it implements LZW as used by the TIFF file format, including
+// an "off by one" algorithmic difference when compared to standard LZW.
+package lzw
+
+/*
+This file was branched from src/pkg/compress/lzw/reader.go in the
+standard library. Differences from the original are marked with "NOTE".
+
+The tif_lzw.c file in the libtiff C library has this comment:
+
+----
+The 5.0 spec describes a different algorithm than Aldus
+implements. Specifically, Aldus does code length transitions
+one code earlier than should be done (for real LZW).
+Earlier versions of this library implemented the correct
+LZW algorithm, but emitted codes in a bit order opposite
+to the TIFF spec. Thus, to maintain compatibility w/ Aldus
+we interpret MSB-LSB ordered codes to be images written w/
+old versions of this library, but otherwise adhere to the
+Aldus "off by one" algorithm.
+----
+
+The Go code doesn't read (invalid) TIFF files written by old versions of
+libtiff, but the LZW algorithm in this package still differs from the one in
+Go's standard package library to accomodate this "off by one" in valid TIFFs.
+*/
+
+import (
+ "bufio"
+ "errors"
+ "fmt"
+ "io"
+)
+
+// Order specifies the bit ordering in an LZW data stream.
+type Order int
+
+const (
+ // LSB means Least Significant Bits first, as used in the GIF file format.
+ LSB Order = iota
+ // MSB means Most Significant Bits first, as used in the TIFF and PDF
+ // file formats.
+ MSB
+)
+
+const (
+ maxWidth = 12
+ decoderInvalidCode = 0xffff
+ flushBuffer = 1 << maxWidth
+)
+
+// decoder is the state from which the readXxx method converts a byte
+// stream into a code stream.
+type decoder struct {
+ r io.ByteReader
+ bits uint32
+ nBits uint
+ width uint
+ read func(*decoder) (uint16, error) // readLSB or readMSB
+ litWidth int // width in bits of literal codes
+ err error
+
+ // The first 1<<litWidth codes are literal codes.
+ // The next two codes mean clear and EOF.
+ // Other valid codes are in the range [lo, hi] where lo := clear + 2,
+ // with the upper bound incrementing on each code seen.
+ // overflow is the code at which hi overflows the code width. NOTE: TIFF's LZW is "off by one".
+ // last is the most recently seen code, or decoderInvalidCode.
+ clear, eof, hi, overflow, last uint16
+
+ // Each code c in [lo, hi] expands to two or more bytes. For c != hi:
+ // suffix[c] is the last of these bytes.
+ // prefix[c] is the code for all but the last byte.
+ // This code can either be a literal code or another code in [lo, c).
+ // The c == hi case is a special case.
+ suffix [1 << maxWidth]uint8
+ prefix [1 << maxWidth]uint16
+
+ // output is the temporary output buffer.
+ // Literal codes are accumulated from the start of the buffer.
+ // Non-literal codes decode to a sequence of suffixes that are first
+ // written right-to-left from the end of the buffer before being copied
+ // to the start of the buffer.
+ // It is flushed when it contains >= 1<<maxWidth bytes,
+ // so that there is always room to decode an entire code.
+ output [2 * 1 << maxWidth]byte
+ o int // write index into output
+ toRead []byte // bytes to return from Read
+}
+
+// readLSB returns the next code for "Least Significant Bits first" data.
+func (d *decoder) readLSB() (uint16, error) {
+ for d.nBits < d.width {
+ x, err := d.r.ReadByte()
+ if err != nil {
+ return 0, err
+ }
+ d.bits |= uint32(x) << d.nBits
+ d.nBits += 8
+ }
+ code := uint16(d.bits & (1<<d.width - 1))
+ d.bits >>= d.width
+ d.nBits -= d.width
+ return code, nil
+}
+
+// readMSB returns the next code for "Most Significant Bits first" data.
+func (d *decoder) readMSB() (uint16, error) {
+ for d.nBits < d.width {
+ x, err := d.r.ReadByte()
+ if err != nil {
+ return 0, err
+ }
+ d.bits |= uint32(x) << (24 - d.nBits)
+ d.nBits += 8
+ }
+ code := uint16(d.bits >> (32 - d.width))
+ d.bits <<= d.width
+ d.nBits -= d.width
+ return code, nil
+}
+
+func (d *decoder) Read(b []byte) (int, error) {
+ for {
+ if len(d.toRead) > 0 {
+ n := copy(b, d.toRead)
+ d.toRead = d.toRead[n:]
+ return n, nil
+ }
+ if d.err != nil {
+ return 0, d.err
+ }
+ d.decode()
+ }
+}
+
+// decode decompresses bytes from r and leaves them in d.toRead.
+// read specifies how to decode bytes into codes.
+// litWidth is the width in bits of literal codes.
+func (d *decoder) decode() {
+ // Loop over the code stream, converting codes into decompressed bytes.
+ for {
+ code, err := d.read(d)
+ if err != nil {
+ if err == io.EOF {
+ err = io.ErrUnexpectedEOF
+ }
+ d.err = err
+ d.flush()
+ return
+ }
+ switch {
+ case code < d.clear:
+ // We have a literal code.
+ d.output[d.o] = uint8(code)
+ d.o++
+ if d.last != decoderInvalidCode {
+ // Save what the hi code expands to.
+ d.suffix[d.hi] = uint8(code)
+ d.prefix[d.hi] = d.last
+ }
+ case code == d.clear:
+ d.width = 1 + uint(d.litWidth)
+ d.hi = d.eof
+ d.overflow = 1 << d.width
+ d.last = decoderInvalidCode
+ continue
+ case code == d.eof:
+ d.flush()
+ d.err = io.EOF
+ return
+ case code <= d.hi:
+ c, i := code, len(d.output)-1
+ if code == d.hi {
+ // code == hi is a special case which expands to the last expansion
+ // followed by the head of the last expansion. To find the head, we walk
+ // the prefix chain until we find a literal code.
+ c = d.last
+ for c >= d.clear {
+ c = d.prefix[c]
+ }
+ d.output[i] = uint8(c)
+ i--
+ c = d.last
+ }
+ // Copy the suffix chain into output and then write that to w.
+ for c >= d.clear {
+ d.output[i] = d.suffix[c]
+ i--
+ c = d.prefix[c]
+ }
+ d.output[i] = uint8(c)
+ d.o += copy(d.output[d.o:], d.output[i:])
+ if d.last != decoderInvalidCode {
+ // Save what the hi code expands to.
+ d.suffix[d.hi] = uint8(c)
+ d.prefix[d.hi] = d.last
+ }
+ default:
+ d.err = errors.New("lzw: invalid code")
+ d.flush()
+ return
+ }
+ d.last, d.hi = code, d.hi+1
+ if d.hi+1 >= d.overflow { // NOTE: the "+1" is where TIFF's LZW differs from the standard algorithm.
+ if d.width == maxWidth {
+ d.last = decoderInvalidCode
+ } else {
+ d.width++
+ d.overflow <<= 1
+ }
+ }
+ if d.o >= flushBuffer {
+ d.flush()
+ return
+ }
+ }
+}
+
+func (d *decoder) flush() {
+ d.toRead = d.output[:d.o]
+ d.o = 0
+}
+
+var errClosed = errors.New("lzw: reader/writer is closed")
+
+func (d *decoder) Close() error {
+ d.err = errClosed // in case any Reads come along
+ return nil
+}
+
+// NewReader creates a new io.ReadCloser.
+// Reads from the returned io.ReadCloser read and decompress data from r.
+// If r does not also implement io.ByteReader,
+// the decompressor may read more data than necessary from r.
+// It is the caller's responsibility to call Close on the ReadCloser when
+// finished reading.
+// The number of bits to use for literal codes, litWidth, must be in the
+// range [2,8] and is typically 8. It must equal the litWidth
+// used during compression.
+func NewReader(r io.Reader, order Order, litWidth int) io.ReadCloser {
+ d := new(decoder)
+ switch order {
+ case LSB:
+ d.read = (*decoder).readLSB
+ case MSB:
+ d.read = (*decoder).readMSB
+ default:
+ d.err = errors.New("lzw: unknown order")
+ return d
+ }
+ if litWidth < 2 || 8 < litWidth {
+ d.err = fmt.Errorf("lzw: litWidth %d out of range", litWidth)
+ return d
+ }
+ if br, ok := r.(io.ByteReader); ok {
+ d.r = br
+ } else {
+ d.r = bufio.NewReader(r)
+ }
+ d.litWidth = litWidth
+ d.width = 1 + uint(litWidth)
+ d.clear = uint16(1) << uint(litWidth)
+ d.eof, d.hi = d.clear+1, d.clear+1
+ d.overflow = uint16(1) << d.width
+ d.last = decoderInvalidCode
+
+ return d
+}
diff --git a/Godeps/_workspace/src/golang.org/x/image/tiff/reader.go b/Godeps/_workspace/src/golang.org/x/image/tiff/reader.go
new file mode 100644
index 000000000..714e3dda7
--- /dev/null
+++ b/Godeps/_workspace/src/golang.org/x/image/tiff/reader.go
@@ -0,0 +1,681 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package tiff implements a TIFF image decoder and encoder.
+//
+// The TIFF specification is at http://partners.adobe.com/public/developer/en/tiff/TIFF6.pdf
+package tiff
+
+import (
+ "compress/zlib"
+ "encoding/binary"
+ "fmt"
+ "image"
+ "image/color"
+ "io"
+ "io/ioutil"
+ "math"
+
+ "golang.org/x/image/tiff/lzw"
+)
+
+// A FormatError reports that the input is not a valid TIFF image.
+type FormatError string
+
+func (e FormatError) Error() string {
+ return "tiff: invalid format: " + string(e)
+}
+
+// An UnsupportedError reports that the input uses a valid but
+// unimplemented feature.
+type UnsupportedError string
+
+func (e UnsupportedError) Error() string {
+ return "tiff: unsupported feature: " + string(e)
+}
+
+// An InternalError reports that an internal error was encountered.
+type InternalError string
+
+func (e InternalError) Error() string {
+ return "tiff: internal error: " + string(e)
+}
+
+var errNoPixels = FormatError("not enough pixel data")
+
+type decoder struct {
+ r io.ReaderAt
+ byteOrder binary.ByteOrder
+ config image.Config
+ mode imageMode
+ bpp uint
+ features map[int][]uint
+ palette []color.Color
+
+ buf []byte
+ off int // Current offset in buf.
+ v uint32 // Buffer value for reading with arbitrary bit depths.
+ nbits uint // Remaining number of bits in v.
+}
+
+// firstVal returns the first uint of the features entry with the given tag,
+// or 0 if the tag does not exist.
+func (d *decoder) firstVal(tag int) uint {
+ f := d.features[tag]
+ if len(f) == 0 {
+ return 0
+ }
+ return f[0]
+}
+
+// ifdUint decodes the IFD entry in p, which must be of the Byte, Short
+// or Long type, and returns the decoded uint values.
+func (d *decoder) ifdUint(p []byte) (u []uint, err error) {
+ var raw []byte
+ if len(p) < ifdLen {
+ return nil, FormatError("bad IFD entry")
+ }
+
+ datatype := d.byteOrder.Uint16(p[2:4])
+ if dt := int(datatype); dt <= 0 || dt >= len(lengths) {
+ return nil, UnsupportedError("IFD entry datatype")
+ }
+
+ count := d.byteOrder.Uint32(p[4:8])
+ if count > math.MaxInt32/lengths[datatype] {
+ return nil, FormatError("IFD data too large")
+ }
+ if datalen := lengths[datatype] * count; datalen > 4 {
+ // The IFD contains a pointer to the real value.
+ raw = make([]byte, datalen)
+ _, err = d.r.ReadAt(raw, int64(d.byteOrder.Uint32(p[8:12])))
+ } else {
+ raw = p[8 : 8+datalen]
+ }
+ if err != nil {
+ return nil, err
+ }
+
+ u = make([]uint, count)
+ switch datatype {
+ case dtByte:
+ for i := uint32(0); i < count; i++ {
+ u[i] = uint(raw[i])
+ }
+ case dtShort:
+ for i := uint32(0); i < count; i++ {
+ u[i] = uint(d.byteOrder.Uint16(raw[2*i : 2*(i+1)]))
+ }
+ case dtLong:
+ for i := uint32(0); i < count; i++ {
+ u[i] = uint(d.byteOrder.Uint32(raw[4*i : 4*(i+1)]))
+ }
+ default:
+ return nil, UnsupportedError("data type")
+ }
+ return u, nil
+}
+
+// parseIFD decides whether the the IFD entry in p is "interesting" and
+// stows away the data in the decoder.
+func (d *decoder) parseIFD(p []byte) error {
+ tag := d.byteOrder.Uint16(p[0:2])
+ switch tag {
+ case tBitsPerSample,
+ tExtraSamples,
+ tPhotometricInterpretation,
+ tCompression,
+ tPredictor,
+ tStripOffsets,
+ tStripByteCounts,
+ tRowsPerStrip,
+ tTileWidth,
+ tTileLength,
+ tTileOffsets,
+ tTileByteCounts,
+ tImageLength,
+ tImageWidth:
+ val, err := d.ifdUint(p)
+ if err != nil {
+ return err
+ }
+ d.features[int(tag)] = val
+ case tColorMap:
+ val, err := d.ifdUint(p)
+ if err != nil {
+ return err
+ }
+ numcolors := len(val) / 3
+ if len(val)%3 != 0 || numcolors <= 0 || numcolors > 256 {
+ return FormatError("bad ColorMap length")
+ }
+ d.palette = make([]color.Color, numcolors)
+ for i := 0; i < numcolors; i++ {
+ d.palette[i] = color.RGBA64{
+ uint16(val[i]),
+ uint16(val[i+numcolors]),
+ uint16(val[i+2*numcolors]),
+ 0xffff,
+ }
+ }
+ case tSampleFormat:
+ // Page 27 of the spec: If the SampleFormat is present and
+ // the value is not 1 [= unsigned integer data], a Baseline
+ // TIFF reader that cannot handle the SampleFormat value
+ // must terminate the import process gracefully.
+ val, err := d.ifdUint(p)
+ if err != nil {
+ return err
+ }
+ for _, v := range val {
+ if v != 1 {
+ return UnsupportedError("sample format")
+ }
+ }
+ }
+ return nil
+}
+
+// readBits reads n bits from the internal buffer starting at the current offset.
+func (d *decoder) readBits(n uint) (v uint32, ok bool) {
+ for d.nbits < n {
+ d.v <<= 8
+ if d.off >= len(d.buf) {
+ return 0, false
+ }
+ d.v |= uint32(d.buf[d.off])
+ d.off++
+ d.nbits += 8
+ }
+ d.nbits -= n
+ rv := d.v >> d.nbits
+ d.v &^= rv << d.nbits
+ return rv, true
+}
+
+// flushBits discards the unread bits in the buffer used by readBits.
+// It is used at the end of a line.
+func (d *decoder) flushBits() {
+ d.v = 0
+ d.nbits = 0
+}
+
+// minInt returns the smaller of x or y.
+func minInt(a, b int) int {
+ if a <= b {
+ return a
+ }
+ return b
+}
+
+// decode decodes the raw data of an image.
+// It reads from d.buf and writes the strip or tile into dst.
+func (d *decoder) decode(dst image.Image, xmin, ymin, xmax, ymax int) error {
+ d.off = 0
+
+ // Apply horizontal predictor if necessary.
+ // In this case, p contains the color difference to the preceding pixel.
+ // See page 64-65 of the spec.
+ if d.firstVal(tPredictor) == prHorizontal {
+ switch d.bpp {
+ case 16:
+ var off int
+ n := 2 * len(d.features[tBitsPerSample]) // bytes per sample times samples per pixel
+ for y := ymin; y < ymax; y++ {
+ off += n
+ for x := 0; x < (xmax-xmin-1)*n; x += 2 {
+ if off+2 > len(d.buf) {
+ return errNoPixels
+ }
+ v0 := d.byteOrder.Uint16(d.buf[off-n : off-n+2])
+ v1 := d.byteOrder.Uint16(d.buf[off : off+2])
+ d.byteOrder.PutUint16(d.buf[off:off+2], v1+v0)
+ off += 2
+ }
+ }
+ case 8:
+ var off int
+ n := 1 * len(d.features[tBitsPerSample]) // bytes per sample times samples per pixel
+ for y := ymin; y < ymax; y++ {
+ off += n
+ for x := 0; x < (xmax-xmin-1)*n; x++ {
+ if off >= len(d.buf) {
+ return errNoPixels
+ }
+ d.buf[off] += d.buf[off-n]
+ off++
+ }
+ }
+ case 1:
+ return UnsupportedError("horizontal predictor with 1 BitsPerSample")
+ }
+ }
+
+ rMaxX := minInt(xmax, dst.Bounds().Max.X)
+ rMaxY := minInt(ymax, dst.Bounds().Max.Y)
+ switch d.mode {
+ case mGray, mGrayInvert:
+ if d.bpp == 16 {
+ img := dst.(*image.Gray16)
+ for y := ymin; y < rMaxY; y++ {
+ for x := xmin; x < rMaxX; x++ {
+ if d.off+2 > len(d.buf) {
+ return errNoPixels
+ }
+ v := d.byteOrder.Uint16(d.buf[d.off : d.off+2])
+ d.off += 2
+ if d.mode == mGrayInvert {
+ v = 0xffff - v
+ }
+ img.SetGray16(x, y, color.Gray16{v})
+ }
+ }
+ } else {
+ img := dst.(*image.Gray)
+ max := uint32((1 << d.bpp) - 1)
+ for y := ymin; y < rMaxY; y++ {
+ for x := xmin; x < rMaxX; x++ {
+ v, ok := d.readBits(d.bpp)
+ if !ok {
+ return errNoPixels
+ }
+ v = v * 0xff / max
+ if d.mode == mGrayInvert {
+ v = 0xff - v
+ }
+ img.SetGray(x, y, color.Gray{uint8(v)})
+ }
+ d.flushBits()
+ }
+ }
+ case mPaletted:
+ img := dst.(*image.Paletted)
+ for y := ymin; y < rMaxY; y++ {
+ for x := xmin; x < rMaxX; x++ {
+ v, ok := d.readBits(d.bpp)
+ if !ok {
+ return errNoPixels
+ }
+ img.SetColorIndex(x, y, uint8(v))
+ }
+ d.flushBits()
+ }
+ case mRGB:
+ if d.bpp == 16 {
+ img := dst.(*image.RGBA64)
+ for y := ymin; y < rMaxY; y++ {
+ for x := xmin; x < rMaxX; x++ {
+ if d.off+6 > len(d.buf) {
+ return errNoPixels
+ }
+ r := d.byteOrder.Uint16(d.buf[d.off+0 : d.off+2])
+ g := d.byteOrder.Uint16(d.buf[d.off+2 : d.off+4])
+ b := d.byteOrder.Uint16(d.buf[d.off+4 : d.off+6])
+ d.off += 6
+ img.SetRGBA64(x, y, color.RGBA64{r, g, b, 0xffff})
+ }
+ }
+ } else {
+ img := dst.(*image.RGBA)
+ for y := ymin; y < rMaxY; y++ {
+ min := img.PixOffset(xmin, y)
+ max := img.PixOffset(rMaxX, y)
+ off := (y - ymin) * (xmax - xmin) * 3
+ for i := min; i < max; i += 4 {
+ if off+3 > len(d.buf) {
+ return errNoPixels
+ }
+ img.Pix[i+0] = d.buf[off+0]
+ img.Pix[i+1] = d.buf[off+1]
+ img.Pix[i+2] = d.buf[off+2]
+ img.Pix[i+3] = 0xff
+ off += 3
+ }
+ }
+ }
+ case mNRGBA:
+ if d.bpp == 16 {
+ img := dst.(*image.NRGBA64)
+ for y := ymin; y < rMaxY; y++ {
+ for x := xmin; x < rMaxX; x++ {
+ if d.off+8 > len(d.buf) {
+ return errNoPixels
+ }
+ r := d.byteOrder.Uint16(d.buf[d.off+0 : d.off+2])
+ g := d.byteOrder.Uint16(d.buf[d.off+2 : d.off+4])
+ b := d.byteOrder.Uint16(d.buf[d.off+4 : d.off+6])
+ a := d.byteOrder.Uint16(d.buf[d.off+6 : d.off+8])
+ d.off += 8
+ img.SetNRGBA64(x, y, color.NRGBA64{r, g, b, a})
+ }
+ }
+ } else {
+ img := dst.(*image.NRGBA)
+ for y := ymin; y < rMaxY; y++ {
+ min := img.PixOffset(xmin, y)
+ max := img.PixOffset(rMaxX, y)
+ i0, i1 := (y-ymin)*(xmax-xmin)*4, (y-ymin+1)*(xmax-xmin)*4
+ if i1 > len(d.buf) {
+ return errNoPixels
+ }
+ copy(img.Pix[min:max], d.buf[i0:i1])
+ }
+ }
+ case mRGBA:
+ if d.bpp == 16 {
+ img := dst.(*image.RGBA64)
+ for y := ymin; y < rMaxY; y++ {
+ for x := xmin; x < rMaxX; x++ {
+ if d.off+8 > len(d.buf) {
+ return errNoPixels
+ }
+ r := d.byteOrder.Uint16(d.buf[d.off+0 : d.off+2])
+ g := d.byteOrder.Uint16(d.buf[d.off+2 : d.off+4])
+ b := d.byteOrder.Uint16(d.buf[d.off+4 : d.off+6])
+ a := d.byteOrder.Uint16(d.buf[d.off+6 : d.off+8])
+ d.off += 8
+ img.SetRGBA64(x, y, color.RGBA64{r, g, b, a})
+ }
+ }
+ } else {
+ img := dst.(*image.RGBA)
+ for y := ymin; y < rMaxY; y++ {
+ min := img.PixOffset(xmin, y)
+ max := img.PixOffset(rMaxX, y)
+ i0, i1 := (y-ymin)*(xmax-xmin)*4, (y-ymin+1)*(xmax-xmin)*4
+ if i1 > len(d.buf) {
+ return errNoPixels
+ }
+ copy(img.Pix[min:max], d.buf[i0:i1])
+ }
+ }
+ }
+
+ return nil
+}
+
+func newDecoder(r io.Reader) (*decoder, error) {
+ d := &decoder{
+ r: newReaderAt(r),
+ features: make(map[int][]uint),
+ }
+
+ p := make([]byte, 8)
+ if _, err := d.r.ReadAt(p, 0); err != nil {
+ return nil, err
+ }
+ switch string(p[0:4]) {
+ case leHeader:
+ d.byteOrder = binary.LittleEndian
+ case beHeader:
+ d.byteOrder = binary.BigEndian
+ default:
+ return nil, FormatError("malformed header")
+ }
+
+ ifdOffset := int64(d.byteOrder.Uint32(p[4:8]))
+
+ // The first two bytes contain the number of entries (12 bytes each).
+ if _, err := d.r.ReadAt(p[0:2], ifdOffset); err != nil {
+ return nil, err
+ }
+ numItems := int(d.byteOrder.Uint16(p[0:2]))
+
+ // All IFD entries are read in one chunk.
+ p = make([]byte, ifdLen*numItems)
+ if _, err := d.r.ReadAt(p, ifdOffset+2); err != nil {
+ return nil, err
+ }
+
+ for i := 0; i < len(p); i += ifdLen {
+ if err := d.parseIFD(p[i : i+ifdLen]); err != nil {
+ return nil, err
+ }
+ }
+
+ d.config.Width = int(d.firstVal(tImageWidth))
+ d.config.Height = int(d.firstVal(tImageLength))
+
+ if _, ok := d.features[tBitsPerSample]; !ok {
+ return nil, FormatError("BitsPerSample tag missing")
+ }
+ d.bpp = d.firstVal(tBitsPerSample)
+ switch d.bpp {
+ case 0:
+ return nil, FormatError("BitsPerSample must not be 0")
+ case 1, 8, 16:
+ // Nothing to do, these are accepted by this implementation.
+ default:
+ return nil, UnsupportedError(fmt.Sprintf("BitsPerSample of %v", d.bpp))
+ }
+
+ // Determine the image mode.
+ switch d.firstVal(tPhotometricInterpretation) {
+ case pRGB:
+ if d.bpp == 16 {
+ for _, b := range d.features[tBitsPerSample] {
+ if b != 16 {
+ return nil, FormatError("wrong number of samples for 16bit RGB")
+ }
+ }
+ } else {
+ for _, b := range d.features[tBitsPerSample] {
+ if b != 8 {
+ return nil, FormatError("wrong number of samples for 8bit RGB")
+ }
+ }
+ }
+ // RGB images normally have 3 samples per pixel.
+ // If there are more, ExtraSamples (p. 31-32 of the spec)
+ // gives their meaning (usually an alpha channel).
+ //
+ // This implementation does not support extra samples
+ // of an unspecified type.
+ switch len(d.features[tBitsPerSample]) {
+ case 3:
+ d.mode = mRGB
+ if d.bpp == 16 {
+ d.config.ColorModel = color.RGBA64Model
+ } else {
+ d.config.ColorModel = color.RGBAModel
+ }
+ case 4:
+ switch d.firstVal(tExtraSamples) {
+ case 1:
+ d.mode = mRGBA
+ if d.bpp == 16 {
+ d.config.ColorModel = color.RGBA64Model
+ } else {
+ d.config.ColorModel = color.RGBAModel
+ }
+ case 2:
+ d.mode = mNRGBA
+ if d.bpp == 16 {
+ d.config.ColorModel = color.NRGBA64Model
+ } else {
+ d.config.ColorModel = color.NRGBAModel
+ }
+ default:
+ return nil, FormatError("wrong number of samples for RGB")
+ }
+ default:
+ return nil, FormatError("wrong number of samples for RGB")
+ }
+ case pPaletted:
+ d.mode = mPaletted
+ d.config.ColorModel = color.Palette(d.palette)
+ case pWhiteIsZero:
+ d.mode = mGrayInvert
+ if d.bpp == 16 {
+ d.config.ColorModel = color.Gray16Model
+ } else {
+ d.config.ColorModel = color.GrayModel
+ }
+ case pBlackIsZero:
+ d.mode = mGray
+ if d.bpp == 16 {
+ d.config.ColorModel = color.Gray16Model
+ } else {
+ d.config.ColorModel = color.GrayModel
+ }
+ default:
+ return nil, UnsupportedError("color model")
+ }
+
+ return d, nil
+}
+
+// DecodeConfig returns the color model and dimensions of a TIFF image without
+// decoding the entire image.
+func DecodeConfig(r io.Reader) (image.Config, error) {
+ d, err := newDecoder(r)
+ if err != nil {
+ return image.Config{}, err
+ }
+ return d.config, nil
+}
+
+// Decode reads a TIFF image from r and returns it as an image.Image.
+// The type of Image returned depends on the contents of the TIFF.
+func Decode(r io.Reader) (img image.Image, err error) {
+ d, err := newDecoder(r)
+ if err != nil {
+ return
+ }
+
+ blockPadding := false
+ blockWidth := d.config.Width
+ blockHeight := d.config.Height
+ blocksAcross := 1
+ blocksDown := 1
+
+ if d.config.Width == 0 {
+ blocksAcross = 0
+ }
+ if d.config.Height == 0 {
+ blocksDown = 0
+ }
+
+ var blockOffsets, blockCounts []uint
+
+ if int(d.firstVal(tTileWidth)) != 0 {
+ blockPadding = true
+
+ blockWidth = int(d.firstVal(tTileWidth))
+ blockHeight = int(d.firstVal(tTileLength))
+
+ if blockWidth != 0 {
+ blocksAcross = (d.config.Width + blockWidth - 1) / blockWidth
+ }
+ if blockHeight != 0 {
+ blocksDown = (d.config.Height + blockHeight - 1) / blockHeight
+ }
+
+ blockCounts = d.features[tTileByteCounts]
+ blockOffsets = d.features[tTileOffsets]
+
+ } else {
+ if int(d.firstVal(tRowsPerStrip)) != 0 {
+ blockHeight = int(d.firstVal(tRowsPerStrip))
+ }
+
+ if blockHeight != 0 {
+ blocksDown = (d.config.Height + blockHeight - 1) / blockHeight
+ }
+
+ blockOffsets = d.features[tStripOffsets]
+ blockCounts = d.features[tStripByteCounts]
+ }
+
+ // Check if we have the right number of strips/tiles, offsets and counts.
+ if n := blocksAcross * blocksDown; len(blockOffsets) < n || len(blockCounts) < n {
+ return nil, FormatError("inconsistent header")
+ }
+
+ imgRect := image.Rect(0, 0, d.config.Width, d.config.Height)
+ switch d.mode {
+ case mGray, mGrayInvert:
+ if d.bpp == 16 {
+ img = image.NewGray16(imgRect)
+ } else {
+ img = image.NewGray(imgRect)
+ }
+ case mPaletted:
+ img = image.NewPaletted(imgRect, d.palette)
+ case mNRGBA:
+ if d.bpp == 16 {
+ img = image.NewNRGBA64(imgRect)
+ } else {
+ img = image.NewNRGBA(imgRect)
+ }
+ case mRGB, mRGBA:
+ if d.bpp == 16 {
+ img = image.NewRGBA64(imgRect)
+ } else {
+ img = image.NewRGBA(imgRect)
+ }
+ }
+
+ for i := 0; i < blocksAcross; i++ {
+ blkW := blockWidth
+ if !blockPadding && i == blocksAcross-1 && d.config.Width%blockWidth != 0 {
+ blkW = d.config.Width % blockWidth
+ }
+ for j := 0; j < blocksDown; j++ {
+ blkH := blockHeight
+ if !blockPadding && j == blocksDown-1 && d.config.Height%blockHeight != 0 {
+ blkH = d.config.Height % blockHeight
+ }
+ offset := int64(blockOffsets[j*blocksAcross+i])
+ n := int64(blockCounts[j*blocksAcross+i])
+ switch d.firstVal(tCompression) {
+
+ // According to the spec, Compression does not have a default value,
+ // but some tools interpret a missing Compression value as none so we do
+ // the same.
+ case cNone, 0:
+ if b, ok := d.r.(*buffer); ok {
+ d.buf, err = b.Slice(int(offset), int(n))
+ } else {
+ d.buf = make([]byte, n)
+ _, err = d.r.ReadAt(d.buf, offset)
+ }
+ case cLZW:
+ r := lzw.NewReader(io.NewSectionReader(d.r, offset, n), lzw.MSB, 8)
+ d.buf, err = ioutil.ReadAll(r)
+ r.Close()
+ case cDeflate, cDeflateOld:
+ var r io.ReadCloser
+ r, err = zlib.NewReader(io.NewSectionReader(d.r, offset, n))
+ if err != nil {
+ return nil, err
+ }
+ d.buf, err = ioutil.ReadAll(r)
+ r.Close()
+ case cPackBits:
+ d.buf, err = unpackBits(io.NewSectionReader(d.r, offset, n))
+ default:
+ err = UnsupportedError(fmt.Sprintf("compression value %d", d.firstVal(tCompression)))
+ }
+ if err != nil {
+ return nil, err
+ }
+
+ xmin := i * blockWidth
+ ymin := j * blockHeight
+ xmax := xmin + blkW
+ ymax := ymin + blkH
+ err = d.decode(img, xmin, ymin, xmax, ymax)
+ if err != nil {
+ return nil, err
+ }
+ }
+ }
+ return
+}
+
+func init() {
+ image.RegisterFormat("tiff", leHeader, Decode, DecodeConfig)
+ image.RegisterFormat("tiff", beHeader, Decode, DecodeConfig)
+}
diff --git a/Godeps/_workspace/src/golang.org/x/image/tiff/writer.go b/Godeps/_workspace/src/golang.org/x/image/tiff/writer.go
new file mode 100644
index 000000000..c8a01cea7
--- /dev/null
+++ b/Godeps/_workspace/src/golang.org/x/image/tiff/writer.go
@@ -0,0 +1,438 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package tiff
+
+import (
+ "bytes"
+ "compress/zlib"
+ "encoding/binary"
+ "image"
+ "io"
+ "sort"
+)
+
+// The TIFF format allows to choose the order of the different elements freely.
+// The basic structure of a TIFF file written by this package is:
+//
+// 1. Header (8 bytes).
+// 2. Image data.
+// 3. Image File Directory (IFD).
+// 4. "Pointer area" for larger entries in the IFD.
+
+// We only write little-endian TIFF files.
+var enc = binary.LittleEndian
+
+// An ifdEntry is a single entry in an Image File Directory.
+// A value of type dtRational is composed of two 32-bit values,
+// thus data contains two uints (numerator and denominator) for a single number.
+type ifdEntry struct {
+ tag int
+ datatype int
+ data []uint32
+}
+
+func (e ifdEntry) putData(p []byte) {
+ for _, d := range e.data {
+ switch e.datatype {
+ case dtByte, dtASCII:
+ p[0] = byte(d)
+ p = p[1:]
+ case dtShort:
+ enc.PutUint16(p, uint16(d))
+ p = p[2:]
+ case dtLong, dtRational:
+ enc.PutUint32(p, uint32(d))
+ p = p[4:]
+ }
+ }
+}
+
+type byTag []ifdEntry
+
+func (d byTag) Len() int { return len(d) }
+func (d byTag) Less(i, j int) bool { return d[i].tag < d[j].tag }
+func (d byTag) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
+
+func encodeGray(w io.Writer, pix []uint8, dx, dy, stride int, predictor bool) error {
+ if !predictor {
+ return writePix(w, pix, dy, dx, stride)
+ }
+ buf := make([]byte, dx)
+ for y := 0; y < dy; y++ {
+ min := y*stride + 0
+ max := y*stride + dx
+ off := 0
+ var v0 uint8
+ for i := min; i < max; i++ {
+ v1 := pix[i]
+ buf[off] = v1 - v0
+ v0 = v1
+ off++
+ }
+ if _, err := w.Write(buf); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func encodeGray16(w io.Writer, pix []uint8, dx, dy, stride int, predictor bool) error {
+ buf := make([]byte, dx*2)
+ for y := 0; y < dy; y++ {
+ min := y*stride + 0
+ max := y*stride + dx*2
+ off := 0
+ var v0 uint16
+ for i := min; i < max; i += 2 {
+ // An image.Gray16's Pix is in big-endian order.
+ v1 := uint16(pix[i])<<8 | uint16(pix[i+1])
+ if predictor {
+ v0, v1 = v1, v1-v0
+ }
+ // We only write little-endian TIFF files.
+ buf[off+0] = byte(v1)
+ buf[off+1] = byte(v1 >> 8)
+ off += 2
+ }
+ if _, err := w.Write(buf); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func encodeRGBA(w io.Writer, pix []uint8, dx, dy, stride int, predictor bool) error {
+ if !predictor {
+ return writePix(w, pix, dy, dx*4, stride)
+ }
+ buf := make([]byte, dx*4)
+ for y := 0; y < dy; y++ {
+ min := y*stride + 0
+ max := y*stride + dx*4
+ off := 0
+ var r0, g0, b0, a0 uint8
+ for i := min; i < max; i += 4 {
+ r1, g1, b1, a1 := pix[i+0], pix[i+1], pix[i+2], pix[i+3]
+ buf[off+0] = r1 - r0
+ buf[off+1] = g1 - g0
+ buf[off+2] = b1 - b0
+ buf[off+3] = a1 - a0
+ off += 4
+ r0, g0, b0, a0 = r1, g1, b1, a1
+ }
+ if _, err := w.Write(buf); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func encodeRGBA64(w io.Writer, pix []uint8, dx, dy, stride int, predictor bool) error {
+ buf := make([]byte, dx*8)
+ for y := 0; y < dy; y++ {
+ min := y*stride + 0
+ max := y*stride + dx*8
+ off := 0
+ var r0, g0, b0, a0 uint16
+ for i := min; i < max; i += 8 {
+ // An image.RGBA64's Pix is in big-endian order.
+ r1 := uint16(pix[i+0])<<8 | uint16(pix[i+1])
+ g1 := uint16(pix[i+2])<<8 | uint16(pix[i+3])
+ b1 := uint16(pix[i+4])<<8 | uint16(pix[i+5])
+ a1 := uint16(pix[i+6])<<8 | uint16(pix[i+7])
+ if predictor {
+ r0, r1 = r1, r1-r0
+ g0, g1 = g1, g1-g0
+ b0, b1 = b1, b1-b0
+ a0, a1 = a1, a1-a0
+ }
+ // We only write little-endian TIFF files.
+ buf[off+0] = byte(r1)
+ buf[off+1] = byte(r1 >> 8)
+ buf[off+2] = byte(g1)
+ buf[off+3] = byte(g1 >> 8)
+ buf[off+4] = byte(b1)
+ buf[off+5] = byte(b1 >> 8)
+ buf[off+6] = byte(a1)
+ buf[off+7] = byte(a1 >> 8)
+ off += 8
+ }
+ if _, err := w.Write(buf); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func encode(w io.Writer, m image.Image, predictor bool) error {
+ bounds := m.Bounds()
+ buf := make([]byte, 4*bounds.Dx())
+ for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
+ off := 0
+ if predictor {
+ var r0, g0, b0, a0 uint8
+ for x := bounds.Min.X; x < bounds.Max.X; x++ {
+ r, g, b, a := m.At(x, y).RGBA()
+ r1 := uint8(r >> 8)
+ g1 := uint8(g >> 8)
+ b1 := uint8(b >> 8)
+ a1 := uint8(a >> 8)
+ buf[off+0] = r1 - r0
+ buf[off+1] = g1 - g0
+ buf[off+2] = b1 - b0
+ buf[off+3] = a1 - a0
+ off += 4
+ r0, g0, b0, a0 = r1, g1, b1, a1
+ }
+ } else {
+ for x := bounds.Min.X; x < bounds.Max.X; x++ {
+ r, g, b, a := m.At(x, y).RGBA()
+ buf[off+0] = uint8(r >> 8)
+ buf[off+1] = uint8(g >> 8)
+ buf[off+2] = uint8(b >> 8)
+ buf[off+3] = uint8(a >> 8)
+ off += 4
+ }
+ }
+ if _, err := w.Write(buf); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// writePix writes the internal byte array of an image to w. It is less general
+// but much faster then encode. writePix is used when pix directly
+// corresponds to one of the TIFF image types.
+func writePix(w io.Writer, pix []byte, nrows, length, stride int) error {
+ if length == stride {
+ _, err := w.Write(pix[:nrows*length])
+ return err
+ }
+ for ; nrows > 0; nrows-- {
+ if _, err := w.Write(pix[:length]); err != nil {
+ return err
+ }
+ pix = pix[stride:]
+ }
+ return nil
+}
+
+func writeIFD(w io.Writer, ifdOffset int, d []ifdEntry) error {
+ var buf [ifdLen]byte
+ // Make space for "pointer area" containing IFD entry data
+ // longer than 4 bytes.
+ parea := make([]byte, 1024)
+ pstart := ifdOffset + ifdLen*len(d) + 6
+ var o int // Current offset in parea.
+
+ // The IFD has to be written with the tags in ascending order.
+ sort.Sort(byTag(d))
+
+ // Write the number of entries in this IFD.
+ if err := binary.Write(w, enc, uint16(len(d))); err != nil {
+ return err
+ }
+ for _, ent := range d {
+ enc.PutUint16(buf[0:2], uint16(ent.tag))
+ enc.PutUint16(buf[2:4], uint16(ent.datatype))
+ count := uint32(len(ent.data))
+ if ent.datatype == dtRational {
+ count /= 2
+ }
+ enc.PutUint32(buf[4:8], count)
+ datalen := int(count * lengths[ent.datatype])
+ if datalen <= 4 {
+ ent.putData(buf[8:12])
+ } else {
+ if (o + datalen) > len(parea) {
+ newlen := len(parea) + 1024
+ for (o + datalen) > newlen {
+ newlen += 1024
+ }
+ newarea := make([]byte, newlen)
+ copy(newarea, parea)
+ parea = newarea
+ }
+ ent.putData(parea[o : o+datalen])
+ enc.PutUint32(buf[8:12], uint32(pstart+o))
+ o += datalen
+ }
+ if _, err := w.Write(buf[:]); err != nil {
+ return err
+ }
+ }
+ // The IFD ends with the offset of the next IFD in the file,
+ // or zero if it is the last one (page 14).
+ if err := binary.Write(w, enc, uint32(0)); err != nil {
+ return err
+ }
+ _, err := w.Write(parea[:o])
+ return err
+}
+
+// Options are the encoding parameters.
+type Options struct {
+ // Compression is the type of compression used.
+ Compression CompressionType
+ // Predictor determines whether a differencing predictor is used;
+ // if true, instead of each pixel's color, the color difference to the
+ // preceding one is saved. This improves the compression for certain
+ // types of images and compressors. For example, it works well for
+ // photos with Deflate compression.
+ Predictor bool
+}
+
+// Encode writes the image m to w. opt determines the options used for
+// encoding, such as the compression type. If opt is nil, an uncompressed
+// image is written.
+func Encode(w io.Writer, m image.Image, opt *Options) error {
+ d := m.Bounds().Size()
+
+ compression := uint32(cNone)
+ predictor := false
+ if opt != nil {
+ compression = opt.Compression.specValue()
+ // The predictor field is only used with LZW. See page 64 of the spec.
+ predictor = opt.Predictor && compression == cLZW
+ }
+
+ _, err := io.WriteString(w, leHeader)
+ if err != nil {
+ return err
+ }
+
+ // Compressed data is written into a buffer first, so that we
+ // know the compressed size.
+ var buf bytes.Buffer
+ // dst holds the destination for the pixel data of the image --
+ // either w or a writer to buf.
+ var dst io.Writer
+ // imageLen is the length of the pixel data in bytes.
+ // The offset of the IFD is imageLen + 8 header bytes.
+ var imageLen int
+
+ switch compression {
+ case cNone:
+ dst = w
+ // Write IFD offset before outputting pixel data.
+ switch m.(type) {
+ case *image.Paletted:
+ imageLen = d.X * d.Y * 1
+ case *image.Gray:
+ imageLen = d.X * d.Y * 1
+ case *image.Gray16:
+ imageLen = d.X * d.Y * 2
+ case *image.RGBA64:
+ imageLen = d.X * d.Y * 8
+ case *image.NRGBA64:
+ imageLen = d.X * d.Y * 8
+ default:
+ imageLen = d.X * d.Y * 4
+ }
+ err = binary.Write(w, enc, uint32(imageLen+8))
+ if err != nil {
+ return err
+ }
+ case cDeflate:
+ dst = zlib.NewWriter(&buf)
+ }
+
+ pr := uint32(prNone)
+ photometricInterpretation := uint32(pRGB)
+ samplesPerPixel := uint32(4)
+ bitsPerSample := []uint32{8, 8, 8, 8}
+ extraSamples := uint32(0)
+ colorMap := []uint32{}
+
+ if predictor {
+ pr = prHorizontal
+ }
+ switch m := m.(type) {
+ case *image.Paletted:
+ photometricInterpretation = pPaletted
+ samplesPerPixel = 1
+ bitsPerSample = []uint32{8}
+ colorMap = make([]uint32, 256*3)
+ for i := 0; i < 256 && i < len(m.Palette); i++ {
+ r, g, b, _ := m.Palette[i].RGBA()
+ colorMap[i+0*256] = uint32(r)
+ colorMap[i+1*256] = uint32(g)
+ colorMap[i+2*256] = uint32(b)
+ }
+ err = encodeGray(dst, m.Pix, d.X, d.Y, m.Stride, predictor)
+ case *image.Gray:
+ photometricInterpretation = pBlackIsZero
+ samplesPerPixel = 1
+ bitsPerSample = []uint32{8}
+ err = encodeGray(dst, m.Pix, d.X, d.Y, m.Stride, predictor)
+ case *image.Gray16:
+ photometricInterpretation = pBlackIsZero
+ samplesPerPixel = 1
+ bitsPerSample = []uint32{16}
+ err = encodeGray16(dst, m.Pix, d.X, d.Y, m.Stride, predictor)
+ case *image.NRGBA:
+ extraSamples = 2 // Unassociated alpha.
+ err = encodeRGBA(dst, m.Pix, d.X, d.Y, m.Stride, predictor)
+ case *image.NRGBA64:
+ extraSamples = 2 // Unassociated alpha.
+ bitsPerSample = []uint32{16, 16, 16, 16}
+ err = encodeRGBA64(dst, m.Pix, d.X, d.Y, m.Stride, predictor)
+ case *image.RGBA:
+ extraSamples = 1 // Associated alpha.
+ err = encodeRGBA(dst, m.Pix, d.X, d.Y, m.Stride, predictor)
+ case *image.RGBA64:
+ extraSamples = 1 // Associated alpha.
+ bitsPerSample = []uint32{16, 16, 16, 16}
+ err = encodeRGBA64(dst, m.Pix, d.X, d.Y, m.Stride, predictor)
+ default:
+ extraSamples = 1 // Associated alpha.
+ err = encode(dst, m, predictor)
+ }
+ if err != nil {
+ return err
+ }
+
+ if compression != cNone {
+ if err = dst.(io.Closer).Close(); err != nil {
+ return err
+ }
+ imageLen = buf.Len()
+ if err = binary.Write(w, enc, uint32(imageLen+8)); err != nil {
+ return err
+ }
+ if _, err = buf.WriteTo(w); err != nil {
+ return err
+ }
+ }
+
+ ifd := []ifdEntry{
+ {tImageWidth, dtShort, []uint32{uint32(d.X)}},
+ {tImageLength, dtShort, []uint32{uint32(d.Y)}},
+ {tBitsPerSample, dtShort, bitsPerSample},
+ {tCompression, dtShort, []uint32{compression}},
+ {tPhotometricInterpretation, dtShort, []uint32{photometricInterpretation}},
+ {tStripOffsets, dtLong, []uint32{8}},
+ {tSamplesPerPixel, dtShort, []uint32{samplesPerPixel}},
+ {tRowsPerStrip, dtShort, []uint32{uint32(d.Y)}},
+ {tStripByteCounts, dtLong, []uint32{uint32(imageLen)}},
+ // There is currently no support for storing the image
+ // resolution, so give a bogus value of 72x72 dpi.
+ {tXResolution, dtRational, []uint32{72, 1}},
+ {tYResolution, dtRational, []uint32{72, 1}},
+ {tResolutionUnit, dtShort, []uint32{resPerInch}},
+ }
+ if pr != prNone {
+ ifd = append(ifd, ifdEntry{tPredictor, dtShort, []uint32{pr}})
+ }
+ if len(colorMap) != 0 {
+ ifd = append(ifd, ifdEntry{tColorMap, dtShort, colorMap})
+ }
+ if extraSamples > 0 {
+ ifd = append(ifd, ifdEntry{tExtraSamples, dtShort, []uint32{extraSamples}})
+ }
+
+ return writeIFD(w, imageLen+8, ifd)
+}
diff --git a/api/file.go b/api/file.go
index 1cb05e81b..be8fc5456 100644
--- a/api/file.go
+++ b/api/file.go
@@ -5,16 +5,15 @@ package api
import (
"bytes"
- "code.google.com/p/graphics-go/graphics"
l4g "code.google.com/p/log4go"
"fmt"
+ "github.com/disintegration/imaging"
"github.com/goamz/goamz/aws"
"github.com/goamz/goamz/s3"
"github.com/gorilla/mux"
"github.com/mattermost/platform/model"
"github.com/mattermost/platform/utils"
"github.com/mssola/user_agent"
- "github.com/nfnt/resize"
"github.com/rwcarlsen/goexif/exif"
_ "golang.org/x/image/bmp"
"image"
@@ -24,7 +23,6 @@ import (
"image/jpeg"
"io"
"io/ioutil"
- "math"
"mime"
"net/http"
"net/url"
@@ -163,7 +161,7 @@ func fireAndForgetHandleImages(filenames []string, fileData [][]byte, teamId, ch
name := filename[:strings.LastIndex(filename, ".")]
go func() {
// Decode image bytes into Image object
- img, _, err := image.Decode(bytes.NewReader(fileData[i]))
+ img, imgType, err := image.Decode(bytes.NewReader(fileData[i]))
if err != nil {
l4g.Error("Unable to decode image channelId=%v userId=%v filename=%v err=%v", channelId, userId, filename, err)
return
@@ -175,47 +173,30 @@ func fireAndForgetHandleImages(filenames []string, fileData [][]byte, teamId, ch
// Get the image's orientation and ignore any errors since not all images will have orientation data
orientation, _ := getImageOrientation(fileData[i])
- // Create a temporary image that will be manipulated and then used to make the thumbnail and preview image
- var temp *image.RGBA
- switch orientation {
- case Upright, UprightMirrored, UpsideDown, UpsideDownMirrored:
- temp = image.NewRGBA(img.Bounds())
- case RotatedCCW, RotatedCCWMirrored, RotatedCW, RotatedCWMirrored:
- bounds := img.Bounds()
- temp = image.NewRGBA(image.Rect(bounds.Min.Y, bounds.Min.X, bounds.Max.Y, bounds.Max.X))
-
- width, height = height, width
+ if imgType == "png" {
+ dst := image.NewRGBA(img.Bounds())
+ draw.Draw(dst, dst.Bounds(), image.NewUniform(color.White), image.Point{}, draw.Src)
+ draw.Draw(dst, dst.Bounds(), img, img.Bounds().Min, draw.Over)
+ img = dst
}
- // Draw a white background since JPEGs lack transparency
- draw.Draw(temp, temp.Bounds(), image.NewUniform(color.White), image.Point{}, draw.Src)
-
- // Copy the original image onto the temporary one while rotating it as necessary
switch orientation {
- case UpsideDown, UpsideDownMirrored:
- // rotate 180 degrees
- err := graphics.Rotate(temp, img, &graphics.RotateOptions{Angle: math.Pi})
- if err != nil {
- l4g.Error("Unable to rotate image")
- }
- case RotatedCW, RotatedCWMirrored:
- // rotate 90 degrees CCW
- graphics.Rotate(temp, img, &graphics.RotateOptions{Angle: 3 * math.Pi / 2})
- if err != nil {
- l4g.Error("Unable to rotate image")
- }
- case RotatedCCW, RotatedCCWMirrored:
- // rotate 90 degrees CW
- graphics.Rotate(temp, img, &graphics.RotateOptions{Angle: math.Pi / 2})
- if err != nil {
- l4g.Error("Unable to rotate image")
- }
- case Upright, UprightMirrored:
- draw.Draw(temp, temp.Bounds(), img, img.Bounds().Min, draw.Over)
+ case UprightMirrored:
+ img = imaging.FlipH(img)
+ case UpsideDown:
+ img = imaging.Rotate180(img)
+ case UpsideDownMirrored:
+ img = imaging.FlipV(img)
+ case RotatedCWMirrored:
+ img = imaging.Transpose(img)
+ case RotatedCCW:
+ img = imaging.Rotate270(img)
+ case RotatedCCWMirrored:
+ img = imaging.Transverse(img)
+ case RotatedCW:
+ img = imaging.Rotate90(img)
}
- img = temp
-
// Create thumbnail
go func() {
thumbWidth := float64(utils.Cfg.FileSettings.ThumbnailWidth)
@@ -227,9 +208,9 @@ func fireAndForgetHandleImages(filenames []string, fileData [][]byte, teamId, ch
if imgHeight < thumbHeight && imgWidth < thumbWidth {
thumbnail = img
} else if imgHeight/imgWidth < thumbHeight/thumbWidth {
- thumbnail = resize.Resize(0, utils.Cfg.FileSettings.ThumbnailHeight, img, resize.Lanczos3)
+ thumbnail = imaging.Resize(img, 0, utils.Cfg.FileSettings.ThumbnailHeight, imaging.Lanczos)
} else {
- thumbnail = resize.Resize(utils.Cfg.FileSettings.ThumbnailWidth, 0, img, resize.Lanczos3)
+ thumbnail = imaging.Resize(img, utils.Cfg.FileSettings.ThumbnailWidth, 0, imaging.Lanczos)
}
buf := new(bytes.Buffer)
@@ -249,7 +230,7 @@ func fireAndForgetHandleImages(filenames []string, fileData [][]byte, teamId, ch
go func() {
var preview image.Image
if width > int(utils.Cfg.FileSettings.PreviewWidth) {
- preview = resize.Resize(utils.Cfg.FileSettings.PreviewWidth, utils.Cfg.FileSettings.PreviewHeight, img, resize.Lanczos3)
+ preview = imaging.Resize(img, utils.Cfg.FileSettings.PreviewWidth, utils.Cfg.FileSettings.PreviewHeight, imaging.Lanczos)
} else {
preview = img
}
diff --git a/api/user.go b/api/user.go
index 9ed4404f1..4240a795e 100644
--- a/api/user.go
+++ b/api/user.go
@@ -8,13 +8,13 @@ import (
l4g "code.google.com/p/log4go"
b64 "encoding/base64"
"fmt"
+ "github.com/disintegration/imaging"
"github.com/golang/freetype"
"github.com/gorilla/mux"
"github.com/mattermost/platform/model"
"github.com/mattermost/platform/store"
"github.com/mattermost/platform/utils"
"github.com/mssola/user_agent"
- "github.com/nfnt/resize"
"hash/fnv"
"image"
"image/color"
@@ -466,10 +466,14 @@ func RevokeAllSession(c *Context, userId string) {
for _, session := range sessions {
c.LogAuditWithUserId(userId, "session_id="+session.Id)
- sessionCache.Remove(session.Token)
- if result := <-Srv.Store.Session().Remove(session.Id); result.Err != nil {
- c.Err = result.Err
- return
+ if session.IsOAuth {
+ RevokeAccessToken(session.Token)
+ } else {
+ sessionCache.Remove(session.Token)
+ if result := <-Srv.Store.Session().Remove(session.Id); result.Err != nil {
+ c.Err = result.Err
+ return
+ }
}
}
}
@@ -798,7 +802,7 @@ func uploadProfileImage(c *Context, w http.ResponseWriter, r *http.Request) {
}
// Scale profile image
- img = resize.Resize(utils.Cfg.FileSettings.ProfileWidth, utils.Cfg.FileSettings.ProfileHeight, img, resize.Lanczos3)
+ img = imaging.Resize(img, utils.Cfg.FileSettings.ProfileWidth, utils.Cfg.FileSettings.ProfileHeight, imaging.Lanczos)
buf := new(bytes.Buffer)
err = png.Encode(buf, img)
@@ -1416,7 +1420,7 @@ func GetAuthorizationCode(c *Context, w http.ResponseWriter, r *http.Request, te
func AuthorizeOAuthUser(service, code, state, redirectUri string) (io.ReadCloser, *model.Team, *model.AppError) {
sso := utils.Cfg.GetSSOService(service)
- if sso != nil && !sso.Enable {
+ if sso == nil || !sso.Enable {
return nil, nil, model.NewAppError("AuthorizeOAuthUser", "Unsupported OAuth service provider", "service="+service)
}
@@ -1458,6 +1462,9 @@ func AuthorizeOAuthUser(service, code, state, redirectUri string) (io.ReadCloser
return nil, nil, model.NewAppError("AuthorizeOAuthUser", "Token request failed", err.Error())
} else {
ar = model.AccessResponseFromJson(resp.Body)
+ if ar == nil {
+ return nil, nil, model.NewAppError("AuthorizeOAuthUser", "Bad response from token request", "")
+ }
}
if strings.ToLower(ar.TokenType) != model.ACCESS_TOKEN_TYPE {
diff --git a/doc/README.md b/doc/README.md
index 6bf96492a..7713c40ab 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -23,3 +23,7 @@
- [Code Contribution Guidelines](developer/code-contribution.md)
- [Developer Machine Setup](install/dev-setup.md)
- [Mattermost Style Guide](developer/style-guide.md)
+
+## End User Help
+
+- [Mattermost Markdown Formatting](help/enduser/markdown.md)
diff --git a/doc/install/single-container-install.md b/doc/install/single-container-install.md
index 772f3becf..fa5265773 100644
--- a/doc/install/single-container-install.md
+++ b/doc/install/single-container-install.md
@@ -4,11 +4,11 @@ The following install instructions are for single-container installs of Mattermo
### Mac OSX ###
-1. Install Boot2Docker using instructions at: http://docs.docker.com/installation/mac/
- 1. Start Boot2Docker from the command line and run: `boot2docker init eval “$(boot2docker shellinit)”`
-2. Get your Docker IP address with: `boot2docker ip`
+1. Install Docker Toolbox using instructions at: http://docs.docker.com/installation/mac/
+ 1. Start Docker Toolbox from the command line and run: `docker-machine create -d virtualbox dev”`
+2. Get your Docker IP address with: `docker-machine ip dev`
3. Use `sudo nano /etc/hosts` to add `<Docker IP> dockerhost` to your /etc/hosts file
-4. Run: `boot2docker shellinit` and copy the export statements to your ~/.bash\_profile by running `sudo nano ~/.bash_profile`. Then run: `source ~/.bash_profile`
+4. Run: `docker-machine env dev` and copy the export statements to your ~/.bash\_profile by running `sudo nano ~/.bash_profile`. Then run: `source ~/.bash_profile`
5. Run: `docker run --name mattermost-dev -d --publish 8065:80 mattermost/platform`
6. When docker is done fetching the image, open http://dockerhost:8065/ in your browser.
diff --git a/doc/integrations/webhook/incoming.md b/doc/integrations/webhook/incoming.md
new file mode 100644
index 000000000..a48448cc5
--- /dev/null
+++ b/doc/integrations/webhook/incoming.md
@@ -0,0 +1,57 @@
+# Incoming Webhooks
+
+With incoming webhooks practically any external source - once given a URL by you - can post a message to any channel you have access to. This is done through a HTTP POST request with a simple JSON payload. The payload can contain some text, and some simple options to allow the external source to customize the post.
+
+## Creating the Webhook URL
+
+To get the incoming webhook URL - where all the HTTP requests will be sent - follow these steps:
+
+1. Login to your Mattermost account.
+2. Open the menu by clicking near your profile picture in the top-left and open Account Settings.
+3. Go to the Integrations tab and click the 'Edit' button next to 'Incoming Webhooks'.
+4. Use the selector to choose a channel and click the 'Add' button to create the webhook.
+5. Your webhook URL will be displayed below in the 'Existing incoming webhooks' section.
+
+
+## Posting a Message
+
+You can send the message by including a JSON string as the `payload` parameter in a HTTP POST request.
+```
+payload={"text": "Hello, this is some text."}
+```
+
+It is also possible to post richly formatted messages using [Markdown](../../help/enduser/markdown.md).
+```
+payload={"text": "# A Header\nThe _text_ below **the** header."}
+```
+
+Just like regular posts, the text will be limited to 4000 characters at maximum.
+
+## Adding Links
+
+In addition to including links in the standard Markdown format, links can also be specified by enclosing the URL in `<>` brackets
+```
+payload={"text": "<http://www.mattermost.com/>"}
+```
+
+They can also include a `|` character to specify some clickable text.
+```
+payload={"text": "Click <http://www.mattermost.com/|here> for a link."}
+```
+
+## Channel Override
+
+You can use a single webhook URL to post messages to different channels by overriding the channel. You can do this by adding the channel name - as it is seen in the channel URL - to the request payload.
+```
+payload={"channel": "off-topic", "text": "Hello, this is some text."}
+```
+
+## Finishing up
+
+Combining everything above, here is an example message made using a curl command:
+
+```
+curl -i -X POST 'payload={"channel": "off-topic", "text": "Hello, this is some text."}' http://yourmattermost.com/hooks/xxxxxxxxxxxxxxxxxxxxxxxxxx
+```
+
+A post with that text will be made to the Off-Topic channel.
diff --git a/model/config.go b/model/config.go
index 31c2b16dc..853e2bbc0 100644
--- a/model/config.go
+++ b/model/config.go
@@ -63,12 +63,12 @@ type FileSettings struct {
Directory string
EnablePublicLink bool
PublicLinkSalt string
- ThumbnailWidth uint
- ThumbnailHeight uint
- PreviewWidth uint
- PreviewHeight uint
- ProfileWidth uint
- ProfileHeight uint
+ ThumbnailWidth int
+ ThumbnailHeight int
+ PreviewWidth int
+ PreviewHeight int
+ ProfileWidth int
+ ProfileHeight int
InitialFont string
AmazonS3AccessKeyId string
AmazonS3SecretAccessKey string
diff --git a/web/react/components/command_list.jsx b/web/react/components/command_list.jsx
index 63bd57c2a..fea7085b7 100644
--- a/web/react/components/command_list.jsx
+++ b/web/react/components/command_list.jsx
@@ -96,4 +96,4 @@ CommandList.defaultProps = {
CommandList.propTypes = {
addCommand: React.PropTypes.func,
channelId: React.PropTypes.string
-}; \ No newline at end of file
+};
diff --git a/web/react/components/popover_list_members.jsx b/web/react/components/popover_list_members.jsx
index a2ca8b00f..95a88c3d6 100644
--- a/web/react/components/popover_list_members.jsx
+++ b/web/react/components/popover_list_members.jsx
@@ -1,6 +1,8 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
+var UserStore = require('../stores/user_store.jsx');
+
export default class PopoverListMembers extends React.Component {
componentDidMount() {
const originalLeave = $.fn.popover.Constructor.prototype.leave;
@@ -32,22 +34,28 @@ export default class PopoverListMembers extends React.Component {
}
render() {
let popoverHtml = '';
+ let count = 0;
+ let countText = '-';
const members = this.props.members;
- let count;
- if (members.length > 20) {
- count = '20+';
- } else {
- count = members.length || '-';
- }
+ const teamMembers = UserStore.getProfilesUsernameMap();
- if (members) {
+ if (members && teamMembers) {
members.sort(function compareByLocal(a, b) {
return a.username.localeCompare(b.username);
});
members.forEach(function addMemberElement(m) {
- popoverHtml += `<div class='text--nowrap'>${m.username}</div>`;
+ if (teamMembers[m.username] && teamMembers[m.username].delete_at <= 0) {
+ popoverHtml += `<div class='text--nowrap'>${m.username}</div>`;
+ count++;
+ }
});
+
+ if (count > 20) {
+ countText = '20+';
+ } else if (count > 0) {
+ countText = count.toString();
+ }
}
return (
@@ -63,7 +71,7 @@ export default class PopoverListMembers extends React.Component {
data-toggle='tooltip'
title='View Channel Members'
>
- {count}
+ {countText}
<span
className='fa fa-user'
aria-hidden='true'
diff --git a/web/react/components/post_body.jsx b/web/react/components/post_body.jsx
index 6e98e4aba..48b268700 100644
--- a/web/react/components/post_body.jsx
+++ b/web/react/components/post_body.jsx
@@ -12,7 +12,10 @@ export default class PostBody extends React.Component {
constructor(props) {
super(props);
+ this.receivedYoutubeData = false;
+
this.parseEmojis = this.parseEmojis.bind(this);
+ this.createYoutubeEmbed = this.createYoutubeEmbed.bind(this);
const linkData = Utils.extractLinks(this.props.post.message);
this.state = {links: linkData.links, message: linkData.text};
@@ -50,6 +53,123 @@ export default class PostBody extends React.Component {
this.setState({links: linkData.links, message: linkData.text});
}
+ handleYoutubeTime(link) {
+ const timeRegex = /[\\?&]t=([0-9hms]+)/;
+
+ const time = link.trim().match(timeRegex);
+ if (!time || !time[1]) {
+ return '';
+ }
+
+ const hours = time[1].match(/([0-9]+)h/);
+ const minutes = time[1].match(/([0-9]+)m/);
+ const seconds = time[1].match(/([0-9]+)s/);
+
+ let ticks = 0;
+
+ if (hours && hours[1]) {
+ ticks += parseInt(hours[1], 10) * 3600;
+ }
+
+ if (minutes && minutes[1]) {
+ ticks += parseInt(minutes[1], 10) * 60;
+ }
+
+ if (seconds && seconds[1]) {
+ ticks += parseInt(seconds[1], 10);
+ }
+
+ return '&start=' + ticks.toString();
+ }
+
+ createYoutubeEmbed(link) {
+ const ytRegex = /.*(?:youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|watch\?(?:[a-zA-Z-_]+=[a-zA-Z0-9-_]+&)+v=)([^#\&\?]*).*/;
+
+ const match = link.trim().match(ytRegex);
+ if (!match || match[1].length !== 11) {
+ return null;
+ }
+
+ const youtubeId = match[1];
+ const time = this.handleYoutubeTime(link);
+
+ function onClick(e) {
+ var div = $(e.target).closest('.video-thumbnail__container')[0];
+ var iframe = document.createElement('iframe');
+ iframe.setAttribute('src',
+ 'https://www.youtube.com/embed/' +
+ div.id +
+ '?autoplay=1&autohide=1&border=0&wmode=opaque&fs=1&enablejsapi=1' +
+ time);
+ iframe.setAttribute('width', '480px');
+ iframe.setAttribute('height', '360px');
+ iframe.setAttribute('type', 'text/html');
+ iframe.setAttribute('frameborder', '0');
+ iframe.setAttribute('allowfullscreen', 'allowfullscreen');
+
+ div.parentNode.replaceChild(iframe, div);
+ }
+
+ function success(data) {
+ if (!data.items.length || !data.items[0].snippet) {
+ return null;
+ }
+ var metadata = data.items[0].snippet;
+ this.receivedYoutubeData = true;
+ this.setState({youtubeUploader: metadata.channelTitle, youtubeTitle: metadata.title});
+ }
+
+ if (global.window.config.GoogleDeveloperKey && !this.receivedYoutubeData) {
+ $.ajax({
+ async: true,
+ url: 'https://www.googleapis.com/youtube/v3/videos',
+ type: 'GET',
+ data: {part: 'snippet', id: youtubeId, key: global.window.config.GoogleDeveloperKey},
+ success
+ });
+ }
+
+ let header = 'Youtube';
+ if (this.state.youtubeTitle) {
+ header = header + ' - ';
+ }
+
+ let uploader = this.state.youtubeUploader;
+ if (!uploader) {
+ uploader = 'unknown';
+ }
+
+ return (
+ <div className='post-comment'>
+ <h4>
+ <span className='video-type'>{header}</span>
+ <span className='video-title'><a href={link}>{this.state.youtubeTitle}</a></span>
+ </h4>
+ <h4 className='video-uploader'>{uploader}</h4>
+ <div
+ className='video-div embed-responsive-item'
+ id={youtubeId}
+ onClick={onClick}
+ >
+ <div className='embed-responsive embed-responsive-4by3 video-div__placeholder'>
+ <div
+ id={youtubeId}
+ className='video-thumbnail__container'
+ >
+ <img
+ className='video-thumbnail'
+ src={'https://i.ytimg.com/vi/' + youtubeId + '/hqdefault.jpg'}
+ />
+ <div className='block'>
+ <span className='play-button'><span/></span>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ );
+ }
+
render() {
const post = this.props.post;
const filenames = this.props.post.filenames;
@@ -133,7 +253,7 @@ export default class PostBody extends React.Component {
let embed;
if (filenames.length === 0 && this.state.links) {
- embed = Utils.getEmbed(this.state.links[0]);
+ embed = this.createYoutubeEmbed(this.state.links[0]);
}
let fileAttachmentHolder = '';
diff --git a/web/react/components/post_info.jsx b/web/react/components/post_info.jsx
index 824e7ef39..82e746dc0 100644
--- a/web/react/components/post_info.jsx
+++ b/web/react/components/post_info.jsx
@@ -150,7 +150,7 @@ export default class PostInfo extends React.Component {
var dropdown = this.createDropdown();
- let tooltip = <Tooltip>{utils.displayDate(post.create_at)} at ${utils.displayTime(post.create_at)}</Tooltip>;
+ let tooltip = <Tooltip id={post.id + 'tooltip'}>{utils.displayDate(post.create_at)} at ${utils.displayTime(post.create_at)}</Tooltip>;
return (
<ul className='post-header post-info'>
diff --git a/web/react/components/post_list.jsx b/web/react/components/post_list.jsx
index 3e1e075bb..a31967257 100644
--- a/web/react/components/post_list.jsx
+++ b/web/react/components/post_list.jsx
@@ -128,6 +128,15 @@ export default class PostList extends React.Component {
this.userHasSeenNew = true;
}
this.isUserScroll = true;
+
+ $('.top-visible-post').removeClass('top-visible-post');
+
+ $(React.findDOMNode(this.refs.postlistcontent)).children().each(function select() {
+ if ($(this).position().top + $(this).height() / 2 > 0) {
+ $(this).addClass('top-visible-post');
+ return false;
+ }
+ });
});
$('.post-list__content div .post').removeClass('post--last');
@@ -665,7 +674,10 @@ export default class PostList extends React.Component {
className={'post-list-holder-by-time ' + activeClass}
>
<div className='post-list__table'>
- <div className='post-list__content'>
+ <div
+ ref='postlistcontent'
+ className='post-list__content'
+ >
{moreMessages}
{postCtls}
</div>
diff --git a/web/react/components/sidebar_right.jsx b/web/react/components/sidebar_right.jsx
index 913715154..e63418ae8 100644
--- a/web/react/components/sidebar_right.jsx
+++ b/web/react/components/sidebar_right.jsx
@@ -14,9 +14,10 @@ export default class SidebarRight extends React.Component {
constructor(props) {
super(props);
+ this.plScrolledToBottom = true;
+
this.onSelectedChange = this.onSelectedChange.bind(this);
this.onSearchChange = this.onSearchChange.bind(this);
- this.resize = this.resize.bind(this);
this.state = getStateFromStores();
}
@@ -28,6 +29,14 @@ export default class SidebarRight extends React.Component {
PostStore.removeSearchChangeListener(this.onSearchChange);
PostStore.removeSelectedPostChangeListener(this.onSelectedChange);
}
+ componentDidUpdate() {
+ if (!this.plScrolledToBottom) {
+ $('.top-visible-post')[0].scrollIntoView();
+ } else {
+ var postHolder = $('.post-list-holder-by-time');
+ postHolder.scrollTop(postHolder[0].scrollHeight);
+ }
+ }
onSelectedChange(fromSearch) {
var newState = getStateFromStores(fromSearch);
newState.from_search = fromSearch;
@@ -41,15 +50,15 @@ export default class SidebarRight extends React.Component {
this.setState(newState);
}
}
- resize() {
- var postHolder = $('.post-list-holder-by-time');
- postHolder[0].scrollTop = postHolder[0].scrollHeight - 224;
- }
render() {
+ var postHolder = $('.post-list-holder-by-time');
+ const position = postHolder.scrollTop() + postHolder.height() + 14;
+ const bottom = postHolder[0].scrollHeight;
+ this.plScrolledToBottom = position >= bottom;
+
if (!(this.state.search_visible || this.state.post_right_visible)) {
$('.inner__wrap').removeClass('move--left').removeClass('move--right');
$('.sidebar--right').removeClass('move--left');
- this.resize();
return (
<div></div>
);
@@ -59,8 +68,8 @@ export default class SidebarRight extends React.Component {
$('.sidebar--left').removeClass('move--right');
$('.sidebar--right').addClass('move--left');
$('.sidebar--right').prepend('<div class="sidebar__overlay"></div>');
- this.resize();
- setTimeout(function overlayTimer() {
+
+ setTimeout(() => {
$('.sidebar__overlay').fadeOut('200', function fadeOverlay() {
$(this).remove();
});
diff --git a/web/react/components/user_settings/import_theme_modal.jsx b/web/react/components/user_settings/import_theme_modal.jsx
index 48be83afe..0f5cb59f5 100644
--- a/web/react/components/user_settings/import_theme_modal.jsx
+++ b/web/react/components/user_settings/import_theme_modal.jsx
@@ -140,7 +140,7 @@ export default class ImportThemeModal extends React.Component {
>
<Modal.Body>
<p>
- {'To import a theme, go to a Slack team and look for “”Preferences” -> Sidebar Theme”. Open the custom theme option, copy the theme color values and paste them here:'}
+ {'To import a theme, go to a Slack team and look for “Preferences -> Sidebar Theme”. Open the custom theme option, copy the theme color values and paste them here:'}
</p>
<div className='form-group less'>
<div className='col-sm-9'>
diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx
index 61dcae6d8..9425f3ea6 100644
--- a/web/react/utils/utils.jsx
+++ b/web/react/utils/utils.jsx
@@ -255,163 +255,6 @@ export function escapeRegExp(string) {
return string.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, '\\$1');
}
-function handleYoutubeTime(link) {
- var timeRegex = /[\\?&]t=([0-9hms]+)/;
-
- var time = link.trim().match(timeRegex);
- if (!time || !time[1]) {
- return '';
- }
-
- var hours = time[1].match(/([0-9]+)h/);
- var minutes = time[1].match(/([0-9]+)m/);
- var seconds = time[1].match(/([0-9]+)s/);
-
- var ticks = 0;
-
- if (hours && hours[1]) {
- ticks += parseInt(hours[1], 10) * 3600;
- }
-
- if (minutes && minutes[1]) {
- ticks += parseInt(minutes[1], 10) * 60;
- }
-
- if (seconds && seconds[1]) {
- ticks += parseInt(seconds[1], 10);
- }
-
- return '&start=' + ticks.toString();
-}
-
-function getYoutubeEmbed(link) {
- var regex = /.*(?:youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|watch\?(?:[a-zA-Z-_]+=[a-zA-Z0-9-_]+&)+v=)([^#\&\?]*).*/;
-
- var youtubeId = link.trim().match(regex)[1];
- var time = handleYoutubeTime(link);
-
- function onClick(e) {
- var div = $(e.target).closest('.video-thumbnail__container')[0];
- var iframe = document.createElement('iframe');
- iframe.setAttribute('src',
- 'https://www.youtube.com/embed/' +
- div.id +
- '?autoplay=1&autohide=1&border=0&wmode=opaque&fs=1&enablejsapi=1' +
- time);
- iframe.setAttribute('width', '480px');
- iframe.setAttribute('height', '360px');
- iframe.setAttribute('type', 'text/html');
- iframe.setAttribute('frameborder', '0');
- iframe.setAttribute('allowfullscreen', 'allowfullscreen');
-
- div.parentNode.replaceChild(iframe, div);
- }
-
- function success(data) {
- if (!data.items.length || !data.items[0].snippet) {
- return;
- }
- var metadata = data.items[0].snippet;
- $('.video-type.' + youtubeId).html('Youtube - ');
- $('.video-uploader.' + youtubeId).html(metadata.channelTitle);
- $('.video-title.' + youtubeId).find('a').html(metadata.title);
- }
-
- if (global.window.config.GoogleDeveloperKey) {
- $.ajax({
- async: true,
- url: 'https://www.googleapis.com/youtube/v3/videos',
- type: 'GET',
- data: {part: 'snippet', id: youtubeId, key: global.window.config.GoogleDeveloperKey},
- success: success
- });
- }
-
- return (
- <div className='post-comment'>
- <h4>
- <span className={'video-type ' + youtubeId}>YouTube</span>
- <span className={'video-title ' + youtubeId}><a href={link}></a></span>
- </h4>
- <h4 className={'video-uploader ' + youtubeId}></h4>
- <div
- className='video-div embed-responsive-item'
- id={youtubeId}
- onClick={onClick}
- >
- <div className='embed-responsive embed-responsive-4by3 video-div__placeholder'>
- <div
- id={youtubeId}
- className='video-thumbnail__container'
- >
- <img
- className='video-thumbnail'
- src={'https://i.ytimg.com/vi/' + youtubeId + '/hqdefault.jpg'}
- />
- <div className='block'>
- <span className='play-button'><span/></span>
- </div>
- </div>
- </div>
- </div>
- </div>
- );
-}
-
-export function getEmbed(link) {
- var ytRegex = /.*(?:youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|watch\?(?:[a-zA-Z-_]+=[a-zA-Z0-9-_]+&)+v=)([^#\&\?]*).*/;
-
- var match = link.trim().match(ytRegex);
- if (match && match[1].length === 11) {
- return getYoutubeEmbed(link);
- }
-
- // Generl embed feature turned off for now
- return '';
-
- // NEEDS REFACTORING WHEN TURNED BACK ON
- /*
- var id = parseInt((Math.random() * 1000000) + 1);
-
- $.ajax({
- type: 'GET',
- url: 'https://query.yahooapis.com/v1/public/yql',
- data: {
- q: 'select * from html where url="' + link + "\" and xpath='html/head'",
- format: 'json'
- },
- async: true
- }).done(function(data) {
- if(!data.query.results) {
- return;
- }
-
- var headerData = data.query.results.head;
-
- var description = ''
- for(var i = 0; i < headerData.meta.length; i++) {
- if(headerData.meta[i].name && (headerData.meta[i].name === 'description' || headerData.meta[i].name === 'Description')){
- description = headerData.meta[i].content;
- break;
- }
- }
-
- $('.embed-title.'+id).html(headerData.title);
- $('.embed-description.'+id).html(description);
- })
-
- return (
- <div className='post-comment'>
- <div className={'web-embed-data'}>
- <p className={'embed-title ' + id} />
- <p className={'embed-description ' + id} />
- <p className={'embed-link ' + id}>{link}</p>
- </div>
- </div>
- );
- */
-}
-
export function areStatesEqual(state1, state2) {
return JSON.stringify(state1) === JSON.stringify(state2);
}
@@ -606,7 +449,7 @@ export function applyTheme(theme) {
}
if (theme.centerChannelBg) {
- changeCss('.app__content, .markdown__table, .markdown__table tbody tr', 'background:' + theme.centerChannelBg, 1);
+ changeCss('.app__content, .markdown__table, .markdown__table tbody tr, .command-box', 'background:' + theme.centerChannelBg, 1);
changeCss('#post-list .post-list-holder-by-time', 'background:' + theme.centerChannelBg, 1);
changeCss('#post-create', 'background:' + theme.centerChannelBg, 1);
changeCss('.search-bar__container .search__form .search-bar', 'background:' + theme.centerChannelBg, 1);
@@ -616,14 +459,18 @@ export function applyTheme(theme) {
}
if (theme.centerChannelColor) {
- changeCss('.app__content, .post-create__container .post-create-body .btn-file, .post-create__container .post-create-footer .msg-typing, .loading-screen .loading__content .round', 'color:' + theme.centerChannelColor, 1);
+ changeCss('.app__content, .post-create__container .post-create-body .btn-file, .post-create__container .post-create-footer .msg-typing, .loading-screen .loading__content .round, .command-name', 'color:' + theme.centerChannelColor, 1);
changeCss('#post-create', 'color:' + theme.centerChannelColor, 2);
+ changeCss('.mentions--top, .command-box', 'box-shadow:' + changeOpacity(theme.centerChannelColor, 0.2) + ' 1px -3px 12px', 3);
+ changeCss('.mentions--top, .command-box', '-webkit-box-shadow:' + changeOpacity(theme.centerChannelColor, 0.2) + ' 1px -3px 12px', 2);
+ changeCss('.mentions--top, .command-box', '-moz-box-shadow:' + changeOpacity(theme.centerChannelColor, 0.2) + ' 1px -3px 12px', 1);
changeCss('.post-body hr', 'background:' + theme.centerChannelColor, 1);
changeCss('.channel-header .heading', 'color:' + theme.centerChannelColor, 1);
changeCss('.markdown__table tbody tr:nth-child(2n)', 'background:' + changeOpacity(theme.centerChannelColor, 0.07), 1);
changeCss('.channel-header__info>div.dropdown .header-dropdown__icon', 'color:' + changeOpacity(theme.centerChannelColor, 0.8), 1);
changeCss('.channel-header #member_popover', 'color:' + changeOpacity(theme.centerChannelColor, 0.8), 1);
- changeCss('.custom-textarea, .custom-textarea:focus, .preview-container .preview-div, .post-image__column .post-image__details, .sidebar--right .sidebar-right__body, .markdown__table th, .markdown__table td', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1);
+ changeCss('.custom-textarea, .custom-textarea:focus, .preview-container .preview-div, .post-image__column .post-image__details, .sidebar--right .sidebar-right__body, .markdown__table th, .markdown__table td, .command-box', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1);
+ changeCss('.command-name', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1);
changeCss('.custom-textarea', 'color:' + theme.centerChannelColor, 1);
changeCss('.post-image__column', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 2);
changeCss('.post-image__column .post-image__details', 'color:' + theme.centerChannelColor, 2);
@@ -641,7 +488,7 @@ export function applyTheme(theme) {
changeCss('@media(max-width: 1800px){.inner__wrap.move--left .post.post--comment.same--root', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.07), 2);
changeCss('.post:hover, .sidebar--right .sidebar--right__header', 'background:' + changeOpacity(theme.centerChannelColor, 0.07), 1);
changeCss('.date-separator.hovered--before:after, .new-separator.hovered--before:after', 'background:' + changeOpacity(theme.centerChannelColor, 0.07), 1);
- changeCss('.date-separator.hovered--after:before, .new-separator.hovered--after:before', 'background:' + changeOpacity(theme.centerChannelColor, 0.07), 1);
+ changeCss('.date-separator.hovered--after:before, .new-separator.hovered--after:before, .command-name:hover', 'background:' + changeOpacity(theme.centerChannelColor, 0.07), 1);
changeCss('.post.current--user:hover .post-body ', 'background: none;', 1);
changeCss('.sidebar--right', 'color:' + theme.centerChannelColor, 2);
}
diff --git a/web/sass-files/sass/partials/_command-box.scss b/web/sass-files/sass/partials/_command-box.scss
index 44eb9b8df..f1aa4dca2 100644
--- a/web/sass-files/sass/partials/_command-box.scss
+++ b/web/sass-files/sass/partials/_command-box.scss
@@ -4,20 +4,29 @@
width: 100%;
border: $border-gray;
bottom: 38px;
+ overflow: auto;
@extend %popover-box-shadow;
+ .sidebar--right & {
+ bottom: 100px;
+ }
}
.command-name {
position: relative;
width: 100%;
- background-color: #fff;
- height: 37px;
- line-height: 37px;
- padding: 2px 10px 2px 5px;
+ line-height: 24px;
+ padding: 5px 10px 8px;
z-index: 101;
+ font-size: 0.95em;
+ border-bottom: 1px solid #ddd;
&:hover {
background-color: #e8eaed;
}
+ .command__desc {
+ margin-left: 5px;
+ @include opacity(0.5);
+ line-height: normal;
+ }
}
.command-desc {