draw: add mask fields to Options.

This change only adds the fields, more or less. Follow-up changes will
actually honor the masks.

Change-Id: I81411dc1aac4b3c846dcdf13e2cb0b5cd60fb2b4
Reviewed-on: https://go-review.googlesource.com/8902
Reviewed-by: Rob Pike <r@golang.org>
diff --git a/draw/gen.go b/draw/gen.go
index 345c7fd..5d560df 100644
--- a/draw/gen.go
+++ b/draw/gen.go
@@ -644,7 +644,7 @@
 
 func expnSwitch(op, dType string, expandBoth bool, template string) string {
 	if op == "" && dType != "anyDType" {
-		lines := []string{"switch op {"}
+		lines := []string{"switch o.Op {"}
 		for _, op = range ops {
 			lines = append(lines,
 				fmt.Sprintf("case %s:", op),
@@ -818,51 +818,65 @@
 const (
 	codeRoot = `
 		func (z $receiver) Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle, opts *Options) {
+			var o Options
+			if opts != nil {
+				o = *opts
+			}
+
 			// adr is the affected destination pixels, relative to dr.Min.
 			adr := dst.Bounds().Intersect(dr).Sub(dr.Min)
+			// TODO: clip adr to o.DstMask.Bounds().
 			if adr.Empty() || sr.Empty() {
 				return
 			}
-			op := opts.op()
-			if op == Over && opaque(src) { // TODO: also check that opts.SrcMask == nil.
-				op = Src
+			if o.Op == Over && o.SrcMask == nil && opaque(src) {
+				o.Op = Src
 			}
+
 			// sr is the source pixels. If it extends beyond the src bounds,
 			// we cannot use the type-specific fast paths, as they access
 			// the Pix fields directly without bounds checking.
-			if !sr.In(src.Bounds()) {
-				switch op {
+			//
+			// Similarly, the fast paths assume that the masks are nil.
+			if o.DstMask != nil || o.SrcMask != nil || !sr.In(src.Bounds()) {
+				switch o.Op {
 				case Over:
 					z.scale_Image_Image_Over(dst, dr, adr, src, sr)
 				case Src:
 					z.scale_Image_Image_Src(dst, dr, adr, src, sr)
 				}
 			} else if _, ok := src.(*image.Uniform); ok {
-				Draw(dst, dr, src, src.Bounds().Min, op)
+				Draw(dst, dr, src, src.Bounds().Min, o.Op)
 			} else {
 				$switch z.scale_$dTypeRN_$sTypeRN$sratio_$op(dst, dr, adr, src, sr)
 			}
 		}
 
 		func (z $receiver) Transform(dst Image, s2d *f64.Aff3, src image.Image, sr image.Rectangle, opts *Options) {
+			var o Options
+			if opts != nil {
+				o = *opts
+			}
+
 			dr := transformRect(s2d, &sr)
 			// adr is the affected destination pixels.
 			adr := dst.Bounds().Intersect(dr)
+			// TODO: clip adr to o.DstMask.Bounds().
 			if adr.Empty() || sr.Empty() {
 				return
 			}
-			op := opts.op()
-			if op == Over && opaque(src) { // TODO: also check that opts.SrcMask == nil.
-				op = Src
+			if o.Op == Over && o.SrcMask == nil && opaque(src) {
+				o.Op = Src
 			}
+
 			d2s := invert(s2d)
-			// bias is a translation of the mapping from dst co-ordinates to
-			// src co-ordinates such that the latter temporarily have
-			// non-negative X and Y co-ordinates. This allows us to write
-			// int(f) instead of int(math.Floor(f)), since "round to zero" and
-			// "round down" are equivalent when f >= 0, but the former is much
-			// cheaper. The X-- and Y-- are because the TransformLeaf methods
-			// have a "sx -= 0.5" adjustment.
+			// bias is a translation of the mapping from dst coordinates to src
+			// coordinates such that the latter temporarily have non-negative X
+			// and Y coordinates. This allows us to write int(f) instead of
+			// int(math.Floor(f)), since "round to zero" and "round down" are
+			// equivalent when f >= 0, but the former is much cheaper. The X--
+			// and Y-- are because the TransformLeaf methods have a "sx -= 0.5"
+			// adjustment.
 			bias := transformRect(&d2s, &adr).Min
 			bias.X--
 			bias.Y--
@@ -873,15 +887,17 @@
 			// sr is the source pixels. If it extends beyond the src bounds,
 			// we cannot use the type-specific fast paths, as they access
 			// the Pix fields directly without bounds checking.
-			if !sr.In(src.Bounds()) {
-				switch op {
+			//
+			// Similarly, the fast paths assume that the masks are nil.
+			if o.DstMask != nil || o.SrcMask != nil || !sr.In(src.Bounds()) {
+				switch o.Op {
 				case Over:
 					z.transform_Image_Image_Over(dst, dr, adr, &d2s, src, sr, bias)
 				case Src:
 					z.transform_Image_Image_Src(dst, dr, adr, &d2s, src, sr, bias)
 				}
 			} else if u, ok := src.(*image.Uniform); ok {
-				transform_Uniform(dst, dr, adr, &d2s, u, sr, bias, op)
+				transform_Uniform(dst, dr, adr, &d2s, u, sr, bias, o.Op)
 			} else {
 				$switch z.transform_$dTypeRN_$sTypeRN$sratio_$op(dst, dr, adr, &d2s, src, sr, bias)
 			}
@@ -1042,18 +1058,24 @@
 				z.kernel.Scale(dst, dr, src, sr, opts)
 				return
 			}
+
+			var o Options
+			if opts != nil {
+				o = *opts
+			}
+
 			// adr is the affected destination pixels, relative to dr.Min.
 			adr := dst.Bounds().Intersect(dr).Sub(dr.Min)
+			// TODO: clip adr to o.DstMask.Bounds().
 			if adr.Empty() || sr.Empty() {
 				return
 			}
-			op := opts.op()
-			if op == Over && opaque(src) { // TODO: also check that opts.SrcMask == nil.
-				op = Src
+			if o.Op == Over && o.SrcMask == nil && opaque(src) {
+				o.Op = Src
 			}
 
-			if _, ok := src.(*image.Uniform); ok && sr.In(src.Bounds()) {
-				Draw(dst, dr, src, src.Bounds().Min, op)
+			if _, ok := src.(*image.Uniform); ok && o.DstMask == nil && o.SrcMask == nil && sr.In(src.Bounds()) {
+				Draw(dst, dr, src, src.Bounds().Min, o.Op)
 				return
 			}
 
@@ -1072,34 +1094,42 @@
 			// sr is the source pixels. If it extends beyond the src bounds,
 			// we cannot use the type-specific fast paths, as they access
 			// the Pix fields directly without bounds checking.
-			if !sr.In(src.Bounds()) {
+			//
+			// Similarly, the fast paths assume that the masks are nil.
+			if o.SrcMask != nil || !sr.In(src.Bounds()) {
 				z.scaleX_Image(tmp, src, sr)
 			} else {
 				$switchS z.scaleX_$sTypeRN$sratio(tmp, src, sr)
 			}
 
+			// TODO: honor o.DstMask.
 			$switchD z.scaleY_$dTypeRN_$op(dst, dr, adr, tmp)
 		}
 
 		func (q *Kernel) Transform(dst Image, s2d *f64.Aff3, src image.Image, sr image.Rectangle, opts *Options) {
+			var o Options
+			if opts != nil {
+				o = *opts
+			}
+
 			dr := transformRect(s2d, &sr)
 			// adr is the affected destination pixels.
 			adr := dst.Bounds().Intersect(dr)
+			// TODO: clip adr to o.DstMask.Bounds().
 			if adr.Empty() || sr.Empty() {
 				return
 			}
-			op := opts.op()
-			if op == Over && opaque(src) { // TODO: also check that opts.SrcMask == nil.
-				op = Src
+			if o.Op == Over && o.SrcMask == nil && opaque(src) {
+				o.Op = Src
 			}
 			d2s := invert(s2d)
-			// bias is a translation of the mapping from dst co-ordinates to
-			// src co-ordinates such that the latter temporarily have
-			// non-negative X and Y co-ordinates. This allows us to write
-			// int(f) instead of int(math.Floor(f)), since "round to zero" and
-			// "round down" are equivalent when f >= 0, but the former is much
-			// cheaper. The X-- and Y-- are because the TransformLeaf methods
-			// have a "sx -= 0.5" adjustment.
+			// bias is a translation of the mapping from dst coordinates to src
+			// coordinates such that the latter temporarily have non-negative X
+			// and Y coordinates. This allows us to write int(f) instead of
+			// int(math.Floor(f)), since "round to zero" and "round down" are
+			// equivalent when f >= 0, but the former is much cheaper. The X--
+			// and Y-- are because the TransformLeaf methods have a "sx -= 0.5"
+			// adjustment.
 			bias := transformRect(&d2s, &adr).Min
 			bias.X--
 			bias.Y--
@@ -1108,8 +1138,8 @@
 			// Make adr relative to dr.Min.
 			adr = adr.Sub(dr.Min)
 
-			if u, ok := src.(*image.Uniform); ok && sr.In(src.Bounds()) {
-				transform_Uniform(dst, dr, adr, &d2s, u, sr, bias, op)
+			if u, ok := src.(*image.Uniform); ok && o.DstMask != nil && o.SrcMask != nil && sr.In(src.Bounds()) {
+				transform_Uniform(dst, dr, adr, &d2s, u, sr, bias, o.Op)
 				return
 			}
 
@@ -1125,8 +1155,10 @@
 			// sr is the source pixels. If it extends beyond the src bounds,
 			// we cannot use the type-specific fast paths, as they access
 			// the Pix fields directly without bounds checking.
-			if !sr.In(src.Bounds()) {
-				switch op {
+			//
+			// Similarly, the fast paths assume that the masks are nil.
+			if o.DstMask != nil || o.SrcMask != nil || !sr.In(src.Bounds()) {
+				switch o.Op {
 				case Over:
 					q.transform_Image_Image_Over(dst, dr, adr, &d2s, src, sr, bias, xscale, yscale)
 				case Src:
diff --git a/draw/impl.go b/draw/impl.go
index 5292ced..789b8a3 100644
--- a/draw/impl.go
+++ b/draw/impl.go
@@ -11,29 +11,37 @@
 )
 
 func (z nnInterpolator) Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle, opts *Options) {
+	var o Options
+	if opts != nil {
+		o = *opts
+	}
+
 	// adr is the affected destination pixels, relative to dr.Min.
 	adr := dst.Bounds().Intersect(dr).Sub(dr.Min)
+	// TODO: clip adr to o.DstMask.Bounds().
 	if adr.Empty() || sr.Empty() {
 		return
 	}
-	op := opts.op()
-	if op == Over && opaque(src) { // TODO: also check that opts.SrcMask == nil.
-		op = Src
+	if o.Op == Over && o.SrcMask == nil && opaque(src) {
+		o.Op = Src
 	}
+
 	// sr is the source pixels. If it extends beyond the src bounds,
 	// we cannot use the type-specific fast paths, as they access
 	// the Pix fields directly without bounds checking.
-	if !sr.In(src.Bounds()) {
-		switch op {
+	//
+	// Similarly, the fast paths assume that the masks are nil.
+	if o.DstMask != nil || o.SrcMask != nil || !sr.In(src.Bounds()) {
+		switch o.Op {
 		case Over:
 			z.scale_Image_Image_Over(dst, dr, adr, src, sr)
 		case Src:
 			z.scale_Image_Image_Src(dst, dr, adr, src, sr)
 		}
 	} else if _, ok := src.(*image.Uniform); ok {
-		Draw(dst, dr, src, src.Bounds().Min, op)
+		Draw(dst, dr, src, src.Bounds().Min, o.Op)
 	} else {
-		switch op {
+		switch o.Op {
 		case Over:
 			switch dst := dst.(type) {
 			case *image.RGBA:
@@ -88,24 +96,30 @@
 }
 
 func (z nnInterpolator) Transform(dst Image, s2d *f64.Aff3, src image.Image, sr image.Rectangle, opts *Options) {
+	var o Options
+	if opts != nil {
+		o = *opts
+	}
+
 	dr := transformRect(s2d, &sr)
 	// adr is the affected destination pixels.
 	adr := dst.Bounds().Intersect(dr)
+	// TODO: clip adr to o.DstMask.Bounds().
 	if adr.Empty() || sr.Empty() {
 		return
 	}
-	op := opts.op()
-	if op == Over && opaque(src) { // TODO: also check that opts.SrcMask == nil.
-		op = Src
+	if o.Op == Over && o.SrcMask == nil && opaque(src) {
+		o.Op = Src
 	}
+
 	d2s := invert(s2d)
-	// bias is a translation of the mapping from dst co-ordinates to
-	// src co-ordinates such that the latter temporarily have
-	// non-negative X and Y co-ordinates. This allows us to write
-	// int(f) instead of int(math.Floor(f)), since "round to zero" and
-	// "round down" are equivalent when f >= 0, but the former is much
-	// cheaper. The X-- and Y-- are because the TransformLeaf methods
-	// have a "sx -= 0.5" adjustment.
+	// bias is a translation of the mapping from dst coordinates to src
+	// coordinates such that the latter temporarily have non-negative X
+	// and Y coordinates. This allows us to write int(f) instead of
+	// int(math.Floor(f)), since "round to zero" and "round down" are
+	// equivalent when f >= 0, but the former is much cheaper. The X--
+	// and Y-- are because the TransformLeaf methods have a "sx -= 0.5"
+	// adjustment.
 	bias := transformRect(&d2s, &adr).Min
 	bias.X--
 	bias.Y--
@@ -116,17 +130,19 @@
 	// sr is the source pixels. If it extends beyond the src bounds,
 	// we cannot use the type-specific fast paths, as they access
 	// the Pix fields directly without bounds checking.
-	if !sr.In(src.Bounds()) {
-		switch op {
+	//
+	// Similarly, the fast paths assume that the masks are nil.
+	if o.DstMask != nil || o.SrcMask != nil || !sr.In(src.Bounds()) {
+		switch o.Op {
 		case Over:
 			z.transform_Image_Image_Over(dst, dr, adr, &d2s, src, sr, bias)
 		case Src:
 			z.transform_Image_Image_Src(dst, dr, adr, &d2s, src, sr, bias)
 		}
 	} else if u, ok := src.(*image.Uniform); ok {
-		transform_Uniform(dst, dr, adr, &d2s, u, sr, bias, op)
+		transform_Uniform(dst, dr, adr, &d2s, u, sr, bias, o.Op)
 	} else {
-		switch op {
+		switch o.Op {
 		case Over:
 			switch dst := dst.(type) {
 			case *image.RGBA:
@@ -934,29 +950,37 @@
 }
 
 func (z ablInterpolator) Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle, opts *Options) {
+	var o Options
+	if opts != nil {
+		o = *opts
+	}
+
 	// adr is the affected destination pixels, relative to dr.Min.
 	adr := dst.Bounds().Intersect(dr).Sub(dr.Min)
+	// TODO: clip adr to o.DstMask.Bounds().
 	if adr.Empty() || sr.Empty() {
 		return
 	}
-	op := opts.op()
-	if op == Over && opaque(src) { // TODO: also check that opts.SrcMask == nil.
-		op = Src
+	if o.Op == Over && o.SrcMask == nil && opaque(src) {
+		o.Op = Src
 	}
+
 	// sr is the source pixels. If it extends beyond the src bounds,
 	// we cannot use the type-specific fast paths, as they access
 	// the Pix fields directly without bounds checking.
-	if !sr.In(src.Bounds()) {
-		switch op {
+	//
+	// Similarly, the fast paths assume that the masks are nil.
+	if o.DstMask != nil || o.SrcMask != nil || !sr.In(src.Bounds()) {
+		switch o.Op {
 		case Over:
 			z.scale_Image_Image_Over(dst, dr, adr, src, sr)
 		case Src:
 			z.scale_Image_Image_Src(dst, dr, adr, src, sr)
 		}
 	} else if _, ok := src.(*image.Uniform); ok {
-		Draw(dst, dr, src, src.Bounds().Min, op)
+		Draw(dst, dr, src, src.Bounds().Min, o.Op)
 	} else {
-		switch op {
+		switch o.Op {
 		case Over:
 			switch dst := dst.(type) {
 			case *image.RGBA:
@@ -1011,24 +1035,30 @@
 }
 
 func (z ablInterpolator) Transform(dst Image, s2d *f64.Aff3, src image.Image, sr image.Rectangle, opts *Options) {
+	var o Options
+	if opts != nil {
+		o = *opts
+	}
+
 	dr := transformRect(s2d, &sr)
 	// adr is the affected destination pixels.
 	adr := dst.Bounds().Intersect(dr)
+	// TODO: clip adr to o.DstMask.Bounds().
 	if adr.Empty() || sr.Empty() {
 		return
 	}
-	op := opts.op()
-	if op == Over && opaque(src) { // TODO: also check that opts.SrcMask == nil.
-		op = Src
+	if o.Op == Over && o.SrcMask == nil && opaque(src) {
+		o.Op = Src
 	}
+
 	d2s := invert(s2d)
-	// bias is a translation of the mapping from dst co-ordinates to
-	// src co-ordinates such that the latter temporarily have
-	// non-negative X and Y co-ordinates. This allows us to write
-	// int(f) instead of int(math.Floor(f)), since "round to zero" and
-	// "round down" are equivalent when f >= 0, but the former is much
-	// cheaper. The X-- and Y-- are because the TransformLeaf methods
-	// have a "sx -= 0.5" adjustment.
+	// bias is a translation of the mapping from dst coordinates to src
+	// coordinates such that the latter temporarily have non-negative X
+	// and Y coordinates. This allows us to write int(f) instead of
+	// int(math.Floor(f)), since "round to zero" and "round down" are
+	// equivalent when f >= 0, but the former is much cheaper. The X--
+	// and Y-- are because the TransformLeaf methods have a "sx -= 0.5"
+	// adjustment.
 	bias := transformRect(&d2s, &adr).Min
 	bias.X--
 	bias.Y--
@@ -1039,17 +1069,19 @@
 	// sr is the source pixels. If it extends beyond the src bounds,
 	// we cannot use the type-specific fast paths, as they access
 	// the Pix fields directly without bounds checking.
-	if !sr.In(src.Bounds()) {
-		switch op {
+	//
+	// Similarly, the fast paths assume that the masks are nil.
+	if o.DstMask != nil || o.SrcMask != nil || !sr.In(src.Bounds()) {
+		switch o.Op {
 		case Over:
 			z.transform_Image_Image_Over(dst, dr, adr, &d2s, src, sr, bias)
 		case Src:
 			z.transform_Image_Image_Src(dst, dr, adr, &d2s, src, sr, bias)
 		}
 	} else if u, ok := src.(*image.Uniform); ok {
-		transform_Uniform(dst, dr, adr, &d2s, u, sr, bias, op)
+		transform_Uniform(dst, dr, adr, &d2s, u, sr, bias, o.Op)
 	} else {
-		switch op {
+		switch o.Op {
 		case Over:
 			switch dst := dst.(type) {
 			case *image.RGBA:
@@ -4033,18 +4065,24 @@
 		z.kernel.Scale(dst, dr, src, sr, opts)
 		return
 	}
+
+	var o Options
+	if opts != nil {
+		o = *opts
+	}
+
 	// adr is the affected destination pixels, relative to dr.Min.
 	adr := dst.Bounds().Intersect(dr).Sub(dr.Min)
+	// TODO: clip adr to o.DstMask.Bounds().
 	if adr.Empty() || sr.Empty() {
 		return
 	}
-	op := opts.op()
-	if op == Over && opaque(src) { // TODO: also check that opts.SrcMask == nil.
-		op = Src
+	if o.Op == Over && o.SrcMask == nil && opaque(src) {
+		o.Op = Src
 	}
 
-	if _, ok := src.(*image.Uniform); ok && sr.In(src.Bounds()) {
-		Draw(dst, dr, src, src.Bounds().Min, op)
+	if _, ok := src.(*image.Uniform); ok && o.DstMask == nil && o.SrcMask == nil && sr.In(src.Bounds()) {
+		Draw(dst, dr, src, src.Bounds().Min, o.Op)
 		return
 	}
 
@@ -4063,7 +4101,9 @@
 	// sr is the source pixels. If it extends beyond the src bounds,
 	// we cannot use the type-specific fast paths, as they access
 	// the Pix fields directly without bounds checking.
-	if !sr.In(src.Bounds()) {
+	//
+	// Similarly, the fast paths assume that the masks are nil.
+	if o.SrcMask != nil || !sr.In(src.Bounds()) {
 		z.scaleX_Image(tmp, src, sr)
 	} else {
 		switch src := src.(type) {
@@ -4091,7 +4131,8 @@
 		}
 	}
 
-	switch op {
+	// TODO: honor o.DstMask.
+	switch o.Op {
 	case Over:
 		switch dst := dst.(type) {
 		case *image.RGBA:
@@ -4110,24 +4151,29 @@
 }
 
 func (q *Kernel) Transform(dst Image, s2d *f64.Aff3, src image.Image, sr image.Rectangle, opts *Options) {
+	var o Options
+	if opts != nil {
+		o = *opts
+	}
+
 	dr := transformRect(s2d, &sr)
 	// adr is the affected destination pixels.
 	adr := dst.Bounds().Intersect(dr)
+	// TODO: clip adr to o.DstMask.Bounds().
 	if adr.Empty() || sr.Empty() {
 		return
 	}
-	op := opts.op()
-	if op == Over && opaque(src) { // TODO: also check that opts.SrcMask == nil.
-		op = Src
+	if o.Op == Over && o.SrcMask == nil && opaque(src) {
+		o.Op = Src
 	}
 	d2s := invert(s2d)
-	// bias is a translation of the mapping from dst co-ordinates to
-	// src co-ordinates such that the latter temporarily have
-	// non-negative X and Y co-ordinates. This allows us to write
-	// int(f) instead of int(math.Floor(f)), since "round to zero" and
-	// "round down" are equivalent when f >= 0, but the former is much
-	// cheaper. The X-- and Y-- are because the TransformLeaf methods
-	// have a "sx -= 0.5" adjustment.
+	// bias is a translation of the mapping from dst coordinates to src
+	// coordinates such that the latter temporarily have non-negative X
+	// and Y coordinates. This allows us to write int(f) instead of
+	// int(math.Floor(f)), since "round to zero" and "round down" are
+	// equivalent when f >= 0, but the former is much cheaper. The X--
+	// and Y-- are because the TransformLeaf methods have a "sx -= 0.5"
+	// adjustment.
 	bias := transformRect(&d2s, &adr).Min
 	bias.X--
 	bias.Y--
@@ -4136,8 +4182,8 @@
 	// Make adr relative to dr.Min.
 	adr = adr.Sub(dr.Min)
 
-	if u, ok := src.(*image.Uniform); ok && sr.In(src.Bounds()) {
-		transform_Uniform(dst, dr, adr, &d2s, u, sr, bias, op)
+	if u, ok := src.(*image.Uniform); ok && o.DstMask != nil && o.SrcMask != nil && sr.In(src.Bounds()) {
+		transform_Uniform(dst, dr, adr, &d2s, u, sr, bias, o.Op)
 		return
 	}
 
@@ -4153,15 +4199,17 @@
 	// sr is the source pixels. If it extends beyond the src bounds,
 	// we cannot use the type-specific fast paths, as they access
 	// the Pix fields directly without bounds checking.
-	if !sr.In(src.Bounds()) {
-		switch op {
+	//
+	// Similarly, the fast paths assume that the masks are nil.
+	if o.DstMask != nil || o.SrcMask != nil || !sr.In(src.Bounds()) {
+		switch o.Op {
 		case Over:
 			q.transform_Image_Image_Over(dst, dr, adr, &d2s, src, sr, bias, xscale, yscale)
 		case Src:
 			q.transform_Image_Image_Src(dst, dr, adr, &d2s, src, sr, bias, xscale, yscale)
 		}
 	} else {
-		switch op {
+		switch o.Op {
 		case Over:
 			switch dst := dst.(type) {
 			case *image.RGBA:
diff --git a/draw/scale.go b/draw/scale.go
index 8207e81..85659cf 100644
--- a/draw/scale.go
+++ b/draw/scale.go
@@ -19,12 +19,13 @@
 // the part of the destination image defined by dst and the translation of sr
 // so that sr.Min translates to dp.
 func Copy(dst Image, dp image.Point, src image.Image, sr image.Rectangle, opts *Options) {
-	mask, mp := image.Image(nil), image.Point{}
+	var o Options
 	if opts != nil {
-		// TODO: set mask and mp.
+		o = *opts
 	}
 	dr := sr.Add(dp.Sub(sr.Min))
-	DrawMask(dst, dr, src, sr.Min, mask, mp, opts.op())
+	// TODO: honor o.DstMask and o.SrcMask.
+	DrawMask(dst, dr, src, sr.Min, nil, image.Point{}, o.Op)
 }
 
 // Scaler scales the part of the source image defined by src and sr and writes
@@ -59,15 +60,38 @@
 	// Op is the compositing operator. The default value is Over.
 	Op Op
 
-	// TODO: add fields a la
-	// https://groups.google.com/forum/#!topic/golang-dev/fgn_xM0aeq4
-}
+	// Masks limit what parts of the dst image are drawn to and what parts of
+	// the src image are drawn from.
+	//
+	// A dst or src mask image having a zero alpha (transparent) pixel value in
+	// the respective coordinate space means that that dst pixel is entirely
+	// unaffected or that src pixel is considered transparent black. A full
+	// alpha (opaque) value means that the dst pixel is maximally affected or
+	// the src pixel contributes maximally. The default values, nil, are
+	// equivalent to fully opaque, infinitely large mask images.
+	//
+	// The DstMask is otherwise known as a clip mask, and its pixels map 1:1 to
+	// the dst image's pixels. DstMaskP in DstMask space corresponds to
+	// image.Point{X:0, Y:0} in dst space. For example, when limiting
+	// repainting to a 'dirty rectangle', use that image.Rectangle and a zero
+	// image.Point as the DstMask and DstMaskP.
+	//
+	// The SrcMask's pixels map 1:1 to the src image's pixels. SrcMaskP in
+	// SrcMask space corresponds to image.Point{X:0, Y:0} in src space. For
+	// example, when drawing font glyphs in a uniform color, use an
+	// *image.Uniform as the src, and use the glyph atlas image and the
+	// per-glyph offset as SrcMask and SrcMaskP:
+	//	Copy(dst, dp, image.NewUniform(color), image.Rect(0, 0, glyphWidth, glyphHeight), &Options{
+	//		SrcMask:  glyphAtlas,
+	//		SrcMaskP: glyphOffset,
+	//	})
+	DstMask  image.Image
+	DstMaskP image.Point
+	SrcMask  image.Image
+	SrcMaskP image.Point
+	// TODO: actually implement DstMask and SrcMask.
 
-func (o *Options) op() Op {
-	if o == nil {
-		return Over
-	}
-	return o.Op
+	// TODO: a smooth vs sharp edges option, for arbitrary rotations?
 }
 
 // Interpolator is an interpolation algorithm, when dst and src pixels don't
@@ -218,7 +242,7 @@
 
 	// Make the sources slice, one source for each column or row, and temporarily
 	// appropriate its elements' fields so that invTotalWeight is the scaled
-	// co-ordinate of the source column or row, and i and j are the lower and
+	// coordinate of the source column or row, and i and j are the lower and
 	// upper bounds of the range of destination columns or rows affected by the
 	// source column or row.
 	n, sources := int32(0), make([]source, dw)