123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615 |
- /*
- 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))
- }
|