summaryrefslogtreecommitdiffstats
path: root/Godeps/_workspace/src/github.com/nfnt/resize/resize.go
diff options
context:
space:
mode:
Diffstat (limited to 'Godeps/_workspace/src/github.com/nfnt/resize/resize.go')
-rw-r--r--Godeps/_workspace/src/github.com/nfnt/resize/resize.go614
1 files changed, 614 insertions, 0 deletions
diff --git a/Godeps/_workspace/src/github.com/nfnt/resize/resize.go b/Godeps/_workspace/src/github.com/nfnt/resize/resize.go
new file mode 100644
index 000000000..4d4ff6e3e
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/nfnt/resize/resize.go
@@ -0,0 +1,614 @@
+/*
+Copyright (c) 2012, Jan Schlicht <jan.schlicht@gmail.com>
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+*/
+
+// Package resize implements various image resizing methods.
+//
+// The package works with the Image interface described in the image package.
+// Various interpolation methods are provided and multiple processors may be
+// utilized in the computations.
+//
+// Example:
+// imgResized := resize.Resize(1000, 0, imgOld, resize.MitchellNetravali)
+package resize
+
+import (
+ "image"
+ "runtime"
+ "sync"
+)
+
+// An InterpolationFunction provides the parameters that describe an
+// interpolation kernel. It returns the number of samples to take
+// and the kernel function to use for sampling.
+type InterpolationFunction int
+
+// InterpolationFunction constants
+const (
+ // Nearest-neighbor interpolation
+ NearestNeighbor InterpolationFunction = iota
+ // Bilinear interpolation
+ Bilinear
+ // Bicubic interpolation (with cubic hermite spline)
+ Bicubic
+ // Mitchell-Netravali interpolation
+ MitchellNetravali
+ // Lanczos interpolation (a=2)
+ Lanczos2
+ // Lanczos interpolation (a=3)
+ Lanczos3
+)
+
+// kernal, returns an InterpolationFunctions taps and kernel.
+func (i InterpolationFunction) kernel() (int, func(float64) float64) {
+ switch i {
+ case Bilinear:
+ return 2, linear
+ case Bicubic:
+ return 4, cubic
+ case MitchellNetravali:
+ return 4, mitchellnetravali
+ case Lanczos2:
+ return 4, lanczos2
+ case Lanczos3:
+ return 6, lanczos3
+ default:
+ // Default to NearestNeighbor.
+ return 2, nearest
+ }
+}
+
+// values <1 will sharpen the image
+var blur = 1.0
+
+// Resize scales an image to new width and height using the interpolation function interp.
+// A new image with the given dimensions will be returned.
+// If one of the parameters width or height is set to 0, its size will be calculated so that
+// the aspect ratio is that of the originating image.
+// The resizing algorithm uses channels for parallel computation.
+func Resize(width, height uint, img image.Image, interp InterpolationFunction) image.Image {
+ scaleX, scaleY := calcFactors(width, height, float64(img.Bounds().Dx()), float64(img.Bounds().Dy()))
+ if width == 0 {
+ width = uint(0.7 + float64(img.Bounds().Dx())/scaleX)
+ }
+ if height == 0 {
+ height = uint(0.7 + float64(img.Bounds().Dy())/scaleY)
+ }
+
+ // Trivial case: return input image
+ if int(width) == img.Bounds().Dx() && int(height) == img.Bounds().Dy() {
+ return img
+ }
+
+ if interp == NearestNeighbor {
+ return resizeNearest(width, height, scaleX, scaleY, img, interp)
+ }
+
+ taps, kernel := interp.kernel()
+ cpus := runtime.GOMAXPROCS(0)
+ wg := sync.WaitGroup{}
+
+ // Generic access to image.Image is slow in tight loops.
+ // The optimal access has to be determined from the concrete image type.
+ switch input := img.(type) {
+ case *image.RGBA:
+ // 8-bit precision
+ temp := image.NewNRGBA(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
+ result := image.NewNRGBA(image.Rect(0, 0, int(width), int(height)))
+
+ // horizontal filter, results in transposed temporary image
+ coeffs, offset, filterLength := createWeights8(temp.Bounds().Dy(), taps, blur, scaleX, kernel)
+ wg.Add(cpus)
+ for i := 0; i < cpus; i++ {
+ slice := makeSlice(temp, i, cpus).(*image.NRGBA)
+ go func() {
+ defer wg.Done()
+ resizeRGBA(input, slice, scaleX, coeffs, offset, filterLength)
+ }()
+ }
+ wg.Wait()
+
+ // horizontal filter on transposed image, result is not transposed
+ coeffs, offset, filterLength = createWeights8(result.Bounds().Dy(), taps, blur, scaleY, kernel)
+ wg.Add(cpus)
+ for i := 0; i < cpus; i++ {
+ slice := makeSlice(result, i, cpus).(*image.NRGBA)
+ go func() {
+ defer wg.Done()
+ resizeNRGBA(temp, slice, scaleY, coeffs, offset, filterLength)
+ }()
+ }
+ wg.Wait()
+ return result
+ case *image.NRGBA:
+ // 8-bit precision
+ temp := image.NewNRGBA(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
+ result := image.NewNRGBA(image.Rect(0, 0, int(width), int(height)))
+
+ // horizontal filter, results in transposed temporary image
+ coeffs, offset, filterLength := createWeights8(temp.Bounds().Dy(), taps, blur, scaleX, kernel)
+ wg.Add(cpus)
+ for i := 0; i < cpus; i++ {
+ slice := makeSlice(temp, i, cpus).(*image.NRGBA)
+ go func() {
+ defer wg.Done()
+ resizeNRGBA(input, slice, scaleX, coeffs, offset, filterLength)
+ }()
+ }
+ wg.Wait()
+
+ // horizontal filter on transposed image, result is not transposed
+ coeffs, offset, filterLength = createWeights8(result.Bounds().Dy(), taps, blur, scaleY, kernel)
+ wg.Add(cpus)
+ for i := 0; i < cpus; i++ {
+ slice := makeSlice(result, i, cpus).(*image.NRGBA)
+ go func() {
+ defer wg.Done()
+ resizeNRGBA(temp, slice, scaleY, coeffs, offset, filterLength)
+ }()
+ }
+ wg.Wait()
+ return result
+
+ case *image.YCbCr:
+ // 8-bit precision
+ // accessing the YCbCr arrays in a tight loop is slow.
+ // converting the image to ycc increases performance by 2x.
+ temp := newYCC(image.Rect(0, 0, input.Bounds().Dy(), int(width)), input.SubsampleRatio)
+ result := newYCC(image.Rect(0, 0, int(width), int(height)), input.SubsampleRatio)
+
+ coeffs, offset, filterLength := createWeights8(temp.Bounds().Dy(), taps, blur, scaleX, kernel)
+ in := imageYCbCrToYCC(input)
+ wg.Add(cpus)
+ for i := 0; i < cpus; i++ {
+ slice := makeSlice(temp, i, cpus).(*ycc)
+ go func() {
+ defer wg.Done()
+ resizeYCbCr(in, slice, scaleX, coeffs, offset, filterLength)
+ }()
+ }
+ wg.Wait()
+
+ coeffs, offset, filterLength = createWeights8(result.Bounds().Dy(), taps, blur, scaleY, kernel)
+ wg.Add(cpus)
+ for i := 0; i < cpus; i++ {
+ slice := makeSlice(result, i, cpus).(*ycc)
+ go func() {
+ defer wg.Done()
+ resizeYCbCr(temp, slice, scaleY, coeffs, offset, filterLength)
+ }()
+ }
+ wg.Wait()
+ return result.YCbCr()
+ case *image.RGBA64:
+ // 16-bit precision
+ temp := image.NewNRGBA64(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
+ result := image.NewNRGBA64(image.Rect(0, 0, int(width), int(height)))
+
+ // horizontal filter, results in transposed temporary image
+ coeffs, offset, filterLength := createWeights16(temp.Bounds().Dy(), taps, blur, scaleX, kernel)
+ wg.Add(cpus)
+ for i := 0; i < cpus; i++ {
+ slice := makeSlice(temp, i, cpus).(*image.NRGBA64)
+ go func() {
+ defer wg.Done()
+ resizeRGBA64(input, slice, scaleX, coeffs, offset, filterLength)
+ }()
+ }
+ wg.Wait()
+
+ // horizontal filter on transposed image, result is not transposed
+ coeffs, offset, filterLength = createWeights16(result.Bounds().Dy(), taps, blur, scaleY, kernel)
+ wg.Add(cpus)
+ for i := 0; i < cpus; i++ {
+ slice := makeSlice(result, i, cpus).(*image.NRGBA64)
+ go func() {
+ defer wg.Done()
+ resizeNRGBA64(temp, slice, scaleY, coeffs, offset, filterLength)
+ }()
+ }
+ wg.Wait()
+ return result
+ case *image.NRGBA64:
+ // 16-bit precision
+ temp := image.NewNRGBA64(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
+ result := image.NewNRGBA64(image.Rect(0, 0, int(width), int(height)))
+
+ // horizontal filter, results in transposed temporary image
+ coeffs, offset, filterLength := createWeights16(temp.Bounds().Dy(), taps, blur, scaleX, kernel)
+ wg.Add(cpus)
+ for i := 0; i < cpus; i++ {
+ slice := makeSlice(temp, i, cpus).(*image.NRGBA64)
+ go func() {
+ defer wg.Done()
+ resizeNRGBA64(input, slice, scaleX, coeffs, offset, filterLength)
+ }()
+ }
+ wg.Wait()
+
+ // horizontal filter on transposed image, result is not transposed
+ coeffs, offset, filterLength = createWeights16(result.Bounds().Dy(), taps, blur, scaleY, kernel)
+ wg.Add(cpus)
+ for i := 0; i < cpus; i++ {
+ slice := makeSlice(result, i, cpus).(*image.NRGBA64)
+ go func() {
+ defer wg.Done()
+ resizeNRGBA64(temp, slice, scaleY, coeffs, offset, filterLength)
+ }()
+ }
+ wg.Wait()
+ return result
+ case *image.Gray:
+ // 8-bit precision
+ temp := image.NewGray(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
+ result := image.NewGray(image.Rect(0, 0, int(width), int(height)))
+
+ // horizontal filter, results in transposed temporary image
+ coeffs, offset, filterLength := createWeights8(temp.Bounds().Dy(), taps, blur, scaleX, kernel)
+ wg.Add(cpus)
+ for i := 0; i < cpus; i++ {
+ slice := makeSlice(temp, i, cpus).(*image.Gray)
+ go func() {
+ defer wg.Done()
+ resizeGray(input, slice, scaleX, coeffs, offset, filterLength)
+ }()
+ }
+ wg.Wait()
+
+ // horizontal filter on transposed image, result is not transposed
+ coeffs, offset, filterLength = createWeights8(result.Bounds().Dy(), taps, blur, scaleY, kernel)
+ wg.Add(cpus)
+ for i := 0; i < cpus; i++ {
+ slice := makeSlice(result, i, cpus).(*image.Gray)
+ go func() {
+ defer wg.Done()
+ resizeGray(temp, slice, scaleY, coeffs, offset, filterLength)
+ }()
+ }
+ wg.Wait()
+ return result
+ case *image.Gray16:
+ // 16-bit precision
+ temp := image.NewGray16(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
+ result := image.NewGray16(image.Rect(0, 0, int(width), int(height)))
+
+ // horizontal filter, results in transposed temporary image
+ coeffs, offset, filterLength := createWeights16(temp.Bounds().Dy(), taps, blur, scaleX, kernel)
+ wg.Add(cpus)
+ for i := 0; i < cpus; i++ {
+ slice := makeSlice(temp, i, cpus).(*image.Gray16)
+ go func() {
+ defer wg.Done()
+ resizeGray16(input, slice, scaleX, coeffs, offset, filterLength)
+ }()
+ }
+ wg.Wait()
+
+ // horizontal filter on transposed image, result is not transposed
+ coeffs, offset, filterLength = createWeights16(result.Bounds().Dy(), taps, blur, scaleY, kernel)
+ wg.Add(cpus)
+ for i := 0; i < cpus; i++ {
+ slice := makeSlice(result, i, cpus).(*image.Gray16)
+ go func() {
+ defer wg.Done()
+ resizeGray16(temp, slice, scaleY, coeffs, offset, filterLength)
+ }()
+ }
+ wg.Wait()
+ return result
+ default:
+ // 16-bit precision
+ temp := image.NewNRGBA64(image.Rect(0, 0, img.Bounds().Dy(), int(width)))
+ result := image.NewNRGBA64(image.Rect(0, 0, int(width), int(height)))
+
+ // horizontal filter, results in transposed temporary image
+ coeffs, offset, filterLength := createWeights16(temp.Bounds().Dy(), taps, blur, scaleX, kernel)
+ wg.Add(cpus)
+ for i := 0; i < cpus; i++ {
+ slice := makeSlice(temp, i, cpus).(*image.NRGBA64)
+ go func() {
+ defer wg.Done()
+ resizeGeneric(img, slice, scaleX, coeffs, offset, filterLength)
+ }()
+ }
+ wg.Wait()
+
+ // horizontal filter on transposed image, result is not transposed
+ coeffs, offset, filterLength = createWeights16(result.Bounds().Dy(), taps, blur, scaleY, kernel)
+ wg.Add(cpus)
+ for i := 0; i < cpus; i++ {
+ slice := makeSlice(result, i, cpus).(*image.NRGBA64)
+ go func() {
+ defer wg.Done()
+ resizeNRGBA64(temp, slice, scaleY, coeffs, offset, filterLength)
+ }()
+ }
+ wg.Wait()
+ return result
+ }
+}
+
+func resizeNearest(width, height uint, scaleX, scaleY float64, img image.Image, interp InterpolationFunction) image.Image {
+ taps, _ := interp.kernel()
+ cpus := runtime.GOMAXPROCS(0)
+ wg := sync.WaitGroup{}
+
+ switch input := img.(type) {
+ case *image.RGBA:
+ // 8-bit precision
+ temp := image.NewRGBA(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
+ result := image.NewRGBA(image.Rect(0, 0, int(width), int(height)))
+
+ // horizontal filter, results in transposed temporary image
+ coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX)
+ wg.Add(cpus)
+ for i := 0; i < cpus; i++ {
+ slice := makeSlice(temp, i, cpus).(*image.RGBA)
+ go func() {
+ defer wg.Done()
+ nearestRGBA(input, slice, scaleX, coeffs, offset, filterLength)
+ }()
+ }
+ wg.Wait()
+
+ // horizontal filter on transposed image, result is not transposed
+ coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY)
+ wg.Add(cpus)
+ for i := 0; i < cpus; i++ {
+ slice := makeSlice(result, i, cpus).(*image.RGBA)
+ go func() {
+ defer wg.Done()
+ nearestRGBA(temp, slice, scaleY, coeffs, offset, filterLength)
+ }()
+ }
+ wg.Wait()
+ return result
+ case *image.NRGBA:
+ // 8-bit precision
+ temp := image.NewNRGBA(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
+ result := image.NewNRGBA(image.Rect(0, 0, int(width), int(height)))
+
+ // horizontal filter, results in transposed temporary image
+ coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX)
+ wg.Add(cpus)
+ for i := 0; i < cpus; i++ {
+ slice := makeSlice(temp, i, cpus).(*image.NRGBA)
+ go func() {
+ defer wg.Done()
+ nearestNRGBA(input, slice, scaleX, coeffs, offset, filterLength)
+ }()
+ }
+ wg.Wait()
+
+ // horizontal filter on transposed image, result is not transposed
+ coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY)
+ wg.Add(cpus)
+ for i := 0; i < cpus; i++ {
+ slice := makeSlice(result, i, cpus).(*image.NRGBA)
+ go func() {
+ defer wg.Done()
+ nearestNRGBA(temp, slice, scaleY, coeffs, offset, filterLength)
+ }()
+ }
+ wg.Wait()
+ return result
+ case *image.YCbCr:
+ // 8-bit precision
+ // accessing the YCbCr arrays in a tight loop is slow.
+ // converting the image to ycc increases performance by 2x.
+ temp := newYCC(image.Rect(0, 0, input.Bounds().Dy(), int(width)), input.SubsampleRatio)
+ result := newYCC(image.Rect(0, 0, int(width), int(height)), input.SubsampleRatio)
+
+ coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX)
+ in := imageYCbCrToYCC(input)
+ wg.Add(cpus)
+ for i := 0; i < cpus; i++ {
+ slice := makeSlice(temp, i, cpus).(*ycc)
+ go func() {
+ defer wg.Done()
+ nearestYCbCr(in, slice, scaleX, coeffs, offset, filterLength)
+ }()
+ }
+ wg.Wait()
+
+ coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY)
+ wg.Add(cpus)
+ for i := 0; i < cpus; i++ {
+ slice := makeSlice(result, i, cpus).(*ycc)
+ go func() {
+ defer wg.Done()
+ nearestYCbCr(temp, slice, scaleY, coeffs, offset, filterLength)
+ }()
+ }
+ wg.Wait()
+ return result.YCbCr()
+ case *image.RGBA64:
+ // 16-bit precision
+ temp := image.NewRGBA64(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
+ result := image.NewRGBA64(image.Rect(0, 0, int(width), int(height)))
+
+ // horizontal filter, results in transposed temporary image
+ coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX)
+ wg.Add(cpus)
+ for i := 0; i < cpus; i++ {
+ slice := makeSlice(temp, i, cpus).(*image.RGBA64)
+ go func() {
+ defer wg.Done()
+ nearestRGBA64(input, slice, scaleX, coeffs, offset, filterLength)
+ }()
+ }
+ wg.Wait()
+
+ // horizontal filter on transposed image, result is not transposed
+ coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY)
+ wg.Add(cpus)
+ for i := 0; i < cpus; i++ {
+ slice := makeSlice(result, i, cpus).(*image.RGBA64)
+ go func() {
+ defer wg.Done()
+ nearestRGBA64(temp, slice, scaleY, coeffs, offset, filterLength)
+ }()
+ }
+ wg.Wait()
+ return result
+ case *image.NRGBA64:
+ // 16-bit precision
+ temp := image.NewNRGBA64(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
+ result := image.NewNRGBA64(image.Rect(0, 0, int(width), int(height)))
+
+ // horizontal filter, results in transposed temporary image
+ coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX)
+ wg.Add(cpus)
+ for i := 0; i < cpus; i++ {
+ slice := makeSlice(temp, i, cpus).(*image.NRGBA64)
+ go func() {
+ defer wg.Done()
+ nearestNRGBA64(input, slice, scaleX, coeffs, offset, filterLength)
+ }()
+ }
+ wg.Wait()
+
+ // horizontal filter on transposed image, result is not transposed
+ coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY)
+ wg.Add(cpus)
+ for i := 0; i < cpus; i++ {
+ slice := makeSlice(result, i, cpus).(*image.NRGBA64)
+ go func() {
+ defer wg.Done()
+ nearestNRGBA64(temp, slice, scaleY, coeffs, offset, filterLength)
+ }()
+ }
+ wg.Wait()
+ return result
+ case *image.Gray:
+ // 8-bit precision
+ temp := image.NewGray(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
+ result := image.NewGray(image.Rect(0, 0, int(width), int(height)))
+
+ // horizontal filter, results in transposed temporary image
+ coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX)
+ wg.Add(cpus)
+ for i := 0; i < cpus; i++ {
+ slice := makeSlice(temp, i, cpus).(*image.Gray)
+ go func() {
+ defer wg.Done()
+ nearestGray(input, slice, scaleX, coeffs, offset, filterLength)
+ }()
+ }
+ wg.Wait()
+
+ // horizontal filter on transposed image, result is not transposed
+ coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY)
+ wg.Add(cpus)
+ for i := 0; i < cpus; i++ {
+ slice := makeSlice(result, i, cpus).(*image.Gray)
+ go func() {
+ defer wg.Done()
+ nearestGray(temp, slice, scaleY, coeffs, offset, filterLength)
+ }()
+ }
+ wg.Wait()
+ return result
+ case *image.Gray16:
+ // 16-bit precision
+ temp := image.NewGray16(image.Rect(0, 0, input.Bounds().Dy(), int(width)))
+ result := image.NewGray16(image.Rect(0, 0, int(width), int(height)))
+
+ // horizontal filter, results in transposed temporary image
+ coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX)
+ wg.Add(cpus)
+ for i := 0; i < cpus; i++ {
+ slice := makeSlice(temp, i, cpus).(*image.Gray16)
+ go func() {
+ defer wg.Done()
+ nearestGray16(input, slice, scaleX, coeffs, offset, filterLength)
+ }()
+ }
+ wg.Wait()
+
+ // horizontal filter on transposed image, result is not transposed
+ coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY)
+ wg.Add(cpus)
+ for i := 0; i < cpus; i++ {
+ slice := makeSlice(result, i, cpus).(*image.Gray16)
+ go func() {
+ defer wg.Done()
+ nearestGray16(temp, slice, scaleY, coeffs, offset, filterLength)
+ }()
+ }
+ wg.Wait()
+ return result
+ default:
+ // 16-bit precision
+ temp := image.NewRGBA64(image.Rect(0, 0, img.Bounds().Dy(), int(width)))
+ result := image.NewRGBA64(image.Rect(0, 0, int(width), int(height)))
+
+ // horizontal filter, results in transposed temporary image
+ coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX)
+ wg.Add(cpus)
+ for i := 0; i < cpus; i++ {
+ slice := makeSlice(temp, i, cpus).(*image.RGBA64)
+ go func() {
+ defer wg.Done()
+ nearestGeneric(img, slice, scaleX, coeffs, offset, filterLength)
+ }()
+ }
+ wg.Wait()
+
+ // horizontal filter on transposed image, result is not transposed
+ coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), taps, blur, scaleY)
+ wg.Add(cpus)
+ for i := 0; i < cpus; i++ {
+ slice := makeSlice(result, i, cpus).(*image.RGBA64)
+ go func() {
+ defer wg.Done()
+ nearestRGBA64(temp, slice, scaleY, coeffs, offset, filterLength)
+ }()
+ }
+ wg.Wait()
+ return result
+ }
+
+}
+
+// Calculates scaling factors using old and new image dimensions.
+func calcFactors(width, height uint, oldWidth, oldHeight float64) (scaleX, scaleY float64) {
+ if width == 0 {
+ if height == 0 {
+ scaleX = 1.0
+ scaleY = 1.0
+ } else {
+ scaleY = oldHeight / float64(height)
+ scaleX = scaleY
+ }
+ } else {
+ scaleX = oldWidth / float64(width)
+ if height == 0 {
+ scaleY = scaleX
+ } else {
+ scaleY = oldHeight / float64(height)
+ }
+ }
+ return
+}
+
+type imageWithSubImage interface {
+ image.Image
+ SubImage(image.Rectangle) image.Image
+}
+
+func makeSlice(img imageWithSubImage, i, n int) image.Image {
+ return img.SubImage(image.Rect(img.Bounds().Min.X, img.Bounds().Min.Y+i*img.Bounds().Dy()/n, img.Bounds().Max.X, img.Bounds().Min.Y+(i+1)*img.Bounds().Dy()/n))
+}