mirror of https://github.com/gogits/gogs.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
614 lines
20 KiB
614 lines
20 KiB
/* |
|
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.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 := createWeights8(temp.Bounds().Dy(), 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, 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.RGBA) |
|
go func() { |
|
defer wg.Done() |
|
resizeRGBA(temp, slice, scaleY, coeffs, offset, filterLength) |
|
}() |
|
} |
|
wg.Wait() |
|
return result |
|
case *image.NRGBA: |
|
// 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 := createWeights8(temp.Bounds().Dy(), 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() |
|
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.RGBA) |
|
go func() { |
|
defer wg.Done() |
|
resizeRGBA(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)), image.YCbCrSubsampleRatio444) |
|
|
|
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.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(), 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(), 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 |
|
case *image.NRGBA64: |
|
// 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(), 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() |
|
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.RGBA64) |
|
go func() { |
|
defer wg.Done() |
|
resizeRGBA64(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.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(), 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(), 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.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)), image.YCbCrSubsampleRatio444) |
|
|
|
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)) |
|
}
|
|
|