diff --git a/draw/gen.go b/draw/gen.go index a16bf84..bc11b3e 100644 --- a/draw/gen.go +++ b/draw/gen.go @@ -24,10 +24,15 @@ func main() { w := new(bytes.Buffer) w.WriteString("// generated by \"go run gen.go\". DO NOT EDIT.\n\n" + - "package draw\n\nimport (\n\"image\"\n\"image/color\"\n)\n") + "package draw\n\nimport (\n" + + "\"image\"\n" + + "\"image/color\"\n" + + "\n" + + "\"golang.org/x/image/math/f64\"\n" + + ")\n") - gen(w, "nnInterpolator", codeNNLeaf) - gen(w, "ablInterpolator", codeABLLeaf) + gen(w, "nnInterpolator", codeNNScaleLeaf) + gen(w, "ablInterpolator", codeABLScaleLeaf) genKernel(w) if *debug { @@ -99,12 +104,12 @@ func gen(w *bytes.Buffer, receiver string, code string) { func genKernel(w *bytes.Buffer) { expn(w, codeKernelRoot, &data{}) for _, sType := range sTypes { - expn(w, codeKernelLeafX, &data{ + expn(w, codeKernelScaleLeafX, &data{ sType: sType, }) } for _, dType := range dTypes { - expn(w, codeKernelLeafY, &data{ + expn(w, codeKernelScaleLeafY, &data{ dType: dType, }) } @@ -407,7 +412,7 @@ func relName(s string) string { const ( codeRoot = ` - func (z $receiver) Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle) { + func (z $receiver) Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle, opts *Options) { // adr is the affected destination pixels, relative to dr.Min. adr := dst.Bounds().Intersect(dr).Sub(dr.Min) if adr.Empty() || sr.Empty() { @@ -422,9 +427,13 @@ const ( $switch z.scale_$dTypeRN_$sTypeRN(dst, dr, adr, src, sr) } } + + func (z $receiver) Transform(dst Image, m *f64.Aff3, src image.Image, sr image.Rectangle, opts *Options) { + panic("unimplemented") + } ` - codeNNLeaf = ` + codeNNScaleLeaf = ` func (nnInterpolator) scale_$dTypeRN_$sTypeRN(dst $dType, dr, adr image.Rectangle, src $sType, sr image.Rectangle) { dw2 := uint64(dr.Dx()) * 2 dh2 := uint64(dr.Dy()) * 2 @@ -443,7 +452,7 @@ const ( } ` - codeABLLeaf = ` + codeABLScaleLeaf = ` func (ablInterpolator) scale_$dTypeRN_$sTypeRN(dst $dType, dr, adr image.Rectangle, src $sType, sr image.Rectangle) { sw := int32(sr.Dx()) sh := int32(sr.Dy()) @@ -491,9 +500,9 @@ const ( ` codeKernelRoot = ` - func (z *kernelScaler) Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle) { + func (z *kernelScaler) Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle, opts *Options) { if z.dw != int32(dr.Dx()) || z.dh != int32(dr.Dy()) || z.sw != int32(sr.Dx()) || z.sh != int32(sr.Dy()) { - z.kernel.Scale(dst, dr, src, sr) + z.kernel.Scale(dst, dr, src, sr, opts) return } // adr is the affected destination pixels, relative to dr.Min. @@ -518,9 +527,13 @@ const ( $switchD z.scaleY_$dTypeRN(dst, dr, adr, tmp) } + + func (z *Kernel) Transform(dst Image, m *f64.Aff3, src image.Image, sr image.Rectangle, opts *Options) { + panic("unimplemented") + } ` - codeKernelLeafX = ` + codeKernelScaleLeafX = ` func (z *kernelScaler) scaleX_$sTypeRN(tmp [][4]float64, src $sType, sr image.Rectangle) { t := 0 for y := int32(0); y < z.sh; y++ { @@ -541,7 +554,7 @@ const ( } ` - codeKernelLeafY = ` + codeKernelScaleLeafY = ` func (z *kernelScaler) scaleY_$dTypeRN(dst $dType, dr, adr image.Rectangle, tmp [][4]float64) { $preOuter for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx++ { diff --git a/draw/impl.go b/draw/impl.go index 7a0a5bb..ff9f988 100644 --- a/draw/impl.go +++ b/draw/impl.go @@ -5,9 +5,11 @@ package draw import ( "image" "image/color" + + "golang.org/x/image/math/f64" ) -func (z nnInterpolator) Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle) { +func (z nnInterpolator) Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle, opts *Options) { // adr is the affected destination pixels, relative to dr.Min. adr := dst.Bounds().Intersect(dr).Sub(dr.Min) if adr.Empty() || sr.Empty() { @@ -44,6 +46,10 @@ func (z nnInterpolator) Scale(dst Image, dr image.Rectangle, src image.Image, sr } } +func (z nnInterpolator) Transform(dst Image, m *f64.Aff3, src image.Image, sr image.Rectangle, opts *Options) { + panic("unimplemented") +} + func (nnInterpolator) scale_RGBA_Gray(dst *image.RGBA, dr, adr image.Rectangle, src *image.Gray, sr image.Rectangle) { dw2 := uint64(dr.Dx()) * 2 dh2 := uint64(dr.Dy()) * 2 @@ -189,7 +195,7 @@ func (nnInterpolator) scale_Image_Image(dst Image, dr, adr image.Rectangle, src } } -func (z ablInterpolator) Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle) { +func (z ablInterpolator) Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle, opts *Options) { // adr is the affected destination pixels, relative to dr.Min. adr := dst.Bounds().Intersect(dr).Sub(dr.Min) if adr.Empty() || sr.Empty() { @@ -226,6 +232,10 @@ func (z ablInterpolator) Scale(dst Image, dr image.Rectangle, src image.Image, s } } +func (z ablInterpolator) Transform(dst Image, m *f64.Aff3, src image.Image, sr image.Rectangle, opts *Options) { + panic("unimplemented") +} + func (ablInterpolator) scale_RGBA_Gray(dst *image.RGBA, dr, adr image.Rectangle, src *image.Gray, sr image.Rectangle) { sw := int32(sr.Dx()) sh := int32(sr.Dy()) @@ -754,9 +764,9 @@ func (ablInterpolator) scale_Image_Image(dst Image, dr, adr image.Rectangle, src } } -func (z *kernelScaler) Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle) { +func (z *kernelScaler) Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle, opts *Options) { if z.dw != int32(dr.Dx()) || z.dh != int32(dr.Dy()) || z.sw != int32(sr.Dx()) || z.sh != int32(sr.Dy()) { - z.kernel.Scale(dst, dr, src, sr) + z.kernel.Scale(dst, dr, src, sr, opts) return } // adr is the affected destination pixels, relative to dr.Min. @@ -800,6 +810,10 @@ func (z *kernelScaler) Scale(dst Image, dr image.Rectangle, src image.Image, sr } } +func (z *Kernel) Transform(dst Image, m *f64.Aff3, src image.Image, sr image.Rectangle, opts *Options) { + panic("unimplemented") +} + func (z *kernelScaler) scaleX_Gray(tmp [][4]float64, src *image.Gray, sr image.Rectangle) { t := 0 for y := int32(0); y < z.sh; y++ { diff --git a/draw/scale.go b/draw/scale.go index a603d80..10a3442 100644 --- a/draw/scale.go +++ b/draw/scale.go @@ -6,12 +6,11 @@ package draw -// TODO: add an Options type a la -// https://groups.google.com/forum/#!topic/golang-dev/fgn_xM0aeq4 - import ( "image" "math" + + "golang.org/x/image/math/f64" ) // Scaler scales the part of the source image defined by src and sr and writes @@ -19,7 +18,32 @@ import ( // // A Scaler is safe to use concurrently. type Scaler interface { - Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle) + Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle, opts *Options) +} + +// Transformer transforms the part of the source image defined by src and sr +// and writes to the part of the destination image defined by dst and the +// affine transform m applied to sr. +// +// For example, if m is the matrix +// +// m00 m01 m02 +// m10 m11 m12 +// +// then the src-space point (sx, sy) maps to the dst-space point +// (m00*sx + m01*sy + m02, m10*sx + m11*sy + m12). +// +// A Transformer is safe to use concurrently. +type Transformer interface { + Transform(dst Image, m *f64.Aff3, src image.Image, sr image.Rectangle, opts *Options) +} + +// Options are optional parameters to Scale and Transform. +// +// A nil *Options means to use the default (zero) values of each field. +type Options struct { + // TODO: add fields a la + // https://groups.google.com/forum/#!topic/golang-dev/fgn_xM0aeq4 } // Interpolator is an interpolation algorithm, when dst and src pixels don't @@ -35,7 +59,7 @@ type Scaler interface { // non-kernel interpolators, especially when scaling down. type Interpolator interface { Scaler - // TODO: Transformer + Transformer } // Kernel is an interpolator that blends source pixels weighted by a symmetric @@ -50,8 +74,8 @@ type Kernel struct { } // Scale implements the Scaler interface. -func (k *Kernel) Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle) { - k.NewScaler(dr.Dx(), dr.Dy(), sr.Dx(), sr.Dy()).Scale(dst, dr, src, sr) +func (k *Kernel) Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle, opts *Options) { + k.NewScaler(dr.Dx(), dr.Dy(), sr.Dx(), sr.Dy()).Scale(dst, dr, src, sr, opts) } // NewScaler returns a Scaler that is optimized for scaling multiple times with diff --git a/draw/scale_test.go b/draw/scale_test.go index 8bc5a4b..ef5192e 100644 --- a/draw/scale_test.go +++ b/draw/scale_test.go @@ -47,7 +47,7 @@ func testScale(t *testing.T, w int, h int, direction, srcFilename string) { gotFilename := fmt.Sprintf("../testdata/go-turns-two-%s-%s.png", direction, name) got := image.NewRGBA(image.Rect(0, 0, w, h)) - q.Scale(got, got.Bounds(), src, src.Bounds()) + q.Scale(got, got.Bounds(), src, src.Bounds(), nil) if *genScaleFiles { g, err := os.Create(gotFilename) if err != nil { @@ -112,12 +112,12 @@ func TestScaleClipCommute(t *testing.T) { } // Scale then clip. - q.Scale(dst0, outer, src, src.Bounds()) + q.Scale(dst0, outer, src, src.Bounds(), nil) dst0 = dst0.SubImage(inner).(*image.RGBA) // Clip then scale. dst1 = dst1.SubImage(inner).(*image.RGBA) - q.Scale(dst1, outer, src, src.Bounds()) + q.Scale(dst1, outer, src, src.Bounds(), nil) loop: for y := inner.Min.Y; y < inner.Max.Y; y++ { @@ -187,8 +187,8 @@ func TestFastPaths(t *testing.T) { dst1 := image.NewRGBA(drs[0]) Draw(dst0, dst0.Bounds(), blue, image.Point{}, Src) Draw(dstWrapper{dst1}, dst1.Bounds(), srcWrapper{blue}, image.Point{}, Src) - q.Scale(dst0, dr, src, sr) - q.Scale(dstWrapper{dst1}, dr, srcWrapper{src}, sr) + q.Scale(dst0, dr, src, sr, nil) + q.Scale(dstWrapper{dst1}, dr, srcWrapper{src}, sr, nil) if !bytes.Equal(dst0.Pix, dst1.Pix) { t.Errorf("pix differ for dr=%v, src=%T, sr=%v, q=%T", dr, src, sr, q) } @@ -269,7 +269,7 @@ func benchScale(b *testing.B, srcf func(image.Rectangle) (image.Image, error), w b.ResetTimer() for i := 0; i < b.N; i++ { - scaler.Scale(dst, dr, src, sr) + scaler.Scale(dst, dr, src, sr, nil) } }