add imaging
Этот коммит содержится в:
21
Godeps/_workspace/src/github.com/disintegration/imaging/LICENSE
сгенерированный
поставляемый
Обычный файл
21
Godeps/_workspace/src/github.com/disintegration/imaging/LICENSE
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2012-2014 Grigory Dryapak
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
160
Godeps/_workspace/src/github.com/disintegration/imaging/README.md
сгенерированный
поставляемый
Обычный файл
160
Godeps/_workspace/src/github.com/disintegration/imaging/README.md
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,160 @@
|
|||||||
|
# Imaging
|
||||||
|
|
||||||
|
Package imaging provides basic image manipulation functions (resize, rotate, flip, crop, etc.).
|
||||||
|
This package is based on the standard Go image package and works best along with it.
|
||||||
|
|
||||||
|
Image manipulation functions provided by the package take any image type
|
||||||
|
that implements `image.Image` interface as an input, and return a new image of
|
||||||
|
`*image.NRGBA` type (32bit RGBA colors, not premultiplied by alpha).
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Imaging requires Go version 1.2 or greater.
|
||||||
|
|
||||||
|
go get -u github.com/disintegration/imaging
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
http://godoc.org/github.com/disintegration/imaging
|
||||||
|
|
||||||
|
## Usage examples
|
||||||
|
|
||||||
|
A few usage examples can be found below. See the documentation for the full list of supported functions.
|
||||||
|
|
||||||
|
### Image resizing
|
||||||
|
```go
|
||||||
|
// resize srcImage to size = 128x128px using the Lanczos filter
|
||||||
|
dstImage128 := imaging.Resize(srcImage, 128, 128, imaging.Lanczos)
|
||||||
|
|
||||||
|
// resize srcImage to width = 800px preserving the aspect ratio
|
||||||
|
dstImage800 := imaging.Resize(srcImage, 800, 0, imaging.Lanczos)
|
||||||
|
|
||||||
|
// scale down srcImage to fit the 800x600px bounding box
|
||||||
|
dstImageFit := imaging.Fit(srcImage, 800, 600, imaging.Lanczos)
|
||||||
|
|
||||||
|
// resize and crop the srcImage to make a 100x100px thumbnail
|
||||||
|
dstImageThumb := imaging.Thumbnail(srcImage, 100, 100, imaging.Lanczos)
|
||||||
|
```
|
||||||
|
|
||||||
|
Imaging supports image resizing using various resampling filters. The most notable ones:
|
||||||
|
- `NearestNeighbor` - Fastest resampling filter, no antialiasing.
|
||||||
|
- `Box` - Simple and fast averaging filter appropriate for downscaling. When upscaling it's similar to NearestNeighbor.
|
||||||
|
- `Linear` - Bilinear filter, smooth and reasonably fast.
|
||||||
|
- `MitchellNetravali` - А smooth bicubic filter.
|
||||||
|
- `CatmullRom` - A sharp bicubic filter.
|
||||||
|
- `Gaussian` - Blurring filter that uses gaussian function, useful for noise removal.
|
||||||
|
- `Lanczos` - High-quality resampling filter for photographic images yielding sharp results, but it's slower than cubic filters.
|
||||||
|
|
||||||
|
The full list of supported filters: NearestNeighbor, Box, Linear, Hermite, MitchellNetravali, CatmullRom, BSpline, Gaussian, Lanczos, Hann, Hamming, Blackman, Bartlett, Welch, Cosine. Custom filters can be created using ResampleFilter struct.
|
||||||
|
|
||||||
|
**Resampling filters comparison**
|
||||||
|
|
||||||
|
Original image. Will be resized from 512x512px to 128x128px.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Filter | Resize result
|
||||||
|
---|---
|
||||||
|
`imaging.NearestNeighbor` | 
|
||||||
|
`imaging.Box` | 
|
||||||
|
`imaging.Linear` | 
|
||||||
|
`imaging.MitchellNetravali` | 
|
||||||
|
`imaging.CatmullRom` | 
|
||||||
|
`imaging.Gaussian` | 
|
||||||
|
`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)
|
||||||
|
```
|
||||||
|
|
||||||
|
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 = 20 | Contrast = -20
|
||||||
|
---|---|---
|
||||||
|
 |  | 
|
||||||
|
|
||||||
|
### Brightness adjustment
|
||||||
|
```go
|
||||||
|
dstImage := imaging.AdjustBrightness(srcImage, 20)
|
||||||
|
```
|
||||||
|
|
||||||
|
Original image | Brightness = 20 | Brightness = -20
|
||||||
|
---|---|---
|
||||||
|
 |  | 
|
||||||
|
|
||||||
|
|
||||||
|
### Complete code example
|
||||||
|
Here is the code example that loads several images, makes thumbnails of them
|
||||||
|
and combines them together side-by-side.
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
|
||||||
|
"github.com/disintegration/imaging"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
// input files
|
||||||
|
files := []string{"01.jpg", "02.jpg", "03.jpg"}
|
||||||
|
|
||||||
|
// load images and make 100x100 thumbnails of them
|
||||||
|
var thumbnails []image.Image
|
||||||
|
for _, file := range files {
|
||||||
|
img, err := imaging.Open(file)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
thumb := imaging.Thumbnail(img, 100, 100, imaging.CatmullRom)
|
||||||
|
thumbnails = append(thumbnails, thumb)
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a new blank image
|
||||||
|
dst := imaging.New(100*len(thumbnails), 100, color.NRGBA{0, 0, 0, 0})
|
||||||
|
|
||||||
|
// paste thumbnails into the new image side by side
|
||||||
|
for i, thumb := range thumbnails {
|
||||||
|
dst = imaging.Paste(dst, thumb, image.Pt(i*100, 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
// save the combined image to file
|
||||||
|
err := imaging.Save(dst, "dst.jpg")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
200
Godeps/_workspace/src/github.com/disintegration/imaging/adjust.go
сгенерированный
поставляемый
Обычный файл
200
Godeps/_workspace/src/github.com/disintegration/imaging/adjust.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,200 @@
|
|||||||
|
package imaging
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AdjustFunc applies the fn function to each pixel of the img image and returns the adjusted image.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// dstImage = imaging.AdjustFunc(
|
||||||
|
// srcImage,
|
||||||
|
// func(c color.NRGBA) color.NRGBA {
|
||||||
|
// // shift the red channel by 16
|
||||||
|
// r := int(c.R) + 16
|
||||||
|
// if r > 255 {
|
||||||
|
// r = 255
|
||||||
|
// }
|
||||||
|
// return color.NRGBA{uint8(r), c.G, c.B, c.A}
|
||||||
|
// }
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
func AdjustFunc(img image.Image, fn func(c color.NRGBA) color.NRGBA) *image.NRGBA {
|
||||||
|
src := toNRGBA(img)
|
||||||
|
width := src.Bounds().Max.X
|
||||||
|
height := src.Bounds().Max.Y
|
||||||
|
dst := image.NewNRGBA(image.Rect(0, 0, width, height))
|
||||||
|
|
||||||
|
parallel(height, func(partStart, partEnd int) {
|
||||||
|
for y := partStart; y < partEnd; y++ {
|
||||||
|
for x := 0; x < width; x++ {
|
||||||
|
i := y*src.Stride + x*4
|
||||||
|
j := y*dst.Stride + x*4
|
||||||
|
|
||||||
|
r := src.Pix[i+0]
|
||||||
|
g := src.Pix[i+1]
|
||||||
|
b := src.Pix[i+2]
|
||||||
|
a := src.Pix[i+3]
|
||||||
|
|
||||||
|
c := fn(color.NRGBA{r, g, b, a})
|
||||||
|
|
||||||
|
dst.Pix[j+0] = c.R
|
||||||
|
dst.Pix[j+1] = c.G
|
||||||
|
dst.Pix[j+2] = c.B
|
||||||
|
dst.Pix[j+3] = c.A
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// AdjustGamma performs a gamma correction on the image and returns the adjusted image.
|
||||||
|
// Gamma parameter must be positive. Gamma = 1.0 gives the original image.
|
||||||
|
// Gamma less than 1.0 darkens the image and gamma greater than 1.0 lightens it.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// dstImage = imaging.AdjustGamma(srcImage, 0.7)
|
||||||
|
//
|
||||||
|
func AdjustGamma(img image.Image, gamma float64) *image.NRGBA {
|
||||||
|
e := 1.0 / math.Max(gamma, 0.0001)
|
||||||
|
lut := make([]uint8, 256)
|
||||||
|
|
||||||
|
for i := 0; i < 256; i++ {
|
||||||
|
lut[i] = clamp(math.Pow(float64(i)/255.0, e) * 255.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn := func(c color.NRGBA) color.NRGBA {
|
||||||
|
return color.NRGBA{lut[c.R], lut[c.G], lut[c.B], c.A}
|
||||||
|
}
|
||||||
|
|
||||||
|
return AdjustFunc(img, fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sigmoid(a, b, x float64) float64 {
|
||||||
|
return 1 / (1 + math.Exp(b*(a-x)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// AdjustSigmoid changes the contrast of the image using a sigmoidal function and returns the adjusted image.
|
||||||
|
// It's a non-linear contrast change useful for photo adjustments as it preserves highlight and shadow detail.
|
||||||
|
// The midpoint parameter is the midpoint of contrast that must be between 0 and 1, typically 0.5.
|
||||||
|
// The factor parameter indicates how much to increase or decrease the contrast, typically in range (-10, 10).
|
||||||
|
// If the factor parameter is positive the image contrast is increased otherwise the contrast is decreased.
|
||||||
|
//
|
||||||
|
// Examples:
|
||||||
|
//
|
||||||
|
// dstImage = imaging.AdjustSigmoid(srcImage, 0.5, 3.0) // increase the contrast
|
||||||
|
// dstImage = imaging.AdjustSigmoid(srcImage, 0.5, -3.0) // decrease the contrast
|
||||||
|
//
|
||||||
|
func AdjustSigmoid(img image.Image, midpoint, factor float64) *image.NRGBA {
|
||||||
|
if factor == 0 {
|
||||||
|
return Clone(img)
|
||||||
|
}
|
||||||
|
|
||||||
|
lut := make([]uint8, 256)
|
||||||
|
a := math.Min(math.Max(midpoint, 0.0), 1.0)
|
||||||
|
b := math.Abs(factor)
|
||||||
|
sig0 := sigmoid(a, b, 0)
|
||||||
|
sig1 := sigmoid(a, b, 1)
|
||||||
|
e := 1.0e-6
|
||||||
|
|
||||||
|
if factor > 0 {
|
||||||
|
for i := 0; i < 256; i++ {
|
||||||
|
x := float64(i) / 255.0
|
||||||
|
sigX := sigmoid(a, b, x)
|
||||||
|
f := (sigX - sig0) / (sig1 - sig0)
|
||||||
|
lut[i] = clamp(f * 255.0)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for i := 0; i < 256; i++ {
|
||||||
|
x := float64(i) / 255.0
|
||||||
|
arg := math.Min(math.Max((sig1-sig0)*x+sig0, e), 1.0-e)
|
||||||
|
f := a - math.Log(1.0/arg-1.0)/b
|
||||||
|
lut[i] = clamp(f * 255.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn := func(c color.NRGBA) color.NRGBA {
|
||||||
|
return color.NRGBA{lut[c.R], lut[c.G], lut[c.B], c.A}
|
||||||
|
}
|
||||||
|
|
||||||
|
return AdjustFunc(img, fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AdjustContrast changes the contrast of the image using the percentage parameter and returns the adjusted image.
|
||||||
|
// The percentage must be in range (-100, 100). The percentage = 0 gives the original image.
|
||||||
|
// The percentage = -100 gives solid grey image.
|
||||||
|
//
|
||||||
|
// Examples:
|
||||||
|
//
|
||||||
|
// dstImage = imaging.AdjustContrast(srcImage, -10) // decrease image contrast by 10%
|
||||||
|
// dstImage = imaging.AdjustContrast(srcImage, 20) // increase image contrast by 20%
|
||||||
|
//
|
||||||
|
func AdjustContrast(img image.Image, percentage float64) *image.NRGBA {
|
||||||
|
percentage = math.Min(math.Max(percentage, -100.0), 100.0)
|
||||||
|
lut := make([]uint8, 256)
|
||||||
|
|
||||||
|
v := (100.0 + percentage) / 100.0
|
||||||
|
for i := 0; i < 256; i++ {
|
||||||
|
if 0 <= v && v <= 1 {
|
||||||
|
lut[i] = clamp((0.5 + (float64(i)/255.0-0.5)*v) * 255.0)
|
||||||
|
} else if 1 < v && v < 2 {
|
||||||
|
lut[i] = clamp((0.5 + (float64(i)/255.0-0.5)*(1/(2.0-v))) * 255.0)
|
||||||
|
} else {
|
||||||
|
lut[i] = uint8(float64(i)/255.0+0.5) * 255
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn := func(c color.NRGBA) color.NRGBA {
|
||||||
|
return color.NRGBA{lut[c.R], lut[c.G], lut[c.B], c.A}
|
||||||
|
}
|
||||||
|
|
||||||
|
return AdjustFunc(img, fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AdjustBrightness changes the brightness of the image using the percentage parameter and returns the adjusted image.
|
||||||
|
// The percentage must be in range (-100, 100). The percentage = 0 gives the original image.
|
||||||
|
// The percentage = -100 gives solid black image. The percentage = 100 gives solid white image.
|
||||||
|
//
|
||||||
|
// Examples:
|
||||||
|
//
|
||||||
|
// dstImage = imaging.AdjustBrightness(srcImage, -15) // decrease image brightness by 15%
|
||||||
|
// dstImage = imaging.AdjustBrightness(srcImage, 10) // increase image brightness by 10%
|
||||||
|
//
|
||||||
|
func AdjustBrightness(img image.Image, percentage float64) *image.NRGBA {
|
||||||
|
percentage = math.Min(math.Max(percentage, -100.0), 100.0)
|
||||||
|
lut := make([]uint8, 256)
|
||||||
|
|
||||||
|
shift := 255.0 * percentage / 100.0
|
||||||
|
for i := 0; i < 256; i++ {
|
||||||
|
lut[i] = clamp(float64(i) + shift)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn := func(c color.NRGBA) color.NRGBA {
|
||||||
|
return color.NRGBA{lut[c.R], lut[c.G], lut[c.B], c.A}
|
||||||
|
}
|
||||||
|
|
||||||
|
return AdjustFunc(img, fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grayscale produces grayscale version of the image.
|
||||||
|
func Grayscale(img image.Image) *image.NRGBA {
|
||||||
|
fn := func(c color.NRGBA) color.NRGBA {
|
||||||
|
f := 0.299*float64(c.R) + 0.587*float64(c.G) + 0.114*float64(c.B)
|
||||||
|
y := uint8(f + 0.5)
|
||||||
|
return color.NRGBA{y, y, y, c.A}
|
||||||
|
}
|
||||||
|
return AdjustFunc(img, fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invert produces inverted (negated) version of the image.
|
||||||
|
func Invert(img image.Image) *image.NRGBA {
|
||||||
|
fn := func(c color.NRGBA) color.NRGBA {
|
||||||
|
return color.NRGBA{255 - c.R, 255 - c.G, 255 - c.B, c.A}
|
||||||
|
}
|
||||||
|
return AdjustFunc(img, fn)
|
||||||
|
}
|
||||||
187
Godeps/_workspace/src/github.com/disintegration/imaging/effects.go
сгенерированный
поставляемый
Обычный файл
187
Godeps/_workspace/src/github.com/disintegration/imaging/effects.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,187 @@
|
|||||||
|
package imaging
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
func gaussianBlurKernel(x, sigma float64) float64 {
|
||||||
|
return math.Exp(-(x*x)/(2*sigma*sigma)) / (sigma * math.Sqrt(2*math.Pi))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blur produces a blurred version of the image using a Gaussian function.
|
||||||
|
// Sigma parameter must be positive and indicates how much the image will be blurred.
|
||||||
|
//
|
||||||
|
// Usage example:
|
||||||
|
//
|
||||||
|
// dstImage := imaging.Blur(srcImage, 3.5)
|
||||||
|
//
|
||||||
|
func Blur(img image.Image, sigma float64) *image.NRGBA {
|
||||||
|
if sigma <= 0 {
|
||||||
|
// sigma parameter must be positive!
|
||||||
|
return Clone(img)
|
||||||
|
}
|
||||||
|
|
||||||
|
src := toNRGBA(img)
|
||||||
|
radius := int(math.Ceil(sigma * 3.0))
|
||||||
|
kernel := make([]float64, radius+1)
|
||||||
|
|
||||||
|
for i := 0; i <= radius; i++ {
|
||||||
|
kernel[i] = gaussianBlurKernel(float64(i), sigma)
|
||||||
|
}
|
||||||
|
|
||||||
|
var dst *image.NRGBA
|
||||||
|
dst = blurHorizontal(src, kernel)
|
||||||
|
dst = blurVertical(dst, kernel)
|
||||||
|
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
func blurHorizontal(src *image.NRGBA, kernel []float64) *image.NRGBA {
|
||||||
|
radius := len(kernel) - 1
|
||||||
|
width := src.Bounds().Max.X
|
||||||
|
height := src.Bounds().Max.Y
|
||||||
|
|
||||||
|
dst := image.NewNRGBA(image.Rect(0, 0, width, height))
|
||||||
|
|
||||||
|
parallel(width, func(partStart, partEnd int) {
|
||||||
|
for x := partStart; x < partEnd; x++ {
|
||||||
|
start := x - radius
|
||||||
|
if start < 0 {
|
||||||
|
start = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
end := x + radius
|
||||||
|
if end > width-1 {
|
||||||
|
end = width - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
weightSum := 0.0
|
||||||
|
for ix := start; ix <= end; ix++ {
|
||||||
|
weightSum += kernel[absint(x-ix)]
|
||||||
|
}
|
||||||
|
|
||||||
|
for y := 0; y < height; y++ {
|
||||||
|
|
||||||
|
r, g, b, a := 0.0, 0.0, 0.0, 0.0
|
||||||
|
for ix := start; ix <= end; ix++ {
|
||||||
|
weight := kernel[absint(x-ix)]
|
||||||
|
i := y*src.Stride + ix*4
|
||||||
|
r += float64(src.Pix[i+0]) * weight
|
||||||
|
g += float64(src.Pix[i+1]) * weight
|
||||||
|
b += float64(src.Pix[i+2]) * weight
|
||||||
|
a += float64(src.Pix[i+3]) * weight
|
||||||
|
}
|
||||||
|
|
||||||
|
r = math.Min(math.Max(r/weightSum, 0.0), 255.0)
|
||||||
|
g = math.Min(math.Max(g/weightSum, 0.0), 255.0)
|
||||||
|
b = math.Min(math.Max(b/weightSum, 0.0), 255.0)
|
||||||
|
a = math.Min(math.Max(a/weightSum, 0.0), 255.0)
|
||||||
|
|
||||||
|
j := y*dst.Stride + x*4
|
||||||
|
dst.Pix[j+0] = uint8(r + 0.5)
|
||||||
|
dst.Pix[j+1] = uint8(g + 0.5)
|
||||||
|
dst.Pix[j+2] = uint8(b + 0.5)
|
||||||
|
dst.Pix[j+3] = uint8(a + 0.5)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
func blurVertical(src *image.NRGBA, kernel []float64) *image.NRGBA {
|
||||||
|
radius := len(kernel) - 1
|
||||||
|
width := src.Bounds().Max.X
|
||||||
|
height := src.Bounds().Max.Y
|
||||||
|
|
||||||
|
dst := image.NewNRGBA(image.Rect(0, 0, width, height))
|
||||||
|
|
||||||
|
parallel(height, func(partStart, partEnd int) {
|
||||||
|
for y := partStart; y < partEnd; y++ {
|
||||||
|
start := y - radius
|
||||||
|
if start < 0 {
|
||||||
|
start = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
end := y + radius
|
||||||
|
if end > height-1 {
|
||||||
|
end = height - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
weightSum := 0.0
|
||||||
|
for iy := start; iy <= end; iy++ {
|
||||||
|
weightSum += kernel[absint(y-iy)]
|
||||||
|
}
|
||||||
|
|
||||||
|
for x := 0; x < width; x++ {
|
||||||
|
|
||||||
|
r, g, b, a := 0.0, 0.0, 0.0, 0.0
|
||||||
|
for iy := start; iy <= end; iy++ {
|
||||||
|
weight := kernel[absint(y-iy)]
|
||||||
|
i := iy*src.Stride + x*4
|
||||||
|
r += float64(src.Pix[i+0]) * weight
|
||||||
|
g += float64(src.Pix[i+1]) * weight
|
||||||
|
b += float64(src.Pix[i+2]) * weight
|
||||||
|
a += float64(src.Pix[i+3]) * weight
|
||||||
|
}
|
||||||
|
|
||||||
|
r = math.Min(math.Max(r/weightSum, 0.0), 255.0)
|
||||||
|
g = math.Min(math.Max(g/weightSum, 0.0), 255.0)
|
||||||
|
b = math.Min(math.Max(b/weightSum, 0.0), 255.0)
|
||||||
|
a = math.Min(math.Max(a/weightSum, 0.0), 255.0)
|
||||||
|
|
||||||
|
j := y*dst.Stride + x*4
|
||||||
|
dst.Pix[j+0] = uint8(r + 0.5)
|
||||||
|
dst.Pix[j+1] = uint8(g + 0.5)
|
||||||
|
dst.Pix[j+2] = uint8(b + 0.5)
|
||||||
|
dst.Pix[j+3] = uint8(a + 0.5)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sharpen produces a sharpened version of the image.
|
||||||
|
// Sigma parameter must be positive and indicates how much the image will be sharpened.
|
||||||
|
//
|
||||||
|
// Usage example:
|
||||||
|
//
|
||||||
|
// dstImage := imaging.Sharpen(srcImage, 3.5)
|
||||||
|
//
|
||||||
|
func Sharpen(img image.Image, sigma float64) *image.NRGBA {
|
||||||
|
if sigma <= 0 {
|
||||||
|
// sigma parameter must be positive!
|
||||||
|
return Clone(img)
|
||||||
|
}
|
||||||
|
|
||||||
|
src := toNRGBA(img)
|
||||||
|
blurred := Blur(img, sigma)
|
||||||
|
|
||||||
|
width := src.Bounds().Max.X
|
||||||
|
height := src.Bounds().Max.Y
|
||||||
|
dst := image.NewNRGBA(image.Rect(0, 0, width, height))
|
||||||
|
|
||||||
|
parallel(height, func(partStart, partEnd int) {
|
||||||
|
for y := partStart; y < partEnd; y++ {
|
||||||
|
for x := 0; x < width; x++ {
|
||||||
|
i := y*src.Stride + x*4
|
||||||
|
for j := 0; j < 4; j++ {
|
||||||
|
k := i + j
|
||||||
|
val := int(src.Pix[k]) + (int(src.Pix[k]) - int(blurred.Pix[k]))
|
||||||
|
if val < 0 {
|
||||||
|
val = 0
|
||||||
|
} else if val > 255 {
|
||||||
|
val = 255
|
||||||
|
}
|
||||||
|
dst.Pix[k] = uint8(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return dst
|
||||||
|
}
|
||||||
392
Godeps/_workspace/src/github.com/disintegration/imaging/helpers.go
сгенерированный
поставляемый
Обычный файл
392
Godeps/_workspace/src/github.com/disintegration/imaging/helpers.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,392 @@
|
|||||||
|
/*
|
||||||
|
Package imaging provides basic image manipulation functions (resize, rotate, flip, crop, etc.).
|
||||||
|
This package is based on the standard Go image package and works best along with it.
|
||||||
|
|
||||||
|
Image manipulation functions provided by the package take any image type
|
||||||
|
that implements `image.Image` interface as an input, and return a new image of
|
||||||
|
`*image.NRGBA` type (32bit RGBA colors, not premultiplied by alpha).
|
||||||
|
*/
|
||||||
|
package imaging
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"image/gif"
|
||||||
|
"image/jpeg"
|
||||||
|
"image/png"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/image/bmp"
|
||||||
|
"golang.org/x/image/tiff"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Format int
|
||||||
|
|
||||||
|
const (
|
||||||
|
JPEG Format = iota
|
||||||
|
PNG
|
||||||
|
GIF
|
||||||
|
TIFF
|
||||||
|
BMP
|
||||||
|
)
|
||||||
|
|
||||||
|
func (f Format) String() string {
|
||||||
|
switch f {
|
||||||
|
case JPEG:
|
||||||
|
return "JPEG"
|
||||||
|
case PNG:
|
||||||
|
return "PNG"
|
||||||
|
case GIF:
|
||||||
|
return "GIF"
|
||||||
|
case TIFF:
|
||||||
|
return "TIFF"
|
||||||
|
case BMP:
|
||||||
|
return "BMP"
|
||||||
|
default:
|
||||||
|
return "Unsupported"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrUnsupportedFormat = errors.New("imaging: unsupported image format")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Decode reads an image from r.
|
||||||
|
func Decode(r io.Reader) (image.Image, error) {
|
||||||
|
img, _, err := image.Decode(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return toNRGBA(img), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open loads an image from file
|
||||||
|
func Open(filename string) (image.Image, error) {
|
||||||
|
file, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
img, err := Decode(file)
|
||||||
|
return img, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode writes the image img to w in the specified format (JPEG, PNG, GIF, TIFF or BMP).
|
||||||
|
func Encode(w io.Writer, img image.Image, format Format) error {
|
||||||
|
var err error
|
||||||
|
switch format {
|
||||||
|
case JPEG:
|
||||||
|
var rgba *image.RGBA
|
||||||
|
if nrgba, ok := img.(*image.NRGBA); ok {
|
||||||
|
if nrgba.Opaque() {
|
||||||
|
rgba = &image.RGBA{
|
||||||
|
Pix: nrgba.Pix,
|
||||||
|
Stride: nrgba.Stride,
|
||||||
|
Rect: nrgba.Rect,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if rgba != nil {
|
||||||
|
err = jpeg.Encode(w, rgba, &jpeg.Options{Quality: 95})
|
||||||
|
} else {
|
||||||
|
err = jpeg.Encode(w, img, &jpeg.Options{Quality: 95})
|
||||||
|
}
|
||||||
|
|
||||||
|
case PNG:
|
||||||
|
err = png.Encode(w, img)
|
||||||
|
case GIF:
|
||||||
|
err = gif.Encode(w, img, &gif.Options{NumColors: 256})
|
||||||
|
case TIFF:
|
||||||
|
err = tiff.Encode(w, img, &tiff.Options{Compression: tiff.Deflate, Predictor: true})
|
||||||
|
case BMP:
|
||||||
|
err = bmp.Encode(w, img)
|
||||||
|
default:
|
||||||
|
err = ErrUnsupportedFormat
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save saves the image to file with the specified filename.
|
||||||
|
// The format is determined from the filename extension: "jpg" (or "jpeg"), "png", "gif", "tif" (or "tiff") and "bmp" are supported.
|
||||||
|
func Save(img image.Image, filename string) (err error) {
|
||||||
|
formats := map[string]Format{
|
||||||
|
".jpg": JPEG,
|
||||||
|
".jpeg": JPEG,
|
||||||
|
".png": PNG,
|
||||||
|
".tif": TIFF,
|
||||||
|
".tiff": TIFF,
|
||||||
|
".bmp": BMP,
|
||||||
|
".gif": GIF,
|
||||||
|
}
|
||||||
|
|
||||||
|
ext := strings.ToLower(filepath.Ext(filename))
|
||||||
|
f, ok := formats[ext]
|
||||||
|
if !ok {
|
||||||
|
return ErrUnsupportedFormat
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := os.Create(filename)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
return Encode(file, img, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new image with the specified width and height, and fills it with the specified color.
|
||||||
|
func New(width, height int, fillColor color.Color) *image.NRGBA {
|
||||||
|
if width <= 0 || height <= 0 {
|
||||||
|
return &image.NRGBA{}
|
||||||
|
}
|
||||||
|
|
||||||
|
dst := image.NewNRGBA(image.Rect(0, 0, width, height))
|
||||||
|
c := color.NRGBAModel.Convert(fillColor).(color.NRGBA)
|
||||||
|
|
||||||
|
if c.R == 0 && c.G == 0 && c.B == 0 && c.A == 0 {
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
cs := []uint8{c.R, c.G, c.B, c.A}
|
||||||
|
|
||||||
|
// fill the first row
|
||||||
|
for x := 0; x < width; x++ {
|
||||||
|
copy(dst.Pix[x*4:(x+1)*4], cs)
|
||||||
|
}
|
||||||
|
// copy the first row to other rows
|
||||||
|
for y := 1; y < height; y++ {
|
||||||
|
copy(dst.Pix[y*dst.Stride:y*dst.Stride+width*4], dst.Pix[0:width*4])
|
||||||
|
}
|
||||||
|
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clone returns a copy of the given image.
|
||||||
|
func Clone(img image.Image) *image.NRGBA {
|
||||||
|
srcBounds := img.Bounds()
|
||||||
|
srcMinX := srcBounds.Min.X
|
||||||
|
srcMinY := srcBounds.Min.Y
|
||||||
|
|
||||||
|
dstBounds := srcBounds.Sub(srcBounds.Min)
|
||||||
|
dstW := dstBounds.Dx()
|
||||||
|
dstH := dstBounds.Dy()
|
||||||
|
dst := image.NewNRGBA(dstBounds)
|
||||||
|
|
||||||
|
switch src := img.(type) {
|
||||||
|
|
||||||
|
case *image.NRGBA:
|
||||||
|
rowSize := srcBounds.Dx() * 4
|
||||||
|
parallel(dstH, func(partStart, partEnd int) {
|
||||||
|
for dstY := partStart; dstY < partEnd; dstY++ {
|
||||||
|
di := dst.PixOffset(0, dstY)
|
||||||
|
si := src.PixOffset(srcMinX, srcMinY+dstY)
|
||||||
|
copy(dst.Pix[di:di+rowSize], src.Pix[si:si+rowSize])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
case *image.NRGBA64:
|
||||||
|
parallel(dstH, func(partStart, partEnd int) {
|
||||||
|
for dstY := partStart; dstY < partEnd; dstY++ {
|
||||||
|
di := dst.PixOffset(0, dstY)
|
||||||
|
si := src.PixOffset(srcMinX, srcMinY+dstY)
|
||||||
|
for dstX := 0; dstX < dstW; dstX++ {
|
||||||
|
|
||||||
|
dst.Pix[di+0] = src.Pix[si+0]
|
||||||
|
dst.Pix[di+1] = src.Pix[si+2]
|
||||||
|
dst.Pix[di+2] = src.Pix[si+4]
|
||||||
|
dst.Pix[di+3] = src.Pix[si+6]
|
||||||
|
|
||||||
|
di += 4
|
||||||
|
si += 8
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
case *image.RGBA:
|
||||||
|
parallel(dstH, func(partStart, partEnd int) {
|
||||||
|
for dstY := partStart; dstY < partEnd; dstY++ {
|
||||||
|
di := dst.PixOffset(0, dstY)
|
||||||
|
si := src.PixOffset(srcMinX, srcMinY+dstY)
|
||||||
|
for dstX := 0; dstX < dstW; dstX++ {
|
||||||
|
|
||||||
|
a := src.Pix[si+3]
|
||||||
|
dst.Pix[di+3] = a
|
||||||
|
switch a {
|
||||||
|
case 0:
|
||||||
|
dst.Pix[di+0] = 0
|
||||||
|
dst.Pix[di+1] = 0
|
||||||
|
dst.Pix[di+2] = 0
|
||||||
|
case 0xff:
|
||||||
|
dst.Pix[di+0] = src.Pix[si+0]
|
||||||
|
dst.Pix[di+1] = src.Pix[si+1]
|
||||||
|
dst.Pix[di+2] = src.Pix[si+2]
|
||||||
|
default:
|
||||||
|
dst.Pix[di+0] = uint8(uint16(src.Pix[si+0]) * 0xff / uint16(a))
|
||||||
|
dst.Pix[di+1] = uint8(uint16(src.Pix[si+1]) * 0xff / uint16(a))
|
||||||
|
dst.Pix[di+2] = uint8(uint16(src.Pix[si+2]) * 0xff / uint16(a))
|
||||||
|
}
|
||||||
|
|
||||||
|
di += 4
|
||||||
|
si += 4
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
case *image.RGBA64:
|
||||||
|
parallel(dstH, func(partStart, partEnd int) {
|
||||||
|
for dstY := partStart; dstY < partEnd; dstY++ {
|
||||||
|
di := dst.PixOffset(0, dstY)
|
||||||
|
si := src.PixOffset(srcMinX, srcMinY+dstY)
|
||||||
|
for dstX := 0; dstX < dstW; dstX++ {
|
||||||
|
|
||||||
|
a := src.Pix[si+6]
|
||||||
|
dst.Pix[di+3] = a
|
||||||
|
switch a {
|
||||||
|
case 0:
|
||||||
|
dst.Pix[di+0] = 0
|
||||||
|
dst.Pix[di+1] = 0
|
||||||
|
dst.Pix[di+2] = 0
|
||||||
|
case 0xff:
|
||||||
|
dst.Pix[di+0] = src.Pix[si+0]
|
||||||
|
dst.Pix[di+1] = src.Pix[si+2]
|
||||||
|
dst.Pix[di+2] = src.Pix[si+4]
|
||||||
|
default:
|
||||||
|
dst.Pix[di+0] = uint8(uint16(src.Pix[si+0]) * 0xff / uint16(a))
|
||||||
|
dst.Pix[di+1] = uint8(uint16(src.Pix[si+2]) * 0xff / uint16(a))
|
||||||
|
dst.Pix[di+2] = uint8(uint16(src.Pix[si+4]) * 0xff / uint16(a))
|
||||||
|
}
|
||||||
|
|
||||||
|
di += 4
|
||||||
|
si += 8
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
case *image.Gray:
|
||||||
|
parallel(dstH, func(partStart, partEnd int) {
|
||||||
|
for dstY := partStart; dstY < partEnd; dstY++ {
|
||||||
|
di := dst.PixOffset(0, dstY)
|
||||||
|
si := src.PixOffset(srcMinX, srcMinY+dstY)
|
||||||
|
for dstX := 0; dstX < dstW; dstX++ {
|
||||||
|
|
||||||
|
c := src.Pix[si]
|
||||||
|
dst.Pix[di+0] = c
|
||||||
|
dst.Pix[di+1] = c
|
||||||
|
dst.Pix[di+2] = c
|
||||||
|
dst.Pix[di+3] = 0xff
|
||||||
|
|
||||||
|
di += 4
|
||||||
|
si += 1
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
case *image.Gray16:
|
||||||
|
parallel(dstH, func(partStart, partEnd int) {
|
||||||
|
for dstY := partStart; dstY < partEnd; dstY++ {
|
||||||
|
di := dst.PixOffset(0, dstY)
|
||||||
|
si := src.PixOffset(srcMinX, srcMinY+dstY)
|
||||||
|
for dstX := 0; dstX < dstW; dstX++ {
|
||||||
|
|
||||||
|
c := src.Pix[si]
|
||||||
|
dst.Pix[di+0] = c
|
||||||
|
dst.Pix[di+1] = c
|
||||||
|
dst.Pix[di+2] = c
|
||||||
|
dst.Pix[di+3] = 0xff
|
||||||
|
|
||||||
|
di += 4
|
||||||
|
si += 2
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
case *image.YCbCr:
|
||||||
|
parallel(dstH, func(partStart, partEnd int) {
|
||||||
|
for dstY := partStart; dstY < partEnd; dstY++ {
|
||||||
|
di := dst.PixOffset(0, dstY)
|
||||||
|
for dstX := 0; dstX < dstW; dstX++ {
|
||||||
|
|
||||||
|
srcX := srcMinX + dstX
|
||||||
|
srcY := srcMinY + dstY
|
||||||
|
siy := src.YOffset(srcX, srcY)
|
||||||
|
sic := src.COffset(srcX, srcY)
|
||||||
|
r, g, b := color.YCbCrToRGB(src.Y[siy], src.Cb[sic], src.Cr[sic])
|
||||||
|
dst.Pix[di+0] = r
|
||||||
|
dst.Pix[di+1] = g
|
||||||
|
dst.Pix[di+2] = b
|
||||||
|
dst.Pix[di+3] = 0xff
|
||||||
|
|
||||||
|
di += 4
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
case *image.Paletted:
|
||||||
|
plen := len(src.Palette)
|
||||||
|
pnew := make([]color.NRGBA, plen)
|
||||||
|
for i := 0; i < plen; i++ {
|
||||||
|
pnew[i] = color.NRGBAModel.Convert(src.Palette[i]).(color.NRGBA)
|
||||||
|
}
|
||||||
|
|
||||||
|
parallel(dstH, func(partStart, partEnd int) {
|
||||||
|
for dstY := partStart; dstY < partEnd; dstY++ {
|
||||||
|
di := dst.PixOffset(0, dstY)
|
||||||
|
si := src.PixOffset(srcMinX, srcMinY+dstY)
|
||||||
|
for dstX := 0; dstX < dstW; dstX++ {
|
||||||
|
|
||||||
|
c := pnew[src.Pix[si]]
|
||||||
|
dst.Pix[di+0] = c.R
|
||||||
|
dst.Pix[di+1] = c.G
|
||||||
|
dst.Pix[di+2] = c.B
|
||||||
|
dst.Pix[di+3] = c.A
|
||||||
|
|
||||||
|
di += 4
|
||||||
|
si += 1
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
default:
|
||||||
|
parallel(dstH, func(partStart, partEnd int) {
|
||||||
|
for dstY := partStart; dstY < partEnd; dstY++ {
|
||||||
|
di := dst.PixOffset(0, dstY)
|
||||||
|
for dstX := 0; dstX < dstW; dstX++ {
|
||||||
|
|
||||||
|
c := color.NRGBAModel.Convert(img.At(srcMinX+dstX, srcMinY+dstY)).(color.NRGBA)
|
||||||
|
dst.Pix[di+0] = c.R
|
||||||
|
dst.Pix[di+1] = c.G
|
||||||
|
dst.Pix[di+2] = c.B
|
||||||
|
dst.Pix[di+3] = c.A
|
||||||
|
|
||||||
|
di += 4
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function used internally to convert any image type to NRGBA if needed.
|
||||||
|
func toNRGBA(img image.Image) *image.NRGBA {
|
||||||
|
srcBounds := img.Bounds()
|
||||||
|
if srcBounds.Min.X == 0 && srcBounds.Min.Y == 0 {
|
||||||
|
if src0, ok := img.(*image.NRGBA); ok {
|
||||||
|
return src0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Clone(img)
|
||||||
|
}
|
||||||
564
Godeps/_workspace/src/github.com/disintegration/imaging/resize.go
сгенерированный
поставляемый
Обычный файл
564
Godeps/_workspace/src/github.com/disintegration/imaging/resize.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,564 @@
|
|||||||
|
package imaging
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
type iwpair struct {
|
||||||
|
i int
|
||||||
|
w int32
|
||||||
|
}
|
||||||
|
|
||||||
|
type pweights struct {
|
||||||
|
iwpairs []iwpair
|
||||||
|
wsum int32
|
||||||
|
}
|
||||||
|
|
||||||
|
func precomputeWeights(dstSize, srcSize int, filter ResampleFilter) []pweights {
|
||||||
|
du := float64(srcSize) / float64(dstSize)
|
||||||
|
scale := du
|
||||||
|
if scale < 1.0 {
|
||||||
|
scale = 1.0
|
||||||
|
}
|
||||||
|
ru := math.Ceil(scale * filter.Support)
|
||||||
|
|
||||||
|
out := make([]pweights, dstSize)
|
||||||
|
|
||||||
|
for v := 0; v < dstSize; v++ {
|
||||||
|
fu := (float64(v)+0.5)*du - 0.5
|
||||||
|
|
||||||
|
startu := int(math.Ceil(fu - ru))
|
||||||
|
if startu < 0 {
|
||||||
|
startu = 0
|
||||||
|
}
|
||||||
|
endu := int(math.Floor(fu + ru))
|
||||||
|
if endu > srcSize-1 {
|
||||||
|
endu = srcSize - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
wsum := int32(0)
|
||||||
|
for u := startu; u <= endu; u++ {
|
||||||
|
w := int32(0xff * filter.Kernel((float64(u)-fu)/scale))
|
||||||
|
if w != 0 {
|
||||||
|
wsum += w
|
||||||
|
out[v].iwpairs = append(out[v].iwpairs, iwpair{u, w})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out[v].wsum = wsum
|
||||||
|
}
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resize resizes the image to the specified width and height using the specified resampling
|
||||||
|
// filter and returns the transformed image. If one of width or height is 0, the image aspect
|
||||||
|
// ratio is preserved.
|
||||||
|
//
|
||||||
|
// Supported resample filters: NearestNeighbor, Box, Linear, Hermite, MitchellNetravali,
|
||||||
|
// CatmullRom, BSpline, Gaussian, Lanczos, Hann, Hamming, Blackman, Bartlett, Welch, Cosine.
|
||||||
|
//
|
||||||
|
// Usage example:
|
||||||
|
//
|
||||||
|
// dstImage := imaging.Resize(srcImage, 800, 600, imaging.Lanczos)
|
||||||
|
//
|
||||||
|
func Resize(img image.Image, width, height int, filter ResampleFilter) *image.NRGBA {
|
||||||
|
dstW, dstH := width, height
|
||||||
|
|
||||||
|
if dstW < 0 || dstH < 0 {
|
||||||
|
return &image.NRGBA{}
|
||||||
|
}
|
||||||
|
if dstW == 0 && dstH == 0 {
|
||||||
|
return &image.NRGBA{}
|
||||||
|
}
|
||||||
|
|
||||||
|
src := toNRGBA(img)
|
||||||
|
|
||||||
|
srcW := src.Bounds().Max.X
|
||||||
|
srcH := src.Bounds().Max.Y
|
||||||
|
|
||||||
|
if srcW <= 0 || srcH <= 0 {
|
||||||
|
return &image.NRGBA{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if new width or height is 0 then preserve aspect ratio, minimum 1px
|
||||||
|
if dstW == 0 {
|
||||||
|
tmpW := float64(dstH) * float64(srcW) / float64(srcH)
|
||||||
|
dstW = int(math.Max(1.0, math.Floor(tmpW+0.5)))
|
||||||
|
}
|
||||||
|
if dstH == 0 {
|
||||||
|
tmpH := float64(dstW) * float64(srcH) / float64(srcW)
|
||||||
|
dstH = int(math.Max(1.0, math.Floor(tmpH+0.5)))
|
||||||
|
}
|
||||||
|
|
||||||
|
var dst *image.NRGBA
|
||||||
|
|
||||||
|
if filter.Support <= 0.0 {
|
||||||
|
// nearest-neighbor special case
|
||||||
|
dst = resizeNearest(src, dstW, dstH)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// two-pass resize
|
||||||
|
if srcW != dstW {
|
||||||
|
dst = resizeHorizontal(src, dstW, filter)
|
||||||
|
} else {
|
||||||
|
dst = src
|
||||||
|
}
|
||||||
|
|
||||||
|
if srcH != dstH {
|
||||||
|
dst = resizeVertical(dst, dstH, filter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
func resizeHorizontal(src *image.NRGBA, width int, filter ResampleFilter) *image.NRGBA {
|
||||||
|
srcBounds := src.Bounds()
|
||||||
|
srcW := srcBounds.Max.X
|
||||||
|
srcH := srcBounds.Max.Y
|
||||||
|
|
||||||
|
dstW := width
|
||||||
|
dstH := srcH
|
||||||
|
|
||||||
|
dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
|
||||||
|
|
||||||
|
weights := precomputeWeights(dstW, srcW, filter)
|
||||||
|
|
||||||
|
parallel(dstH, func(partStart, partEnd int) {
|
||||||
|
for dstY := partStart; dstY < partEnd; dstY++ {
|
||||||
|
for dstX := 0; dstX < dstW; dstX++ {
|
||||||
|
var c [4]int32
|
||||||
|
for _, iw := range weights[dstX].iwpairs {
|
||||||
|
i := dstY*src.Stride + iw.i*4
|
||||||
|
c[0] += int32(src.Pix[i+0]) * iw.w
|
||||||
|
c[1] += int32(src.Pix[i+1]) * iw.w
|
||||||
|
c[2] += int32(src.Pix[i+2]) * iw.w
|
||||||
|
c[3] += int32(src.Pix[i+3]) * iw.w
|
||||||
|
}
|
||||||
|
j := dstY*dst.Stride + dstX*4
|
||||||
|
sum := weights[dstX].wsum
|
||||||
|
dst.Pix[j+0] = clampint32(int32(float32(c[0])/float32(sum) + 0.5))
|
||||||
|
dst.Pix[j+1] = clampint32(int32(float32(c[1])/float32(sum) + 0.5))
|
||||||
|
dst.Pix[j+2] = clampint32(int32(float32(c[2])/float32(sum) + 0.5))
|
||||||
|
dst.Pix[j+3] = clampint32(int32(float32(c[3])/float32(sum) + 0.5))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
func resizeVertical(src *image.NRGBA, height int, filter ResampleFilter) *image.NRGBA {
|
||||||
|
srcBounds := src.Bounds()
|
||||||
|
srcW := srcBounds.Max.X
|
||||||
|
srcH := srcBounds.Max.Y
|
||||||
|
|
||||||
|
dstW := srcW
|
||||||
|
dstH := height
|
||||||
|
|
||||||
|
dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
|
||||||
|
|
||||||
|
weights := precomputeWeights(dstH, srcH, filter)
|
||||||
|
|
||||||
|
parallel(dstW, func(partStart, partEnd int) {
|
||||||
|
|
||||||
|
for dstX := partStart; dstX < partEnd; dstX++ {
|
||||||
|
for dstY := 0; dstY < dstH; dstY++ {
|
||||||
|
var c [4]int32
|
||||||
|
for _, iw := range weights[dstY].iwpairs {
|
||||||
|
i := iw.i*src.Stride + dstX*4
|
||||||
|
c[0] += int32(src.Pix[i+0]) * iw.w
|
||||||
|
c[1] += int32(src.Pix[i+1]) * iw.w
|
||||||
|
c[2] += int32(src.Pix[i+2]) * iw.w
|
||||||
|
c[3] += int32(src.Pix[i+3]) * iw.w
|
||||||
|
}
|
||||||
|
j := dstY*dst.Stride + dstX*4
|
||||||
|
sum := weights[dstY].wsum
|
||||||
|
dst.Pix[j+0] = clampint32(int32(float32(c[0])/float32(sum) + 0.5))
|
||||||
|
dst.Pix[j+1] = clampint32(int32(float32(c[1])/float32(sum) + 0.5))
|
||||||
|
dst.Pix[j+2] = clampint32(int32(float32(c[2])/float32(sum) + 0.5))
|
||||||
|
dst.Pix[j+3] = clampint32(int32(float32(c[3])/float32(sum) + 0.5))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// fast nearest-neighbor resize, no filtering
|
||||||
|
func resizeNearest(src *image.NRGBA, width, height int) *image.NRGBA {
|
||||||
|
dstW, dstH := width, height
|
||||||
|
|
||||||
|
srcBounds := src.Bounds()
|
||||||
|
srcW := srcBounds.Max.X
|
||||||
|
srcH := srcBounds.Max.Y
|
||||||
|
|
||||||
|
dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
|
||||||
|
|
||||||
|
dx := float64(srcW) / float64(dstW)
|
||||||
|
dy := float64(srcH) / float64(dstH)
|
||||||
|
|
||||||
|
parallel(dstH, func(partStart, partEnd int) {
|
||||||
|
|
||||||
|
for dstY := partStart; dstY < partEnd; dstY++ {
|
||||||
|
fy := (float64(dstY)+0.5)*dy - 0.5
|
||||||
|
|
||||||
|
for dstX := 0; dstX < dstW; dstX++ {
|
||||||
|
fx := (float64(dstX)+0.5)*dx - 0.5
|
||||||
|
|
||||||
|
srcX := int(math.Min(math.Max(math.Floor(fx+0.5), 0.0), float64(srcW)))
|
||||||
|
srcY := int(math.Min(math.Max(math.Floor(fy+0.5), 0.0), float64(srcH)))
|
||||||
|
|
||||||
|
srcOff := srcY*src.Stride + srcX*4
|
||||||
|
dstOff := dstY*dst.Stride + dstX*4
|
||||||
|
|
||||||
|
copy(dst.Pix[dstOff:dstOff+4], src.Pix[srcOff:srcOff+4])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fit scales down the image using the specified resample filter to fit the specified
|
||||||
|
// maximum width and height and returns the transformed image.
|
||||||
|
//
|
||||||
|
// Supported resample filters: NearestNeighbor, Box, Linear, Hermite, MitchellNetravali,
|
||||||
|
// CatmullRom, BSpline, Gaussian, Lanczos, Hann, Hamming, Blackman, Bartlett, Welch, Cosine.
|
||||||
|
//
|
||||||
|
// Usage example:
|
||||||
|
//
|
||||||
|
// dstImage := imaging.Fit(srcImage, 800, 600, imaging.Lanczos)
|
||||||
|
//
|
||||||
|
func Fit(img image.Image, width, height int, filter ResampleFilter) *image.NRGBA {
|
||||||
|
maxW, maxH := width, height
|
||||||
|
|
||||||
|
if maxW <= 0 || maxH <= 0 {
|
||||||
|
return &image.NRGBA{}
|
||||||
|
}
|
||||||
|
|
||||||
|
srcBounds := img.Bounds()
|
||||||
|
srcW := srcBounds.Dx()
|
||||||
|
srcH := srcBounds.Dy()
|
||||||
|
|
||||||
|
if srcW <= 0 || srcH <= 0 {
|
||||||
|
return &image.NRGBA{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if srcW <= maxW && srcH <= maxH {
|
||||||
|
return Clone(img)
|
||||||
|
}
|
||||||
|
|
||||||
|
srcAspectRatio := float64(srcW) / float64(srcH)
|
||||||
|
maxAspectRatio := float64(maxW) / float64(maxH)
|
||||||
|
|
||||||
|
var newW, newH int
|
||||||
|
if srcAspectRatio > maxAspectRatio {
|
||||||
|
newW = maxW
|
||||||
|
newH = int(float64(newW) / srcAspectRatio)
|
||||||
|
} else {
|
||||||
|
newH = maxH
|
||||||
|
newW = int(float64(newH) * srcAspectRatio)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Resize(img, newW, newH, filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Thumbnail scales the image up or down using the specified resample filter, crops it
|
||||||
|
// to the specified width and hight and returns the transformed image.
|
||||||
|
//
|
||||||
|
// Supported resample filters: NearestNeighbor, Box, Linear, Hermite, MitchellNetravali,
|
||||||
|
// CatmullRom, BSpline, Gaussian, Lanczos, Hann, Hamming, Blackman, Bartlett, Welch, Cosine.
|
||||||
|
//
|
||||||
|
// Usage example:
|
||||||
|
//
|
||||||
|
// dstImage := imaging.Thumbnail(srcImage, 100, 100, imaging.Lanczos)
|
||||||
|
//
|
||||||
|
func Thumbnail(img image.Image, width, height int, filter ResampleFilter) *image.NRGBA {
|
||||||
|
thumbW, thumbH := width, height
|
||||||
|
|
||||||
|
if thumbW <= 0 || thumbH <= 0 {
|
||||||
|
return &image.NRGBA{}
|
||||||
|
}
|
||||||
|
|
||||||
|
srcBounds := img.Bounds()
|
||||||
|
srcW := srcBounds.Dx()
|
||||||
|
srcH := srcBounds.Dy()
|
||||||
|
|
||||||
|
if srcW <= 0 || srcH <= 0 {
|
||||||
|
return &image.NRGBA{}
|
||||||
|
}
|
||||||
|
|
||||||
|
srcAspectRatio := float64(srcW) / float64(srcH)
|
||||||
|
thumbAspectRatio := float64(thumbW) / float64(thumbH)
|
||||||
|
|
||||||
|
var tmp image.Image
|
||||||
|
if srcAspectRatio > thumbAspectRatio {
|
||||||
|
tmp = Resize(img, 0, thumbH, filter)
|
||||||
|
} else {
|
||||||
|
tmp = Resize(img, thumbW, 0, filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
return CropCenter(tmp, thumbW, thumbH)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resample filter struct. It can be used to make custom filters.
|
||||||
|
//
|
||||||
|
// Supported resample filters: NearestNeighbor, Box, Linear, Hermite, MitchellNetravali,
|
||||||
|
// CatmullRom, BSpline, Gaussian, Lanczos, Hann, Hamming, Blackman, Bartlett, Welch, Cosine.
|
||||||
|
//
|
||||||
|
// General filter recommendations:
|
||||||
|
//
|
||||||
|
// - Lanczos
|
||||||
|
// Probably the best resampling filter for photographic images yielding sharp results,
|
||||||
|
// but it's slower than cubic filters (see below).
|
||||||
|
//
|
||||||
|
// - CatmullRom
|
||||||
|
// A sharp cubic filter. It's a good filter for both upscaling and downscaling if sharp results are needed.
|
||||||
|
//
|
||||||
|
// - MitchellNetravali
|
||||||
|
// A high quality cubic filter that produces smoother results with less ringing than CatmullRom.
|
||||||
|
//
|
||||||
|
// - BSpline
|
||||||
|
// A good filter if a very smooth output is needed.
|
||||||
|
//
|
||||||
|
// - Linear
|
||||||
|
// Bilinear interpolation filter, produces reasonably good, smooth output. It's faster than cubic filters.
|
||||||
|
//
|
||||||
|
// - Box
|
||||||
|
// Simple and fast resampling filter appropriate for downscaling.
|
||||||
|
// When upscaling it's similar to NearestNeighbor.
|
||||||
|
//
|
||||||
|
// - NearestNeighbor
|
||||||
|
// Fastest resample filter, no antialiasing at all. Rarely used.
|
||||||
|
//
|
||||||
|
type ResampleFilter struct {
|
||||||
|
Support float64
|
||||||
|
Kernel func(float64) float64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nearest-neighbor filter, no anti-aliasing.
|
||||||
|
var NearestNeighbor ResampleFilter
|
||||||
|
|
||||||
|
// Box filter (averaging pixels).
|
||||||
|
var Box ResampleFilter
|
||||||
|
|
||||||
|
// Linear filter.
|
||||||
|
var Linear ResampleFilter
|
||||||
|
|
||||||
|
// Hermite cubic spline filter (BC-spline; B=0; C=0).
|
||||||
|
var Hermite ResampleFilter
|
||||||
|
|
||||||
|
// Mitchell-Netravali cubic filter (BC-spline; B=1/3; C=1/3).
|
||||||
|
var MitchellNetravali ResampleFilter
|
||||||
|
|
||||||
|
// Catmull-Rom - sharp cubic filter (BC-spline; B=0; C=0.5).
|
||||||
|
var CatmullRom ResampleFilter
|
||||||
|
|
||||||
|
// Cubic B-spline - smooth cubic filter (BC-spline; B=1; C=0).
|
||||||
|
var BSpline ResampleFilter
|
||||||
|
|
||||||
|
// Gaussian Blurring Filter.
|
||||||
|
var Gaussian ResampleFilter
|
||||||
|
|
||||||
|
// Bartlett-windowed sinc filter (3 lobes).
|
||||||
|
var Bartlett ResampleFilter
|
||||||
|
|
||||||
|
// Lanczos filter (3 lobes).
|
||||||
|
var Lanczos ResampleFilter
|
||||||
|
|
||||||
|
// Hann-windowed sinc filter (3 lobes).
|
||||||
|
var Hann ResampleFilter
|
||||||
|
|
||||||
|
// Hamming-windowed sinc filter (3 lobes).
|
||||||
|
var Hamming ResampleFilter
|
||||||
|
|
||||||
|
// Blackman-windowed sinc filter (3 lobes).
|
||||||
|
var Blackman ResampleFilter
|
||||||
|
|
||||||
|
// Welch-windowed sinc filter (parabolic window, 3 lobes).
|
||||||
|
var Welch ResampleFilter
|
||||||
|
|
||||||
|
// Cosine-windowed sinc filter (3 lobes).
|
||||||
|
var Cosine ResampleFilter
|
||||||
|
|
||||||
|
func bcspline(x, b, c float64) float64 {
|
||||||
|
x = math.Abs(x)
|
||||||
|
if x < 1.0 {
|
||||||
|
return ((12-9*b-6*c)*x*x*x + (-18+12*b+6*c)*x*x + (6 - 2*b)) / 6
|
||||||
|
}
|
||||||
|
if x < 2.0 {
|
||||||
|
return ((-b-6*c)*x*x*x + (6*b+30*c)*x*x + (-12*b-48*c)*x + (8*b + 24*c)) / 6
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func sinc(x float64) float64 {
|
||||||
|
if x == 0 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return math.Sin(math.Pi*x) / (math.Pi * x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
NearestNeighbor = ResampleFilter{
|
||||||
|
Support: 0.0, // special case - not applying the filter
|
||||||
|
}
|
||||||
|
|
||||||
|
Box = ResampleFilter{
|
||||||
|
Support: 0.5,
|
||||||
|
Kernel: func(x float64) float64 {
|
||||||
|
x = math.Abs(x)
|
||||||
|
if x <= 0.5 {
|
||||||
|
return 1.0
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
Linear = ResampleFilter{
|
||||||
|
Support: 1.0,
|
||||||
|
Kernel: func(x float64) float64 {
|
||||||
|
x = math.Abs(x)
|
||||||
|
if x < 1.0 {
|
||||||
|
return 1.0 - x
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
Hermite = ResampleFilter{
|
||||||
|
Support: 1.0,
|
||||||
|
Kernel: func(x float64) float64 {
|
||||||
|
x = math.Abs(x)
|
||||||
|
if x < 1.0 {
|
||||||
|
return bcspline(x, 0.0, 0.0)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
MitchellNetravali = ResampleFilter{
|
||||||
|
Support: 2.0,
|
||||||
|
Kernel: func(x float64) float64 {
|
||||||
|
x = math.Abs(x)
|
||||||
|
if x < 2.0 {
|
||||||
|
return bcspline(x, 1.0/3.0, 1.0/3.0)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
CatmullRom = ResampleFilter{
|
||||||
|
Support: 2.0,
|
||||||
|
Kernel: func(x float64) float64 {
|
||||||
|
x = math.Abs(x)
|
||||||
|
if x < 2.0 {
|
||||||
|
return bcspline(x, 0.0, 0.5)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
BSpline = ResampleFilter{
|
||||||
|
Support: 2.0,
|
||||||
|
Kernel: func(x float64) float64 {
|
||||||
|
x = math.Abs(x)
|
||||||
|
if x < 2.0 {
|
||||||
|
return bcspline(x, 1.0, 0.0)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
Gaussian = ResampleFilter{
|
||||||
|
Support: 2.0,
|
||||||
|
Kernel: func(x float64) float64 {
|
||||||
|
x = math.Abs(x)
|
||||||
|
if x < 2.0 {
|
||||||
|
return math.Exp(-2 * x * x)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
Bartlett = ResampleFilter{
|
||||||
|
Support: 3.0,
|
||||||
|
Kernel: func(x float64) float64 {
|
||||||
|
x = math.Abs(x)
|
||||||
|
if x < 3.0 {
|
||||||
|
return sinc(x) * (3.0 - x) / 3.0
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
Lanczos = ResampleFilter{
|
||||||
|
Support: 3.0,
|
||||||
|
Kernel: func(x float64) float64 {
|
||||||
|
x = math.Abs(x)
|
||||||
|
if x < 3.0 {
|
||||||
|
return sinc(x) * sinc(x/3.0)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
Hann = ResampleFilter{
|
||||||
|
Support: 3.0,
|
||||||
|
Kernel: func(x float64) float64 {
|
||||||
|
x = math.Abs(x)
|
||||||
|
if x < 3.0 {
|
||||||
|
return sinc(x) * (0.5 + 0.5*math.Cos(math.Pi*x/3.0))
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
Hamming = ResampleFilter{
|
||||||
|
Support: 3.0,
|
||||||
|
Kernel: func(x float64) float64 {
|
||||||
|
x = math.Abs(x)
|
||||||
|
if x < 3.0 {
|
||||||
|
return sinc(x) * (0.54 + 0.46*math.Cos(math.Pi*x/3.0))
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
Blackman = ResampleFilter{
|
||||||
|
Support: 3.0,
|
||||||
|
Kernel: func(x float64) float64 {
|
||||||
|
x = math.Abs(x)
|
||||||
|
if x < 3.0 {
|
||||||
|
return sinc(x) * (0.42 - 0.5*math.Cos(math.Pi*x/3.0+math.Pi) + 0.08*math.Cos(2.0*math.Pi*x/3.0))
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
Welch = ResampleFilter{
|
||||||
|
Support: 3.0,
|
||||||
|
Kernel: func(x float64) float64 {
|
||||||
|
x = math.Abs(x)
|
||||||
|
if x < 3.0 {
|
||||||
|
return sinc(x) * (1.0 - (x * x / 9.0))
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
Cosine = ResampleFilter{
|
||||||
|
Support: 3.0,
|
||||||
|
Kernel: func(x float64) float64 {
|
||||||
|
x = math.Abs(x)
|
||||||
|
if x < 3.0 {
|
||||||
|
return sinc(x) * math.Cos((math.Pi/2.0)*(x/3.0))
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
182
Godeps/_workspace/src/github.com/disintegration/imaging/tools.go
сгенерированный
поставляемый
Обычный файл
182
Godeps/_workspace/src/github.com/disintegration/imaging/tools.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,182 @@
|
|||||||
|
package imaging
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Anchor is the anchor point for image alignment.
|
||||||
|
type Anchor int
|
||||||
|
|
||||||
|
const (
|
||||||
|
Center Anchor = iota
|
||||||
|
TopLeft
|
||||||
|
Top
|
||||||
|
TopRight
|
||||||
|
Left
|
||||||
|
Right
|
||||||
|
BottomLeft
|
||||||
|
Bottom
|
||||||
|
BottomRight
|
||||||
|
)
|
||||||
|
|
||||||
|
func anchorPt(b image.Rectangle, w, h int, anchor Anchor) image.Point {
|
||||||
|
var x, y int
|
||||||
|
switch anchor {
|
||||||
|
case TopLeft:
|
||||||
|
x = b.Min.X
|
||||||
|
y = b.Min.Y
|
||||||
|
case Top:
|
||||||
|
x = b.Min.X + (b.Dx()-w)/2
|
||||||
|
y = b.Min.Y
|
||||||
|
case TopRight:
|
||||||
|
x = b.Max.X - w
|
||||||
|
y = b.Min.Y
|
||||||
|
case Left:
|
||||||
|
x = b.Min.X
|
||||||
|
y = b.Min.Y + (b.Dy()-h)/2
|
||||||
|
case Right:
|
||||||
|
x = b.Max.X - w
|
||||||
|
y = b.Min.Y + (b.Dy()-h)/2
|
||||||
|
case BottomLeft:
|
||||||
|
x = b.Min.X
|
||||||
|
y = b.Max.Y - h
|
||||||
|
case Bottom:
|
||||||
|
x = b.Min.X + (b.Dx()-w)/2
|
||||||
|
y = b.Max.Y - h
|
||||||
|
case BottomRight:
|
||||||
|
x = b.Max.X - w
|
||||||
|
y = b.Max.Y - h
|
||||||
|
default:
|
||||||
|
x = b.Min.X + (b.Dx()-w)/2
|
||||||
|
y = b.Min.Y + (b.Dy()-h)/2
|
||||||
|
}
|
||||||
|
return image.Pt(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Crop cuts out a rectangular region with the specified bounds
|
||||||
|
// from the image and returns the cropped image.
|
||||||
|
func Crop(img image.Image, rect image.Rectangle) *image.NRGBA {
|
||||||
|
src := toNRGBA(img)
|
||||||
|
srcRect := rect.Sub(img.Bounds().Min)
|
||||||
|
sub := src.SubImage(srcRect)
|
||||||
|
return Clone(sub) // New image Bounds().Min point will be (0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CropAnchor cuts out a rectangular region with the specified size
|
||||||
|
// from the image using the specified anchor point and returns the cropped image.
|
||||||
|
func CropAnchor(img image.Image, width, height int, anchor Anchor) *image.NRGBA {
|
||||||
|
srcBounds := img.Bounds()
|
||||||
|
pt := anchorPt(srcBounds, width, height, anchor)
|
||||||
|
r := image.Rect(0, 0, width, height).Add(pt)
|
||||||
|
b := srcBounds.Intersect(r)
|
||||||
|
return Crop(img, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CropCenter cuts out a rectangular region with the specified size
|
||||||
|
// from the center of the image and returns the cropped image.
|
||||||
|
func CropCenter(img image.Image, width, height int) *image.NRGBA {
|
||||||
|
return CropAnchor(img, width, height, Center)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Paste pastes the img image to the background image at the specified position and returns the combined image.
|
||||||
|
func Paste(background, img image.Image, pos image.Point) *image.NRGBA {
|
||||||
|
src := toNRGBA(img)
|
||||||
|
dst := Clone(background) // cloned image bounds start at (0, 0)
|
||||||
|
startPt := pos.Sub(background.Bounds().Min) // so we should translate start point
|
||||||
|
endPt := startPt.Add(src.Bounds().Size())
|
||||||
|
pasteBounds := image.Rectangle{startPt, endPt}
|
||||||
|
|
||||||
|
if dst.Bounds().Overlaps(pasteBounds) {
|
||||||
|
intersectBounds := dst.Bounds().Intersect(pasteBounds)
|
||||||
|
|
||||||
|
rowSize := intersectBounds.Dx() * 4
|
||||||
|
numRows := intersectBounds.Dy()
|
||||||
|
|
||||||
|
srcStartX := intersectBounds.Min.X - pasteBounds.Min.X
|
||||||
|
srcStartY := intersectBounds.Min.Y - pasteBounds.Min.Y
|
||||||
|
|
||||||
|
i0 := dst.PixOffset(intersectBounds.Min.X, intersectBounds.Min.Y)
|
||||||
|
j0 := src.PixOffset(srcStartX, srcStartY)
|
||||||
|
|
||||||
|
di := dst.Stride
|
||||||
|
dj := src.Stride
|
||||||
|
|
||||||
|
for row := 0; row < numRows; row++ {
|
||||||
|
copy(dst.Pix[i0:i0+rowSize], src.Pix[j0:j0+rowSize])
|
||||||
|
i0 += di
|
||||||
|
j0 += dj
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// PasteCenter pastes the img image to the center of the background image and returns the combined image.
|
||||||
|
func PasteCenter(background, img image.Image) *image.NRGBA {
|
||||||
|
bgBounds := background.Bounds()
|
||||||
|
bgW := bgBounds.Dx()
|
||||||
|
bgH := bgBounds.Dy()
|
||||||
|
bgMinX := bgBounds.Min.X
|
||||||
|
bgMinY := bgBounds.Min.Y
|
||||||
|
|
||||||
|
centerX := bgMinX + bgW/2
|
||||||
|
centerY := bgMinY + bgH/2
|
||||||
|
|
||||||
|
x0 := centerX - img.Bounds().Dx()/2
|
||||||
|
y0 := centerY - img.Bounds().Dy()/2
|
||||||
|
|
||||||
|
return Paste(background, img, image.Pt(x0, y0))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overlay draws the img image over the background image at given position
|
||||||
|
// and returns the combined image. Opacity parameter is the opacity of the img
|
||||||
|
// image layer, used to compose the images, it must be from 0.0 to 1.0.
|
||||||
|
//
|
||||||
|
// Usage examples:
|
||||||
|
//
|
||||||
|
// // draw the sprite over the background at position (50, 50)
|
||||||
|
// dstImage := imaging.Overlay(backgroundImage, spriteImage, image.Pt(50, 50), 1.0)
|
||||||
|
//
|
||||||
|
// // blend two opaque images of the same size
|
||||||
|
// dstImage := imaging.Overlay(imageOne, imageTwo, image.Pt(0, 0), 0.5)
|
||||||
|
//
|
||||||
|
func Overlay(background, img image.Image, pos image.Point, opacity float64) *image.NRGBA {
|
||||||
|
opacity = math.Min(math.Max(opacity, 0.0), 1.0) // check: 0.0 <= opacity <= 1.0
|
||||||
|
|
||||||
|
src := toNRGBA(img)
|
||||||
|
dst := Clone(background) // cloned image bounds start at (0, 0)
|
||||||
|
startPt := pos.Sub(background.Bounds().Min) // so we should translate start point
|
||||||
|
endPt := startPt.Add(src.Bounds().Size())
|
||||||
|
pasteBounds := image.Rectangle{startPt, endPt}
|
||||||
|
|
||||||
|
if dst.Bounds().Overlaps(pasteBounds) {
|
||||||
|
intersectBounds := dst.Bounds().Intersect(pasteBounds)
|
||||||
|
|
||||||
|
for y := intersectBounds.Min.Y; y < intersectBounds.Max.Y; y++ {
|
||||||
|
for x := intersectBounds.Min.X; x < intersectBounds.Max.X; x++ {
|
||||||
|
i := y*dst.Stride + x*4
|
||||||
|
|
||||||
|
srcX := x - pasteBounds.Min.X
|
||||||
|
srcY := y - pasteBounds.Min.Y
|
||||||
|
j := srcY*src.Stride + srcX*4
|
||||||
|
|
||||||
|
a1 := float64(dst.Pix[i+3])
|
||||||
|
a2 := float64(src.Pix[j+3])
|
||||||
|
|
||||||
|
coef2 := opacity * a2 / 255.0
|
||||||
|
coef1 := (1 - coef2) * a1 / 255.0
|
||||||
|
coefSum := coef1 + coef2
|
||||||
|
coef1 /= coefSum
|
||||||
|
coef2 /= coefSum
|
||||||
|
|
||||||
|
dst.Pix[i+0] = uint8(float64(dst.Pix[i+0])*coef1 + float64(src.Pix[j+0])*coef2)
|
||||||
|
dst.Pix[i+1] = uint8(float64(dst.Pix[i+1])*coef1 + float64(src.Pix[j+1])*coef2)
|
||||||
|
dst.Pix[i+2] = uint8(float64(dst.Pix[i+2])*coef1 + float64(src.Pix[j+2])*coef2)
|
||||||
|
dst.Pix[i+3] = uint8(math.Min(a1+a2*opacity*(255.0-a1)/255.0, 255.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dst
|
||||||
|
}
|
||||||
201
Godeps/_workspace/src/github.com/disintegration/imaging/transform.go
сгенерированный
поставляемый
Обычный файл
201
Godeps/_workspace/src/github.com/disintegration/imaging/transform.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,201 @@
|
|||||||
|
package imaging
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Rotate90 rotates the image 90 degrees counterclockwise and returns the transformed image.
|
||||||
|
func Rotate90(img image.Image) *image.NRGBA {
|
||||||
|
src := toNRGBA(img)
|
||||||
|
srcW := src.Bounds().Max.X
|
||||||
|
srcH := src.Bounds().Max.Y
|
||||||
|
dstW := srcH
|
||||||
|
dstH := srcW
|
||||||
|
dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
|
||||||
|
|
||||||
|
parallel(dstH, func(partStart, partEnd int) {
|
||||||
|
|
||||||
|
for dstY := partStart; dstY < partEnd; dstY++ {
|
||||||
|
for dstX := 0; dstX < dstW; dstX++ {
|
||||||
|
srcX := dstH - dstY - 1
|
||||||
|
srcY := dstX
|
||||||
|
|
||||||
|
srcOff := srcY*src.Stride + srcX*4
|
||||||
|
dstOff := dstY*dst.Stride + dstX*4
|
||||||
|
|
||||||
|
copy(dst.Pix[dstOff:dstOff+4], src.Pix[srcOff:srcOff+4])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rotate180 rotates the image 180 degrees counterclockwise and returns the transformed image.
|
||||||
|
func Rotate180(img image.Image) *image.NRGBA {
|
||||||
|
src := toNRGBA(img)
|
||||||
|
srcW := src.Bounds().Max.X
|
||||||
|
srcH := src.Bounds().Max.Y
|
||||||
|
dstW := srcW
|
||||||
|
dstH := srcH
|
||||||
|
dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
|
||||||
|
|
||||||
|
parallel(dstH, func(partStart, partEnd int) {
|
||||||
|
|
||||||
|
for dstY := partStart; dstY < partEnd; dstY++ {
|
||||||
|
for dstX := 0; dstX < dstW; dstX++ {
|
||||||
|
srcX := dstW - dstX - 1
|
||||||
|
srcY := dstH - dstY - 1
|
||||||
|
|
||||||
|
srcOff := srcY*src.Stride + srcX*4
|
||||||
|
dstOff := dstY*dst.Stride + dstX*4
|
||||||
|
|
||||||
|
copy(dst.Pix[dstOff:dstOff+4], src.Pix[srcOff:srcOff+4])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rotate270 rotates the image 270 degrees counterclockwise and returns the transformed image.
|
||||||
|
func Rotate270(img image.Image) *image.NRGBA {
|
||||||
|
src := toNRGBA(img)
|
||||||
|
srcW := src.Bounds().Max.X
|
||||||
|
srcH := src.Bounds().Max.Y
|
||||||
|
dstW := srcH
|
||||||
|
dstH := srcW
|
||||||
|
dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
|
||||||
|
|
||||||
|
parallel(dstH, func(partStart, partEnd int) {
|
||||||
|
|
||||||
|
for dstY := partStart; dstY < partEnd; dstY++ {
|
||||||
|
for dstX := 0; dstX < dstW; dstX++ {
|
||||||
|
srcX := dstY
|
||||||
|
srcY := dstW - dstX - 1
|
||||||
|
|
||||||
|
srcOff := srcY*src.Stride + srcX*4
|
||||||
|
dstOff := dstY*dst.Stride + dstX*4
|
||||||
|
|
||||||
|
copy(dst.Pix[dstOff:dstOff+4], src.Pix[srcOff:srcOff+4])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlipH flips the image horizontally (from left to right) and returns the transformed image.
|
||||||
|
func FlipH(img image.Image) *image.NRGBA {
|
||||||
|
src := toNRGBA(img)
|
||||||
|
srcW := src.Bounds().Max.X
|
||||||
|
srcH := src.Bounds().Max.Y
|
||||||
|
dstW := srcW
|
||||||
|
dstH := srcH
|
||||||
|
dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
|
||||||
|
|
||||||
|
parallel(dstH, func(partStart, partEnd int) {
|
||||||
|
|
||||||
|
for dstY := partStart; dstY < partEnd; dstY++ {
|
||||||
|
for dstX := 0; dstX < dstW; dstX++ {
|
||||||
|
srcX := dstW - dstX - 1
|
||||||
|
srcY := dstY
|
||||||
|
|
||||||
|
srcOff := srcY*src.Stride + srcX*4
|
||||||
|
dstOff := dstY*dst.Stride + dstX*4
|
||||||
|
|
||||||
|
copy(dst.Pix[dstOff:dstOff+4], src.Pix[srcOff:srcOff+4])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlipV flips the image vertically (from top to bottom) and returns the transformed image.
|
||||||
|
func FlipV(img image.Image) *image.NRGBA {
|
||||||
|
src := toNRGBA(img)
|
||||||
|
srcW := src.Bounds().Max.X
|
||||||
|
srcH := src.Bounds().Max.Y
|
||||||
|
dstW := srcW
|
||||||
|
dstH := srcH
|
||||||
|
dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
|
||||||
|
|
||||||
|
parallel(dstH, func(partStart, partEnd int) {
|
||||||
|
|
||||||
|
for dstY := partStart; dstY < partEnd; dstY++ {
|
||||||
|
for dstX := 0; dstX < dstW; dstX++ {
|
||||||
|
srcX := dstX
|
||||||
|
srcY := dstH - dstY - 1
|
||||||
|
|
||||||
|
srcOff := srcY*src.Stride + srcX*4
|
||||||
|
dstOff := dstY*dst.Stride + dstX*4
|
||||||
|
|
||||||
|
copy(dst.Pix[dstOff:dstOff+4], src.Pix[srcOff:srcOff+4])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transpose flips the image horizontally and rotates 90 degrees counter-clockwise.
|
||||||
|
func Transpose(img image.Image) *image.NRGBA {
|
||||||
|
src := toNRGBA(img)
|
||||||
|
srcW := src.Bounds().Max.X
|
||||||
|
srcH := src.Bounds().Max.Y
|
||||||
|
dstW := srcH
|
||||||
|
dstH := srcW
|
||||||
|
dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
|
||||||
|
|
||||||
|
parallel(dstH, func(partStart, partEnd int) {
|
||||||
|
|
||||||
|
for dstY := partStart; dstY < partEnd; dstY++ {
|
||||||
|
for dstX := 0; dstX < dstW; dstX++ {
|
||||||
|
srcX := dstY
|
||||||
|
srcY := dstX
|
||||||
|
|
||||||
|
srcOff := srcY*src.Stride + srcX*4
|
||||||
|
dstOff := dstY*dst.Stride + dstX*4
|
||||||
|
|
||||||
|
copy(dst.Pix[dstOff:dstOff+4], src.Pix[srcOff:srcOff+4])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transverse flips the image vertically and rotates 90 degrees counter-clockwise.
|
||||||
|
func Transverse(img image.Image) *image.NRGBA {
|
||||||
|
src := toNRGBA(img)
|
||||||
|
srcW := src.Bounds().Max.X
|
||||||
|
srcH := src.Bounds().Max.Y
|
||||||
|
dstW := srcH
|
||||||
|
dstH := srcW
|
||||||
|
dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
|
||||||
|
|
||||||
|
parallel(dstH, func(partStart, partEnd int) {
|
||||||
|
|
||||||
|
for dstY := partStart; dstY < partEnd; dstY++ {
|
||||||
|
for dstX := 0; dstX < dstW; dstX++ {
|
||||||
|
srcX := dstH - dstY - 1
|
||||||
|
srcY := dstW - dstX - 1
|
||||||
|
|
||||||
|
srcOff := srcY*src.Stride + srcX*4
|
||||||
|
dstOff := dstY*dst.Stride + dstX*4
|
||||||
|
|
||||||
|
copy(dst.Pix[dstOff:dstOff+4], src.Pix[srcOff:srcOff+4])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
return dst
|
||||||
|
}
|
||||||
77
Godeps/_workspace/src/github.com/disintegration/imaging/utils.go
сгенерированный
поставляемый
Обычный файл
77
Godeps/_workspace/src/github.com/disintegration/imaging/utils.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,77 @@
|
|||||||
|
package imaging
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
var parallelizationEnabled = true
|
||||||
|
|
||||||
|
// if GOMAXPROCS = 1: no goroutines used
|
||||||
|
// if GOMAXPROCS > 1: spawn N=GOMAXPROCS workers in separate goroutines
|
||||||
|
func parallel(dataSize int, fn func(partStart, partEnd int)) {
|
||||||
|
numGoroutines := 1
|
||||||
|
partSize := dataSize
|
||||||
|
|
||||||
|
if parallelizationEnabled {
|
||||||
|
numProcs := runtime.GOMAXPROCS(0)
|
||||||
|
if numProcs > 1 {
|
||||||
|
numGoroutines = numProcs
|
||||||
|
partSize = dataSize / (numGoroutines * 10)
|
||||||
|
if partSize < 1 {
|
||||||
|
partSize = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if numGoroutines == 1 {
|
||||||
|
fn(0, dataSize)
|
||||||
|
} else {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(numGoroutines)
|
||||||
|
idx := uint64(0)
|
||||||
|
|
||||||
|
for p := 0; p < numGoroutines; p++ {
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
for {
|
||||||
|
partStart := int(atomic.AddUint64(&idx, uint64(partSize))) - partSize
|
||||||
|
if partStart >= dataSize {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
partEnd := partStart + partSize
|
||||||
|
if partEnd > dataSize {
|
||||||
|
partEnd = dataSize
|
||||||
|
}
|
||||||
|
fn(partStart, partEnd)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func absint(i int) int {
|
||||||
|
if i < 0 {
|
||||||
|
return -i
|
||||||
|
}
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
// clamp & round float64 to uint8 (0..255)
|
||||||
|
func clamp(v float64) uint8 {
|
||||||
|
return uint8(math.Min(math.Max(v, 0.0), 255.0) + 0.5)
|
||||||
|
}
|
||||||
|
|
||||||
|
// clamp int32 to uint8 (0..255)
|
||||||
|
func clampint32(v int32) uint8 {
|
||||||
|
if v < 0 {
|
||||||
|
return 0
|
||||||
|
} else if v > 255 {
|
||||||
|
return 255
|
||||||
|
}
|
||||||
|
return uint8(v)
|
||||||
|
}
|
||||||
Ссылка в новой задаче
Block a user