feat(training): new features
* add flip-image option * add command to list models * add option to override image size when training is launched * add option to disable aws spot instance
This commit is contained in:
		
							
								
								
									
										12
									
								
								vendor/github.com/disintegration/imaging/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								vendor/github.com/disintegration/imaging/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
language: go
 | 
			
		||||
go:
 | 
			
		||||
  - "1.10.x"
 | 
			
		||||
  - "1.11.x"
 | 
			
		||||
  - "1.12.x"
 | 
			
		||||
 | 
			
		||||
before_install:
 | 
			
		||||
  - go get github.com/mattn/goveralls
 | 
			
		||||
 | 
			
		||||
script:
 | 
			
		||||
  - go test -v -race -cover
 | 
			
		||||
  - $GOPATH/bin/goveralls -service=travis-ci
 | 
			
		||||
							
								
								
									
										21
									
								
								vendor/github.com/disintegration/imaging/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/disintegration/imaging/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
The MIT License (MIT)
 | 
			
		||||
 | 
			
		||||
Copyright (c) 2012 Grigory Dryapak
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
in the Software without restriction, including without limitation the rights
 | 
			
		||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in all
 | 
			
		||||
copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
			
		||||
SOFTWARE.
 | 
			
		||||
							
								
								
									
										226
									
								
								vendor/github.com/disintegration/imaging/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										226
									
								
								vendor/github.com/disintegration/imaging/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,226 @@
 | 
			
		||||
# Imaging
 | 
			
		||||
 | 
			
		||||
[](https://godoc.org/github.com/disintegration/imaging)
 | 
			
		||||
[](https://travis-ci.org/disintegration/imaging)
 | 
			
		||||
[](https://coveralls.io/github/disintegration/imaging?branch=master)
 | 
			
		||||
[](https://goreportcard.com/report/github.com/disintegration/imaging)
 | 
			
		||||
 | 
			
		||||
Package imaging provides basic image processing functions (resize, rotate, crop, brightness/contrast adjustments, etc.).
 | 
			
		||||
 | 
			
		||||
All the image processing functions provided by the package accept any image type that implements `image.Image` interface
 | 
			
		||||
as an input, and return a new image of `*image.NRGBA` type (32bit RGBA colors, non-premultiplied alpha).
 | 
			
		||||
 | 
			
		||||
## Installation
 | 
			
		||||
 | 
			
		||||
    go get -u github.com/disintegration/imaging
 | 
			
		||||
 | 
			
		||||
## Documentation
 | 
			
		||||
 | 
			
		||||
http://godoc.org/github.com/disintegration/imaging
 | 
			
		||||
 | 
			
		||||
## Usage examples
 | 
			
		||||
 | 
			
		||||
A few usage examples can be found below. See the documentation for the full list of supported functions.
 | 
			
		||||
 | 
			
		||||
### Image resizing
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
// Resize srcImage to size = 128x128px using the Lanczos filter.
 | 
			
		||||
dstImage128 := imaging.Resize(srcImage, 128, 128, imaging.Lanczos)
 | 
			
		||||
 | 
			
		||||
// Resize srcImage to width = 800px preserving the aspect ratio.
 | 
			
		||||
dstImage800 := imaging.Resize(srcImage, 800, 0, imaging.Lanczos)
 | 
			
		||||
 | 
			
		||||
// Scale down srcImage to fit the 800x600px bounding box.
 | 
			
		||||
dstImageFit := imaging.Fit(srcImage, 800, 600, imaging.Lanczos)
 | 
			
		||||
 | 
			
		||||
// Resize and crop the srcImage to fill the 100x100px area.
 | 
			
		||||
dstImageFill := imaging.Fill(srcImage, 100, 100, imaging.Center, imaging.Lanczos)
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Imaging supports image resizing using various resampling filters. The most notable ones:
 | 
			
		||||
- `Lanczos` - A high-quality resampling filter for photographic images yielding sharp results.
 | 
			
		||||
- `CatmullRom` - A sharp cubic filter that is faster than Lanczos filter while providing similar results.
 | 
			
		||||
- `MitchellNetravali` - A cubic filter that produces smoother results with less ringing artifacts than CatmullRom.
 | 
			
		||||
- `Linear` - Bilinear resampling filter, produces smooth output. Faster than cubic filters.
 | 
			
		||||
- `Box` - Simple and fast averaging filter appropriate for downscaling. When upscaling it's similar to NearestNeighbor.
 | 
			
		||||
- `NearestNeighbor` - Fastest resampling filter, no antialiasing.
 | 
			
		||||
 | 
			
		||||
The full list of supported filters:  NearestNeighbor, Box, Linear, Hermite, MitchellNetravali, CatmullRom, BSpline, Gaussian, Lanczos, Hann, Hamming, Blackman, Bartlett, Welch, Cosine. Custom filters can be created using ResampleFilter struct.
 | 
			
		||||
 | 
			
		||||
**Resampling filters comparison**
 | 
			
		||||
 | 
			
		||||
Original image:
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
The same image resized from 600x400px to 150x100px using different resampling filters.
 | 
			
		||||
From faster (lower quality) to slower (higher quality):
 | 
			
		||||
 | 
			
		||||
Filter                    | Resize result
 | 
			
		||||
--------------------------|---------------------------------------------
 | 
			
		||||
`imaging.NearestNeighbor` | 
 | 
			
		||||
`imaging.Linear`          | 
 | 
			
		||||
`imaging.CatmullRom`      | 
 | 
			
		||||
`imaging.Lanczos`         | 
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### Gaussian Blur
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
dstImage := imaging.Blur(srcImage, 0.5)
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Sigma parameter allows to control the strength of the blurring effect.
 | 
			
		||||
 | 
			
		||||
Original image                     | Sigma = 0.5                            | Sigma = 1.5
 | 
			
		||||
-----------------------------------|----------------------------------------|---------------------------------------
 | 
			
		||||
 |  | 
 | 
			
		||||
 | 
			
		||||
### Sharpening
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
dstImage := imaging.Sharpen(srcImage, 0.5)
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
`Sharpen` uses gaussian function internally. Sigma parameter allows to control the strength of the sharpening effect.
 | 
			
		||||
 | 
			
		||||
Original image                     | Sigma = 0.5                               | Sigma = 1.5
 | 
			
		||||
-----------------------------------|-------------------------------------------|------------------------------------------
 | 
			
		||||
 |  | 
 | 
			
		||||
 | 
			
		||||
### Gamma correction
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
dstImage := imaging.AdjustGamma(srcImage, 0.75)
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Original image                     | Gamma = 0.75                             | Gamma = 1.25
 | 
			
		||||
-----------------------------------|------------------------------------------|-----------------------------------------
 | 
			
		||||
 |  | 
 | 
			
		||||
 | 
			
		||||
### Contrast adjustment
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
dstImage := imaging.AdjustContrast(srcImage, 20)
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Original image                     | Contrast = 15                              | Contrast = -15
 | 
			
		||||
-----------------------------------|--------------------------------------------|-------------------------------------------
 | 
			
		||||
 |  | 
 | 
			
		||||
 | 
			
		||||
### Brightness adjustment
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
dstImage := imaging.AdjustBrightness(srcImage, 20)
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Original image                     | Brightness = 10                              | Brightness = -10
 | 
			
		||||
-----------------------------------|----------------------------------------------|---------------------------------------------
 | 
			
		||||
 |  | 
 | 
			
		||||
 | 
			
		||||
### Saturation adjustment
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
dstImage := imaging.AdjustSaturation(srcImage, 20)
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Original image                     | Saturation = 30                              | Saturation = -30
 | 
			
		||||
-----------------------------------|----------------------------------------------|---------------------------------------------
 | 
			
		||||
 |  | 
 | 
			
		||||
 | 
			
		||||
## FAQ
 | 
			
		||||
 | 
			
		||||
### Incorrect image orientation after processing (e.g. an image appears rotated after resizing)
 | 
			
		||||
 | 
			
		||||
Most probably, the given image contains the EXIF orientation tag.
 | 
			
		||||
The stadard `image/*` packages do not support loading and saving
 | 
			
		||||
this kind of information. To fix the issue, try opening images with
 | 
			
		||||
the `AutoOrientation` decode option. If this option is set to `true`,
 | 
			
		||||
the image orientation is changed after decoding, according to the
 | 
			
		||||
orientation tag (if present). Here's the example:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
img, err := imaging.Open("test.jpg", imaging.AutoOrientation(true))
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### What's the difference between `imaging` and `gift` packages?
 | 
			
		||||
 | 
			
		||||
[imaging](https://github.com/disintegration/imaging)
 | 
			
		||||
is designed to be a lightweight and simple image manipulation package.
 | 
			
		||||
It provides basic image processing functions and a few helper functions
 | 
			
		||||
such as `Open` and `Save`. It consistently returns *image.NRGBA image 
 | 
			
		||||
type (8 bits per channel, RGBA).
 | 
			
		||||
 | 
			
		||||
[gift](https://github.com/disintegration/gift)
 | 
			
		||||
supports more advanced image processing, for example, sRGB/Linear color
 | 
			
		||||
space conversions. It also supports different output image types
 | 
			
		||||
(e.g. 16 bits per channel) and provides easy-to-use API for chaining
 | 
			
		||||
multiple processing steps together.
 | 
			
		||||
 | 
			
		||||
## Example code
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"image"
 | 
			
		||||
	"image/color"
 | 
			
		||||
	"log"
 | 
			
		||||
 | 
			
		||||
	"github.com/disintegration/imaging"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	// Open a test image.
 | 
			
		||||
	src, err := imaging.Open("testdata/flowers.png")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatalf("failed to open image: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Crop the original image to 300x300px size using the center anchor.
 | 
			
		||||
	src = imaging.CropAnchor(src, 300, 300, imaging.Center)
 | 
			
		||||
 | 
			
		||||
	// Resize the cropped image to width = 200px preserving the aspect ratio.
 | 
			
		||||
	src = imaging.Resize(src, 200, 0, imaging.Lanczos)
 | 
			
		||||
 | 
			
		||||
	// Create a blurred version of the image.
 | 
			
		||||
	img1 := imaging.Blur(src, 5)
 | 
			
		||||
 | 
			
		||||
	// Create a grayscale version of the image with higher contrast and sharpness.
 | 
			
		||||
	img2 := imaging.Grayscale(src)
 | 
			
		||||
	img2 = imaging.AdjustContrast(img2, 20)
 | 
			
		||||
	img2 = imaging.Sharpen(img2, 2)
 | 
			
		||||
 | 
			
		||||
	// Create an inverted version of the image.
 | 
			
		||||
	img3 := imaging.Invert(src)
 | 
			
		||||
 | 
			
		||||
	// Create an embossed version of the image using a convolution filter.
 | 
			
		||||
	img4 := imaging.Convolve3x3(
 | 
			
		||||
		src,
 | 
			
		||||
		[9]float64{
 | 
			
		||||
			-1, -1, 0,
 | 
			
		||||
			-1, 1, 1,
 | 
			
		||||
			0, 1, 1,
 | 
			
		||||
		},
 | 
			
		||||
		nil,
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	// Create a new image and paste the four produced images into it.
 | 
			
		||||
	dst := imaging.New(400, 400, color.NRGBA{0, 0, 0, 0})
 | 
			
		||||
	dst = imaging.Paste(dst, img1, image.Pt(0, 0))
 | 
			
		||||
	dst = imaging.Paste(dst, img2, image.Pt(0, 200))
 | 
			
		||||
	dst = imaging.Paste(dst, img3, image.Pt(200, 0))
 | 
			
		||||
	dst = imaging.Paste(dst, img4, image.Pt(200, 200))
 | 
			
		||||
 | 
			
		||||
	// Save the resulting image as JPEG.
 | 
			
		||||
	err = imaging.Save(dst, "testdata/out_example.jpg")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatalf("failed to save image: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Output:
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
							
								
								
									
										253
									
								
								vendor/github.com/disintegration/imaging/adjust.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										253
									
								
								vendor/github.com/disintegration/imaging/adjust.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,253 @@
 | 
			
		||||
package imaging
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"image"
 | 
			
		||||
	"image/color"
 | 
			
		||||
	"math"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Grayscale produces a grayscale version of the image.
 | 
			
		||||
func Grayscale(img image.Image) *image.NRGBA {
 | 
			
		||||
	src := newScanner(img)
 | 
			
		||||
	dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h))
 | 
			
		||||
	parallel(0, src.h, func(ys <-chan int) {
 | 
			
		||||
		for y := range ys {
 | 
			
		||||
			i := y * dst.Stride
 | 
			
		||||
			src.scan(0, y, src.w, y+1, dst.Pix[i:i+src.w*4])
 | 
			
		||||
			for x := 0; x < src.w; x++ {
 | 
			
		||||
				d := dst.Pix[i : i+3 : i+3]
 | 
			
		||||
				r := d[0]
 | 
			
		||||
				g := d[1]
 | 
			
		||||
				b := d[2]
 | 
			
		||||
				f := 0.299*float64(r) + 0.587*float64(g) + 0.114*float64(b)
 | 
			
		||||
				y := uint8(f + 0.5)
 | 
			
		||||
				d[0] = y
 | 
			
		||||
				d[1] = y
 | 
			
		||||
				d[2] = y
 | 
			
		||||
				i += 4
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
	return dst
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Invert produces an inverted (negated) version of the image.
 | 
			
		||||
func Invert(img image.Image) *image.NRGBA {
 | 
			
		||||
	src := newScanner(img)
 | 
			
		||||
	dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h))
 | 
			
		||||
	parallel(0, src.h, func(ys <-chan int) {
 | 
			
		||||
		for y := range ys {
 | 
			
		||||
			i := y * dst.Stride
 | 
			
		||||
			src.scan(0, y, src.w, y+1, dst.Pix[i:i+src.w*4])
 | 
			
		||||
			for x := 0; x < src.w; x++ {
 | 
			
		||||
				d := dst.Pix[i : i+3 : i+3]
 | 
			
		||||
				d[0] = 255 - d[0]
 | 
			
		||||
				d[1] = 255 - d[1]
 | 
			
		||||
				d[2] = 255 - d[2]
 | 
			
		||||
				i += 4
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
	return dst
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AdjustSaturation changes the saturation of the image using the percentage parameter and returns the adjusted image.
 | 
			
		||||
// The percentage must be in the range (-100, 100).
 | 
			
		||||
// The percentage = 0 gives the original image.
 | 
			
		||||
// The percentage = 100 gives the image with the saturation value doubled for each pixel.
 | 
			
		||||
// The percentage = -100 gives the image with the saturation value zeroed for each pixel (grayscale).
 | 
			
		||||
//
 | 
			
		||||
// Examples:
 | 
			
		||||
//  dstImage = imaging.AdjustSaturation(srcImage, 25) // Increase image saturation by 25%.
 | 
			
		||||
//  dstImage = imaging.AdjustSaturation(srcImage, -10) // Decrease image saturation by 10%.
 | 
			
		||||
//
 | 
			
		||||
func AdjustSaturation(img image.Image, percentage float64) *image.NRGBA {
 | 
			
		||||
	percentage = math.Min(math.Max(percentage, -100), 100)
 | 
			
		||||
	multiplier := 1 + percentage/100
 | 
			
		||||
 | 
			
		||||
	return AdjustFunc(img, func(c color.NRGBA) color.NRGBA {
 | 
			
		||||
		h, s, l := rgbToHSL(c.R, c.G, c.B)
 | 
			
		||||
		s *= multiplier
 | 
			
		||||
		if s > 1 {
 | 
			
		||||
			s = 1
 | 
			
		||||
		}
 | 
			
		||||
		r, g, b := hslToRGB(h, s, l)
 | 
			
		||||
		return color.NRGBA{r, g, b, c.A}
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AdjustContrast changes the contrast of the image using the percentage parameter and returns the adjusted image.
 | 
			
		||||
// The percentage must be in range (-100, 100). The percentage = 0 gives the original image.
 | 
			
		||||
// The percentage = -100 gives solid gray image.
 | 
			
		||||
//
 | 
			
		||||
// Examples:
 | 
			
		||||
//
 | 
			
		||||
//	dstImage = imaging.AdjustContrast(srcImage, -10) // Decrease image contrast by 10%.
 | 
			
		||||
//	dstImage = imaging.AdjustContrast(srcImage, 20) // Increase image contrast by 20%.
 | 
			
		||||
//
 | 
			
		||||
func AdjustContrast(img image.Image, percentage float64) *image.NRGBA {
 | 
			
		||||
	percentage = math.Min(math.Max(percentage, -100.0), 100.0)
 | 
			
		||||
	lut := make([]uint8, 256)
 | 
			
		||||
 | 
			
		||||
	v := (100.0 + percentage) / 100.0
 | 
			
		||||
	for i := 0; i < 256; i++ {
 | 
			
		||||
		switch {
 | 
			
		||||
		case 0 <= v && v <= 1:
 | 
			
		||||
			lut[i] = clamp((0.5 + (float64(i)/255.0-0.5)*v) * 255.0)
 | 
			
		||||
		case 1 < v && v < 2:
 | 
			
		||||
			lut[i] = clamp((0.5 + (float64(i)/255.0-0.5)*(1/(2.0-v))) * 255.0)
 | 
			
		||||
		default:
 | 
			
		||||
			lut[i] = uint8(float64(i)/255.0+0.5) * 255
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return adjustLUT(img, lut)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AdjustBrightness changes the brightness of the image using the percentage parameter and returns the adjusted image.
 | 
			
		||||
// The percentage must be in range (-100, 100). The percentage = 0 gives the original image.
 | 
			
		||||
// The percentage = -100 gives solid black image. The percentage = 100 gives solid white image.
 | 
			
		||||
//
 | 
			
		||||
// Examples:
 | 
			
		||||
//
 | 
			
		||||
//	dstImage = imaging.AdjustBrightness(srcImage, -15) // Decrease image brightness by 15%.
 | 
			
		||||
//	dstImage = imaging.AdjustBrightness(srcImage, 10) // Increase image brightness by 10%.
 | 
			
		||||
//
 | 
			
		||||
func AdjustBrightness(img image.Image, percentage float64) *image.NRGBA {
 | 
			
		||||
	percentage = math.Min(math.Max(percentage, -100.0), 100.0)
 | 
			
		||||
	lut := make([]uint8, 256)
 | 
			
		||||
 | 
			
		||||
	shift := 255.0 * percentage / 100.0
 | 
			
		||||
	for i := 0; i < 256; i++ {
 | 
			
		||||
		lut[i] = clamp(float64(i) + shift)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return adjustLUT(img, lut)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AdjustGamma performs a gamma correction on the image and returns the adjusted image.
 | 
			
		||||
// Gamma parameter must be positive. Gamma = 1.0 gives the original image.
 | 
			
		||||
// Gamma less than 1.0 darkens the image and gamma greater than 1.0 lightens it.
 | 
			
		||||
//
 | 
			
		||||
// Example:
 | 
			
		||||
//
 | 
			
		||||
//	dstImage = imaging.AdjustGamma(srcImage, 0.7)
 | 
			
		||||
//
 | 
			
		||||
func AdjustGamma(img image.Image, gamma float64) *image.NRGBA {
 | 
			
		||||
	e := 1.0 / math.Max(gamma, 0.0001)
 | 
			
		||||
	lut := make([]uint8, 256)
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < 256; i++ {
 | 
			
		||||
		lut[i] = clamp(math.Pow(float64(i)/255.0, e) * 255.0)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return adjustLUT(img, lut)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AdjustSigmoid changes the contrast of the image using a sigmoidal function and returns the adjusted image.
 | 
			
		||||
// It's a non-linear contrast change useful for photo adjustments as it preserves highlight and shadow detail.
 | 
			
		||||
// The midpoint parameter is the midpoint of contrast that must be between 0 and 1, typically 0.5.
 | 
			
		||||
// The factor parameter indicates how much to increase or decrease the contrast, typically in range (-10, 10).
 | 
			
		||||
// If the factor parameter is positive the image contrast is increased otherwise the contrast is decreased.
 | 
			
		||||
//
 | 
			
		||||
// Examples:
 | 
			
		||||
//
 | 
			
		||||
//	dstImage = imaging.AdjustSigmoid(srcImage, 0.5, 3.0) // Increase the contrast.
 | 
			
		||||
//	dstImage = imaging.AdjustSigmoid(srcImage, 0.5, -3.0) // Decrease the contrast.
 | 
			
		||||
//
 | 
			
		||||
func AdjustSigmoid(img image.Image, midpoint, factor float64) *image.NRGBA {
 | 
			
		||||
	if factor == 0 {
 | 
			
		||||
		return Clone(img)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	lut := make([]uint8, 256)
 | 
			
		||||
	a := math.Min(math.Max(midpoint, 0.0), 1.0)
 | 
			
		||||
	b := math.Abs(factor)
 | 
			
		||||
	sig0 := sigmoid(a, b, 0)
 | 
			
		||||
	sig1 := sigmoid(a, b, 1)
 | 
			
		||||
	e := 1.0e-6
 | 
			
		||||
 | 
			
		||||
	if factor > 0 {
 | 
			
		||||
		for i := 0; i < 256; i++ {
 | 
			
		||||
			x := float64(i) / 255.0
 | 
			
		||||
			sigX := sigmoid(a, b, x)
 | 
			
		||||
			f := (sigX - sig0) / (sig1 - sig0)
 | 
			
		||||
			lut[i] = clamp(f * 255.0)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		for i := 0; i < 256; i++ {
 | 
			
		||||
			x := float64(i) / 255.0
 | 
			
		||||
			arg := math.Min(math.Max((sig1-sig0)*x+sig0, e), 1.0-e)
 | 
			
		||||
			f := a - math.Log(1.0/arg-1.0)/b
 | 
			
		||||
			lut[i] = clamp(f * 255.0)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return adjustLUT(img, lut)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func sigmoid(a, b, x float64) float64 {
 | 
			
		||||
	return 1 / (1 + math.Exp(b*(a-x)))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// adjustLUT applies the given lookup table to the colors of the image.
 | 
			
		||||
func adjustLUT(img image.Image, lut []uint8) *image.NRGBA {
 | 
			
		||||
	src := newScanner(img)
 | 
			
		||||
	dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h))
 | 
			
		||||
	lut = lut[0:256]
 | 
			
		||||
	parallel(0, src.h, func(ys <-chan int) {
 | 
			
		||||
		for y := range ys {
 | 
			
		||||
			i := y * dst.Stride
 | 
			
		||||
			src.scan(0, y, src.w, y+1, dst.Pix[i:i+src.w*4])
 | 
			
		||||
			for x := 0; x < src.w; x++ {
 | 
			
		||||
				d := dst.Pix[i : i+3 : i+3]
 | 
			
		||||
				d[0] = lut[d[0]]
 | 
			
		||||
				d[1] = lut[d[1]]
 | 
			
		||||
				d[2] = lut[d[2]]
 | 
			
		||||
				i += 4
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
	return dst
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AdjustFunc applies the fn function to each pixel of the img image and returns the adjusted image.
 | 
			
		||||
//
 | 
			
		||||
// Example:
 | 
			
		||||
//
 | 
			
		||||
//	dstImage = imaging.AdjustFunc(
 | 
			
		||||
//		srcImage,
 | 
			
		||||
//		func(c color.NRGBA) color.NRGBA {
 | 
			
		||||
//			// Shift the red channel by 16.
 | 
			
		||||
//			r := int(c.R) + 16
 | 
			
		||||
//			if r > 255 {
 | 
			
		||||
//				r = 255
 | 
			
		||||
//			}
 | 
			
		||||
//			return color.NRGBA{uint8(r), c.G, c.B, c.A}
 | 
			
		||||
//		}
 | 
			
		||||
//	)
 | 
			
		||||
//
 | 
			
		||||
func AdjustFunc(img image.Image, fn func(c color.NRGBA) color.NRGBA) *image.NRGBA {
 | 
			
		||||
	src := newScanner(img)
 | 
			
		||||
	dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h))
 | 
			
		||||
	parallel(0, src.h, func(ys <-chan int) {
 | 
			
		||||
		for y := range ys {
 | 
			
		||||
			i := y * dst.Stride
 | 
			
		||||
			src.scan(0, y, src.w, y+1, dst.Pix[i:i+src.w*4])
 | 
			
		||||
			for x := 0; x < src.w; x++ {
 | 
			
		||||
				d := dst.Pix[i : i+4 : i+4]
 | 
			
		||||
				r := d[0]
 | 
			
		||||
				g := d[1]
 | 
			
		||||
				b := d[2]
 | 
			
		||||
				a := d[3]
 | 
			
		||||
				c := fn(color.NRGBA{r, g, b, a})
 | 
			
		||||
				d[0] = c.R
 | 
			
		||||
				d[1] = c.G
 | 
			
		||||
				d[2] = c.B
 | 
			
		||||
				d[3] = c.A
 | 
			
		||||
				i += 4
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
	return dst
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										148
									
								
								vendor/github.com/disintegration/imaging/convolution.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								vendor/github.com/disintegration/imaging/convolution.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,148 @@
 | 
			
		||||
package imaging
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"image"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ConvolveOptions are convolution parameters.
 | 
			
		||||
type ConvolveOptions struct {
 | 
			
		||||
	// If Normalize is true the kernel is normalized before convolution.
 | 
			
		||||
	Normalize bool
 | 
			
		||||
 | 
			
		||||
	// If Abs is true the absolute value of each color channel is taken after convolution.
 | 
			
		||||
	Abs bool
 | 
			
		||||
 | 
			
		||||
	// Bias is added to each color channel value after convolution.
 | 
			
		||||
	Bias int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Convolve3x3 convolves the image with the specified 3x3 convolution kernel.
 | 
			
		||||
// Default parameters are used if a nil *ConvolveOptions is passed.
 | 
			
		||||
func Convolve3x3(img image.Image, kernel [9]float64, options *ConvolveOptions) *image.NRGBA {
 | 
			
		||||
	return convolve(img, kernel[:], options)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Convolve5x5 convolves the image with the specified 5x5 convolution kernel.
 | 
			
		||||
// Default parameters are used if a nil *ConvolveOptions is passed.
 | 
			
		||||
func Convolve5x5(img image.Image, kernel [25]float64, options *ConvolveOptions) *image.NRGBA {
 | 
			
		||||
	return convolve(img, kernel[:], options)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func convolve(img image.Image, kernel []float64, options *ConvolveOptions) *image.NRGBA {
 | 
			
		||||
	src := toNRGBA(img)
 | 
			
		||||
	w := src.Bounds().Max.X
 | 
			
		||||
	h := src.Bounds().Max.Y
 | 
			
		||||
	dst := image.NewNRGBA(image.Rect(0, 0, w, h))
 | 
			
		||||
 | 
			
		||||
	if w < 1 || h < 1 {
 | 
			
		||||
		return dst
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if options == nil {
 | 
			
		||||
		options = &ConvolveOptions{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if options.Normalize {
 | 
			
		||||
		normalizeKernel(kernel)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	type coef struct {
 | 
			
		||||
		x, y int
 | 
			
		||||
		k    float64
 | 
			
		||||
	}
 | 
			
		||||
	var coefs []coef
 | 
			
		||||
	var m int
 | 
			
		||||
 | 
			
		||||
	switch len(kernel) {
 | 
			
		||||
	case 9:
 | 
			
		||||
		m = 1
 | 
			
		||||
	case 25:
 | 
			
		||||
		m = 2
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	i := 0
 | 
			
		||||
	for y := -m; y <= m; y++ {
 | 
			
		||||
		for x := -m; x <= m; x++ {
 | 
			
		||||
			if kernel[i] != 0 {
 | 
			
		||||
				coefs = append(coefs, coef{x: x, y: y, k: kernel[i]})
 | 
			
		||||
			}
 | 
			
		||||
			i++
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	parallel(0, h, func(ys <-chan int) {
 | 
			
		||||
		for y := range ys {
 | 
			
		||||
			for x := 0; x < w; x++ {
 | 
			
		||||
				var r, g, b float64
 | 
			
		||||
				for _, c := range coefs {
 | 
			
		||||
					ix := x + c.x
 | 
			
		||||
					if ix < 0 {
 | 
			
		||||
						ix = 0
 | 
			
		||||
					} else if ix >= w {
 | 
			
		||||
						ix = w - 1
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					iy := y + c.y
 | 
			
		||||
					if iy < 0 {
 | 
			
		||||
						iy = 0
 | 
			
		||||
					} else if iy >= h {
 | 
			
		||||
						iy = h - 1
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					off := iy*src.Stride + ix*4
 | 
			
		||||
					s := src.Pix[off : off+3 : off+3]
 | 
			
		||||
					r += float64(s[0]) * c.k
 | 
			
		||||
					g += float64(s[1]) * c.k
 | 
			
		||||
					b += float64(s[2]) * c.k
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if options.Abs {
 | 
			
		||||
					if r < 0 {
 | 
			
		||||
						r = -r
 | 
			
		||||
					}
 | 
			
		||||
					if g < 0 {
 | 
			
		||||
						g = -g
 | 
			
		||||
					}
 | 
			
		||||
					if b < 0 {
 | 
			
		||||
						b = -b
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if options.Bias != 0 {
 | 
			
		||||
					r += float64(options.Bias)
 | 
			
		||||
					g += float64(options.Bias)
 | 
			
		||||
					b += float64(options.Bias)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				srcOff := y*src.Stride + x*4
 | 
			
		||||
				dstOff := y*dst.Stride + x*4
 | 
			
		||||
				d := dst.Pix[dstOff : dstOff+4 : dstOff+4]
 | 
			
		||||
				d[0] = clamp(r)
 | 
			
		||||
				d[1] = clamp(g)
 | 
			
		||||
				d[2] = clamp(b)
 | 
			
		||||
				d[3] = src.Pix[srcOff+3]
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	return dst
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func normalizeKernel(kernel []float64) {
 | 
			
		||||
	var sum, sumpos float64
 | 
			
		||||
	for i := range kernel {
 | 
			
		||||
		sum += kernel[i]
 | 
			
		||||
		if kernel[i] > 0 {
 | 
			
		||||
			sumpos += kernel[i]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if sum != 0 {
 | 
			
		||||
		for i := range kernel {
 | 
			
		||||
			kernel[i] /= sum
 | 
			
		||||
		}
 | 
			
		||||
	} else if sumpos != 0 {
 | 
			
		||||
		for i := range kernel {
 | 
			
		||||
			kernel[i] /= sumpos
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								vendor/github.com/disintegration/imaging/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								vendor/github.com/disintegration/imaging/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
/*
 | 
			
		||||
Package imaging provides basic image processing functions (resize, rotate, crop, brightness/contrast adjustments, etc.).
 | 
			
		||||
 | 
			
		||||
All the image processing functions provided by the package accept any image type that implements image.Image interface
 | 
			
		||||
as an input, and return a new image of *image.NRGBA type (32bit RGBA colors, non-premultiplied alpha).
 | 
			
		||||
*/
 | 
			
		||||
package imaging
 | 
			
		||||
							
								
								
									
										169
									
								
								vendor/github.com/disintegration/imaging/effects.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										169
									
								
								vendor/github.com/disintegration/imaging/effects.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,169 @@
 | 
			
		||||
package imaging
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"image"
 | 
			
		||||
	"math"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func gaussianBlurKernel(x, sigma float64) float64 {
 | 
			
		||||
	return math.Exp(-(x*x)/(2*sigma*sigma)) / (sigma * math.Sqrt(2*math.Pi))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Blur produces a blurred version of the image using a Gaussian function.
 | 
			
		||||
// Sigma parameter must be positive and indicates how much the image will be blurred.
 | 
			
		||||
//
 | 
			
		||||
// Example:
 | 
			
		||||
//
 | 
			
		||||
//	dstImage := imaging.Blur(srcImage, 3.5)
 | 
			
		||||
//
 | 
			
		||||
func Blur(img image.Image, sigma float64) *image.NRGBA {
 | 
			
		||||
	if sigma <= 0 {
 | 
			
		||||
		return Clone(img)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	radius := int(math.Ceil(sigma * 3.0))
 | 
			
		||||
	kernel := make([]float64, radius+1)
 | 
			
		||||
 | 
			
		||||
	for i := 0; i <= radius; i++ {
 | 
			
		||||
		kernel[i] = gaussianBlurKernel(float64(i), sigma)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return blurVertical(blurHorizontal(img, kernel), kernel)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func blurHorizontal(img image.Image, kernel []float64) *image.NRGBA {
 | 
			
		||||
	src := newScanner(img)
 | 
			
		||||
	dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h))
 | 
			
		||||
	radius := len(kernel) - 1
 | 
			
		||||
 | 
			
		||||
	parallel(0, src.h, func(ys <-chan int) {
 | 
			
		||||
		scanLine := make([]uint8, src.w*4)
 | 
			
		||||
		scanLineF := make([]float64, len(scanLine))
 | 
			
		||||
		for y := range ys {
 | 
			
		||||
			src.scan(0, y, src.w, y+1, scanLine)
 | 
			
		||||
			for i, v := range scanLine {
 | 
			
		||||
				scanLineF[i] = float64(v)
 | 
			
		||||
			}
 | 
			
		||||
			for x := 0; x < src.w; x++ {
 | 
			
		||||
				min := x - radius
 | 
			
		||||
				if min < 0 {
 | 
			
		||||
					min = 0
 | 
			
		||||
				}
 | 
			
		||||
				max := x + radius
 | 
			
		||||
				if max > src.w-1 {
 | 
			
		||||
					max = src.w - 1
 | 
			
		||||
				}
 | 
			
		||||
				var r, g, b, a, wsum float64
 | 
			
		||||
				for ix := min; ix <= max; ix++ {
 | 
			
		||||
					i := ix * 4
 | 
			
		||||
					weight := kernel[absint(x-ix)]
 | 
			
		||||
					wsum += weight
 | 
			
		||||
					s := scanLineF[i : i+4 : i+4]
 | 
			
		||||
					wa := s[3] * weight
 | 
			
		||||
					r += s[0] * wa
 | 
			
		||||
					g += s[1] * wa
 | 
			
		||||
					b += s[2] * wa
 | 
			
		||||
					a += wa
 | 
			
		||||
				}
 | 
			
		||||
				if a != 0 {
 | 
			
		||||
					aInv := 1 / a
 | 
			
		||||
					j := y*dst.Stride + x*4
 | 
			
		||||
					d := dst.Pix[j : j+4 : j+4]
 | 
			
		||||
					d[0] = clamp(r * aInv)
 | 
			
		||||
					d[1] = clamp(g * aInv)
 | 
			
		||||
					d[2] = clamp(b * aInv)
 | 
			
		||||
					d[3] = clamp(a / wsum)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	return dst
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func blurVertical(img image.Image, kernel []float64) *image.NRGBA {
 | 
			
		||||
	src := newScanner(img)
 | 
			
		||||
	dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h))
 | 
			
		||||
	radius := len(kernel) - 1
 | 
			
		||||
 | 
			
		||||
	parallel(0, src.w, func(xs <-chan int) {
 | 
			
		||||
		scanLine := make([]uint8, src.h*4)
 | 
			
		||||
		scanLineF := make([]float64, len(scanLine))
 | 
			
		||||
		for x := range xs {
 | 
			
		||||
			src.scan(x, 0, x+1, src.h, scanLine)
 | 
			
		||||
			for i, v := range scanLine {
 | 
			
		||||
				scanLineF[i] = float64(v)
 | 
			
		||||
			}
 | 
			
		||||
			for y := 0; y < src.h; y++ {
 | 
			
		||||
				min := y - radius
 | 
			
		||||
				if min < 0 {
 | 
			
		||||
					min = 0
 | 
			
		||||
				}
 | 
			
		||||
				max := y + radius
 | 
			
		||||
				if max > src.h-1 {
 | 
			
		||||
					max = src.h - 1
 | 
			
		||||
				}
 | 
			
		||||
				var r, g, b, a, wsum float64
 | 
			
		||||
				for iy := min; iy <= max; iy++ {
 | 
			
		||||
					i := iy * 4
 | 
			
		||||
					weight := kernel[absint(y-iy)]
 | 
			
		||||
					wsum += weight
 | 
			
		||||
					s := scanLineF[i : i+4 : i+4]
 | 
			
		||||
					wa := s[3] * weight
 | 
			
		||||
					r += s[0] * wa
 | 
			
		||||
					g += s[1] * wa
 | 
			
		||||
					b += s[2] * wa
 | 
			
		||||
					a += wa
 | 
			
		||||
				}
 | 
			
		||||
				if a != 0 {
 | 
			
		||||
					aInv := 1 / a
 | 
			
		||||
					j := y*dst.Stride + x*4
 | 
			
		||||
					d := dst.Pix[j : j+4 : j+4]
 | 
			
		||||
					d[0] = clamp(r * aInv)
 | 
			
		||||
					d[1] = clamp(g * aInv)
 | 
			
		||||
					d[2] = clamp(b * aInv)
 | 
			
		||||
					d[3] = clamp(a / wsum)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	return dst
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Sharpen produces a sharpened version of the image.
 | 
			
		||||
// Sigma parameter must be positive and indicates how much the image will be sharpened.
 | 
			
		||||
//
 | 
			
		||||
// Example:
 | 
			
		||||
//
 | 
			
		||||
//	dstImage := imaging.Sharpen(srcImage, 3.5)
 | 
			
		||||
//
 | 
			
		||||
func Sharpen(img image.Image, sigma float64) *image.NRGBA {
 | 
			
		||||
	if sigma <= 0 {
 | 
			
		||||
		return Clone(img)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	src := newScanner(img)
 | 
			
		||||
	dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h))
 | 
			
		||||
	blurred := Blur(img, sigma)
 | 
			
		||||
 | 
			
		||||
	parallel(0, src.h, func(ys <-chan int) {
 | 
			
		||||
		scanLine := make([]uint8, src.w*4)
 | 
			
		||||
		for y := range ys {
 | 
			
		||||
			src.scan(0, y, src.w, y+1, scanLine)
 | 
			
		||||
			j := y * dst.Stride
 | 
			
		||||
			for i := 0; i < src.w*4; i++ {
 | 
			
		||||
				val := int(scanLine[i])<<1 - int(blurred.Pix[j])
 | 
			
		||||
				if val < 0 {
 | 
			
		||||
					val = 0
 | 
			
		||||
				} else if val > 0xff {
 | 
			
		||||
					val = 0xff
 | 
			
		||||
				}
 | 
			
		||||
				dst.Pix[j] = uint8(val)
 | 
			
		||||
				j++
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	return dst
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										52
									
								
								vendor/github.com/disintegration/imaging/histogram.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								vendor/github.com/disintegration/imaging/histogram.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,52 @@
 | 
			
		||||
package imaging
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"image"
 | 
			
		||||
	"sync"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Histogram returns a normalized histogram of an image.
 | 
			
		||||
//
 | 
			
		||||
// Resulting histogram is represented as an array of 256 floats, where
 | 
			
		||||
// histogram[i] is a probability of a pixel being of a particular luminance i.
 | 
			
		||||
func Histogram(img image.Image) [256]float64 {
 | 
			
		||||
	var mu sync.Mutex
 | 
			
		||||
	var histogram [256]float64
 | 
			
		||||
	var total float64
 | 
			
		||||
 | 
			
		||||
	src := newScanner(img)
 | 
			
		||||
	if src.w == 0 || src.h == 0 {
 | 
			
		||||
		return histogram
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	parallel(0, src.h, func(ys <-chan int) {
 | 
			
		||||
		var tmpHistogram [256]float64
 | 
			
		||||
		var tmpTotal float64
 | 
			
		||||
		scanLine := make([]uint8, src.w*4)
 | 
			
		||||
		for y := range ys {
 | 
			
		||||
			src.scan(0, y, src.w, y+1, scanLine)
 | 
			
		||||
			i := 0
 | 
			
		||||
			for x := 0; x < src.w; x++ {
 | 
			
		||||
				s := scanLine[i : i+3 : i+3]
 | 
			
		||||
				r := s[0]
 | 
			
		||||
				g := s[1]
 | 
			
		||||
				b := s[2]
 | 
			
		||||
				y := 0.299*float32(r) + 0.587*float32(g) + 0.114*float32(b)
 | 
			
		||||
				tmpHistogram[int(y+0.5)]++
 | 
			
		||||
				tmpTotal++
 | 
			
		||||
				i += 4
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		mu.Lock()
 | 
			
		||||
		for i := 0; i < 256; i++ {
 | 
			
		||||
			histogram[i] += tmpHistogram[i]
 | 
			
		||||
		}
 | 
			
		||||
		total += tmpTotal
 | 
			
		||||
		mu.Unlock()
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < 256; i++ {
 | 
			
		||||
		histogram[i] = histogram[i] / total
 | 
			
		||||
	}
 | 
			
		||||
	return histogram
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										444
									
								
								vendor/github.com/disintegration/imaging/io.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										444
									
								
								vendor/github.com/disintegration/imaging/io.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,444 @@
 | 
			
		||||
package imaging
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"image"
 | 
			
		||||
	"image/draw"
 | 
			
		||||
	"image/gif"
 | 
			
		||||
	"image/jpeg"
 | 
			
		||||
	"image/png"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/image/bmp"
 | 
			
		||||
	"golang.org/x/image/tiff"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type fileSystem interface {
 | 
			
		||||
	Create(string) (io.WriteCloser, error)
 | 
			
		||||
	Open(string) (io.ReadCloser, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type localFS struct{}
 | 
			
		||||
 | 
			
		||||
func (localFS) Create(name string) (io.WriteCloser, error) { return os.Create(name) }
 | 
			
		||||
func (localFS) Open(name string) (io.ReadCloser, error)    { return os.Open(name) }
 | 
			
		||||
 | 
			
		||||
var fs fileSystem = localFS{}
 | 
			
		||||
 | 
			
		||||
type decodeConfig struct {
 | 
			
		||||
	autoOrientation bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var defaultDecodeConfig = decodeConfig{
 | 
			
		||||
	autoOrientation: false,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DecodeOption sets an optional parameter for the Decode and Open functions.
 | 
			
		||||
type DecodeOption func(*decodeConfig)
 | 
			
		||||
 | 
			
		||||
// AutoOrientation returns a DecodeOption that sets the auto-orientation mode.
 | 
			
		||||
// If auto-orientation is enabled, the image will be transformed after decoding
 | 
			
		||||
// according to the EXIF orientation tag (if present). By default it's disabled.
 | 
			
		||||
func AutoOrientation(enabled bool) DecodeOption {
 | 
			
		||||
	return func(c *decodeConfig) {
 | 
			
		||||
		c.autoOrientation = enabled
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Decode reads an image from r.
 | 
			
		||||
func Decode(r io.Reader, opts ...DecodeOption) (image.Image, error) {
 | 
			
		||||
	cfg := defaultDecodeConfig
 | 
			
		||||
	for _, option := range opts {
 | 
			
		||||
		option(&cfg)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !cfg.autoOrientation {
 | 
			
		||||
		img, _, err := image.Decode(r)
 | 
			
		||||
		return img, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var orient orientation
 | 
			
		||||
	pr, pw := io.Pipe()
 | 
			
		||||
	r = io.TeeReader(r, pw)
 | 
			
		||||
	done := make(chan struct{})
 | 
			
		||||
	go func() {
 | 
			
		||||
		defer close(done)
 | 
			
		||||
		orient = readOrientation(pr)
 | 
			
		||||
		io.Copy(ioutil.Discard, pr)
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	img, _, err := image.Decode(r)
 | 
			
		||||
	pw.Close()
 | 
			
		||||
	<-done
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return fixOrientation(img, orient), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Open loads an image from file.
 | 
			
		||||
//
 | 
			
		||||
// Examples:
 | 
			
		||||
//
 | 
			
		||||
//	// Load an image from file.
 | 
			
		||||
//	img, err := imaging.Open("test.jpg")
 | 
			
		||||
//
 | 
			
		||||
//	// Load an image and transform it depending on the EXIF orientation tag (if present).
 | 
			
		||||
//	img, err := imaging.Open("test.jpg", imaging.AutoOrientation(true))
 | 
			
		||||
//
 | 
			
		||||
func Open(filename string, opts ...DecodeOption) (image.Image, error) {
 | 
			
		||||
	file, err := fs.Open(filename)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	defer file.Close()
 | 
			
		||||
	return Decode(file, opts...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Format is an image file format.
 | 
			
		||||
type Format int
 | 
			
		||||
 | 
			
		||||
// Image file formats.
 | 
			
		||||
const (
 | 
			
		||||
	JPEG Format = iota
 | 
			
		||||
	PNG
 | 
			
		||||
	GIF
 | 
			
		||||
	TIFF
 | 
			
		||||
	BMP
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var formatExts = map[string]Format{
 | 
			
		||||
	"jpg":  JPEG,
 | 
			
		||||
	"jpeg": JPEG,
 | 
			
		||||
	"png":  PNG,
 | 
			
		||||
	"gif":  GIF,
 | 
			
		||||
	"tif":  TIFF,
 | 
			
		||||
	"tiff": TIFF,
 | 
			
		||||
	"bmp":  BMP,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var formatNames = map[Format]string{
 | 
			
		||||
	JPEG: "JPEG",
 | 
			
		||||
	PNG:  "PNG",
 | 
			
		||||
	GIF:  "GIF",
 | 
			
		||||
	TIFF: "TIFF",
 | 
			
		||||
	BMP:  "BMP",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f Format) String() string {
 | 
			
		||||
	return formatNames[f]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ErrUnsupportedFormat means the given image format is not supported.
 | 
			
		||||
var ErrUnsupportedFormat = errors.New("imaging: unsupported image format")
 | 
			
		||||
 | 
			
		||||
// FormatFromExtension parses image format from filename extension:
 | 
			
		||||
// "jpg" (or "jpeg"), "png", "gif", "tif" (or "tiff") and "bmp" are supported.
 | 
			
		||||
func FormatFromExtension(ext string) (Format, error) {
 | 
			
		||||
	if f, ok := formatExts[strings.ToLower(strings.TrimPrefix(ext, "."))]; ok {
 | 
			
		||||
		return f, nil
 | 
			
		||||
	}
 | 
			
		||||
	return -1, ErrUnsupportedFormat
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FormatFromFilename parses image format from filename:
 | 
			
		||||
// "jpg" (or "jpeg"), "png", "gif", "tif" (or "tiff") and "bmp" are supported.
 | 
			
		||||
func FormatFromFilename(filename string) (Format, error) {
 | 
			
		||||
	ext := filepath.Ext(filename)
 | 
			
		||||
	return FormatFromExtension(ext)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type encodeConfig struct {
 | 
			
		||||
	jpegQuality         int
 | 
			
		||||
	gifNumColors        int
 | 
			
		||||
	gifQuantizer        draw.Quantizer
 | 
			
		||||
	gifDrawer           draw.Drawer
 | 
			
		||||
	pngCompressionLevel png.CompressionLevel
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var defaultEncodeConfig = encodeConfig{
 | 
			
		||||
	jpegQuality:         95,
 | 
			
		||||
	gifNumColors:        256,
 | 
			
		||||
	gifQuantizer:        nil,
 | 
			
		||||
	gifDrawer:           nil,
 | 
			
		||||
	pngCompressionLevel: png.DefaultCompression,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EncodeOption sets an optional parameter for the Encode and Save functions.
 | 
			
		||||
type EncodeOption func(*encodeConfig)
 | 
			
		||||
 | 
			
		||||
// JPEGQuality returns an EncodeOption that sets the output JPEG quality.
 | 
			
		||||
// Quality ranges from 1 to 100 inclusive, higher is better. Default is 95.
 | 
			
		||||
func JPEGQuality(quality int) EncodeOption {
 | 
			
		||||
	return func(c *encodeConfig) {
 | 
			
		||||
		c.jpegQuality = quality
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GIFNumColors returns an EncodeOption that sets the maximum number of colors
 | 
			
		||||
// used in the GIF-encoded image. It ranges from 1 to 256.  Default is 256.
 | 
			
		||||
func GIFNumColors(numColors int) EncodeOption {
 | 
			
		||||
	return func(c *encodeConfig) {
 | 
			
		||||
		c.gifNumColors = numColors
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GIFQuantizer returns an EncodeOption that sets the quantizer that is used to produce
 | 
			
		||||
// a palette of the GIF-encoded image.
 | 
			
		||||
func GIFQuantizer(quantizer draw.Quantizer) EncodeOption {
 | 
			
		||||
	return func(c *encodeConfig) {
 | 
			
		||||
		c.gifQuantizer = quantizer
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GIFDrawer returns an EncodeOption that sets the drawer that is used to convert
 | 
			
		||||
// the source image to the desired palette of the GIF-encoded image.
 | 
			
		||||
func GIFDrawer(drawer draw.Drawer) EncodeOption {
 | 
			
		||||
	return func(c *encodeConfig) {
 | 
			
		||||
		c.gifDrawer = drawer
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PNGCompressionLevel returns an EncodeOption that sets the compression level
 | 
			
		||||
// of the PNG-encoded image. Default is png.DefaultCompression.
 | 
			
		||||
func PNGCompressionLevel(level png.CompressionLevel) EncodeOption {
 | 
			
		||||
	return func(c *encodeConfig) {
 | 
			
		||||
		c.pngCompressionLevel = level
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Encode writes the image img to w in the specified format (JPEG, PNG, GIF, TIFF or BMP).
 | 
			
		||||
func Encode(w io.Writer, img image.Image, format Format, opts ...EncodeOption) error {
 | 
			
		||||
	cfg := defaultEncodeConfig
 | 
			
		||||
	for _, option := range opts {
 | 
			
		||||
		option(&cfg)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch format {
 | 
			
		||||
	case JPEG:
 | 
			
		||||
		if nrgba, ok := img.(*image.NRGBA); ok && nrgba.Opaque() {
 | 
			
		||||
			rgba := &image.RGBA{
 | 
			
		||||
				Pix:    nrgba.Pix,
 | 
			
		||||
				Stride: nrgba.Stride,
 | 
			
		||||
				Rect:   nrgba.Rect,
 | 
			
		||||
			}
 | 
			
		||||
			return jpeg.Encode(w, rgba, &jpeg.Options{Quality: cfg.jpegQuality})
 | 
			
		||||
		}
 | 
			
		||||
		return jpeg.Encode(w, img, &jpeg.Options{Quality: cfg.jpegQuality})
 | 
			
		||||
 | 
			
		||||
	case PNG:
 | 
			
		||||
		encoder := png.Encoder{CompressionLevel: cfg.pngCompressionLevel}
 | 
			
		||||
		return encoder.Encode(w, img)
 | 
			
		||||
 | 
			
		||||
	case GIF:
 | 
			
		||||
		return gif.Encode(w, img, &gif.Options{
 | 
			
		||||
			NumColors: cfg.gifNumColors,
 | 
			
		||||
			Quantizer: cfg.gifQuantizer,
 | 
			
		||||
			Drawer:    cfg.gifDrawer,
 | 
			
		||||
		})
 | 
			
		||||
 | 
			
		||||
	case TIFF:
 | 
			
		||||
		return tiff.Encode(w, img, &tiff.Options{Compression: tiff.Deflate, Predictor: true})
 | 
			
		||||
 | 
			
		||||
	case BMP:
 | 
			
		||||
		return bmp.Encode(w, img)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ErrUnsupportedFormat
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Save saves the image to file with the specified filename.
 | 
			
		||||
// The format is determined from the filename extension:
 | 
			
		||||
// "jpg" (or "jpeg"), "png", "gif", "tif" (or "tiff") and "bmp" are supported.
 | 
			
		||||
//
 | 
			
		||||
// Examples:
 | 
			
		||||
//
 | 
			
		||||
//	// Save the image as PNG.
 | 
			
		||||
//	err := imaging.Save(img, "out.png")
 | 
			
		||||
//
 | 
			
		||||
//	// Save the image as JPEG with optional quality parameter set to 80.
 | 
			
		||||
//	err := imaging.Save(img, "out.jpg", imaging.JPEGQuality(80))
 | 
			
		||||
//
 | 
			
		||||
func Save(img image.Image, filename string, opts ...EncodeOption) (err error) {
 | 
			
		||||
	f, err := FormatFromFilename(filename)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	file, err := fs.Create(filename)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	err = Encode(file, img, f, opts...)
 | 
			
		||||
	errc := file.Close()
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		err = errc
 | 
			
		||||
	}
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// orientation is an EXIF flag that specifies the transformation
 | 
			
		||||
// that should be applied to image to display it correctly.
 | 
			
		||||
type orientation int
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	orientationUnspecified = 0
 | 
			
		||||
	orientationNormal      = 1
 | 
			
		||||
	orientationFlipH       = 2
 | 
			
		||||
	orientationRotate180   = 3
 | 
			
		||||
	orientationFlipV       = 4
 | 
			
		||||
	orientationTranspose   = 5
 | 
			
		||||
	orientationRotate270   = 6
 | 
			
		||||
	orientationTransverse  = 7
 | 
			
		||||
	orientationRotate90    = 8
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// readOrientation tries to read the orientation EXIF flag from image data in r.
 | 
			
		||||
// If the EXIF data block is not found or the orientation flag is not found
 | 
			
		||||
// or any other error occures while reading the data, it returns the
 | 
			
		||||
// orientationUnspecified (0) value.
 | 
			
		||||
func readOrientation(r io.Reader) orientation {
 | 
			
		||||
	const (
 | 
			
		||||
		markerSOI      = 0xffd8
 | 
			
		||||
		markerAPP1     = 0xffe1
 | 
			
		||||
		exifHeader     = 0x45786966
 | 
			
		||||
		byteOrderBE    = 0x4d4d
 | 
			
		||||
		byteOrderLE    = 0x4949
 | 
			
		||||
		orientationTag = 0x0112
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	// Check if JPEG SOI marker is present.
 | 
			
		||||
	var soi uint16
 | 
			
		||||
	if err := binary.Read(r, binary.BigEndian, &soi); err != nil {
 | 
			
		||||
		return orientationUnspecified
 | 
			
		||||
	}
 | 
			
		||||
	if soi != markerSOI {
 | 
			
		||||
		return orientationUnspecified // Missing JPEG SOI marker.
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Find JPEG APP1 marker.
 | 
			
		||||
	for {
 | 
			
		||||
		var marker, size uint16
 | 
			
		||||
		if err := binary.Read(r, binary.BigEndian, &marker); err != nil {
 | 
			
		||||
			return orientationUnspecified
 | 
			
		||||
		}
 | 
			
		||||
		if err := binary.Read(r, binary.BigEndian, &size); err != nil {
 | 
			
		||||
			return orientationUnspecified
 | 
			
		||||
		}
 | 
			
		||||
		if marker>>8 != 0xff {
 | 
			
		||||
			return orientationUnspecified // Invalid JPEG marker.
 | 
			
		||||
		}
 | 
			
		||||
		if marker == markerAPP1 {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		if size < 2 {
 | 
			
		||||
			return orientationUnspecified // Invalid block size.
 | 
			
		||||
		}
 | 
			
		||||
		if _, err := io.CopyN(ioutil.Discard, r, int64(size-2)); err != nil {
 | 
			
		||||
			return orientationUnspecified
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Check if EXIF header is present.
 | 
			
		||||
	var header uint32
 | 
			
		||||
	if err := binary.Read(r, binary.BigEndian, &header); err != nil {
 | 
			
		||||
		return orientationUnspecified
 | 
			
		||||
	}
 | 
			
		||||
	if header != exifHeader {
 | 
			
		||||
		return orientationUnspecified
 | 
			
		||||
	}
 | 
			
		||||
	if _, err := io.CopyN(ioutil.Discard, r, 2); err != nil {
 | 
			
		||||
		return orientationUnspecified
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Read byte order information.
 | 
			
		||||
	var (
 | 
			
		||||
		byteOrderTag uint16
 | 
			
		||||
		byteOrder    binary.ByteOrder
 | 
			
		||||
	)
 | 
			
		||||
	if err := binary.Read(r, binary.BigEndian, &byteOrderTag); err != nil {
 | 
			
		||||
		return orientationUnspecified
 | 
			
		||||
	}
 | 
			
		||||
	switch byteOrderTag {
 | 
			
		||||
	case byteOrderBE:
 | 
			
		||||
		byteOrder = binary.BigEndian
 | 
			
		||||
	case byteOrderLE:
 | 
			
		||||
		byteOrder = binary.LittleEndian
 | 
			
		||||
	default:
 | 
			
		||||
		return orientationUnspecified // Invalid byte order flag.
 | 
			
		||||
	}
 | 
			
		||||
	if _, err := io.CopyN(ioutil.Discard, r, 2); err != nil {
 | 
			
		||||
		return orientationUnspecified
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Skip the EXIF offset.
 | 
			
		||||
	var offset uint32
 | 
			
		||||
	if err := binary.Read(r, byteOrder, &offset); err != nil {
 | 
			
		||||
		return orientationUnspecified
 | 
			
		||||
	}
 | 
			
		||||
	if offset < 8 {
 | 
			
		||||
		return orientationUnspecified // Invalid offset value.
 | 
			
		||||
	}
 | 
			
		||||
	if _, err := io.CopyN(ioutil.Discard, r, int64(offset-8)); err != nil {
 | 
			
		||||
		return orientationUnspecified
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Read the number of tags.
 | 
			
		||||
	var numTags uint16
 | 
			
		||||
	if err := binary.Read(r, byteOrder, &numTags); err != nil {
 | 
			
		||||
		return orientationUnspecified
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Find the orientation tag.
 | 
			
		||||
	for i := 0; i < int(numTags); i++ {
 | 
			
		||||
		var tag uint16
 | 
			
		||||
		if err := binary.Read(r, byteOrder, &tag); err != nil {
 | 
			
		||||
			return orientationUnspecified
 | 
			
		||||
		}
 | 
			
		||||
		if tag != orientationTag {
 | 
			
		||||
			if _, err := io.CopyN(ioutil.Discard, r, 10); err != nil {
 | 
			
		||||
				return orientationUnspecified
 | 
			
		||||
			}
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if _, err := io.CopyN(ioutil.Discard, r, 6); err != nil {
 | 
			
		||||
			return orientationUnspecified
 | 
			
		||||
		}
 | 
			
		||||
		var val uint16
 | 
			
		||||
		if err := binary.Read(r, byteOrder, &val); err != nil {
 | 
			
		||||
			return orientationUnspecified
 | 
			
		||||
		}
 | 
			
		||||
		if val < 1 || val > 8 {
 | 
			
		||||
			return orientationUnspecified // Invalid tag value.
 | 
			
		||||
		}
 | 
			
		||||
		return orientation(val)
 | 
			
		||||
	}
 | 
			
		||||
	return orientationUnspecified // Missing orientation tag.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// fixOrientation applies a transform to img corresponding to the given orientation flag.
 | 
			
		||||
func fixOrientation(img image.Image, o orientation) image.Image {
 | 
			
		||||
	switch o {
 | 
			
		||||
	case orientationNormal:
 | 
			
		||||
	case orientationFlipH:
 | 
			
		||||
		img = FlipH(img)
 | 
			
		||||
	case orientationFlipV:
 | 
			
		||||
		img = FlipV(img)
 | 
			
		||||
	case orientationRotate90:
 | 
			
		||||
		img = Rotate90(img)
 | 
			
		||||
	case orientationRotate180:
 | 
			
		||||
		img = Rotate180(img)
 | 
			
		||||
	case orientationRotate270:
 | 
			
		||||
		img = Rotate270(img)
 | 
			
		||||
	case orientationTranspose:
 | 
			
		||||
		img = Transpose(img)
 | 
			
		||||
	case orientationTransverse:
 | 
			
		||||
		img = Transverse(img)
 | 
			
		||||
	}
 | 
			
		||||
	return img
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										595
									
								
								vendor/github.com/disintegration/imaging/resize.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										595
									
								
								vendor/github.com/disintegration/imaging/resize.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,595 @@
 | 
			
		||||
package imaging
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"image"
 | 
			
		||||
	"math"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type indexWeight struct {
 | 
			
		||||
	index  int
 | 
			
		||||
	weight float64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func precomputeWeights(dstSize, srcSize int, filter ResampleFilter) [][]indexWeight {
 | 
			
		||||
	du := float64(srcSize) / float64(dstSize)
 | 
			
		||||
	scale := du
 | 
			
		||||
	if scale < 1.0 {
 | 
			
		||||
		scale = 1.0
 | 
			
		||||
	}
 | 
			
		||||
	ru := math.Ceil(scale * filter.Support)
 | 
			
		||||
 | 
			
		||||
	out := make([][]indexWeight, dstSize)
 | 
			
		||||
	tmp := make([]indexWeight, 0, dstSize*int(ru+2)*2)
 | 
			
		||||
 | 
			
		||||
	for v := 0; v < dstSize; v++ {
 | 
			
		||||
		fu := (float64(v)+0.5)*du - 0.5
 | 
			
		||||
 | 
			
		||||
		begin := int(math.Ceil(fu - ru))
 | 
			
		||||
		if begin < 0 {
 | 
			
		||||
			begin = 0
 | 
			
		||||
		}
 | 
			
		||||
		end := int(math.Floor(fu + ru))
 | 
			
		||||
		if end > srcSize-1 {
 | 
			
		||||
			end = srcSize - 1
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var sum float64
 | 
			
		||||
		for u := begin; u <= end; u++ {
 | 
			
		||||
			w := filter.Kernel((float64(u) - fu) / scale)
 | 
			
		||||
			if w != 0 {
 | 
			
		||||
				sum += w
 | 
			
		||||
				tmp = append(tmp, indexWeight{index: u, weight: w})
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if sum != 0 {
 | 
			
		||||
			for i := range tmp {
 | 
			
		||||
				tmp[i].weight /= sum
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		out[v] = tmp
 | 
			
		||||
		tmp = tmp[len(tmp):]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return out
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Resize resizes the image to the specified width and height using the specified resampling
 | 
			
		||||
// filter and returns the transformed image. If one of width or height is 0, the image aspect
 | 
			
		||||
// ratio is preserved.
 | 
			
		||||
//
 | 
			
		||||
// Example:
 | 
			
		||||
//
 | 
			
		||||
//	dstImage := imaging.Resize(srcImage, 800, 600, imaging.Lanczos)
 | 
			
		||||
//
 | 
			
		||||
func Resize(img image.Image, width, height int, filter ResampleFilter) *image.NRGBA {
 | 
			
		||||
	dstW, dstH := width, height
 | 
			
		||||
	if dstW < 0 || dstH < 0 {
 | 
			
		||||
		return &image.NRGBA{}
 | 
			
		||||
	}
 | 
			
		||||
	if dstW == 0 && dstH == 0 {
 | 
			
		||||
		return &image.NRGBA{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	srcW := img.Bounds().Dx()
 | 
			
		||||
	srcH := img.Bounds().Dy()
 | 
			
		||||
	if srcW <= 0 || srcH <= 0 {
 | 
			
		||||
		return &image.NRGBA{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If new width or height is 0 then preserve aspect ratio, minimum 1px.
 | 
			
		||||
	if dstW == 0 {
 | 
			
		||||
		tmpW := float64(dstH) * float64(srcW) / float64(srcH)
 | 
			
		||||
		dstW = int(math.Max(1.0, math.Floor(tmpW+0.5)))
 | 
			
		||||
	}
 | 
			
		||||
	if dstH == 0 {
 | 
			
		||||
		tmpH := float64(dstW) * float64(srcH) / float64(srcW)
 | 
			
		||||
		dstH = int(math.Max(1.0, math.Floor(tmpH+0.5)))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if filter.Support <= 0 {
 | 
			
		||||
		// Nearest-neighbor special case.
 | 
			
		||||
		return resizeNearest(img, dstW, dstH)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if srcW != dstW && srcH != dstH {
 | 
			
		||||
		return resizeVertical(resizeHorizontal(img, dstW, filter), dstH, filter)
 | 
			
		||||
	}
 | 
			
		||||
	if srcW != dstW {
 | 
			
		||||
		return resizeHorizontal(img, dstW, filter)
 | 
			
		||||
	}
 | 
			
		||||
	if srcH != dstH {
 | 
			
		||||
		return resizeVertical(img, dstH, filter)
 | 
			
		||||
	}
 | 
			
		||||
	return Clone(img)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func resizeHorizontal(img image.Image, width int, filter ResampleFilter) *image.NRGBA {
 | 
			
		||||
	src := newScanner(img)
 | 
			
		||||
	dst := image.NewNRGBA(image.Rect(0, 0, width, src.h))
 | 
			
		||||
	weights := precomputeWeights(width, src.w, filter)
 | 
			
		||||
	parallel(0, src.h, func(ys <-chan int) {
 | 
			
		||||
		scanLine := make([]uint8, src.w*4)
 | 
			
		||||
		for y := range ys {
 | 
			
		||||
			src.scan(0, y, src.w, y+1, scanLine)
 | 
			
		||||
			j0 := y * dst.Stride
 | 
			
		||||
			for x := range weights {
 | 
			
		||||
				var r, g, b, a float64
 | 
			
		||||
				for _, w := range weights[x] {
 | 
			
		||||
					i := w.index * 4
 | 
			
		||||
					s := scanLine[i : i+4 : i+4]
 | 
			
		||||
					aw := float64(s[3]) * w.weight
 | 
			
		||||
					r += float64(s[0]) * aw
 | 
			
		||||
					g += float64(s[1]) * aw
 | 
			
		||||
					b += float64(s[2]) * aw
 | 
			
		||||
					a += aw
 | 
			
		||||
				}
 | 
			
		||||
				if a != 0 {
 | 
			
		||||
					aInv := 1 / a
 | 
			
		||||
					j := j0 + x*4
 | 
			
		||||
					d := dst.Pix[j : j+4 : j+4]
 | 
			
		||||
					d[0] = clamp(r * aInv)
 | 
			
		||||
					d[1] = clamp(g * aInv)
 | 
			
		||||
					d[2] = clamp(b * aInv)
 | 
			
		||||
					d[3] = clamp(a)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
	return dst
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func resizeVertical(img image.Image, height int, filter ResampleFilter) *image.NRGBA {
 | 
			
		||||
	src := newScanner(img)
 | 
			
		||||
	dst := image.NewNRGBA(image.Rect(0, 0, src.w, height))
 | 
			
		||||
	weights := precomputeWeights(height, src.h, filter)
 | 
			
		||||
	parallel(0, src.w, func(xs <-chan int) {
 | 
			
		||||
		scanLine := make([]uint8, src.h*4)
 | 
			
		||||
		for x := range xs {
 | 
			
		||||
			src.scan(x, 0, x+1, src.h, scanLine)
 | 
			
		||||
			for y := range weights {
 | 
			
		||||
				var r, g, b, a float64
 | 
			
		||||
				for _, w := range weights[y] {
 | 
			
		||||
					i := w.index * 4
 | 
			
		||||
					s := scanLine[i : i+4 : i+4]
 | 
			
		||||
					aw := float64(s[3]) * w.weight
 | 
			
		||||
					r += float64(s[0]) * aw
 | 
			
		||||
					g += float64(s[1]) * aw
 | 
			
		||||
					b += float64(s[2]) * aw
 | 
			
		||||
					a += aw
 | 
			
		||||
				}
 | 
			
		||||
				if a != 0 {
 | 
			
		||||
					aInv := 1 / a
 | 
			
		||||
					j := y*dst.Stride + x*4
 | 
			
		||||
					d := dst.Pix[j : j+4 : j+4]
 | 
			
		||||
					d[0] = clamp(r * aInv)
 | 
			
		||||
					d[1] = clamp(g * aInv)
 | 
			
		||||
					d[2] = clamp(b * aInv)
 | 
			
		||||
					d[3] = clamp(a)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
	return dst
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// resizeNearest is a fast nearest-neighbor resize, no filtering.
 | 
			
		||||
func resizeNearest(img image.Image, width, height int) *image.NRGBA {
 | 
			
		||||
	dst := image.NewNRGBA(image.Rect(0, 0, width, height))
 | 
			
		||||
	dx := float64(img.Bounds().Dx()) / float64(width)
 | 
			
		||||
	dy := float64(img.Bounds().Dy()) / float64(height)
 | 
			
		||||
 | 
			
		||||
	if dx > 1 && dy > 1 {
 | 
			
		||||
		src := newScanner(img)
 | 
			
		||||
		parallel(0, height, func(ys <-chan int) {
 | 
			
		||||
			for y := range ys {
 | 
			
		||||
				srcY := int((float64(y) + 0.5) * dy)
 | 
			
		||||
				dstOff := y * dst.Stride
 | 
			
		||||
				for x := 0; x < width; x++ {
 | 
			
		||||
					srcX := int((float64(x) + 0.5) * dx)
 | 
			
		||||
					src.scan(srcX, srcY, srcX+1, srcY+1, dst.Pix[dstOff:dstOff+4])
 | 
			
		||||
					dstOff += 4
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	} else {
 | 
			
		||||
		src := toNRGBA(img)
 | 
			
		||||
		parallel(0, height, func(ys <-chan int) {
 | 
			
		||||
			for y := range ys {
 | 
			
		||||
				srcY := int((float64(y) + 0.5) * dy)
 | 
			
		||||
				srcOff0 := srcY * src.Stride
 | 
			
		||||
				dstOff := y * dst.Stride
 | 
			
		||||
				for x := 0; x < width; x++ {
 | 
			
		||||
					srcX := int((float64(x) + 0.5) * dx)
 | 
			
		||||
					srcOff := srcOff0 + srcX*4
 | 
			
		||||
					copy(dst.Pix[dstOff:dstOff+4], src.Pix[srcOff:srcOff+4])
 | 
			
		||||
					dstOff += 4
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return dst
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Fit scales down the image using the specified resample filter to fit the specified
 | 
			
		||||
// maximum width and height and returns the transformed image.
 | 
			
		||||
//
 | 
			
		||||
// Example:
 | 
			
		||||
//
 | 
			
		||||
//	dstImage := imaging.Fit(srcImage, 800, 600, imaging.Lanczos)
 | 
			
		||||
//
 | 
			
		||||
func Fit(img image.Image, width, height int, filter ResampleFilter) *image.NRGBA {
 | 
			
		||||
	maxW, maxH := width, height
 | 
			
		||||
 | 
			
		||||
	if maxW <= 0 || maxH <= 0 {
 | 
			
		||||
		return &image.NRGBA{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	srcBounds := img.Bounds()
 | 
			
		||||
	srcW := srcBounds.Dx()
 | 
			
		||||
	srcH := srcBounds.Dy()
 | 
			
		||||
 | 
			
		||||
	if srcW <= 0 || srcH <= 0 {
 | 
			
		||||
		return &image.NRGBA{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if srcW <= maxW && srcH <= maxH {
 | 
			
		||||
		return Clone(img)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	srcAspectRatio := float64(srcW) / float64(srcH)
 | 
			
		||||
	maxAspectRatio := float64(maxW) / float64(maxH)
 | 
			
		||||
 | 
			
		||||
	var newW, newH int
 | 
			
		||||
	if srcAspectRatio > maxAspectRatio {
 | 
			
		||||
		newW = maxW
 | 
			
		||||
		newH = int(float64(newW) / srcAspectRatio)
 | 
			
		||||
	} else {
 | 
			
		||||
		newH = maxH
 | 
			
		||||
		newW = int(float64(newH) * srcAspectRatio)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return Resize(img, newW, newH, filter)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Fill creates an image with the specified dimensions and fills it with the scaled source image.
 | 
			
		||||
// To achieve the correct aspect ratio without stretching, the source image will be cropped.
 | 
			
		||||
//
 | 
			
		||||
// Example:
 | 
			
		||||
//
 | 
			
		||||
//	dstImage := imaging.Fill(srcImage, 800, 600, imaging.Center, imaging.Lanczos)
 | 
			
		||||
//
 | 
			
		||||
func Fill(img image.Image, width, height int, anchor Anchor, filter ResampleFilter) *image.NRGBA {
 | 
			
		||||
	dstW, dstH := width, height
 | 
			
		||||
 | 
			
		||||
	if dstW <= 0 || dstH <= 0 {
 | 
			
		||||
		return &image.NRGBA{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	srcBounds := img.Bounds()
 | 
			
		||||
	srcW := srcBounds.Dx()
 | 
			
		||||
	srcH := srcBounds.Dy()
 | 
			
		||||
 | 
			
		||||
	if srcW <= 0 || srcH <= 0 {
 | 
			
		||||
		return &image.NRGBA{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if srcW == dstW && srcH == dstH {
 | 
			
		||||
		return Clone(img)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if srcW >= 100 && srcH >= 100 {
 | 
			
		||||
		return cropAndResize(img, dstW, dstH, anchor, filter)
 | 
			
		||||
	}
 | 
			
		||||
	return resizeAndCrop(img, dstW, dstH, anchor, filter)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// cropAndResize crops the image to the smallest possible size that has the required aspect ratio using
 | 
			
		||||
// the given anchor point, then scales it to the specified dimensions and returns the transformed image.
 | 
			
		||||
//
 | 
			
		||||
// This is generally faster than resizing first, but may result in inaccuracies when used on small source images.
 | 
			
		||||
func cropAndResize(img image.Image, width, height int, anchor Anchor, filter ResampleFilter) *image.NRGBA {
 | 
			
		||||
	dstW, dstH := width, height
 | 
			
		||||
 | 
			
		||||
	srcBounds := img.Bounds()
 | 
			
		||||
	srcW := srcBounds.Dx()
 | 
			
		||||
	srcH := srcBounds.Dy()
 | 
			
		||||
	srcAspectRatio := float64(srcW) / float64(srcH)
 | 
			
		||||
	dstAspectRatio := float64(dstW) / float64(dstH)
 | 
			
		||||
 | 
			
		||||
	var tmp *image.NRGBA
 | 
			
		||||
	if srcAspectRatio < dstAspectRatio {
 | 
			
		||||
		cropH := float64(srcW) * float64(dstH) / float64(dstW)
 | 
			
		||||
		tmp = CropAnchor(img, srcW, int(math.Max(1, cropH)+0.5), anchor)
 | 
			
		||||
	} else {
 | 
			
		||||
		cropW := float64(srcH) * float64(dstW) / float64(dstH)
 | 
			
		||||
		tmp = CropAnchor(img, int(math.Max(1, cropW)+0.5), srcH, anchor)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return Resize(tmp, dstW, dstH, filter)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// resizeAndCrop resizes the image to the smallest possible size that will cover the specified dimensions,
 | 
			
		||||
// crops the resized image to the specified dimensions using the given anchor point and returns
 | 
			
		||||
// the transformed image.
 | 
			
		||||
func resizeAndCrop(img image.Image, width, height int, anchor Anchor, filter ResampleFilter) *image.NRGBA {
 | 
			
		||||
	dstW, dstH := width, height
 | 
			
		||||
 | 
			
		||||
	srcBounds := img.Bounds()
 | 
			
		||||
	srcW := srcBounds.Dx()
 | 
			
		||||
	srcH := srcBounds.Dy()
 | 
			
		||||
	srcAspectRatio := float64(srcW) / float64(srcH)
 | 
			
		||||
	dstAspectRatio := float64(dstW) / float64(dstH)
 | 
			
		||||
 | 
			
		||||
	var tmp *image.NRGBA
 | 
			
		||||
	if srcAspectRatio < dstAspectRatio {
 | 
			
		||||
		tmp = Resize(img, dstW, 0, filter)
 | 
			
		||||
	} else {
 | 
			
		||||
		tmp = Resize(img, 0, dstH, filter)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return CropAnchor(tmp, dstW, dstH, anchor)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Thumbnail scales the image up or down using the specified resample filter, crops it
 | 
			
		||||
// to the specified width and hight and returns the transformed image.
 | 
			
		||||
//
 | 
			
		||||
// Example:
 | 
			
		||||
//
 | 
			
		||||
//	dstImage := imaging.Thumbnail(srcImage, 100, 100, imaging.Lanczos)
 | 
			
		||||
//
 | 
			
		||||
func Thumbnail(img image.Image, width, height int, filter ResampleFilter) *image.NRGBA {
 | 
			
		||||
	return Fill(img, width, height, Center, filter)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ResampleFilter specifies a resampling filter to be used for image resizing.
 | 
			
		||||
//
 | 
			
		||||
//	General filter recommendations:
 | 
			
		||||
//
 | 
			
		||||
//	- Lanczos
 | 
			
		||||
//		A high-quality resampling filter for photographic images yielding sharp results.
 | 
			
		||||
//
 | 
			
		||||
//	- CatmullRom
 | 
			
		||||
//		A sharp cubic filter that is faster than Lanczos filter while providing similar results.
 | 
			
		||||
//
 | 
			
		||||
//	- MitchellNetravali
 | 
			
		||||
//		A cubic filter that produces smoother results with less ringing artifacts than CatmullRom.
 | 
			
		||||
//
 | 
			
		||||
//	- Linear
 | 
			
		||||
//		Bilinear resampling filter, produces a smooth output. Faster than cubic filters.
 | 
			
		||||
//
 | 
			
		||||
//	- Box
 | 
			
		||||
//		Simple and fast averaging filter appropriate for downscaling.
 | 
			
		||||
//		When upscaling it's similar to NearestNeighbor.
 | 
			
		||||
//
 | 
			
		||||
//	- NearestNeighbor
 | 
			
		||||
//		Fastest resampling filter, no antialiasing.
 | 
			
		||||
//
 | 
			
		||||
type ResampleFilter struct {
 | 
			
		||||
	Support float64
 | 
			
		||||
	Kernel  func(float64) float64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NearestNeighbor is a nearest-neighbor filter (no anti-aliasing).
 | 
			
		||||
var NearestNeighbor ResampleFilter
 | 
			
		||||
 | 
			
		||||
// Box filter (averaging pixels).
 | 
			
		||||
var Box ResampleFilter
 | 
			
		||||
 | 
			
		||||
// Linear filter.
 | 
			
		||||
var Linear ResampleFilter
 | 
			
		||||
 | 
			
		||||
// Hermite cubic spline filter (BC-spline; B=0; C=0).
 | 
			
		||||
var Hermite ResampleFilter
 | 
			
		||||
 | 
			
		||||
// MitchellNetravali is Mitchell-Netravali cubic filter (BC-spline; B=1/3; C=1/3).
 | 
			
		||||
var MitchellNetravali ResampleFilter
 | 
			
		||||
 | 
			
		||||
// CatmullRom is a Catmull-Rom - sharp cubic filter (BC-spline; B=0; C=0.5).
 | 
			
		||||
var CatmullRom ResampleFilter
 | 
			
		||||
 | 
			
		||||
// BSpline is a smooth cubic filter (BC-spline; B=1; C=0).
 | 
			
		||||
var BSpline ResampleFilter
 | 
			
		||||
 | 
			
		||||
// Gaussian is a Gaussian blurring filter.
 | 
			
		||||
var Gaussian ResampleFilter
 | 
			
		||||
 | 
			
		||||
// Bartlett is a Bartlett-windowed sinc filter (3 lobes).
 | 
			
		||||
var Bartlett ResampleFilter
 | 
			
		||||
 | 
			
		||||
// Lanczos filter (3 lobes).
 | 
			
		||||
var Lanczos ResampleFilter
 | 
			
		||||
 | 
			
		||||
// Hann is a Hann-windowed sinc filter (3 lobes).
 | 
			
		||||
var Hann ResampleFilter
 | 
			
		||||
 | 
			
		||||
// Hamming is a Hamming-windowed sinc filter (3 lobes).
 | 
			
		||||
var Hamming ResampleFilter
 | 
			
		||||
 | 
			
		||||
// Blackman is a Blackman-windowed sinc filter (3 lobes).
 | 
			
		||||
var Blackman ResampleFilter
 | 
			
		||||
 | 
			
		||||
// Welch is a Welch-windowed sinc filter (parabolic window, 3 lobes).
 | 
			
		||||
var Welch ResampleFilter
 | 
			
		||||
 | 
			
		||||
// Cosine is a Cosine-windowed sinc filter (3 lobes).
 | 
			
		||||
var Cosine ResampleFilter
 | 
			
		||||
 | 
			
		||||
func bcspline(x, b, c float64) float64 {
 | 
			
		||||
	var y float64
 | 
			
		||||
	x = math.Abs(x)
 | 
			
		||||
	if x < 1.0 {
 | 
			
		||||
		y = ((12-9*b-6*c)*x*x*x + (-18+12*b+6*c)*x*x + (6 - 2*b)) / 6
 | 
			
		||||
	} else if x < 2.0 {
 | 
			
		||||
		y = ((-b-6*c)*x*x*x + (6*b+30*c)*x*x + (-12*b-48*c)*x + (8*b + 24*c)) / 6
 | 
			
		||||
	}
 | 
			
		||||
	return y
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func sinc(x float64) float64 {
 | 
			
		||||
	if x == 0 {
 | 
			
		||||
		return 1
 | 
			
		||||
	}
 | 
			
		||||
	return math.Sin(math.Pi*x) / (math.Pi * x)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	NearestNeighbor = ResampleFilter{
 | 
			
		||||
		Support: 0.0, // special case - not applying the filter
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Box = ResampleFilter{
 | 
			
		||||
		Support: 0.5,
 | 
			
		||||
		Kernel: func(x float64) float64 {
 | 
			
		||||
			x = math.Abs(x)
 | 
			
		||||
			if x <= 0.5 {
 | 
			
		||||
				return 1.0
 | 
			
		||||
			}
 | 
			
		||||
			return 0
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Linear = ResampleFilter{
 | 
			
		||||
		Support: 1.0,
 | 
			
		||||
		Kernel: func(x float64) float64 {
 | 
			
		||||
			x = math.Abs(x)
 | 
			
		||||
			if x < 1.0 {
 | 
			
		||||
				return 1.0 - x
 | 
			
		||||
			}
 | 
			
		||||
			return 0
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Hermite = ResampleFilter{
 | 
			
		||||
		Support: 1.0,
 | 
			
		||||
		Kernel: func(x float64) float64 {
 | 
			
		||||
			x = math.Abs(x)
 | 
			
		||||
			if x < 1.0 {
 | 
			
		||||
				return bcspline(x, 0.0, 0.0)
 | 
			
		||||
			}
 | 
			
		||||
			return 0
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	MitchellNetravali = ResampleFilter{
 | 
			
		||||
		Support: 2.0,
 | 
			
		||||
		Kernel: func(x float64) float64 {
 | 
			
		||||
			x = math.Abs(x)
 | 
			
		||||
			if x < 2.0 {
 | 
			
		||||
				return bcspline(x, 1.0/3.0, 1.0/3.0)
 | 
			
		||||
			}
 | 
			
		||||
			return 0
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	CatmullRom = ResampleFilter{
 | 
			
		||||
		Support: 2.0,
 | 
			
		||||
		Kernel: func(x float64) float64 {
 | 
			
		||||
			x = math.Abs(x)
 | 
			
		||||
			if x < 2.0 {
 | 
			
		||||
				return bcspline(x, 0.0, 0.5)
 | 
			
		||||
			}
 | 
			
		||||
			return 0
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	BSpline = ResampleFilter{
 | 
			
		||||
		Support: 2.0,
 | 
			
		||||
		Kernel: func(x float64) float64 {
 | 
			
		||||
			x = math.Abs(x)
 | 
			
		||||
			if x < 2.0 {
 | 
			
		||||
				return bcspline(x, 1.0, 0.0)
 | 
			
		||||
			}
 | 
			
		||||
			return 0
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Gaussian = ResampleFilter{
 | 
			
		||||
		Support: 2.0,
 | 
			
		||||
		Kernel: func(x float64) float64 {
 | 
			
		||||
			x = math.Abs(x)
 | 
			
		||||
			if x < 2.0 {
 | 
			
		||||
				return math.Exp(-2 * x * x)
 | 
			
		||||
			}
 | 
			
		||||
			return 0
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Bartlett = ResampleFilter{
 | 
			
		||||
		Support: 3.0,
 | 
			
		||||
		Kernel: func(x float64) float64 {
 | 
			
		||||
			x = math.Abs(x)
 | 
			
		||||
			if x < 3.0 {
 | 
			
		||||
				return sinc(x) * (3.0 - x) / 3.0
 | 
			
		||||
			}
 | 
			
		||||
			return 0
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Lanczos = ResampleFilter{
 | 
			
		||||
		Support: 3.0,
 | 
			
		||||
		Kernel: func(x float64) float64 {
 | 
			
		||||
			x = math.Abs(x)
 | 
			
		||||
			if x < 3.0 {
 | 
			
		||||
				return sinc(x) * sinc(x/3.0)
 | 
			
		||||
			}
 | 
			
		||||
			return 0
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Hann = ResampleFilter{
 | 
			
		||||
		Support: 3.0,
 | 
			
		||||
		Kernel: func(x float64) float64 {
 | 
			
		||||
			x = math.Abs(x)
 | 
			
		||||
			if x < 3.0 {
 | 
			
		||||
				return sinc(x) * (0.5 + 0.5*math.Cos(math.Pi*x/3.0))
 | 
			
		||||
			}
 | 
			
		||||
			return 0
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Hamming = ResampleFilter{
 | 
			
		||||
		Support: 3.0,
 | 
			
		||||
		Kernel: func(x float64) float64 {
 | 
			
		||||
			x = math.Abs(x)
 | 
			
		||||
			if x < 3.0 {
 | 
			
		||||
				return sinc(x) * (0.54 + 0.46*math.Cos(math.Pi*x/3.0))
 | 
			
		||||
			}
 | 
			
		||||
			return 0
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Blackman = ResampleFilter{
 | 
			
		||||
		Support: 3.0,
 | 
			
		||||
		Kernel: func(x float64) float64 {
 | 
			
		||||
			x = math.Abs(x)
 | 
			
		||||
			if x < 3.0 {
 | 
			
		||||
				return sinc(x) * (0.42 - 0.5*math.Cos(math.Pi*x/3.0+math.Pi) + 0.08*math.Cos(2.0*math.Pi*x/3.0))
 | 
			
		||||
			}
 | 
			
		||||
			return 0
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Welch = ResampleFilter{
 | 
			
		||||
		Support: 3.0,
 | 
			
		||||
		Kernel: func(x float64) float64 {
 | 
			
		||||
			x = math.Abs(x)
 | 
			
		||||
			if x < 3.0 {
 | 
			
		||||
				return sinc(x) * (1.0 - (x * x / 9.0))
 | 
			
		||||
			}
 | 
			
		||||
			return 0
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Cosine = ResampleFilter{
 | 
			
		||||
		Support: 3.0,
 | 
			
		||||
		Kernel: func(x float64) float64 {
 | 
			
		||||
			x = math.Abs(x)
 | 
			
		||||
			if x < 3.0 {
 | 
			
		||||
				return sinc(x) * math.Cos((math.Pi/2.0)*(x/3.0))
 | 
			
		||||
			}
 | 
			
		||||
			return 0
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										285
									
								
								vendor/github.com/disintegration/imaging/scanner.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										285
									
								
								vendor/github.com/disintegration/imaging/scanner.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,285 @@
 | 
			
		||||
package imaging
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"image"
 | 
			
		||||
	"image/color"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type scanner struct {
 | 
			
		||||
	image   image.Image
 | 
			
		||||
	w, h    int
 | 
			
		||||
	palette []color.NRGBA
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newScanner(img image.Image) *scanner {
 | 
			
		||||
	s := &scanner{
 | 
			
		||||
		image: img,
 | 
			
		||||
		w:     img.Bounds().Dx(),
 | 
			
		||||
		h:     img.Bounds().Dy(),
 | 
			
		||||
	}
 | 
			
		||||
	if img, ok := img.(*image.Paletted); ok {
 | 
			
		||||
		s.palette = make([]color.NRGBA, len(img.Palette))
 | 
			
		||||
		for i := 0; i < len(img.Palette); i++ {
 | 
			
		||||
			s.palette[i] = color.NRGBAModel.Convert(img.Palette[i]).(color.NRGBA)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// scan scans the given rectangular region of the image into dst.
 | 
			
		||||
func (s *scanner) scan(x1, y1, x2, y2 int, dst []uint8) {
 | 
			
		||||
	switch img := s.image.(type) {
 | 
			
		||||
	case *image.NRGBA:
 | 
			
		||||
		size := (x2 - x1) * 4
 | 
			
		||||
		j := 0
 | 
			
		||||
		i := y1*img.Stride + x1*4
 | 
			
		||||
		if size == 4 {
 | 
			
		||||
			for y := y1; y < y2; y++ {
 | 
			
		||||
				d := dst[j : j+4 : j+4]
 | 
			
		||||
				s := img.Pix[i : i+4 : i+4]
 | 
			
		||||
				d[0] = s[0]
 | 
			
		||||
				d[1] = s[1]
 | 
			
		||||
				d[2] = s[2]
 | 
			
		||||
				d[3] = s[3]
 | 
			
		||||
				j += size
 | 
			
		||||
				i += img.Stride
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			for y := y1; y < y2; y++ {
 | 
			
		||||
				copy(dst[j:j+size], img.Pix[i:i+size])
 | 
			
		||||
				j += size
 | 
			
		||||
				i += img.Stride
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	case *image.NRGBA64:
 | 
			
		||||
		j := 0
 | 
			
		||||
		for y := y1; y < y2; y++ {
 | 
			
		||||
			i := y*img.Stride + x1*8
 | 
			
		||||
			for x := x1; x < x2; x++ {
 | 
			
		||||
				s := img.Pix[i : i+8 : i+8]
 | 
			
		||||
				d := dst[j : j+4 : j+4]
 | 
			
		||||
				d[0] = s[0]
 | 
			
		||||
				d[1] = s[2]
 | 
			
		||||
				d[2] = s[4]
 | 
			
		||||
				d[3] = s[6]
 | 
			
		||||
				j += 4
 | 
			
		||||
				i += 8
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	case *image.RGBA:
 | 
			
		||||
		j := 0
 | 
			
		||||
		for y := y1; y < y2; y++ {
 | 
			
		||||
			i := y*img.Stride + x1*4
 | 
			
		||||
			for x := x1; x < x2; x++ {
 | 
			
		||||
				d := dst[j : j+4 : j+4]
 | 
			
		||||
				a := img.Pix[i+3]
 | 
			
		||||
				switch a {
 | 
			
		||||
				case 0:
 | 
			
		||||
					d[0] = 0
 | 
			
		||||
					d[1] = 0
 | 
			
		||||
					d[2] = 0
 | 
			
		||||
					d[3] = a
 | 
			
		||||
				case 0xff:
 | 
			
		||||
					s := img.Pix[i : i+4 : i+4]
 | 
			
		||||
					d[0] = s[0]
 | 
			
		||||
					d[1] = s[1]
 | 
			
		||||
					d[2] = s[2]
 | 
			
		||||
					d[3] = a
 | 
			
		||||
				default:
 | 
			
		||||
					s := img.Pix[i : i+4 : i+4]
 | 
			
		||||
					r16 := uint16(s[0])
 | 
			
		||||
					g16 := uint16(s[1])
 | 
			
		||||
					b16 := uint16(s[2])
 | 
			
		||||
					a16 := uint16(a)
 | 
			
		||||
					d[0] = uint8(r16 * 0xff / a16)
 | 
			
		||||
					d[1] = uint8(g16 * 0xff / a16)
 | 
			
		||||
					d[2] = uint8(b16 * 0xff / a16)
 | 
			
		||||
					d[3] = a
 | 
			
		||||
				}
 | 
			
		||||
				j += 4
 | 
			
		||||
				i += 4
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	case *image.RGBA64:
 | 
			
		||||
		j := 0
 | 
			
		||||
		for y := y1; y < y2; y++ {
 | 
			
		||||
			i := y*img.Stride + x1*8
 | 
			
		||||
			for x := x1; x < x2; x++ {
 | 
			
		||||
				s := img.Pix[i : i+8 : i+8]
 | 
			
		||||
				d := dst[j : j+4 : j+4]
 | 
			
		||||
				a := s[6]
 | 
			
		||||
				switch a {
 | 
			
		||||
				case 0:
 | 
			
		||||
					d[0] = 0
 | 
			
		||||
					d[1] = 0
 | 
			
		||||
					d[2] = 0
 | 
			
		||||
				case 0xff:
 | 
			
		||||
					d[0] = s[0]
 | 
			
		||||
					d[1] = s[2]
 | 
			
		||||
					d[2] = s[4]
 | 
			
		||||
				default:
 | 
			
		||||
					r32 := uint32(s[0])<<8 | uint32(s[1])
 | 
			
		||||
					g32 := uint32(s[2])<<8 | uint32(s[3])
 | 
			
		||||
					b32 := uint32(s[4])<<8 | uint32(s[5])
 | 
			
		||||
					a32 := uint32(s[6])<<8 | uint32(s[7])
 | 
			
		||||
					d[0] = uint8((r32 * 0xffff / a32) >> 8)
 | 
			
		||||
					d[1] = uint8((g32 * 0xffff / a32) >> 8)
 | 
			
		||||
					d[2] = uint8((b32 * 0xffff / a32) >> 8)
 | 
			
		||||
				}
 | 
			
		||||
				d[3] = a
 | 
			
		||||
				j += 4
 | 
			
		||||
				i += 8
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	case *image.Gray:
 | 
			
		||||
		j := 0
 | 
			
		||||
		for y := y1; y < y2; y++ {
 | 
			
		||||
			i := y*img.Stride + x1
 | 
			
		||||
			for x := x1; x < x2; x++ {
 | 
			
		||||
				c := img.Pix[i]
 | 
			
		||||
				d := dst[j : j+4 : j+4]
 | 
			
		||||
				d[0] = c
 | 
			
		||||
				d[1] = c
 | 
			
		||||
				d[2] = c
 | 
			
		||||
				d[3] = 0xff
 | 
			
		||||
				j += 4
 | 
			
		||||
				i++
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	case *image.Gray16:
 | 
			
		||||
		j := 0
 | 
			
		||||
		for y := y1; y < y2; y++ {
 | 
			
		||||
			i := y*img.Stride + x1*2
 | 
			
		||||
			for x := x1; x < x2; x++ {
 | 
			
		||||
				c := img.Pix[i]
 | 
			
		||||
				d := dst[j : j+4 : j+4]
 | 
			
		||||
				d[0] = c
 | 
			
		||||
				d[1] = c
 | 
			
		||||
				d[2] = c
 | 
			
		||||
				d[3] = 0xff
 | 
			
		||||
				j += 4
 | 
			
		||||
				i += 2
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	case *image.YCbCr:
 | 
			
		||||
		j := 0
 | 
			
		||||
		x1 += img.Rect.Min.X
 | 
			
		||||
		x2 += img.Rect.Min.X
 | 
			
		||||
		y1 += img.Rect.Min.Y
 | 
			
		||||
		y2 += img.Rect.Min.Y
 | 
			
		||||
 | 
			
		||||
		hy := img.Rect.Min.Y / 2
 | 
			
		||||
		hx := img.Rect.Min.X / 2
 | 
			
		||||
		for y := y1; y < y2; y++ {
 | 
			
		||||
			iy := (y-img.Rect.Min.Y)*img.YStride + (x1 - img.Rect.Min.X)
 | 
			
		||||
 | 
			
		||||
			var yBase int
 | 
			
		||||
			switch img.SubsampleRatio {
 | 
			
		||||
			case image.YCbCrSubsampleRatio444, image.YCbCrSubsampleRatio422:
 | 
			
		||||
				yBase = (y - img.Rect.Min.Y) * img.CStride
 | 
			
		||||
			case image.YCbCrSubsampleRatio420, image.YCbCrSubsampleRatio440:
 | 
			
		||||
				yBase = (y/2 - hy) * img.CStride
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			for x := x1; x < x2; x++ {
 | 
			
		||||
				var ic int
 | 
			
		||||
				switch img.SubsampleRatio {
 | 
			
		||||
				case image.YCbCrSubsampleRatio444, image.YCbCrSubsampleRatio440:
 | 
			
		||||
					ic = yBase + (x - img.Rect.Min.X)
 | 
			
		||||
				case image.YCbCrSubsampleRatio422, image.YCbCrSubsampleRatio420:
 | 
			
		||||
					ic = yBase + (x/2 - hx)
 | 
			
		||||
				default:
 | 
			
		||||
					ic = img.COffset(x, y)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				yy1 := int32(img.Y[iy]) * 0x10101
 | 
			
		||||
				cb1 := int32(img.Cb[ic]) - 128
 | 
			
		||||
				cr1 := int32(img.Cr[ic]) - 128
 | 
			
		||||
 | 
			
		||||
				r := yy1 + 91881*cr1
 | 
			
		||||
				if uint32(r)&0xff000000 == 0 {
 | 
			
		||||
					r >>= 16
 | 
			
		||||
				} else {
 | 
			
		||||
					r = ^(r >> 31)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				g := yy1 - 22554*cb1 - 46802*cr1
 | 
			
		||||
				if uint32(g)&0xff000000 == 0 {
 | 
			
		||||
					g >>= 16
 | 
			
		||||
				} else {
 | 
			
		||||
					g = ^(g >> 31)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				b := yy1 + 116130*cb1
 | 
			
		||||
				if uint32(b)&0xff000000 == 0 {
 | 
			
		||||
					b >>= 16
 | 
			
		||||
				} else {
 | 
			
		||||
					b = ^(b >> 31)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				d := dst[j : j+4 : j+4]
 | 
			
		||||
				d[0] = uint8(r)
 | 
			
		||||
				d[1] = uint8(g)
 | 
			
		||||
				d[2] = uint8(b)
 | 
			
		||||
				d[3] = 0xff
 | 
			
		||||
 | 
			
		||||
				iy++
 | 
			
		||||
				j += 4
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	case *image.Paletted:
 | 
			
		||||
		j := 0
 | 
			
		||||
		for y := y1; y < y2; y++ {
 | 
			
		||||
			i := y*img.Stride + x1
 | 
			
		||||
			for x := x1; x < x2; x++ {
 | 
			
		||||
				c := s.palette[img.Pix[i]]
 | 
			
		||||
				d := dst[j : j+4 : j+4]
 | 
			
		||||
				d[0] = c.R
 | 
			
		||||
				d[1] = c.G
 | 
			
		||||
				d[2] = c.B
 | 
			
		||||
				d[3] = c.A
 | 
			
		||||
				j += 4
 | 
			
		||||
				i++
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		j := 0
 | 
			
		||||
		b := s.image.Bounds()
 | 
			
		||||
		x1 += b.Min.X
 | 
			
		||||
		x2 += b.Min.X
 | 
			
		||||
		y1 += b.Min.Y
 | 
			
		||||
		y2 += b.Min.Y
 | 
			
		||||
		for y := y1; y < y2; y++ {
 | 
			
		||||
			for x := x1; x < x2; x++ {
 | 
			
		||||
				r16, g16, b16, a16 := s.image.At(x, y).RGBA()
 | 
			
		||||
				d := dst[j : j+4 : j+4]
 | 
			
		||||
				switch a16 {
 | 
			
		||||
				case 0xffff:
 | 
			
		||||
					d[0] = uint8(r16 >> 8)
 | 
			
		||||
					d[1] = uint8(g16 >> 8)
 | 
			
		||||
					d[2] = uint8(b16 >> 8)
 | 
			
		||||
					d[3] = 0xff
 | 
			
		||||
				case 0:
 | 
			
		||||
					d[0] = 0
 | 
			
		||||
					d[1] = 0
 | 
			
		||||
					d[2] = 0
 | 
			
		||||
					d[3] = 0
 | 
			
		||||
				default:
 | 
			
		||||
					d[0] = uint8(((r16 * 0xffff) / a16) >> 8)
 | 
			
		||||
					d[1] = uint8(((g16 * 0xffff) / a16) >> 8)
 | 
			
		||||
					d[2] = uint8(((b16 * 0xffff) / a16) >> 8)
 | 
			
		||||
					d[3] = uint8(a16 >> 8)
 | 
			
		||||
				}
 | 
			
		||||
				j += 4
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										249
									
								
								vendor/github.com/disintegration/imaging/tools.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										249
									
								
								vendor/github.com/disintegration/imaging/tools.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,249 @@
 | 
			
		||||
package imaging
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"image"
 | 
			
		||||
	"image/color"
 | 
			
		||||
	"math"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// New creates a new image with the specified width and height, and fills it with the specified color.
 | 
			
		||||
func New(width, height int, fillColor color.Color) *image.NRGBA {
 | 
			
		||||
	if width <= 0 || height <= 0 {
 | 
			
		||||
		return &image.NRGBA{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c := color.NRGBAModel.Convert(fillColor).(color.NRGBA)
 | 
			
		||||
	if (c == color.NRGBA{0, 0, 0, 0}) {
 | 
			
		||||
		return image.NewNRGBA(image.Rect(0, 0, width, height))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &image.NRGBA{
 | 
			
		||||
		Pix:    bytes.Repeat([]byte{c.R, c.G, c.B, c.A}, width*height),
 | 
			
		||||
		Stride: 4 * width,
 | 
			
		||||
		Rect:   image.Rect(0, 0, width, height),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Clone returns a copy of the given image.
 | 
			
		||||
func Clone(img image.Image) *image.NRGBA {
 | 
			
		||||
	src := newScanner(img)
 | 
			
		||||
	dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h))
 | 
			
		||||
	size := src.w * 4
 | 
			
		||||
	parallel(0, src.h, func(ys <-chan int) {
 | 
			
		||||
		for y := range ys {
 | 
			
		||||
			i := y * dst.Stride
 | 
			
		||||
			src.scan(0, y, src.w, y+1, dst.Pix[i:i+size])
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
	return dst
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Anchor is the anchor point for image alignment.
 | 
			
		||||
type Anchor int
 | 
			
		||||
 | 
			
		||||
// Anchor point positions.
 | 
			
		||||
const (
 | 
			
		||||
	Center Anchor = iota
 | 
			
		||||
	TopLeft
 | 
			
		||||
	Top
 | 
			
		||||
	TopRight
 | 
			
		||||
	Left
 | 
			
		||||
	Right
 | 
			
		||||
	BottomLeft
 | 
			
		||||
	Bottom
 | 
			
		||||
	BottomRight
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func anchorPt(b image.Rectangle, w, h int, anchor Anchor) image.Point {
 | 
			
		||||
	var x, y int
 | 
			
		||||
	switch anchor {
 | 
			
		||||
	case TopLeft:
 | 
			
		||||
		x = b.Min.X
 | 
			
		||||
		y = b.Min.Y
 | 
			
		||||
	case Top:
 | 
			
		||||
		x = b.Min.X + (b.Dx()-w)/2
 | 
			
		||||
		y = b.Min.Y
 | 
			
		||||
	case TopRight:
 | 
			
		||||
		x = b.Max.X - w
 | 
			
		||||
		y = b.Min.Y
 | 
			
		||||
	case Left:
 | 
			
		||||
		x = b.Min.X
 | 
			
		||||
		y = b.Min.Y + (b.Dy()-h)/2
 | 
			
		||||
	case Right:
 | 
			
		||||
		x = b.Max.X - w
 | 
			
		||||
		y = b.Min.Y + (b.Dy()-h)/2
 | 
			
		||||
	case BottomLeft:
 | 
			
		||||
		x = b.Min.X
 | 
			
		||||
		y = b.Max.Y - h
 | 
			
		||||
	case Bottom:
 | 
			
		||||
		x = b.Min.X + (b.Dx()-w)/2
 | 
			
		||||
		y = b.Max.Y - h
 | 
			
		||||
	case BottomRight:
 | 
			
		||||
		x = b.Max.X - w
 | 
			
		||||
		y = b.Max.Y - h
 | 
			
		||||
	default:
 | 
			
		||||
		x = b.Min.X + (b.Dx()-w)/2
 | 
			
		||||
		y = b.Min.Y + (b.Dy()-h)/2
 | 
			
		||||
	}
 | 
			
		||||
	return image.Pt(x, y)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Crop cuts out a rectangular region with the specified bounds
 | 
			
		||||
// from the image and returns the cropped image.
 | 
			
		||||
func Crop(img image.Image, rect image.Rectangle) *image.NRGBA {
 | 
			
		||||
	r := rect.Intersect(img.Bounds()).Sub(img.Bounds().Min)
 | 
			
		||||
	if r.Empty() {
 | 
			
		||||
		return &image.NRGBA{}
 | 
			
		||||
	}
 | 
			
		||||
	src := newScanner(img)
 | 
			
		||||
	dst := image.NewNRGBA(image.Rect(0, 0, r.Dx(), r.Dy()))
 | 
			
		||||
	rowSize := r.Dx() * 4
 | 
			
		||||
	parallel(r.Min.Y, r.Max.Y, func(ys <-chan int) {
 | 
			
		||||
		for y := range ys {
 | 
			
		||||
			i := (y - r.Min.Y) * dst.Stride
 | 
			
		||||
			src.scan(r.Min.X, y, r.Max.X, y+1, dst.Pix[i:i+rowSize])
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
	return dst
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CropAnchor cuts out a rectangular region with the specified size
 | 
			
		||||
// from the image using the specified anchor point and returns the cropped image.
 | 
			
		||||
func CropAnchor(img image.Image, width, height int, anchor Anchor) *image.NRGBA {
 | 
			
		||||
	srcBounds := img.Bounds()
 | 
			
		||||
	pt := anchorPt(srcBounds, width, height, anchor)
 | 
			
		||||
	r := image.Rect(0, 0, width, height).Add(pt)
 | 
			
		||||
	b := srcBounds.Intersect(r)
 | 
			
		||||
	return Crop(img, b)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CropCenter cuts out a rectangular region with the specified size
 | 
			
		||||
// from the center of the image and returns the cropped image.
 | 
			
		||||
func CropCenter(img image.Image, width, height int) *image.NRGBA {
 | 
			
		||||
	return CropAnchor(img, width, height, Center)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Paste pastes the img image to the background image at the specified position and returns the combined image.
 | 
			
		||||
func Paste(background, img image.Image, pos image.Point) *image.NRGBA {
 | 
			
		||||
	dst := Clone(background)
 | 
			
		||||
	pos = pos.Sub(background.Bounds().Min)
 | 
			
		||||
	pasteRect := image.Rectangle{Min: pos, Max: pos.Add(img.Bounds().Size())}
 | 
			
		||||
	interRect := pasteRect.Intersect(dst.Bounds())
 | 
			
		||||
	if interRect.Empty() {
 | 
			
		||||
		return dst
 | 
			
		||||
	}
 | 
			
		||||
	src := newScanner(img)
 | 
			
		||||
	parallel(interRect.Min.Y, interRect.Max.Y, func(ys <-chan int) {
 | 
			
		||||
		for y := range ys {
 | 
			
		||||
			x1 := interRect.Min.X - pasteRect.Min.X
 | 
			
		||||
			x2 := interRect.Max.X - pasteRect.Min.X
 | 
			
		||||
			y1 := y - pasteRect.Min.Y
 | 
			
		||||
			y2 := y1 + 1
 | 
			
		||||
			i1 := y*dst.Stride + interRect.Min.X*4
 | 
			
		||||
			i2 := i1 + interRect.Dx()*4
 | 
			
		||||
			src.scan(x1, y1, x2, y2, dst.Pix[i1:i2])
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
	return dst
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PasteCenter pastes the img image to the center of the background image and returns the combined image.
 | 
			
		||||
func PasteCenter(background, img image.Image) *image.NRGBA {
 | 
			
		||||
	bgBounds := background.Bounds()
 | 
			
		||||
	bgW := bgBounds.Dx()
 | 
			
		||||
	bgH := bgBounds.Dy()
 | 
			
		||||
	bgMinX := bgBounds.Min.X
 | 
			
		||||
	bgMinY := bgBounds.Min.Y
 | 
			
		||||
 | 
			
		||||
	centerX := bgMinX + bgW/2
 | 
			
		||||
	centerY := bgMinY + bgH/2
 | 
			
		||||
 | 
			
		||||
	x0 := centerX - img.Bounds().Dx()/2
 | 
			
		||||
	y0 := centerY - img.Bounds().Dy()/2
 | 
			
		||||
 | 
			
		||||
	return Paste(background, img, image.Pt(x0, y0))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Overlay draws the img image over the background image at given position
 | 
			
		||||
// and returns the combined image. Opacity parameter is the opacity of the img
 | 
			
		||||
// image layer, used to compose the images, it must be from 0.0 to 1.0.
 | 
			
		||||
//
 | 
			
		||||
// Examples:
 | 
			
		||||
//
 | 
			
		||||
//	// Draw spriteImage over backgroundImage at the given position (x=50, y=50).
 | 
			
		||||
//	dstImage := imaging.Overlay(backgroundImage, spriteImage, image.Pt(50, 50), 1.0)
 | 
			
		||||
//
 | 
			
		||||
//	// Blend two opaque images of the same size.
 | 
			
		||||
//	dstImage := imaging.Overlay(imageOne, imageTwo, image.Pt(0, 0), 0.5)
 | 
			
		||||
//
 | 
			
		||||
func Overlay(background, img image.Image, pos image.Point, opacity float64) *image.NRGBA {
 | 
			
		||||
	opacity = math.Min(math.Max(opacity, 0.0), 1.0) // Ensure 0.0 <= opacity <= 1.0.
 | 
			
		||||
	dst := Clone(background)
 | 
			
		||||
	pos = pos.Sub(background.Bounds().Min)
 | 
			
		||||
	pasteRect := image.Rectangle{Min: pos, Max: pos.Add(img.Bounds().Size())}
 | 
			
		||||
	interRect := pasteRect.Intersect(dst.Bounds())
 | 
			
		||||
	if interRect.Empty() {
 | 
			
		||||
		return dst
 | 
			
		||||
	}
 | 
			
		||||
	src := newScanner(img)
 | 
			
		||||
	parallel(interRect.Min.Y, interRect.Max.Y, func(ys <-chan int) {
 | 
			
		||||
		scanLine := make([]uint8, interRect.Dx()*4)
 | 
			
		||||
		for y := range ys {
 | 
			
		||||
			x1 := interRect.Min.X - pasteRect.Min.X
 | 
			
		||||
			x2 := interRect.Max.X - pasteRect.Min.X
 | 
			
		||||
			y1 := y - pasteRect.Min.Y
 | 
			
		||||
			y2 := y1 + 1
 | 
			
		||||
			src.scan(x1, y1, x2, y2, scanLine)
 | 
			
		||||
			i := y*dst.Stride + interRect.Min.X*4
 | 
			
		||||
			j := 0
 | 
			
		||||
			for x := interRect.Min.X; x < interRect.Max.X; x++ {
 | 
			
		||||
				d := dst.Pix[i : i+4 : i+4]
 | 
			
		||||
				r1 := float64(d[0])
 | 
			
		||||
				g1 := float64(d[1])
 | 
			
		||||
				b1 := float64(d[2])
 | 
			
		||||
				a1 := float64(d[3])
 | 
			
		||||
 | 
			
		||||
				s := scanLine[j : j+4 : j+4]
 | 
			
		||||
				r2 := float64(s[0])
 | 
			
		||||
				g2 := float64(s[1])
 | 
			
		||||
				b2 := float64(s[2])
 | 
			
		||||
				a2 := float64(s[3])
 | 
			
		||||
 | 
			
		||||
				coef2 := opacity * a2 / 255
 | 
			
		||||
				coef1 := (1 - coef2) * a1 / 255
 | 
			
		||||
				coefSum := coef1 + coef2
 | 
			
		||||
				coef1 /= coefSum
 | 
			
		||||
				coef2 /= coefSum
 | 
			
		||||
 | 
			
		||||
				d[0] = uint8(r1*coef1 + r2*coef2)
 | 
			
		||||
				d[1] = uint8(g1*coef1 + g2*coef2)
 | 
			
		||||
				d[2] = uint8(b1*coef1 + b2*coef2)
 | 
			
		||||
				d[3] = uint8(math.Min(a1+a2*opacity*(255-a1)/255, 255))
 | 
			
		||||
 | 
			
		||||
				i += 4
 | 
			
		||||
				j += 4
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
	return dst
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OverlayCenter overlays the img image to the center of the background image and
 | 
			
		||||
// returns the combined image. Opacity parameter is the opacity of the img
 | 
			
		||||
// image layer, used to compose the images, it must be from 0.0 to 1.0.
 | 
			
		||||
func OverlayCenter(background, img image.Image, opacity float64) *image.NRGBA {
 | 
			
		||||
	bgBounds := background.Bounds()
 | 
			
		||||
	bgW := bgBounds.Dx()
 | 
			
		||||
	bgH := bgBounds.Dy()
 | 
			
		||||
	bgMinX := bgBounds.Min.X
 | 
			
		||||
	bgMinY := bgBounds.Min.Y
 | 
			
		||||
 | 
			
		||||
	centerX := bgMinX + bgW/2
 | 
			
		||||
	centerY := bgMinY + bgH/2
 | 
			
		||||
 | 
			
		||||
	x0 := centerX - img.Bounds().Dx()/2
 | 
			
		||||
	y0 := centerY - img.Bounds().Dy()/2
 | 
			
		||||
 | 
			
		||||
	return Overlay(background, img, image.Point{x0, y0}, opacity)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										268
									
								
								vendor/github.com/disintegration/imaging/transform.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										268
									
								
								vendor/github.com/disintegration/imaging/transform.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,268 @@
 | 
			
		||||
package imaging
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"image"
 | 
			
		||||
	"image/color"
 | 
			
		||||
	"math"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// FlipH flips the image horizontally (from left to right) and returns the transformed image.
 | 
			
		||||
func FlipH(img image.Image) *image.NRGBA {
 | 
			
		||||
	src := newScanner(img)
 | 
			
		||||
	dstW := src.w
 | 
			
		||||
	dstH := src.h
 | 
			
		||||
	rowSize := dstW * 4
 | 
			
		||||
	dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
 | 
			
		||||
	parallel(0, dstH, func(ys <-chan int) {
 | 
			
		||||
		for dstY := range ys {
 | 
			
		||||
			i := dstY * dst.Stride
 | 
			
		||||
			srcY := dstY
 | 
			
		||||
			src.scan(0, srcY, src.w, srcY+1, dst.Pix[i:i+rowSize])
 | 
			
		||||
			reverse(dst.Pix[i : i+rowSize])
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
	return dst
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FlipV flips the image vertically (from top to bottom) and returns the transformed image.
 | 
			
		||||
func FlipV(img image.Image) *image.NRGBA {
 | 
			
		||||
	src := newScanner(img)
 | 
			
		||||
	dstW := src.w
 | 
			
		||||
	dstH := src.h
 | 
			
		||||
	rowSize := dstW * 4
 | 
			
		||||
	dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
 | 
			
		||||
	parallel(0, dstH, func(ys <-chan int) {
 | 
			
		||||
		for dstY := range ys {
 | 
			
		||||
			i := dstY * dst.Stride
 | 
			
		||||
			srcY := dstH - dstY - 1
 | 
			
		||||
			src.scan(0, srcY, src.w, srcY+1, dst.Pix[i:i+rowSize])
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
	return dst
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Transpose flips the image horizontally and rotates 90 degrees counter-clockwise.
 | 
			
		||||
func Transpose(img image.Image) *image.NRGBA {
 | 
			
		||||
	src := newScanner(img)
 | 
			
		||||
	dstW := src.h
 | 
			
		||||
	dstH := src.w
 | 
			
		||||
	rowSize := dstW * 4
 | 
			
		||||
	dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
 | 
			
		||||
	parallel(0, dstH, func(ys <-chan int) {
 | 
			
		||||
		for dstY := range ys {
 | 
			
		||||
			i := dstY * dst.Stride
 | 
			
		||||
			srcX := dstY
 | 
			
		||||
			src.scan(srcX, 0, srcX+1, src.h, dst.Pix[i:i+rowSize])
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
	return dst
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Transverse flips the image vertically and rotates 90 degrees counter-clockwise.
 | 
			
		||||
func Transverse(img image.Image) *image.NRGBA {
 | 
			
		||||
	src := newScanner(img)
 | 
			
		||||
	dstW := src.h
 | 
			
		||||
	dstH := src.w
 | 
			
		||||
	rowSize := dstW * 4
 | 
			
		||||
	dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
 | 
			
		||||
	parallel(0, dstH, func(ys <-chan int) {
 | 
			
		||||
		for dstY := range ys {
 | 
			
		||||
			i := dstY * dst.Stride
 | 
			
		||||
			srcX := dstH - dstY - 1
 | 
			
		||||
			src.scan(srcX, 0, srcX+1, src.h, dst.Pix[i:i+rowSize])
 | 
			
		||||
			reverse(dst.Pix[i : i+rowSize])
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
	return dst
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Rotate90 rotates the image 90 degrees counter-clockwise and returns the transformed image.
 | 
			
		||||
func Rotate90(img image.Image) *image.NRGBA {
 | 
			
		||||
	src := newScanner(img)
 | 
			
		||||
	dstW := src.h
 | 
			
		||||
	dstH := src.w
 | 
			
		||||
	rowSize := dstW * 4
 | 
			
		||||
	dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
 | 
			
		||||
	parallel(0, dstH, func(ys <-chan int) {
 | 
			
		||||
		for dstY := range ys {
 | 
			
		||||
			i := dstY * dst.Stride
 | 
			
		||||
			srcX := dstH - dstY - 1
 | 
			
		||||
			src.scan(srcX, 0, srcX+1, src.h, dst.Pix[i:i+rowSize])
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
	return dst
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Rotate180 rotates the image 180 degrees counter-clockwise and returns the transformed image.
 | 
			
		||||
func Rotate180(img image.Image) *image.NRGBA {
 | 
			
		||||
	src := newScanner(img)
 | 
			
		||||
	dstW := src.w
 | 
			
		||||
	dstH := src.h
 | 
			
		||||
	rowSize := dstW * 4
 | 
			
		||||
	dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
 | 
			
		||||
	parallel(0, dstH, func(ys <-chan int) {
 | 
			
		||||
		for dstY := range ys {
 | 
			
		||||
			i := dstY * dst.Stride
 | 
			
		||||
			srcY := dstH - dstY - 1
 | 
			
		||||
			src.scan(0, srcY, src.w, srcY+1, dst.Pix[i:i+rowSize])
 | 
			
		||||
			reverse(dst.Pix[i : i+rowSize])
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
	return dst
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Rotate270 rotates the image 270 degrees counter-clockwise and returns the transformed image.
 | 
			
		||||
func Rotate270(img image.Image) *image.NRGBA {
 | 
			
		||||
	src := newScanner(img)
 | 
			
		||||
	dstW := src.h
 | 
			
		||||
	dstH := src.w
 | 
			
		||||
	rowSize := dstW * 4
 | 
			
		||||
	dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
 | 
			
		||||
	parallel(0, dstH, func(ys <-chan int) {
 | 
			
		||||
		for dstY := range ys {
 | 
			
		||||
			i := dstY * dst.Stride
 | 
			
		||||
			srcX := dstY
 | 
			
		||||
			src.scan(srcX, 0, srcX+1, src.h, dst.Pix[i:i+rowSize])
 | 
			
		||||
			reverse(dst.Pix[i : i+rowSize])
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
	return dst
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Rotate rotates an image by the given angle counter-clockwise .
 | 
			
		||||
// The angle parameter is the rotation angle in degrees.
 | 
			
		||||
// The bgColor parameter specifies the color of the uncovered zone after the rotation.
 | 
			
		||||
func Rotate(img image.Image, angle float64, bgColor color.Color) *image.NRGBA {
 | 
			
		||||
	angle = angle - math.Floor(angle/360)*360
 | 
			
		||||
 | 
			
		||||
	switch angle {
 | 
			
		||||
	case 0:
 | 
			
		||||
		return Clone(img)
 | 
			
		||||
	case 90:
 | 
			
		||||
		return Rotate90(img)
 | 
			
		||||
	case 180:
 | 
			
		||||
		return Rotate180(img)
 | 
			
		||||
	case 270:
 | 
			
		||||
		return Rotate270(img)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	src := toNRGBA(img)
 | 
			
		||||
	srcW := src.Bounds().Max.X
 | 
			
		||||
	srcH := src.Bounds().Max.Y
 | 
			
		||||
	dstW, dstH := rotatedSize(srcW, srcH, angle)
 | 
			
		||||
	dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
 | 
			
		||||
 | 
			
		||||
	if dstW <= 0 || dstH <= 0 {
 | 
			
		||||
		return dst
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	srcXOff := float64(srcW)/2 - 0.5
 | 
			
		||||
	srcYOff := float64(srcH)/2 - 0.5
 | 
			
		||||
	dstXOff := float64(dstW)/2 - 0.5
 | 
			
		||||
	dstYOff := float64(dstH)/2 - 0.5
 | 
			
		||||
 | 
			
		||||
	bgColorNRGBA := color.NRGBAModel.Convert(bgColor).(color.NRGBA)
 | 
			
		||||
	sin, cos := math.Sincos(math.Pi * angle / 180)
 | 
			
		||||
 | 
			
		||||
	parallel(0, dstH, func(ys <-chan int) {
 | 
			
		||||
		for dstY := range ys {
 | 
			
		||||
			for dstX := 0; dstX < dstW; dstX++ {
 | 
			
		||||
				xf, yf := rotatePoint(float64(dstX)-dstXOff, float64(dstY)-dstYOff, sin, cos)
 | 
			
		||||
				xf, yf = xf+srcXOff, yf+srcYOff
 | 
			
		||||
				interpolatePoint(dst, dstX, dstY, src, xf, yf, bgColorNRGBA)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	return dst
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func rotatePoint(x, y, sin, cos float64) (float64, float64) {
 | 
			
		||||
	return x*cos - y*sin, x*sin + y*cos
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func rotatedSize(w, h int, angle float64) (int, int) {
 | 
			
		||||
	if w <= 0 || h <= 0 {
 | 
			
		||||
		return 0, 0
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sin, cos := math.Sincos(math.Pi * angle / 180)
 | 
			
		||||
	x1, y1 := rotatePoint(float64(w-1), 0, sin, cos)
 | 
			
		||||
	x2, y2 := rotatePoint(float64(w-1), float64(h-1), sin, cos)
 | 
			
		||||
	x3, y3 := rotatePoint(0, float64(h-1), sin, cos)
 | 
			
		||||
 | 
			
		||||
	minx := math.Min(x1, math.Min(x2, math.Min(x3, 0)))
 | 
			
		||||
	maxx := math.Max(x1, math.Max(x2, math.Max(x3, 0)))
 | 
			
		||||
	miny := math.Min(y1, math.Min(y2, math.Min(y3, 0)))
 | 
			
		||||
	maxy := math.Max(y1, math.Max(y2, math.Max(y3, 0)))
 | 
			
		||||
 | 
			
		||||
	neww := maxx - minx + 1
 | 
			
		||||
	if neww-math.Floor(neww) > 0.1 {
 | 
			
		||||
		neww++
 | 
			
		||||
	}
 | 
			
		||||
	newh := maxy - miny + 1
 | 
			
		||||
	if newh-math.Floor(newh) > 0.1 {
 | 
			
		||||
		newh++
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return int(neww), int(newh)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func interpolatePoint(dst *image.NRGBA, dstX, dstY int, src *image.NRGBA, xf, yf float64, bgColor color.NRGBA) {
 | 
			
		||||
	j := dstY*dst.Stride + dstX*4
 | 
			
		||||
	d := dst.Pix[j : j+4 : j+4]
 | 
			
		||||
 | 
			
		||||
	x0 := int(math.Floor(xf))
 | 
			
		||||
	y0 := int(math.Floor(yf))
 | 
			
		||||
	bounds := src.Bounds()
 | 
			
		||||
	if !image.Pt(x0, y0).In(image.Rect(bounds.Min.X-1, bounds.Min.Y-1, bounds.Max.X, bounds.Max.Y)) {
 | 
			
		||||
		d[0] = bgColor.R
 | 
			
		||||
		d[1] = bgColor.G
 | 
			
		||||
		d[2] = bgColor.B
 | 
			
		||||
		d[3] = bgColor.A
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	xq := xf - float64(x0)
 | 
			
		||||
	yq := yf - float64(y0)
 | 
			
		||||
	points := [4]image.Point{
 | 
			
		||||
		{x0, y0},
 | 
			
		||||
		{x0 + 1, y0},
 | 
			
		||||
		{x0, y0 + 1},
 | 
			
		||||
		{x0 + 1, y0 + 1},
 | 
			
		||||
	}
 | 
			
		||||
	weights := [4]float64{
 | 
			
		||||
		(1 - xq) * (1 - yq),
 | 
			
		||||
		xq * (1 - yq),
 | 
			
		||||
		(1 - xq) * yq,
 | 
			
		||||
		xq * yq,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var r, g, b, a float64
 | 
			
		||||
	for i := 0; i < 4; i++ {
 | 
			
		||||
		p := points[i]
 | 
			
		||||
		w := weights[i]
 | 
			
		||||
		if p.In(bounds) {
 | 
			
		||||
			i := p.Y*src.Stride + p.X*4
 | 
			
		||||
			s := src.Pix[i : i+4 : i+4]
 | 
			
		||||
			wa := float64(s[3]) * w
 | 
			
		||||
			r += float64(s[0]) * wa
 | 
			
		||||
			g += float64(s[1]) * wa
 | 
			
		||||
			b += float64(s[2]) * wa
 | 
			
		||||
			a += wa
 | 
			
		||||
		} else {
 | 
			
		||||
			wa := float64(bgColor.A) * w
 | 
			
		||||
			r += float64(bgColor.R) * wa
 | 
			
		||||
			g += float64(bgColor.G) * wa
 | 
			
		||||
			b += float64(bgColor.B) * wa
 | 
			
		||||
			a += wa
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if a != 0 {
 | 
			
		||||
		aInv := 1 / a
 | 
			
		||||
		d[0] = clamp(r * aInv)
 | 
			
		||||
		d[1] = clamp(g * aInv)
 | 
			
		||||
		d[2] = clamp(b * aInv)
 | 
			
		||||
		d[3] = clamp(a)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										167
									
								
								vendor/github.com/disintegration/imaging/utils.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										167
									
								
								vendor/github.com/disintegration/imaging/utils.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,167 @@
 | 
			
		||||
package imaging
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"image"
 | 
			
		||||
	"math"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"sync"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// parallel processes the data in separate goroutines.
 | 
			
		||||
func parallel(start, stop int, fn func(<-chan int)) {
 | 
			
		||||
	count := stop - start
 | 
			
		||||
	if count < 1 {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	procs := runtime.GOMAXPROCS(0)
 | 
			
		||||
	if procs > count {
 | 
			
		||||
		procs = count
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c := make(chan int, count)
 | 
			
		||||
	for i := start; i < stop; i++ {
 | 
			
		||||
		c <- i
 | 
			
		||||
	}
 | 
			
		||||
	close(c)
 | 
			
		||||
 | 
			
		||||
	var wg sync.WaitGroup
 | 
			
		||||
	for i := 0; i < procs; i++ {
 | 
			
		||||
		wg.Add(1)
 | 
			
		||||
		go func() {
 | 
			
		||||
			defer wg.Done()
 | 
			
		||||
			fn(c)
 | 
			
		||||
		}()
 | 
			
		||||
	}
 | 
			
		||||
	wg.Wait()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// absint returns the absolute value of i.
 | 
			
		||||
func absint(i int) int {
 | 
			
		||||
	if i < 0 {
 | 
			
		||||
		return -i
 | 
			
		||||
	}
 | 
			
		||||
	return i
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// clamp rounds and clamps float64 value to fit into uint8.
 | 
			
		||||
func clamp(x float64) uint8 {
 | 
			
		||||
	v := int64(x + 0.5)
 | 
			
		||||
	if v > 255 {
 | 
			
		||||
		return 255
 | 
			
		||||
	}
 | 
			
		||||
	if v > 0 {
 | 
			
		||||
		return uint8(v)
 | 
			
		||||
	}
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func reverse(pix []uint8) {
 | 
			
		||||
	if len(pix) <= 4 {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	i := 0
 | 
			
		||||
	j := len(pix) - 4
 | 
			
		||||
	for i < j {
 | 
			
		||||
		pi := pix[i : i+4 : i+4]
 | 
			
		||||
		pj := pix[j : j+4 : j+4]
 | 
			
		||||
		pi[0], pj[0] = pj[0], pi[0]
 | 
			
		||||
		pi[1], pj[1] = pj[1], pi[1]
 | 
			
		||||
		pi[2], pj[2] = pj[2], pi[2]
 | 
			
		||||
		pi[3], pj[3] = pj[3], pi[3]
 | 
			
		||||
		i += 4
 | 
			
		||||
		j -= 4
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func toNRGBA(img image.Image) *image.NRGBA {
 | 
			
		||||
	if img, ok := img.(*image.NRGBA); ok {
 | 
			
		||||
		return &image.NRGBA{
 | 
			
		||||
			Pix:    img.Pix,
 | 
			
		||||
			Stride: img.Stride,
 | 
			
		||||
			Rect:   img.Rect.Sub(img.Rect.Min),
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return Clone(img)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// rgbToHSL converts a color from RGB to HSL.
 | 
			
		||||
func rgbToHSL(r, g, b uint8) (float64, float64, float64) {
 | 
			
		||||
	rr := float64(r) / 255
 | 
			
		||||
	gg := float64(g) / 255
 | 
			
		||||
	bb := float64(b) / 255
 | 
			
		||||
 | 
			
		||||
	max := math.Max(rr, math.Max(gg, bb))
 | 
			
		||||
	min := math.Min(rr, math.Min(gg, bb))
 | 
			
		||||
 | 
			
		||||
	l := (max + min) / 2
 | 
			
		||||
 | 
			
		||||
	if max == min {
 | 
			
		||||
		return 0, 0, l
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var h, s float64
 | 
			
		||||
	d := max - min
 | 
			
		||||
	if l > 0.5 {
 | 
			
		||||
		s = d / (2 - max - min)
 | 
			
		||||
	} else {
 | 
			
		||||
		s = d / (max + min)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch max {
 | 
			
		||||
	case rr:
 | 
			
		||||
		h = (gg - bb) / d
 | 
			
		||||
		if g < b {
 | 
			
		||||
			h += 6
 | 
			
		||||
		}
 | 
			
		||||
	case gg:
 | 
			
		||||
		h = (bb-rr)/d + 2
 | 
			
		||||
	case bb:
 | 
			
		||||
		h = (rr-gg)/d + 4
 | 
			
		||||
	}
 | 
			
		||||
	h /= 6
 | 
			
		||||
 | 
			
		||||
	return h, s, l
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// hslToRGB converts a color from HSL to RGB.
 | 
			
		||||
func hslToRGB(h, s, l float64) (uint8, uint8, uint8) {
 | 
			
		||||
	var r, g, b float64
 | 
			
		||||
	if s == 0 {
 | 
			
		||||
		v := clamp(l * 255)
 | 
			
		||||
		return v, v, v
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var q float64
 | 
			
		||||
	if l < 0.5 {
 | 
			
		||||
		q = l * (1 + s)
 | 
			
		||||
	} else {
 | 
			
		||||
		q = l + s - l*s
 | 
			
		||||
	}
 | 
			
		||||
	p := 2*l - q
 | 
			
		||||
 | 
			
		||||
	r = hueToRGB(p, q, h+1/3.0)
 | 
			
		||||
	g = hueToRGB(p, q, h)
 | 
			
		||||
	b = hueToRGB(p, q, h-1/3.0)
 | 
			
		||||
 | 
			
		||||
	return clamp(r * 255), clamp(g * 255), clamp(b * 255)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func hueToRGB(p, q, t float64) float64 {
 | 
			
		||||
	if t < 0 {
 | 
			
		||||
		t++
 | 
			
		||||
	}
 | 
			
		||||
	if t > 1 {
 | 
			
		||||
		t--
 | 
			
		||||
	}
 | 
			
		||||
	if t < 1/6.0 {
 | 
			
		||||
		return p + (q-p)*6*t
 | 
			
		||||
	}
 | 
			
		||||
	if t < 1/2.0 {
 | 
			
		||||
		return q
 | 
			
		||||
	}
 | 
			
		||||
	if t < 2/3.0 {
 | 
			
		||||
		return p + (q-p)*(2/3.0-t)*6
 | 
			
		||||
	}
 | 
			
		||||
	return p
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user