draw: fix crash caused by Scale by Copy shortcut

When DstMask is not nil, this shortcut causes stack overflow because
Copy function in turn will call Scale with same dr and sr.

Fixes golang/go#23107

Change-Id: I8ccadbd9b7f16363ac17b6114308527d6fa9456e
Reviewed-on: https://go-review.googlesource.com/83537
Reviewed-by: Nigel Tao <nigeltao@golang.org>
Run-TryBot: Nigel Tao <nigeltao@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
diff --git a/draw/gen.go b/draw/gen.go
index 65a7123..822bb6a 100644
--- a/draw/gen.go
+++ b/draw/gen.go
@@ -877,8 +877,9 @@
 const (
 	codeRoot = `
 		func (z $receiver) Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle, op Op, opts *Options) {
-			// Try to simplify a Scale to a Copy.
-			if dr.Size() == sr.Size() {
+			// Try to simplify a Scale to a Copy when DstMask is not specified.
+			// If DstMask is not nil, Copy will call Scale back with same dr and sr, and cause stack overflow.
+			if dr.Size() == sr.Size() && (opts == nil || opts.DstMask == nil) {
 				Copy(dst, dr.Min, src, sr, op, opts)
 				return
 			}
diff --git a/draw/impl.go b/draw/impl.go
index 637887b..75498ad 100644
--- a/draw/impl.go
+++ b/draw/impl.go
@@ -11,8 +11,9 @@
 )
 
 func (z nnInterpolator) Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle, op Op, opts *Options) {
-	// Try to simplify a Scale to a Copy.
-	if dr.Size() == sr.Size() {
+	// Try to simplify a Scale to a Copy when DstMask is not specified.
+	// If DstMask is not nil, Copy will call Scale back with same dr and sr, and cause stack overflow.
+	if dr.Size() == sr.Size() && (opts == nil || opts.DstMask == nil) {
 		Copy(dst, dr.Min, src, sr, op, opts)
 		return
 	}
@@ -1048,8 +1049,9 @@
 }
 
 func (z ablInterpolator) Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle, op Op, opts *Options) {
-	// Try to simplify a Scale to a Copy.
-	if dr.Size() == sr.Size() {
+	// Try to simplify a Scale to a Copy when DstMask is not specified.
+	// If DstMask is not nil, Copy will call Scale back with same dr and sr, and cause stack overflow.
+	if dr.Size() == sr.Size() && (opts == nil || opts.DstMask == nil) {
 		Copy(dst, dr.Min, src, sr, op, opts)
 		return
 	}
diff --git a/draw/scale_test.go b/draw/scale_test.go
index 5e184c2..ea41940 100644
--- a/draw/scale_test.go
+++ b/draw/scale_test.go
@@ -551,6 +551,17 @@
 	}
 }
 
+func TestDstMaskSameSizeCopy(t *testing.T) {
+	bounds := image.Rect(0, 0, 42, 42)
+	src := image.Opaque
+	dst := image.NewRGBA(bounds)
+	mask := image.NewRGBA(bounds)
+
+	Copy(dst, image.ZP, src, bounds, Src, &Options{
+		DstMask: mask,
+	})
+}
+
 // TODO: delete this wrapper type once Go 1.5 is released, where an
 // image.Rectangle implements image.Image.
 type rectImage image.Rectangle