diff options
Diffstat (limited to 'vendor/github.com/disintegration/imaging')
35 files changed, 878 insertions, 274 deletions
diff --git a/vendor/github.com/disintegration/imaging/.travis.yml b/vendor/github.com/disintegration/imaging/.travis.yml index 0e214c00d..110437d6a 100644 --- a/vendor/github.com/disintegration/imaging/.travis.yml +++ b/vendor/github.com/disintegration/imaging/.travis.yml @@ -9,6 +9,7 @@ go: - 1.5 - 1.6 - 1.7 + - 1.8 before_install: - go get golang.org/x/tools/cmd/cover diff --git a/vendor/github.com/disintegration/imaging/LICENSE b/vendor/github.com/disintegration/imaging/LICENSE index 95ae410c3..72a26f0e8 100644 --- a/vendor/github.com/disintegration/imaging/LICENSE +++ b/vendor/github.com/disintegration/imaging/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2012-2014 Grigory Dryapak +Copyright (c) 2012-2017 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 diff --git a/vendor/github.com/disintegration/imaging/README.md b/vendor/github.com/disintegration/imaging/README.md index 3dcea2000..f957c3e79 100644 --- a/vendor/github.com/disintegration/imaging/README.md +++ b/vendor/github.com/disintegration/imaging/README.md @@ -26,17 +26,18 @@ http://godoc.org/github.com/disintegration/imaging 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
+// 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
+// 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
+// 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
+// Resize and crop the srcImage to fill the 100x100px area.
dstImageFill := imaging.Fill(srcImage, 100, 100, imaging.Center, imaging.Lanczos)
```
@@ -53,146 +54,138 @@ The full list of supported filters: NearestNeighbor, Box, Linear, Hermite, Mitc **Resampling filters comparison**
-Original image. Will be resized from 512x512px to 128x128px.
-
-![srcImage](http://disintegration.github.io/imaging/in_lena_bw_512.png)
-
-Filter | Resize result
----|---
-`imaging.NearestNeighbor` | ![dstImage](http://disintegration.github.io/imaging/out_resize_down_nearest.png)
-`imaging.Box` | ![dstImage](http://disintegration.github.io/imaging/out_resize_down_box.png)
-`imaging.Linear` | ![dstImage](http://disintegration.github.io/imaging/out_resize_down_linear.png)
-`imaging.MitchellNetravali` | ![dstImage](http://disintegration.github.io/imaging/out_resize_down_mitchell.png)
-`imaging.CatmullRom` | ![dstImage](http://disintegration.github.io/imaging/out_resize_down_catrom.png)
-`imaging.Gaussian` | ![dstImage](http://disintegration.github.io/imaging/out_resize_down_gaussian.png)
-`imaging.Lanczos` | ![dstImage](http://disintegration.github.io/imaging/out_resize_down_lanczos.png)
-
-**Resize functions comparison**
+The original image.
-Original image:
+![srcImage](testdata/lena_512.png)
-![srcImage](http://disintegration.github.io/imaging/in.jpg)
-
-Resize the image to width=100px and height=100px:
-
-```go
-dstImage := imaging.Resize(srcImage, 100, 100, imaging.Lanczos)
-```
-![dstImage](http://disintegration.github.io/imaging/out-comp-resize.jpg)
+The same image resized from 512x512px to 128x128px using different resampling filters.
+From faster (lower quality) to slower (higher quality):
-Resize the image to width=100px preserving the aspect ratio:
-
-```go
-dstImage := imaging.Resize(srcImage, 100, 0, imaging.Lanczos)
-```
-![dstImage](http://disintegration.github.io/imaging/out-comp-fit.jpg)
-
-Resize the image to fit the 100x100px boundng box preserving the aspect ratio:
-
-```go
-dstImage := imaging.Fit(srcImage, 100, 100, imaging.Lanczos)
-```
-![dstImage](http://disintegration.github.io/imaging/out-comp-fit.jpg)
+Filter | Resize result
+--------------------------|---------------------------------------------
+`imaging.NearestNeighbor` | ![dstImage](testdata/out_resize_nearest.png)
+`imaging.Linear` | ![dstImage](testdata/out_resize_linear.png)
+`imaging.CatmullRom` | ![dstImage](testdata/out_resize_catrom.png)
+`imaging.Lanczos` | ![dstImage](testdata/out_resize_lanczos.png)
-Resize and crop the image with a center anchor point to fill the 100x100px area:
-
-```go
-dstImage := imaging.Fill(srcImage, 100, 100, imaging.Center, imaging.Lanczos)
-```
-![dstImage](http://disintegration.github.io/imaging/out-comp-fill.jpg)
### Gaussian Blur
+
```go
dstImage := imaging.Blur(srcImage, 0.5)
```
Sigma parameter allows to control the strength of the blurring effect.
-Original image | Sigma = 0.5 | Sigma = 1.5
----|---|---
-![srcImage](http://disintegration.github.io/imaging/in_lena_bw_128.png) | ![dstImage](http://disintegration.github.io/imaging/out_blur_0.5.png) | ![dstImage](http://disintegration.github.io/imaging/out_blur_1.5.png)
+Original image | Sigma = 0.5 | Sigma = 1.5
+-----------------------------------|----------------------------------------|---------------------------------------
+![srcImage](testdata/lena_128.png) | ![dstImage](testdata/out_blur_0.5.png) | ![dstImage](testdata/out_blur_1.5.png)
### Sharpening
+
```go
dstImage := imaging.Sharpen(srcImage, 0.5)
```
-Uses gaussian function internally. Sigma parameter allows to control the strength of the sharpening effect.
+`Sharpen` uses gaussian function internally. Sigma parameter allows to control the strength of the sharpening effect.
-Original image | Sigma = 0.5 | Sigma = 1.5
----|---|---
-![srcImage](http://disintegration.github.io/imaging/in_lena_bw_128.png) | ![dstImage](http://disintegration.github.io/imaging/out_sharpen_0.5.png) | ![dstImage](http://disintegration.github.io/imaging/out_sharpen_1.5.png)
+Original image | Sigma = 0.5 | Sigma = 1.5
+-----------------------------------|-------------------------------------------|------------------------------------------
+![srcImage](testdata/lena_128.png) | ![dstImage](testdata/out_sharpen_0.5.png) | ![dstImage](testdata/out_sharpen_1.5.png)
### Gamma correction
+
```go
dstImage := imaging.AdjustGamma(srcImage, 0.75)
```
-Original image | Gamma = 0.75 | Gamma = 1.25
----|---|---
-![srcImage](http://disintegration.github.io/imaging/in_lena_bw_128.png) | ![dstImage](http://disintegration.github.io/imaging/out_gamma_0.75.png) | ![dstImage](http://disintegration.github.io/imaging/out_gamma_1.25.png)
+Original image | Gamma = 0.75 | Gamma = 1.25
+-----------------------------------|------------------------------------------|-----------------------------------------
+![srcImage](testdata/lena_128.png) | ![dstImage](testdata/out_gamma_0.75.png) | ![dstImage](testdata/out_gamma_1.25.png)
### Contrast adjustment
+
```go
dstImage := imaging.AdjustContrast(srcImage, 20)
```
-Original image | Contrast = 20 | Contrast = -20
----|---|---
-![srcImage](http://disintegration.github.io/imaging/in_lena_bw_128.png) | ![dstImage](http://disintegration.github.io/imaging/out_contrast_p20.png) | ![dstImage](http://disintegration.github.io/imaging/out_contrast_m20.png)
+Original image | Contrast = 10 | Contrast = -10
+-----------------------------------|--------------------------------------------|-------------------------------------------
+![srcImage](testdata/lena_128.png) | ![dstImage](testdata/out_contrast_p10.png) | ![dstImage](testdata/out_contrast_m10.png)
### Brightness adjustment
+
```go
dstImage := imaging.AdjustBrightness(srcImage, 20)
```
-Original image | Brightness = 20 | Brightness = -20
----|---|---
-![srcImage](http://disintegration.github.io/imaging/in_lena_bw_128.png) | ![dstImage](http://disintegration.github.io/imaging/out_brightness_p20.png) | ![dstImage](http://disintegration.github.io/imaging/out_brightness_m20.png)
-
+Original image | Brightness = 10 | Brightness = -10
+-----------------------------------|----------------------------------------------|---------------------------------------------
+![srcImage](testdata/lena_128.png) | ![dstImage](testdata/out_brightness_p10.png) | ![dstImage](testdata/out_brightness_m10.png)
-### Complete code example
-Here is the code example that loads several images, makes thumbnails of them
-and combines them together side-by-side.
+## Example code
```go
package main
import (
- "image"
- "image/color"
-
- "github.com/disintegration/imaging"
+ "image"
+ "image/color"
+ "log"
+
+ "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)
- }
+ // Open the test image.
+ src, err := imaging.Open("testdata/lena_512.png")
+ if err != nil {
+ log.Fatalf("Open failed: %v", err)
+ }
+
+ // Crop the original image to 350x350px size using the center anchor.
+ src = imaging.CropAnchor(src, 350, 350, imaging.Center)
+
+ // Resize the cropped image to width = 256px preserving the aspect ratio.
+ src = imaging.Resize(src, 256, 0, imaging.Lanczos)
+
+ // Create a blurred version of the image.
+ img1 := imaging.Blur(src, 2)
+
+ // 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(512, 512, color.NRGBA{0, 0, 0, 0})
+ dst = imaging.Paste(dst, img1, image.Pt(0, 0))
+ dst = imaging.Paste(dst, img2, image.Pt(0, 256))
+ dst = imaging.Paste(dst, img3, image.Pt(256, 0))
+ dst = imaging.Paste(dst, img4, image.Pt(256, 256))
+
+ // Save the resulting image using JPEG format.
+ err = imaging.Save(dst, "testdata/out_example.jpg")
+ if err != nil {
+ log.Fatalf("Save failed: %v", err)
+ }
}
```
+
+Output:
+
+![dstImage](testdata/out_example.jpg)
\ No newline at end of file diff --git a/vendor/github.com/disintegration/imaging/adjust_test.go b/vendor/github.com/disintegration/imaging/adjust_test.go index 99898b0dc..60183d6ea 100644 --- a/vendor/github.com/disintegration/imaging/adjust_test.go +++ b/vendor/github.com/disintegration/imaging/adjust_test.go @@ -206,6 +206,26 @@ func TestAdjustContrast(t *testing.T) { } } +func TestAdjustContrastGolden(t *testing.T) { + src, err := Open("testdata/lena_128.png") + if err != nil { + t.Errorf("Open: %v", err) + } + for name, p := range map[string]float64{ + "out_contrast_m10.png": -10, + "out_contrast_p10.png": 10, + } { + got := AdjustContrast(src, p) + want, err := Open("testdata/" + name) + if err != nil { + t.Errorf("Open: %v", err) + } + if !compareNRGBA(got, toNRGBA(want), 0) { + t.Errorf("resulting image differs from golden: %s", name) + } + } +} + func TestAdjustBrightness(t *testing.T) { td := []struct { desc string @@ -333,6 +353,26 @@ func TestAdjustBrightness(t *testing.T) { } } +func TestAdjustBrightnessGolden(t *testing.T) { + src, err := Open("testdata/lena_128.png") + if err != nil { + t.Errorf("Open: %v", err) + } + for name, p := range map[string]float64{ + "out_brightness_m10.png": -10, + "out_brightness_p10.png": 10, + } { + got := AdjustBrightness(src, p) + want, err := Open("testdata/" + name) + if err != nil { + t.Errorf("Open: %v", err) + } + if !compareNRGBA(got, toNRGBA(want), 0) { + t.Errorf("resulting image differs from golden: %s", name) + } + } +} + func TestAdjustGamma(t *testing.T) { td := []struct { desc string @@ -416,6 +456,26 @@ func TestAdjustGamma(t *testing.T) { } } +func TestAdjustGammaGolden(t *testing.T) { + src, err := Open("testdata/lena_128.png") + if err != nil { + t.Errorf("Open: %v", err) + } + for name, g := range map[string]float64{ + "out_gamma_0.75.png": 0.75, + "out_gamma_1.25.png": 1.25, + } { + got := AdjustGamma(src, g) + want, err := Open("testdata/" + name) + if err != nil { + t.Errorf("Open: %v", err) + } + if !compareNRGBA(got, toNRGBA(want), 0) { + t.Errorf("resulting image differs from golden: %s", name) + } + } +} + func TestAdjustSigmoid(t *testing.T) { td := []struct { desc string diff --git a/vendor/github.com/disintegration/imaging/convolution.go b/vendor/github.com/disintegration/imaging/convolution.go new file mode 100644 index 000000000..ed7540f05 --- /dev/null +++ b/vendor/github.com/disintegration/imaging/convolution.go @@ -0,0 +1,148 @@ +package imaging + +import ( + "image" +) + +// ConvolveOptions are convolution parameters. +type ConvolveOptions struct { + // If Normalize is true the kernel is normalized before convolution. + Normalize bool + + // If Abs is true the absolute value of each color channel is taken after convolution. + Abs bool + + // Bias is added to each color channel value after convolution. + Bias int +} + +// Convolve3x3 convolves the image with the specified 3x3 convolution kernel. +// Default parameters are used if a nil *ConvolveOptions is passed. +func Convolve3x3(img image.Image, kernel [9]float64, options *ConvolveOptions) *image.NRGBA { + return convolve(img, kernel[:], options) +} + +// Convolve5x5 convolves the image with the specified 5x5 convolution kernel. +// Default parameters are used if a nil *ConvolveOptions is passed. +func Convolve5x5(img image.Image, kernel [25]float64, options *ConvolveOptions) *image.NRGBA { + return convolve(img, kernel[:], options) +} + +func convolve(img image.Image, kernel []float64, options *ConvolveOptions) *image.NRGBA { + src := toNRGBA(img) + w := src.Bounds().Max.X + h := src.Bounds().Max.Y + dst := image.NewNRGBA(image.Rect(0, 0, w, h)) + + if w < 1 || h < 1 { + return dst + } + + if options == nil { + options = &ConvolveOptions{} + } + + if options.Normalize { + normalizeKernel(kernel) + } + + type coef struct { + x, y int + k float64 + } + var coefs []coef + var m int + + switch len(kernel) { + case 9: + m = 1 + case 25: + m = 2 + default: + return dst + } + + 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(h, func(partStart, partEnd int) { + for y := partStart; y < partEnd; y++ { + 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 + r += float64(src.Pix[off+0]) * c.k + g += float64(src.Pix[off+1]) * c.k + b += float64(src.Pix[off+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 + dst.Pix[dstOff+0] = clamp(r) + dst.Pix[dstOff+1] = clamp(g) + dst.Pix[dstOff+2] = clamp(b) + dst.Pix[dstOff+3] = src.Pix[srcOff+3] + } + } + }) + + return dst +} + +func normalizeKernel(kernel []float64) { + var sum, sumpos float64 + for i := range kernel { + sum += kernel[i] + if kernel[i] > 0 { + sumpos += kernel[i] + } + } + if sum != 0 { + for i := range kernel { + kernel[i] /= sum + } + } else if sumpos != 0 { + for i := range kernel { + kernel[i] /= sumpos + } + } +} diff --git a/vendor/github.com/disintegration/imaging/convolution_test.go b/vendor/github.com/disintegration/imaging/convolution_test.go new file mode 100644 index 000000000..5a824ead9 --- /dev/null +++ b/vendor/github.com/disintegration/imaging/convolution_test.go @@ -0,0 +1,275 @@ +package imaging + +import ( + "image" + "testing" +) + +func TestConvolve3x3(t *testing.T) { + testCases := []struct { + desc string + src image.Image + kernel [9]float64 + options *ConvolveOptions + want *image.NRGBA + }{ + { + "Convolve3x3 0x0", + &image.NRGBA{ + Rect: image.Rect(0, 0, 0, 0), + Stride: 0, + Pix: []uint8{}, + }, + [9]float64{ + 0, 0, 0, + 0, 1, 0, + 0, 0, 0, + }, + nil, + &image.NRGBA{Rect: image.Rect(0, 0, 0, 0)}, + }, + { + "Convolve3x3 4x4 identity", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 3, 3), + Stride: 4 * 4, + Pix: []uint8{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + }, + }, + [9]float64{ + 0, 0, 0, + 0, 1, 0, + 0, 0, 0, + }, + nil, + &image.NRGBA{ + Rect: image.Rect(0, 0, 4, 4), + Stride: 4 * 4, + Pix: []uint8{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + }, + }, + }, + { + "Convolve3x3 4x4 abs", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 3, 3), + Stride: 4 * 4, + Pix: []uint8{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + }, + }, + [9]float64{ + 0, 0, 0, + 0, -1, 0, + 0, 0, 0, + }, + &ConvolveOptions{Abs: true}, + &image.NRGBA{ + Rect: image.Rect(0, 0, 4, 4), + Stride: 4 * 4, + Pix: []uint8{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + }, + }, + }, + { + "Convolve3x3 4x4 bias", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 3, 3), + Stride: 4 * 4, + Pix: []uint8{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + }, + }, + [9]float64{ + 0, 0, 0, + 0, 1, 0, + 0, 0, 0, + }, + &ConvolveOptions{Bias: 0x10}, + &image.NRGBA{ + Rect: image.Rect(0, 0, 4, 4), + Stride: 4 * 4, + Pix: []uint8{ + 0x10, 0x11, 0x12, 0x03, 0x14, 0x15, 0x16, 0x07, 0x18, 0x19, 0x1a, 0x0b, 0x1c, 0x1d, 0x1e, 0x0f, + 0x20, 0x21, 0x22, 0x13, 0x24, 0x25, 0x26, 0x17, 0x28, 0x29, 0x2a, 0x1b, 0x2c, 0x2d, 0x2e, 0x1f, + 0x30, 0x31, 0x32, 0x23, 0x34, 0x35, 0x36, 0x27, 0x38, 0x39, 0x3a, 0x2b, 0x3c, 0x3d, 0x3e, 0x2f, + 0x40, 0x41, 0x42, 0x33, 0x44, 0x45, 0x46, 0x37, 0x48, 0x49, 0x4a, 0x3b, 0x4c, 0x4d, 0x4e, 0x3f, + }, + }, + }, + { + "Convolve3x3 4x4 norm", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 3, 3), + Stride: 4 * 4, + Pix: []uint8{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + }, + }, + [9]float64{ + 1, 1, 1, + 1, 1, 1, + 1, 1, 1, + }, + &ConvolveOptions{Normalize: true}, + &image.NRGBA{ + Rect: image.Rect(0, 0, 4, 4), + Stride: 4 * 4, + Pix: []uint8{ + 0x07, 0x08, 0x09, 0x03, 0x09, 0x0a, 0x0b, 0x07, 0x0d, 0x0e, 0x0f, 0x0b, 0x10, 0x11, 0x12, 0x0f, + 0x11, 0x12, 0x13, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1b, 0x1c, 0x1d, 0x1f, + 0x21, 0x22, 0x23, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2b, 0x2c, 0x2d, 0x2f, + 0x2c, 0x2d, 0x2e, 0x33, 0x2f, 0x30, 0x31, 0x37, 0x33, 0x34, 0x35, 0x3b, 0x35, 0x36, 0x37, 0x3f, + }, + }, + }, + { + "Convolve3x3 3x3 laplacian", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 2, 2), + Stride: 3 * 4, + Pix: []uint8{ + 0x00, 0x01, 0x01, 0xff, 0x00, 0x01, 0x02, 0xff, 0x00, 0x01, 0x03, 0xff, + 0x00, 0x01, 0x04, 0xff, 0x10, 0x10, 0x10, 0xff, 0x00, 0x01, 0x05, 0xff, + 0x00, 0x01, 0x06, 0xff, 0x00, 0x01, 0x07, 0xff, 0x00, 0x01, 0x08, 0xff, + }, + }, + [9]float64{ + -1, -1, -1, + -1, 8, -1, + -1, -1, -1, + }, + nil, + &image.NRGBA{ + Rect: image.Rect(0, 0, 3, 3), + Stride: 3 * 4, + Pix: []uint8{ + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x80, 0x78, 0x5c, 0xff, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, + }, + }, + }, + } + + for _, tc := range testCases { + got := Convolve3x3(tc.src, tc.kernel, tc.options) + want := tc.want + if !compareNRGBA(got, want, 0) { + t.Errorf("test [%s] failed: want %#v got %#v", tc.desc, want, got) + } + } +} + +func TestConvolve5x5(t *testing.T) { + testCases := []struct { + desc string + src image.Image + kernel [25]float64 + options *ConvolveOptions + want *image.NRGBA + }{ + { + "Convolve5x5 4x4 translate", + &image.NRGBA{ + Rect: image.Rect(-1, -1, 3, 3), + Stride: 4 * 4, + Pix: []uint8{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + }, + }, + [25]float64{ + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, + }, + nil, + &image.NRGBA{ + Rect: image.Rect(0, 0, 4, 4), + Stride: 4 * 4, + Pix: []uint8{ + 0x28, 0x29, 0x2a, 0x03, 0x2c, 0x2d, 0x2e, 0x07, 0x2c, 0x2d, 0x2e, 0x0b, 0x2c, 0x2d, 0x2e, 0x0f, + 0x38, 0x39, 0x3a, 0x13, 0x3c, 0x3d, 0x3e, 0x17, 0x3c, 0x3d, 0x3e, 0x1b, 0x3c, 0x3d, 0x3e, 0x1f, + 0x38, 0x39, 0x3a, 0x23, 0x3c, 0x3d, 0x3e, 0x27, 0x3c, 0x3d, 0x3e, 0x2b, 0x3c, 0x3d, 0x3e, 0x2f, + 0x38, 0x39, 0x3a, 0x33, 0x3c, 0x3d, 0x3e, 0x37, 0x3c, 0x3d, 0x3e, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + }, + }, + }, + } + + for _, tc := range testCases { + got := Convolve5x5(tc.src, tc.kernel, tc.options) + want := tc.want + if !compareNRGBA(got, want, 0) { + t.Errorf("test [%s] failed: want %#v got %#v", tc.desc, want, got) + } + } +} + +func BenchmarkConvolve3x3(b *testing.B) { + b.StopTimer() + img, err := Open("testdata/lena_512.png") + if err != nil { + b.Fatalf("Open: %v", err) + } + b.StartTimer() + for i := 0; i < b.N; i++ { + Convolve3x3( + img, + [9]float64{ + -1, -1, 0, + -1, 0, 1, + 0, 1, 1, + }, + nil, + ) + } +} + +func BenchmarkConvolve5x5(b *testing.B) { + b.StopTimer() + img, err := Open("testdata/lena_512.png") + if err != nil { + b.Fatalf("Open: %v", err) + } + b.StartTimer() + for i := 0; i < b.N; i++ { + Convolve5x5( + img, + [25]float64{ + -1, -1, -1, -1, 0, + -1, -1, -1, 0, 1, + -1, -1, 0, 1, 1, + -1, 0, 1, 1, 1, + 0, 1, 1, 1, 1, + }, + nil, + ) + } +} diff --git a/vendor/github.com/disintegration/imaging/effects.go b/vendor/github.com/disintegration/imaging/effects.go index 19d6e405e..25c940381 100644 --- a/vendor/github.com/disintegration/imaging/effects.go +++ b/vendor/github.com/disintegration/imaging/effects.go @@ -62,8 +62,7 @@ func blurHorizontal(src *image.NRGBA, kernel []float64) *image.NRGBA { } for y := 0; y < height; y++ { - - r, g, b, a := 0.0, 0.0, 0.0, 0.0 + var r, g, b, a float64 for ix := start; ix <= end; ix++ { weight := kernel[absint(x-ix)] i := y*src.Stride + ix*4 @@ -74,17 +73,11 @@ func blurHorizontal(src *image.NRGBA, kernel []float64) *image.NRGBA { a += wa } - r = math.Min(math.Max(r/a, 0.0), 255.0) - g = math.Min(math.Max(g/a, 0.0), 255.0) - b = math.Min(math.Max(b/a, 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) - + dst.Pix[j+0] = clamp(r / a) + dst.Pix[j+1] = clamp(g / a) + dst.Pix[j+2] = clamp(b / a) + dst.Pix[j+3] = clamp(a / weightSum) } } }) @@ -117,8 +110,7 @@ func blurVertical(src *image.NRGBA, kernel []float64) *image.NRGBA { } for x := 0; x < width; x++ { - - r, g, b, a := 0.0, 0.0, 0.0, 0.0 + var r, g, b, a float64 for iy := start; iy <= end; iy++ { weight := kernel[absint(y-iy)] i := iy*src.Stride + x*4 @@ -129,17 +121,11 @@ func blurVertical(src *image.NRGBA, kernel []float64) *image.NRGBA { a += wa } - r = math.Min(math.Max(r/a, 0.0), 255.0) - g = math.Min(math.Max(g/a, 0.0), 255.0) - b = math.Min(math.Max(b/a, 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) - + dst.Pix[j+0] = clamp(r / a) + dst.Pix[j+1] = clamp(g / a) + dst.Pix[j+2] = clamp(b / a) + dst.Pix[j+3] = clamp(a / weightSum) } } }) @@ -173,7 +159,7 @@ func Sharpen(img image.Image, sigma float64) *image.NRGBA { 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])) + val := int(src.Pix[k])<<1 - int(blurred.Pix[k]) if val < 0 { val = 0 } else if val > 255 { diff --git a/vendor/github.com/disintegration/imaging/effects_test.go b/vendor/github.com/disintegration/imaging/effects_test.go index 998ffe399..9c787cb49 100644 --- a/vendor/github.com/disintegration/imaging/effects_test.go +++ b/vendor/github.com/disintegration/imaging/effects_test.go @@ -88,6 +88,38 @@ func TestBlur(t *testing.T) { } } +func TestBlurGolden(t *testing.T) { + src, err := Open("testdata/lena_128.png") + if err != nil { + t.Errorf("Open: %v", err) + } + for name, sigma := range map[string]float64{ + "out_blur_0.5.png": 0.5, + "out_blur_1.5.png": 1.5, + } { + got := Blur(src, sigma) + want, err := Open("testdata/" + name) + if err != nil { + t.Errorf("Open: %v", err) + } + if !compareNRGBA(got, toNRGBA(want), 0) { + t.Errorf("resulting image differs from golden: %s", name) + } + } +} + +func BenchmarkBlur(b *testing.B) { + b.StopTimer() + img, err := Open("testdata/lena_512.png") + if err != nil { + b.Fatalf("Open: %v", err) + } + b.StartTimer() + for i := 0; i < b.N; i++ { + Blur(img, 3) + } +} + func TestSharpen(t *testing.T) { td := []struct { desc string @@ -188,3 +220,35 @@ func TestSharpen(t *testing.T) { } } } + +func TestSharpenGolden(t *testing.T) { + src, err := Open("testdata/lena_128.png") + if err != nil { + t.Errorf("Open: %v", err) + } + for name, sigma := range map[string]float64{ + "out_sharpen_0.5.png": 0.5, + "out_sharpen_1.5.png": 1.5, + } { + got := Sharpen(src, sigma) + want, err := Open("testdata/" + name) + if err != nil { + t.Errorf("Open: %v", err) + } + if !compareNRGBA(got, toNRGBA(want), 0) { + t.Errorf("resulting image differs from golden: %s", name) + } + } +} + +func BenchmarkSharpen(b *testing.B) { + b.StopTimer() + img, err := Open("testdata/lena_512.png") + if err != nil { + b.Fatalf("Open: %v", err) + } + b.StartTimer() + for i := 0; i < b.N; i++ { + Sharpen(img, 3) + } +} diff --git a/vendor/github.com/disintegration/imaging/example_test.go b/vendor/github.com/disintegration/imaging/example_test.go new file mode 100644 index 000000000..d79451f03 --- /dev/null +++ b/vendor/github.com/disintegration/imaging/example_test.go @@ -0,0 +1,58 @@ +package imaging_test + +import ( + "image" + "image/color" + "log" + + "github.com/disintegration/imaging" +) + +func Example() { + // Open the test image. + src, err := imaging.Open("testdata/lena_512.png") + if err != nil { + log.Fatalf("Open failed: %v", err) + } + + // Crop the original image to 350x350px size using the center anchor. + src = imaging.CropAnchor(src, 350, 350, imaging.Center) + + // Resize the cropped image to width = 256px preserving the aspect ratio. + src = imaging.Resize(src, 256, 0, imaging.Lanczos) + + // Create a blurred version of the image. + img1 := imaging.Blur(src, 2) + + // 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(512, 512, color.NRGBA{0, 0, 0, 0}) + dst = imaging.Paste(dst, img1, image.Pt(0, 0)) + dst = imaging.Paste(dst, img2, image.Pt(0, 256)) + dst = imaging.Paste(dst, img3, image.Pt(256, 0)) + dst = imaging.Paste(dst, img4, image.Pt(256, 256)) + + // Save the resulting image using JPEG format. + err = imaging.Save(dst, "testdata/out_example.jpg") + if err != nil { + log.Fatalf("Save failed: %v", err) + } +} diff --git a/vendor/github.com/disintegration/imaging/helpers.go b/vendor/github.com/disintegration/imaging/helpers.go index 79967ae44..3a4cbde80 100644 --- a/vendor/github.com/disintegration/imaging/helpers.go +++ b/vendor/github.com/disintegration/imaging/helpers.go @@ -1,11 +1,9 @@ -/* -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 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 ( @@ -24,8 +22,10 @@ import ( "golang.org/x/image/tiff" ) +// Format is an image file format. type Format int +// Image file formats. const ( JPEG Format = iota PNG @@ -52,6 +52,7 @@ func (f Format) String() string { } var ( + // ErrUnsupportedFormat means the given image format (or file extension) is unsupported. ErrUnsupportedFormat = errors.New("imaging: unsupported image format") ) @@ -194,15 +195,12 @@ func Clone(img image.Image) *image.NRGBA { 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 - } } }) @@ -213,9 +211,9 @@ func Clone(img image.Image) *image.NRGBA { 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 @@ -237,7 +235,6 @@ func Clone(img image.Image) *image.NRGBA { di += 4 si += 4 - } } }) @@ -248,9 +245,9 @@ func Clone(img image.Image) *image.NRGBA { 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 @@ -272,7 +269,6 @@ func Clone(img image.Image) *image.NRGBA { di += 4 si += 8 - } } }) @@ -283,16 +279,13 @@ func Clone(img image.Image) *image.NRGBA { 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 - } } }) @@ -303,16 +296,13 @@ func Clone(img image.Image) *image.NRGBA { 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 - } } }) @@ -322,7 +312,6 @@ func Clone(img image.Image) *image.NRGBA { 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) @@ -332,9 +321,7 @@ func Clone(img image.Image) *image.NRGBA { dst.Pix[di+1] = g dst.Pix[di+2] = b dst.Pix[di+3] = 0xff - di += 4 - } } }) @@ -345,22 +332,18 @@ func Clone(img image.Image) *image.NRGBA { 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 - } } }) @@ -370,15 +353,12 @@ func Clone(img image.Image) *image.NRGBA { 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 - } } }) @@ -388,7 +368,7 @@ func Clone(img image.Image) *image.NRGBA { return dst } -// This function used internally to convert any image type to NRGBA if needed. +// toNRGBA converts any image type to *image.NRGBA with min-point at (0, 0). func toNRGBA(img image.Image) *image.NRGBA { srcBounds := img.Bounds() if srcBounds.Min.X == 0 && srcBounds.Min.Y == 0 { diff --git a/vendor/github.com/disintegration/imaging/histogram.go b/vendor/github.com/disintegration/imaging/histogram.go index aef333822..3afcb7ae2 100644 --- a/vendor/github.com/disintegration/imaging/histogram.go +++ b/vendor/github.com/disintegration/imaging/histogram.go @@ -17,7 +17,7 @@ func Histogram(img image.Image) [256]float64 { var total float64 if width == 0 || height == 0 { - return histogram + return histogram } for y := 0; y < height; y++ { diff --git a/vendor/github.com/disintegration/imaging/histogram_test.go b/vendor/github.com/disintegration/imaging/histogram_test.go index 0bcf82588..10f4c3fef 100644 --- a/vendor/github.com/disintegration/imaging/histogram_test.go +++ b/vendor/github.com/disintegration/imaging/histogram_test.go @@ -36,7 +36,7 @@ func TestHistogram(t *testing.T) { for _, val := range h { if val != 0 { t.Errorf("Histogram for an empty image should be a zero histogram.") - return + return } } } diff --git a/vendor/github.com/disintegration/imaging/resize.go b/vendor/github.com/disintegration/imaging/resize.go index b21eed544..937f63fe9 100644 --- a/vendor/github.com/disintegration/imaging/resize.go +++ b/vendor/github.com/disintegration/imaging/resize.go @@ -5,17 +5,12 @@ import ( "math" ) -type iwpair struct { - i int - w int32 +type indexWeight struct { + index int + weight float64 } -type pweights struct { - iwpairs []iwpair - wsum int32 -} - -func precomputeWeights(dstSize, srcSize int, filter ResampleFilter) []pweights { +func precomputeWeights(dstSize, srcSize int, filter ResampleFilter) [][]indexWeight { du := float64(srcSize) / float64(dstSize) scale := du if scale < 1.0 { @@ -23,7 +18,7 @@ func precomputeWeights(dstSize, srcSize int, filter ResampleFilter) []pweights { } ru := math.Ceil(scale * filter.Support) - out := make([]pweights, dstSize) + out := make([][]indexWeight, dstSize) for v := 0; v < dstSize; v++ { fu := (float64(v)+0.5)*du - 0.5 @@ -37,15 +32,19 @@ func precomputeWeights(dstSize, srcSize int, filter ResampleFilter) []pweights { endu = srcSize - 1 } - wsum := int32(0) + var sum float64 for u := startu; u <= endu; u++ { - w := int32(0xff * filter.Kernel((float64(u)-fu)/scale)) + w := filter.Kernel((float64(u) - fu) / scale) if w != 0 { - wsum += w - out[v].iwpairs = append(out[v].iwpairs, iwpair{u, w}) + sum += w + out[v] = append(out[v], indexWeight{index: u, weight: w}) + } + } + if sum != 0 { + for i := range out[v] { + out[v][i].weight /= sum } } - out[v].wsum = wsum } return out @@ -127,22 +126,26 @@ func resizeHorizontal(src *image.NRGBA, width int, filter ResampleFilter) *image parallel(dstH, func(partStart, partEnd int) { for dstY := partStart; dstY < partEnd; dstY++ { + i0 := dstY * src.Stride + j0 := dstY * dst.Stride for dstX := 0; dstX < dstW; dstX++ { - var c [4]int64 - for _, iw := range weights[dstX].iwpairs { - i := dstY*src.Stride + iw.i*4 - a := int64(src.Pix[i+3]) * int64(iw.w) - c[0] += int64(src.Pix[i+0]) * a - c[1] += int64(src.Pix[i+1]) * a - c[2] += int64(src.Pix[i+2]) * a - c[3] += a + var r, g, b, a float64 + for _, w := range weights[dstX] { + i := i0 + w.index*4 + aw := float64(src.Pix[i+3]) * w.weight + r += float64(src.Pix[i+0]) * aw + g += float64(src.Pix[i+1]) * aw + b += float64(src.Pix[i+2]) * aw + a += aw + } + if a != 0 { + aInv := 1 / a + j := j0 + dstX*4 + dst.Pix[j+0] = clamp(r * aInv) + dst.Pix[j+1] = clamp(g * aInv) + dst.Pix[j+2] = clamp(b * aInv) + dst.Pix[j+3] = clamp(a) } - j := dstY*dst.Stride + dstX*4 - sum := weights[dstX].wsum - dst.Pix[j+0] = clampint32(int32(float64(c[0])/float64(c[3]) + 0.5)) - dst.Pix[j+1] = clampint32(int32(float64(c[1])/float64(c[3]) + 0.5)) - dst.Pix[j+2] = clampint32(int32(float64(c[2])/float64(c[3]) + 0.5)) - dst.Pix[j+3] = clampint32(int32(float64(c[3])/float64(sum) + 0.5)) } } }) @@ -163,33 +166,33 @@ func resizeVertical(src *image.NRGBA, height int, filter ResampleFilter) *image. 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]int64 - for _, iw := range weights[dstY].iwpairs { - i := iw.i*src.Stride + dstX*4 - a := int64(src.Pix[i+3]) * int64(iw.w) - c[0] += int64(src.Pix[i+0]) * a - c[1] += int64(src.Pix[i+1]) * a - c[2] += int64(src.Pix[i+2]) * a - c[3] += a + var r, g, b, a float64 + for _, w := range weights[dstY] { + i := w.index*src.Stride + dstX*4 + aw := float64(src.Pix[i+3]) * w.weight + r += float64(src.Pix[i+0]) * aw + g += float64(src.Pix[i+1]) * aw + b += float64(src.Pix[i+2]) * aw + a += aw + } + if a != 0 { + aInv := 1 / a + j := dstY*dst.Stride + dstX*4 + dst.Pix[j+0] = clamp(r * aInv) + dst.Pix[j+1] = clamp(g * aInv) + dst.Pix[j+2] = clamp(b * aInv) + dst.Pix[j+3] = clamp(a) } - j := dstY*dst.Stride + dstX*4 - sum := weights[dstY].wsum - dst.Pix[j+0] = clampint32(int32(float64(c[0])/float64(c[3]) + 0.5)) - dst.Pix[j+1] = clampint32(int32(float64(c[1])/float64(c[3]) + 0.5)) - dst.Pix[j+2] = clampint32(int32(float64(c[2])/float64(c[3]) + 0.5)) - dst.Pix[j+3] = clampint32(int32(float64(c[3])/float64(sum) + 0.5)) } } - }) return dst } -// fast nearest-neighbor resize, no filtering +// resizeNearest is a fast nearest-neighbor resize, no filtering. func resizeNearest(src *image.NRGBA, width, height int) *image.NRGBA { dstW, dstH := width, height @@ -205,13 +208,16 @@ func resizeNearest(src *image.NRGBA, width, height int) *image.NRGBA { parallel(dstH, func(partStart, partEnd int) { for dstY := partStart; dstY < partEnd; dstY++ { - fy := (float64(dstY)+0.5)*dy - 0.5 + srcY := int((float64(dstY) + 0.5) * dy) + if srcY > srcH-1 { + srcY = srcH - 1 + } 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))) + srcX := int((float64(dstX) + 0.5) * dx) + if srcX > srcW-1 { + srcX = srcW - 1 + } srcOff := srcY*src.Stride + srcX*4 dstOff := dstY*dst.Stride + dstX*4 @@ -326,7 +332,7 @@ func Thumbnail(img image.Image, width, height int, filter ResampleFilter) *image return Fill(img, width, height, Center, filter) } -// Resample filter struct. It can be used to make custom filters. +// ResampleFilter is a resampling filter struct. It can be used to define custom filters. // // Supported resample filters: NearestNeighbor, Box, Linear, Hermite, MitchellNetravali, // CatmullRom, BSpline, Gaussian, Lanczos, Hann, Hamming, Blackman, Bartlett, Welch, Cosine. @@ -361,7 +367,7 @@ type ResampleFilter struct { Kernel func(float64) float64 } -// Nearest-neighbor filter, no anti-aliasing. +// NearestNeighbor is a nearest-neighbor filter (no anti-aliasing). var NearestNeighbor ResampleFilter // Box filter (averaging pixels). @@ -373,37 +379,37 @@ 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). +// MitchellNetravali is 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). +// CatmullRom is a 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). +// BSpline is a smooth cubic filter (BC-spline; B=1; C=0). var BSpline ResampleFilter -// Gaussian Blurring Filter. +// Gaussian is a Gaussian blurring Filter. var Gaussian ResampleFilter -// Bartlett-windowed sinc filter (3 lobes). +// Bartlett is a Bartlett-windowed sinc filter (3 lobes). var Bartlett ResampleFilter // Lanczos filter (3 lobes). var Lanczos ResampleFilter -// Hann-windowed sinc filter (3 lobes). +// Hann is a Hann-windowed sinc filter (3 lobes). var Hann ResampleFilter -// Hamming-windowed sinc filter (3 lobes). +// Hamming is a Hamming-windowed sinc filter (3 lobes). var Hamming ResampleFilter -// Blackman-windowed sinc filter (3 lobes). +// Blackman is a Blackman-windowed sinc filter (3 lobes). var Blackman ResampleFilter -// Welch-windowed sinc filter (parabolic window, 3 lobes). +// Welch is a Welch-windowed sinc filter (parabolic window, 3 lobes). var Welch ResampleFilter -// Cosine-windowed sinc filter (3 lobes). +// Cosine is a Cosine-windowed sinc filter (3 lobes). var Cosine ResampleFilter func bcspline(x, b, c float64) float64 { diff --git a/vendor/github.com/disintegration/imaging/resize_test.go b/vendor/github.com/disintegration/imaging/resize_test.go index 927f92512..72701e093 100644 --- a/vendor/github.com/disintegration/imaging/resize_test.go +++ b/vendor/github.com/disintegration/imaging/resize_test.go @@ -108,10 +108,10 @@ func TestResize(t *testing.T) { Rect: image.Rect(0, 0, 4, 4), Stride: 4 * 4, Pix: []uint8{ - 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x3f, 0xff, 0x00, 0x00, 0xc0, 0xff, 0x00, 0x00, 0xff, - 0x00, 0xff, 0x00, 0x3f, 0x6d, 0x6e, 0x24, 0x6f, 0xb1, 0x13, 0x3a, 0xd0, 0xc0, 0x00, 0x3f, 0xff, - 0x00, 0xff, 0x00, 0xc0, 0x13, 0xb2, 0x3a, 0xcf, 0x33, 0x32, 0x9a, 0xef, 0x3f, 0x00, 0xc0, 0xff, - 0x00, 0xff, 0x00, 0xff, 0x00, 0xc0, 0x3f, 0xff, 0x00, 0x3f, 0xc0, 0xff, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x40, 0xff, 0x00, 0x00, 0xbf, 0xff, 0x00, 0x00, 0xff, + 0x00, 0xff, 0x00, 0x40, 0x6e, 0x6d, 0x25, 0x70, 0xb0, 0x14, 0x3b, 0xcf, 0xbf, 0x00, 0x40, 0xff, + 0x00, 0xff, 0x00, 0xbf, 0x14, 0xb0, 0x3b, 0xcf, 0x33, 0x33, 0x99, 0xef, 0x40, 0x00, 0xbf, 0xff, + 0x00, 0xff, 0x00, 0xff, 0x00, 0xbf, 0x40, 0xff, 0x00, 0x40, 0xbf, 0xff, 0x00, 0x00, 0xff, 0xff, }, }, }, @@ -158,7 +158,7 @@ func TestResize(t *testing.T) { for _, d := range td { got := Resize(d.src, d.w, d.h, d.f) want := d.want - if !compareNRGBA(got, want, 1) { + if !compareNRGBA(got, want, 0) { t.Errorf("test [%s] failed: %#v", d.desc, got) } } @@ -201,6 +201,28 @@ func TestResize(t *testing.T) { } } +func TestResizeGolden(t *testing.T) { + src, err := Open("testdata/lena_512.png") + if err != nil { + t.Errorf("Open: %v", err) + } + for name, filter := range map[string]ResampleFilter{ + "out_resize_nearest.png": NearestNeighbor, + "out_resize_linear.png": Linear, + "out_resize_catrom.png": CatmullRom, + "out_resize_lanczos.png": Lanczos, + } { + got := Resize(src, 128, 0, filter) + want, err := Open("testdata/" + name) + if err != nil { + t.Errorf("Open: %v", err) + } + if !compareNRGBA(got, toNRGBA(want), 0) { + t.Errorf("resulting image differs from golden: %s", name) + } + } +} + func TestFit(t *testing.T) { td := []struct { desc string @@ -568,3 +590,39 @@ func TestThumbnail(t *testing.T) { } } } + +func BenchmarkResizeLanczosUp(b *testing.B) { + benchmarkResize(b, "testdata/lena_128.png", 512, Lanczos) +} + +func BenchmarkResizeLinearUp(b *testing.B) { + benchmarkResize(b, "testdata/lena_128.png", 512, Linear) +} + +func BenchmarkResizeNearestNeighborUp(b *testing.B) { + benchmarkResize(b, "testdata/lena_128.png", 512, NearestNeighbor) +} + +func BenchmarkResizeLanczosDown(b *testing.B) { + benchmarkResize(b, "testdata/lena_512.png", 128, Lanczos) +} + +func BenchmarkResizeLinearDown(b *testing.B) { + benchmarkResize(b, "testdata/lena_512.png", 128, Linear) +} + +func BenchmarkResizeNearestNeighborDown(b *testing.B) { + benchmarkResize(b, "testdata/lena_512.png", 128, NearestNeighbor) +} + +func benchmarkResize(b *testing.B, filename string, size int, f ResampleFilter) { + b.StopTimer() + img, err := Open(filename) + if err != nil { + b.Fatalf("Open: %v", err) + } + b.StartTimer() + for i := 0; i < b.N; i++ { + Resize(img, size, size, f) + } +} diff --git a/vendor/github.com/disintegration/imaging/testdata/lena_128.png b/vendor/github.com/disintegration/imaging/testdata/lena_128.png Binary files differnew file mode 100644 index 000000000..43eb8e7c6 --- /dev/null +++ b/vendor/github.com/disintegration/imaging/testdata/lena_128.png diff --git a/vendor/github.com/disintegration/imaging/testdata/lena_512.png b/vendor/github.com/disintegration/imaging/testdata/lena_512.png Binary files differnew file mode 100644 index 000000000..0e0f9e6bc --- /dev/null +++ b/vendor/github.com/disintegration/imaging/testdata/lena_512.png diff --git a/vendor/github.com/disintegration/imaging/testdata/out_blur_0.5.png b/vendor/github.com/disintegration/imaging/testdata/out_blur_0.5.png Binary files differnew file mode 100644 index 000000000..90d19347e --- /dev/null +++ b/vendor/github.com/disintegration/imaging/testdata/out_blur_0.5.png diff --git a/vendor/github.com/disintegration/imaging/testdata/out_blur_1.5.png b/vendor/github.com/disintegration/imaging/testdata/out_blur_1.5.png Binary files differnew file mode 100644 index 000000000..3df019c33 --- /dev/null +++ b/vendor/github.com/disintegration/imaging/testdata/out_blur_1.5.png diff --git a/vendor/github.com/disintegration/imaging/testdata/out_brightness_m10.png b/vendor/github.com/disintegration/imaging/testdata/out_brightness_m10.png Binary files differnew file mode 100644 index 000000000..a59191e9d --- /dev/null +++ b/vendor/github.com/disintegration/imaging/testdata/out_brightness_m10.png diff --git a/vendor/github.com/disintegration/imaging/testdata/out_brightness_p10.png b/vendor/github.com/disintegration/imaging/testdata/out_brightness_p10.png Binary files differnew file mode 100644 index 000000000..0b8bb3e10 --- /dev/null +++ b/vendor/github.com/disintegration/imaging/testdata/out_brightness_p10.png diff --git a/vendor/github.com/disintegration/imaging/testdata/out_contrast_m10.png b/vendor/github.com/disintegration/imaging/testdata/out_contrast_m10.png Binary files differnew file mode 100644 index 000000000..8b8e0b8e0 --- /dev/null +++ b/vendor/github.com/disintegration/imaging/testdata/out_contrast_m10.png diff --git a/vendor/github.com/disintegration/imaging/testdata/out_contrast_p10.png b/vendor/github.com/disintegration/imaging/testdata/out_contrast_p10.png Binary files differnew file mode 100644 index 000000000..dbfd40eea --- /dev/null +++ b/vendor/github.com/disintegration/imaging/testdata/out_contrast_p10.png diff --git a/vendor/github.com/disintegration/imaging/testdata/out_example.jpg b/vendor/github.com/disintegration/imaging/testdata/out_example.jpg Binary files differnew file mode 100644 index 000000000..5f225b58f --- /dev/null +++ b/vendor/github.com/disintegration/imaging/testdata/out_example.jpg diff --git a/vendor/github.com/disintegration/imaging/testdata/out_gamma_0.75.png b/vendor/github.com/disintegration/imaging/testdata/out_gamma_0.75.png Binary files differnew file mode 100644 index 000000000..89d28735b --- /dev/null +++ b/vendor/github.com/disintegration/imaging/testdata/out_gamma_0.75.png diff --git a/vendor/github.com/disintegration/imaging/testdata/out_gamma_1.25.png b/vendor/github.com/disintegration/imaging/testdata/out_gamma_1.25.png Binary files differnew file mode 100644 index 000000000..915e6d53d --- /dev/null +++ b/vendor/github.com/disintegration/imaging/testdata/out_gamma_1.25.png diff --git a/vendor/github.com/disintegration/imaging/testdata/out_resize_catrom.png b/vendor/github.com/disintegration/imaging/testdata/out_resize_catrom.png Binary files differnew file mode 100644 index 000000000..628272f97 --- /dev/null +++ b/vendor/github.com/disintegration/imaging/testdata/out_resize_catrom.png diff --git a/vendor/github.com/disintegration/imaging/testdata/out_resize_lanczos.png b/vendor/github.com/disintegration/imaging/testdata/out_resize_lanczos.png Binary files differnew file mode 100644 index 000000000..6d168f70e --- /dev/null +++ b/vendor/github.com/disintegration/imaging/testdata/out_resize_lanczos.png diff --git a/vendor/github.com/disintegration/imaging/testdata/out_resize_linear.png b/vendor/github.com/disintegration/imaging/testdata/out_resize_linear.png Binary files differnew file mode 100644 index 000000000..0a74e5e5f --- /dev/null +++ b/vendor/github.com/disintegration/imaging/testdata/out_resize_linear.png diff --git a/vendor/github.com/disintegration/imaging/testdata/out_resize_nearest.png b/vendor/github.com/disintegration/imaging/testdata/out_resize_nearest.png Binary files differnew file mode 100644 index 000000000..108cb6601 --- /dev/null +++ b/vendor/github.com/disintegration/imaging/testdata/out_resize_nearest.png diff --git a/vendor/github.com/disintegration/imaging/testdata/out_sharpen_0.5.png b/vendor/github.com/disintegration/imaging/testdata/out_sharpen_0.5.png Binary files differnew file mode 100644 index 000000000..056ba59b2 --- /dev/null +++ b/vendor/github.com/disintegration/imaging/testdata/out_sharpen_0.5.png diff --git a/vendor/github.com/disintegration/imaging/testdata/out_sharpen_1.5.png b/vendor/github.com/disintegration/imaging/testdata/out_sharpen_1.5.png Binary files differnew file mode 100644 index 000000000..a47dc6040 --- /dev/null +++ b/vendor/github.com/disintegration/imaging/testdata/out_sharpen_1.5.png diff --git a/vendor/github.com/disintegration/imaging/tools.go b/vendor/github.com/disintegration/imaging/tools.go index 76f14447b..2c6d68eea 100644 --- a/vendor/github.com/disintegration/imaging/tools.go +++ b/vendor/github.com/disintegration/imaging/tools.go @@ -8,6 +8,7 @@ import ( // Anchor is the anchor point for image alignment. type Anchor int +// Anchor point positions. const ( Center Anchor = iota TopLeft diff --git a/vendor/github.com/disintegration/imaging/tools_test.go b/vendor/github.com/disintegration/imaging/tools_test.go index 2ac7b31d3..83a258c26 100644 --- a/vendor/github.com/disintegration/imaging/tools_test.go +++ b/vendor/github.com/disintegration/imaging/tools_test.go @@ -598,7 +598,7 @@ func TestOverlay(t *testing.T) { for _, d := range td { got := Overlay(d.src1, d.src2, d.p, d.a) want := d.want - if !compareNRGBA(got, want, 1) { + if !compareNRGBA(got, want, 0) { t.Errorf("test [%s] failed: %#v", d.desc, got) } } diff --git a/vendor/github.com/disintegration/imaging/utils.go b/vendor/github.com/disintegration/imaging/utils.go index 8b1ab8adb..9f5926aaa 100644 --- a/vendor/github.com/disintegration/imaging/utils.go +++ b/vendor/github.com/disintegration/imaging/utils.go @@ -1,28 +1,24 @@ 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 +// parallel starts parallel image processing based on the current GOMAXPROCS value. +// If GOMAXPROCS = 1 it uses no parallelization. +// If GOMAXPROCS > 1 it spawns 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 - } + numProcs := runtime.GOMAXPROCS(0) + if numProcs > 1 { + numGoroutines = numProcs + partSize = dataSize / (numGoroutines * 10) + if partSize < 1 { + partSize = 1 } } @@ -54,6 +50,7 @@ func parallel(dataSize int, fn func(partStart, partEnd int)) { } } +// absint returns the absolute value of i. func absint(i int) int { if i < 0 { return -i @@ -61,17 +58,14 @@ func absint(i int) int { 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 { +// 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 } - return uint8(v) + if v > 0 { + return uint8(v) + } + return 0 } diff --git a/vendor/github.com/disintegration/imaging/utils_test.go b/vendor/github.com/disintegration/imaging/utils_test.go index 99aa8c82f..44cc75102 100644 --- a/vendor/github.com/disintegration/imaging/utils_test.go +++ b/vendor/github.com/disintegration/imaging/utils_test.go @@ -15,7 +15,7 @@ func testParallelN(enabled bool, n, procs int) bool { } }) for i := 0; i < n; i++ { - if data[i] != true { + if !data[i] { return false } } @@ -27,7 +27,7 @@ func TestParallel(t *testing.T) { for _, e := range []bool{true, false} { for _, n := range []int{1, 10, 100, 1000} { for _, p := range []int{1, 2, 4, 8, 16, 100} { - if testParallelN(e, n, p) != true { + if !testParallelN(e, n, p) { t.Errorf("test [parallel %v %d %d] failed", e, n, p) } } @@ -59,23 +59,3 @@ func TestClamp(t *testing.T) { } } } - -func TestClampint32(t *testing.T) { - td := []struct { - i int32 - u uint8 - }{ - {0, 0}, - {255, 255}, - {128, 128}, - {256, 255}, - {2500, 255}, - {-10, 0}, - } - - for _, d := range td { - if clampint32(d.i) != d.u { - t.Errorf("test [clampint32 %v %v] failed: %v", d.i, d.u, clampint32(d.i)) - } - } -} |