mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 12:02:26 -05:00 
			
		
		
		
	[performance] use single-threaded image transforms (#3252)
* use single-threaded image resizing in native code so we have more control over goroutines * implement parallel-free versions of image transform functions also * remove debug code
This commit is contained in:
		
					parent
					
						
							
								88a81fbcaf
							
						
					
				
			
			
				commit
				
					
						0a1555521d
					
				
			
		
					 34 changed files with 653 additions and 7130 deletions
				
			
		
							
								
								
									
										1
									
								
								go.mod
									
										
									
									
									
								
							
							
						
						
									
										1
									
								
								go.mod
									
										
									
									
									
								
							|  | @ -29,7 +29,6 @@ require ( | ||||||
| 	github.com/KimMachineGun/automemlimit v0.6.1 | 	github.com/KimMachineGun/automemlimit v0.6.1 | ||||||
| 	github.com/buckket/go-blurhash v1.1.0 | 	github.com/buckket/go-blurhash v1.1.0 | ||||||
| 	github.com/coreos/go-oidc/v3 v3.11.0 | 	github.com/coreos/go-oidc/v3 v3.11.0 | ||||||
| 	github.com/disintegration/imaging v1.6.2 |  | ||||||
| 	github.com/gin-contrib/cors v1.7.2 | 	github.com/gin-contrib/cors v1.7.2 | ||||||
| 	github.com/gin-contrib/gzip v1.0.1 | 	github.com/gin-contrib/gzip v1.0.1 | ||||||
| 	github.com/gin-contrib/sessions v1.0.1 | 	github.com/gin-contrib/sessions v1.0.1 | ||||||
|  |  | ||||||
							
								
								
									
										3
									
								
								go.sum
									
										
									
									
									
								
							
							
						
						
									
										3
									
								
								go.sum
									
										
									
									
									
								
							|  | @ -145,8 +145,6 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs | ||||||
| github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||||
| github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= | ||||||
| github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/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/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= | github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= | ||||||
| github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= | github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= | ||||||
| github.com/dsoprea/go-exif/v2 v2.0.0-20200321225314-640175a69fe4/go.mod h1:Lm2lMM2zx8p4a34ZemkaUV95AnMl4ZvLbCUbwOvLC2E= | github.com/dsoprea/go-exif/v2 v2.0.0-20200321225314-640175a69fe4/go.mod h1:Lm2lMM2zx8p4a34ZemkaUV95AnMl4ZvLbCUbwOvLC2E= | ||||||
|  | @ -692,7 +690,6 @@ golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUF | ||||||
| golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= | golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= | ||||||
| golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= | 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-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= | ||||||
| golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= |  | ||||||
| golang.org/x/image v0.19.0 h1:D9FX4QWkLfkeqaC62SonffIIuYdOk/UE2XKUBgRIBIQ= | golang.org/x/image v0.19.0 h1:D9FX4QWkLfkeqaC62SonffIIuYdOk/UE2XKUBgRIBIQ= | ||||||
| golang.org/x/image v0.19.0/go.mod h1:y0zrRqlQRWQ5PXaYCOMLTW2fpsxZ8Qh9I/ohnInJEys= | golang.org/x/image v0.19.0/go.mod h1:y0zrRqlQRWQ5PXaYCOMLTW2fpsxZ8Qh9I/ohnInJEys= | ||||||
| golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= | ||||||
|  |  | ||||||
|  | @ -858,7 +858,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch8() { | ||||||
|   "static_url": "http://localhost:8080/fileserver/01AY6P665V14JJR0AFVRT7311Y/attachment/small/`+instanceAccount.AvatarMediaAttachment.ID+`.webp",`+` |   "static_url": "http://localhost:8080/fileserver/01AY6P665V14JJR0AFVRT7311Y/attachment/small/`+instanceAccount.AvatarMediaAttachment.ID+`.webp",`+` | ||||||
|   "thumbnail_static_type": "image/webp", |   "thumbnail_static_type": "image/webp", | ||||||
|   "thumbnail_description": "A bouncing little green peglin.", |   "thumbnail_description": "A bouncing little green peglin.", | ||||||
|   "blurhash": "LF9kG$RR4YtP%dR+V^t5D,oxx?WC" |   "blurhash": "LE9801Rl4Yt5%dWCV]t5Dmoex?WC" | ||||||
| }`, string(instanceV2ThumbnailJson)) | }`, string(instanceV2ThumbnailJson)) | ||||||
| 
 | 
 | ||||||
| 	// double extra special bonus: now update the image description without changing the image | 	// double extra special bonus: now update the image description without changing the image | ||||||
|  |  | ||||||
|  | @ -206,7 +206,7 @@ func (suite *MediaCreateTestSuite) TestMediaCreateSuccessful() { | ||||||
| 			Y: 0.5, | 			Y: 0.5, | ||||||
| 		}, | 		}, | ||||||
| 	}, *attachmentReply.Meta) | 	}, *attachmentReply.Meta) | ||||||
| 	suite.Equal("LiBzRk#6V[WF_NvzV@WY_3rqV@a$", *attachmentReply.Blurhash) | 	suite.Equal("LiB|W-#6RQR.~qvzRjWF_3rqV@a$", *attachmentReply.Blurhash) | ||||||
| 	suite.NotEmpty(attachmentReply.ID) | 	suite.NotEmpty(attachmentReply.ID) | ||||||
| 	suite.NotEmpty(attachmentReply.URL) | 	suite.NotEmpty(attachmentReply.URL) | ||||||
| 	suite.NotEmpty(attachmentReply.PreviewURL) | 	suite.NotEmpty(attachmentReply.PreviewURL) | ||||||
|  | @ -291,7 +291,7 @@ func (suite *MediaCreateTestSuite) TestMediaCreateSuccessfulV2() { | ||||||
| 			Y: 0.5, | 			Y: 0.5, | ||||||
| 		}, | 		}, | ||||||
| 	}, *attachmentReply.Meta) | 	}, *attachmentReply.Meta) | ||||||
| 	suite.Equal("LiBzRk#6V[WF_NvzV@WY_3rqV@a$", *attachmentReply.Blurhash) | 	suite.Equal("LiB|W-#6RQR.~qvzRjWF_3rqV@a$", *attachmentReply.Blurhash) | ||||||
| 	suite.NotEmpty(attachmentReply.ID) | 	suite.NotEmpty(attachmentReply.ID) | ||||||
| 	suite.Nil(attachmentReply.URL) | 	suite.Nil(attachmentReply.URL) | ||||||
| 	suite.NotEmpty(attachmentReply.PreviewURL) | 	suite.NotEmpty(attachmentReply.PreviewURL) | ||||||
|  |  | ||||||
							
								
								
									
										623
									
								
								internal/media/imaging.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										623
									
								
								internal/media/imaging.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,623 @@ | ||||||
|  | // GoToSocial | ||||||
|  | // Copyright (C) GoToSocial Authors admin@gotosocial.org | ||||||
|  | // SPDX-License-Identifier: AGPL-3.0-or-later | ||||||
|  | // | ||||||
|  | // This program is free software: you can redistribute it and/or modify | ||||||
|  | // it under the terms of the GNU Affero General Public License as published by | ||||||
|  | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  | // (at your option) any later version. | ||||||
|  | // | ||||||
|  | // This program is distributed in the hope that it will be useful, | ||||||
|  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | // GNU Affero General Public License for more details. | ||||||
|  | // | ||||||
|  | // You should have received a copy of the GNU Affero General Public License | ||||||
|  | // along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  | 
 | ||||||
|  | package media | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"image" | ||||||
|  | 	"image/color" | ||||||
|  | 	"math" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // NOTE: | ||||||
|  | // the following code is borrowed from | ||||||
|  | // github.com/disintegration/imaging | ||||||
|  | // and collapses in some places for our | ||||||
|  | // particular usecases and with parallel() | ||||||
|  | // function (spans work across goroutines) | ||||||
|  | // removed, instead working synchronously. | ||||||
|  | // | ||||||
|  | // at gotosocial we take particular | ||||||
|  | // care about where we spawn goroutines | ||||||
|  | // to ensure we're in control of the | ||||||
|  | // amount of concurrency in relation | ||||||
|  | // to the amount configured by user. | ||||||
|  | 
 | ||||||
|  | // resizeDownLinear resizes image to given width x height using linear resampling. | ||||||
|  | // This is specifically optimized for resizing down (i.e. smaller), else is noop. | ||||||
|  | func resizeDownLinear(img image.Image, width, height int) image.Image { | ||||||
|  | 	srcW, srcH := img.Bounds().Dx(), img.Bounds().Dy() | ||||||
|  | 	if srcW <= 0 || srcH <= 0 || | ||||||
|  | 		width < 0 || height < 0 { | ||||||
|  | 		return &image.NRGBA{} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if width == 0 { | ||||||
|  | 		// If no width is given, use aspect preserving width. | ||||||
|  | 		tmp := float64(height) * float64(srcW) / float64(srcH) | ||||||
|  | 		width = int(math.Max(1.0, math.Floor(tmp+0.5))) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if height == 0 { | ||||||
|  | 		// If no height is given, use aspect preserving height. | ||||||
|  | 		tmp := float64(width) * float64(srcH) / float64(srcW) | ||||||
|  | 		height = int(math.Max(1.0, math.Floor(tmp+0.5))) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if width < srcW { | ||||||
|  | 		// Width is smaller, resize horizontally. | ||||||
|  | 		img = resizeHorizontalLinear(img, width) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if height < srcH { | ||||||
|  | 		// Height is smaller, resize vertically. | ||||||
|  | 		img = resizeVerticalLinear(img, height) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return img | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // flipH flips the image horizontally (left to right). | ||||||
|  | func flipH(img image.Image) image.Image { | ||||||
|  | 	src := newScanner(img) | ||||||
|  | 	dstW := src.w | ||||||
|  | 	dstH := src.h | ||||||
|  | 	rowSize := dstW * 4 | ||||||
|  | 	dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH)) | ||||||
|  | 	for y := 0; y < dstH; y++ { | ||||||
|  | 		i := y * dst.Stride | ||||||
|  | 		srcY := y | ||||||
|  | 		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). | ||||||
|  | func flipV(img image.Image) image.Image { | ||||||
|  | 	src := newScanner(img) | ||||||
|  | 	dstW := src.w | ||||||
|  | 	dstH := src.h | ||||||
|  | 	rowSize := dstW * 4 | ||||||
|  | 	dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH)) | ||||||
|  | 	for y := 0; y < dstH; y++ { | ||||||
|  | 		i := y * dst.Stride | ||||||
|  | 		srcY := dstH - y - 1 | ||||||
|  | 		src.scan(0, srcY, src.w, srcY+1, dst.Pix[i:i+rowSize]) | ||||||
|  | 	} | ||||||
|  | 	return dst | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // rotate90 rotates the image 90 counter-clockwise. | ||||||
|  | func rotate90(img image.Image) image.Image { | ||||||
|  | 	src := newScanner(img) | ||||||
|  | 	dstW := src.h | ||||||
|  | 	dstH := src.w | ||||||
|  | 	rowSize := dstW * 4 | ||||||
|  | 	dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH)) | ||||||
|  | 	for y := 0; y < dstH; y++ { | ||||||
|  | 		i := y * dst.Stride | ||||||
|  | 		srcX := dstH - y - 1 | ||||||
|  | 		src.scan(srcX, 0, srcX+1, src.h, dst.Pix[i:i+rowSize]) | ||||||
|  | 	} | ||||||
|  | 	return dst | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // rotate180 rotates the image 180 counter-clockwise. | ||||||
|  | func rotate180(img image.Image) image.Image { | ||||||
|  | 	src := newScanner(img) | ||||||
|  | 	dstW := src.w | ||||||
|  | 	dstH := src.h | ||||||
|  | 	rowSize := dstW * 4 | ||||||
|  | 	dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH)) | ||||||
|  | 	for y := 0; y < dstH; y++ { | ||||||
|  | 		i := y * dst.Stride | ||||||
|  | 		srcY := dstH - y - 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 counter-clockwise. | ||||||
|  | func rotate270(img image.Image) image.Image { | ||||||
|  | 	src := newScanner(img) | ||||||
|  | 	dstW := src.h | ||||||
|  | 	dstH := src.w | ||||||
|  | 	rowSize := dstW * 4 | ||||||
|  | 	dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH)) | ||||||
|  | 	for y := 0; y < dstH; y++ { | ||||||
|  | 		i := y * dst.Stride | ||||||
|  | 		srcX := y | ||||||
|  | 		src.scan(srcX, 0, srcX+1, src.h, dst.Pix[i:i+rowSize]) | ||||||
|  | 		reverse(dst.Pix[i : i+rowSize]) | ||||||
|  | 	} | ||||||
|  | 	return dst | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // transpose flips the image horizontally and rotates 90 counter-clockwise. | ||||||
|  | func transpose(img image.Image) image.Image { | ||||||
|  | 	src := newScanner(img) | ||||||
|  | 	dstW := src.h | ||||||
|  | 	dstH := src.w | ||||||
|  | 	rowSize := dstW * 4 | ||||||
|  | 	dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH)) | ||||||
|  | 	for y := 0; y < dstH; y++ { | ||||||
|  | 		i := y * dst.Stride | ||||||
|  | 		srcX := y | ||||||
|  | 		src.scan(srcX, 0, srcX+1, src.h, dst.Pix[i:i+rowSize]) | ||||||
|  | 	} | ||||||
|  | 	return dst | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // transverse flips the image vertically and rotates 90 counter-clockwise. | ||||||
|  | func transverse(img image.Image) image.Image { | ||||||
|  | 	src := newScanner(img) | ||||||
|  | 	dstW := src.h | ||||||
|  | 	dstH := src.w | ||||||
|  | 	rowSize := dstW * 4 | ||||||
|  | 	dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH)) | ||||||
|  | 	for y := 0; y < dstH; y++ { | ||||||
|  | 		i := y * dst.Stride | ||||||
|  | 		srcX := dstH - y - 1 | ||||||
|  | 		src.scan(srcX, 0, srcX+1, src.h, dst.Pix[i:i+rowSize]) | ||||||
|  | 		reverse(dst.Pix[i : i+rowSize]) | ||||||
|  | 	} | ||||||
|  | 	return dst | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // resizeHorizontalLinear resizes image to given width using linear resampling. | ||||||
|  | func resizeHorizontalLinear(img image.Image, dstWidth int) image.Image { | ||||||
|  | 	src := newScanner(img) | ||||||
|  | 	dst := image.NewRGBA(image.Rect(0, 0, dstWidth, src.h)) | ||||||
|  | 	weights := precomputeWeightsLinear(dstWidth, src.w) | ||||||
|  | 	scanLine := make([]uint8, src.w*4) | ||||||
|  | 	for y := 0; y < src.h; y++ { | ||||||
|  | 		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] = clampFloat(r * aInv) | ||||||
|  | 				d[1] = clampFloat(g * aInv) | ||||||
|  | 				d[2] = clampFloat(b * aInv) | ||||||
|  | 				d[3] = clampFloat(a) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return dst | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // resizeVerticalLinear resizes image to given height using linear resampling. | ||||||
|  | func resizeVerticalLinear(img image.Image, height int) image.Image { | ||||||
|  | 	src := newScanner(img) | ||||||
|  | 	dst := image.NewNRGBA(image.Rect(0, 0, src.w, height)) | ||||||
|  | 	weights := precomputeWeightsLinear(height, src.h) | ||||||
|  | 	scanLine := make([]uint8, src.h*4) | ||||||
|  | 	for x := 0; x < src.w; x++ { | ||||||
|  | 		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] = clampFloat(r * aInv) | ||||||
|  | 				d[1] = clampFloat(g * aInv) | ||||||
|  | 				d[2] = clampFloat(b * aInv) | ||||||
|  | 				d[3] = clampFloat(a) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return dst | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type indexWeight struct { | ||||||
|  | 	index  int | ||||||
|  | 	weight float64 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func precomputeWeightsLinear(dstSize, srcSize int) [][]indexWeight { | ||||||
|  | 	du := float64(srcSize) / float64(dstSize) | ||||||
|  | 	scale := du | ||||||
|  | 	if scale < 1.0 { | ||||||
|  | 		scale = 1.0 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	ru := math.Ceil(scale) | ||||||
|  | 	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 := resampleLinear((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 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // resampleLinear is the resample kernel func for linear filtering. | ||||||
|  | func resampleLinear(x float64) float64 { | ||||||
|  | 	x = math.Abs(x) | ||||||
|  | 	if x < 1.0 { | ||||||
|  | 		return 1.0 - x | ||||||
|  | 	} | ||||||
|  | 	return 0 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // scanner wraps an image.Image for | ||||||
|  | // easier size access and image type | ||||||
|  | // agnostic access to data at coords. | ||||||
|  | type scanner struct { | ||||||
|  | 	image   image.Image | ||||||
|  | 	w, h    int | ||||||
|  | 	palette []color.NRGBA | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // newScanner wraps an image.Image in scanner{} type. | ||||||
|  | func newScanner(img image.Image) *scanner { | ||||||
|  | 	b := img.Bounds() | ||||||
|  | 	s := &scanner{ | ||||||
|  | 		image: img, | ||||||
|  | 
 | ||||||
|  | 		w: b.Dx(), | ||||||
|  | 		h: b.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 | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // reverse reverses the data | ||||||
|  | // in contained pixel slice. | ||||||
|  | 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 | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // clampFloat rounds and clamps float64 value to fit into uint8. | ||||||
|  | func clampFloat(x float64) uint8 { | ||||||
|  | 	v := int64(x + 0.5) | ||||||
|  | 	if v > 255 { | ||||||
|  | 		return 255 | ||||||
|  | 	} | ||||||
|  | 	if v > 0 { | ||||||
|  | 		return uint8(v) | ||||||
|  | 	} | ||||||
|  | 	return 0 | ||||||
|  | } | ||||||
|  | @ -276,7 +276,7 @@ func (suite *ManagerTestSuite) TestSimpleJpegProcess() { | ||||||
| 	suite.Equal("image/jpeg", attachment.Thumbnail.ContentType) | 	suite.Equal("image/jpeg", attachment.Thumbnail.ContentType) | ||||||
| 	suite.Equal(269739, attachment.File.FileSize) | 	suite.Equal(269739, attachment.File.FileSize) | ||||||
| 	suite.Equal(22858, attachment.Thumbnail.FileSize) | 	suite.Equal(22858, attachment.Thumbnail.FileSize) | ||||||
| 	suite.Equal("LiBzRk#6V[WF_NvzV@WY_3rqV@a$", attachment.Blurhash) | 	suite.Equal("LiB|W-#6RQR.~qvzRjWF_3rqV@a$", attachment.Blurhash) | ||||||
| 
 | 
 | ||||||
| 	// now make sure the attachment is in the database | 	// now make sure the attachment is in the database | ||||||
| 	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachment.ID) | 	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachment.ID) | ||||||
|  | @ -429,7 +429,7 @@ func (suite *ManagerTestSuite) TestSlothVineProcess() { | ||||||
| 	suite.Equal("image/webp", attachment.Thumbnail.ContentType) | 	suite.Equal("image/webp", attachment.Thumbnail.ContentType) | ||||||
| 	suite.Equal(312453, attachment.File.FileSize) | 	suite.Equal(312453, attachment.File.FileSize) | ||||||
| 	suite.Equal(5648, attachment.Thumbnail.FileSize) | 	suite.Equal(5648, attachment.Thumbnail.FileSize) | ||||||
| 	suite.Equal("LfIYH~xtNskCxtfPW.kB_4aespof", attachment.Blurhash) | 	suite.Equal("LgIYH}xtNsofxtfPW.j[_4axn+of", attachment.Blurhash) | ||||||
| 
 | 
 | ||||||
| 	// now make sure the attachment is in the database | 	// now make sure the attachment is in the database | ||||||
| 	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachment.ID) | 	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachment.ID) | ||||||
|  | @ -489,7 +489,7 @@ func (suite *ManagerTestSuite) TestLongerMp4Process() { | ||||||
| 	suite.Equal("image/webp", attachment.Thumbnail.ContentType) | 	suite.Equal("image/webp", attachment.Thumbnail.ContentType) | ||||||
| 	suite.Equal(109569, attachment.File.FileSize) | 	suite.Equal(109569, attachment.File.FileSize) | ||||||
| 	suite.Equal(2976, attachment.Thumbnail.FileSize) | 	suite.Equal(2976, attachment.Thumbnail.FileSize) | ||||||
| 	suite.Equal("LJQJfm?bM{?b~qRjt7WBayWBofWB", attachment.Blurhash) | 	suite.Equal("LIQJfl_3IU?b~qM{ofayWBWVofRj", attachment.Blurhash) | ||||||
| 
 | 
 | ||||||
| 	// now make sure the attachment is in the database | 	// now make sure the attachment is in the database | ||||||
| 	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachment.ID) | 	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachment.ID) | ||||||
|  | @ -549,7 +549,7 @@ func (suite *ManagerTestSuite) TestBirdnestMp4Process() { | ||||||
| 	suite.Equal("image/webp", attachment.Thumbnail.ContentType) | 	suite.Equal("image/webp", attachment.Thumbnail.ContentType) | ||||||
| 	suite.Equal(1409625, attachment.File.FileSize) | 	suite.Equal(1409625, attachment.File.FileSize) | ||||||
| 	suite.Equal(14478, attachment.Thumbnail.FileSize) | 	suite.Equal(14478, attachment.Thumbnail.FileSize) | ||||||
| 	suite.Equal("LJF?FZV@RO.99DM_RPWAx]V?ayMw", attachment.Blurhash) | 	suite.Equal("LLF$qyaeRO.9DgM_RPaetkV@WCMw", attachment.Blurhash) | ||||||
| 
 | 
 | ||||||
| 	// now make sure the attachment is in the database | 	// now make sure the attachment is in the database | ||||||
| 	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachment.ID) | 	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachment.ID) | ||||||
|  | @ -657,7 +657,7 @@ func (suite *ManagerTestSuite) TestPngNoAlphaChannelProcess() { | ||||||
| 	suite.Equal("image/jpeg", attachment.Thumbnail.ContentType) | 	suite.Equal("image/jpeg", attachment.Thumbnail.ContentType) | ||||||
| 	suite.Equal(17471, attachment.File.FileSize) | 	suite.Equal(17471, attachment.File.FileSize) | ||||||
| 	suite.Equal(6446, attachment.Thumbnail.FileSize) | 	suite.Equal(6446, attachment.Thumbnail.FileSize) | ||||||
| 	suite.Equal("LFQT7e.A%O%4?co$M}M{_1W9~TxV", attachment.Blurhash) | 	suite.Equal("LGP%YL.A-?tA.9o#RURQ~ojp^~xW", attachment.Blurhash) | ||||||
| 
 | 
 | ||||||
| 	// now make sure the attachment is in the database | 	// now make sure the attachment is in the database | ||||||
| 	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachment.ID) | 	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachment.ID) | ||||||
|  | @ -713,7 +713,7 @@ func (suite *ManagerTestSuite) TestPngAlphaChannelProcess() { | ||||||
| 	suite.Equal("image/webp", attachment.Thumbnail.ContentType) | 	suite.Equal("image/webp", attachment.Thumbnail.ContentType) | ||||||
| 	suite.Equal(18832, attachment.File.FileSize) | 	suite.Equal(18832, attachment.File.FileSize) | ||||||
| 	suite.Equal(3592, attachment.Thumbnail.FileSize) | 	suite.Equal(3592, attachment.Thumbnail.FileSize) | ||||||
| 	suite.Equal("LCONII.A%Oxw?co#M}M{_1ac~TxV", attachment.Blurhash) | 	suite.Equal("LCN^lE.A-?xd?co#N1RQ~ojp~SxW", attachment.Blurhash) | ||||||
| 
 | 
 | ||||||
| 	// now make sure the attachment is in the database | 	// now make sure the attachment is in the database | ||||||
| 	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachment.ID) | 	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachment.ID) | ||||||
|  | @ -769,7 +769,7 @@ func (suite *ManagerTestSuite) TestSimpleJpegProcessWithCallback() { | ||||||
| 	suite.Equal("image/jpeg", attachment.Thumbnail.ContentType) | 	suite.Equal("image/jpeg", attachment.Thumbnail.ContentType) | ||||||
| 	suite.Equal(269739, attachment.File.FileSize) | 	suite.Equal(269739, attachment.File.FileSize) | ||||||
| 	suite.Equal(22858, attachment.Thumbnail.FileSize) | 	suite.Equal(22858, attachment.Thumbnail.FileSize) | ||||||
| 	suite.Equal("LiBzRk#6V[WF_NvzV@WY_3rqV@a$", attachment.Blurhash) | 	suite.Equal("LiB|W-#6RQR.~qvzRjWF_3rqV@a$", attachment.Blurhash) | ||||||
| 
 | 
 | ||||||
| 	// now make sure the attachment is in the database | 	// now make sure the attachment is in the database | ||||||
| 	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachment.ID) | 	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachment.ID) | ||||||
|  | @ -847,7 +847,7 @@ func (suite *ManagerTestSuite) TestSimpleJpegProcessWithDiskStorage() { | ||||||
| 	suite.Equal("image/jpeg", attachment.Thumbnail.ContentType) | 	suite.Equal("image/jpeg", attachment.Thumbnail.ContentType) | ||||||
| 	suite.Equal(269739, attachment.File.FileSize) | 	suite.Equal(269739, attachment.File.FileSize) | ||||||
| 	suite.Equal(22858, attachment.Thumbnail.FileSize) | 	suite.Equal(22858, attachment.Thumbnail.FileSize) | ||||||
| 	suite.Equal("LiBzRk#6V[WF_NvzV@WY_3rqV@a$", attachment.Blurhash) | 	suite.Equal("LiB|W-#6RQR.~qvzRjWF_3rqV@a$", attachment.Blurhash) | ||||||
| 
 | 
 | ||||||
| 	// now make sure the attachment is in the database | 	// now make sure the attachment is in the database | ||||||
| 	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachment.ID) | 	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachment.ID) | ||||||
|  |  | ||||||
|  | @ -28,7 +28,6 @@ import ( | ||||||
| 	"strings" | 	"strings" | ||||||
| 
 | 
 | ||||||
| 	"github.com/buckket/go-blurhash" | 	"github.com/buckket/go-blurhash" | ||||||
| 	"github.com/disintegration/imaging" |  | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/gtserror" | 	"github.com/superseriousbusiness/gotosocial/internal/gtserror" | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/log" | 	"github.com/superseriousbusiness/gotosocial/internal/log" | ||||||
| 	"golang.org/x/image/webp" | 	"golang.org/x/image/webp" | ||||||
|  | @ -248,32 +247,25 @@ func generateNativeThumb( | ||||||
| 	// taking orientation into account. | 	// taking orientation into account. | ||||||
| 	switch orientation { | 	switch orientation { | ||||||
| 	case orientationFlipH: | 	case orientationFlipH: | ||||||
| 		img = imaging.FlipH(img) | 		img = flipH(img) | ||||||
| 	case orientationFlipV: | 	case orientationFlipV: | ||||||
| 		img = imaging.FlipV(img) | 		img = flipV(img) | ||||||
| 	case orientationRotate90: | 	case orientationRotate90: | ||||||
| 		img = imaging.Rotate90(img) | 		img = rotate90(img) | ||||||
| 	case orientationRotate180: | 	case orientationRotate180: | ||||||
| 		img = imaging.Rotate180(img) | 		img = rotate180(img) | ||||||
| 	case orientationRotate270: | 	case orientationRotate270: | ||||||
| 		img = imaging.Rotate270(img) | 		img = rotate270(img) | ||||||
| 	case orientationTranspose: | 	case orientationTranspose: | ||||||
| 		img = imaging.Transpose(img) | 		img = transpose(img) | ||||||
| 	case orientationTransverse: | 	case orientationTransverse: | ||||||
| 		img = imaging.Transverse(img) | 		img = transverse(img) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Resize image to dimens only if necessary. | 	// Resize image to dimens. | ||||||
| 	if img.Bounds().Dx() > maxThumbWidth || | 	img = resizeDownLinear(img, | ||||||
| 		img.Bounds().Dy() > maxThumbHeight { | 		width, height, | ||||||
| 		// Note: We could call "imaging.Fit" here | 	) | ||||||
| 		// but there's no point, as we've already |  | ||||||
| 		// calculated target dimensions beforehand. |  | ||||||
| 		img = imaging.Resize(img, |  | ||||||
| 			width, height, |  | ||||||
| 			imaging.Linear, |  | ||||||
| 		) |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	// Open output file at given path. | 	// Open output file at given path. | ||||||
| 	outfile, err := os.Create(outpath) | 	outfile, err := os.Create(outpath) | ||||||
|  | @ -293,9 +285,10 @@ func generateNativeThumb( | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if needBlurhash { | 	if needBlurhash { | ||||||
| 		// for generating blurhashes, it's more cost effective to | 		// for generating blurhashes, it's more | ||||||
| 		// lose detail since it's blurry, so make a tiny version. | 		// cost effective to lose detail since | ||||||
| 		tiny := imaging.Resize(img, 32, 0, imaging.NearestNeighbor) | 		// it's blurry, so make a tiny version. | ||||||
|  | 		tiny := resizeDownLinear(img, 32, 0) | ||||||
| 
 | 
 | ||||||
| 		// Drop the larger image | 		// Drop the larger image | ||||||
| 		// ref as soon as possible | 		// ref as soon as possible | ||||||
|  | @ -332,9 +325,10 @@ func generateWebpBlurhash(filepath string) (string, error) { | ||||||
| 		return "", gtserror.Newf("error decoding file %s: %w", filepath, err) | 		return "", gtserror.Newf("error decoding file %s: %w", filepath, err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// for generating blurhashes, it's more cost effective to | 	// for generating blurhashes, it's more | ||||||
| 	// lose detail since it's blurry, so make a tiny version. | 	// cost effective to lose detail since | ||||||
| 	tiny := imaging.Resize(img, 32, 0, imaging.NearestNeighbor) | 	// it's blurry, so make a tiny version. | ||||||
|  | 	tiny := resizeDownLinear(img, 32, 0) | ||||||
| 
 | 
 | ||||||
| 	// Drop the larger image | 	// Drop the larger image | ||||||
| 	// ref as soon as possible | 	// ref as soon as possible | ||||||
|  |  | ||||||
							
								
								
									
										12
									
								
								vendor/github.com/disintegration/imaging/.travis.yml
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								vendor/github.com/disintegration/imaging/.travis.yml
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,12 +0,0 @@ | ||||||
| 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 |  | ||||||
							
								
								
									
										21
									
								
								vendor/github.com/disintegration/imaging/LICENSE
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/disintegration/imaging/LICENSE
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,21 +0,0 @@ | ||||||
| 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. |  | ||||||
							
								
								
									
										226
									
								
								vendor/github.com/disintegration/imaging/README.md
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										226
									
								
								vendor/github.com/disintegration/imaging/README.md
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,226 +0,0 @@ | ||||||
| # 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: |  | ||||||
| 
 |  | ||||||
|  |  | ||||||
							
								
								
									
										253
									
								
								vendor/github.com/disintegration/imaging/adjust.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										253
									
								
								vendor/github.com/disintegration/imaging/adjust.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,253 +0,0 @@ | ||||||
| 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 |  | ||||||
| } |  | ||||||
							
								
								
									
										148
									
								
								vendor/github.com/disintegration/imaging/convolution.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										148
									
								
								vendor/github.com/disintegration/imaging/convolution.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,148 +0,0 @@ | ||||||
| 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 |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
							
								
								
									
										7
									
								
								vendor/github.com/disintegration/imaging/doc.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								vendor/github.com/disintegration/imaging/doc.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,7 +0,0 @@ | ||||||
| /* |  | ||||||
| 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 |  | ||||||
							
								
								
									
										169
									
								
								vendor/github.com/disintegration/imaging/effects.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										169
									
								
								vendor/github.com/disintegration/imaging/effects.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,169 +0,0 @@ | ||||||
| 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 |  | ||||||
| } |  | ||||||
							
								
								
									
										52
									
								
								vendor/github.com/disintegration/imaging/histogram.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										52
									
								
								vendor/github.com/disintegration/imaging/histogram.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,52 +0,0 @@ | ||||||
| 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 |  | ||||||
| } |  | ||||||
							
								
								
									
										444
									
								
								vendor/github.com/disintegration/imaging/io.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										444
									
								
								vendor/github.com/disintegration/imaging/io.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,444 +0,0 @@ | ||||||
| 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 |  | ||||||
| } |  | ||||||
							
								
								
									
										595
									
								
								vendor/github.com/disintegration/imaging/resize.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										595
									
								
								vendor/github.com/disintegration/imaging/resize.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,595 +0,0 @@ | ||||||
| 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 |  | ||||||
| 		}, |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
							
								
								
									
										285
									
								
								vendor/github.com/disintegration/imaging/scanner.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										285
									
								
								vendor/github.com/disintegration/imaging/scanner.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,285 +0,0 @@ | ||||||
| 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 |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
							
								
								
									
										249
									
								
								vendor/github.com/disintegration/imaging/tools.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										249
									
								
								vendor/github.com/disintegration/imaging/tools.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,249 +0,0 @@ | ||||||
| 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) |  | ||||||
| } |  | ||||||
							
								
								
									
										268
									
								
								vendor/github.com/disintegration/imaging/transform.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										268
									
								
								vendor/github.com/disintegration/imaging/transform.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,268 +0,0 @@ | ||||||
| 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) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
							
								
								
									
										167
									
								
								vendor/github.com/disintegration/imaging/utils.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										167
									
								
								vendor/github.com/disintegration/imaging/utils.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,167 +0,0 @@ | ||||||
| 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 |  | ||||||
| } |  | ||||||
							
								
								
									
										253
									
								
								vendor/golang.org/x/image/bmp/reader.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										253
									
								
								vendor/golang.org/x/image/bmp/reader.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,253 +0,0 @@ | ||||||
| // 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, allowAlpha 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] |  | ||||||
| 			if !allowAlpha { |  | ||||||
| 				p[i+3] = 0xFF |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	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, allowAlpha, 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, allowAlpha) |  | ||||||
| 	} |  | ||||||
| 	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, allowAlpha bool, err error) { |  | ||||||
| 	// We only support those BMP images with one of the following DIB headers: |  | ||||||
| 	// - BITMAPINFOHEADER (40 bytes) |  | ||||||
| 	// - BITMAPV4HEADER (108 bytes) |  | ||||||
| 	// - BITMAPV5HEADER (124 bytes) |  | ||||||
| 	const ( |  | ||||||
| 		fileHeaderLen   = 14 |  | ||||||
| 		infoHeaderLen   = 40 |  | ||||||
| 		v4InfoHeaderLen = 108 |  | ||||||
| 		v5InfoHeaderLen = 124 |  | ||||||
| 	) |  | ||||||
| 	var b [1024]byte |  | ||||||
| 	if _, err := io.ReadFull(r, b[:fileHeaderLen+4]); err != nil { |  | ||||||
| 		if err == io.EOF { |  | ||||||
| 			err = io.ErrUnexpectedEOF |  | ||||||
| 		} |  | ||||||
| 		return image.Config{}, 0, false, false, err |  | ||||||
| 	} |  | ||||||
| 	if string(b[:2]) != "BM" { |  | ||||||
| 		return image.Config{}, 0, false, 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, false, ErrUnsupported |  | ||||||
| 	} |  | ||||||
| 	if _, err := io.ReadFull(r, b[fileHeaderLen+4:fileHeaderLen+infoLen]); err != nil { |  | ||||||
| 		if err == io.EOF { |  | ||||||
| 			err = io.ErrUnexpectedEOF |  | ||||||
| 		} |  | ||||||
| 		return image.Config{}, 0, false, 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, 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 BI_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, false, ErrUnsupported |  | ||||||
| 	} |  | ||||||
| 	switch bpp { |  | ||||||
| 	case 8: |  | ||||||
| 		colorUsed := readUint32(b[46:50]) |  | ||||||
| 		// If colorUsed is 0, it is set to the maximum number of colors for the given bpp, which is 2^bpp. |  | ||||||
| 		if colorUsed == 0 { |  | ||||||
| 			colorUsed = 256 |  | ||||||
| 		} else if colorUsed > 256 { |  | ||||||
| 			return image.Config{}, 0, false, false, ErrUnsupported |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if offset != fileHeaderLen+infoLen+colorUsed*4 { |  | ||||||
| 			return image.Config{}, 0, false, false, ErrUnsupported |  | ||||||
| 		} |  | ||||||
| 		_, err = io.ReadFull(r, b[:colorUsed*4]) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return image.Config{}, 0, false, false, err |  | ||||||
| 		} |  | ||||||
| 		pcm := make(color.Palette, colorUsed) |  | ||||||
| 		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, false, nil |  | ||||||
| 	case 24: |  | ||||||
| 		if offset != fileHeaderLen+infoLen { |  | ||||||
| 			return image.Config{}, 0, false, false, ErrUnsupported |  | ||||||
| 		} |  | ||||||
| 		return image.Config{ColorModel: color.RGBAModel, Width: width, Height: height}, 24, topDown, false, nil |  | ||||||
| 	case 32: |  | ||||||
| 		if offset != fileHeaderLen+infoLen { |  | ||||||
| 			return image.Config{}, 0, false, false, ErrUnsupported |  | ||||||
| 		} |  | ||||||
| 		// 32 bits per pixel is possibly RGBX (X is padding) or RGBA (A is |  | ||||||
| 		// alpha transparency). However, for BMP images, "Alpha is a |  | ||||||
| 		// poorly-documented and inconsistently-used feature" says |  | ||||||
| 		// https://source.chromium.org/chromium/chromium/src/+/bc0a792d7ebc587190d1a62ccddba10abeea274b:third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_reader.cc;l=621 |  | ||||||
| 		// |  | ||||||
| 		// That goes on to say "BITMAPV3HEADER+ have an alpha bitmask in the |  | ||||||
| 		// info header... so we respect it at all times... [For earlier |  | ||||||
| 		// (smaller) headers we] ignore alpha in Windows V3 BMPs except inside |  | ||||||
| 		// ICO files". |  | ||||||
| 		// |  | ||||||
| 		// "Ignore" means to always set alpha to 0xFF (fully opaque): |  | ||||||
| 		// https://source.chromium.org/chromium/chromium/src/+/bc0a792d7ebc587190d1a62ccddba10abeea274b:third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_reader.h;l=272 |  | ||||||
| 		// |  | ||||||
| 		// Confusingly, "Windows V3" does not correspond to BITMAPV3HEADER, but |  | ||||||
| 		// instead corresponds to the earlier (smaller) BITMAPINFOHEADER: |  | ||||||
| 		// https://source.chromium.org/chromium/chromium/src/+/bc0a792d7ebc587190d1a62ccddba10abeea274b:third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_reader.cc;l=258 |  | ||||||
| 		// |  | ||||||
| 		// This Go package does not support ICO files and the (infoLen > |  | ||||||
| 		// infoHeaderLen) condition distinguishes BITMAPINFOHEADER (40 bytes) |  | ||||||
| 		// vs later (larger) headers. |  | ||||||
| 		allowAlpha = infoLen > infoHeaderLen |  | ||||||
| 		return image.Config{ColorModel: color.RGBAModel, Width: width, Height: height}, 32, topDown, allowAlpha, nil |  | ||||||
| 	} |  | ||||||
| 	return image.Config{}, 0, false, false, ErrUnsupported |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func init() { |  | ||||||
| 	image.RegisterFormat("bmp", "BM????\x00\x00\x00\x00", Decode, DecodeConfig) |  | ||||||
| } |  | ||||||
							
								
								
									
										262
									
								
								vendor/golang.org/x/image/bmp/writer.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										262
									
								
								vendor/golang.org/x/image/bmp/writer.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,262 +0,0 @@ | ||||||
| // 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) |  | ||||||
| } |  | ||||||
							
								
								
									
										795
									
								
								vendor/golang.org/x/image/ccitt/reader.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										795
									
								
								vendor/golang.org/x/image/ccitt/reader.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,795 +0,0 @@ | ||||||
| // 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 ( |  | ||||||
| 	errIncompleteCode          = errors.New("ccitt: incomplete code") |  | ||||||
| 	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 |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // AutoDetectHeight is passed as the height argument to NewReader to indicate |  | ||||||
| // that the image height (the number of rows) is not known in advance. |  | ||||||
| const AutoDetectHeight = -1 |  | ||||||
| 
 |  | ||||||
| // 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 { |  | ||||||
| 			if err == io.EOF { |  | ||||||
| 				err = errIncompleteCode |  | ||||||
| 			} |  | ||||||
| 			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 |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // decodeEOL decodes the 12-bit EOL code 0000_0000_0001. |  | ||||||
| func decodeEOL(b *bitReader) error { |  | ||||||
| 	nBitsRead, bitsRead := uint32(0), uint64(0) |  | ||||||
| 	for { |  | ||||||
| 		bit, err := b.nextBit() |  | ||||||
| 		if err != nil { |  | ||||||
| 			if err == io.EOF { |  | ||||||
| 				err = errMissingEOL |  | ||||||
| 			} |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 		bitsRead |= bit << (63 - nBitsRead) |  | ||||||
| 		nBitsRead++ |  | ||||||
| 
 |  | ||||||
| 		if nBitsRead < 12 { |  | ||||||
| 			if bit&1 == 0 { |  | ||||||
| 				continue |  | ||||||
| 			} |  | ||||||
| 		} else if bit&1 != 0 { |  | ||||||
| 			return nil |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// Unread the bits we've read, then return errMissingEOL. |  | ||||||
| 		b.bits = (b.bits >> nBitsRead) | bitsRead |  | ||||||
| 		b.nBits += nBitsRead |  | ||||||
| 		return errMissingEOL |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 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. Alternatively, it may be negative if the image height is |  | ||||||
| 	// not known in advance at the time of the NewReader call. |  | ||||||
| 	// |  | ||||||
| 	// 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 |  | ||||||
| 
 |  | ||||||
| 	// truncated is whether the input is missing the final 6 consecutive EOL's |  | ||||||
| 	// (for Group3) or 2 consecutive EOL's (for Group4). Omitting that trailer |  | ||||||
| 	// (but otherwise padding to a byte boundary, with either all 0 bits or all |  | ||||||
| 	// 1 bits) is invalid according to the spec, but happens in practice when |  | ||||||
| 	// exporting from Adobe Acrobat to TIFF + CCITT. This package silently |  | ||||||
| 	// ignores the format error for CCITT input that has been truncated in that |  | ||||||
| 	// fashion, returning the full decoded image. |  | ||||||
| 	// |  | ||||||
| 	// Detecting trailer truncation (just after the final row of pixels) |  | ||||||
| 	// requires knowing which row is the final row, and therefore does not |  | ||||||
| 	// trigger if the image height is not known in advance. |  | ||||||
| 	truncated 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 { |  | ||||||
| 				// We do not know the image height in advance. See if the next |  | ||||||
| 				// code is an EOL. If it is, it is consumed. If it isn't, the |  | ||||||
| 				// bitReader shouldn't advance along the bit stream, and we |  | ||||||
| 				// simply decode another row of pixel data. |  | ||||||
| 				// |  | ||||||
| 				// For the Group4 subFormat, we may need to align to a byte |  | ||||||
| 				// boundary. For the Group3 subFormat, the previous z.decodeRow |  | ||||||
| 				// call (or z.startDecode call) has already consumed one of the |  | ||||||
| 				// 6 consecutive EOL's. The next EOL is actually the second of |  | ||||||
| 				// 6, in the middle, and we shouldn't align at that point. |  | ||||||
| 				if z.align && (z.subFormat == Group4) { |  | ||||||
| 					z.br.alignToByteBoundary() |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				if err := z.decodeEOL(); err == errMissingEOL { |  | ||||||
| 					// No-op. It's another row of pixel data. |  | ||||||
| 				} else if err != nil { |  | ||||||
| 					z.readErr = err |  | ||||||
| 					break |  | ||||||
| 				} else { |  | ||||||
| 					if z.readErr = z.finishDecode(true); z.readErr != nil { |  | ||||||
| 						break |  | ||||||
| 					} |  | ||||||
| 					z.readErr = io.EOF |  | ||||||
| 					break |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 			} else if z.rowsRemaining == 0 { |  | ||||||
| 				// We do know the image height in advance, and we have already |  | ||||||
| 				// decoded exactly that many rows. |  | ||||||
| 				if z.readErr = z.finishDecode(false); z.readErr != nil { |  | ||||||
| 					break |  | ||||||
| 				} |  | ||||||
| 				z.readErr = io.EOF |  | ||||||
| 				break |  | ||||||
| 
 |  | ||||||
| 			} else { |  | ||||||
| 				z.rowsRemaining-- |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			if z.readErr = z.decodeRow(z.rowsRemaining == 0); z.readErr != nil { |  | ||||||
| 				break |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// 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(alreadySeenEOL bool) error { |  | ||||||
| 	numberOfEOLs := 0 |  | ||||||
| 	switch z.subFormat { |  | ||||||
| 	case Group3: |  | ||||||
| 		if z.truncated { |  | ||||||
| 			return nil |  | ||||||
| 		} |  | ||||||
| 		// 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: |  | ||||||
| 		autoDetectHeight := z.rowsRemaining < 0 |  | ||||||
| 		if autoDetectHeight { |  | ||||||
| 			// Aligning to a byte boundary was already handled by reader.Read. |  | ||||||
| 		} else if z.align { |  | ||||||
| 			z.br.alignToByteBoundary() |  | ||||||
| 		} |  | ||||||
| 		// The stream ends with two EOL's. If the first one is missing, and we |  | ||||||
| 		// had an explicit image height, we just assume that the trailing two |  | ||||||
| 		// EOL's were truncated and return a nil error. |  | ||||||
| 		if err := z.decodeEOL(); err != nil { |  | ||||||
| 			if (err == errMissingEOL) && !autoDetectHeight { |  | ||||||
| 				z.truncated = true |  | ||||||
| 				return nil |  | ||||||
| 			} |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 		numberOfEOLs = 1 |  | ||||||
| 
 |  | ||||||
| 	default: |  | ||||||
| 		return errUnsupportedSubFormat |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if alreadySeenEOL { |  | ||||||
| 		numberOfEOLs-- |  | ||||||
| 	} |  | ||||||
| 	for ; numberOfEOLs > 0; numberOfEOLs-- { |  | ||||||
| 		if err := z.decodeEOL(); err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (z *reader) decodeEOL() error { |  | ||||||
| 	return decodeEOL(&z.br) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (z *reader) decodeRow(finalRow bool) 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 |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		err := z.decodeEOL() |  | ||||||
| 		if finalRow && (err == errMissingEOL) { |  | ||||||
| 			z.truncated = true |  | ||||||
| 			return nil |  | ||||||
| 		} |  | ||||||
| 		return err |  | ||||||
| 
 |  | ||||||
| 	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(y+1 == bounds.Max.Y); err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 		z.curr, z.prev = nil, z.curr |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if err := z.finishDecode(false); 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. |  | ||||||
| // |  | ||||||
| // A negative height, such as passing AutoDetectHeight, means that the image |  | ||||||
| // height is not known in advance. A negative width is invalid. |  | ||||||
| func NewReader(r io.Reader, order Order, sf SubFormat, width int, height int, opts *Options) io.Reader { |  | ||||||
| 	readErr := error(nil) |  | ||||||
| 	if width < 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, |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
							
								
								
									
										972
									
								
								vendor/golang.org/x/image/ccitt/table.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										972
									
								
								vendor/golang.org/x/image/ccitt/table.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,972 +0,0 @@ | ||||||
| // 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 |  | ||||||
| //	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:  {0, ^9}, |  | ||||||
| 	10: {^8, ^5}, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // 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" |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // 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 |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // COPY PASTE table.go END |  | ||||||
							
								
								
									
										102
									
								
								vendor/golang.org/x/image/ccitt/writer.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										102
									
								
								vendor/golang.org/x/image/ccitt/writer.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,102 +0,0 @@ | ||||||
| // 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 |  | ||||||
| } |  | ||||||
							
								
								
									
										69
									
								
								vendor/golang.org/x/image/tiff/buffer.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										69
									
								
								vendor/golang.org/x/image/tiff/buffer.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,69 +0,0 @@ | ||||||
| // 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), |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
							
								
								
									
										58
									
								
								vendor/golang.org/x/image/tiff/compress.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										58
									
								
								vendor/golang.org/x/image/tiff/compress.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,58 +0,0 @@ | ||||||
| // 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]...) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
							
								
								
									
										149
									
								
								vendor/golang.org/x/image/tiff/consts.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										149
									
								
								vendor/golang.org/x/image/tiff/consts.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,149 +0,0 @@ | ||||||
| // 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 |  | ||||||
| } |  | ||||||
							
								
								
									
										29
									
								
								vendor/golang.org/x/image/tiff/fuzz.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										29
									
								
								vendor/golang.org/x/image/tiff/fuzz.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,29 +0,0 @@ | ||||||
| // 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: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 |  | ||||||
| } |  | ||||||
							
								
								
									
										272
									
								
								vendor/golang.org/x/image/tiff/lzw/reader.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										272
									
								
								vendor/golang.org/x/image/tiff/lzw/reader.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,272 +0,0 @@ | ||||||
| // 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 accommodate 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 |  | ||||||
| } |  | ||||||
							
								
								
									
										785
									
								
								vendor/golang.org/x/image/tiff/reader.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										785
									
								
								vendor/golang.org/x/image/tiff/reader.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,785 +0,0 @@ | ||||||
| // 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 ( |  | ||||||
| 	"bytes" |  | ||||||
| 	"compress/zlib" |  | ||||||
| 	"encoding/binary" |  | ||||||
| 	"fmt" |  | ||||||
| 	"image" |  | ||||||
| 	"image/color" |  | ||||||
| 	"io" |  | ||||||
| 	"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") |  | ||||||
| 	errInvalidColorIndex = FormatError("invalid color index") |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| const maxChunkSize = 10 << 20 // 10M |  | ||||||
| 
 |  | ||||||
| // safeReadAt is a verbatim copy of internal/saferio.ReadDataAt from the |  | ||||||
| // standard library, which is used to read data from a reader using a length |  | ||||||
| // provided by untrusted data, without allocating the entire slice ahead of time |  | ||||||
| // if it is large (>maxChunkSize). This allows us to avoid allocating giant |  | ||||||
| // slices before learning that we can't actually read that much data from the |  | ||||||
| // reader. |  | ||||||
| func safeReadAt(r io.ReaderAt, n uint64, off int64) ([]byte, error) { |  | ||||||
| 	if int64(n) < 0 || n != uint64(int(n)) { |  | ||||||
| 		// n is too large to fit in int, so we can't allocate |  | ||||||
| 		// a buffer large enough. Treat this as a read failure. |  | ||||||
| 		return nil, io.ErrUnexpectedEOF |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if n < maxChunkSize { |  | ||||||
| 		buf := make([]byte, n) |  | ||||||
| 		_, err := r.ReadAt(buf, off) |  | ||||||
| 		if err != nil { |  | ||||||
| 			// io.SectionReader can return EOF for n == 0, |  | ||||||
| 			// but for our purposes that is a success. |  | ||||||
| 			if err != io.EOF || n > 0 { |  | ||||||
| 				return nil, err |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		return buf, nil |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	var buf []byte |  | ||||||
| 	buf1 := make([]byte, maxChunkSize) |  | ||||||
| 	for n > 0 { |  | ||||||
| 		next := n |  | ||||||
| 		if next > maxChunkSize { |  | ||||||
| 			next = maxChunkSize |  | ||||||
| 		} |  | ||||||
| 		_, err := r.ReadAt(buf1[:next], off) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return nil, err |  | ||||||
| 		} |  | ||||||
| 		buf = append(buf, buf1[:next]...) |  | ||||||
| 		n -= next |  | ||||||
| 		off += int64(next) |  | ||||||
| 	} |  | ||||||
| 	return buf, nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 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, err = safeReadAt(d.r, uint64(datalen), 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) |  | ||||||
| 		pLen := len(d.palette) |  | ||||||
| 		for y := ymin; y < rMaxY; y++ { |  | ||||||
| 			for x := xmin; x < rMaxX; x++ { |  | ||||||
| 				v, ok := d.readBits(d.bpp) |  | ||||||
| 				if !ok { |  | ||||||
| 					return errNoPixels |  | ||||||
| 				} |  | ||||||
| 				idx := uint8(v) |  | ||||||
| 				if int(idx) >= pLen { |  | ||||||
| 					return errInvalidColorIndex |  | ||||||
| 				} |  | ||||||
| 				img.SetColorIndex(x, y, idx) |  | ||||||
| 			} |  | ||||||
| 			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 { |  | ||||||
| 		if err == io.EOF { |  | ||||||
| 			err = io.ErrUnexpectedEOF |  | ||||||
| 		} |  | ||||||
| 		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. |  | ||||||
| 	var err error |  | ||||||
| 	p, err = safeReadAt(d.r, uint64(ifdLen*numItems), ifdOffset+2) |  | ||||||
| 	if 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") |  | ||||||
| 	} |  | ||||||
| 	if d.firstVal(tPhotometricInterpretation) != pRGB { |  | ||||||
| 		if len(d.features[tBitsPerSample]) != 1 { |  | ||||||
| 			return nil, UnsupportedError("extra samples") |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	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)) |  | ||||||
| 
 |  | ||||||
| 		// The specification says that tile widths and lengths must be a multiple of 16. |  | ||||||
| 		// We currently permit invalid sizes, but reject anything too small to limit the |  | ||||||
| 		// amount of work a malicious input can force us to perform. |  | ||||||
| 		if blockWidth < 8 || blockHeight < 8 { |  | ||||||
| 			return nil, FormatError("tile size is too small") |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		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) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if blocksAcross == 0 || blocksDown == 0 { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	// Maximum data per pixel is 8 bytes (RGBA64). |  | ||||||
| 	blockMaxDataSize := int64(blockWidth) * int64(blockHeight) * 8 |  | ||||||
| 	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, err = safeReadAt(d.r, uint64(n), 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 = readBuf(r, d.buf, blockMaxDataSize) |  | ||||||
| 			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 = readBuf(r, d.buf, blockMaxDataSize) |  | ||||||
| 			case cLZW: |  | ||||||
| 				r := lzw.NewReader(io.NewSectionReader(d.r, offset, n), lzw.MSB, 8) |  | ||||||
| 				d.buf, err = readBuf(r, d.buf, blockMaxDataSize) |  | ||||||
| 				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 = readBuf(r, d.buf, blockMaxDataSize) |  | ||||||
| 				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 readBuf(r io.Reader, buf []byte, lim int64) ([]byte, error) { |  | ||||||
| 	b := bytes.NewBuffer(buf[:0]) |  | ||||||
| 	_, err := b.ReadFrom(io.LimitReader(r, lim)) |  | ||||||
| 	return b.Bytes(), err |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func init() { |  | ||||||
| 	image.RegisterFormat("tiff", leHeader, Decode, DecodeConfig) |  | ||||||
| 	image.RegisterFormat("tiff", beHeader, Decode, DecodeConfig) |  | ||||||
| } |  | ||||||
							
								
								
									
										441
									
								
								vendor/golang.org/x/image/tiff/writer.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										441
									
								
								vendor/golang.org/x/image/tiff/writer.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -1,441 +0,0 @@ | ||||||
| // 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" |  | ||||||
| 	"errors" |  | ||||||
| 	"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) |  | ||||||
| 	default: |  | ||||||
| 		return errors.New("tiff: unsupported compression") |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	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) |  | ||||||
| } |  | ||||||
							
								
								
									
										7
									
								
								vendor/modules.txt
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								vendor/modules.txt
									
										
									
									
										vendored
									
									
								
							|  | @ -175,9 +175,6 @@ github.com/coreos/go-systemd/v22/dbus | ||||||
| # github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc | # github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc | ||||||
| ## explicit | ## explicit | ||||||
| github.com/davecgh/go-spew/spew | github.com/davecgh/go-spew/spew | ||||||
| # github.com/disintegration/imaging v1.6.2 |  | ||||||
| ## explicit |  | ||||||
| github.com/disintegration/imaging |  | ||||||
| # github.com/docker/go-units v0.5.0 | # github.com/docker/go-units v0.5.0 | ||||||
| ## explicit | ## explicit | ||||||
| github.com/docker/go-units | github.com/docker/go-units | ||||||
|  | @ -1095,11 +1092,7 @@ golang.org/x/exp/slog/internal | ||||||
| golang.org/x/exp/slog/internal/buffer | golang.org/x/exp/slog/internal/buffer | ||||||
| # golang.org/x/image v0.19.0 | # golang.org/x/image v0.19.0 | ||||||
| ## explicit; go 1.18 | ## explicit; go 1.18 | ||||||
| golang.org/x/image/bmp |  | ||||||
| golang.org/x/image/ccitt |  | ||||||
| golang.org/x/image/riff | golang.org/x/image/riff | ||||||
| golang.org/x/image/tiff |  | ||||||
| golang.org/x/image/tiff/lzw |  | ||||||
| golang.org/x/image/vp8 | golang.org/x/image/vp8 | ||||||
| golang.org/x/image/vp8l | golang.org/x/image/vp8l | ||||||
| golang.org/x/image/webp | golang.org/x/image/webp | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue