draw: implement srcMask.

Change-Id: Ibf710521f466847afaf2d005dc8a2bb817169298
Reviewed-on: https://go-review.googlesource.com/9276
Reviewed-by: Rob Pike <r@golang.org>
diff --git a/draw/example_test.go b/draw/example_test.go
index 948be8d..6a2cacf 100644
--- a/draw/example_test.go
+++ b/draw/example_test.go
@@ -10,6 +10,7 @@
 	"image/color"
 	"image/png"
 	"log"
+	"math"
 	"os"
 
 	"golang.org/x/image/draw"
@@ -27,7 +28,6 @@
 		log.Fatal(err)
 	}
 
-	sr := src.Bounds()
 	dst := image.NewRGBA(image.Rect(0, 0, 400, 300))
 	green := image.NewUniform(color.RGBA{0x00, 0x1f, 0x00, 0xff})
 	draw.Copy(dst, image.Point{}, green, dst.Bounds(), nil)
@@ -42,11 +42,11 @@
 		+2 * sin60, +2 * cos60, 100,
 	}
 
-	draw.Copy(dst, image.Point{20, 30}, src, sr, nil)
+	draw.Copy(dst, image.Point{20, 30}, src, src.Bounds(), nil)
 	for i, q := range qs {
-		q.Scale(dst, image.Rect(200+10*i, 100*i, 600+10*i, 150+100*i), src, sr, nil)
+		q.Scale(dst, image.Rect(200+10*i, 100*i, 600+10*i, 150+100*i), src, src.Bounds(), nil)
 	}
-	draw.NearestNeighbor.Transform(dst, t, src, sr, nil)
+	draw.NearestNeighbor.Transform(dst, t, src, src.Bounds(), nil)
 
 	red := image.NewNRGBA(image.Rect(0, 0, 16, 16))
 	for y := 0; y < 16; y++ {
@@ -75,6 +75,31 @@
 		q.Transform(dst, t, red, red.Bounds(), opts)
 	}
 
+	dr := image.Rect(0, 0, 128, 128)
+	checkerboard := image.NewAlpha(dr)
+	for y := dr.Min.Y; y < dr.Max.Y; y++ {
+		for x := dr.Min.X; x < dr.Max.X; x++ {
+			if (x/20)%2 == (y/20)%2 {
+				checkerboard.SetAlpha(x, y, color.Alpha{0xff})
+			}
+		}
+	}
+	sr := image.Rect(0, 0, 16, 16)
+	circle := image.NewAlpha(sr)
+	for y := sr.Min.Y; y < sr.Max.Y; y++ {
+		for x := sr.Min.X; x < sr.Max.X; x++ {
+			dx, dy := x-10, y-8
+			if d := 32 * math.Sqrt(float64(dx*dx)+float64(dy*dy)); d < 0xff {
+				circle.SetAlpha(x, y, color.Alpha{0xff - uint8(d)})
+			}
+		}
+	}
+	cyan := image.NewUniform(color.RGBA{0x00, 0xff, 0xff, 0xff})
+	draw.NearestNeighbor.Scale(dst, dr, cyan, sr, &draw.Options{
+		DstMask: checkerboard,
+		SrcMask: circle,
+	})
+
 	// Change false to true to write the resultant image to disk.
 	if false {
 		fDst, err := os.Create("out.png")
diff --git a/draw/gen.go b/draw/gen.go
index 33830ac..8cb3e74 100644
--- a/draw/gen.go
+++ b/draw/gen.go
@@ -232,7 +232,11 @@
 		default:
 			return ";"
 		case "Image":
-			return "" +
+			s := ""
+			if d.sType == "image.Image" {
+				s = "srcMask, smp := opts.SrcMask, opts.SrcMaskP\n"
+			}
+			return s +
 				"dstMask, dmp := opts.DstMask, opts.DstMaskP\n" +
 				"dstColorRGBA64 := &color.RGBA64{}\n" +
 				"dstColor := color.Color(dstColorRGBA64)"
@@ -246,6 +250,14 @@
 			return "d := " + pixOffset("dst", "dr.Min.X+adr.Min.X", "dr.Min.Y+int(dy)", "*4", "*dst.Stride")
 		}
 
+	case "preKernelOuter":
+		switch d.sType {
+		default:
+			return ";"
+		case "image.Image":
+			return "srcMask, smp := opts.SrcMask, opts.SrcMaskP"
+		}
+
 	case "preKernelInner":
 		switch d.dType {
 		default:
@@ -547,6 +559,22 @@
 				"%sr%s, %sg%s, %sb%s, %sa%s := src.At(%s, %s).RGBA()\n",
 				lhs, tmp, lhs, tmp, lhs, tmp, lhs, tmp, args[0], args[1],
 			)
+			if d.dType == "" || d.dType == "Image" {
+				fmt.Fprintf(buf, ""+
+					"if srcMask != nil {\n"+
+					"	_, _, _, ma := srcMask.At(smp.X+%s, smp.Y+%s).RGBA()\n"+
+					"	%sr%s = %sr%s * ma / 0xffff\n"+
+					"	%sg%s = %sg%s * ma / 0xffff\n"+
+					"	%sb%s = %sb%s * ma / 0xffff\n"+
+					"	%sa%s = %sa%s * ma / 0xffff\n"+
+					"}\n",
+					args[0], args[1],
+					lhs, tmp, lhs, tmp,
+					lhs, tmp, lhs, tmp,
+					lhs, tmp, lhs, tmp,
+					lhs, tmp, lhs, tmp,
+				)
+			}
 		case "*image.Gray":
 			fmt.Fprintf(buf, ""+
 				"%si := %s\n"+
@@ -1218,6 +1246,7 @@
 	codeKernelScaleLeafX = `
 		func (z *kernelScaler) scaleX_$sTypeRN$sratio(tmp [][4]float64, src $sType, sr image.Rectangle, opts *Options) {
 			t := 0
+			$preKernelOuter
 			for y := int32(0); y < z.sh; y++ {
 				for _, s := range z.horizontal.sources {
 					var pr, pg, pb, pa float64 $tweakVarP
diff --git a/draw/impl.go b/draw/impl.go
index 69ba309..0bf27e9 100644
--- a/draw/impl.go
+++ b/draw/impl.go
@@ -529,6 +529,7 @@
 	dh2 := uint64(dr.Dy()) * 2
 	sw := uint64(sr.Dx())
 	sh := uint64(sr.Dy())
+	srcMask, smp := opts.SrcMask, opts.SrcMaskP
 	dstMask, dmp := opts.DstMask, opts.DstMaskP
 	dstColorRGBA64 := &color.RGBA64{}
 	dstColor := color.Color(dstColorRGBA64)
@@ -537,6 +538,13 @@
 		for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx++ {
 			sx := (2*uint64(dx) + 1) * sw / dw2
 			pr, pg, pb, pa := src.At(sr.Min.X+int(sx), sr.Min.Y+int(sy)).RGBA()
+			if srcMask != nil {
+				_, _, _, ma := srcMask.At(smp.X+sr.Min.X+int(sx), smp.Y+sr.Min.Y+int(sy)).RGBA()
+				pr = pr * ma / 0xffff
+				pg = pg * ma / 0xffff
+				pb = pb * ma / 0xffff
+				pa = pa * ma / 0xffff
+			}
 			qr, qg, qb, qa := dst.At(dr.Min.X+int(dx), dr.Min.Y+int(dy)).RGBA()
 			if dstMask != nil {
 				_, _, _, ma := dstMask.At(dmp.X+dr.Min.X+int(dx), dmp.Y+dr.Min.Y+int(dy)).RGBA()
@@ -560,6 +568,7 @@
 	dh2 := uint64(dr.Dy()) * 2
 	sw := uint64(sr.Dx())
 	sh := uint64(sr.Dy())
+	srcMask, smp := opts.SrcMask, opts.SrcMaskP
 	dstMask, dmp := opts.DstMask, opts.DstMaskP
 	dstColorRGBA64 := &color.RGBA64{}
 	dstColor := color.Color(dstColorRGBA64)
@@ -568,6 +577,13 @@
 		for dx := int32(adr.Min.X); dx < int32(adr.Max.X); dx++ {
 			sx := (2*uint64(dx) + 1) * sw / dw2
 			pr, pg, pb, pa := src.At(sr.Min.X+int(sx), sr.Min.Y+int(sy)).RGBA()
+			if srcMask != nil {
+				_, _, _, ma := srcMask.At(smp.X+sr.Min.X+int(sx), smp.Y+sr.Min.Y+int(sy)).RGBA()
+				pr = pr * ma / 0xffff
+				pg = pg * ma / 0xffff
+				pb = pb * ma / 0xffff
+				pa = pa * ma / 0xffff
+			}
 			if dstMask != nil {
 				qr, qg, qb, qa := dst.At(dr.Min.X+int(dx), dr.Min.Y+int(dy)).RGBA()
 				_, _, _, ma := dstMask.At(dmp.X+dr.Min.X+int(dx), dmp.Y+dr.Min.Y+int(dy)).RGBA()
@@ -930,6 +946,7 @@
 }
 
 func (nnInterpolator) transform_Image_Image_Over(dst Image, dr, adr image.Rectangle, d2s *f64.Aff3, src image.Image, sr image.Rectangle, bias image.Point, opts *Options) {
+	srcMask, smp := opts.SrcMask, opts.SrcMaskP
 	dstMask, dmp := opts.DstMask, opts.DstMaskP
 	dstColorRGBA64 := &color.RGBA64{}
 	dstColor := color.Color(dstColorRGBA64)
@@ -943,6 +960,13 @@
 				continue
 			}
 			pr, pg, pb, pa := src.At(sx0, sy0).RGBA()
+			if srcMask != nil {
+				_, _, _, ma := srcMask.At(smp.X+sx0, smp.Y+sy0).RGBA()
+				pr = pr * ma / 0xffff
+				pg = pg * ma / 0xffff
+				pb = pb * ma / 0xffff
+				pa = pa * ma / 0xffff
+			}
 			qr, qg, qb, qa := dst.At(dr.Min.X+int(dx), dr.Min.Y+int(dy)).RGBA()
 			if dstMask != nil {
 				_, _, _, ma := dstMask.At(dmp.X+dr.Min.X+int(dx), dmp.Y+dr.Min.Y+int(dy)).RGBA()
@@ -962,6 +986,7 @@
 }
 
 func (nnInterpolator) transform_Image_Image_Src(dst Image, dr, adr image.Rectangle, d2s *f64.Aff3, src image.Image, sr image.Rectangle, bias image.Point, opts *Options) {
+	srcMask, smp := opts.SrcMask, opts.SrcMaskP
 	dstMask, dmp := opts.DstMask, opts.DstMaskP
 	dstColorRGBA64 := &color.RGBA64{}
 	dstColor := color.Color(dstColorRGBA64)
@@ -975,6 +1000,13 @@
 				continue
 			}
 			pr, pg, pb, pa := src.At(sx0, sy0).RGBA()
+			if srcMask != nil {
+				_, _, _, ma := srcMask.At(smp.X+sx0, smp.Y+sy0).RGBA()
+				pr = pr * ma / 0xffff
+				pg = pg * ma / 0xffff
+				pb = pb * ma / 0xffff
+				pa = pa * ma / 0xffff
+			}
 			if dstMask != nil {
 				qr, qg, qb, qa := dst.At(dr.Min.X+int(dx), dr.Min.Y+int(dy)).RGBA()
 				_, _, _, ma := dstMask.At(dmp.X+dr.Min.X+int(dx), dmp.Y+dr.Min.Y+int(dy)).RGBA()
@@ -2522,6 +2554,7 @@
 	yscale := float64(sh) / float64(dr.Dy())
 	xscale := float64(sw) / float64(dr.Dx())
 	swMinus1, shMinus1 := sw-1, sh-1
+	srcMask, smp := opts.SrcMask, opts.SrcMaskP
 	dstMask, dmp := opts.DstMask, opts.DstMaskP
 	dstColorRGBA64 := &color.RGBA64{}
 	dstColor := color.Color(dstColorRGBA64)
@@ -2558,11 +2591,25 @@
 			}
 
 			s00ru, s00gu, s00bu, s00au := src.At(sr.Min.X+int(sx0), sr.Min.Y+int(sy0)).RGBA()
+			if srcMask != nil {
+				_, _, _, ma := srcMask.At(smp.X+sr.Min.X+int(sx0), smp.Y+sr.Min.Y+int(sy0)).RGBA()
+				s00ru = s00ru * ma / 0xffff
+				s00gu = s00gu * ma / 0xffff
+				s00bu = s00bu * ma / 0xffff
+				s00au = s00au * ma / 0xffff
+			}
 			s00r := float64(s00ru)
 			s00g := float64(s00gu)
 			s00b := float64(s00bu)
 			s00a := float64(s00au)
 			s10ru, s10gu, s10bu, s10au := src.At(sr.Min.X+int(sx1), sr.Min.Y+int(sy0)).RGBA()
+			if srcMask != nil {
+				_, _, _, ma := srcMask.At(smp.X+sr.Min.X+int(sx1), smp.Y+sr.Min.Y+int(sy0)).RGBA()
+				s10ru = s10ru * ma / 0xffff
+				s10gu = s10gu * ma / 0xffff
+				s10bu = s10bu * ma / 0xffff
+				s10au = s10au * ma / 0xffff
+			}
 			s10r := float64(s10ru)
 			s10g := float64(s10gu)
 			s10b := float64(s10bu)
@@ -2572,11 +2619,25 @@
 			s10b = xFrac1*s00b + xFrac0*s10b
 			s10a = xFrac1*s00a + xFrac0*s10a
 			s01ru, s01gu, s01bu, s01au := src.At(sr.Min.X+int(sx0), sr.Min.Y+int(sy1)).RGBA()
+			if srcMask != nil {
+				_, _, _, ma := srcMask.At(smp.X+sr.Min.X+int(sx0), smp.Y+sr.Min.Y+int(sy1)).RGBA()
+				s01ru = s01ru * ma / 0xffff
+				s01gu = s01gu * ma / 0xffff
+				s01bu = s01bu * ma / 0xffff
+				s01au = s01au * ma / 0xffff
+			}
 			s01r := float64(s01ru)
 			s01g := float64(s01gu)
 			s01b := float64(s01bu)
 			s01a := float64(s01au)
 			s11ru, s11gu, s11bu, s11au := src.At(sr.Min.X+int(sx1), sr.Min.Y+int(sy1)).RGBA()
+			if srcMask != nil {
+				_, _, _, ma := srcMask.At(smp.X+sr.Min.X+int(sx1), smp.Y+sr.Min.Y+int(sy1)).RGBA()
+				s11ru = s11ru * ma / 0xffff
+				s11gu = s11gu * ma / 0xffff
+				s11bu = s11bu * ma / 0xffff
+				s11au = s11au * ma / 0xffff
+			}
 			s11r := float64(s11ru)
 			s11g := float64(s11gu)
 			s11b := float64(s11bu)
@@ -2617,6 +2678,7 @@
 	yscale := float64(sh) / float64(dr.Dy())
 	xscale := float64(sw) / float64(dr.Dx())
 	swMinus1, shMinus1 := sw-1, sh-1
+	srcMask, smp := opts.SrcMask, opts.SrcMaskP
 	dstMask, dmp := opts.DstMask, opts.DstMaskP
 	dstColorRGBA64 := &color.RGBA64{}
 	dstColor := color.Color(dstColorRGBA64)
@@ -2653,11 +2715,25 @@
 			}
 
 			s00ru, s00gu, s00bu, s00au := src.At(sr.Min.X+int(sx0), sr.Min.Y+int(sy0)).RGBA()
+			if srcMask != nil {
+				_, _, _, ma := srcMask.At(smp.X+sr.Min.X+int(sx0), smp.Y+sr.Min.Y+int(sy0)).RGBA()
+				s00ru = s00ru * ma / 0xffff
+				s00gu = s00gu * ma / 0xffff
+				s00bu = s00bu * ma / 0xffff
+				s00au = s00au * ma / 0xffff
+			}
 			s00r := float64(s00ru)
 			s00g := float64(s00gu)
 			s00b := float64(s00bu)
 			s00a := float64(s00au)
 			s10ru, s10gu, s10bu, s10au := src.At(sr.Min.X+int(sx1), sr.Min.Y+int(sy0)).RGBA()
+			if srcMask != nil {
+				_, _, _, ma := srcMask.At(smp.X+sr.Min.X+int(sx1), smp.Y+sr.Min.Y+int(sy0)).RGBA()
+				s10ru = s10ru * ma / 0xffff
+				s10gu = s10gu * ma / 0xffff
+				s10bu = s10bu * ma / 0xffff
+				s10au = s10au * ma / 0xffff
+			}
 			s10r := float64(s10ru)
 			s10g := float64(s10gu)
 			s10b := float64(s10bu)
@@ -2667,11 +2743,25 @@
 			s10b = xFrac1*s00b + xFrac0*s10b
 			s10a = xFrac1*s00a + xFrac0*s10a
 			s01ru, s01gu, s01bu, s01au := src.At(sr.Min.X+int(sx0), sr.Min.Y+int(sy1)).RGBA()
+			if srcMask != nil {
+				_, _, _, ma := srcMask.At(smp.X+sr.Min.X+int(sx0), smp.Y+sr.Min.Y+int(sy1)).RGBA()
+				s01ru = s01ru * ma / 0xffff
+				s01gu = s01gu * ma / 0xffff
+				s01bu = s01bu * ma / 0xffff
+				s01au = s01au * ma / 0xffff
+			}
 			s01r := float64(s01ru)
 			s01g := float64(s01gu)
 			s01b := float64(s01bu)
 			s01a := float64(s01au)
 			s11ru, s11gu, s11bu, s11au := src.At(sr.Min.X+int(sx1), sr.Min.Y+int(sy1)).RGBA()
+			if srcMask != nil {
+				_, _, _, ma := srcMask.At(smp.X+sr.Min.X+int(sx1), smp.Y+sr.Min.Y+int(sy1)).RGBA()
+				s11ru = s11ru * ma / 0xffff
+				s11gu = s11gu * ma / 0xffff
+				s11bu = s11bu * ma / 0xffff
+				s11au = s11au * ma / 0xffff
+			}
 			s11r := float64(s11ru)
 			s11g := float64(s11gu)
 			s11b := float64(s11bu)
@@ -4053,6 +4143,7 @@
 }
 
 func (ablInterpolator) transform_Image_Image_Over(dst Image, dr, adr image.Rectangle, d2s *f64.Aff3, src image.Image, sr image.Rectangle, bias image.Point, opts *Options) {
+	srcMask, smp := opts.SrcMask, opts.SrcMaskP
 	dstMask, dmp := opts.DstMask, opts.DstMaskP
 	dstColorRGBA64 := &color.RGBA64{}
 	dstColor := color.Color(dstColorRGBA64)
@@ -4095,11 +4186,25 @@
 			}
 
 			s00ru, s00gu, s00bu, s00au := src.At(sx0, sy0).RGBA()
+			if srcMask != nil {
+				_, _, _, ma := srcMask.At(smp.X+sx0, smp.Y+sy0).RGBA()
+				s00ru = s00ru * ma / 0xffff
+				s00gu = s00gu * ma / 0xffff
+				s00bu = s00bu * ma / 0xffff
+				s00au = s00au * ma / 0xffff
+			}
 			s00r := float64(s00ru)
 			s00g := float64(s00gu)
 			s00b := float64(s00bu)
 			s00a := float64(s00au)
 			s10ru, s10gu, s10bu, s10au := src.At(sx1, sy0).RGBA()
+			if srcMask != nil {
+				_, _, _, ma := srcMask.At(smp.X+sx1, smp.Y+sy0).RGBA()
+				s10ru = s10ru * ma / 0xffff
+				s10gu = s10gu * ma / 0xffff
+				s10bu = s10bu * ma / 0xffff
+				s10au = s10au * ma / 0xffff
+			}
 			s10r := float64(s10ru)
 			s10g := float64(s10gu)
 			s10b := float64(s10bu)
@@ -4109,11 +4214,25 @@
 			s10b = xFrac1*s00b + xFrac0*s10b
 			s10a = xFrac1*s00a + xFrac0*s10a
 			s01ru, s01gu, s01bu, s01au := src.At(sx0, sy1).RGBA()
+			if srcMask != nil {
+				_, _, _, ma := srcMask.At(smp.X+sx0, smp.Y+sy1).RGBA()
+				s01ru = s01ru * ma / 0xffff
+				s01gu = s01gu * ma / 0xffff
+				s01bu = s01bu * ma / 0xffff
+				s01au = s01au * ma / 0xffff
+			}
 			s01r := float64(s01ru)
 			s01g := float64(s01gu)
 			s01b := float64(s01bu)
 			s01a := float64(s01au)
 			s11ru, s11gu, s11bu, s11au := src.At(sx1, sy1).RGBA()
+			if srcMask != nil {
+				_, _, _, ma := srcMask.At(smp.X+sx1, smp.Y+sy1).RGBA()
+				s11ru = s11ru * ma / 0xffff
+				s11gu = s11gu * ma / 0xffff
+				s11bu = s11bu * ma / 0xffff
+				s11au = s11au * ma / 0xffff
+			}
 			s11r := float64(s11ru)
 			s11g := float64(s11gu)
 			s11b := float64(s11bu)
@@ -4149,6 +4268,7 @@
 }
 
 func (ablInterpolator) transform_Image_Image_Src(dst Image, dr, adr image.Rectangle, d2s *f64.Aff3, src image.Image, sr image.Rectangle, bias image.Point, opts *Options) {
+	srcMask, smp := opts.SrcMask, opts.SrcMaskP
 	dstMask, dmp := opts.DstMask, opts.DstMaskP
 	dstColorRGBA64 := &color.RGBA64{}
 	dstColor := color.Color(dstColorRGBA64)
@@ -4191,11 +4311,25 @@
 			}
 
 			s00ru, s00gu, s00bu, s00au := src.At(sx0, sy0).RGBA()
+			if srcMask != nil {
+				_, _, _, ma := srcMask.At(smp.X+sx0, smp.Y+sy0).RGBA()
+				s00ru = s00ru * ma / 0xffff
+				s00gu = s00gu * ma / 0xffff
+				s00bu = s00bu * ma / 0xffff
+				s00au = s00au * ma / 0xffff
+			}
 			s00r := float64(s00ru)
 			s00g := float64(s00gu)
 			s00b := float64(s00bu)
 			s00a := float64(s00au)
 			s10ru, s10gu, s10bu, s10au := src.At(sx1, sy0).RGBA()
+			if srcMask != nil {
+				_, _, _, ma := srcMask.At(smp.X+sx1, smp.Y+sy0).RGBA()
+				s10ru = s10ru * ma / 0xffff
+				s10gu = s10gu * ma / 0xffff
+				s10bu = s10bu * ma / 0xffff
+				s10au = s10au * ma / 0xffff
+			}
 			s10r := float64(s10ru)
 			s10g := float64(s10gu)
 			s10b := float64(s10bu)
@@ -4205,11 +4339,25 @@
 			s10b = xFrac1*s00b + xFrac0*s10b
 			s10a = xFrac1*s00a + xFrac0*s10a
 			s01ru, s01gu, s01bu, s01au := src.At(sx0, sy1).RGBA()
+			if srcMask != nil {
+				_, _, _, ma := srcMask.At(smp.X+sx0, smp.Y+sy1).RGBA()
+				s01ru = s01ru * ma / 0xffff
+				s01gu = s01gu * ma / 0xffff
+				s01bu = s01bu * ma / 0xffff
+				s01au = s01au * ma / 0xffff
+			}
 			s01r := float64(s01ru)
 			s01g := float64(s01gu)
 			s01b := float64(s01bu)
 			s01a := float64(s01au)
 			s11ru, s11gu, s11bu, s11au := src.At(sx1, sy1).RGBA()
+			if srcMask != nil {
+				_, _, _, ma := srcMask.At(smp.X+sx1, smp.Y+sy1).RGBA()
+				s11ru = s11ru * ma / 0xffff
+				s11gu = s11gu * ma / 0xffff
+				s11bu = s11bu * ma / 0xffff
+				s11au = s11au * ma / 0xffff
+			}
 			s11r := float64(s11ru)
 			s11g := float64(s11gu)
 			s11b := float64(s11bu)
@@ -4729,11 +4877,19 @@
 
 func (z *kernelScaler) scaleX_Image(tmp [][4]float64, src image.Image, sr image.Rectangle, opts *Options) {
 	t := 0
+	srcMask, smp := opts.SrcMask, opts.SrcMaskP
 	for y := int32(0); y < z.sh; y++ {
 		for _, s := range z.horizontal.sources {
 			var pr, pg, pb, pa float64
 			for _, c := range z.horizontal.contribs[s.i:s.j] {
 				pru, pgu, pbu, pau := src.At(sr.Min.X+int(c.coord), sr.Min.Y+int(y)).RGBA()
+				if srcMask != nil {
+					_, _, _, ma := srcMask.At(smp.X+sr.Min.X+int(c.coord), smp.Y+sr.Min.Y+int(y)).RGBA()
+					pru = pru * ma / 0xffff
+					pgu = pgu * ma / 0xffff
+					pbu = pbu * ma / 0xffff
+					pau = pau * ma / 0xffff
+				}
 				pr += float64(pru) * c.weight
 				pg += float64(pgu) * c.weight
 				pb += float64(pbu) * c.weight
@@ -6224,6 +6380,7 @@
 	xWeights := make([]float64, 1+2*int(math.Ceil(xHalfWidth)))
 	yWeights := make([]float64, 1+2*int(math.Ceil(yHalfWidth)))
 
+	srcMask, smp := opts.SrcMask, opts.SrcMaskP
 	dstMask, dmp := opts.DstMask, opts.DstMaskP
 	dstColorRGBA64 := &color.RGBA64{}
 	dstColor := color.Color(dstColorRGBA64)
@@ -6293,6 +6450,13 @@
 					for kx := ix; kx < jx; kx++ {
 						if w := xWeights[kx-ix] * yWeight; w != 0 {
 							pru, pgu, pbu, pau := src.At(kx, ky).RGBA()
+							if srcMask != nil {
+								_, _, _, ma := srcMask.At(smp.X+kx, smp.Y+ky).RGBA()
+								pru = pru * ma / 0xffff
+								pgu = pgu * ma / 0xffff
+								pbu = pbu * ma / 0xffff
+								pau = pau * ma / 0xffff
+							}
 							pr += float64(pru) * w
 							pg += float64(pgu) * w
 							pb += float64(pbu) * w
@@ -6351,6 +6515,7 @@
 	xWeights := make([]float64, 1+2*int(math.Ceil(xHalfWidth)))
 	yWeights := make([]float64, 1+2*int(math.Ceil(yHalfWidth)))
 
+	srcMask, smp := opts.SrcMask, opts.SrcMaskP
 	dstMask, dmp := opts.DstMask, opts.DstMaskP
 	dstColorRGBA64 := &color.RGBA64{}
 	dstColor := color.Color(dstColorRGBA64)
@@ -6420,6 +6585,13 @@
 					for kx := ix; kx < jx; kx++ {
 						if w := xWeights[kx-ix] * yWeight; w != 0 {
 							pru, pgu, pbu, pau := src.At(kx, ky).RGBA()
+							if srcMask != nil {
+								_, _, _, ma := srcMask.At(smp.X+kx, smp.Y+ky).RGBA()
+								pru = pru * ma / 0xffff
+								pgu = pgu * ma / 0xffff
+								pbu = pbu * ma / 0xffff
+								pau = pau * ma / 0xffff
+							}
 							pr += float64(pru) * w
 							pg += float64(pgu) * w
 							pb += float64(pbu) * w
diff --git a/draw/scale.go b/draw/scale.go
index db67f88..07cd20b 100644
--- a/draw/scale.go
+++ b/draw/scale.go
@@ -25,8 +25,7 @@
 	}
 	dr := sr.Add(dp.Sub(sr.Min))
 	if o.DstMask == nil {
-		// TODO: honor o.SrcMask.
-		DrawMask(dst, dr, src, sr.Min, nil, image.Point{}, o.Op)
+		DrawMask(dst, dr, src, sr.Min, o.SrcMask, o.SrcMaskP.Add(sr.Min), o.Op)
 	} else {
 		NearestNeighbor.Scale(dst, dr, src, sr, opts)
 	}
@@ -93,7 +92,6 @@
 	DstMaskP image.Point
 	SrcMask  image.Image
 	SrcMaskP image.Point
-	// TODO: actually implement SrcMask.
 
 	// TODO: a smooth vs sharp edges option, for arbitrary rotations?
 }
diff --git a/draw/scale_test.go b/draw/scale_test.go
index 233c70e..d9265a1 100644
--- a/draw/scale_test.go
+++ b/draw/scale_test.go
@@ -316,6 +316,41 @@
 	}
 }
 
+func TestSrcMask(t *testing.T) {
+	srcMask := image.NewRGBA(image.Rect(0, 0, 23, 1))
+	srcMask.SetRGBA(19, 0, color.RGBA{0x00, 0x00, 0x00, 0x7f})
+	srcMask.SetRGBA(20, 0, color.RGBA{0x00, 0x00, 0x00, 0xff})
+	srcMask.SetRGBA(21, 0, color.RGBA{0x00, 0x00, 0x00, 0x3f})
+	srcMask.SetRGBA(22, 0, color.RGBA{0x00, 0x00, 0x00, 0x00})
+	red := image.NewUniform(color.RGBA{0xff, 0x00, 0x00, 0xff})
+	blue := image.NewUniform(color.RGBA{0x00, 0x00, 0xff, 0xff})
+	dst := image.NewRGBA(image.Rect(0, 0, 6, 1))
+	Copy(dst, image.Point{}, blue, dst.Bounds(), nil)
+	NearestNeighbor.Scale(dst, dst.Bounds(), red, image.Rect(0, 0, 3, 1), &Options{
+		SrcMask:  srcMask,
+		SrcMaskP: image.Point{20, 0},
+	})
+	got := [6]color.RGBA{
+		dst.RGBAAt(0, 0),
+		dst.RGBAAt(1, 0),
+		dst.RGBAAt(2, 0),
+		dst.RGBAAt(3, 0),
+		dst.RGBAAt(4, 0),
+		dst.RGBAAt(5, 0),
+	}
+	want := [6]color.RGBA{
+		{0xff, 0x00, 0x00, 0xff},
+		{0xff, 0x00, 0x00, 0xff},
+		{0x3f, 0x00, 0xc0, 0xff},
+		{0x3f, 0x00, 0xc0, 0xff},
+		{0x00, 0x00, 0xff, 0xff},
+		{0x00, 0x00, 0xff, 0xff},
+	}
+	if got != want {
+		t.Errorf("\ngot  %v\nwant %v", got, want)
+	}
+}
+
 func TestDstMask(t *testing.T) {
 	dstMask := image.NewRGBA(image.Rect(0, 0, 23, 1))
 	dstMask.SetRGBA(19, 0, color.RGBA{0x00, 0x00, 0x00, 0x7f})