|
|
@@ -33,36 +33,41 @@ import (
|
|
|
// 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 func() (int, func(float64) float64)
|
|
|
+type InterpolationFunction int
|
|
|
|
|
|
-// Nearest-neighbor interpolation
|
|
|
-func NearestNeighbor() (int, func(float64) float64) {
|
|
|
- return 2, nearest
|
|
|
-}
|
|
|
-
|
|
|
-// Bilinear interpolation
|
|
|
-func Bilinear() (int, func(float64) float64) {
|
|
|
- return 2, linear
|
|
|
-}
|
|
|
-
|
|
|
-// Bicubic interpolation (with cubic hermite spline)
|
|
|
-func Bicubic() (int, func(float64) float64) {
|
|
|
- return 4, cubic
|
|
|
-}
|
|
|
-
|
|
|
-// Mitchell-Netravali interpolation
|
|
|
-func MitchellNetravali() (int, func(float64) float64) {
|
|
|
- return 4, mitchellnetravali
|
|
|
-}
|
|
|
-
|
|
|
-// Lanczos interpolation (a=2)
|
|
|
-func Lanczos2() (int, func(float64) float64) {
|
|
|
- return 4, lanczos2
|
|
|
-}
|
|
|
+// 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
|
|
|
+)
|
|
|
|
|
|
-// Lanczos interpolation (a=3)
|
|
|
-func Lanczos3() (int, func(float64) float64) {
|
|
|
- return 6, 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
|
|
|
@@ -81,8 +86,11 @@ func Resize(width, height uint, img image.Image, interp InterpolationFunction) i
|
|
|
if height == 0 {
|
|
|
height = uint(0.7 + float64(img.Bounds().Dy())/scaleY)
|
|
|
}
|
|
|
+ if interp == NearestNeighbor {
|
|
|
+ return resizeNearest(width, height, scaleX, scaleY, img, interp)
|
|
|
+ }
|
|
|
|
|
|
- taps, kernel := interp()
|
|
|
+ taps, kernel := interp.kernel()
|
|
|
cpus := runtime.NumCPU()
|
|
|
wg := sync.WaitGroup{}
|
|
|
|
|
|
@@ -95,25 +103,25 @@ func Resize(width, height uint, img image.Image, interp InterpolationFunction) i
|
|
|
result := image.NewRGBA(image.Rect(0, 0, int(width), int(height)))
|
|
|
|
|
|
// horizontal filter, results in transposed temporary image
|
|
|
- coeffs, filterLength := createWeights8(temp.Bounds().Dy(), input.Bounds().Min.X, taps, blur, scaleX, kernel)
|
|
|
+ coeffs, offset, filterLength := createWeights8(temp.Bounds().Dy(), input.Bounds().Min.X, taps, blur, scaleX, kernel)
|
|
|
wg.Add(cpus)
|
|
|
for i := 0; i < cpus; i++ {
|
|
|
slice := makeSlice(temp, i, cpus).(*image.RGBA)
|
|
|
go func() {
|
|
|
defer wg.Done()
|
|
|
- resizeRGBA(input, slice, scaleX, coeffs, filterLength)
|
|
|
+ resizeRGBA(input, slice, scaleX, coeffs, offset, filterLength)
|
|
|
}()
|
|
|
}
|
|
|
wg.Wait()
|
|
|
|
|
|
// horizontal filter on transposed image, result is not transposed
|
|
|
- coeffs, filterLength = createWeights8(result.Bounds().Dy(), temp.Bounds().Min.X, taps, blur, scaleY, kernel)
|
|
|
+ coeffs, offset, filterLength = createWeights8(result.Bounds().Dy(), temp.Bounds().Min.X, taps, blur, scaleY, kernel)
|
|
|
wg.Add(cpus)
|
|
|
for i := 0; i < cpus; i++ {
|
|
|
slice := makeSlice(result, i, cpus).(*image.RGBA)
|
|
|
go func() {
|
|
|
defer wg.Done()
|
|
|
- resizeRGBA(temp, slice, scaleY, coeffs, filterLength)
|
|
|
+ resizeRGBA(temp, slice, scaleY, coeffs, offset, filterLength)
|
|
|
}()
|
|
|
}
|
|
|
wg.Wait()
|
|
|
@@ -121,60 +129,242 @@ func Resize(width, height uint, img image.Image, interp InterpolationFunction) i
|
|
|
case *image.YCbCr:
|
|
|
// 8-bit precision
|
|
|
// accessing the YCbCr arrays in a tight loop is slow.
|
|
|
- // converting the image before filtering will improve performance.
|
|
|
- inputAsRGBA := convertYCbCrToRGBA(input)
|
|
|
+ // 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(), input.Bounds().Min.X, 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(), temp.Bounds().Min.X, 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.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 := createWeights16(temp.Bounds().Dy(), input.Bounds().Min.X, taps, blur, scaleX, kernel)
|
|
|
+ wg.Add(cpus)
|
|
|
+ for i := 0; i < cpus; i++ {
|
|
|
+ slice := makeSlice(temp, i, cpus).(*image.RGBA64)
|
|
|
+ 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(), temp.Bounds().Min.X, taps, blur, scaleY, kernel)
|
|
|
+ wg.Add(cpus)
|
|
|
+ for i := 0; i < cpus; i++ {
|
|
|
+ slice := makeSlice(result, i, cpus).(*image.RGBA64)
|
|
|
+ go func() {
|
|
|
+ defer wg.Done()
|
|
|
+ resizeGeneric(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(), input.Bounds().Min.X, 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(), temp.Bounds().Min.X, 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(), input.Bounds().Min.X, 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(), temp.Bounds().Min.X, 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.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 := createWeights16(temp.Bounds().Dy(), img.Bounds().Min.X, taps, blur, scaleX, kernel)
|
|
|
+ wg.Add(cpus)
|
|
|
+ for i := 0; i < cpus; i++ {
|
|
|
+ slice := makeSlice(temp, i, cpus).(*image.RGBA64)
|
|
|
+ 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(), temp.Bounds().Min.X, taps, blur, scaleY, kernel)
|
|
|
+ wg.Add(cpus)
|
|
|
+ for i := 0; i < cpus; i++ {
|
|
|
+ slice := makeSlice(result, i, cpus).(*image.RGBA64)
|
|
|
+ go func() {
|
|
|
+ defer wg.Done()
|
|
|
+ resizeRGBA64(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.NumCPU()
|
|
|
+ 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, filterLength := createWeights8(temp.Bounds().Dy(), input.Bounds().Min.X, taps, blur, scaleX, kernel)
|
|
|
+ coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), input.Bounds().Min.X, taps, blur, scaleX)
|
|
|
wg.Add(cpus)
|
|
|
for i := 0; i < cpus; i++ {
|
|
|
slice := makeSlice(temp, i, cpus).(*image.RGBA)
|
|
|
go func() {
|
|
|
defer wg.Done()
|
|
|
- resizeRGBA(inputAsRGBA, slice, scaleX, coeffs, filterLength)
|
|
|
+ nearestRGBA(input, slice, scaleX, coeffs, offset, filterLength)
|
|
|
}()
|
|
|
}
|
|
|
wg.Wait()
|
|
|
|
|
|
// horizontal filter on transposed image, result is not transposed
|
|
|
- coeffs, filterLength = createWeights8(result.Bounds().Dy(), temp.Bounds().Min.X, taps, blur, scaleY, kernel)
|
|
|
+ coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), temp.Bounds().Min.X, taps, blur, scaleY)
|
|
|
wg.Add(cpus)
|
|
|
for i := 0; i < cpus; i++ {
|
|
|
slice := makeSlice(result, i, cpus).(*image.RGBA)
|
|
|
go func() {
|
|
|
defer wg.Done()
|
|
|
- resizeRGBA(temp, slice, scaleY, coeffs, filterLength)
|
|
|
+ nearestRGBA(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(), input.Bounds().Min.X, 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(), temp.Bounds().Min.X, 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, filterLength := createWeights16(temp.Bounds().Dy(), input.Bounds().Min.X, taps, blur, scaleX, kernel)
|
|
|
+ coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), input.Bounds().Min.X, taps, blur, scaleX)
|
|
|
wg.Add(cpus)
|
|
|
for i := 0; i < cpus; i++ {
|
|
|
slice := makeSlice(temp, i, cpus).(*image.RGBA64)
|
|
|
go func() {
|
|
|
defer wg.Done()
|
|
|
- resizeRGBA64(input, slice, scaleX, coeffs, filterLength)
|
|
|
+ nearestRGBA64(input, slice, scaleX, coeffs, offset, filterLength)
|
|
|
}()
|
|
|
}
|
|
|
wg.Wait()
|
|
|
|
|
|
// horizontal filter on transposed image, result is not transposed
|
|
|
- coeffs, filterLength = createWeights16(result.Bounds().Dy(), temp.Bounds().Min.X, taps, blur, scaleY, kernel)
|
|
|
+ coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), temp.Bounds().Min.X, taps, blur, scaleY)
|
|
|
wg.Add(cpus)
|
|
|
for i := 0; i < cpus; i++ {
|
|
|
slice := makeSlice(result, i, cpus).(*image.RGBA64)
|
|
|
go func() {
|
|
|
defer wg.Done()
|
|
|
- resizeGeneric(temp, slice, scaleY, coeffs, filterLength)
|
|
|
+ nearestGeneric(temp, slice, scaleY, coeffs, offset, filterLength)
|
|
|
}()
|
|
|
}
|
|
|
wg.Wait()
|
|
|
@@ -185,25 +375,25 @@ func Resize(width, height uint, img image.Image, interp InterpolationFunction) i
|
|
|
result := image.NewGray(image.Rect(0, 0, int(width), int(height)))
|
|
|
|
|
|
// horizontal filter, results in transposed temporary image
|
|
|
- coeffs, filterLength := createWeights8(temp.Bounds().Dy(), input.Bounds().Min.X, taps, blur, scaleX, kernel)
|
|
|
+ coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), input.Bounds().Min.X, taps, blur, scaleX)
|
|
|
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, filterLength)
|
|
|
+ nearestGray(input, slice, scaleX, coeffs, offset, filterLength)
|
|
|
}()
|
|
|
}
|
|
|
wg.Wait()
|
|
|
|
|
|
// horizontal filter on transposed image, result is not transposed
|
|
|
- coeffs, filterLength = createWeights8(result.Bounds().Dy(), temp.Bounds().Min.X, taps, blur, scaleY, kernel)
|
|
|
+ coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), temp.Bounds().Min.X, taps, blur, scaleY)
|
|
|
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, filterLength)
|
|
|
+ nearestGray(temp, slice, scaleY, coeffs, offset, filterLength)
|
|
|
}()
|
|
|
}
|
|
|
wg.Wait()
|
|
|
@@ -214,25 +404,25 @@ func Resize(width, height uint, img image.Image, interp InterpolationFunction) i
|
|
|
result := image.NewGray16(image.Rect(0, 0, int(width), int(height)))
|
|
|
|
|
|
// horizontal filter, results in transposed temporary image
|
|
|
- coeffs, filterLength := createWeights16(temp.Bounds().Dy(), input.Bounds().Min.X, taps, blur, scaleX, kernel)
|
|
|
+ coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), input.Bounds().Min.X, taps, blur, scaleX)
|
|
|
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, filterLength)
|
|
|
+ nearestGray16(input, slice, scaleX, coeffs, offset, filterLength)
|
|
|
}()
|
|
|
}
|
|
|
wg.Wait()
|
|
|
|
|
|
// horizontal filter on transposed image, result is not transposed
|
|
|
- coeffs, filterLength = createWeights16(result.Bounds().Dy(), temp.Bounds().Min.X, taps, blur, scaleY, kernel)
|
|
|
+ coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), temp.Bounds().Min.X, taps, blur, scaleY)
|
|
|
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, filterLength)
|
|
|
+ nearestGray16(temp, slice, scaleY, coeffs, offset, filterLength)
|
|
|
}()
|
|
|
}
|
|
|
wg.Wait()
|
|
|
@@ -243,30 +433,31 @@ func Resize(width, height uint, img image.Image, interp InterpolationFunction) i
|
|
|
result := image.NewRGBA64(image.Rect(0, 0, int(width), int(height)))
|
|
|
|
|
|
// horizontal filter, results in transposed temporary image
|
|
|
- coeffs, filterLength := createWeights16(temp.Bounds().Dy(), img.Bounds().Min.X, taps, blur, scaleX, kernel)
|
|
|
+ coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), img.Bounds().Min.X, taps, blur, scaleX)
|
|
|
wg.Add(cpus)
|
|
|
for i := 0; i < cpus; i++ {
|
|
|
slice := makeSlice(temp, i, cpus).(*image.RGBA64)
|
|
|
go func() {
|
|
|
defer wg.Done()
|
|
|
- resizeGeneric(img, slice, scaleX, coeffs, filterLength)
|
|
|
+ nearestGeneric(img, slice, scaleX, coeffs, offset, filterLength)
|
|
|
}()
|
|
|
}
|
|
|
wg.Wait()
|
|
|
|
|
|
// horizontal filter on transposed image, result is not transposed
|
|
|
- coeffs, filterLength = createWeights16(result.Bounds().Dy(), temp.Bounds().Min.X, taps, blur, scaleY, kernel)
|
|
|
+ coeffs, offset, filterLength = createWeightsNearest(result.Bounds().Dy(), temp.Bounds().Min.X, taps, blur, scaleY)
|
|
|
wg.Add(cpus)
|
|
|
for i := 0; i < cpus; i++ {
|
|
|
slice := makeSlice(result, i, cpus).(*image.RGBA64)
|
|
|
go func() {
|
|
|
defer wg.Done()
|
|
|
- resizeRGBA64(temp, slice, scaleY, coeffs, filterLength)
|
|
|
+ nearestRGBA64(temp, slice, scaleY, coeffs, offset, filterLength)
|
|
|
}()
|
|
|
}
|
|
|
wg.Wait()
|
|
|
return result
|
|
|
}
|
|
|
+
|
|
|
}
|
|
|
|
|
|
// Calculates scaling factors using old and new image dimensions.
|