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 <r@golang.org>
This commit is contained in:
Nigel Tao 2015-03-05 15:46:57 +11:00
parent 7bd522e167
commit 08593990c4
4 changed files with 80 additions and 29 deletions

View File

@ -24,10 +24,15 @@ func main() {
w := new(bytes.Buffer) w := new(bytes.Buffer)
w.WriteString("// generated by \"go run gen.go\". DO NOT EDIT.\n\n" + 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, "nnInterpolator", codeNNScaleLeaf)
gen(w, "ablInterpolator", codeABLLeaf) gen(w, "ablInterpolator", codeABLScaleLeaf)
genKernel(w) genKernel(w)
if *debug { if *debug {
@ -99,12 +104,12 @@ func gen(w *bytes.Buffer, receiver string, code string) {
func genKernel(w *bytes.Buffer) { func genKernel(w *bytes.Buffer) {
expn(w, codeKernelRoot, &data{}) expn(w, codeKernelRoot, &data{})
for _, sType := range sTypes { for _, sType := range sTypes {
expn(w, codeKernelLeafX, &data{ expn(w, codeKernelScaleLeafX, &data{
sType: sType, sType: sType,
}) })
} }
for _, dType := range dTypes { for _, dType := range dTypes {
expn(w, codeKernelLeafY, &data{ expn(w, codeKernelScaleLeafY, &data{
dType: dType, dType: dType,
}) })
} }
@ -407,7 +412,7 @@ func relName(s string) string {
const ( const (
codeRoot = ` 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 is the affected destination pixels, relative to dr.Min.
adr := dst.Bounds().Intersect(dr).Sub(dr.Min) adr := dst.Bounds().Intersect(dr).Sub(dr.Min)
if adr.Empty() || sr.Empty() { if adr.Empty() || sr.Empty() {
@ -422,9 +427,13 @@ const (
$switch z.scale_$dTypeRN_$sTypeRN(dst, dr, adr, src, sr) $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) { func (nnInterpolator) scale_$dTypeRN_$sTypeRN(dst $dType, dr, adr image.Rectangle, src $sType, sr image.Rectangle) {
dw2 := uint64(dr.Dx()) * 2 dw2 := uint64(dr.Dx()) * 2
dh2 := uint64(dr.Dy()) * 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) { func (ablInterpolator) scale_$dTypeRN_$sTypeRN(dst $dType, dr, adr image.Rectangle, src $sType, sr image.Rectangle) {
sw := int32(sr.Dx()) sw := int32(sr.Dx())
sh := int32(sr.Dy()) sh := int32(sr.Dy())
@ -491,9 +500,9 @@ const (
` `
codeKernelRoot = ` 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()) { 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 return
} }
// adr is the affected destination pixels, relative to dr.Min. // adr is the affected destination pixels, relative to dr.Min.
@ -518,9 +527,13 @@ const (
$switchD z.scaleY_$dTypeRN(dst, dr, adr, tmp) $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) { func (z *kernelScaler) scaleX_$sTypeRN(tmp [][4]float64, src $sType, sr image.Rectangle) {
t := 0 t := 0
for y := int32(0); y < z.sh; y++ { 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) { func (z *kernelScaler) scaleY_$dTypeRN(dst $dType, dr, adr image.Rectangle, tmp [][4]float64) {
$preOuter $preOuter
for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx++ { for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx++ {

View File

@ -5,9 +5,11 @@ package draw
import ( import (
"image" "image"
"image/color" "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 is the affected destination pixels, relative to dr.Min.
adr := dst.Bounds().Intersect(dr).Sub(dr.Min) adr := dst.Bounds().Intersect(dr).Sub(dr.Min)
if adr.Empty() || sr.Empty() { 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) { func (nnInterpolator) scale_RGBA_Gray(dst *image.RGBA, dr, adr image.Rectangle, src *image.Gray, sr image.Rectangle) {
dw2 := uint64(dr.Dx()) * 2 dw2 := uint64(dr.Dx()) * 2
dh2 := uint64(dr.Dy()) * 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 is the affected destination pixels, relative to dr.Min.
adr := dst.Bounds().Intersect(dr).Sub(dr.Min) adr := dst.Bounds().Intersect(dr).Sub(dr.Min)
if adr.Empty() || sr.Empty() { 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) { func (ablInterpolator) scale_RGBA_Gray(dst *image.RGBA, dr, adr image.Rectangle, src *image.Gray, sr image.Rectangle) {
sw := int32(sr.Dx()) sw := int32(sr.Dx())
sh := int32(sr.Dy()) 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()) { 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 return
} }
// adr is the affected destination pixels, relative to dr.Min. // 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) { func (z *kernelScaler) scaleX_Gray(tmp [][4]float64, src *image.Gray, sr image.Rectangle) {
t := 0 t := 0
for y := int32(0); y < z.sh; y++ { for y := int32(0); y < z.sh; y++ {

View File

@ -6,12 +6,11 @@
package draw package draw
// TODO: add an Options type a la
// https://groups.google.com/forum/#!topic/golang-dev/fgn_xM0aeq4
import ( import (
"image" "image"
"math" "math"
"golang.org/x/image/math/f64"
) )
// Scaler scales the part of the source image defined by src and sr and writes // 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. // A Scaler is safe to use concurrently.
type Scaler interface { 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 // 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. // non-kernel interpolators, especially when scaling down.
type Interpolator interface { type Interpolator interface {
Scaler Scaler
// TODO: Transformer Transformer
} }
// Kernel is an interpolator that blends source pixels weighted by a symmetric // Kernel is an interpolator that blends source pixels weighted by a symmetric
@ -50,8 +74,8 @@ type Kernel struct {
} }
// Scale implements the Scaler interface. // Scale implements the Scaler interface.
func (k *Kernel) Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle) { 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) 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 // NewScaler returns a Scaler that is optimized for scaling multiple times with

View File

@ -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) gotFilename := fmt.Sprintf("../testdata/go-turns-two-%s-%s.png", direction, name)
got := image.NewRGBA(image.Rect(0, 0, w, h)) 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 { if *genScaleFiles {
g, err := os.Create(gotFilename) g, err := os.Create(gotFilename)
if err != nil { if err != nil {
@ -112,12 +112,12 @@ func TestScaleClipCommute(t *testing.T) {
} }
// Scale then clip. // Scale then clip.
q.Scale(dst0, outer, src, src.Bounds()) q.Scale(dst0, outer, src, src.Bounds(), nil)
dst0 = dst0.SubImage(inner).(*image.RGBA) dst0 = dst0.SubImage(inner).(*image.RGBA)
// Clip then scale. // Clip then scale.
dst1 = dst1.SubImage(inner).(*image.RGBA) dst1 = dst1.SubImage(inner).(*image.RGBA)
q.Scale(dst1, outer, src, src.Bounds()) q.Scale(dst1, outer, src, src.Bounds(), nil)
loop: loop:
for y := inner.Min.Y; y < inner.Max.Y; y++ { for y := inner.Min.Y; y < inner.Max.Y; y++ {
@ -187,8 +187,8 @@ func TestFastPaths(t *testing.T) {
dst1 := image.NewRGBA(drs[0]) dst1 := image.NewRGBA(drs[0])
Draw(dst0, dst0.Bounds(), blue, image.Point{}, Src) Draw(dst0, dst0.Bounds(), blue, image.Point{}, Src)
Draw(dstWrapper{dst1}, dst1.Bounds(), srcWrapper{blue}, image.Point{}, Src) Draw(dstWrapper{dst1}, dst1.Bounds(), srcWrapper{blue}, image.Point{}, Src)
q.Scale(dst0, dr, src, sr) q.Scale(dst0, dr, src, sr, nil)
q.Scale(dstWrapper{dst1}, dr, srcWrapper{src}, sr) q.Scale(dstWrapper{dst1}, dr, srcWrapper{src}, sr, nil)
if !bytes.Equal(dst0.Pix, dst1.Pix) { if !bytes.Equal(dst0.Pix, dst1.Pix) {
t.Errorf("pix differ for dr=%v, src=%T, sr=%v, q=%T", dr, src, sr, q) 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() b.ResetTimer()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
scaler.Scale(dst, dr, src, sr) scaler.Scale(dst, dr, src, sr, nil)
} }
} }