git clone https://git.lucas.co/go_mono.git
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)
}
}