diff options
| author | 2022-08-10 14:05:14 +0200 | |
|---|---|---|
| committer | 2022-08-10 14:05:14 +0200 | |
| commit | 91c8d5d20d9abee8113614ef49b1a626c76c16b4 (patch) | |
| tree | 745d1b3df4b32b50bc098770d44e5f4f9f72311b | |
| parent | [bugfix] Fix placeholder typo in user panel (#745) (diff) | |
| download | gotosocial-91c8d5d20d9abee8113614ef49b1a626c76c16b4.tar.xz | |
[bugfix] Fix thumbnails not taking exif rotation into account (#746)
* use disintegration/imaging instead of nfnt/resize
* update tests
* use disintegration lib for thumbing (if necessary)
47 files changed, 6984 insertions, 2163 deletions
@@ -207,6 +207,7 @@ The following libraries and frameworks are used by GoToSocial, with gratitude ðŸ  - [buckket/go-blurhash](https://github.com/buckket/go-blurhash); used for generating image blurhashes. [GPL-3.0 License](https://spdx.org/licenses/GPL-3.0-only.html).  - [coreos/go-oidc](https://github.com/coreos/go-oidc); OIDC client library. [Apache-2.0 License](https://spdx.org/licenses/Apache-2.0.html). +- [disintegration/imaging](https://github.com/disintegration/imaging); image resizing. [MIT License](https://spdx.org/licenses/MIT.html).  - [gin-gonic/gin](https://github.com/gin-gonic/gin); speedy router engine. [MIT License](https://spdx.org/licenses/MIT.html).    - [gin-contrib/cors](https://github.com/gin-contrib/cors); Gin CORS middleware. [MIT License](https://spdx.org/licenses/MIT.html).    - [gin-contrib/gzip](https://github.com/gin-contrib/gzip); Gin gzip middleware. [MIT License](https://spdx.org/licenses/MIT.html). @@ -232,7 +233,6 @@ The following libraries and frameworks are used by GoToSocial, with gratitude ðŸ    - [modernc.org/ccgo](https://gitlab.com/cznic/ccgo); c99 AST -> Go translater. [BSD-3-Clause License](https://spdx.org/licenses/BSD-3-Clause.html).    - [modernc.org/libc](https://gitlab.com/cznic/libc); C-runtime services. [BSD-3-Clause License](https://spdx.org/licenses/BSD-3-Clause.html).  - [mvdan/xurls](https://github.com/mvdan/xurls); URL parsing regular expressions. [BSD-3-Clause License](https://spdx.org/licenses/BSD-3-Clause.html). -- [nfnt/resize](https://github.com/nfnt/resize); convenient image resizing. [ISC License](https://spdx.org/licenses/ISC.html).  - [oklog/ulid](https://github.com/oklog/ulid); sequential, database-friendly ID generation. [Apache-2.0 License](https://spdx.org/licenses/Apache-2.0.html).  - [ReneKroon/ttlcache](https://github.com/ReneKroon/ttlcache); in-memory caching. [MIT License](https://spdx.org/licenses/MIT.html).  - [robfig/cron](https://github.com/robfig/cron); cron job scheduling. [MIT License](https://spdx.org/licenses/MIT.html). @@ -16,6 +16,7 @@ require (  	codeberg.org/gruf/go-store v1.3.8  	github.com/buckket/go-blurhash v1.1.0  	github.com/coreos/go-oidc/v3 v3.1.0 +	github.com/disintegration/imaging v1.6.2  	github.com/gin-contrib/cors v1.3.1  	github.com/gin-contrib/gzip v0.0.5  	github.com/gin-contrib/sessions v0.0.5 @@ -31,7 +32,6 @@ require (  	github.com/miekg/dns v1.1.49  	github.com/minio/minio-go/v7 v7.0.29  	github.com/mitchellh/mapstructure v1.5.0 -	github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646  	github.com/oklog/ulid v1.3.1  	github.com/robfig/cron/v3 v3.0.1  	github.com/russross/blackfriday/v2 v2.1.0 @@ -126,6 +126,7 @@ require (  	github.com/ugorji/go/codec v1.2.7 // indirect  	github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect  	github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect +	golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 // indirect  	golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect  	golang.org/x/sys v0.0.0-20220429233432-b5fbb4746d32 // indirect  	golang.org/x/tools v0.1.10 // indirect @@ -122,6 +122,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3  github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=  github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=  github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= +github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=  github.com/djherbis/atime v1.1.0/go.mod h1:28OF6Y8s3NQWwacXc5eZTsEsiMzp7LF8MbXE+XJPdBE=  github.com/dsoprea/go-exif/v2 v2.0.0-20200321225314-640175a69fe4/go.mod h1:Lm2lMM2zx8p4a34ZemkaUV95AnMl4ZvLbCUbwOvLC2E=  github.com/dsoprea/go-exif/v3 v3.0.0-20200717053412-08f1b6708903/go.mod h1:0nsO1ce0mh5czxGeLo4+OCZ/C6Eo6ZlMWsz7rH/Gxv8= @@ -432,8 +434,6 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G  github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=  github.com/moul/http2curl v1.0.0 h1:dRMWoAtb+ePxMlLkrCbAqh4TlPHXvoGUSQ323/9Zahs=  github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= -github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= -github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=  github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=  github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=  github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= @@ -635,6 +635,8 @@ golang.org/x/exp v0.0.0-20220613132600-b0d781184e0d h1:vtUKgx8dahOomfFzLREU8nSv2  golang.org/x/exp v0.0.0-20220613132600-b0d781184e0d/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=  golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=  golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 h1:hVwzHzIUGRjiF7EcUjqNxk3NCfkPxbDKRdnNE1Rpg0U= +golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=  golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=  golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=  golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= diff --git a/internal/api/client/media/mediacreate_test.go b/internal/api/client/media/mediacreate_test.go index 8651fd982..be3bf300e 100644 --- a/internal/api/client/media/mediacreate_test.go +++ b/internal/api/client/media/mediacreate_test.go @@ -212,7 +212,7 @@ func (suite *MediaCreateTestSuite) TestMediaCreateSuccessful() {  			Y: 0.5,  		},  	}, attachmentReply.Meta) -	suite.Equal("LjBzUo#6RQR._NvzRjWF?urqV@a$", attachmentReply.Blurhash) +	suite.Equal("LiBzRk#6V[WF_NvzV@WY_3rqV@a$", attachmentReply.Blurhash)  	suite.NotEmpty(attachmentReply.ID)  	suite.NotEmpty(attachmentReply.URL)  	suite.NotEmpty(attachmentReply.PreviewURL) @@ -306,7 +306,7 @@ func (suite *MediaCreateTestSuite) TestMediaCreateSuccessfulV2() {  			Y: 0.5,  		},  	}, attachmentReply.Meta) -	suite.Equal("LjBzUo#6RQR._NvzRjWF?urqV@a$", attachmentReply.Blurhash) +	suite.Equal("LiBzRk#6V[WF_NvzV@WY_3rqV@a$", attachmentReply.Blurhash)  	suite.NotEmpty(attachmentReply.ID)  	suite.Nil(attachmentReply.URL)  	suite.NotEmpty(attachmentReply.PreviewURL) diff --git a/internal/media/image.go b/internal/media/image.go index cb26ad539..9dd699dc6 100644 --- a/internal/media/image.go +++ b/internal/media/image.go @@ -29,7 +29,7 @@ import (  	"io"  	"github.com/buckket/go-blurhash" -	"github.com/nfnt/resize" +	"github.com/disintegration/imaging"  )  const ( @@ -114,33 +114,40 @@ func deriveThumbnail(r io.Reader, contentType string, createBlurhash bool) (*ima  	var err error  	switch contentType { -	case mimeImageJpeg: -		i, err = jpeg.Decode(r) +	case mimeImageJpeg, mimeImageGif: +		i, err = imaging.Decode(r, imaging.AutoOrientation(true))  	case mimeImagePng: -		i, err = StrippedPngDecode(r) -	case mimeImageGif: -		i, err = gif.Decode(r) +		strippedPngReader := io.Reader(&PNGAncillaryChunkStripper{ +			Reader: r, +		}) +		i, err = imaging.Decode(strippedPngReader, imaging.AutoOrientation(true))  	default:  		err = fmt.Errorf("content type %s can't be thumbnailed", contentType)  	}  	if err != nil { -		return nil, fmt.Errorf("error decoding image as %s: %s", contentType, err) +		return nil, fmt.Errorf("error decoding %s: %s", contentType, err)  	} -	if i == nil { -		return nil, errors.New("processed image was nil") +	originalX := i.Bounds().Size().X +	originalY := i.Bounds().Size().Y + +	var thumb image.Image +	if originalX <= thumbnailMaxWidth && originalY <= thumbnailMaxHeight { +		// it's already small, no need to resize +		thumb = i +	} else { +		thumb = imaging.Fit(i, thumbnailMaxWidth, thumbnailMaxHeight, imaging.Linear)  	} -	thumb := resize.Thumbnail(thumbnailMaxWidth, thumbnailMaxHeight, i, resize.NearestNeighbor) -	width := thumb.Bounds().Size().X -	height := thumb.Bounds().Size().Y -	size := width * height -	aspect := float64(width) / float64(height) +	thumbX := thumb.Bounds().Size().X +	thumbY := thumb.Bounds().Size().Y +	size := thumbX * thumbY +	aspect := float64(thumbX) / float64(thumbY)  	im := &imageMeta{ -		width:  width, -		height: height, +		width:  thumbX, +		height: thumbY,  		size:   size,  		aspect: aspect,  	} @@ -148,7 +155,7 @@ func deriveThumbnail(r io.Reader, contentType string, createBlurhash bool) (*ima  	if createBlurhash {  		// for generating blurhashes, it's more cost effective to lose detail rather than  		// pass a big image into the blurhash algorithm, so make a teeny tiny version -		tiny := resize.Thumbnail(32, 32, thumb, resize.NearestNeighbor) +		tiny := imaging.Resize(thumb, 32, 0, imaging.NearestNeighbor)  		bh, err := blurhash.Encode(4, 3, tiny)  		if err != nil {  			return nil, fmt.Errorf("error creating blurhash: %s", err) diff --git a/internal/media/manager_test.go b/internal/media/manager_test.go index f55763439..396df1523 100644 --- a/internal/media/manager_test.go +++ b/internal/media/manager_test.go @@ -80,7 +80,7 @@ func (suite *ManagerTestSuite) TestSimpleJpegProcessBlocking() {  	suite.Equal("image/jpeg", attachment.File.ContentType)  	suite.Equal("image/jpeg", attachment.Thumbnail.ContentType)  	suite.Equal(269739, attachment.File.FileSize) -	suite.Equal("LjBzUo#6RQR._NvzRjWF?urqV@a$", attachment.Blurhash) +	suite.Equal("LiBzRk#6V[WF_NvzV@WY_3rqV@a$", attachment.Blurhash)  	// now make sure the attachment is in the database  	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachmentID) @@ -152,7 +152,7 @@ func (suite *ManagerTestSuite) TestPngNoAlphaChannelProcessBlocking() {  	suite.Equal("image/png", attachment.File.ContentType)  	suite.Equal("image/jpeg", attachment.Thumbnail.ContentType)  	suite.Equal(17471, attachment.File.FileSize) -	suite.Equal("LFP?{^.A-?xd.9o#RVRQ~oj:_0xW", attachment.Blurhash) +	suite.Equal("LFQT7e.A%O%4?co$M}M{_1W9~TxV", attachment.Blurhash)  	// now make sure the attachment is in the database  	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachmentID) @@ -224,7 +224,7 @@ func (suite *ManagerTestSuite) TestPngAlphaChannelProcessBlocking() {  	suite.Equal("image/png", attachment.File.ContentType)  	suite.Equal("image/jpeg", attachment.Thumbnail.ContentType)  	suite.Equal(18904, attachment.File.FileSize) -	suite.Equal("LFP?{^.A-?xd.9o#RVRQ~oj:_0xW", attachment.Blurhash) +	suite.Equal("LFQT7e.A%O%4?co$M}M{_1W9~TxV", attachment.Blurhash)  	// now make sure the attachment is in the database  	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachmentID) @@ -307,7 +307,7 @@ func (suite *ManagerTestSuite) TestSimpleJpegProcessBlockingWithCallback() {  	suite.Equal("image/jpeg", attachment.File.ContentType)  	suite.Equal("image/jpeg", attachment.Thumbnail.ContentType)  	suite.Equal(269739, attachment.File.FileSize) -	suite.Equal("LjBzUo#6RQR._NvzRjWF?urqV@a$", attachment.Blurhash) +	suite.Equal("LiBzRk#6V[WF_NvzV@WY_3rqV@a$", attachment.Blurhash)  	// now make sure the attachment is in the database  	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachmentID) @@ -386,7 +386,7 @@ func (suite *ManagerTestSuite) TestSimpleJpegProcessAsync() {  	suite.Equal("image/jpeg", attachment.File.ContentType)  	suite.Equal("image/jpeg", attachment.Thumbnail.ContentType)  	suite.Equal(269739, attachment.File.FileSize) -	suite.Equal("LjBzUo#6RQR._NvzRjWF?urqV@a$", attachment.Blurhash) +	suite.Equal("LiBzRk#6V[WF_NvzV@WY_3rqV@a$", attachment.Blurhash)  	// now make sure the attachment is in the database  	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachmentID) @@ -467,7 +467,7 @@ func (suite *ManagerTestSuite) TestSimpleJpegQueueSpamming() {  		suite.Equal("image/jpeg", attachment.File.ContentType)  		suite.Equal("image/jpeg", attachment.Thumbnail.ContentType)  		suite.Equal(269739, attachment.File.FileSize) -		suite.Equal("LjBzUo#6RQR._NvzRjWF?urqV@a$", attachment.Blurhash) +		suite.Equal("LiBzRk#6V[WF_NvzV@WY_3rqV@a$", attachment.Blurhash)  		// now make sure the attachment is in the database  		dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachmentID) @@ -556,7 +556,7 @@ func (suite *ManagerTestSuite) TestSimpleJpegProcessBlockingWithDiskStorage() {  	suite.Equal("image/jpeg", attachment.File.ContentType)  	suite.Equal("image/jpeg", attachment.Thumbnail.ContentType)  	suite.Equal(269739, attachment.File.FileSize) -	suite.Equal("LjBzUo#6RQR._NvzRjWF?urqV@a$", attachment.Blurhash) +	suite.Equal("LiBzRk#6V[WF_NvzV@WY_3rqV@a$", attachment.Blurhash)  	// now make sure the attachment is in the database  	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachmentID) diff --git a/internal/media/test/test-jpeg-thumbnail.jpg b/internal/media/test/test-jpeg-thumbnail.jpg Binary files differindex eeb947ed1..80170e7c8 100644 --- a/internal/media/test/test-jpeg-thumbnail.jpg +++ b/internal/media/test/test-jpeg-thumbnail.jpg diff --git a/vendor/github.com/disintegration/imaging/.travis.yml b/vendor/github.com/disintegration/imaging/.travis.yml new file mode 100644 index 000000000..7ae5e4b25 --- /dev/null +++ b/vendor/github.com/disintegration/imaging/.travis.yml @@ -0,0 +1,12 @@ +language: go +go: +  - "1.10.x" +  - "1.11.x" +  - "1.12.x" + +before_install: +  - go get github.com/mattn/goveralls + +script: +  - go test -v -race -cover +  - $GOPATH/bin/goveralls -service=travis-ci diff --git a/vendor/github.com/disintegration/imaging/LICENSE b/vendor/github.com/disintegration/imaging/LICENSE new file mode 100644 index 000000000..a4144a9d2 --- /dev/null +++ b/vendor/github.com/disintegration/imaging/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2012 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. diff --git a/vendor/github.com/disintegration/imaging/README.md b/vendor/github.com/disintegration/imaging/README.md new file mode 100644 index 000000000..a1fd764d2 --- /dev/null +++ b/vendor/github.com/disintegration/imaging/README.md @@ -0,0 +1,226 @@ +# Imaging
 +
 +[](https://godoc.org/github.com/disintegration/imaging)
 +[](https://travis-ci.org/disintegration/imaging)
 +[](https://coveralls.io/github/disintegration/imaging?branch=master)
 +[](https://goreportcard.com/report/github.com/disintegration/imaging)
 +
 +Package imaging provides basic image processing functions (resize, rotate, crop, brightness/contrast adjustments, etc.).
 +
 +All the image processing functions provided by the package accept any image type that implements `image.Image` interface
 +as an input, and return a new image of `*image.NRGBA` type (32bit RGBA colors, non-premultiplied alpha).
 +
 +## Installation
 +
 +    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 fill the 100x100px area.
 +dstImageFill := imaging.Fill(srcImage, 100, 100, imaging.Center, imaging.Lanczos)
 +```
 +
 +Imaging supports image resizing using various resampling filters. The most notable ones:
 +- `Lanczos` - A high-quality resampling filter for photographic images yielding sharp results.
 +- `CatmullRom` - A sharp cubic filter that is faster than Lanczos filter while providing similar results.
 +- `MitchellNetravali` - A cubic filter that produces smoother results with less ringing artifacts than CatmullRom.
 +- `Linear` - Bilinear resampling filter, produces smooth output. Faster than cubic filters.
 +- `Box` - Simple and fast averaging filter appropriate for downscaling. When upscaling it's similar to NearestNeighbor.
 +- `NearestNeighbor` - Fastest resampling filter, no antialiasing.
 +
 +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:
 +
 +
 +
 +The same image resized from 600x400px to 150x100px using different resampling filters.
 +From faster (lower quality) to slower (higher quality):
 +
 +Filter                    | Resize result
 +--------------------------|---------------------------------------------
 +`imaging.NearestNeighbor` | 
 +`imaging.Linear`          | 
 +`imaging.CatmullRom`      | 
 +`imaging.Lanczos`         | 
 +
 +
 +### 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
 +-----------------------------------|----------------------------------------|---------------------------------------
 + |  | 
 +
 +### Sharpening
 +
 +```go
 +dstImage := imaging.Sharpen(srcImage, 0.5)
 +```
 +
 +`Sharpen` uses gaussian function internally. Sigma parameter allows to control the strength of the sharpening effect.
 +
 +Original image                     | Sigma = 0.5                               | Sigma = 1.5
 +-----------------------------------|-------------------------------------------|------------------------------------------
 + |  | 
 +
 +### Gamma correction
 +
 +```go
 +dstImage := imaging.AdjustGamma(srcImage, 0.75)
 +```
 +
 +Original image                     | Gamma = 0.75                             | Gamma = 1.25
 +-----------------------------------|------------------------------------------|-----------------------------------------
 + |  | 
 +
 +### Contrast adjustment
 +
 +```go
 +dstImage := imaging.AdjustContrast(srcImage, 20)
 +```
 +
 +Original image                     | Contrast = 15                              | Contrast = -15
 +-----------------------------------|--------------------------------------------|-------------------------------------------
 + |  | 
 +
 +### Brightness adjustment
 +
 +```go
 +dstImage := imaging.AdjustBrightness(srcImage, 20)
 +```
 +
 +Original image                     | Brightness = 10                              | Brightness = -10
 +-----------------------------------|----------------------------------------------|---------------------------------------------
 + |  | 
 +
 +### Saturation adjustment
 +
 +```go
 +dstImage := imaging.AdjustSaturation(srcImage, 20)
 +```
 +
 +Original image                     | Saturation = 30                              | Saturation = -30
 +-----------------------------------|----------------------------------------------|---------------------------------------------
 + |  | 
 +
 +## FAQ
 +
 +### Incorrect image orientation after processing (e.g. an image appears rotated after resizing)
 +
 +Most probably, the given image contains the EXIF orientation tag.
 +The stadard `image/*` packages do not support loading and saving
 +this kind of information. To fix the issue, try opening images with
 +the `AutoOrientation` decode option. If this option is set to `true`,
 +the image orientation is changed after decoding, according to the
 +orientation tag (if present). Here's the example:
 +
 +```go
 +img, err := imaging.Open("test.jpg", imaging.AutoOrientation(true))
 +```
 +
 +### What's the difference between `imaging` and `gift` packages?
 +
 +[imaging](https://github.com/disintegration/imaging)
 +is designed to be a lightweight and simple image manipulation package.
 +It provides basic image processing functions and a few helper functions
 +such as `Open` and `Save`. It consistently returns *image.NRGBA image 
 +type (8 bits per channel, RGBA).
 +
 +[gift](https://github.com/disintegration/gift)
 +supports more advanced image processing, for example, sRGB/Linear color
 +space conversions. It also supports different output image types
 +(e.g. 16 bits per channel) and provides easy-to-use API for chaining
 +multiple processing steps together.
 +
 +## Example code
 +
 +```go
 +package main
 +
 +import (
 +	"image"
 +	"image/color"
 +	"log"
 +
 +	"github.com/disintegration/imaging"
 +)
 +
 +func main() {
 +	// Open a test image.
 +	src, err := imaging.Open("testdata/flowers.png")
 +	if err != nil {
 +		log.Fatalf("failed to open image: %v", err)
 +	}
 +
 +	// Crop the original image to 300x300px size using the center anchor.
 +	src = imaging.CropAnchor(src, 300, 300, imaging.Center)
 +
 +	// Resize the cropped image to width = 200px preserving the aspect ratio.
 +	src = imaging.Resize(src, 200, 0, imaging.Lanczos)
 +
 +	// Create a blurred version of the image.
 +	img1 := imaging.Blur(src, 5)
 +
 +	// Create a grayscale version of the image with higher contrast and sharpness.
 +	img2 := imaging.Grayscale(src)
 +	img2 = imaging.AdjustContrast(img2, 20)
 +	img2 = imaging.Sharpen(img2, 2)
 +
 +	// Create an inverted version of the image.
 +	img3 := imaging.Invert(src)
 +
 +	// Create an embossed version of the image using a convolution filter.
 +	img4 := imaging.Convolve3x3(
 +		src,
 +		[9]float64{
 +			-1, -1, 0,
 +			-1, 1, 1,
 +			0, 1, 1,
 +		},
 +		nil,
 +	)
 +
 +	// Create a new image and paste the four produced images into it.
 +	dst := imaging.New(400, 400, color.NRGBA{0, 0, 0, 0})
 +	dst = imaging.Paste(dst, img1, image.Pt(0, 0))
 +	dst = imaging.Paste(dst, img2, image.Pt(0, 200))
 +	dst = imaging.Paste(dst, img3, image.Pt(200, 0))
 +	dst = imaging.Paste(dst, img4, image.Pt(200, 200))
 +
 +	// Save the resulting image as JPEG.
 +	err = imaging.Save(dst, "testdata/out_example.jpg")
 +	if err != nil {
 +		log.Fatalf("failed to save image: %v", err)
 +	}
 +}
 +```
 +
 +Output:
 +
 +
 diff --git a/vendor/github.com/disintegration/imaging/adjust.go b/vendor/github.com/disintegration/imaging/adjust.go new file mode 100644 index 000000000..daaf1de86 --- /dev/null +++ b/vendor/github.com/disintegration/imaging/adjust.go @@ -0,0 +1,253 @@ +package imaging + +import ( +	"image" +	"image/color" +	"math" +) + +// Grayscale produces a grayscale version of the image. +func Grayscale(img image.Image) *image.NRGBA { +	src := newScanner(img) +	dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h)) +	parallel(0, src.h, func(ys <-chan int) { +		for y := range ys { +			i := y * dst.Stride +			src.scan(0, y, src.w, y+1, dst.Pix[i:i+src.w*4]) +			for x := 0; x < src.w; x++ { +				d := dst.Pix[i : i+3 : i+3] +				r := d[0] +				g := d[1] +				b := d[2] +				f := 0.299*float64(r) + 0.587*float64(g) + 0.114*float64(b) +				y := uint8(f + 0.5) +				d[0] = y +				d[1] = y +				d[2] = y +				i += 4 +			} +		} +	}) +	return dst +} + +// Invert produces an inverted (negated) version of the image. +func Invert(img image.Image) *image.NRGBA { +	src := newScanner(img) +	dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h)) +	parallel(0, src.h, func(ys <-chan int) { +		for y := range ys { +			i := y * dst.Stride +			src.scan(0, y, src.w, y+1, dst.Pix[i:i+src.w*4]) +			for x := 0; x < src.w; x++ { +				d := dst.Pix[i : i+3 : i+3] +				d[0] = 255 - d[0] +				d[1] = 255 - d[1] +				d[2] = 255 - d[2] +				i += 4 +			} +		} +	}) +	return dst +} + +// AdjustSaturation changes the saturation of the image using the percentage parameter and returns the adjusted image. +// The percentage must be in the range (-100, 100). +// The percentage = 0 gives the original image. +// The percentage = 100 gives the image with the saturation value doubled for each pixel. +// The percentage = -100 gives the image with the saturation value zeroed for each pixel (grayscale). +// +// Examples: +//  dstImage = imaging.AdjustSaturation(srcImage, 25) // Increase image saturation by 25%. +//  dstImage = imaging.AdjustSaturation(srcImage, -10) // Decrease image saturation by 10%. +// +func AdjustSaturation(img image.Image, percentage float64) *image.NRGBA { +	percentage = math.Min(math.Max(percentage, -100), 100) +	multiplier := 1 + percentage/100 + +	return AdjustFunc(img, func(c color.NRGBA) color.NRGBA { +		h, s, l := rgbToHSL(c.R, c.G, c.B) +		s *= multiplier +		if s > 1 { +			s = 1 +		} +		r, g, b := hslToRGB(h, s, l) +		return color.NRGBA{r, g, b, c.A} +	}) +} + +// 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 gray 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++ { +		switch { +		case 0 <= v && v <= 1: +			lut[i] = clamp((0.5 + (float64(i)/255.0-0.5)*v) * 255.0) +		case 1 < v && v < 2: +			lut[i] = clamp((0.5 + (float64(i)/255.0-0.5)*(1/(2.0-v))) * 255.0) +		default: +			lut[i] = uint8(float64(i)/255.0+0.5) * 255 +		} +	} + +	return adjustLUT(img, lut) +} + +// 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) +	} + +	return adjustLUT(img, lut) +} + +// 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) +	} + +	return adjustLUT(img, lut) +} + +// 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) +		} +	} + +	return adjustLUT(img, lut) +} + +func sigmoid(a, b, x float64) float64 { +	return 1 / (1 + math.Exp(b*(a-x))) +} + +// adjustLUT applies the given lookup table to the colors of the image. +func adjustLUT(img image.Image, lut []uint8) *image.NRGBA { +	src := newScanner(img) +	dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h)) +	lut = lut[0:256] +	parallel(0, src.h, func(ys <-chan int) { +		for y := range ys { +			i := y * dst.Stride +			src.scan(0, y, src.w, y+1, dst.Pix[i:i+src.w*4]) +			for x := 0; x < src.w; x++ { +				d := dst.Pix[i : i+3 : i+3] +				d[0] = lut[d[0]] +				d[1] = lut[d[1]] +				d[2] = lut[d[2]] +				i += 4 +			} +		} +	}) +	return dst +} + +// 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 := newScanner(img) +	dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h)) +	parallel(0, src.h, func(ys <-chan int) { +		for y := range ys { +			i := y * dst.Stride +			src.scan(0, y, src.w, y+1, dst.Pix[i:i+src.w*4]) +			for x := 0; x < src.w; x++ { +				d := dst.Pix[i : i+4 : i+4] +				r := d[0] +				g := d[1] +				b := d[2] +				a := d[3] +				c := fn(color.NRGBA{r, g, b, a}) +				d[0] = c.R +				d[1] = c.G +				d[2] = c.B +				d[3] = c.A +				i += 4 +			} +		} +	}) +	return dst +} diff --git a/vendor/github.com/disintegration/imaging/convolution.go b/vendor/github.com/disintegration/imaging/convolution.go new file mode 100644 index 000000000..11eddc162 --- /dev/null +++ b/vendor/github.com/disintegration/imaging/convolution.go @@ -0,0 +1,148 @@ +package imaging + +import ( +	"image" +) + +// ConvolveOptions are convolution parameters. +type ConvolveOptions struct { +	// If Normalize is true the kernel is normalized before convolution. +	Normalize bool + +	// If Abs is true the absolute value of each color channel is taken after convolution. +	Abs bool + +	// Bias is added to each color channel value after convolution. +	Bias int +} + +// Convolve3x3 convolves the image with the specified 3x3 convolution kernel. +// Default parameters are used if a nil *ConvolveOptions is passed. +func Convolve3x3(img image.Image, kernel [9]float64, options *ConvolveOptions) *image.NRGBA { +	return convolve(img, kernel[:], options) +} + +// Convolve5x5 convolves the image with the specified 5x5 convolution kernel. +// Default parameters are used if a nil *ConvolveOptions is passed. +func Convolve5x5(img image.Image, kernel [25]float64, options *ConvolveOptions) *image.NRGBA { +	return convolve(img, kernel[:], options) +} + +func convolve(img image.Image, kernel []float64, options *ConvolveOptions) *image.NRGBA { +	src := toNRGBA(img) +	w := src.Bounds().Max.X +	h := src.Bounds().Max.Y +	dst := image.NewNRGBA(image.Rect(0, 0, w, h)) + +	if w < 1 || h < 1 { +		return dst +	} + +	if options == nil { +		options = &ConvolveOptions{} +	} + +	if options.Normalize { +		normalizeKernel(kernel) +	} + +	type coef struct { +		x, y int +		k    float64 +	} +	var coefs []coef +	var m int + +	switch len(kernel) { +	case 9: +		m = 1 +	case 25: +		m = 2 +	} + +	i := 0 +	for y := -m; y <= m; y++ { +		for x := -m; x <= m; x++ { +			if kernel[i] != 0 { +				coefs = append(coefs, coef{x: x, y: y, k: kernel[i]}) +			} +			i++ +		} +	} + +	parallel(0, h, func(ys <-chan int) { +		for y := range ys { +			for x := 0; x < w; x++ { +				var r, g, b float64 +				for _, c := range coefs { +					ix := x + c.x +					if ix < 0 { +						ix = 0 +					} else if ix >= w { +						ix = w - 1 +					} + +					iy := y + c.y +					if iy < 0 { +						iy = 0 +					} else if iy >= h { +						iy = h - 1 +					} + +					off := iy*src.Stride + ix*4 +					s := src.Pix[off : off+3 : off+3] +					r += float64(s[0]) * c.k +					g += float64(s[1]) * c.k +					b += float64(s[2]) * c.k +				} + +				if options.Abs { +					if r < 0 { +						r = -r +					} +					if g < 0 { +						g = -g +					} +					if b < 0 { +						b = -b +					} +				} + +				if options.Bias != 0 { +					r += float64(options.Bias) +					g += float64(options.Bias) +					b += float64(options.Bias) +				} + +				srcOff := y*src.Stride + x*4 +				dstOff := y*dst.Stride + x*4 +				d := dst.Pix[dstOff : dstOff+4 : dstOff+4] +				d[0] = clamp(r) +				d[1] = clamp(g) +				d[2] = clamp(b) +				d[3] = src.Pix[srcOff+3] +			} +		} +	}) + +	return dst +} + +func normalizeKernel(kernel []float64) { +	var sum, sumpos float64 +	for i := range kernel { +		sum += kernel[i] +		if kernel[i] > 0 { +			sumpos += kernel[i] +		} +	} +	if sum != 0 { +		for i := range kernel { +			kernel[i] /= sum +		} +	} else if sumpos != 0 { +		for i := range kernel { +			kernel[i] /= sumpos +		} +	} +} diff --git a/vendor/github.com/disintegration/imaging/doc.go b/vendor/github.com/disintegration/imaging/doc.go new file mode 100644 index 000000000..c98c91250 --- /dev/null +++ b/vendor/github.com/disintegration/imaging/doc.go @@ -0,0 +1,7 @@ +/* +Package imaging provides basic image processing functions (resize, rotate, crop, brightness/contrast adjustments, etc.). + +All the image processing functions provided by the package accept any image type that implements image.Image interface +as an input, and return a new image of *image.NRGBA type (32bit RGBA colors, non-premultiplied alpha). +*/ +package imaging diff --git a/vendor/github.com/disintegration/imaging/effects.go b/vendor/github.com/disintegration/imaging/effects.go new file mode 100644 index 000000000..47316b701 --- /dev/null +++ b/vendor/github.com/disintegration/imaging/effects.go @@ -0,0 +1,169 @@ +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. +// +// Example: +// +//	dstImage := imaging.Blur(srcImage, 3.5) +// +func Blur(img image.Image, sigma float64) *image.NRGBA { +	if sigma <= 0 { +		return Clone(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) +	} + +	return blurVertical(blurHorizontal(img, kernel), kernel) +} + +func blurHorizontal(img image.Image, kernel []float64) *image.NRGBA { +	src := newScanner(img) +	dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h)) +	radius := len(kernel) - 1 + +	parallel(0, src.h, func(ys <-chan int) { +		scanLine := make([]uint8, src.w*4) +		scanLineF := make([]float64, len(scanLine)) +		for y := range ys { +			src.scan(0, y, src.w, y+1, scanLine) +			for i, v := range scanLine { +				scanLineF[i] = float64(v) +			} +			for x := 0; x < src.w; x++ { +				min := x - radius +				if min < 0 { +					min = 0 +				} +				max := x + radius +				if max > src.w-1 { +					max = src.w - 1 +				} +				var r, g, b, a, wsum float64 +				for ix := min; ix <= max; ix++ { +					i := ix * 4 +					weight := kernel[absint(x-ix)] +					wsum += weight +					s := scanLineF[i : i+4 : i+4] +					wa := s[3] * weight +					r += s[0] * wa +					g += s[1] * wa +					b += s[2] * wa +					a += wa +				} +				if a != 0 { +					aInv := 1 / a +					j := y*dst.Stride + x*4 +					d := dst.Pix[j : j+4 : j+4] +					d[0] = clamp(r * aInv) +					d[1] = clamp(g * aInv) +					d[2] = clamp(b * aInv) +					d[3] = clamp(a / wsum) +				} +			} +		} +	}) + +	return dst +} + +func blurVertical(img image.Image, kernel []float64) *image.NRGBA { +	src := newScanner(img) +	dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h)) +	radius := len(kernel) - 1 + +	parallel(0, src.w, func(xs <-chan int) { +		scanLine := make([]uint8, src.h*4) +		scanLineF := make([]float64, len(scanLine)) +		for x := range xs { +			src.scan(x, 0, x+1, src.h, scanLine) +			for i, v := range scanLine { +				scanLineF[i] = float64(v) +			} +			for y := 0; y < src.h; y++ { +				min := y - radius +				if min < 0 { +					min = 0 +				} +				max := y + radius +				if max > src.h-1 { +					max = src.h - 1 +				} +				var r, g, b, a, wsum float64 +				for iy := min; iy <= max; iy++ { +					i := iy * 4 +					weight := kernel[absint(y-iy)] +					wsum += weight +					s := scanLineF[i : i+4 : i+4] +					wa := s[3] * weight +					r += s[0] * wa +					g += s[1] * wa +					b += s[2] * wa +					a += wa +				} +				if a != 0 { +					aInv := 1 / a +					j := y*dst.Stride + x*4 +					d := dst.Pix[j : j+4 : j+4] +					d[0] = clamp(r * aInv) +					d[1] = clamp(g * aInv) +					d[2] = clamp(b * aInv) +					d[3] = clamp(a / wsum) +				} +			} +		} +	}) + +	return dst +} + +// Sharpen produces a sharpened version of the image. +// Sigma parameter must be positive and indicates how much the image will be sharpened. +// +// Example: +// +//	dstImage := imaging.Sharpen(srcImage, 3.5) +// +func Sharpen(img image.Image, sigma float64) *image.NRGBA { +	if sigma <= 0 { +		return Clone(img) +	} + +	src := newScanner(img) +	dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h)) +	blurred := Blur(img, sigma) + +	parallel(0, src.h, func(ys <-chan int) { +		scanLine := make([]uint8, src.w*4) +		for y := range ys { +			src.scan(0, y, src.w, y+1, scanLine) +			j := y * dst.Stride +			for i := 0; i < src.w*4; i++ { +				val := int(scanLine[i])<<1 - int(blurred.Pix[j]) +				if val < 0 { +					val = 0 +				} else if val > 0xff { +					val = 0xff +				} +				dst.Pix[j] = uint8(val) +				j++ +			} +		} +	}) + +	return dst +} diff --git a/vendor/github.com/disintegration/imaging/histogram.go b/vendor/github.com/disintegration/imaging/histogram.go new file mode 100644 index 000000000..c547fe822 --- /dev/null +++ b/vendor/github.com/disintegration/imaging/histogram.go @@ -0,0 +1,52 @@ +package imaging + +import ( +	"image" +	"sync" +) + +// Histogram returns a normalized histogram of an image. +// +// Resulting histogram is represented as an array of 256 floats, where +// histogram[i] is a probability of a pixel being of a particular luminance i. +func Histogram(img image.Image) [256]float64 { +	var mu sync.Mutex +	var histogram [256]float64 +	var total float64 + +	src := newScanner(img) +	if src.w == 0 || src.h == 0 { +		return histogram +	} + +	parallel(0, src.h, func(ys <-chan int) { +		var tmpHistogram [256]float64 +		var tmpTotal float64 +		scanLine := make([]uint8, src.w*4) +		for y := range ys { +			src.scan(0, y, src.w, y+1, scanLine) +			i := 0 +			for x := 0; x < src.w; x++ { +				s := scanLine[i : i+3 : i+3] +				r := s[0] +				g := s[1] +				b := s[2] +				y := 0.299*float32(r) + 0.587*float32(g) + 0.114*float32(b) +				tmpHistogram[int(y+0.5)]++ +				tmpTotal++ +				i += 4 +			} +		} +		mu.Lock() +		for i := 0; i < 256; i++ { +			histogram[i] += tmpHistogram[i] +		} +		total += tmpTotal +		mu.Unlock() +	}) + +	for i := 0; i < 256; i++ { +		histogram[i] = histogram[i] / total +	} +	return histogram +} diff --git a/vendor/github.com/disintegration/imaging/io.go b/vendor/github.com/disintegration/imaging/io.go new file mode 100644 index 000000000..f6c6da86b --- /dev/null +++ b/vendor/github.com/disintegration/imaging/io.go @@ -0,0 +1,444 @@ +package imaging + +import ( +	"encoding/binary" +	"errors" +	"image" +	"image/draw" +	"image/gif" +	"image/jpeg" +	"image/png" +	"io" +	"io/ioutil" +	"os" +	"path/filepath" +	"strings" + +	"golang.org/x/image/bmp" +	"golang.org/x/image/tiff" +) + +type fileSystem interface { +	Create(string) (io.WriteCloser, error) +	Open(string) (io.ReadCloser, error) +} + +type localFS struct{} + +func (localFS) Create(name string) (io.WriteCloser, error) { return os.Create(name) } +func (localFS) Open(name string) (io.ReadCloser, error)    { return os.Open(name) } + +var fs fileSystem = localFS{} + +type decodeConfig struct { +	autoOrientation bool +} + +var defaultDecodeConfig = decodeConfig{ +	autoOrientation: false, +} + +// DecodeOption sets an optional parameter for the Decode and Open functions. +type DecodeOption func(*decodeConfig) + +// AutoOrientation returns a DecodeOption that sets the auto-orientation mode. +// If auto-orientation is enabled, the image will be transformed after decoding +// according to the EXIF orientation tag (if present). By default it's disabled. +func AutoOrientation(enabled bool) DecodeOption { +	return func(c *decodeConfig) { +		c.autoOrientation = enabled +	} +} + +// Decode reads an image from r. +func Decode(r io.Reader, opts ...DecodeOption) (image.Image, error) { +	cfg := defaultDecodeConfig +	for _, option := range opts { +		option(&cfg) +	} + +	if !cfg.autoOrientation { +		img, _, err := image.Decode(r) +		return img, err +	} + +	var orient orientation +	pr, pw := io.Pipe() +	r = io.TeeReader(r, pw) +	done := make(chan struct{}) +	go func() { +		defer close(done) +		orient = readOrientation(pr) +		io.Copy(ioutil.Discard, pr) +	}() + +	img, _, err := image.Decode(r) +	pw.Close() +	<-done +	if err != nil { +		return nil, err +	} + +	return fixOrientation(img, orient), nil +} + +// Open loads an image from file. +// +// Examples: +// +//	// Load an image from file. +//	img, err := imaging.Open("test.jpg") +// +//	// Load an image and transform it depending on the EXIF orientation tag (if present). +//	img, err := imaging.Open("test.jpg", imaging.AutoOrientation(true)) +// +func Open(filename string, opts ...DecodeOption) (image.Image, error) { +	file, err := fs.Open(filename) +	if err != nil { +		return nil, err +	} +	defer file.Close() +	return Decode(file, opts...) +} + +// Format is an image file format. +type Format int + +// Image file formats. +const ( +	JPEG Format = iota +	PNG +	GIF +	TIFF +	BMP +) + +var formatExts = map[string]Format{ +	"jpg":  JPEG, +	"jpeg": JPEG, +	"png":  PNG, +	"gif":  GIF, +	"tif":  TIFF, +	"tiff": TIFF, +	"bmp":  BMP, +} + +var formatNames = map[Format]string{ +	JPEG: "JPEG", +	PNG:  "PNG", +	GIF:  "GIF", +	TIFF: "TIFF", +	BMP:  "BMP", +} + +func (f Format) String() string { +	return formatNames[f] +} + +// ErrUnsupportedFormat means the given image format is not supported. +var ErrUnsupportedFormat = errors.New("imaging: unsupported image format") + +// FormatFromExtension parses image format from filename extension: +// "jpg" (or "jpeg"), "png", "gif", "tif" (or "tiff") and "bmp" are supported. +func FormatFromExtension(ext string) (Format, error) { +	if f, ok := formatExts[strings.ToLower(strings.TrimPrefix(ext, "."))]; ok { +		return f, nil +	} +	return -1, ErrUnsupportedFormat +} + +// FormatFromFilename parses image format from filename: +// "jpg" (or "jpeg"), "png", "gif", "tif" (or "tiff") and "bmp" are supported. +func FormatFromFilename(filename string) (Format, error) { +	ext := filepath.Ext(filename) +	return FormatFromExtension(ext) +} + +type encodeConfig struct { +	jpegQuality         int +	gifNumColors        int +	gifQuantizer        draw.Quantizer +	gifDrawer           draw.Drawer +	pngCompressionLevel png.CompressionLevel +} + +var defaultEncodeConfig = encodeConfig{ +	jpegQuality:         95, +	gifNumColors:        256, +	gifQuantizer:        nil, +	gifDrawer:           nil, +	pngCompressionLevel: png.DefaultCompression, +} + +// EncodeOption sets an optional parameter for the Encode and Save functions. +type EncodeOption func(*encodeConfig) + +// JPEGQuality returns an EncodeOption that sets the output JPEG quality. +// Quality ranges from 1 to 100 inclusive, higher is better. Default is 95. +func JPEGQuality(quality int) EncodeOption { +	return func(c *encodeConfig) { +		c.jpegQuality = quality +	} +} + +// GIFNumColors returns an EncodeOption that sets the maximum number of colors +// used in the GIF-encoded image. It ranges from 1 to 256.  Default is 256. +func GIFNumColors(numColors int) EncodeOption { +	return func(c *encodeConfig) { +		c.gifNumColors = numColors +	} +} + +// GIFQuantizer returns an EncodeOption that sets the quantizer that is used to produce +// a palette of the GIF-encoded image. +func GIFQuantizer(quantizer draw.Quantizer) EncodeOption { +	return func(c *encodeConfig) { +		c.gifQuantizer = quantizer +	} +} + +// GIFDrawer returns an EncodeOption that sets the drawer that is used to convert +// the source image to the desired palette of the GIF-encoded image. +func GIFDrawer(drawer draw.Drawer) EncodeOption { +	return func(c *encodeConfig) { +		c.gifDrawer = drawer +	} +} + +// PNGCompressionLevel returns an EncodeOption that sets the compression level +// of the PNG-encoded image. Default is png.DefaultCompression. +func PNGCompressionLevel(level png.CompressionLevel) EncodeOption { +	return func(c *encodeConfig) { +		c.pngCompressionLevel = level +	} +} + +// 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, opts ...EncodeOption) error { +	cfg := defaultEncodeConfig +	for _, option := range opts { +		option(&cfg) +	} + +	switch format { +	case JPEG: +		if nrgba, ok := img.(*image.NRGBA); ok && nrgba.Opaque() { +			rgba := &image.RGBA{ +				Pix:    nrgba.Pix, +				Stride: nrgba.Stride, +				Rect:   nrgba.Rect, +			} +			return jpeg.Encode(w, rgba, &jpeg.Options{Quality: cfg.jpegQuality}) +		} +		return jpeg.Encode(w, img, &jpeg.Options{Quality: cfg.jpegQuality}) + +	case PNG: +		encoder := png.Encoder{CompressionLevel: cfg.pngCompressionLevel} +		return encoder.Encode(w, img) + +	case GIF: +		return gif.Encode(w, img, &gif.Options{ +			NumColors: cfg.gifNumColors, +			Quantizer: cfg.gifQuantizer, +			Drawer:    cfg.gifDrawer, +		}) + +	case TIFF: +		return tiff.Encode(w, img, &tiff.Options{Compression: tiff.Deflate, Predictor: true}) + +	case BMP: +		return bmp.Encode(w, img) +	} + +	return ErrUnsupportedFormat +} + +// 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. +// +// Examples: +// +//	// Save the image as PNG. +//	err := imaging.Save(img, "out.png") +// +//	// Save the image as JPEG with optional quality parameter set to 80. +//	err := imaging.Save(img, "out.jpg", imaging.JPEGQuality(80)) +// +func Save(img image.Image, filename string, opts ...EncodeOption) (err error) { +	f, err := FormatFromFilename(filename) +	if err != nil { +		return err +	} +	file, err := fs.Create(filename) +	if err != nil { +		return err +	} +	err = Encode(file, img, f, opts...) +	errc := file.Close() +	if err == nil { +		err = errc +	} +	return err +} + +// orientation is an EXIF flag that specifies the transformation +// that should be applied to image to display it correctly. +type orientation int + +const ( +	orientationUnspecified = 0 +	orientationNormal      = 1 +	orientationFlipH       = 2 +	orientationRotate180   = 3 +	orientationFlipV       = 4 +	orientationTranspose   = 5 +	orientationRotate270   = 6 +	orientationTransverse  = 7 +	orientationRotate90    = 8 +) + +// readOrientation tries to read the orientation EXIF flag from image data in r. +// If the EXIF data block is not found or the orientation flag is not found +// or any other error occures while reading the data, it returns the +// orientationUnspecified (0) value. +func readOrientation(r io.Reader) orientation { +	const ( +		markerSOI      = 0xffd8 +		markerAPP1     = 0xffe1 +		exifHeader     = 0x45786966 +		byteOrderBE    = 0x4d4d +		byteOrderLE    = 0x4949 +		orientationTag = 0x0112 +	) + +	// Check if JPEG SOI marker is present. +	var soi uint16 +	if err := binary.Read(r, binary.BigEndian, &soi); err != nil { +		return orientationUnspecified +	} +	if soi != markerSOI { +		return orientationUnspecified // Missing JPEG SOI marker. +	} + +	// Find JPEG APP1 marker. +	for { +		var marker, size uint16 +		if err := binary.Read(r, binary.BigEndian, &marker); err != nil { +			return orientationUnspecified +		} +		if err := binary.Read(r, binary.BigEndian, &size); err != nil { +			return orientationUnspecified +		} +		if marker>>8 != 0xff { +			return orientationUnspecified // Invalid JPEG marker. +		} +		if marker == markerAPP1 { +			break +		} +		if size < 2 { +			return orientationUnspecified // Invalid block size. +		} +		if _, err := io.CopyN(ioutil.Discard, r, int64(size-2)); err != nil { +			return orientationUnspecified +		} +	} + +	// Check if EXIF header is present. +	var header uint32 +	if err := binary.Read(r, binary.BigEndian, &header); err != nil { +		return orientationUnspecified +	} +	if header != exifHeader { +		return orientationUnspecified +	} +	if _, err := io.CopyN(ioutil.Discard, r, 2); err != nil { +		return orientationUnspecified +	} + +	// Read byte order information. +	var ( +		byteOrderTag uint16 +		byteOrder    binary.ByteOrder +	) +	if err := binary.Read(r, binary.BigEndian, &byteOrderTag); err != nil { +		return orientationUnspecified +	} +	switch byteOrderTag { +	case byteOrderBE: +		byteOrder = binary.BigEndian +	case byteOrderLE: +		byteOrder = binary.LittleEndian +	default: +		return orientationUnspecified // Invalid byte order flag. +	} +	if _, err := io.CopyN(ioutil.Discard, r, 2); err != nil { +		return orientationUnspecified +	} + +	// Skip the EXIF offset. +	var offset uint32 +	if err := binary.Read(r, byteOrder, &offset); err != nil { +		return orientationUnspecified +	} +	if offset < 8 { +		return orientationUnspecified // Invalid offset value. +	} +	if _, err := io.CopyN(ioutil.Discard, r, int64(offset-8)); err != nil { +		return orientationUnspecified +	} + +	// Read the number of tags. +	var numTags uint16 +	if err := binary.Read(r, byteOrder, &numTags); err != nil { +		return orientationUnspecified +	} + +	// Find the orientation tag. +	for i := 0; i < int(numTags); i++ { +		var tag uint16 +		if err := binary.Read(r, byteOrder, &tag); err != nil { +			return orientationUnspecified +		} +		if tag != orientationTag { +			if _, err := io.CopyN(ioutil.Discard, r, 10); err != nil { +				return orientationUnspecified +			} +			continue +		} +		if _, err := io.CopyN(ioutil.Discard, r, 6); err != nil { +			return orientationUnspecified +		} +		var val uint16 +		if err := binary.Read(r, byteOrder, &val); err != nil { +			return orientationUnspecified +		} +		if val < 1 || val > 8 { +			return orientationUnspecified // Invalid tag value. +		} +		return orientation(val) +	} +	return orientationUnspecified // Missing orientation tag. +} + +// fixOrientation applies a transform to img corresponding to the given orientation flag. +func fixOrientation(img image.Image, o orientation) image.Image { +	switch o { +	case orientationNormal: +	case orientationFlipH: +		img = FlipH(img) +	case orientationFlipV: +		img = FlipV(img) +	case orientationRotate90: +		img = Rotate90(img) +	case orientationRotate180: +		img = Rotate180(img) +	case orientationRotate270: +		img = Rotate270(img) +	case orientationTranspose: +		img = Transpose(img) +	case orientationTransverse: +		img = Transverse(img) +	} +	return img +} diff --git a/vendor/github.com/disintegration/imaging/resize.go b/vendor/github.com/disintegration/imaging/resize.go new file mode 100644 index 000000000..706435e3d --- /dev/null +++ b/vendor/github.com/disintegration/imaging/resize.go @@ -0,0 +1,595 @@ +package imaging + +import ( +	"image" +	"math" +) + +type indexWeight struct { +	index  int +	weight float64 +} + +func precomputeWeights(dstSize, srcSize int, filter ResampleFilter) [][]indexWeight { +	du := float64(srcSize) / float64(dstSize) +	scale := du +	if scale < 1.0 { +		scale = 1.0 +	} +	ru := math.Ceil(scale * filter.Support) + +	out := make([][]indexWeight, dstSize) +	tmp := make([]indexWeight, 0, dstSize*int(ru+2)*2) + +	for v := 0; v < dstSize; v++ { +		fu := (float64(v)+0.5)*du - 0.5 + +		begin := int(math.Ceil(fu - ru)) +		if begin < 0 { +			begin = 0 +		} +		end := int(math.Floor(fu + ru)) +		if end > srcSize-1 { +			end = srcSize - 1 +		} + +		var sum float64 +		for u := begin; u <= end; u++ { +			w := filter.Kernel((float64(u) - fu) / scale) +			if w != 0 { +				sum += w +				tmp = append(tmp, indexWeight{index: u, weight: w}) +			} +		} +		if sum != 0 { +			for i := range tmp { +				tmp[i].weight /= sum +			} +		} + +		out[v] = tmp +		tmp = tmp[len(tmp):] +	} + +	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. +// +// 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{} +	} + +	srcW := img.Bounds().Dx() +	srcH := img.Bounds().Dy() +	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))) +	} + +	if filter.Support <= 0 { +		// Nearest-neighbor special case. +		return resizeNearest(img, dstW, dstH) +	} + +	if srcW != dstW && srcH != dstH { +		return resizeVertical(resizeHorizontal(img, dstW, filter), dstH, filter) +	} +	if srcW != dstW { +		return resizeHorizontal(img, dstW, filter) +	} +	if srcH != dstH { +		return resizeVertical(img, dstH, filter) +	} +	return Clone(img) +} + +func resizeHorizontal(img image.Image, width int, filter ResampleFilter) *image.NRGBA { +	src := newScanner(img) +	dst := image.NewNRGBA(image.Rect(0, 0, width, src.h)) +	weights := precomputeWeights(width, src.w, filter) +	parallel(0, src.h, func(ys <-chan int) { +		scanLine := make([]uint8, src.w*4) +		for y := range ys { +			src.scan(0, y, src.w, y+1, scanLine) +			j0 := y * dst.Stride +			for x := range weights { +				var r, g, b, a float64 +				for _, w := range weights[x] { +					i := w.index * 4 +					s := scanLine[i : i+4 : i+4] +					aw := float64(s[3]) * w.weight +					r += float64(s[0]) * aw +					g += float64(s[1]) * aw +					b += float64(s[2]) * aw +					a += aw +				} +				if a != 0 { +					aInv := 1 / a +					j := j0 + x*4 +					d := dst.Pix[j : j+4 : j+4] +					d[0] = clamp(r * aInv) +					d[1] = clamp(g * aInv) +					d[2] = clamp(b * aInv) +					d[3] = clamp(a) +				} +			} +		} +	}) +	return dst +} + +func resizeVertical(img image.Image, height int, filter ResampleFilter) *image.NRGBA { +	src := newScanner(img) +	dst := image.NewNRGBA(image.Rect(0, 0, src.w, height)) +	weights := precomputeWeights(height, src.h, filter) +	parallel(0, src.w, func(xs <-chan int) { +		scanLine := make([]uint8, src.h*4) +		for x := range xs { +			src.scan(x, 0, x+1, src.h, scanLine) +			for y := range weights { +				var r, g, b, a float64 +				for _, w := range weights[y] { +					i := w.index * 4 +					s := scanLine[i : i+4 : i+4] +					aw := float64(s[3]) * w.weight +					r += float64(s[0]) * aw +					g += float64(s[1]) * aw +					b += float64(s[2]) * aw +					a += aw +				} +				if a != 0 { +					aInv := 1 / a +					j := y*dst.Stride + x*4 +					d := dst.Pix[j : j+4 : j+4] +					d[0] = clamp(r * aInv) +					d[1] = clamp(g * aInv) +					d[2] = clamp(b * aInv) +					d[3] = clamp(a) +				} +			} +		} +	}) +	return dst +} + +// resizeNearest is a fast nearest-neighbor resize, no filtering. +func resizeNearest(img image.Image, width, height int) *image.NRGBA { +	dst := image.NewNRGBA(image.Rect(0, 0, width, height)) +	dx := float64(img.Bounds().Dx()) / float64(width) +	dy := float64(img.Bounds().Dy()) / float64(height) + +	if dx > 1 && dy > 1 { +		src := newScanner(img) +		parallel(0, height, func(ys <-chan int) { +			for y := range ys { +				srcY := int((float64(y) + 0.5) * dy) +				dstOff := y * dst.Stride +				for x := 0; x < width; x++ { +					srcX := int((float64(x) + 0.5) * dx) +					src.scan(srcX, srcY, srcX+1, srcY+1, dst.Pix[dstOff:dstOff+4]) +					dstOff += 4 +				} +			} +		}) +	} else { +		src := toNRGBA(img) +		parallel(0, height, func(ys <-chan int) { +			for y := range ys { +				srcY := int((float64(y) + 0.5) * dy) +				srcOff0 := srcY * src.Stride +				dstOff := y * dst.Stride +				for x := 0; x < width; x++ { +					srcX := int((float64(x) + 0.5) * dx) +					srcOff := srcOff0 + srcX*4 +					copy(dst.Pix[dstOff:dstOff+4], src.Pix[srcOff:srcOff+4]) +					dstOff += 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. +// +// 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) +} + +// Fill creates an image with the specified dimensions and fills it with the scaled source image. +// To achieve the correct aspect ratio without stretching, the source image will be cropped. +// +// Example: +// +//	dstImage := imaging.Fill(srcImage, 800, 600, imaging.Center, imaging.Lanczos) +// +func Fill(img image.Image, width, height int, anchor Anchor, filter ResampleFilter) *image.NRGBA { +	dstW, dstH := width, height + +	if dstW <= 0 || dstH <= 0 { +		return &image.NRGBA{} +	} + +	srcBounds := img.Bounds() +	srcW := srcBounds.Dx() +	srcH := srcBounds.Dy() + +	if srcW <= 0 || srcH <= 0 { +		return &image.NRGBA{} +	} + +	if srcW == dstW && srcH == dstH { +		return Clone(img) +	} + +	if srcW >= 100 && srcH >= 100 { +		return cropAndResize(img, dstW, dstH, anchor, filter) +	} +	return resizeAndCrop(img, dstW, dstH, anchor, filter) +} + +// cropAndResize crops the image to the smallest possible size that has the required aspect ratio using +// the given anchor point, then scales it to the specified dimensions and returns the transformed image. +// +// This is generally faster than resizing first, but may result in inaccuracies when used on small source images. +func cropAndResize(img image.Image, width, height int, anchor Anchor, filter ResampleFilter) *image.NRGBA { +	dstW, dstH := width, height + +	srcBounds := img.Bounds() +	srcW := srcBounds.Dx() +	srcH := srcBounds.Dy() +	srcAspectRatio := float64(srcW) / float64(srcH) +	dstAspectRatio := float64(dstW) / float64(dstH) + +	var tmp *image.NRGBA +	if srcAspectRatio < dstAspectRatio { +		cropH := float64(srcW) * float64(dstH) / float64(dstW) +		tmp = CropAnchor(img, srcW, int(math.Max(1, cropH)+0.5), anchor) +	} else { +		cropW := float64(srcH) * float64(dstW) / float64(dstH) +		tmp = CropAnchor(img, int(math.Max(1, cropW)+0.5), srcH, anchor) +	} + +	return Resize(tmp, dstW, dstH, filter) +} + +// resizeAndCrop resizes the image to the smallest possible size that will cover the specified dimensions, +// crops the resized image to the specified dimensions using the given anchor point and returns +// the transformed image. +func resizeAndCrop(img image.Image, width, height int, anchor Anchor, filter ResampleFilter) *image.NRGBA { +	dstW, dstH := width, height + +	srcBounds := img.Bounds() +	srcW := srcBounds.Dx() +	srcH := srcBounds.Dy() +	srcAspectRatio := float64(srcW) / float64(srcH) +	dstAspectRatio := float64(dstW) / float64(dstH) + +	var tmp *image.NRGBA +	if srcAspectRatio < dstAspectRatio { +		tmp = Resize(img, dstW, 0, filter) +	} else { +		tmp = Resize(img, 0, dstH, filter) +	} + +	return CropAnchor(tmp, dstW, dstH, anchor) +} + +// 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. +// +// Example: +// +//	dstImage := imaging.Thumbnail(srcImage, 100, 100, imaging.Lanczos) +// +func Thumbnail(img image.Image, width, height int, filter ResampleFilter) *image.NRGBA { +	return Fill(img, width, height, Center, filter) +} + +// ResampleFilter specifies a resampling filter to be used for image resizing. +// +//	General filter recommendations: +// +//	- Lanczos +//		A high-quality resampling filter for photographic images yielding sharp results. +// +//	- CatmullRom +//		A sharp cubic filter that is faster than Lanczos filter while providing similar results. +// +//	- MitchellNetravali +//		A cubic filter that produces smoother results with less ringing artifacts than CatmullRom. +// +//	- Linear +//		Bilinear resampling filter, produces a smooth output. Faster than cubic filters. +// +//	- Box +//		Simple and fast averaging filter appropriate for downscaling. +//		When upscaling it's similar to NearestNeighbor. +// +//	- NearestNeighbor +//		Fastest resampling filter, no antialiasing. +// +type ResampleFilter struct { +	Support float64 +	Kernel  func(float64) float64 +} + +// NearestNeighbor is a 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 + +// MitchellNetravali is Mitchell-Netravali cubic filter (BC-spline; B=1/3; C=1/3). +var MitchellNetravali ResampleFilter + +// CatmullRom is a Catmull-Rom - sharp cubic filter (BC-spline; B=0; C=0.5). +var CatmullRom ResampleFilter + +// BSpline is a smooth cubic filter (BC-spline; B=1; C=0). +var BSpline ResampleFilter + +// Gaussian is a Gaussian blurring filter. +var Gaussian ResampleFilter + +// Bartlett is a Bartlett-windowed sinc filter (3 lobes). +var Bartlett ResampleFilter + +// Lanczos filter (3 lobes). +var Lanczos ResampleFilter + +// Hann is a Hann-windowed sinc filter (3 lobes). +var Hann ResampleFilter + +// Hamming is a Hamming-windowed sinc filter (3 lobes). +var Hamming ResampleFilter + +// Blackman is a Blackman-windowed sinc filter (3 lobes). +var Blackman ResampleFilter + +// Welch is a Welch-windowed sinc filter (parabolic window, 3 lobes). +var Welch ResampleFilter + +// Cosine is a Cosine-windowed sinc filter (3 lobes). +var Cosine ResampleFilter + +func bcspline(x, b, c float64) float64 { +	var y float64 +	x = math.Abs(x) +	if x < 1.0 { +		y = ((12-9*b-6*c)*x*x*x + (-18+12*b+6*c)*x*x + (6 - 2*b)) / 6 +	} else if x < 2.0 { +		y = ((-b-6*c)*x*x*x + (6*b+30*c)*x*x + (-12*b-48*c)*x + (8*b + 24*c)) / 6 +	} +	return y +} + +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/vendor/github.com/disintegration/imaging/scanner.go b/vendor/github.com/disintegration/imaging/scanner.go new file mode 100644 index 000000000..37d92cef8 --- /dev/null +++ b/vendor/github.com/disintegration/imaging/scanner.go @@ -0,0 +1,285 @@ +package imaging + +import ( +	"image" +	"image/color" +) + +type scanner struct { +	image   image.Image +	w, h    int +	palette []color.NRGBA +} + +func newScanner(img image.Image) *scanner { +	s := &scanner{ +		image: img, +		w:     img.Bounds().Dx(), +		h:     img.Bounds().Dy(), +	} +	if img, ok := img.(*image.Paletted); ok { +		s.palette = make([]color.NRGBA, len(img.Palette)) +		for i := 0; i < len(img.Palette); i++ { +			s.palette[i] = color.NRGBAModel.Convert(img.Palette[i]).(color.NRGBA) +		} +	} +	return s +} + +// scan scans the given rectangular region of the image into dst. +func (s *scanner) scan(x1, y1, x2, y2 int, dst []uint8) { +	switch img := s.image.(type) { +	case *image.NRGBA: +		size := (x2 - x1) * 4 +		j := 0 +		i := y1*img.Stride + x1*4 +		if size == 4 { +			for y := y1; y < y2; y++ { +				d := dst[j : j+4 : j+4] +				s := img.Pix[i : i+4 : i+4] +				d[0] = s[0] +				d[1] = s[1] +				d[2] = s[2] +				d[3] = s[3] +				j += size +				i += img.Stride +			} +		} else { +			for y := y1; y < y2; y++ { +				copy(dst[j:j+size], img.Pix[i:i+size]) +				j += size +				i += img.Stride +			} +		} + +	case *image.NRGBA64: +		j := 0 +		for y := y1; y < y2; y++ { +			i := y*img.Stride + x1*8 +			for x := x1; x < x2; x++ { +				s := img.Pix[i : i+8 : i+8] +				d := dst[j : j+4 : j+4] +				d[0] = s[0] +				d[1] = s[2] +				d[2] = s[4] +				d[3] = s[6] +				j += 4 +				i += 8 +			} +		} + +	case *image.RGBA: +		j := 0 +		for y := y1; y < y2; y++ { +			i := y*img.Stride + x1*4 +			for x := x1; x < x2; x++ { +				d := dst[j : j+4 : j+4] +				a := img.Pix[i+3] +				switch a { +				case 0: +					d[0] = 0 +					d[1] = 0 +					d[2] = 0 +					d[3] = a +				case 0xff: +					s := img.Pix[i : i+4 : i+4] +					d[0] = s[0] +					d[1] = s[1] +					d[2] = s[2] +					d[3] = a +				default: +					s := img.Pix[i : i+4 : i+4] +					r16 := uint16(s[0]) +					g16 := uint16(s[1]) +					b16 := uint16(s[2]) +					a16 := uint16(a) +					d[0] = uint8(r16 * 0xff / a16) +					d[1] = uint8(g16 * 0xff / a16) +					d[2] = uint8(b16 * 0xff / a16) +					d[3] = a +				} +				j += 4 +				i += 4 +			} +		} + +	case *image.RGBA64: +		j := 0 +		for y := y1; y < y2; y++ { +			i := y*img.Stride + x1*8 +			for x := x1; x < x2; x++ { +				s := img.Pix[i : i+8 : i+8] +				d := dst[j : j+4 : j+4] +				a := s[6] +				switch a { +				case 0: +					d[0] = 0 +					d[1] = 0 +					d[2] = 0 +				case 0xff: +					d[0] = s[0] +					d[1] = s[2] +					d[2] = s[4] +				default: +					r32 := uint32(s[0])<<8 | uint32(s[1]) +					g32 := uint32(s[2])<<8 | uint32(s[3]) +					b32 := uint32(s[4])<<8 | uint32(s[5]) +					a32 := uint32(s[6])<<8 | uint32(s[7]) +					d[0] = uint8((r32 * 0xffff / a32) >> 8) +					d[1] = uint8((g32 * 0xffff / a32) >> 8) +					d[2] = uint8((b32 * 0xffff / a32) >> 8) +				} +				d[3] = a +				j += 4 +				i += 8 +			} +		} + +	case *image.Gray: +		j := 0 +		for y := y1; y < y2; y++ { +			i := y*img.Stride + x1 +			for x := x1; x < x2; x++ { +				c := img.Pix[i] +				d := dst[j : j+4 : j+4] +				d[0] = c +				d[1] = c +				d[2] = c +				d[3] = 0xff +				j += 4 +				i++ +			} +		} + +	case *image.Gray16: +		j := 0 +		for y := y1; y < y2; y++ { +			i := y*img.Stride + x1*2 +			for x := x1; x < x2; x++ { +				c := img.Pix[i] +				d := dst[j : j+4 : j+4] +				d[0] = c +				d[1] = c +				d[2] = c +				d[3] = 0xff +				j += 4 +				i += 2 +			} +		} + +	case *image.YCbCr: +		j := 0 +		x1 += img.Rect.Min.X +		x2 += img.Rect.Min.X +		y1 += img.Rect.Min.Y +		y2 += img.Rect.Min.Y + +		hy := img.Rect.Min.Y / 2 +		hx := img.Rect.Min.X / 2 +		for y := y1; y < y2; y++ { +			iy := (y-img.Rect.Min.Y)*img.YStride + (x1 - img.Rect.Min.X) + +			var yBase int +			switch img.SubsampleRatio { +			case image.YCbCrSubsampleRatio444, image.YCbCrSubsampleRatio422: +				yBase = (y - img.Rect.Min.Y) * img.CStride +			case image.YCbCrSubsampleRatio420, image.YCbCrSubsampleRatio440: +				yBase = (y/2 - hy) * img.CStride +			} + +			for x := x1; x < x2; x++ { +				var ic int +				switch img.SubsampleRatio { +				case image.YCbCrSubsampleRatio444, image.YCbCrSubsampleRatio440: +					ic = yBase + (x - img.Rect.Min.X) +				case image.YCbCrSubsampleRatio422, image.YCbCrSubsampleRatio420: +					ic = yBase + (x/2 - hx) +				default: +					ic = img.COffset(x, y) +				} + +				yy1 := int32(img.Y[iy]) * 0x10101 +				cb1 := int32(img.Cb[ic]) - 128 +				cr1 := int32(img.Cr[ic]) - 128 + +				r := yy1 + 91881*cr1 +				if uint32(r)&0xff000000 == 0 { +					r >>= 16 +				} else { +					r = ^(r >> 31) +				} + +				g := yy1 - 22554*cb1 - 46802*cr1 +				if uint32(g)&0xff000000 == 0 { +					g >>= 16 +				} else { +					g = ^(g >> 31) +				} + +				b := yy1 + 116130*cb1 +				if uint32(b)&0xff000000 == 0 { +					b >>= 16 +				} else { +					b = ^(b >> 31) +				} + +				d := dst[j : j+4 : j+4] +				d[0] = uint8(r) +				d[1] = uint8(g) +				d[2] = uint8(b) +				d[3] = 0xff + +				iy++ +				j += 4 +			} +		} + +	case *image.Paletted: +		j := 0 +		for y := y1; y < y2; y++ { +			i := y*img.Stride + x1 +			for x := x1; x < x2; x++ { +				c := s.palette[img.Pix[i]] +				d := dst[j : j+4 : j+4] +				d[0] = c.R +				d[1] = c.G +				d[2] = c.B +				d[3] = c.A +				j += 4 +				i++ +			} +		} + +	default: +		j := 0 +		b := s.image.Bounds() +		x1 += b.Min.X +		x2 += b.Min.X +		y1 += b.Min.Y +		y2 += b.Min.Y +		for y := y1; y < y2; y++ { +			for x := x1; x < x2; x++ { +				r16, g16, b16, a16 := s.image.At(x, y).RGBA() +				d := dst[j : j+4 : j+4] +				switch a16 { +				case 0xffff: +					d[0] = uint8(r16 >> 8) +					d[1] = uint8(g16 >> 8) +					d[2] = uint8(b16 >> 8) +					d[3] = 0xff +				case 0: +					d[0] = 0 +					d[1] = 0 +					d[2] = 0 +					d[3] = 0 +				default: +					d[0] = uint8(((r16 * 0xffff) / a16) >> 8) +					d[1] = uint8(((g16 * 0xffff) / a16) >> 8) +					d[2] = uint8(((b16 * 0xffff) / a16) >> 8) +					d[3] = uint8(a16 >> 8) +				} +				j += 4 +			} +		} +	} +} diff --git a/vendor/github.com/disintegration/imaging/tools.go b/vendor/github.com/disintegration/imaging/tools.go new file mode 100644 index 000000000..0ec19a039 --- /dev/null +++ b/vendor/github.com/disintegration/imaging/tools.go @@ -0,0 +1,249 @@ +package imaging + +import ( +	"bytes" +	"image" +	"image/color" +	"math" +) + +// 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{} +	} + +	c := color.NRGBAModel.Convert(fillColor).(color.NRGBA) +	if (c == color.NRGBA{0, 0, 0, 0}) { +		return image.NewNRGBA(image.Rect(0, 0, width, height)) +	} + +	return &image.NRGBA{ +		Pix:    bytes.Repeat([]byte{c.R, c.G, c.B, c.A}, width*height), +		Stride: 4 * width, +		Rect:   image.Rect(0, 0, width, height), +	} +} + +// Clone returns a copy of the given image. +func Clone(img image.Image) *image.NRGBA { +	src := newScanner(img) +	dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h)) +	size := src.w * 4 +	parallel(0, src.h, func(ys <-chan int) { +		for y := range ys { +			i := y * dst.Stride +			src.scan(0, y, src.w, y+1, dst.Pix[i:i+size]) +		} +	}) +	return dst +} + +// Anchor is the anchor point for image alignment. +type Anchor int + +// Anchor point positions. +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 { +	r := rect.Intersect(img.Bounds()).Sub(img.Bounds().Min) +	if r.Empty() { +		return &image.NRGBA{} +	} +	src := newScanner(img) +	dst := image.NewNRGBA(image.Rect(0, 0, r.Dx(), r.Dy())) +	rowSize := r.Dx() * 4 +	parallel(r.Min.Y, r.Max.Y, func(ys <-chan int) { +		for y := range ys { +			i := (y - r.Min.Y) * dst.Stride +			src.scan(r.Min.X, y, r.Max.X, y+1, dst.Pix[i:i+rowSize]) +		} +	}) +	return dst +} + +// 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 { +	dst := Clone(background) +	pos = pos.Sub(background.Bounds().Min) +	pasteRect := image.Rectangle{Min: pos, Max: pos.Add(img.Bounds().Size())} +	interRect := pasteRect.Intersect(dst.Bounds()) +	if interRect.Empty() { +		return dst +	} +	src := newScanner(img) +	parallel(interRect.Min.Y, interRect.Max.Y, func(ys <-chan int) { +		for y := range ys { +			x1 := interRect.Min.X - pasteRect.Min.X +			x2 := interRect.Max.X - pasteRect.Min.X +			y1 := y - pasteRect.Min.Y +			y2 := y1 + 1 +			i1 := y*dst.Stride + interRect.Min.X*4 +			i2 := i1 + interRect.Dx()*4 +			src.scan(x1, y1, x2, y2, dst.Pix[i1:i2]) +		} +	}) +	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. +// +// Examples: +// +//	// Draw spriteImage over backgroundImage at the given position (x=50, y=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) // Ensure 0.0 <= opacity <= 1.0. +	dst := Clone(background) +	pos = pos.Sub(background.Bounds().Min) +	pasteRect := image.Rectangle{Min: pos, Max: pos.Add(img.Bounds().Size())} +	interRect := pasteRect.Intersect(dst.Bounds()) +	if interRect.Empty() { +		return dst +	} +	src := newScanner(img) +	parallel(interRect.Min.Y, interRect.Max.Y, func(ys <-chan int) { +		scanLine := make([]uint8, interRect.Dx()*4) +		for y := range ys { +			x1 := interRect.Min.X - pasteRect.Min.X +			x2 := interRect.Max.X - pasteRect.Min.X +			y1 := y - pasteRect.Min.Y +			y2 := y1 + 1 +			src.scan(x1, y1, x2, y2, scanLine) +			i := y*dst.Stride + interRect.Min.X*4 +			j := 0 +			for x := interRect.Min.X; x < interRect.Max.X; x++ { +				d := dst.Pix[i : i+4 : i+4] +				r1 := float64(d[0]) +				g1 := float64(d[1]) +				b1 := float64(d[2]) +				a1 := float64(d[3]) + +				s := scanLine[j : j+4 : j+4] +				r2 := float64(s[0]) +				g2 := float64(s[1]) +				b2 := float64(s[2]) +				a2 := float64(s[3]) + +				coef2 := opacity * a2 / 255 +				coef1 := (1 - coef2) * a1 / 255 +				coefSum := coef1 + coef2 +				coef1 /= coefSum +				coef2 /= coefSum + +				d[0] = uint8(r1*coef1 + r2*coef2) +				d[1] = uint8(g1*coef1 + g2*coef2) +				d[2] = uint8(b1*coef1 + b2*coef2) +				d[3] = uint8(math.Min(a1+a2*opacity*(255-a1)/255, 255)) + +				i += 4 +				j += 4 +			} +		} +	}) +	return dst +} + +// OverlayCenter overlays the img image to the center of the background image 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. +func OverlayCenter(background, img image.Image, opacity float64) *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 Overlay(background, img, image.Point{x0, y0}, opacity) +} diff --git a/vendor/github.com/disintegration/imaging/transform.go b/vendor/github.com/disintegration/imaging/transform.go new file mode 100644 index 000000000..fe4a92f9d --- /dev/null +++ b/vendor/github.com/disintegration/imaging/transform.go @@ -0,0 +1,268 @@ +package imaging + +import ( +	"image" +	"image/color" +	"math" +) + +// FlipH flips the image horizontally (from left to right) and returns the transformed image. +func FlipH(img image.Image) *image.NRGBA { +	src := newScanner(img) +	dstW := src.w +	dstH := src.h +	rowSize := dstW * 4 +	dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH)) +	parallel(0, dstH, func(ys <-chan int) { +		for dstY := range ys { +			i := dstY * dst.Stride +			srcY := dstY +			src.scan(0, srcY, src.w, srcY+1, dst.Pix[i:i+rowSize]) +			reverse(dst.Pix[i : i+rowSize]) +		} +	}) +	return dst +} + +// FlipV flips the image vertically (from top to bottom) and returns the transformed image. +func FlipV(img image.Image) *image.NRGBA { +	src := newScanner(img) +	dstW := src.w +	dstH := src.h +	rowSize := dstW * 4 +	dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH)) +	parallel(0, dstH, func(ys <-chan int) { +		for dstY := range ys { +			i := dstY * dst.Stride +			srcY := dstH - dstY - 1 +			src.scan(0, srcY, src.w, srcY+1, dst.Pix[i:i+rowSize]) +		} +	}) +	return dst +} + +// Transpose flips the image horizontally and rotates 90 degrees counter-clockwise. +func Transpose(img image.Image) *image.NRGBA { +	src := newScanner(img) +	dstW := src.h +	dstH := src.w +	rowSize := dstW * 4 +	dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH)) +	parallel(0, dstH, func(ys <-chan int) { +		for dstY := range ys { +			i := dstY * dst.Stride +			srcX := dstY +			src.scan(srcX, 0, srcX+1, src.h, dst.Pix[i:i+rowSize]) +		} +	}) +	return dst +} + +// Transverse flips the image vertically and rotates 90 degrees counter-clockwise. +func Transverse(img image.Image) *image.NRGBA { +	src := newScanner(img) +	dstW := src.h +	dstH := src.w +	rowSize := dstW * 4 +	dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH)) +	parallel(0, dstH, func(ys <-chan int) { +		for dstY := range ys { +			i := dstY * dst.Stride +			srcX := dstH - dstY - 1 +			src.scan(srcX, 0, srcX+1, src.h, dst.Pix[i:i+rowSize]) +			reverse(dst.Pix[i : i+rowSize]) +		} +	}) +	return dst +} + +// Rotate90 rotates the image 90 degrees counter-clockwise and returns the transformed image. +func Rotate90(img image.Image) *image.NRGBA { +	src := newScanner(img) +	dstW := src.h +	dstH := src.w +	rowSize := dstW * 4 +	dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH)) +	parallel(0, dstH, func(ys <-chan int) { +		for dstY := range ys { +			i := dstY * dst.Stride +			srcX := dstH - dstY - 1 +			src.scan(srcX, 0, srcX+1, src.h, dst.Pix[i:i+rowSize]) +		} +	}) +	return dst +} + +// Rotate180 rotates the image 180 degrees counter-clockwise and returns the transformed image. +func Rotate180(img image.Image) *image.NRGBA { +	src := newScanner(img) +	dstW := src.w +	dstH := src.h +	rowSize := dstW * 4 +	dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH)) +	parallel(0, dstH, func(ys <-chan int) { +		for dstY := range ys { +			i := dstY * dst.Stride +			srcY := dstH - dstY - 1 +			src.scan(0, srcY, src.w, srcY+1, dst.Pix[i:i+rowSize]) +			reverse(dst.Pix[i : i+rowSize]) +		} +	}) +	return dst +} + +// Rotate270 rotates the image 270 degrees counter-clockwise and returns the transformed image. +func Rotate270(img image.Image) *image.NRGBA { +	src := newScanner(img) +	dstW := src.h +	dstH := src.w +	rowSize := dstW * 4 +	dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH)) +	parallel(0, dstH, func(ys <-chan int) { +		for dstY := range ys { +			i := dstY * dst.Stride +			srcX := dstY +			src.scan(srcX, 0, srcX+1, src.h, dst.Pix[i:i+rowSize]) +			reverse(dst.Pix[i : i+rowSize]) +		} +	}) +	return dst +} + +// Rotate rotates an image by the given angle counter-clockwise . +// The angle parameter is the rotation angle in degrees. +// The bgColor parameter specifies the color of the uncovered zone after the rotation. +func Rotate(img image.Image, angle float64, bgColor color.Color) *image.NRGBA { +	angle = angle - math.Floor(angle/360)*360 + +	switch angle { +	case 0: +		return Clone(img) +	case 90: +		return Rotate90(img) +	case 180: +		return Rotate180(img) +	case 270: +		return Rotate270(img) +	} + +	src := toNRGBA(img) +	srcW := src.Bounds().Max.X +	srcH := src.Bounds().Max.Y +	dstW, dstH := rotatedSize(srcW, srcH, angle) +	dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH)) + +	if dstW <= 0 || dstH <= 0 { +		return dst +	} + +	srcXOff := float64(srcW)/2 - 0.5 +	srcYOff := float64(srcH)/2 - 0.5 +	dstXOff := float64(dstW)/2 - 0.5 +	dstYOff := float64(dstH)/2 - 0.5 + +	bgColorNRGBA := color.NRGBAModel.Convert(bgColor).(color.NRGBA) +	sin, cos := math.Sincos(math.Pi * angle / 180) + +	parallel(0, dstH, func(ys <-chan int) { +		for dstY := range ys { +			for dstX := 0; dstX < dstW; dstX++ { +				xf, yf := rotatePoint(float64(dstX)-dstXOff, float64(dstY)-dstYOff, sin, cos) +				xf, yf = xf+srcXOff, yf+srcYOff +				interpolatePoint(dst, dstX, dstY, src, xf, yf, bgColorNRGBA) +			} +		} +	}) + +	return dst +} + +func rotatePoint(x, y, sin, cos float64) (float64, float64) { +	return x*cos - y*sin, x*sin + y*cos +} + +func rotatedSize(w, h int, angle float64) (int, int) { +	if w <= 0 || h <= 0 { +		return 0, 0 +	} + +	sin, cos := math.Sincos(math.Pi * angle / 180) +	x1, y1 := rotatePoint(float64(w-1), 0, sin, cos) +	x2, y2 := rotatePoint(float64(w-1), float64(h-1), sin, cos) +	x3, y3 := rotatePoint(0, float64(h-1), sin, cos) + +	minx := math.Min(x1, math.Min(x2, math.Min(x3, 0))) +	maxx := math.Max(x1, math.Max(x2, math.Max(x3, 0))) +	miny := math.Min(y1, math.Min(y2, math.Min(y3, 0))) +	maxy := math.Max(y1, math.Max(y2, math.Max(y3, 0))) + +	neww := maxx - minx + 1 +	if neww-math.Floor(neww) > 0.1 { +		neww++ +	} +	newh := maxy - miny + 1 +	if newh-math.Floor(newh) > 0.1 { +		newh++ +	} + +	return int(neww), int(newh) +} + +func interpolatePoint(dst *image.NRGBA, dstX, dstY int, src *image.NRGBA, xf, yf float64, bgColor color.NRGBA) { +	j := dstY*dst.Stride + dstX*4 +	d := dst.Pix[j : j+4 : j+4] + +	x0 := int(math.Floor(xf)) +	y0 := int(math.Floor(yf)) +	bounds := src.Bounds() +	if !image.Pt(x0, y0).In(image.Rect(bounds.Min.X-1, bounds.Min.Y-1, bounds.Max.X, bounds.Max.Y)) { +		d[0] = bgColor.R +		d[1] = bgColor.G +		d[2] = bgColor.B +		d[3] = bgColor.A +		return +	} + +	xq := xf - float64(x0) +	yq := yf - float64(y0) +	points := [4]image.Point{ +		{x0, y0}, +		{x0 + 1, y0}, +		{x0, y0 + 1}, +		{x0 + 1, y0 + 1}, +	} +	weights := [4]float64{ +		(1 - xq) * (1 - yq), +		xq * (1 - yq), +		(1 - xq) * yq, +		xq * yq, +	} + +	var r, g, b, a float64 +	for i := 0; i < 4; i++ { +		p := points[i] +		w := weights[i] +		if p.In(bounds) { +			i := p.Y*src.Stride + p.X*4 +			s := src.Pix[i : i+4 : i+4] +			wa := float64(s[3]) * w +			r += float64(s[0]) * wa +			g += float64(s[1]) * wa +			b += float64(s[2]) * wa +			a += wa +		} else { +			wa := float64(bgColor.A) * w +			r += float64(bgColor.R) * wa +			g += float64(bgColor.G) * wa +			b += float64(bgColor.B) * wa +			a += wa +		} +	} +	if a != 0 { +		aInv := 1 / a +		d[0] = clamp(r * aInv) +		d[1] = clamp(g * aInv) +		d[2] = clamp(b * aInv) +		d[3] = clamp(a) +	} +} diff --git a/vendor/github.com/disintegration/imaging/utils.go b/vendor/github.com/disintegration/imaging/utils.go new file mode 100644 index 000000000..6c7af1a51 --- /dev/null +++ b/vendor/github.com/disintegration/imaging/utils.go @@ -0,0 +1,167 @@ +package imaging + +import ( +	"image" +	"math" +	"runtime" +	"sync" +) + +// parallel processes the data in separate goroutines. +func parallel(start, stop int, fn func(<-chan int)) { +	count := stop - start +	if count < 1 { +		return +	} + +	procs := runtime.GOMAXPROCS(0) +	if procs > count { +		procs = count +	} + +	c := make(chan int, count) +	for i := start; i < stop; i++ { +		c <- i +	} +	close(c) + +	var wg sync.WaitGroup +	for i := 0; i < procs; i++ { +		wg.Add(1) +		go func() { +			defer wg.Done() +			fn(c) +		}() +	} +	wg.Wait() +} + +// absint returns the absolute value of i. +func absint(i int) int { +	if i < 0 { +		return -i +	} +	return i +} + +// clamp rounds and clamps float64 value to fit into uint8. +func clamp(x float64) uint8 { +	v := int64(x + 0.5) +	if v > 255 { +		return 255 +	} +	if v > 0 { +		return uint8(v) +	} +	return 0 +} + +func reverse(pix []uint8) { +	if len(pix) <= 4 { +		return +	} +	i := 0 +	j := len(pix) - 4 +	for i < j { +		pi := pix[i : i+4 : i+4] +		pj := pix[j : j+4 : j+4] +		pi[0], pj[0] = pj[0], pi[0] +		pi[1], pj[1] = pj[1], pi[1] +		pi[2], pj[2] = pj[2], pi[2] +		pi[3], pj[3] = pj[3], pi[3] +		i += 4 +		j -= 4 +	} +} + +func toNRGBA(img image.Image) *image.NRGBA { +	if img, ok := img.(*image.NRGBA); ok { +		return &image.NRGBA{ +			Pix:    img.Pix, +			Stride: img.Stride, +			Rect:   img.Rect.Sub(img.Rect.Min), +		} +	} +	return Clone(img) +} + +// rgbToHSL converts a color from RGB to HSL. +func rgbToHSL(r, g, b uint8) (float64, float64, float64) { +	rr := float64(r) / 255 +	gg := float64(g) / 255 +	bb := float64(b) / 255 + +	max := math.Max(rr, math.Max(gg, bb)) +	min := math.Min(rr, math.Min(gg, bb)) + +	l := (max + min) / 2 + +	if max == min { +		return 0, 0, l +	} + +	var h, s float64 +	d := max - min +	if l > 0.5 { +		s = d / (2 - max - min) +	} else { +		s = d / (max + min) +	} + +	switch max { +	case rr: +		h = (gg - bb) / d +		if g < b { +			h += 6 +		} +	case gg: +		h = (bb-rr)/d + 2 +	case bb: +		h = (rr-gg)/d + 4 +	} +	h /= 6 + +	return h, s, l +} + +// hslToRGB converts a color from HSL to RGB. +func hslToRGB(h, s, l float64) (uint8, uint8, uint8) { +	var r, g, b float64 +	if s == 0 { +		v := clamp(l * 255) +		return v, v, v +	} + +	var q float64 +	if l < 0.5 { +		q = l * (1 + s) +	} else { +		q = l + s - l*s +	} +	p := 2*l - q + +	r = hueToRGB(p, q, h+1/3.0) +	g = hueToRGB(p, q, h) +	b = hueToRGB(p, q, h-1/3.0) + +	return clamp(r * 255), clamp(g * 255), clamp(b * 255) +} + +func hueToRGB(p, q, t float64) float64 { +	if t < 0 { +		t++ +	} +	if t > 1 { +		t-- +	} +	if t < 1/6.0 { +		return p + (q-p)*6*t +	} +	if t < 1/2.0 { +		return q +	} +	if t < 2/3.0 { +		return p + (q-p)*(2/3.0-t)*6 +	} +	return p +} diff --git a/vendor/github.com/nfnt/resize/.travis.yml b/vendor/github.com/nfnt/resize/.travis.yml deleted file mode 100644 index 5ff08e7e4..000000000 --- a/vendor/github.com/nfnt/resize/.travis.yml +++ /dev/null @@ -1,7 +0,0 @@ -language: go - -go: -  - "1.x" -  - "1.1" -  - "1.4" -  - "1.10" diff --git a/vendor/github.com/nfnt/resize/LICENSE b/vendor/github.com/nfnt/resize/LICENSE deleted file mode 100644 index 7836cad5f..000000000 --- a/vendor/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/vendor/github.com/nfnt/resize/README.md b/vendor/github.com/nfnt/resize/README.md deleted file mode 100644 index 372777d2e..000000000 --- a/vendor/github.com/nfnt/resize/README.md +++ /dev/null @@ -1,151 +0,0 @@ -# This package is no longer being updated! Please look for alternatives if that bothers you. - -Resize -====== - -Image resizing for the [Go programming language](http://golang.org) with common interpolation methods. - -[](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 - - -<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   - - -<table> -<tr> -<th><img src="http://nfnt.github.com/img/IMG_3694_300_NearestNeighbor.png" /><br>Nearest-Neighbor</th> -<th><img src="http://nfnt.github.com/img/IMG_3694_300_Bilinear.png" /><br>Bilinear</th> -</tr> -<tr> -<th><img src="http://nfnt.github.com/img/IMG_3694_300_Bicubic.png" /><br>Bicubic</th> -<th><img src="http://nfnt.github.com/img/IMG_3694_300_MitchellNetravali.png" /><br>Mitchell-Netravali</th> -</tr> -<tr> -<th><img src="http://nfnt.github.com/img/IMG_3694_300_Lanczos2.png" /><br>Lanczos2</th> -<th><img src="http://nfnt.github.com/img/IMG_3694_300_Lanczos3.png" /><br>Lanczos3</th> -</tr> -</table> - - -License -------- - -Copyright (c) 2012 Jan Schlicht <janschlicht@gmail.com> -Resize is released under a MIT style license. diff --git a/vendor/github.com/nfnt/resize/converter.go b/vendor/github.com/nfnt/resize/converter.go deleted file mode 100644 index f9c520d09..000000000 --- a/vendor/github.com/nfnt/resize/converter.go +++ /dev/null @@ -1,438 +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.RGBA64, scale float64, coeffs []int32, offset []int, filterLength int) { -	newBounds := out.Bounds() -	maxX := in.Bounds().Dx() - 1 - -	for x := newBounds.Min.X; x < newBounds.Max.X; x++ { -		for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ { -			var rgba [4]int64 -			var sum int64 -			start := offset[y] -			ci := y * filterLength -			for i := 0; i < filterLength; i++ { -				coeff := coeffs[ci+i] -				if coeff != 0 { -					xi := start + i -					switch { -					case xi < 0: -						xi = 0 -					case xi >= maxX: -						xi = maxX -					} - -					r, g, b, a := in.At(xi+in.Bounds().Min.X, x+in.Bounds().Min.Y).RGBA() - -					rgba[0] += int64(coeff) * int64(r) -					rgba[1] += int64(coeff) * int64(g) -					rgba[2] += int64(coeff) * int64(b) -					rgba[3] += int64(coeff) * int64(a) -					sum += int64(coeff) -				} -			} - -			offset := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*8 - -			value := clampUint16(rgba[0] / sum) -			out.Pix[offset+0] = uint8(value >> 8) -			out.Pix[offset+1] = uint8(value) -			value = clampUint16(rgba[1] / sum) -			out.Pix[offset+2] = uint8(value >> 8) -			out.Pix[offset+3] = uint8(value) -			value = clampUint16(rgba[2] / sum) -			out.Pix[offset+4] = uint8(value >> 8) -			out.Pix[offset+5] = uint8(value) -			value = clampUint16(rgba[3] / sum) -			out.Pix[offset+6] = uint8(value >> 8) -			out.Pix[offset+7] = uint8(value) -		} -	} -} - -func resizeRGBA(in *image.RGBA, out *image.RGBA, scale float64, coeffs []int16, offset []int, filterLength int) { -	newBounds := out.Bounds() -	maxX := in.Bounds().Dx() - 1 - -	for x := newBounds.Min.X; x < newBounds.Max.X; x++ { -		row := in.Pix[x*in.Stride:] -		for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ { -			var rgba [4]int32 -			var sum int32 -			start := offset[y] -			ci := y * filterLength -			for i := 0; i < filterLength; i++ { -				coeff := coeffs[ci+i] -				if coeff != 0 { -					xi := start + i -					switch { -					case uint(xi) < uint(maxX): -						xi *= 4 -					case xi >= maxX: -						xi = 4 * maxX -					default: -						xi = 0 -					} - -					rgba[0] += int32(coeff) * int32(row[xi+0]) -					rgba[1] += int32(coeff) * int32(row[xi+1]) -					rgba[2] += int32(coeff) * int32(row[xi+2]) -					rgba[3] += int32(coeff) * int32(row[xi+3]) -					sum += int32(coeff) -				} -			} - -			xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*4 - -			out.Pix[xo+0] = clampUint8(rgba[0] / sum) -			out.Pix[xo+1] = clampUint8(rgba[1] / sum) -			out.Pix[xo+2] = clampUint8(rgba[2] / sum) -			out.Pix[xo+3] = clampUint8(rgba[3] / sum) -		} -	} -} - -func resizeNRGBA(in *image.NRGBA, out *image.RGBA, scale float64, coeffs []int16, offset []int, filterLength int) { -	newBounds := out.Bounds() -	maxX := in.Bounds().Dx() - 1 - -	for x := newBounds.Min.X; x < newBounds.Max.X; x++ { -		row := in.Pix[x*in.Stride:] -		for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ { -			var rgba [4]int32 -			var sum int32 -			start := offset[y] -			ci := y * filterLength -			for i := 0; i < filterLength; i++ { -				coeff := coeffs[ci+i] -				if coeff != 0 { -					xi := start + i -					switch { -					case uint(xi) < uint(maxX): -						xi *= 4 -					case xi >= maxX: -						xi = 4 * maxX -					default: -						xi = 0 -					} - -					// Forward alpha-premultiplication -					a := int32(row[xi+3]) -					r := int32(row[xi+0]) * a -					r /= 0xff -					g := int32(row[xi+1]) * a -					g /= 0xff -					b := int32(row[xi+2]) * a -					b /= 0xff - -					rgba[0] += int32(coeff) * r -					rgba[1] += int32(coeff) * g -					rgba[2] += int32(coeff) * b -					rgba[3] += int32(coeff) * a -					sum += int32(coeff) -				} -			} - -			xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*4 - -			out.Pix[xo+0] = clampUint8(rgba[0] / sum) -			out.Pix[xo+1] = clampUint8(rgba[1] / sum) -			out.Pix[xo+2] = clampUint8(rgba[2] / sum) -			out.Pix[xo+3] = clampUint8(rgba[3] / sum) -		} -	} -} - -func resizeRGBA64(in *image.RGBA64, out *image.RGBA64, scale float64, coeffs []int32, offset []int, filterLength int) { -	newBounds := out.Bounds() -	maxX := in.Bounds().Dx() - 1 - -	for x := newBounds.Min.X; x < newBounds.Max.X; x++ { -		row := in.Pix[x*in.Stride:] -		for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ { -			var rgba [4]int64 -			var sum int64 -			start := offset[y] -			ci := y * filterLength -			for i := 0; i < filterLength; i++ { -				coeff := coeffs[ci+i] -				if coeff != 0 { -					xi := start + i -					switch { -					case uint(xi) < uint(maxX): -						xi *= 8 -					case xi >= maxX: -						xi = 8 * maxX -					default: -						xi = 0 -					} - -					rgba[0] += int64(coeff) * (int64(row[xi+0])<<8 | int64(row[xi+1])) -					rgba[1] += int64(coeff) * (int64(row[xi+2])<<8 | int64(row[xi+3])) -					rgba[2] += int64(coeff) * (int64(row[xi+4])<<8 | int64(row[xi+5])) -					rgba[3] += int64(coeff) * (int64(row[xi+6])<<8 | int64(row[xi+7])) -					sum += int64(coeff) -				} -			} - -			xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*8 - -			value := clampUint16(rgba[0] / sum) -			out.Pix[xo+0] = uint8(value >> 8) -			out.Pix[xo+1] = uint8(value) -			value = clampUint16(rgba[1] / sum) -			out.Pix[xo+2] = uint8(value >> 8) -			out.Pix[xo+3] = uint8(value) -			value = clampUint16(rgba[2] / sum) -			out.Pix[xo+4] = uint8(value >> 8) -			out.Pix[xo+5] = uint8(value) -			value = clampUint16(rgba[3] / sum) -			out.Pix[xo+6] = uint8(value >> 8) -			out.Pix[xo+7] = uint8(value) -		} -	} -} - -func resizeNRGBA64(in *image.NRGBA64, out *image.RGBA64, scale float64, coeffs []int32, offset []int, filterLength int) { -	newBounds := out.Bounds() -	maxX := in.Bounds().Dx() - 1 - -	for x := newBounds.Min.X; x < newBounds.Max.X; x++ { -		row := in.Pix[x*in.Stride:] -		for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ { -			var rgba [4]int64 -			var sum int64 -			start := offset[y] -			ci := y * filterLength -			for i := 0; i < filterLength; i++ { -				coeff := coeffs[ci+i] -				if coeff != 0 { -					xi := start + i -					switch { -					case uint(xi) < uint(maxX): -						xi *= 8 -					case xi >= maxX: -						xi = 8 * maxX -					default: -						xi = 0 -					} - -					// Forward alpha-premultiplication -					a := int64(uint16(row[xi+6])<<8 | uint16(row[xi+7])) -					r := int64(uint16(row[xi+0])<<8|uint16(row[xi+1])) * a -					r /= 0xffff -					g := int64(uint16(row[xi+2])<<8|uint16(row[xi+3])) * a -					g /= 0xffff -					b := int64(uint16(row[xi+4])<<8|uint16(row[xi+5])) * a -					b /= 0xffff - -					rgba[0] += int64(coeff) * r -					rgba[1] += int64(coeff) * g -					rgba[2] += int64(coeff) * b -					rgba[3] += int64(coeff) * a -					sum += int64(coeff) -				} -			} - -			xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*8 - -			value := clampUint16(rgba[0] / sum) -			out.Pix[xo+0] = uint8(value >> 8) -			out.Pix[xo+1] = uint8(value) -			value = clampUint16(rgba[1] / sum) -			out.Pix[xo+2] = uint8(value >> 8) -			out.Pix[xo+3] = uint8(value) -			value = clampUint16(rgba[2] / sum) -			out.Pix[xo+4] = uint8(value >> 8) -			out.Pix[xo+5] = uint8(value) -			value = clampUint16(rgba[3] / sum) -			out.Pix[xo+6] = uint8(value >> 8) -			out.Pix[xo+7] = uint8(value) -		} -	} -} - -func resizeGray(in *image.Gray, out *image.Gray, scale float64, coeffs []int16, offset []int, filterLength int) { -	newBounds := out.Bounds() -	maxX := in.Bounds().Dx() - 1 - -	for x := newBounds.Min.X; x < newBounds.Max.X; x++ { -		row := in.Pix[(x-newBounds.Min.X)*in.Stride:] -		for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ { -			var gray int32 -			var sum int32 -			start := offset[y] -			ci := y * filterLength -			for i := 0; i < filterLength; i++ { -				coeff := coeffs[ci+i] -				if coeff != 0 { -					xi := start + i -					switch { -					case xi < 0: -						xi = 0 -					case xi >= maxX: -						xi = maxX -					} -					gray += int32(coeff) * int32(row[xi]) -					sum += int32(coeff) -				} -			} - -			offset := (y-newBounds.Min.Y)*out.Stride + (x - newBounds.Min.X) -			out.Pix[offset] = clampUint8(gray / sum) -		} -	} -} - -func resizeGray16(in *image.Gray16, out *image.Gray16, scale float64, coeffs []int32, offset []int, filterLength int) { -	newBounds := out.Bounds() -	maxX := in.Bounds().Dx() - 1 - -	for x := newBounds.Min.X; x < newBounds.Max.X; x++ { -		row := in.Pix[x*in.Stride:] -		for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ { -			var gray int64 -			var sum int64 -			start := offset[y] -			ci := y * filterLength -			for i := 0; i < filterLength; i++ { -				coeff := coeffs[ci+i] -				if coeff != 0 { -					xi := start + i -					switch { -					case uint(xi) < uint(maxX): -						xi *= 2 -					case xi >= maxX: -						xi = 2 * maxX -					default: -						xi = 0 -					} -					gray += int64(coeff) * int64(uint16(row[xi+0])<<8|uint16(row[xi+1])) -					sum += int64(coeff) -				} -			} - -			offset := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*2 -			value := clampUint16(gray / sum) -			out.Pix[offset+0] = uint8(value >> 8) -			out.Pix[offset+1] = uint8(value) -		} -	} -} - -func resizeYCbCr(in *ycc, out *ycc, scale float64, coeffs []int16, offset []int, filterLength int) { -	newBounds := out.Bounds() -	maxX := in.Bounds().Dx() - 1 - -	for x := newBounds.Min.X; x < newBounds.Max.X; x++ { -		row := in.Pix[x*in.Stride:] -		for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ { -			var p [3]int32 -			var sum int32 -			start := offset[y] -			ci := y * filterLength -			for i := 0; i < filterLength; i++ { -				coeff := coeffs[ci+i] -				if coeff != 0 { -					xi := start + i -					switch { -					case uint(xi) < uint(maxX): -						xi *= 3 -					case xi >= maxX: -						xi = 3 * maxX -					default: -						xi = 0 -					} -					p[0] += int32(coeff) * int32(row[xi+0]) -					p[1] += int32(coeff) * int32(row[xi+1]) -					p[2] += int32(coeff) * int32(row[xi+2]) -					sum += int32(coeff) -				} -			} - -			xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*3 -			out.Pix[xo+0] = clampUint8(p[0] / sum) -			out.Pix[xo+1] = clampUint8(p[1] / sum) -			out.Pix[xo+2] = clampUint8(p[2] / sum) -		} -	} -} - -func nearestYCbCr(in *ycc, out *ycc, scale float64, coeffs []bool, offset []int, filterLength int) { -	newBounds := out.Bounds() -	maxX := in.Bounds().Dx() - 1 - -	for x := newBounds.Min.X; x < newBounds.Max.X; x++ { -		row := in.Pix[x*in.Stride:] -		for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ { -			var p [3]float32 -			var sum float32 -			start := offset[y] -			ci := y * filterLength -			for i := 0; i < filterLength; i++ { -				if coeffs[ci+i] { -					xi := start + i -					switch { -					case uint(xi) < uint(maxX): -						xi *= 3 -					case xi >= maxX: -						xi = 3 * maxX -					default: -						xi = 0 -					} -					p[0] += float32(row[xi+0]) -					p[1] += float32(row[xi+1]) -					p[2] += float32(row[xi+2]) -					sum++ -				} -			} - -			xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*3 -			out.Pix[xo+0] = floatToUint8(p[0] / sum) -			out.Pix[xo+1] = floatToUint8(p[1] / sum) -			out.Pix[xo+2] = floatToUint8(p[2] / sum) -		} -	} -} diff --git a/vendor/github.com/nfnt/resize/filters.go b/vendor/github.com/nfnt/resize/filters.go deleted file mode 100644 index 4ce04e389..000000000 --- a/vendor/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/vendor/github.com/nfnt/resize/nearest.go b/vendor/github.com/nfnt/resize/nearest.go deleted file mode 100644 index 888039d85..000000000 --- a/vendor/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/vendor/github.com/nfnt/resize/resize.go b/vendor/github.com/nfnt/resize/resize.go deleted file mode 100644 index 0d7fbf69a..000000000 --- a/vendor/github.com/nfnt/resize/resize.go +++ /dev/null @@ -1,620 +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. -// If the input image has width or height of 0, it is returned unchanged. -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 -	} - -	// Input image has no pixels -	if img.Bounds().Dx() <= 0 || img.Bounds().Dy() <= 0 { -		return img -	} - -	if interp == NearestNeighbor { -		return resizeNearest(width, height, scaleX, scaleY, img, interp) -	} - -	taps, kernel := interp.kernel() -	cpus := runtime.GOMAXPROCS(0) -	wg := sync.WaitGroup{} - -	// Generic access to image.Image is slow in tight loops. -	// The optimal access has to be determined from the concrete image type. -	switch input := img.(type) { -	case *image.RGBA: -		// 8-bit precision -		temp := image.NewRGBA(image.Rect(0, 0, input.Bounds().Dy(), int(width))) -		result := image.NewRGBA(image.Rect(0, 0, int(width), int(height))) - -		// horizontal filter, results in transposed temporary image -		coeffs, offset, filterLength := createWeights8(temp.Bounds().Dy(), taps, blur, scaleX, kernel) -		wg.Add(cpus) -		for i := 0; i < cpus; i++ { -			slice := makeSlice(temp, i, cpus).(*image.RGBA) -			go func() { -				defer wg.Done() -				resizeRGBA(input, slice, scaleX, coeffs, offset, filterLength) -			}() -		} -		wg.Wait() - -		// horizontal filter on transposed image, result is not transposed -		coeffs, offset, filterLength = createWeights8(result.Bounds().Dy(), taps, blur, scaleY, kernel) -		wg.Add(cpus) -		for i := 0; i < cpus; i++ { -			slice := makeSlice(result, i, cpus).(*image.RGBA) -			go func() { -				defer wg.Done() -				resizeRGBA(temp, slice, scaleY, coeffs, offset, filterLength) -			}() -		} -		wg.Wait() -		return result -	case *image.NRGBA: -		// 8-bit precision -		temp := image.NewRGBA(image.Rect(0, 0, input.Bounds().Dy(), int(width))) -		result := image.NewRGBA(image.Rect(0, 0, int(width), int(height))) - -		// horizontal filter, results in transposed temporary image -		coeffs, offset, filterLength := createWeights8(temp.Bounds().Dy(), taps, blur, scaleX, kernel) -		wg.Add(cpus) -		for i := 0; i < cpus; i++ { -			slice := makeSlice(temp, i, cpus).(*image.RGBA) -			go func() { -				defer wg.Done() -				resizeNRGBA(input, slice, scaleX, coeffs, offset, filterLength) -			}() -		} -		wg.Wait() - -		// horizontal filter on transposed image, result is not transposed -		coeffs, offset, filterLength = createWeights8(result.Bounds().Dy(), taps, blur, scaleY, kernel) -		wg.Add(cpus) -		for i := 0; i < cpus; i++ { -			slice := makeSlice(result, i, cpus).(*image.RGBA) -			go func() { -				defer wg.Done() -				resizeRGBA(temp, slice, scaleY, coeffs, offset, filterLength) -			}() -		} -		wg.Wait() -		return result - -	case *image.YCbCr: -		// 8-bit precision -		// accessing the YCbCr arrays in a tight loop is slow. -		// converting the image to ycc increases performance by 2x. -		temp := newYCC(image.Rect(0, 0, input.Bounds().Dy(), int(width)), input.SubsampleRatio) -		result := newYCC(image.Rect(0, 0, int(width), int(height)), image.YCbCrSubsampleRatio444) - -		coeffs, offset, filterLength := createWeights8(temp.Bounds().Dy(), taps, blur, scaleX, kernel) -		in := imageYCbCrToYCC(input) -		wg.Add(cpus) -		for i := 0; i < cpus; i++ { -			slice := makeSlice(temp, i, cpus).(*ycc) -			go func() { -				defer wg.Done() -				resizeYCbCr(in, slice, scaleX, coeffs, offset, filterLength) -			}() -		} -		wg.Wait() - -		coeffs, offset, filterLength = createWeights8(result.Bounds().Dy(), taps, blur, scaleY, kernel) -		wg.Add(cpus) -		for i := 0; i < cpus; i++ { -			slice := makeSlice(result, i, cpus).(*ycc) -			go func() { -				defer wg.Done() -				resizeYCbCr(temp, slice, scaleY, coeffs, offset, filterLength) -			}() -		} -		wg.Wait() -		return result.YCbCr() -	case *image.RGBA64: -		// 16-bit precision -		temp := image.NewRGBA64(image.Rect(0, 0, input.Bounds().Dy(), int(width))) -		result := image.NewRGBA64(image.Rect(0, 0, int(width), int(height))) - -		// horizontal filter, results in transposed temporary image -		coeffs, offset, filterLength := createWeights16(temp.Bounds().Dy(), taps, blur, scaleX, kernel) -		wg.Add(cpus) -		for i := 0; i < cpus; i++ { -			slice := makeSlice(temp, i, cpus).(*image.RGBA64) -			go func() { -				defer wg.Done() -				resizeRGBA64(input, slice, scaleX, coeffs, offset, filterLength) -			}() -		} -		wg.Wait() - -		// horizontal filter on transposed image, result is not transposed -		coeffs, offset, filterLength = createWeights16(result.Bounds().Dy(), taps, blur, scaleY, kernel) -		wg.Add(cpus) -		for i := 0; i < cpus; i++ { -			slice := makeSlice(result, i, cpus).(*image.RGBA64) -			go func() { -				defer wg.Done() -				resizeRGBA64(temp, slice, scaleY, coeffs, offset, filterLength) -			}() -		} -		wg.Wait() -		return result -	case *image.NRGBA64: -		// 16-bit precision -		temp := image.NewRGBA64(image.Rect(0, 0, input.Bounds().Dy(), int(width))) -		result := image.NewRGBA64(image.Rect(0, 0, int(width), int(height))) - -		// horizontal filter, results in transposed temporary image -		coeffs, offset, filterLength := createWeights16(temp.Bounds().Dy(), taps, blur, scaleX, kernel) -		wg.Add(cpus) -		for i := 0; i < cpus; i++ { -			slice := makeSlice(temp, i, cpus).(*image.RGBA64) -			go func() { -				defer wg.Done() -				resizeNRGBA64(input, slice, scaleX, coeffs, offset, filterLength) -			}() -		} -		wg.Wait() - -		// horizontal filter on transposed image, result is not transposed -		coeffs, offset, filterLength = createWeights16(result.Bounds().Dy(), taps, blur, scaleY, kernel) -		wg.Add(cpus) -		for i := 0; i < cpus; i++ { -			slice := makeSlice(result, i, cpus).(*image.RGBA64) -			go func() { -				defer wg.Done() -				resizeRGBA64(temp, slice, scaleY, coeffs, offset, filterLength) -			}() -		} -		wg.Wait() -		return result -	case *image.Gray: -		// 8-bit precision -		temp := image.NewGray(image.Rect(0, 0, input.Bounds().Dy(), int(width))) -		result := image.NewGray(image.Rect(0, 0, int(width), int(height))) - -		// horizontal filter, results in transposed temporary image -		coeffs, offset, filterLength := createWeights8(temp.Bounds().Dy(), taps, blur, scaleX, kernel) -		wg.Add(cpus) -		for i := 0; i < cpus; i++ { -			slice := makeSlice(temp, i, cpus).(*image.Gray) -			go func() { -				defer wg.Done() -				resizeGray(input, slice, scaleX, coeffs, offset, filterLength) -			}() -		} -		wg.Wait() - -		// horizontal filter on transposed image, result is not transposed -		coeffs, offset, filterLength = createWeights8(result.Bounds().Dy(), taps, blur, scaleY, kernel) -		wg.Add(cpus) -		for i := 0; i < cpus; i++ { -			slice := makeSlice(result, i, cpus).(*image.Gray) -			go func() { -				defer wg.Done() -				resizeGray(temp, slice, scaleY, coeffs, offset, filterLength) -			}() -		} -		wg.Wait() -		return result -	case *image.Gray16: -		// 16-bit precision -		temp := image.NewGray16(image.Rect(0, 0, input.Bounds().Dy(), int(width))) -		result := image.NewGray16(image.Rect(0, 0, int(width), int(height))) - -		// horizontal filter, results in transposed temporary image -		coeffs, offset, filterLength := createWeights16(temp.Bounds().Dy(), taps, blur, scaleX, kernel) -		wg.Add(cpus) -		for i := 0; i < cpus; i++ { -			slice := makeSlice(temp, i, cpus).(*image.Gray16) -			go func() { -				defer wg.Done() -				resizeGray16(input, slice, scaleX, coeffs, offset, filterLength) -			}() -		} -		wg.Wait() - -		// horizontal filter on transposed image, result is not transposed -		coeffs, offset, filterLength = createWeights16(result.Bounds().Dy(), taps, blur, scaleY, kernel) -		wg.Add(cpus) -		for i := 0; i < cpus; i++ { -			slice := makeSlice(result, i, cpus).(*image.Gray16) -			go func() { -				defer wg.Done() -				resizeGray16(temp, slice, scaleY, coeffs, offset, filterLength) -			}() -		} -		wg.Wait() -		return result -	default: -		// 16-bit precision -		temp := image.NewRGBA64(image.Rect(0, 0, img.Bounds().Dy(), int(width))) -		result := image.NewRGBA64(image.Rect(0, 0, int(width), int(height))) - -		// horizontal filter, results in transposed temporary image -		coeffs, offset, filterLength := createWeights16(temp.Bounds().Dy(), taps, blur, scaleX, kernel) -		wg.Add(cpus) -		for i := 0; i < cpus; i++ { -			slice := makeSlice(temp, i, cpus).(*image.RGBA64) -			go func() { -				defer wg.Done() -				resizeGeneric(img, slice, scaleX, coeffs, offset, filterLength) -			}() -		} -		wg.Wait() - -		// horizontal filter on transposed image, result is not transposed -		coeffs, offset, filterLength = createWeights16(result.Bounds().Dy(), taps, blur, scaleY, kernel) -		wg.Add(cpus) -		for i := 0; i < cpus; i++ { -			slice := makeSlice(result, i, cpus).(*image.RGBA64) -			go func() { -				defer wg.Done() -				resizeRGBA64(temp, slice, scaleY, coeffs, offset, filterLength) -			}() -		} -		wg.Wait() -		return result -	} -} - -func resizeNearest(width, height uint, scaleX, scaleY float64, img image.Image, interp InterpolationFunction) image.Image { -	taps, _ := interp.kernel() -	cpus := runtime.GOMAXPROCS(0) -	wg := sync.WaitGroup{} - -	switch input := img.(type) { -	case *image.RGBA: -		// 8-bit precision -		temp := image.NewRGBA(image.Rect(0, 0, input.Bounds().Dy(), int(width))) -		result := image.NewRGBA(image.Rect(0, 0, int(width), int(height))) - -		// horizontal filter, results in transposed temporary image -		coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX) -		wg.Add(cpus) -		for i := 0; i < cpus; i++ { -			slice := makeSlice(temp, i, cpus).(*image.RGBA) -			go func() { -				defer wg.Done() -				nearestRGBA(input, slice, scaleX, coeffs, offset, filterLength) -			}() -		} -		wg.Wait() - -		// horizontal filter on transposed image, result is not transposed -		coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY) -		wg.Add(cpus) -		for i := 0; i < cpus; i++ { -			slice := makeSlice(result, i, cpus).(*image.RGBA) -			go func() { -				defer wg.Done() -				nearestRGBA(temp, slice, scaleY, coeffs, offset, filterLength) -			}() -		} -		wg.Wait() -		return result -	case *image.NRGBA: -		// 8-bit precision -		temp := image.NewNRGBA(image.Rect(0, 0, input.Bounds().Dy(), int(width))) -		result := image.NewNRGBA(image.Rect(0, 0, int(width), int(height))) - -		// horizontal filter, results in transposed temporary image -		coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX) -		wg.Add(cpus) -		for i := 0; i < cpus; i++ { -			slice := makeSlice(temp, i, cpus).(*image.NRGBA) -			go func() { -				defer wg.Done() -				nearestNRGBA(input, slice, scaleX, coeffs, offset, filterLength) -			}() -		} -		wg.Wait() - -		// horizontal filter on transposed image, result is not transposed -		coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY) -		wg.Add(cpus) -		for i := 0; i < cpus; i++ { -			slice := makeSlice(result, i, cpus).(*image.NRGBA) -			go func() { -				defer wg.Done() -				nearestNRGBA(temp, slice, scaleY, coeffs, offset, filterLength) -			}() -		} -		wg.Wait() -		return result -	case *image.YCbCr: -		// 8-bit precision -		// accessing the YCbCr arrays in a tight loop is slow. -		// converting the image to ycc increases performance by 2x. -		temp := newYCC(image.Rect(0, 0, input.Bounds().Dy(), int(width)), input.SubsampleRatio) -		result := newYCC(image.Rect(0, 0, int(width), int(height)), image.YCbCrSubsampleRatio444) - -		coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX) -		in := imageYCbCrToYCC(input) -		wg.Add(cpus) -		for i := 0; i < cpus; i++ { -			slice := makeSlice(temp, i, cpus).(*ycc) -			go func() { -				defer wg.Done() -				nearestYCbCr(in, slice, scaleX, coeffs, offset, filterLength) -			}() -		} -		wg.Wait() - -		coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY) -		wg.Add(cpus) -		for i := 0; i < cpus; i++ { -			slice := makeSlice(result, i, cpus).(*ycc) -			go func() { -				defer wg.Done() -				nearestYCbCr(temp, slice, scaleY, coeffs, offset, filterLength) -			}() -		} -		wg.Wait() -		return result.YCbCr() -	case *image.RGBA64: -		// 16-bit precision -		temp := image.NewRGBA64(image.Rect(0, 0, input.Bounds().Dy(), int(width))) -		result := image.NewRGBA64(image.Rect(0, 0, int(width), int(height))) - -		// horizontal filter, results in transposed temporary image -		coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX) -		wg.Add(cpus) -		for i := 0; i < cpus; i++ { -			slice := makeSlice(temp, i, cpus).(*image.RGBA64) -			go func() { -				defer wg.Done() -				nearestRGBA64(input, slice, scaleX, coeffs, offset, filterLength) -			}() -		} -		wg.Wait() - -		// horizontal filter on transposed image, result is not transposed -		coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY) -		wg.Add(cpus) -		for i := 0; i < cpus; i++ { -			slice := makeSlice(result, i, cpus).(*image.RGBA64) -			go func() { -				defer wg.Done() -				nearestRGBA64(temp, slice, scaleY, coeffs, offset, filterLength) -			}() -		} -		wg.Wait() -		return result -	case *image.NRGBA64: -		// 16-bit precision -		temp := image.NewNRGBA64(image.Rect(0, 0, input.Bounds().Dy(), int(width))) -		result := image.NewNRGBA64(image.Rect(0, 0, int(width), int(height))) - -		// horizontal filter, results in transposed temporary image -		coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX) -		wg.Add(cpus) -		for i := 0; i < cpus; i++ { -			slice := makeSlice(temp, i, cpus).(*image.NRGBA64) -			go func() { -				defer wg.Done() -				nearestNRGBA64(input, slice, scaleX, coeffs, offset, filterLength) -			}() -		} -		wg.Wait() - -		// horizontal filter on transposed image, result is not transposed -		coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY) -		wg.Add(cpus) -		for i := 0; i < cpus; i++ { -			slice := makeSlice(result, i, cpus).(*image.NRGBA64) -			go func() { -				defer wg.Done() -				nearestNRGBA64(temp, slice, scaleY, coeffs, offset, filterLength) -			}() -		} -		wg.Wait() -		return result -	case *image.Gray: -		// 8-bit precision -		temp := image.NewGray(image.Rect(0, 0, input.Bounds().Dy(), int(width))) -		result := image.NewGray(image.Rect(0, 0, int(width), int(height))) - -		// horizontal filter, results in transposed temporary image -		coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX) -		wg.Add(cpus) -		for i := 0; i < cpus; i++ { -			slice := makeSlice(temp, i, cpus).(*image.Gray) -			go func() { -				defer wg.Done() -				nearestGray(input, slice, scaleX, coeffs, offset, filterLength) -			}() -		} -		wg.Wait() - -		// horizontal filter on transposed image, result is not transposed -		coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY) -		wg.Add(cpus) -		for i := 0; i < cpus; i++ { -			slice := makeSlice(result, i, cpus).(*image.Gray) -			go func() { -				defer wg.Done() -				nearestGray(temp, slice, scaleY, coeffs, offset, filterLength) -			}() -		} -		wg.Wait() -		return result -	case *image.Gray16: -		// 16-bit precision -		temp := image.NewGray16(image.Rect(0, 0, input.Bounds().Dy(), int(width))) -		result := image.NewGray16(image.Rect(0, 0, int(width), int(height))) - -		// horizontal filter, results in transposed temporary image -		coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX) -		wg.Add(cpus) -		for i := 0; i < cpus; i++ { -			slice := makeSlice(temp, i, cpus).(*image.Gray16) -			go func() { -				defer wg.Done() -				nearestGray16(input, slice, scaleX, coeffs, offset, filterLength) -			}() -		} -		wg.Wait() - -		// horizontal filter on transposed image, result is not transposed -		coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY) -		wg.Add(cpus) -		for i := 0; i < cpus; i++ { -			slice := makeSlice(result, i, cpus).(*image.Gray16) -			go func() { -				defer wg.Done() -				nearestGray16(temp, slice, scaleY, coeffs, offset, filterLength) -			}() -		} -		wg.Wait() -		return result -	default: -		// 16-bit precision -		temp := image.NewRGBA64(image.Rect(0, 0, img.Bounds().Dy(), int(width))) -		result := image.NewRGBA64(image.Rect(0, 0, int(width), int(height))) - -		// horizontal filter, results in transposed temporary image -		coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX) -		wg.Add(cpus) -		for i := 0; i < cpus; i++ { -			slice := makeSlice(temp, i, cpus).(*image.RGBA64) -			go func() { -				defer wg.Done() -				nearestGeneric(img, slice, scaleX, coeffs, offset, filterLength) -			}() -		} -		wg.Wait() - -		// horizontal filter on transposed image, result is not transposed -		coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY) -		wg.Add(cpus) -		for i := 0; i < cpus; i++ { -			slice := makeSlice(result, i, cpus).(*image.RGBA64) -			go func() { -				defer wg.Done() -				nearestRGBA64(temp, slice, scaleY, coeffs, offset, filterLength) -			}() -		} -		wg.Wait() -		return result -	} - -} - -// Calculates scaling factors using old and new image dimensions. -func calcFactors(width, height uint, oldWidth, oldHeight float64) (scaleX, scaleY float64) { -	if width == 0 { -		if height == 0 { -			scaleX = 1.0 -			scaleY = 1.0 -		} else { -			scaleY = oldHeight / float64(height) -			scaleX = scaleY -		} -	} else { -		scaleX = oldWidth / float64(width) -		if height == 0 { -			scaleY = scaleX -		} else { -			scaleY = oldHeight / float64(height) -		} -	} -	return -} - -type imageWithSubImage interface { -	image.Image -	SubImage(image.Rectangle) image.Image -} - -func makeSlice(img imageWithSubImage, i, n int) image.Image { -	return img.SubImage(image.Rect(img.Bounds().Min.X, img.Bounds().Min.Y+i*img.Bounds().Dy()/n, img.Bounds().Max.X, img.Bounds().Min.Y+(i+1)*img.Bounds().Dy()/n)) -} diff --git a/vendor/github.com/nfnt/resize/thumbnail.go b/vendor/github.com/nfnt/resize/thumbnail.go deleted file mode 100644 index 9efc246be..000000000 --- a/vendor/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/vendor/github.com/nfnt/resize/ycc.go b/vendor/github.com/nfnt/resize/ycc.go deleted file mode 100644 index 143e4d06a..000000000 --- a/vendor/github.com/nfnt/resize/ycc.go +++ /dev/null @@ -1,387 +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} -} - -// Copy of image.YCbCrSubsampleRatio constants - this allows us to support -// older versions of Go where these constants are not defined (i.e. Go 1.4) -const ( -	ycbcrSubsampleRatio444 image.YCbCrSubsampleRatio = iota -	ycbcrSubsampleRatio422 -	ycbcrSubsampleRatio420 -	ycbcrSubsampleRatio440 -	ycbcrSubsampleRatio411 -	ycbcrSubsampleRatio410 -) - -// 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) -	switch ycbcr.SubsampleRatio { -	case ycbcrSubsampleRatio422: -		return p.ycbcr422(ycbcr) -	case ycbcrSubsampleRatio420: -		return p.ycbcr420(ycbcr) -	case ycbcrSubsampleRatio440: -		return p.ycbcr440(ycbcr) -	case ycbcrSubsampleRatio444: -		return p.ycbcr444(ycbcr) -	case ycbcrSubsampleRatio411: -		return p.ycbcr411(ycbcr) -	case ycbcrSubsampleRatio410: -		return p.ycbcr410(ycbcr) -	} -	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() -	p := ycc{ -		Pix:            make([]uint8, 3*w*h), -		Stride:         3 * w, -		Rect:           image.Rect(0, 0, w, h), -		SubsampleRatio: in.SubsampleRatio, -	} -	switch in.SubsampleRatio { -	case ycbcrSubsampleRatio422: -		return convertToYCC422(in, &p) -	case ycbcrSubsampleRatio420: -		return convertToYCC420(in, &p) -	case ycbcrSubsampleRatio440: -		return convertToYCC440(in, &p) -	case ycbcrSubsampleRatio444: -		return convertToYCC444(in, &p) -	case ycbcrSubsampleRatio411: -		return convertToYCC411(in, &p) -	case ycbcrSubsampleRatio410: -		return convertToYCC410(in, &p) -	} -	return &p -} - -func (p *ycc) ycbcr422(ycbcr *image.YCbCr) *image.YCbCr { -	var off int -	Pix := p.Pix -	Y := ycbcr.Y -	Cb := ycbcr.Cb -	Cr := ycbcr.Cr -	for y := 0; y < ycbcr.Rect.Max.Y-ycbcr.Rect.Min.Y; y++ { -		yy := y * ycbcr.YStride -		cy := y * ycbcr.CStride -		for x := 0; x < ycbcr.Rect.Max.X-ycbcr.Rect.Min.X; x++ { -			ci := cy + x/2 -			Y[yy+x] = Pix[off+0] -			Cb[ci] = Pix[off+1] -			Cr[ci] = Pix[off+2] -			off += 3 -		} -	} -	return ycbcr -} - -func (p *ycc) ycbcr420(ycbcr *image.YCbCr) *image.YCbCr { -	var off int -	Pix := p.Pix -	Y := ycbcr.Y -	Cb := ycbcr.Cb -	Cr := ycbcr.Cr -	for y := 0; y < ycbcr.Rect.Max.Y-ycbcr.Rect.Min.Y; y++ { -		yy := y * ycbcr.YStride -		cy := (y / 2) * ycbcr.CStride -		for x := 0; x < ycbcr.Rect.Max.X-ycbcr.Rect.Min.X; x++ { -			ci := cy + x/2 -			Y[yy+x] = Pix[off+0] -			Cb[ci] = Pix[off+1] -			Cr[ci] = Pix[off+2] -			off += 3 -		} -	} -	return ycbcr -} - -func (p *ycc) ycbcr440(ycbcr *image.YCbCr) *image.YCbCr { -	var off int -	Pix := p.Pix -	Y := ycbcr.Y -	Cb := ycbcr.Cb -	Cr := ycbcr.Cr -	for y := 0; y < ycbcr.Rect.Max.Y-ycbcr.Rect.Min.Y; y++ { -		yy := y * ycbcr.YStride -		cy := (y / 2) * ycbcr.CStride -		for x := 0; x < ycbcr.Rect.Max.X-ycbcr.Rect.Min.X; x++ { -			ci := cy + x -			Y[yy+x] = Pix[off+0] -			Cb[ci] = Pix[off+1] -			Cr[ci] = Pix[off+2] -			off += 3 -		} -	} -	return ycbcr -} - -func (p *ycc) ycbcr444(ycbcr *image.YCbCr) *image.YCbCr { -	var off int -	Pix := p.Pix -	Y := ycbcr.Y -	Cb := ycbcr.Cb -	Cr := ycbcr.Cr -	for y := 0; y < ycbcr.Rect.Max.Y-ycbcr.Rect.Min.Y; y++ { -		yy := y * ycbcr.YStride -		cy := y * ycbcr.CStride -		for x := 0; x < ycbcr.Rect.Max.X-ycbcr.Rect.Min.X; x++ { -			ci := cy + x -			Y[yy+x] = Pix[off+0] -			Cb[ci] = Pix[off+1] -			Cr[ci] = Pix[off+2] -			off += 3 -		} -	} -	return ycbcr -} - -func (p *ycc) ycbcr411(ycbcr *image.YCbCr) *image.YCbCr { -	var off int -	Pix := p.Pix -	Y := ycbcr.Y -	Cb := ycbcr.Cb -	Cr := ycbcr.Cr -	for y := 0; y < ycbcr.Rect.Max.Y-ycbcr.Rect.Min.Y; y++ { -		yy := y * ycbcr.YStride -		cy := y * ycbcr.CStride -		for x := 0; x < ycbcr.Rect.Max.X-ycbcr.Rect.Min.X; x++ { -			ci := cy + x/4 -			Y[yy+x] = Pix[off+0] -			Cb[ci] = Pix[off+1] -			Cr[ci] = Pix[off+2] -			off += 3 -		} -	} -	return ycbcr -} - -func (p *ycc) ycbcr410(ycbcr *image.YCbCr) *image.YCbCr { -	var off int -	Pix := p.Pix -	Y := ycbcr.Y -	Cb := ycbcr.Cb -	Cr := ycbcr.Cr -	for y := 0; y < ycbcr.Rect.Max.Y-ycbcr.Rect.Min.Y; y++ { -		yy := y * ycbcr.YStride -		cy := (y / 2) * ycbcr.CStride -		for x := 0; x < ycbcr.Rect.Max.X-ycbcr.Rect.Min.X; x++ { -			ci := cy + x/4 -			Y[yy+x] = Pix[off+0] -			Cb[ci] = Pix[off+1] -			Cr[ci] = Pix[off+2] -			off += 3 -		} -	} -	return ycbcr -} - -func convertToYCC422(in *image.YCbCr, p *ycc) *ycc { -	var off int -	Pix := p.Pix -	Y := in.Y -	Cb := in.Cb -	Cr := in.Cr -	for y := 0; y < in.Rect.Max.Y-in.Rect.Min.Y; y++ { -		yy := y * in.YStride -		cy := y * in.CStride -		for x := 0; x < in.Rect.Max.X-in.Rect.Min.X; x++ { -			ci := cy + x/2 -			Pix[off+0] = Y[yy+x] -			Pix[off+1] = Cb[ci] -			Pix[off+2] = Cr[ci] -			off += 3 -		} -	} -	return p -} - -func convertToYCC420(in *image.YCbCr, p *ycc) *ycc { -	var off int -	Pix := p.Pix -	Y := in.Y -	Cb := in.Cb -	Cr := in.Cr -	for y := 0; y < in.Rect.Max.Y-in.Rect.Min.Y; y++ { -		yy := y * in.YStride -		cy := (y / 2) * in.CStride -		for x := 0; x < in.Rect.Max.X-in.Rect.Min.X; x++ { -			ci := cy + x/2 -			Pix[off+0] = Y[yy+x] -			Pix[off+1] = Cb[ci] -			Pix[off+2] = Cr[ci] -			off += 3 -		} -	} -	return p -} - -func convertToYCC440(in *image.YCbCr, p *ycc) *ycc { -	var off int -	Pix := p.Pix -	Y := in.Y -	Cb := in.Cb -	Cr := in.Cr -	for y := 0; y < in.Rect.Max.Y-in.Rect.Min.Y; y++ { -		yy := y * in.YStride -		cy := (y / 2) * in.CStride -		for x := 0; x < in.Rect.Max.X-in.Rect.Min.X; x++ { -			ci := cy + x -			Pix[off+0] = Y[yy+x] -			Pix[off+1] = Cb[ci] -			Pix[off+2] = Cr[ci] -			off += 3 -		} -	} -	return p -} - -func convertToYCC444(in *image.YCbCr, p *ycc) *ycc { -	var off int -	Pix := p.Pix -	Y := in.Y -	Cb := in.Cb -	Cr := in.Cr -	for y := 0; y < in.Rect.Max.Y-in.Rect.Min.Y; y++ { -		yy := y * in.YStride -		cy := y * in.CStride -		for x := 0; x < in.Rect.Max.X-in.Rect.Min.X; x++ { -			ci := cy + x -			Pix[off+0] = Y[yy+x] -			Pix[off+1] = Cb[ci] -			Pix[off+2] = Cr[ci] -			off += 3 -		} -	} -	return p -} - -func convertToYCC411(in *image.YCbCr, p *ycc) *ycc { -	var off int -	Pix := p.Pix -	Y := in.Y -	Cb := in.Cb -	Cr := in.Cr -	for y := 0; y < in.Rect.Max.Y-in.Rect.Min.Y; y++ { -		yy := y * in.YStride -		cy := y * in.CStride -		for x := 0; x < in.Rect.Max.X-in.Rect.Min.X; x++ { -			ci := cy + x/4 -			Pix[off+0] = Y[yy+x] -			Pix[off+1] = Cb[ci] -			Pix[off+2] = Cr[ci] -			off += 3 -		} -	} -	return p -} - -func convertToYCC410(in *image.YCbCr, p *ycc) *ycc { -	var off int -	Pix := p.Pix -	Y := in.Y -	Cb := in.Cb -	Cr := in.Cr -	for y := 0; y < in.Rect.Max.Y-in.Rect.Min.Y; y++ { -		yy := y * in.YStride -		cy := (y / 2) * in.CStride -		for x := 0; x < in.Rect.Max.X-in.Rect.Min.X; x++ { -			ci := cy + x/4 -			Pix[off+0] = Y[yy+x] -			Pix[off+1] = Cb[ci] -			Pix[off+2] = Cr[ci] -			off += 3 -		} -	} -	return p -} diff --git a/vendor/golang.org/x/image/AUTHORS b/vendor/golang.org/x/image/AUTHORS new file mode 100644 index 000000000..15167cd74 --- /dev/null +++ b/vendor/golang.org/x/image/AUTHORS @@ -0,0 +1,3 @@ +# This source code refers to The Go Authors for copyright purposes. +# The master list of authors is in the main Go distribution, +# visible at http://tip.golang.org/AUTHORS. diff --git a/vendor/golang.org/x/image/CONTRIBUTORS b/vendor/golang.org/x/image/CONTRIBUTORS new file mode 100644 index 000000000..1c4577e96 --- /dev/null +++ b/vendor/golang.org/x/image/CONTRIBUTORS @@ -0,0 +1,3 @@ +# This source code was written by the Go contributors. +# The master list of contributors is in the main Go distribution, +# visible at http://tip.golang.org/CONTRIBUTORS. diff --git a/vendor/golang.org/x/image/LICENSE b/vendor/golang.org/x/image/LICENSE new file mode 100644 index 000000000..6a66aea5e --- /dev/null +++ b/vendor/golang.org/x/image/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +   * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. +   * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. +   * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/golang.org/x/image/PATENTS b/vendor/golang.org/x/image/PATENTS new file mode 100644 index 000000000..733099041 --- /dev/null +++ b/vendor/golang.org/x/image/PATENTS @@ -0,0 +1,22 @@ +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the Go project. + +Google hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) +patent license to make, have made, use, offer to sell, sell, import, +transfer and otherwise run, modify and propagate the contents of this +implementation of Go, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of Go.  This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation.  If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of Go or any code incorporated within this +implementation of Go constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of Go +shall terminate as of the date such litigation is filed. diff --git a/vendor/golang.org/x/image/bmp/reader.go b/vendor/golang.org/x/image/bmp/reader.go new file mode 100644 index 000000000..c10a022f6 --- /dev/null +++ b/vendor/golang.org/x/image/bmp/reader.go @@ -0,0 +1,213 @@ +// 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 bmp implements a BMP image decoder and encoder. +// +// The BMP specification is at http://www.digicamsoft.com/bmp/bmp.html. +package bmp // import "golang.org/x/image/bmp" + +import ( +	"errors" +	"image" +	"image/color" +	"io" +) + +// ErrUnsupported means that the input BMP image uses a valid but unsupported +// feature. +var ErrUnsupported = errors.New("bmp: unsupported BMP image") + +func readUint16(b []byte) uint16 { +	return uint16(b[0]) | uint16(b[1])<<8 +} + +func readUint32(b []byte) uint32 { +	return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 +} + +// decodePaletted reads an 8 bit-per-pixel BMP image from r. +// If topDown is false, the image rows will be read bottom-up. +func decodePaletted(r io.Reader, c image.Config, topDown bool) (image.Image, error) { +	paletted := image.NewPaletted(image.Rect(0, 0, c.Width, c.Height), c.ColorModel.(color.Palette)) +	if c.Width == 0 || c.Height == 0 { +		return paletted, nil +	} +	var tmp [4]byte +	y0, y1, yDelta := c.Height-1, -1, -1 +	if topDown { +		y0, y1, yDelta = 0, c.Height, +1 +	} +	for y := y0; y != y1; y += yDelta { +		p := paletted.Pix[y*paletted.Stride : y*paletted.Stride+c.Width] +		if _, err := io.ReadFull(r, p); err != nil { +			return nil, err +		} +		// Each row is 4-byte aligned. +		if c.Width%4 != 0 { +			_, err := io.ReadFull(r, tmp[:4-c.Width%4]) +			if err != nil { +				return nil, err +			} +		} +	} +	return paletted, nil +} + +// decodeRGB reads a 24 bit-per-pixel BMP image from r. +// If topDown is false, the image rows will be read bottom-up. +func decodeRGB(r io.Reader, c image.Config, topDown bool) (image.Image, error) { +	rgba := image.NewRGBA(image.Rect(0, 0, c.Width, c.Height)) +	if c.Width == 0 || c.Height == 0 { +		return rgba, nil +	} +	// There are 3 bytes per pixel, and each row is 4-byte aligned. +	b := make([]byte, (3*c.Width+3)&^3) +	y0, y1, yDelta := c.Height-1, -1, -1 +	if topDown { +		y0, y1, yDelta = 0, c.Height, +1 +	} +	for y := y0; y != y1; y += yDelta { +		if _, err := io.ReadFull(r, b); err != nil { +			return nil, err +		} +		p := rgba.Pix[y*rgba.Stride : y*rgba.Stride+c.Width*4] +		for i, j := 0, 0; i < len(p); i, j = i+4, j+3 { +			// BMP images are stored in BGR order rather than RGB order. +			p[i+0] = b[j+2] +			p[i+1] = b[j+1] +			p[i+2] = b[j+0] +			p[i+3] = 0xFF +		} +	} +	return rgba, nil +} + +// decodeNRGBA reads a 32 bit-per-pixel BMP image from r. +// If topDown is false, the image rows will be read bottom-up. +func decodeNRGBA(r io.Reader, c image.Config, topDown bool) (image.Image, error) { +	rgba := image.NewNRGBA(image.Rect(0, 0, c.Width, c.Height)) +	if c.Width == 0 || c.Height == 0 { +		return rgba, nil +	} +	y0, y1, yDelta := c.Height-1, -1, -1 +	if topDown { +		y0, y1, yDelta = 0, c.Height, +1 +	} +	for y := y0; y != y1; y += yDelta { +		p := rgba.Pix[y*rgba.Stride : y*rgba.Stride+c.Width*4] +		if _, err := io.ReadFull(r, p); err != nil { +			return nil, err +		} +		for i := 0; i < len(p); i += 4 { +			// BMP images are stored in BGRA order rather than RGBA order. +			p[i+0], p[i+2] = p[i+2], p[i+0] +		} +	} +	return rgba, nil +} + +// Decode reads a BMP image from r and returns it as an image.Image. +// Limitation: The file must be 8, 24 or 32 bits per pixel. +func Decode(r io.Reader) (image.Image, error) { +	c, bpp, topDown, err := decodeConfig(r) +	if err != nil { +		return nil, err +	} +	switch bpp { +	case 8: +		return decodePaletted(r, c, topDown) +	case 24: +		return decodeRGB(r, c, topDown) +	case 32: +		return decodeNRGBA(r, c, topDown) +	} +	panic("unreachable") +} + +// DecodeConfig returns the color model and dimensions of a BMP image without +// decoding the entire image. +// Limitation: The file must be 8, 24 or 32 bits per pixel. +func DecodeConfig(r io.Reader) (image.Config, error) { +	config, _, _, err := decodeConfig(r) +	return config, err +} + +func decodeConfig(r io.Reader) (config image.Config, bitsPerPixel int, topDown bool, err error) { +	// We only support those BMP images that are a BITMAPFILEHEADER +	// immediately followed by a BITMAPINFOHEADER. +	const ( +		fileHeaderLen   = 14 +		infoHeaderLen   = 40 +		v4InfoHeaderLen = 108 +		v5InfoHeaderLen = 124 +	) +	var b [1024]byte +	if _, err := io.ReadFull(r, b[:fileHeaderLen+4]); err != nil { +		return image.Config{}, 0, false, err +	} +	if string(b[:2]) != "BM" { +		return image.Config{}, 0, false, errors.New("bmp: invalid format") +	} +	offset := readUint32(b[10:14]) +	infoLen := readUint32(b[14:18]) +	if infoLen != infoHeaderLen && infoLen != v4InfoHeaderLen && infoLen != v5InfoHeaderLen { +		return image.Config{}, 0, false, ErrUnsupported +	} +	if _, err := io.ReadFull(r, b[fileHeaderLen+4:fileHeaderLen+infoLen]); err != nil { +		return image.Config{}, 0, false, err +	} +	width := int(int32(readUint32(b[18:22]))) +	height := int(int32(readUint32(b[22:26]))) +	if height < 0 { +		height, topDown = -height, true +	} +	if width < 0 || height < 0 { +		return image.Config{}, 0, false, ErrUnsupported +	} +	// We only support 1 plane and 8, 24 or 32 bits per pixel and no +	// compression. +	planes, bpp, compression := readUint16(b[26:28]), readUint16(b[28:30]), readUint32(b[30:34]) +	// if compression is set to BITFIELDS, but the bitmask is set to the default bitmask +	// that would be used if compression was set to 0, we can continue as if compression was 0 +	if compression == 3 && infoLen > infoHeaderLen && +		readUint32(b[54:58]) == 0xff0000 && readUint32(b[58:62]) == 0xff00 && +		readUint32(b[62:66]) == 0xff && readUint32(b[66:70]) == 0xff000000 { +		compression = 0 +	} +	if planes != 1 || compression != 0 { +		return image.Config{}, 0, false, ErrUnsupported +	} +	switch bpp { +	case 8: +		if offset != fileHeaderLen+infoLen+256*4 { +			return image.Config{}, 0, false, ErrUnsupported +		} +		_, err = io.ReadFull(r, b[:256*4]) +		if err != nil { +			return image.Config{}, 0, false, err +		} +		pcm := make(color.Palette, 256) +		for i := range pcm { +			// BMP images are stored in BGR order rather than RGB order. +			// Every 4th byte is padding. +			pcm[i] = color.RGBA{b[4*i+2], b[4*i+1], b[4*i+0], 0xFF} +		} +		return image.Config{ColorModel: pcm, Width: width, Height: height}, 8, topDown, nil +	case 24: +		if offset != fileHeaderLen+infoLen { +			return image.Config{}, 0, false, ErrUnsupported +		} +		return image.Config{ColorModel: color.RGBAModel, Width: width, Height: height}, 24, topDown, nil +	case 32: +		if offset != fileHeaderLen+infoLen { +			return image.Config{}, 0, false, ErrUnsupported +		} +		return image.Config{ColorModel: color.RGBAModel, Width: width, Height: height}, 32, topDown, nil +	} +	return image.Config{}, 0, false, ErrUnsupported +} + +func init() { +	image.RegisterFormat("bmp", "BM????\x00\x00\x00\x00", Decode, DecodeConfig) +} diff --git a/vendor/golang.org/x/image/bmp/writer.go b/vendor/golang.org/x/image/bmp/writer.go new file mode 100644 index 000000000..f07b39dba --- /dev/null +++ b/vendor/golang.org/x/image/bmp/writer.go @@ -0,0 +1,262 @@ +// Copyright 2013 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 bmp + +import ( +	"encoding/binary" +	"errors" +	"image" +	"io" +) + +type header struct { +	sigBM           [2]byte +	fileSize        uint32 +	resverved       [2]uint16 +	pixOffset       uint32 +	dibHeaderSize   uint32 +	width           uint32 +	height          uint32 +	colorPlane      uint16 +	bpp             uint16 +	compression     uint32 +	imageSize       uint32 +	xPixelsPerMeter uint32 +	yPixelsPerMeter uint32 +	colorUse        uint32 +	colorImportant  uint32 +} + +func encodePaletted(w io.Writer, pix []uint8, dx, dy, stride, step int) error { +	var padding []byte +	if dx < step { +		padding = make([]byte, step-dx) +	} +	for y := dy - 1; y >= 0; y-- { +		min := y*stride + 0 +		max := y*stride + dx +		if _, err := w.Write(pix[min:max]); err != nil { +			return err +		} +		if padding != nil { +			if _, err := w.Write(padding); err != nil { +				return err +			} +		} +	} +	return nil +} + +func encodeRGBA(w io.Writer, pix []uint8, dx, dy, stride, step int, opaque bool) error { +	buf := make([]byte, step) +	if opaque { +		for y := dy - 1; y >= 0; y-- { +			min := y*stride + 0 +			max := y*stride + dx*4 +			off := 0 +			for i := min; i < max; i += 4 { +				buf[off+2] = pix[i+0] +				buf[off+1] = pix[i+1] +				buf[off+0] = pix[i+2] +				off += 3 +			} +			if _, err := w.Write(buf); err != nil { +				return err +			} +		} +	} else { +		for y := dy - 1; y >= 0; y-- { +			min := y*stride + 0 +			max := y*stride + dx*4 +			off := 0 +			for i := min; i < max; i += 4 { +				a := uint32(pix[i+3]) +				if a == 0 { +					buf[off+2] = 0 +					buf[off+1] = 0 +					buf[off+0] = 0 +					buf[off+3] = 0 +					off += 4 +					continue +				} else if a == 0xff { +					buf[off+2] = pix[i+0] +					buf[off+1] = pix[i+1] +					buf[off+0] = pix[i+2] +					buf[off+3] = 0xff +					off += 4 +					continue +				} +				buf[off+2] = uint8(((uint32(pix[i+0]) * 0xffff) / a) >> 8) +				buf[off+1] = uint8(((uint32(pix[i+1]) * 0xffff) / a) >> 8) +				buf[off+0] = uint8(((uint32(pix[i+2]) * 0xffff) / a) >> 8) +				buf[off+3] = uint8(a) +				off += 4 +			} +			if _, err := w.Write(buf); err != nil { +				return err +			} +		} +	} +	return nil +} + +func encodeNRGBA(w io.Writer, pix []uint8, dx, dy, stride, step int, opaque bool) error { +	buf := make([]byte, step) +	if opaque { +		for y := dy - 1; y >= 0; y-- { +			min := y*stride + 0 +			max := y*stride + dx*4 +			off := 0 +			for i := min; i < max; i += 4 { +				buf[off+2] = pix[i+0] +				buf[off+1] = pix[i+1] +				buf[off+0] = pix[i+2] +				off += 3 +			} +			if _, err := w.Write(buf); err != nil { +				return err +			} +		} +	} else { +		for y := dy - 1; y >= 0; y-- { +			min := y*stride + 0 +			max := y*stride + dx*4 +			off := 0 +			for i := min; i < max; i += 4 { +				buf[off+2] = pix[i+0] +				buf[off+1] = pix[i+1] +				buf[off+0] = pix[i+2] +				buf[off+3] = pix[i+3] +				off += 4 +			} +			if _, err := w.Write(buf); err != nil { +				return err +			} +		} +	} +	return nil +} + +func encode(w io.Writer, m image.Image, step int) error { +	b := m.Bounds() +	buf := make([]byte, step) +	for y := b.Max.Y - 1; y >= b.Min.Y; y-- { +		off := 0 +		for x := b.Min.X; x < b.Max.X; x++ { +			r, g, b, _ := m.At(x, y).RGBA() +			buf[off+2] = byte(r >> 8) +			buf[off+1] = byte(g >> 8) +			buf[off+0] = byte(b >> 8) +			off += 3 +		} +		if _, err := w.Write(buf); err != nil { +			return err +		} +	} +	return nil +} + +// Encode writes the image m to w in BMP format. +func Encode(w io.Writer, m image.Image) error { +	d := m.Bounds().Size() +	if d.X < 0 || d.Y < 0 { +		return errors.New("bmp: negative bounds") +	} +	h := &header{ +		sigBM:         [2]byte{'B', 'M'}, +		fileSize:      14 + 40, +		pixOffset:     14 + 40, +		dibHeaderSize: 40, +		width:         uint32(d.X), +		height:        uint32(d.Y), +		colorPlane:    1, +	} + +	var step int +	var palette []byte +	var opaque bool +	switch m := m.(type) { +	case *image.Gray: +		step = (d.X + 3) &^ 3 +		palette = make([]byte, 1024) +		for i := 0; i < 256; i++ { +			palette[i*4+0] = uint8(i) +			palette[i*4+1] = uint8(i) +			palette[i*4+2] = uint8(i) +			palette[i*4+3] = 0xFF +		} +		h.imageSize = uint32(d.Y * step) +		h.fileSize += uint32(len(palette)) + h.imageSize +		h.pixOffset += uint32(len(palette)) +		h.bpp = 8 + +	case *image.Paletted: +		step = (d.X + 3) &^ 3 +		palette = make([]byte, 1024) +		for i := 0; i < len(m.Palette) && i < 256; i++ { +			r, g, b, _ := m.Palette[i].RGBA() +			palette[i*4+0] = uint8(b >> 8) +			palette[i*4+1] = uint8(g >> 8) +			palette[i*4+2] = uint8(r >> 8) +			palette[i*4+3] = 0xFF +		} +		h.imageSize = uint32(d.Y * step) +		h.fileSize += uint32(len(palette)) + h.imageSize +		h.pixOffset += uint32(len(palette)) +		h.bpp = 8 +	case *image.RGBA: +		opaque = m.Opaque() +		if opaque { +			step = (3*d.X + 3) &^ 3 +			h.bpp = 24 +		} else { +			step = 4 * d.X +			h.bpp = 32 +		} +		h.imageSize = uint32(d.Y * step) +		h.fileSize += h.imageSize +	case *image.NRGBA: +		opaque = m.Opaque() +		if opaque { +			step = (3*d.X + 3) &^ 3 +			h.bpp = 24 +		} else { +			step = 4 * d.X +			h.bpp = 32 +		} +		h.imageSize = uint32(d.Y * step) +		h.fileSize += h.imageSize +	default: +		step = (3*d.X + 3) &^ 3 +		h.imageSize = uint32(d.Y * step) +		h.fileSize += h.imageSize +		h.bpp = 24 +	} + +	if err := binary.Write(w, binary.LittleEndian, h); err != nil { +		return err +	} +	if palette != nil { +		if err := binary.Write(w, binary.LittleEndian, palette); err != nil { +			return err +		} +	} + +	if d.X == 0 || d.Y == 0 { +		return nil +	} + +	switch m := m.(type) { +	case *image.Gray: +		return encodePaletted(w, m.Pix, d.X, d.Y, m.Stride, step) +	case *image.Paletted: +		return encodePaletted(w, m.Pix, d.X, d.Y, m.Stride, step) +	case *image.RGBA: +		return encodeRGBA(w, m.Pix, d.X, d.Y, m.Stride, step, opaque) +	case *image.NRGBA: +		return encodeNRGBA(w, m.Pix, d.X, d.Y, m.Stride, step, opaque) +	} +	return encode(w, m, step) +} diff --git a/vendor/golang.org/x/image/ccitt/reader.go b/vendor/golang.org/x/image/ccitt/reader.go new file mode 100644 index 000000000..16bd495d5 --- /dev/null +++ b/vendor/golang.org/x/image/ccitt/reader.go @@ -0,0 +1,697 @@ +// Copyright 2019 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. + +//go:generate go run gen.go + +// Package ccitt implements a CCITT (fax) image decoder. +package ccitt + +import ( +	"encoding/binary" +	"errors" +	"image" +	"io" +	"math/bits" +) + +var ( +	errInvalidBounds           = errors.New("ccitt: invalid bounds") +	errInvalidCode             = errors.New("ccitt: invalid code") +	errInvalidMode             = errors.New("ccitt: invalid mode") +	errInvalidOffset           = errors.New("ccitt: invalid offset") +	errMissingEOL              = errors.New("ccitt: missing End-of-Line") +	errRunLengthOverflowsWidth = errors.New("ccitt: run length overflows width") +	errRunLengthTooLong        = errors.New("ccitt: run length too long") +	errUnsupportedMode         = errors.New("ccitt: unsupported mode") +	errUnsupportedSubFormat    = errors.New("ccitt: unsupported sub-format") +	errUnsupportedWidth        = errors.New("ccitt: unsupported width") +) + +// Order specifies the bit ordering in a CCITT data stream. +type Order uint32 + +const ( +	// LSB means Least Significant Bits first. +	LSB Order = iota +	// MSB means Most Significant Bits first. +	MSB +) + +// SubFormat represents that the CCITT format consists of a number of +// sub-formats. Decoding or encoding a CCITT data stream requires knowing the +// sub-format context. It is not represented in the data stream per se. +type SubFormat uint32 + +const ( +	Group3 SubFormat = iota +	Group4 +) + +// Options are optional parameters. +type Options struct { +	// Align means that some variable-bit-width codes are byte-aligned. +	Align bool +	// Invert means that black is the 1 bit or 0xFF byte, and white is 0. +	Invert bool +} + +// maxWidth is the maximum (inclusive) supported width. This is a limitation of +// this implementation, to guard against integer overflow, and not anything +// inherent to the CCITT format. +const maxWidth = 1 << 20 + +func invertBytes(b []byte) { +	for i, c := range b { +		b[i] = ^c +	} +} + +func reverseBitsWithinBytes(b []byte) { +	for i, c := range b { +		b[i] = bits.Reverse8(c) +	} +} + +// highBits writes to dst (1 bit per pixel, most significant bit first) the +// high (0x80) bits from src (1 byte per pixel). It returns the number of bytes +// written and read such that dst[:d] is the packed form of src[:s]. +// +// For example, if src starts with the 8 bytes [0x7D, 0x7E, 0x7F, 0x80, 0x81, +// 0x82, 0x00, 0xFF] then 0x1D will be written to dst[0]. +// +// If src has (8 * len(dst)) or more bytes then only len(dst) bytes are +// written, (8 * len(dst)) bytes are read, and invert is ignored. +// +// Otherwise, if len(src) is not a multiple of 8 then the final byte written to +// dst is padded with 1 bits (if invert is true) or 0 bits. If inverted, the 1s +// are typically temporary, e.g. they will be flipped back to 0s by an +// invertBytes call in the highBits caller, reader.Read. +func highBits(dst []byte, src []byte, invert bool) (d int, s int) { +	// Pack as many complete groups of 8 src bytes as we can. +	n := len(src) / 8 +	if n > len(dst) { +		n = len(dst) +	} +	dstN := dst[:n] +	for i := range dstN { +		src8 := src[i*8 : i*8+8] +		dstN[i] = ((src8[0] & 0x80) >> 0) | +			((src8[1] & 0x80) >> 1) | +			((src8[2] & 0x80) >> 2) | +			((src8[3] & 0x80) >> 3) | +			((src8[4] & 0x80) >> 4) | +			((src8[5] & 0x80) >> 5) | +			((src8[6] & 0x80) >> 6) | +			((src8[7] & 0x80) >> 7) +	} +	d, s = n, 8*n +	dst, src = dst[d:], src[s:] + +	// Pack up to 7 remaining src bytes, if there's room in dst. +	if (len(dst) > 0) && (len(src) > 0) { +		dstByte := byte(0) +		if invert { +			dstByte = 0xFF >> uint(len(src)) +		} +		for n, srcByte := range src { +			dstByte |= (srcByte & 0x80) >> uint(n) +		} +		dst[0] = dstByte +		d, s = d+1, s+len(src) +	} +	return d, s +} + +type bitReader struct { +	r io.Reader + +	// readErr is the error returned from the most recent r.Read call. As the +	// io.Reader documentation says, when r.Read returns (n, err), "always +	// process the n > 0 bytes returned before considering the error err". +	readErr error + +	// order is whether to process r's bytes LSB first or MSB first. +	order Order + +	// The high nBits bits of the bits field hold upcoming bits in MSB order. +	bits  uint64 +	nBits uint32 + +	// bytes[br:bw] holds bytes read from r but not yet loaded into bits. +	br    uint32 +	bw    uint32 +	bytes [1024]uint8 +} + +func (b *bitReader) alignToByteBoundary() { +	n := b.nBits & 7 +	b.bits <<= n +	b.nBits -= n +} + +// nextBitMaxNBits is the maximum possible value of bitReader.nBits after a +// bitReader.nextBit call, provided that bitReader.nBits was not more than this +// value before that call. +// +// Note that the decode function can unread bits, which can temporarily set the +// bitReader.nBits value above nextBitMaxNBits. +const nextBitMaxNBits = 31 + +func (b *bitReader) nextBit() (uint64, error) { +	for { +		if b.nBits > 0 { +			bit := b.bits >> 63 +			b.bits <<= 1 +			b.nBits-- +			return bit, nil +		} + +		if available := b.bw - b.br; available >= 4 { +			// Read 32 bits, even though b.bits is a uint64, since the decode +			// function may need to unread up to maxCodeLength bits, putting +			// them back in the remaining (64 - 32) bits. TestMaxCodeLength +			// checks that the generated maxCodeLength constant fits. +			// +			// If changing the Uint32 call, also change nextBitMaxNBits. +			b.bits = uint64(binary.BigEndian.Uint32(b.bytes[b.br:])) << 32 +			b.br += 4 +			b.nBits = 32 +			continue +		} else if available > 0 { +			b.bits = uint64(b.bytes[b.br]) << (7 * 8) +			b.br++ +			b.nBits = 8 +			continue +		} + +		if b.readErr != nil { +			return 0, b.readErr +		} + +		n, err := b.r.Read(b.bytes[:]) +		b.br = 0 +		b.bw = uint32(n) +		b.readErr = err + +		if b.order != MSB { +			reverseBitsWithinBytes(b.bytes[:b.bw]) +		} +	} +} + +func decode(b *bitReader, decodeTable [][2]int16) (uint32, error) { +	nBitsRead, bitsRead, state := uint32(0), uint64(0), int32(1) +	for { +		bit, err := b.nextBit() +		if err != nil { +			return 0, err +		} +		bitsRead |= bit << (63 - nBitsRead) +		nBitsRead++ +		// The "&1" is redundant, but can eliminate a bounds check. +		state = int32(decodeTable[state][bit&1]) +		if state < 0 { +			return uint32(^state), nil +		} else if state == 0 { +			// Unread the bits we've read, then return errInvalidCode. +			b.bits = (b.bits >> nBitsRead) | bitsRead +			b.nBits += nBitsRead +			return 0, errInvalidCode +		} +	} +} + +type reader struct { +	br        bitReader +	subFormat SubFormat + +	// width is the image width in pixels. +	width int + +	// rowsRemaining starts at the image height in pixels, when the reader is +	// driven through the io.Reader interface, and decrements to zero as rows +	// are decoded. When driven through DecodeIntoGray, this field is unused. +	rowsRemaining int + +	// curr and prev hold the current and previous rows. Each element is either +	// 0x00 (black) or 0xFF (white). +	// +	// prev may be nil, when processing the first row. +	curr []byte +	prev []byte + +	// ri is the read index. curr[:ri] are those bytes of curr that have been +	// passed along via the Read method. +	// +	// When the reader is driven through DecodeIntoGray, instead of through the +	// io.Reader interface, this field is unused. +	ri int + +	// wi is the write index. curr[:wi] are those bytes of curr that have +	// already been decoded via the decodeRow method. +	// +	// What this implementation calls wi is roughly equivalent to what the spec +	// calls the a0 index. +	wi int + +	// These fields are copied from the *Options (which may be nil). +	align  bool +	invert bool + +	// atStartOfRow is whether we have just started the row. Some parts of the +	// spec say to treat this situation as if "wi = -1". +	atStartOfRow bool + +	// penColorIsWhite is whether the next run is black or white. +	penColorIsWhite bool + +	// seenStartOfImage is whether we've called the startDecode method. +	seenStartOfImage bool + +	// readErr is a sticky error for the Read method. +	readErr error +} + +func (z *reader) Read(p []byte) (int, error) { +	if z.readErr != nil { +		return 0, z.readErr +	} +	originalP := p + +	for len(p) > 0 { +		// Allocate buffers (and decode any start-of-image codes), if +		// processing the first or second row. +		if z.curr == nil { +			if !z.seenStartOfImage { +				if z.readErr = z.startDecode(); z.readErr != nil { +					break +				} +				z.atStartOfRow = true +			} +			z.curr = make([]byte, z.width) +		} + +		// Decode the next row, if necessary. +		if z.atStartOfRow { +			if z.rowsRemaining <= 0 { +				if z.readErr = z.finishDecode(); z.readErr != nil { +					break +				} +				z.readErr = io.EOF +				break +			} +			if z.readErr = z.decodeRow(); z.readErr != nil { +				break +			} +			z.rowsRemaining-- +		} + +		// Pack from z.curr (1 byte per pixel) to p (1 bit per pixel). +		packD, packS := highBits(p, z.curr[z.ri:], z.invert) +		p = p[packD:] +		z.ri += packS + +		// Prepare to decode the next row, if necessary. +		if z.ri == len(z.curr) { +			z.ri, z.curr, z.prev = 0, z.prev, z.curr +			z.atStartOfRow = true +		} +	} + +	n := len(originalP) - len(p) +	if z.invert { +		invertBytes(originalP[:n]) +	} +	return n, z.readErr +} + +func (z *reader) penColor() byte { +	if z.penColorIsWhite { +		return 0xFF +	} +	return 0x00 +} + +func (z *reader) startDecode() error { +	switch z.subFormat { +	case Group3: +		if err := z.decodeEOL(); err != nil { +			return err +		} + +	case Group4: +		// No-op. + +	default: +		return errUnsupportedSubFormat +	} + +	z.seenStartOfImage = true +	return nil +} + +func (z *reader) finishDecode() error { +	numberOfEOLs := 0 +	switch z.subFormat { +	case Group3: +		// The stream ends with a RTC (Return To Control) of 6 consecutive +		// EOL's, but we should have already just seen an EOL, either in +		// z.startDecode (for a zero-height image) or in z.decodeRow. +		numberOfEOLs = 5 + +	case Group4: +		// The stream ends with two EOL's, the first of which is possibly +		// byte-aligned. +		numberOfEOLs = 2 +		if err := z.decodeEOL(); err == nil { +			numberOfEOLs-- +		} else if err == errInvalidCode { +			// Try again, this time starting from a byte boundary. +			z.br.alignToByteBoundary() +		} else { +			return err +		} + +	default: +		return errUnsupportedSubFormat +	} + +	for ; numberOfEOLs > 0; numberOfEOLs-- { +		if err := z.decodeEOL(); err != nil { +			return err +		} +	} +	return nil +} + +func (z *reader) decodeEOL() error { +	// TODO: EOL doesn't have to be in the modeDecodeTable. It could be in its +	// own table, or we could just hard-code it, especially if we might need to +	// cater for optional byte-alignment, or an arbitrary number (potentially +	// more than 8) of 0-valued padding bits. +	if mode, err := decode(&z.br, modeDecodeTable[:]); err != nil { +		return err +	} else if mode != modeEOL { +		return errMissingEOL +	} +	return nil +} + +func (z *reader) decodeRow() error { +	z.wi = 0 +	z.atStartOfRow = true +	z.penColorIsWhite = true + +	if z.align { +		z.br.alignToByteBoundary() +	} + +	switch z.subFormat { +	case Group3: +		for ; z.wi < len(z.curr); z.atStartOfRow = false { +			if err := z.decodeRun(); err != nil { +				return err +			} +		} +		return z.decodeEOL() + +	case Group4: +		for ; z.wi < len(z.curr); z.atStartOfRow = false { +			mode, err := decode(&z.br, modeDecodeTable[:]) +			if err != nil { +				return err +			} +			rm := readerMode{} +			if mode < uint32(len(readerModes)) { +				rm = readerModes[mode] +			} +			if rm.function == nil { +				return errInvalidMode +			} +			if err := rm.function(z, rm.arg); err != nil { +				return err +			} +		} +		return nil +	} + +	return errUnsupportedSubFormat +} + +func (z *reader) decodeRun() error { +	table := blackDecodeTable[:] +	if z.penColorIsWhite { +		table = whiteDecodeTable[:] +	} + +	total := 0 +	for { +		n, err := decode(&z.br, table) +		if err != nil { +			return err +		} +		if n > maxWidth { +			panic("unreachable") +		} +		total += int(n) +		if total > maxWidth { +			return errRunLengthTooLong +		} +		// Anything 0x3F or below is a terminal code. +		if n <= 0x3F { +			break +		} +	} + +	if total > (len(z.curr) - z.wi) { +		return errRunLengthOverflowsWidth +	} +	dst := z.curr[z.wi : z.wi+total] +	penColor := z.penColor() +	for i := range dst { +		dst[i] = penColor +	} +	z.wi += total +	z.penColorIsWhite = !z.penColorIsWhite + +	return nil +} + +// The various modes' semantics are based on determining a row of pixels' +// "changing elements": those pixels whose color differs from the one on its +// immediate left. +// +// The row above the first row is implicitly all white. Similarly, the column +// to the left of the first column is implicitly all white. +// +// For example, here's Figure 1 in "ITU-T Recommendation T.6", where the +// current and previous rows contain black (B) and white (w) pixels. The a? +// indexes point into curr, the b? indexes point into prev. +// +//                 b1 b2 +//                 v  v +// prev: BBBBBwwwwwBBBwwwww +// curr: BBBwwwwwBBBBBBwwww +//          ^    ^     ^ +//          a0   a1    a2 +// +// a0 is the "reference element" or current decoder position, roughly +// equivalent to what this implementation calls reader.wi. +// +// a1 is the next changing element to the right of a0, on the "coding line" +// (the current row). +// +// a2 is the next changing element to the right of a1, again on curr. +// +// b1 is the first changing element on the "reference line" (the previous row) +// to the right of a0 and of opposite color to a0. +// +// b2 is the next changing element to the right of b1, again on prev. +// +// The various modes calculate a1 (and a2, for modeH): +//  - modePass calculates that a1 is at or to the right of b2. +//  - modeH    calculates a1 and a2 without considering b1 or b2. +//  - modeV*   calculates a1 to be b1 plus an adjustment (between -3 and +3). + +const ( +	findB1 = false +	findB2 = true +) + +// findB finds either the b1 or b2 value. +func (z *reader) findB(whichB bool) int { +	// The initial row is a special case. The previous row is implicitly all +	// white, so that there are no changing pixel elements. We return b1 or b2 +	// to be at the end of the row. +	if len(z.prev) != len(z.curr) { +		return len(z.curr) +	} + +	i := z.wi + +	if z.atStartOfRow { +		// a0 is implicitly at -1, on a white pixel. b1 is the first black +		// pixel in the previous row. b2 is the first white pixel after that. +		for ; (i < len(z.prev)) && (z.prev[i] == 0xFF); i++ { +		} +		if whichB == findB2 { +			for ; (i < len(z.prev)) && (z.prev[i] == 0x00); i++ { +			} +		} +		return i +	} + +	// As per figure 1 above, assume that the current pen color is white. +	// First, walk past every contiguous black pixel in prev, starting at a0. +	oppositeColor := ^z.penColor() +	for ; (i < len(z.prev)) && (z.prev[i] == oppositeColor); i++ { +	} + +	// Then walk past every contiguous white pixel. +	penColor := ^oppositeColor +	for ; (i < len(z.prev)) && (z.prev[i] == penColor); i++ { +	} + +	// We're now at a black pixel (or at the end of the row). That's b1. +	if whichB == findB2 { +		// If we're looking for b2, walk past every contiguous black pixel +		// again. +		oppositeColor := ^penColor +		for ; (i < len(z.prev)) && (z.prev[i] == oppositeColor); i++ { +		} +	} + +	return i +} + +type readerMode struct { +	function func(z *reader, arg int) error +	arg      int +} + +var readerModes = [...]readerMode{ +	modePass: {function: readerModePass}, +	modeH:    {function: readerModeH}, +	modeV0:   {function: readerModeV, arg: +0}, +	modeVR1:  {function: readerModeV, arg: +1}, +	modeVR2:  {function: readerModeV, arg: +2}, +	modeVR3:  {function: readerModeV, arg: +3}, +	modeVL1:  {function: readerModeV, arg: -1}, +	modeVL2:  {function: readerModeV, arg: -2}, +	modeVL3:  {function: readerModeV, arg: -3}, +	modeExt:  {function: readerModeExt}, +} + +func readerModePass(z *reader, arg int) error { +	b2 := z.findB(findB2) +	if (b2 < z.wi) || (len(z.curr) < b2) { +		return errInvalidOffset +	} +	dst := z.curr[z.wi:b2] +	penColor := z.penColor() +	for i := range dst { +		dst[i] = penColor +	} +	z.wi = b2 +	return nil +} + +func readerModeH(z *reader, arg int) error { +	// The first iteration finds a1. The second finds a2. +	for i := 0; i < 2; i++ { +		if err := z.decodeRun(); err != nil { +			return err +		} +	} +	return nil +} + +func readerModeV(z *reader, arg int) error { +	a1 := z.findB(findB1) + arg +	if (a1 < z.wi) || (len(z.curr) < a1) { +		return errInvalidOffset +	} +	dst := z.curr[z.wi:a1] +	penColor := z.penColor() +	for i := range dst { +		dst[i] = penColor +	} +	z.wi = a1 +	z.penColorIsWhite = !z.penColorIsWhite +	return nil +} + +func readerModeExt(z *reader, arg int) error { +	return errUnsupportedMode +} + +// DecodeIntoGray decodes the CCITT-formatted data in r into dst. +// +// It returns an error if dst's width and height don't match the implied width +// and height of CCITT-formatted data. +func DecodeIntoGray(dst *image.Gray, r io.Reader, order Order, sf SubFormat, opts *Options) error { +	bounds := dst.Bounds() +	if (bounds.Dx() < 0) || (bounds.Dy() < 0) { +		return errInvalidBounds +	} +	if bounds.Dx() > maxWidth { +		return errUnsupportedWidth +	} + +	z := reader{ +		br:        bitReader{r: r, order: order}, +		subFormat: sf, +		align:     (opts != nil) && opts.Align, +		invert:    (opts != nil) && opts.Invert, +		width:     bounds.Dx(), +	} +	if err := z.startDecode(); err != nil { +		return err +	} + +	width := bounds.Dx() +	for y := bounds.Min.Y; y < bounds.Max.Y; y++ { +		p := (y - bounds.Min.Y) * dst.Stride +		z.curr = dst.Pix[p : p+width] +		if err := z.decodeRow(); err != nil { +			return err +		} +		z.curr, z.prev = nil, z.curr +	} + +	if err := z.finishDecode(); err != nil { +		return err +	} + +	if z.invert { +		for y := bounds.Min.Y; y < bounds.Max.Y; y++ { +			p := (y - bounds.Min.Y) * dst.Stride +			invertBytes(dst.Pix[p : p+width]) +		} +	} + +	return nil +} + +// NewReader returns an io.Reader that decodes the CCITT-formatted data in r. +// The resultant byte stream is one bit per pixel (MSB first), with 1 meaning +// white and 0 meaning black. Each row in the result is byte-aligned. +func NewReader(r io.Reader, order Order, sf SubFormat, width int, height int, opts *Options) io.Reader { +	readErr := error(nil) +	if (width < 0) || (height < 0) { +		readErr = errInvalidBounds +	} else if width > maxWidth { +		readErr = errUnsupportedWidth +	} + +	return &reader{ +		br:            bitReader{r: r, order: order}, +		subFormat:     sf, +		align:         (opts != nil) && opts.Align, +		invert:        (opts != nil) && opts.Invert, +		width:         width, +		rowsRemaining: height, +		readErr:       readErr, +	} +} diff --git a/vendor/golang.org/x/image/ccitt/table.go b/vendor/golang.org/x/image/ccitt/table.go new file mode 100644 index 000000000..f01cc12b5 --- /dev/null +++ b/vendor/golang.org/x/image/ccitt/table.go @@ -0,0 +1,989 @@ +// generated by "go run gen.go". DO NOT EDIT. + +package ccitt + +// Each decodeTable is represented by an array of [2]int16's: a binary tree. +// Each array element (other than element 0, which means invalid) is a branch +// node in that tree. The root node is always element 1 (the second element). +// +// To walk the tree, look at the next bit in the bit stream, using it to select +// the first or second element of the [2]int16. If that int16 is 0, we have an +// invalid code. If it is positive, go to that branch node. If it is negative, +// then we have a leaf node, whose value is the bitwise complement (the ^ +// operator) of that int16. +// +// Comments above each decodeTable also show the same structure visually. The +// "b123" lines show the 123'rd branch node. The "=XXXXX" lines show an invalid +// code. The "=v1234" lines show a leaf node with value 1234. When reading the +// bit stream, a 0 or 1 bit means to go up or down, as you move left to right. +// +// For example, in modeDecodeTable, branch node b005 is three steps up from the +// root node, meaning that we have already seen "000". If the next bit is "0" +// then we move to branch node b006. Otherwise, the next bit is "1", and we +// move to the leaf node v0000 (also known as the modePass constant). Indeed, +// the bits that encode modePass are "0001". +// +// Tables 1, 2 and 3 come from the "ITU-T Recommendation T.6: FACSIMILE CODING +// SCHEMES AND CODING CONTROL FUNCTIONS FOR GROUP 4 FACSIMILE APPARATUS" +// specification: +// +// https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-T.6-198811-I!!PDF-E&type=items + +// modeDecodeTable represents Table 1 and the End-of-Line code. +// +//                              +=XXXXX +// b015                       +-+ +//                            | +=v0010 +// b014                     +-+ +//                          | +=XXXXX +// b013                   +-+ +//                        | +=XXXXX +// b012                 +-+ +//                      | +=XXXXX +// b011               +-+ +//                    | +=XXXXX +// b009             +-+ +//                  | +=v0009 +// b007           +-+ +//                | | +=v0008 +// b010           | +-+ +//                |   +=v0005 +// b006         +-+ +//              | | +=v0007 +// b008         | +-+ +//              |   +=v0004 +// b005       +-+ +//            | +=v0000 +// b003     +-+ +//          | +=v0001 +// b002   +-+ +//        | | +=v0006 +// b004   | +-+ +//        |   +=v0003 +// b001 +-+ +//        +=v0002 +var modeDecodeTable = [...][2]int16{ +	0:  {0, 0}, +	1:  {2, ^2}, +	2:  {3, 4}, +	3:  {5, ^1}, +	4:  {^6, ^3}, +	5:  {6, ^0}, +	6:  {7, 8}, +	7:  {9, 10}, +	8:  {^7, ^4}, +	9:  {11, ^9}, +	10: {^8, ^5}, +	11: {12, 0}, +	12: {13, 0}, +	13: {14, 0}, +	14: {15, 0}, +	15: {0, ^10}, +} + +// whiteDecodeTable represents Tables 2 and 3 for a white run. +// +//                      +=XXXXX +// b059               +-+ +//                    | |     +=v1792 +// b096               | |   +-+ +//                    | |   | | +=v1984 +// b100               | |   | +-+ +//                    | |   |   +=v2048 +// b094               | | +-+ +//                    | | | |   +=v2112 +// b101               | | | | +-+ +//                    | | | | | +=v2176 +// b097               | | | +-+ +//                    | | |   | +=v2240 +// b102               | | |   +-+ +//                    | | |     +=v2304 +// b085               | +-+ +//                    |   |   +=v1856 +// b098               |   | +-+ +//                    |   | | +=v1920 +// b095               |   +-+ +//                    |     |   +=v2368 +// b103               |     | +-+ +//                    |     | | +=v2432 +// b099               |     +-+ +//                    |       | +=v2496 +// b104               |       +-+ +//                    |         +=v2560 +// b040             +-+ +//                  | | +=v0029 +// b060             | +-+ +//                  |   +=v0030 +// b026           +-+ +//                | |   +=v0045 +// b061           | | +-+ +//                | | | +=v0046 +// b041           | +-+ +//                |   +=v0022 +// b016         +-+ +//              | |   +=v0023 +// b042         | | +-+ +//              | | | | +=v0047 +// b062         | | | +-+ +//              | | |   +=v0048 +// b027         | +-+ +//              |   +=v0013 +// b008       +-+ +//            | |     +=v0020 +// b043       | |   +-+ +//            | |   | | +=v0033 +// b063       | |   | +-+ +//            | |   |   +=v0034 +// b028       | | +-+ +//            | | | |   +=v0035 +// b064       | | | | +-+ +//            | | | | | +=v0036 +// b044       | | | +-+ +//            | | |   | +=v0037 +// b065       | | |   +-+ +//            | | |     +=v0038 +// b017       | +-+ +//            |   |   +=v0019 +// b045       |   | +-+ +//            |   | | | +=v0031 +// b066       |   | | +-+ +//            |   | |   +=v0032 +// b029       |   +-+ +//            |     +=v0001 +// b004     +-+ +//          | |     +=v0012 +// b030     | |   +-+ +//          | |   | |   +=v0053 +// b067     | |   | | +-+ +//          | |   | | | +=v0054 +// b046     | |   | +-+ +//          | |   |   +=v0026 +// b018     | | +-+ +//          | | | |     +=v0039 +// b068     | | | |   +-+ +//          | | | |   | +=v0040 +// b047     | | | | +-+ +//          | | | | | | +=v0041 +// b069     | | | | | +-+ +//          | | | | |   +=v0042 +// b031     | | | +-+ +//          | | |   |   +=v0043 +// b070     | | |   | +-+ +//          | | |   | | +=v0044 +// b048     | | |   +-+ +//          | | |     +=v0021 +// b009     | +-+ +//          |   |     +=v0028 +// b049     |   |   +-+ +//          |   |   | | +=v0061 +// b071     |   |   | +-+ +//          |   |   |   +=v0062 +// b032     |   | +-+ +//          |   | | |   +=v0063 +// b072     |   | | | +-+ +//          |   | | | | +=v0000 +// b050     |   | | +-+ +//          |   | |   | +=v0320 +// b073     |   | |   +-+ +//          |   | |     +=v0384 +// b019     |   +-+ +//          |     +=v0010 +// b002   +-+ +//        | |     +=v0011 +// b020   | |   +-+ +//        | |   | |   +=v0027 +// b051   | |   | | +-+ +//        | |   | | | | +=v0059 +// b074   | |   | | | +-+ +//        | |   | | |   +=v0060 +// b033   | |   | +-+ +//        | |   |   |     +=v1472 +// b086   | |   |   |   +-+ +//        | |   |   |   | +=v1536 +// b075   | |   |   | +-+ +//        | |   |   | | | +=v1600 +// b087   | |   |   | | +-+ +//        | |   |   | |   +=v1728 +// b052   | |   |   +-+ +//        | |   |     +=v0018 +// b010   | | +-+ +//        | | | |     +=v0024 +// b053   | | | |   +-+ +//        | | | |   | | +=v0049 +// b076   | | | |   | +-+ +//        | | | |   |   +=v0050 +// b034   | | | | +-+ +//        | | | | | |   +=v0051 +// b077   | | | | | | +-+ +//        | | | | | | | +=v0052 +// b054   | | | | | +-+ +//        | | | | |   +=v0025 +// b021   | | | +-+ +//        | | |   |     +=v0055 +// b078   | | |   |   +-+ +//        | | |   |   | +=v0056 +// b055   | | |   | +-+ +//        | | |   | | | +=v0057 +// b079   | | |   | | +-+ +//        | | |   | |   +=v0058 +// b035   | | |   +-+ +//        | | |     +=v0192 +// b005   | +-+ +//        |   |     +=v1664 +// b036   |   |   +-+ +//        |   |   | |   +=v0448 +// b080   |   |   | | +-+ +//        |   |   | | | +=v0512 +// b056   |   |   | +-+ +//        |   |   |   |   +=v0704 +// b088   |   |   |   | +-+ +//        |   |   |   | | +=v0768 +// b081   |   |   |   +-+ +//        |   |   |     +=v0640 +// b022   |   | +-+ +//        |   | | |     +=v0576 +// b082   |   | | |   +-+ +//        |   | | |   | | +=v0832 +// b089   |   | | |   | +-+ +//        |   | | |   |   +=v0896 +// b057   |   | | | +-+ +//        |   | | | | |   +=v0960 +// b090   |   | | | | | +-+ +//        |   | | | | | | +=v1024 +// b083   |   | | | | +-+ +//        |   | | | |   | +=v1088 +// b091   |   | | | |   +-+ +//        |   | | | |     +=v1152 +// b037   |   | | +-+ +//        |   | |   |     +=v1216 +// b092   |   | |   |   +-+ +//        |   | |   |   | +=v1280 +// b084   |   | |   | +-+ +//        |   | |   | | | +=v1344 +// b093   |   | |   | | +-+ +//        |   | |   | |   +=v1408 +// b058   |   | |   +-+ +//        |   | |     +=v0256 +// b011   |   +-+ +//        |     +=v0002 +// b001 +-+ +//        |     +=v0003 +// b012   |   +-+ +//        |   | | +=v0128 +// b023   |   | +-+ +//        |   |   +=v0008 +// b006   | +-+ +//        | | |   +=v0009 +// b024   | | | +-+ +//        | | | | | +=v0016 +// b038   | | | | +-+ +//        | | | |   +=v0017 +// b013   | | +-+ +//        | |   +=v0004 +// b003   +-+ +//          |   +=v0005 +// b014     | +-+ +//          | | |   +=v0014 +// b039     | | | +-+ +//          | | | | +=v0015 +// b025     | | +-+ +//          | |   +=v0064 +// b007     +-+ +//            | +=v0006 +// b015       +-+ +//              +=v0007 +var whiteDecodeTable = [...][2]int16{ +	0:   {0, 0}, +	1:   {2, 3}, +	2:   {4, 5}, +	3:   {6, 7}, +	4:   {8, 9}, +	5:   {10, 11}, +	6:   {12, 13}, +	7:   {14, 15}, +	8:   {16, 17}, +	9:   {18, 19}, +	10:  {20, 21}, +	11:  {22, ^2}, +	12:  {^3, 23}, +	13:  {24, ^4}, +	14:  {^5, 25}, +	15:  {^6, ^7}, +	16:  {26, 27}, +	17:  {28, 29}, +	18:  {30, 31}, +	19:  {32, ^10}, +	20:  {^11, 33}, +	21:  {34, 35}, +	22:  {36, 37}, +	23:  {^128, ^8}, +	24:  {^9, 38}, +	25:  {39, ^64}, +	26:  {40, 41}, +	27:  {42, ^13}, +	28:  {43, 44}, +	29:  {45, ^1}, +	30:  {^12, 46}, +	31:  {47, 48}, +	32:  {49, 50}, +	33:  {51, 52}, +	34:  {53, 54}, +	35:  {55, ^192}, +	36:  {^1664, 56}, +	37:  {57, 58}, +	38:  {^16, ^17}, +	39:  {^14, ^15}, +	40:  {59, 60}, +	41:  {61, ^22}, +	42:  {^23, 62}, +	43:  {^20, 63}, +	44:  {64, 65}, +	45:  {^19, 66}, +	46:  {67, ^26}, +	47:  {68, 69}, +	48:  {70, ^21}, +	49:  {^28, 71}, +	50:  {72, 73}, +	51:  {^27, 74}, +	52:  {75, ^18}, +	53:  {^24, 76}, +	54:  {77, ^25}, +	55:  {78, 79}, +	56:  {80, 81}, +	57:  {82, 83}, +	58:  {84, ^256}, +	59:  {0, 85}, +	60:  {^29, ^30}, +	61:  {^45, ^46}, +	62:  {^47, ^48}, +	63:  {^33, ^34}, +	64:  {^35, ^36}, +	65:  {^37, ^38}, +	66:  {^31, ^32}, +	67:  {^53, ^54}, +	68:  {^39, ^40}, +	69:  {^41, ^42}, +	70:  {^43, ^44}, +	71:  {^61, ^62}, +	72:  {^63, ^0}, +	73:  {^320, ^384}, +	74:  {^59, ^60}, +	75:  {86, 87}, +	76:  {^49, ^50}, +	77:  {^51, ^52}, +	78:  {^55, ^56}, +	79:  {^57, ^58}, +	80:  {^448, ^512}, +	81:  {88, ^640}, +	82:  {^576, 89}, +	83:  {90, 91}, +	84:  {92, 93}, +	85:  {94, 95}, +	86:  {^1472, ^1536}, +	87:  {^1600, ^1728}, +	88:  {^704, ^768}, +	89:  {^832, ^896}, +	90:  {^960, ^1024}, +	91:  {^1088, ^1152}, +	92:  {^1216, ^1280}, +	93:  {^1344, ^1408}, +	94:  {96, 97}, +	95:  {98, 99}, +	96:  {^1792, 100}, +	97:  {101, 102}, +	98:  {^1856, ^1920}, +	99:  {103, 104}, +	100: {^1984, ^2048}, +	101: {^2112, ^2176}, +	102: {^2240, ^2304}, +	103: {^2368, ^2432}, +	104: {^2496, ^2560}, +} + +// blackDecodeTable represents Tables 2 and 3 for a black run. +// +//                      +=XXXXX +// b017               +-+ +//                    | |     +=v1792 +// b042               | |   +-+ +//                    | |   | | +=v1984 +// b063               | |   | +-+ +//                    | |   |   +=v2048 +// b029               | | +-+ +//                    | | | |   +=v2112 +// b064               | | | | +-+ +//                    | | | | | +=v2176 +// b043               | | | +-+ +//                    | | |   | +=v2240 +// b065               | | |   +-+ +//                    | | |     +=v2304 +// b022               | +-+ +//                    |   |   +=v1856 +// b044               |   | +-+ +//                    |   | | +=v1920 +// b030               |   +-+ +//                    |     |   +=v2368 +// b066               |     | +-+ +//                    |     | | +=v2432 +// b045               |     +-+ +//                    |       | +=v2496 +// b067               |       +-+ +//                    |         +=v2560 +// b013             +-+ +//                  | |     +=v0018 +// b031             | |   +-+ +//                  | |   | |   +=v0052 +// b068             | |   | | +-+ +//                  | |   | | | | +=v0640 +// b095             | |   | | | +-+ +//                  | |   | | |   +=v0704 +// b046             | |   | +-+ +//                  | |   |   |   +=v0768 +// b096             | |   |   | +-+ +//                  | |   |   | | +=v0832 +// b069             | |   |   +-+ +//                  | |   |     +=v0055 +// b023             | | +-+ +//                  | | | |     +=v0056 +// b070             | | | |   +-+ +//                  | | | |   | | +=v1280 +// b097             | | | |   | +-+ +//                  | | | |   |   +=v1344 +// b047             | | | | +-+ +//                  | | | | | |   +=v1408 +// b098             | | | | | | +-+ +//                  | | | | | | | +=v1472 +// b071             | | | | | +-+ +//                  | | | | |   +=v0059 +// b032             | | | +-+ +//                  | | |   |   +=v0060 +// b072             | | |   | +-+ +//                  | | |   | | | +=v1536 +// b099             | | |   | | +-+ +//                  | | |   | |   +=v1600 +// b048             | | |   +-+ +//                  | | |     +=v0024 +// b018             | +-+ +//                  |   |     +=v0025 +// b049             |   |   +-+ +//                  |   |   | |   +=v1664 +// b100             |   |   | | +-+ +//                  |   |   | | | +=v1728 +// b073             |   |   | +-+ +//                  |   |   |   +=v0320 +// b033             |   | +-+ +//                  |   | | |   +=v0384 +// b074             |   | | | +-+ +//                  |   | | | | +=v0448 +// b050             |   | | +-+ +//                  |   | |   |   +=v0512 +// b101             |   | |   | +-+ +//                  |   | |   | | +=v0576 +// b075             |   | |   +-+ +//                  |   | |     +=v0053 +// b024             |   +-+ +//                  |     |     +=v0054 +// b076             |     |   +-+ +//                  |     |   | | +=v0896 +// b102             |     |   | +-+ +//                  |     |   |   +=v0960 +// b051             |     | +-+ +//                  |     | | |   +=v1024 +// b103             |     | | | +-+ +//                  |     | | | | +=v1088 +// b077             |     | | +-+ +//                  |     | |   | +=v1152 +// b104             |     | |   +-+ +//                  |     | |     +=v1216 +// b034             |     +-+ +//                  |       +=v0064 +// b010           +-+ +//                | |   +=v0013 +// b019           | | +-+ +//                | | | |     +=v0023 +// b052           | | | |   +-+ +//                | | | |   | | +=v0050 +// b078           | | | |   | +-+ +//                | | | |   |   +=v0051 +// b035           | | | | +-+ +//                | | | | | |   +=v0044 +// b079           | | | | | | +-+ +//                | | | | | | | +=v0045 +// b053           | | | | | +-+ +//                | | | | |   | +=v0046 +// b080           | | | | |   +-+ +//                | | | | |     +=v0047 +// b025           | | | +-+ +//                | | |   |     +=v0057 +// b081           | | |   |   +-+ +//                | | |   |   | +=v0058 +// b054           | | |   | +-+ +//                | | |   | | | +=v0061 +// b082           | | |   | | +-+ +//                | | |   | |   +=v0256 +// b036           | | |   +-+ +//                | | |     +=v0016 +// b014           | +-+ +//                |   |     +=v0017 +// b037           |   |   +-+ +//                |   |   | |   +=v0048 +// b083           |   |   | | +-+ +//                |   |   | | | +=v0049 +// b055           |   |   | +-+ +//                |   |   |   | +=v0062 +// b084           |   |   |   +-+ +//                |   |   |     +=v0063 +// b026           |   | +-+ +//                |   | | |     +=v0030 +// b085           |   | | |   +-+ +//                |   | | |   | +=v0031 +// b056           |   | | | +-+ +//                |   | | | | | +=v0032 +// b086           |   | | | | +-+ +//                |   | | | |   +=v0033 +// b038           |   | | +-+ +//                |   | |   |   +=v0040 +// b087           |   | |   | +-+ +//                |   | |   | | +=v0041 +// b057           |   | |   +-+ +//                |   | |     +=v0022 +// b020           |   +-+ +//                |     +=v0014 +// b008         +-+ +//              | |   +=v0010 +// b015         | | +-+ +//              | | | +=v0011 +// b011         | +-+ +//              |   |     +=v0015 +// b027         |   |   +-+ +//              |   |   | |     +=v0128 +// b088         |   |   | |   +-+ +//              |   |   | |   | +=v0192 +// b058         |   |   | | +-+ +//              |   |   | | | | +=v0026 +// b089         |   |   | | | +-+ +//              |   |   | | |   +=v0027 +// b039         |   |   | +-+ +//              |   |   |   |   +=v0028 +// b090         |   |   |   | +-+ +//              |   |   |   | | +=v0029 +// b059         |   |   |   +-+ +//              |   |   |     +=v0019 +// b021         |   | +-+ +//              |   | | |     +=v0020 +// b060         |   | | |   +-+ +//              |   | | |   | | +=v0034 +// b091         |   | | |   | +-+ +//              |   | | |   |   +=v0035 +// b040         |   | | | +-+ +//              |   | | | | |   +=v0036 +// b092         |   | | | | | +-+ +//              |   | | | | | | +=v0037 +// b061         |   | | | | +-+ +//              |   | | | |   | +=v0038 +// b093         |   | | | |   +-+ +//              |   | | | |     +=v0039 +// b028         |   | | +-+ +//              |   | |   |   +=v0021 +// b062         |   | |   | +-+ +//              |   | |   | | | +=v0042 +// b094         |   | |   | | +-+ +//              |   | |   | |   +=v0043 +// b041         |   | |   +-+ +//              |   | |     +=v0000 +// b016         |   +-+ +//              |     +=v0012 +// b006       +-+ +//            | |   +=v0009 +// b012       | | +-+ +//            | | | +=v0008 +// b009       | +-+ +//            |   +=v0007 +// b004     +-+ +//          | | +=v0006 +// b007     | +-+ +//          |   +=v0005 +// b002   +-+ +//        | | +=v0001 +// b005   | +-+ +//        |   +=v0004 +// b001 +-+ +//        | +=v0003 +// b003   +-+ +//          +=v0002 +var blackDecodeTable = [...][2]int16{ +	0:   {0, 0}, +	1:   {2, 3}, +	2:   {4, 5}, +	3:   {^3, ^2}, +	4:   {6, 7}, +	5:   {^1, ^4}, +	6:   {8, 9}, +	7:   {^6, ^5}, +	8:   {10, 11}, +	9:   {12, ^7}, +	10:  {13, 14}, +	11:  {15, 16}, +	12:  {^9, ^8}, +	13:  {17, 18}, +	14:  {19, 20}, +	15:  {^10, ^11}, +	16:  {21, ^12}, +	17:  {0, 22}, +	18:  {23, 24}, +	19:  {^13, 25}, +	20:  {26, ^14}, +	21:  {27, 28}, +	22:  {29, 30}, +	23:  {31, 32}, +	24:  {33, 34}, +	25:  {35, 36}, +	26:  {37, 38}, +	27:  {^15, 39}, +	28:  {40, 41}, +	29:  {42, 43}, +	30:  {44, 45}, +	31:  {^18, 46}, +	32:  {47, 48}, +	33:  {49, 50}, +	34:  {51, ^64}, +	35:  {52, 53}, +	36:  {54, ^16}, +	37:  {^17, 55}, +	38:  {56, 57}, +	39:  {58, 59}, +	40:  {60, 61}, +	41:  {62, ^0}, +	42:  {^1792, 63}, +	43:  {64, 65}, +	44:  {^1856, ^1920}, +	45:  {66, 67}, +	46:  {68, 69}, +	47:  {70, 71}, +	48:  {72, ^24}, +	49:  {^25, 73}, +	50:  {74, 75}, +	51:  {76, 77}, +	52:  {^23, 78}, +	53:  {79, 80}, +	54:  {81, 82}, +	55:  {83, 84}, +	56:  {85, 86}, +	57:  {87, ^22}, +	58:  {88, 89}, +	59:  {90, ^19}, +	60:  {^20, 91}, +	61:  {92, 93}, +	62:  {^21, 94}, +	63:  {^1984, ^2048}, +	64:  {^2112, ^2176}, +	65:  {^2240, ^2304}, +	66:  {^2368, ^2432}, +	67:  {^2496, ^2560}, +	68:  {^52, 95}, +	69:  {96, ^55}, +	70:  {^56, 97}, +	71:  {98, ^59}, +	72:  {^60, 99}, +	73:  {100, ^320}, +	74:  {^384, ^448}, +	75:  {101, ^53}, +	76:  {^54, 102}, +	77:  {103, 104}, +	78:  {^50, ^51}, +	79:  {^44, ^45}, +	80:  {^46, ^47}, +	81:  {^57, ^58}, +	82:  {^61, ^256}, +	83:  {^48, ^49}, +	84:  {^62, ^63}, +	85:  {^30, ^31}, +	86:  {^32, ^33}, +	87:  {^40, ^41}, +	88:  {^128, ^192}, +	89:  {^26, ^27}, +	90:  {^28, ^29}, +	91:  {^34, ^35}, +	92:  {^36, ^37}, +	93:  {^38, ^39}, +	94:  {^42, ^43}, +	95:  {^640, ^704}, +	96:  {^768, ^832}, +	97:  {^1280, ^1344}, +	98:  {^1408, ^1472}, +	99:  {^1536, ^1600}, +	100: {^1664, ^1728}, +	101: {^512, ^576}, +	102: {^896, ^960}, +	103: {^1024, ^1088}, +	104: {^1152, ^1216}, +} + +const maxCodeLength = 13 + +// Each encodeTable is represented by an array of bitStrings. + +// bitString is a pair of uint32 values representing a bit code. +// The nBits low bits of bits make up the actual bit code. +// Eg. bitString{0x0004, 8} represents the bitcode "00000100". +type bitString struct { +	bits  uint32 +	nBits uint32 +} + +// modeEncodeTable represents Table 1 and the End-of-Line code. +var modeEncodeTable = [...]bitString{ +	0:  {0x0001, 4},  // "0001" +	1:  {0x0001, 3},  // "001" +	2:  {0x0001, 1},  // "1" +	3:  {0x0003, 3},  // "011" +	4:  {0x0003, 6},  // "000011" +	5:  {0x0003, 7},  // "0000011" +	6:  {0x0002, 3},  // "010" +	7:  {0x0002, 6},  // "000010" +	8:  {0x0002, 7},  // "0000010" +	9:  {0x0001, 7},  // "0000001" +	10: {0x0001, 12}, // "000000000001" +} + +// whiteEncodeTable2 represents Table 2 for a white run. +var whiteEncodeTable2 = [...]bitString{ +	0:  {0x0035, 8}, // "00110101" +	1:  {0x0007, 6}, // "000111" +	2:  {0x0007, 4}, // "0111" +	3:  {0x0008, 4}, // "1000" +	4:  {0x000b, 4}, // "1011" +	5:  {0x000c, 4}, // "1100" +	6:  {0x000e, 4}, // "1110" +	7:  {0x000f, 4}, // "1111" +	8:  {0x0013, 5}, // "10011" +	9:  {0x0014, 5}, // "10100" +	10: {0x0007, 5}, // "00111" +	11: {0x0008, 5}, // "01000" +	12: {0x0008, 6}, // "001000" +	13: {0x0003, 6}, // "000011" +	14: {0x0034, 6}, // "110100" +	15: {0x0035, 6}, // "110101" +	16: {0x002a, 6}, // "101010" +	17: {0x002b, 6}, // "101011" +	18: {0x0027, 7}, // "0100111" +	19: {0x000c, 7}, // "0001100" +	20: {0x0008, 7}, // "0001000" +	21: {0x0017, 7}, // "0010111" +	22: {0x0003, 7}, // "0000011" +	23: {0x0004, 7}, // "0000100" +	24: {0x0028, 7}, // "0101000" +	25: {0x002b, 7}, // "0101011" +	26: {0x0013, 7}, // "0010011" +	27: {0x0024, 7}, // "0100100" +	28: {0x0018, 7}, // "0011000" +	29: {0x0002, 8}, // "00000010" +	30: {0x0003, 8}, // "00000011" +	31: {0x001a, 8}, // "00011010" +	32: {0x001b, 8}, // "00011011" +	33: {0x0012, 8}, // "00010010" +	34: {0x0013, 8}, // "00010011" +	35: {0x0014, 8}, // "00010100" +	36: {0x0015, 8}, // "00010101" +	37: {0x0016, 8}, // "00010110" +	38: {0x0017, 8}, // "00010111" +	39: {0x0028, 8}, // "00101000" +	40: {0x0029, 8}, // "00101001" +	41: {0x002a, 8}, // "00101010" +	42: {0x002b, 8}, // "00101011" +	43: {0x002c, 8}, // "00101100" +	44: {0x002d, 8}, // "00101101" +	45: {0x0004, 8}, // "00000100" +	46: {0x0005, 8}, // "00000101" +	47: {0x000a, 8}, // "00001010" +	48: {0x000b, 8}, // "00001011" +	49: {0x0052, 8}, // "01010010" +	50: {0x0053, 8}, // "01010011" +	51: {0x0054, 8}, // "01010100" +	52: {0x0055, 8}, // "01010101" +	53: {0x0024, 8}, // "00100100" +	54: {0x0025, 8}, // "00100101" +	55: {0x0058, 8}, // "01011000" +	56: {0x0059, 8}, // "01011001" +	57: {0x005a, 8}, // "01011010" +	58: {0x005b, 8}, // "01011011" +	59: {0x004a, 8}, // "01001010" +	60: {0x004b, 8}, // "01001011" +	61: {0x0032, 8}, // "00110010" +	62: {0x0033, 8}, // "00110011" +	63: {0x0034, 8}, // "00110100" +} + +// whiteEncodeTable3 represents Table 3 for a white run. +var whiteEncodeTable3 = [...]bitString{ +	0:  {0x001b, 5},  // "11011" +	1:  {0x0012, 5},  // "10010" +	2:  {0x0017, 6},  // "010111" +	3:  {0x0037, 7},  // "0110111" +	4:  {0x0036, 8},  // "00110110" +	5:  {0x0037, 8},  // "00110111" +	6:  {0x0064, 8},  // "01100100" +	7:  {0x0065, 8},  // "01100101" +	8:  {0x0068, 8},  // "01101000" +	9:  {0x0067, 8},  // "01100111" +	10: {0x00cc, 9},  // "011001100" +	11: {0x00cd, 9},  // "011001101" +	12: {0x00d2, 9},  // "011010010" +	13: {0x00d3, 9},  // "011010011" +	14: {0x00d4, 9},  // "011010100" +	15: {0x00d5, 9},  // "011010101" +	16: {0x00d6, 9},  // "011010110" +	17: {0x00d7, 9},  // "011010111" +	18: {0x00d8, 9},  // "011011000" +	19: {0x00d9, 9},  // "011011001" +	20: {0x00da, 9},  // "011011010" +	21: {0x00db, 9},  // "011011011" +	22: {0x0098, 9},  // "010011000" +	23: {0x0099, 9},  // "010011001" +	24: {0x009a, 9},  // "010011010" +	25: {0x0018, 6},  // "011000" +	26: {0x009b, 9},  // "010011011" +	27: {0x0008, 11}, // "00000001000" +	28: {0x000c, 11}, // "00000001100" +	29: {0x000d, 11}, // "00000001101" +	30: {0x0012, 12}, // "000000010010" +	31: {0x0013, 12}, // "000000010011" +	32: {0x0014, 12}, // "000000010100" +	33: {0x0015, 12}, // "000000010101" +	34: {0x0016, 12}, // "000000010110" +	35: {0x0017, 12}, // "000000010111" +	36: {0x001c, 12}, // "000000011100" +	37: {0x001d, 12}, // "000000011101" +	38: {0x001e, 12}, // "000000011110" +	39: {0x001f, 12}, // "000000011111" +} + +// blackEncodeTable2 represents Table 2 for a black run. +var blackEncodeTable2 = [...]bitString{ +	0:  {0x0037, 10}, // "0000110111" +	1:  {0x0002, 3},  // "010" +	2:  {0x0003, 2},  // "11" +	3:  {0x0002, 2},  // "10" +	4:  {0x0003, 3},  // "011" +	5:  {0x0003, 4},  // "0011" +	6:  {0x0002, 4},  // "0010" +	7:  {0x0003, 5},  // "00011" +	8:  {0x0005, 6},  // "000101" +	9:  {0x0004, 6},  // "000100" +	10: {0x0004, 7},  // "0000100" +	11: {0x0005, 7},  // "0000101" +	12: {0x0007, 7},  // "0000111" +	13: {0x0004, 8},  // "00000100" +	14: {0x0007, 8},  // "00000111" +	15: {0x0018, 9},  // "000011000" +	16: {0x0017, 10}, // "0000010111" +	17: {0x0018, 10}, // "0000011000" +	18: {0x0008, 10}, // "0000001000" +	19: {0x0067, 11}, // "00001100111" +	20: {0x0068, 11}, // "00001101000" +	21: {0x006c, 11}, // "00001101100" +	22: {0x0037, 11}, // "00000110111" +	23: {0x0028, 11}, // "00000101000" +	24: {0x0017, 11}, // "00000010111" +	25: {0x0018, 11}, // "00000011000" +	26: {0x00ca, 12}, // "000011001010" +	27: {0x00cb, 12}, // "000011001011" +	28: {0x00cc, 12}, // "000011001100" +	29: {0x00cd, 12}, // "000011001101" +	30: {0x0068, 12}, // "000001101000" +	31: {0x0069, 12}, // "000001101001" +	32: {0x006a, 12}, // "000001101010" +	33: {0x006b, 12}, // "000001101011" +	34: {0x00d2, 12}, // "000011010010" +	35: {0x00d3, 12}, // "000011010011" +	36: {0x00d4, 12}, // "000011010100" +	37: {0x00d5, 12}, // "000011010101" +	38: {0x00d6, 12}, // "000011010110" +	39: {0x00d7, 12}, // "000011010111" +	40: {0x006c, 12}, // "000001101100" +	41: {0x006d, 12}, // "000001101101" +	42: {0x00da, 12}, // "000011011010" +	43: {0x00db, 12}, // "000011011011" +	44: {0x0054, 12}, // "000001010100" +	45: {0x0055, 12}, // "000001010101" +	46: {0x0056, 12}, // "000001010110" +	47: {0x0057, 12}, // "000001010111" +	48: {0x0064, 12}, // "000001100100" +	49: {0x0065, 12}, // "000001100101" +	50: {0x0052, 12}, // "000001010010" +	51: {0x0053, 12}, // "000001010011" +	52: {0x0024, 12}, // "000000100100" +	53: {0x0037, 12}, // "000000110111" +	54: {0x0038, 12}, // "000000111000" +	55: {0x0027, 12}, // "000000100111" +	56: {0x0028, 12}, // "000000101000" +	57: {0x0058, 12}, // "000001011000" +	58: {0x0059, 12}, // "000001011001" +	59: {0x002b, 12}, // "000000101011" +	60: {0x002c, 12}, // "000000101100" +	61: {0x005a, 12}, // "000001011010" +	62: {0x0066, 12}, // "000001100110" +	63: {0x0067, 12}, // "000001100111" +} + +// blackEncodeTable3 represents Table 3 for a black run. +var blackEncodeTable3 = [...]bitString{ +	0:  {0x000f, 10}, // "0000001111" +	1:  {0x00c8, 12}, // "000011001000" +	2:  {0x00c9, 12}, // "000011001001" +	3:  {0x005b, 12}, // "000001011011" +	4:  {0x0033, 12}, // "000000110011" +	5:  {0x0034, 12}, // "000000110100" +	6:  {0x0035, 12}, // "000000110101" +	7:  {0x006c, 13}, // "0000001101100" +	8:  {0x006d, 13}, // "0000001101101" +	9:  {0x004a, 13}, // "0000001001010" +	10: {0x004b, 13}, // "0000001001011" +	11: {0x004c, 13}, // "0000001001100" +	12: {0x004d, 13}, // "0000001001101" +	13: {0x0072, 13}, // "0000001110010" +	14: {0x0073, 13}, // "0000001110011" +	15: {0x0074, 13}, // "0000001110100" +	16: {0x0075, 13}, // "0000001110101" +	17: {0x0076, 13}, // "0000001110110" +	18: {0x0077, 13}, // "0000001110111" +	19: {0x0052, 13}, // "0000001010010" +	20: {0x0053, 13}, // "0000001010011" +	21: {0x0054, 13}, // "0000001010100" +	22: {0x0055, 13}, // "0000001010101" +	23: {0x005a, 13}, // "0000001011010" +	24: {0x005b, 13}, // "0000001011011" +	25: {0x0064, 13}, // "0000001100100" +	26: {0x0065, 13}, // "0000001100101" +	27: {0x0008, 11}, // "00000001000" +	28: {0x000c, 11}, // "00000001100" +	29: {0x000d, 11}, // "00000001101" +	30: {0x0012, 12}, // "000000010010" +	31: {0x0013, 12}, // "000000010011" +	32: {0x0014, 12}, // "000000010100" +	33: {0x0015, 12}, // "000000010101" +	34: {0x0016, 12}, // "000000010110" +	35: {0x0017, 12}, // "000000010111" +	36: {0x001c, 12}, // "000000011100" +	37: {0x001d, 12}, // "000000011101" +	38: {0x001e, 12}, // "000000011110" +	39: {0x001f, 12}, // "000000011111" +} + +// COPY PASTE table.go BEGIN + +const ( +	modePass = iota // Pass +	modeH           // Horizontal +	modeV0          // Vertical-0 +	modeVR1         // Vertical-Right-1 +	modeVR2         // Vertical-Right-2 +	modeVR3         // Vertical-Right-3 +	modeVL1         // Vertical-Left-1 +	modeVL2         // Vertical-Left-2 +	modeVL3         // Vertical-Left-3 +	modeExt         // Extension +	modeEOL         // End-of-Line +) + +// COPY PASTE table.go END diff --git a/vendor/golang.org/x/image/ccitt/writer.go b/vendor/golang.org/x/image/ccitt/writer.go new file mode 100644 index 000000000..87130ab04 --- /dev/null +++ b/vendor/golang.org/x/image/ccitt/writer.go @@ -0,0 +1,102 @@ +// Copyright 2019 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 ccitt + +import ( +	"encoding/binary" +	"io" +) + +type bitWriter struct { +	w io.Writer + +	// order is whether to process w's bytes LSB first or MSB first. +	order Order + +	// The high nBits bits of the bits field hold encoded bits to be written to w. +	bits  uint64 +	nBits uint32 + +	// bytes[:bw] holds encoded bytes not yet written to w. +	// Overflow protection is ensured by using a multiple of 8 as bytes length. +	bw    uint32 +	bytes [1024]uint8 +} + +// flushBits copies 64 bits from b.bits to b.bytes. If b.bytes is then full, it +// is written to b.w. +func (b *bitWriter) flushBits() error { +	binary.BigEndian.PutUint64(b.bytes[b.bw:], b.bits) +	b.bits = 0 +	b.nBits = 0 +	b.bw += 8 +	if b.bw < uint32(len(b.bytes)) { +		return nil +	} +	b.bw = 0 +	if b.order != MSB { +		reverseBitsWithinBytes(b.bytes[:]) +	} +	_, err := b.w.Write(b.bytes[:]) +	return err +} + +// close finalizes a bitcode stream by writing any +// pending bits to bitWriter's underlying io.Writer. +func (b *bitWriter) close() error { +	// Write any encoded bits to bytes. +	if b.nBits > 0 { +		binary.BigEndian.PutUint64(b.bytes[b.bw:], b.bits) +		b.bw += (b.nBits + 7) >> 3 +	} + +	if b.order != MSB { +		reverseBitsWithinBytes(b.bytes[:b.bw]) +	} + +	// Write b.bw bytes to b.w. +	_, err := b.w.Write(b.bytes[:b.bw]) +	return err +} + +// alignToByteBoundary rounds b.nBits up to a multiple of 8. +// If all 64 bits are used, flush them to bitWriter's bytes. +func (b *bitWriter) alignToByteBoundary() error { +	if b.nBits = (b.nBits + 7) &^ 7; b.nBits == 64 { +		return b.flushBits() +	} +	return nil +} + +// writeCode writes a variable length bitcode to b's underlying io.Writer. +func (b *bitWriter) writeCode(bs bitString) error { +	bits := bs.bits +	nBits := bs.nBits +	if 64-b.nBits >= nBits { +		// b.bits has sufficient room for storing nBits bits. +		b.bits |= uint64(bits) << (64 - nBits - b.nBits) +		b.nBits += nBits +		if b.nBits == 64 { +			return b.flushBits() +		} +		return nil +	} + +	// Number of leading bits that fill b.bits. +	i := 64 - b.nBits + +	// Fill b.bits then flush and write remaining bits. +	b.bits |= uint64(bits) >> (nBits - i) +	b.nBits = 64 + +	if err := b.flushBits(); err != nil { +		return err +	} + +	nBits -= i +	b.bits = uint64(bits) << (64 - nBits) +	b.nBits = nBits +	return nil +} diff --git a/vendor/golang.org/x/image/tiff/buffer.go b/vendor/golang.org/x/image/tiff/buffer.go new file mode 100644 index 000000000..d1801be48 --- /dev/null +++ b/vendor/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/vendor/golang.org/x/image/tiff/compress.go b/vendor/golang.org/x/image/tiff/compress.go new file mode 100644 index 000000000..3f176f00a --- /dev/null +++ b/vendor/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/vendor/golang.org/x/image/tiff/consts.go b/vendor/golang.org/x/image/tiff/consts.go new file mode 100644 index 000000000..3e5f7f14d --- /dev/null +++ b/vendor/golang.org/x/image/tiff/consts.go @@ -0,0 +1,149 @@ +// 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 + +	tFillOrder = 266 + +	tStripOffsets    = 273 +	tSamplesPerPixel = 277 +	tRowsPerStrip    = 278 +	tStripByteCounts = 279 + +	tT4Options = 292 // CCITT Group 3 options, a set of 32 flag bits. +	tT6Options = 293 // CCITT Group 4 options, a set of 32 flag bits. + +	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 +	mCMYK +) + +// CompressionType describes the type of compression used in Options. +type CompressionType int + +// Constants for supported compression types. +const ( +	Uncompressed CompressionType = iota +	Deflate +	LZW +	CCITTGroup3 +	CCITTGroup4 +) + +// specValue returns the compression type constant from the TIFF spec that +// is equivalent to c. +func (c CompressionType) specValue() uint32 { +	switch c { +	case LZW: +		return cLZW +	case Deflate: +		return cDeflate +	case CCITTGroup3: +		return cG3 +	case CCITTGroup4: +		return cG4 +	} +	return cNone +} diff --git a/vendor/golang.org/x/image/tiff/fuzz.go b/vendor/golang.org/x/image/tiff/fuzz.go new file mode 100644 index 000000000..ec52c7882 --- /dev/null +++ b/vendor/golang.org/x/image/tiff/fuzz.go @@ -0,0 +1,29 @@ +// Copyright 2019 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. + +// +build gofuzz + +package tiff + +import "bytes" + +func Fuzz(data []byte) int { +	cfg, err := DecodeConfig(bytes.NewReader(data)) +	if err != nil { +		return 0 +	} +	if cfg.Width*cfg.Height > 1e6 { +		return 0 +	} +	img, err := Decode(bytes.NewReader(data)) +	if err != nil { +		return 0 +	} +	var w bytes.Buffer +	err = Encode(&w, img, nil) +	if err != nil { +		panic(err) +	} +	return 1 +} diff --git a/vendor/golang.org/x/image/tiff/lzw/reader.go b/vendor/golang.org/x/image/tiff/lzw/reader.go new file mode 100644 index 000000000..78204ba92 --- /dev/null +++ b/vendor/golang.org/x/image/tiff/lzw/reader.go @@ -0,0 +1,272 @@ +// 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 // import "golang.org/x/image/tiff/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. +loop: +	for { +		code, err := d.read(d) +		if err != nil { +			if err == io.EOF { +				err = io.ErrUnexpectedEOF +			} +			d.err = err +			break +		} +		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.err = io.EOF +			break loop +		case code <= d.hi: +			c, i := code, len(d.output)-1 +			if code == d.hi && d.last != decoderInvalidCode { +				// 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") +			break loop +		} +		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 { +			break +		} +	} +	// Flush pending output. +	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/vendor/golang.org/x/image/tiff/reader.go b/vendor/golang.org/x/image/tiff/reader.go new file mode 100644 index 000000000..c26ec36bb --- /dev/null +++ b/vendor/golang.org/x/image/tiff/reader.go @@ -0,0 +1,706 @@ +// 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 "golang.org/x/image/tiff" + +import ( +	"compress/zlib" +	"encoding/binary" +	"fmt" +	"image" +	"image/color" +	"io" +	"io/ioutil" +	"math" + +	"golang.org/x/image/ccitt" +	"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) +} + +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 IFD entry in p is "interesting" and +// stows away the data in the decoder. It returns the tag number of the +// entry and an error, if any. +func (d *decoder) parseIFD(p []byte) (int, 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, +		tFillOrder, +		tT4Options, +		tT6Options: +		val, err := d.ifdUint(p) +		if err != nil { +			return 0, err +		} +		d.features[int(tag)] = val +	case tColorMap: +		val, err := d.ifdUint(p) +		if err != nil { +			return 0, err +		} +		numcolors := len(val) / 3 +		if len(val)%3 != 0 || numcolors <= 0 || numcolors > 256 { +			return 0, 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 0, err +		} +		for _, v := range val { +			if v != 1 { +				return 0, UnsupportedError("sample format") +			} +		} +	} +	return int(tag), 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}) +				} +				if rMaxX == img.Bounds().Max.X { +					d.off += 2 * (xmax - img.Bounds().Max.X) +				} +			} +		} 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 +	} + +	prevTag := -1 +	for i := 0; i < len(p); i += ifdLen { +		tag, err := d.parseIFD(p[i : i+ifdLen]) +		if err != nil { +			return nil, err +		} +		if tag <= prevTag { +			return nil, FormatError("tags are not sorted in ascending order") +		} +		prevTag = tag +	} + +	d.config.Width = int(d.firstVal(tImageWidth)) +	d.config.Height = int(d.firstVal(tImageLength)) + +	if _, ok := d.features[tBitsPerSample]; !ok { +		// Default is 1 per specification. +		d.features[tBitsPerSample] = []uint{1} +	} +	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 +} + +func ccittFillOrder(tiffFillOrder uint) ccitt.Order { +	if tiffFillOrder == 2 { +		return ccitt.LSB +	} +	return ccitt.MSB +} + +// 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 cG3: +				inv := d.firstVal(tPhotometricInterpretation) == pWhiteIsZero +				order := ccittFillOrder(d.firstVal(tFillOrder)) +				r := ccitt.NewReader(io.NewSectionReader(d.r, offset, n), order, ccitt.Group3, blkW, blkH, &ccitt.Options{Invert: inv, Align: false}) +				d.buf, err = ioutil.ReadAll(r) +			case cG4: +				inv := d.firstVal(tPhotometricInterpretation) == pWhiteIsZero +				order := ccittFillOrder(d.firstVal(tFillOrder)) +				r := ccitt.NewReader(io.NewSectionReader(d.r, offset, n), order, ccitt.Group4, blkW, blkH, &ccitt.Options{Invert: inv, Align: false}) +				d.buf, err = ioutil.ReadAll(r) +			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/vendor/golang.org/x/image/tiff/writer.go b/vendor/golang.org/x/image/tiff/writer.go new file mode 100644 index 000000000..c8a01cea7 --- /dev/null +++ b/vendor/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/vendor/modules.txt b/vendor/modules.txt index 6004f1c8a..a59777756 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -69,6 +69,9 @@ github.com/coreos/go-oidc/v3/oidc  # github.com/davecgh/go-spew v1.1.1  ## explicit  github.com/davecgh/go-spew/spew +# github.com/disintegration/imaging v1.6.2 +## explicit +github.com/disintegration/imaging  # github.com/dsoprea/go-exif/v3 v3.0.0-20210625224831-a6301f85c82b  ## explicit; go 1.12  github.com/dsoprea/go-exif/v3 @@ -282,7 +285,6 @@ github.com/modern-go/concurrent  github.com/modern-go/reflect2  # github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646  ## explicit -github.com/nfnt/resize  # github.com/oklog/ulid v1.3.1  ## explicit  github.com/oklog/ulid @@ -617,6 +619,12 @@ golang.org/x/crypto/ssh/internal/bcrypt_pbkdf  ## explicit; go 1.18  golang.org/x/exp/constraints  golang.org/x/exp/slices +# golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 +## explicit; go 1.12 +golang.org/x/image/bmp +golang.org/x/image/ccitt +golang.org/x/image/tiff +golang.org/x/image/tiff/lzw  # golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4  ## explicit; go 1.17  golang.org/x/mod/semver  | 
