From 8642173156e132f873e9d6d985981b3306bd14a3 Mon Sep 17 00:00:00 2001 From: Nigel Tao Date: Fri, 24 Apr 2015 14:45:24 +1000 Subject: [PATCH] draw: implement srcMask. Change-Id: Ibf710521f466847afaf2d005dc8a2bb817169298 Reviewed-on: https://go-review.googlesource.com/9276 Reviewed-by: Rob Pike --- draw/example_test.go | 33 ++++++++- draw/gen.go | 31 +++++++- draw/impl.go | 172 +++++++++++++++++++++++++++++++++++++++++++ draw/scale.go | 4 +- draw/scale_test.go | 35 +++++++++ 5 files changed, 267 insertions(+), 8 deletions(-) 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 @@ import ( "image/color" "image/png" "log" + "math" "os" "golang.org/x/image/draw" @@ -27,7 +28,6 @@ func ExampleDraw() { 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 @@ func ExampleDraw() { +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 @@ func ExampleDraw() { 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 @@ func expnDollar(prefix, dollar, suffix string, d *data) string { 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 @@ func expnDollar(prefix, dollar, suffix string, d *data) string { 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 @@ func expnDollar(prefix, dollar, suffix string, d *data) string { "%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 @@ const ( 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 @@ func (nnInterpolator) scale_Image_Image_Over(dst Image, dr, adr image.Rectangle, 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 @@ func (nnInterpolator) scale_Image_Image_Over(dst Image, dr, adr image.Rectangle, 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 @@ func (nnInterpolator) scale_Image_Image_Src(dst Image, dr, adr image.Rectangle, 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 @@ func (nnInterpolator) scale_Image_Image_Src(dst Image, dr, adr image.Rectangle, 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_RGBA_Image_Src(dst *image.RGBA, dr, adr image.Re } 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 @@ func (nnInterpolator) transform_Image_Image_Over(dst Image, dr, adr image.Rectan 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_Over(dst Image, dr, adr image.Rectan } 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 @@ func (nnInterpolator) transform_Image_Image_Src(dst Image, dr, adr image.Rectang 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 @@ func (ablInterpolator) scale_Image_Image_Over(dst Image, dr, adr image.Rectangle 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 @@ func (ablInterpolator) scale_Image_Image_Over(dst Image, dr, adr image.Rectangle } 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 @@ func (ablInterpolator) scale_Image_Image_Over(dst Image, dr, adr image.Rectangle 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 @@ func (ablInterpolator) scale_Image_Image_Src(dst Image, dr, adr image.Rectangle, 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 @@ func (ablInterpolator) scale_Image_Image_Src(dst Image, dr, adr image.Rectangle, } 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 @@ func (ablInterpolator) scale_Image_Image_Src(dst Image, dr, adr image.Rectangle, 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_RGBA_Image_Src(dst *image.RGBA, dr, adr image.R } 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 @@ func (ablInterpolator) transform_Image_Image_Over(dst Image, dr, adr image.Recta } 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 @@ func (ablInterpolator) transform_Image_Image_Over(dst Image, dr, adr image.Recta 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_Over(dst Image, dr, adr image.Recta } 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 @@ func (ablInterpolator) transform_Image_Image_Src(dst Image, dr, adr image.Rectan } 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 @@ func (ablInterpolator) transform_Image_Image_Src(dst Image, dr, adr image.Rectan 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_YCbCr440(tmp [][4]float64, src *image.YCbCr, sr im 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 @@ func (q *Kernel) transform_Image_Image_Over(dst Image, dr, adr image.Rectangle, 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 @@ func (q *Kernel) transform_Image_Image_Over(dst Image, dr, adr image.Rectangle, 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 @@ func (q *Kernel) transform_Image_Image_Src(dst Image, dr, adr image.Rectangle, d 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 @@ func (q *Kernel) transform_Image_Image_Src(dst Image, dr, adr image.Rectangle, d 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 @@ func Copy(dst Image, dp image.Point, src image.Image, sr image.Rectangle, opts * } 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 @@ type Options struct { 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 TestSrcTranslationInvariance(t *testing.T) { } } +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})