git.lucas.co / go_mono
git clone https://git.lucas.co/go_mono.git

commit08593990c4a6f1b9eb49ee955d6c2c3fbf4de1ef
parent7bd522e167
authorNigel Tao <[email protected]>
date2015-03-05 15:46
draw: add Transformer and Option types.

Just stub implementations for now. Actual implementations will be
follow-up changes.

Change-Id: Id21d9042a2073c2dc0f78c9977c4940f000a41df
Reviewed-on: https://go-review.googlesource.com/6805
Reviewed-by: Rob Pike <[email protected]>

 draw/gen.go        | 39 ++++++++++++++++++++++++++-------------
 draw/impl.go       | 22 ++++++++++++++++++----
 draw/scale.go      | 38 +++++++++++++++++++++++++++++++-------
 draw/scale_test.go | 12 ++++++------
 4 files changed, 81 insertions(+), 30 deletions(-)

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")
-
-	gen(w, "nnInterpolator", codeNNLeaf)
-	gen(w, "ablInterpolator", codeABLLeaf)
+		"package draw\n\nimport (\n" +
+		"\"image\"\n" +
+		"\"image/color\"\n" +
+		"\n" +
+		"\"golang.org/x/image/math/f64\"\n" +
+		")\n")
+
+	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)
 	}
 }