diff --git a/draw/gen.go b/draw/gen.go index 1acae2e..6efdafa 100644 --- a/draw/gen.go +++ b/draw/gen.go @@ -936,8 +936,14 @@ const ( // Create a temporary buffer: // scaleX distributes the source image's columns over the temporary image. // scaleY distributes the temporary image's rows over the destination image. - // TODO: is it worth having a sync.Pool for this temporary buffer? - tmp := make([][4]float64, z.dw*z.sh) + var tmp [][4]float64 + if z.pool.New != nil { + tmpp := z.pool.Get().(*[][4]float64) + defer z.pool.Put(tmpp) + tmp = *tmpp + } else { + tmp = z.makeTmpBuf() + } // sr is the source pixels. If it extends beyond the src bounds, // we cannot use the type-specific fast paths, as they access diff --git a/draw/impl.go b/draw/impl.go index 6278c92..a1de1cd 100644 --- a/draw/impl.go +++ b/draw/impl.go @@ -3036,8 +3036,14 @@ func (z *kernelScaler) Scale(dst Image, dr image.Rectangle, src image.Image, sr // Create a temporary buffer: // scaleX distributes the source image's columns over the temporary image. // scaleY distributes the temporary image's rows over the destination image. - // TODO: is it worth having a sync.Pool for this temporary buffer? - tmp := make([][4]float64, z.dw*z.sh) + var tmp [][4]float64 + if z.pool.New != nil { + tmpp := z.pool.Get().(*[][4]float64) + defer z.pool.Put(tmpp) + tmp = *tmpp + } else { + tmp = z.makeTmpBuf() + } // sr is the source pixels. If it extends beyond the src bounds, // we cannot use the type-specific fast paths, as they access diff --git a/draw/scale.go b/draw/scale.go index 1e95971..2e481b1 100644 --- a/draw/scale.go +++ b/draw/scale.go @@ -10,6 +10,7 @@ import ( "image" "image/color" "math" + "sync" "golang.org/x/image/math/f64" ) @@ -88,13 +89,17 @@ type Kernel struct { // Scale implements the Scaler interface. func (q *Kernel) Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle, opts *Options) { - q.NewScaler(dr.Dx(), dr.Dy(), sr.Dx(), sr.Dy()).Scale(dst, dr, src, sr, opts) + q.newScaler(dr.Dx(), dr.Dy(), sr.Dx(), sr.Dy(), false).Scale(dst, dr, src, sr, opts) } // NewScaler returns a Scaler that is optimized for scaling multiple times with // the same fixed destination and source width and height. func (q *Kernel) NewScaler(dw, dh, sw, sh int) Scaler { - return &kernelScaler{ + return q.newScaler(dw, dh, sw, sh, true) +} + +func (q *Kernel) newScaler(dw, dh, sw, sh int, usePool bool) Scaler { + z := &kernelScaler{ kernel: q, dw: int32(dw), dh: int32(dh), @@ -103,6 +108,13 @@ func (q *Kernel) NewScaler(dw, dh, sw, sh int) Scaler { horizontal: newDistrib(q, int32(dw), int32(sw)), vertical: newDistrib(q, int32(dh), int32(sh)), } + if usePool { + z.pool.New = func() interface{} { + tmp := z.makeTmpBuf() + return &tmp + } + } + return z } var ( @@ -152,6 +164,11 @@ type kernelScaler struct { kernel *Kernel dw, dh, sw, sh int32 horizontal, vertical distrib + pool sync.Pool +} + +func (z *kernelScaler) makeTmpBuf() [][4]float64 { + return make([][4]float64, z.dw*z.sh) } // source is a range of contribs, their inverse total weight, and that ITW diff --git a/draw/scale_test.go b/draw/scale_test.go index bad9c5f..9cce5b1 100644 --- a/draw/scale_test.go +++ b/draw/scale_test.go @@ -393,6 +393,7 @@ func benchScale(b *testing.B, srcf func(image.Rectangle) (image.Image, error), w scaler = n.NewScaler(dr.Dx(), dr.Dy(), sr.Dx(), sr.Dy()) } + b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { scaler.Scale(dst, dr, src, sr, nil) @@ -408,6 +409,7 @@ func benchTform(b *testing.B, srcf func(image.Rectangle) (image.Image, error), w sr := src.Bounds() m := transformMatrix(40, 10) + b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { q.Transform(dst, m, src, sr, nil)