draw: add a fast path for an image.Rectangle DstMask.

Change-Id: Id5227b9d217b56a342bc1ffc735dababa8a9e3e9
Reviewed-on: https://go-review.googlesource.com/9233
Reviewed-by: Rob Pike <r@golang.org>
This commit is contained in:
Nigel Tao 2015-04-22 15:24:07 +10:00
parent b621bdc118
commit 24b0de15f1
4 changed files with 78 additions and 32 deletions

View File

@ -854,12 +854,14 @@ const (
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().
// adr is the affected destination pixels.
adr := dst.Bounds().Intersect(dr)
adr, o.DstMask = clipAffectedDestRect(adr, o.DstMask, o.DstMaskP)
if adr.Empty() || sr.Empty() {
return
}
// Make adr relative to dr.Min.
adr = adr.Sub(dr.Min)
if o.Op == Over && o.SrcMask == nil && opaque(src) {
o.Op = Src
}
@ -892,7 +894,7 @@ const (
dr := transformRect(s2d, &sr)
// adr is the affected destination pixels.
adr := dst.Bounds().Intersect(dr)
// TODO: clip adr to o.DstMask.Bounds().
adr, o.DstMask = clipAffectedDestRect(adr, o.DstMask, o.DstMaskP)
if adr.Empty() || sr.Empty() {
return
}
@ -1097,12 +1099,14 @@ const (
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().
// adr is the affected destination pixels.
adr := dst.Bounds().Intersect(dr)
adr, o.DstMask = clipAffectedDestRect(adr, o.DstMask, o.DstMaskP)
if adr.Empty() || sr.Empty() {
return
}
// Make adr relative to dr.Min.
adr = adr.Sub(dr.Min)
if o.Op == Over && o.SrcMask == nil && opaque(src) {
o.Op = Src
}
@ -1156,7 +1160,7 @@ const (
dr := transformRect(s2d, &sr)
// adr is the affected destination pixels.
adr := dst.Bounds().Intersect(dr)
// TODO: clip adr to o.DstMask.Bounds().
adr, o.DstMask = clipAffectedDestRect(adr, o.DstMask, o.DstMaskP)
if adr.Empty() || sr.Empty() {
return
}

View File

@ -16,12 +16,14 @@ func (z nnInterpolator) Scale(dst Image, dr image.Rectangle, src image.Image, sr
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().
// adr is the affected destination pixels.
adr := dst.Bounds().Intersect(dr)
adr, o.DstMask = clipAffectedDestRect(adr, o.DstMask, o.DstMaskP)
if adr.Empty() || sr.Empty() {
return
}
// Make adr relative to dr.Min.
adr = adr.Sub(dr.Min)
if o.Op == Over && o.SrcMask == nil && opaque(src) {
o.Op = Src
}
@ -104,7 +106,7 @@ func (z nnInterpolator) Transform(dst Image, s2d *f64.Aff3, src image.Image, sr
dr := transformRect(s2d, &sr)
// adr is the affected destination pixels.
adr := dst.Bounds().Intersect(dr)
// TODO: clip adr to o.DstMask.Bounds().
adr, o.DstMask = clipAffectedDestRect(adr, o.DstMask, o.DstMaskP)
if adr.Empty() || sr.Empty() {
return
}
@ -1003,12 +1005,14 @@ func (z ablInterpolator) Scale(dst Image, dr image.Rectangle, src image.Image, s
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().
// adr is the affected destination pixels.
adr := dst.Bounds().Intersect(dr)
adr, o.DstMask = clipAffectedDestRect(adr, o.DstMask, o.DstMaskP)
if adr.Empty() || sr.Empty() {
return
}
// Make adr relative to dr.Min.
adr = adr.Sub(dr.Min)
if o.Op == Over && o.SrcMask == nil && opaque(src) {
o.Op = Src
}
@ -1091,7 +1095,7 @@ func (z ablInterpolator) Transform(dst Image, s2d *f64.Aff3, src image.Image, sr
dr := transformRect(s2d, &sr)
// adr is the affected destination pixels.
adr := dst.Bounds().Intersect(dr)
// TODO: clip adr to o.DstMask.Bounds().
adr, o.DstMask = clipAffectedDestRect(adr, o.DstMask, o.DstMaskP)
if adr.Empty() || sr.Empty() {
return
}
@ -4257,12 +4261,14 @@ func (z *kernelScaler) Scale(dst Image, dr image.Rectangle, src image.Image, sr
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().
// adr is the affected destination pixels.
adr := dst.Bounds().Intersect(dr)
adr, o.DstMask = clipAffectedDestRect(adr, o.DstMask, o.DstMaskP)
if adr.Empty() || sr.Empty() {
return
}
// Make adr relative to dr.Min.
adr = adr.Sub(dr.Min)
if o.Op == Over && o.SrcMask == nil && opaque(src) {
o.Op = Src
}
@ -4353,7 +4359,7 @@ func (q *Kernel) Transform(dst Image, s2d *f64.Aff3, src image.Image, sr image.R
dr := transformRect(s2d, &sr)
// adr is the affected destination pixels.
adr := dst.Bounds().Intersect(dr)
// TODO: clip adr to o.DstMask.Bounds().
adr, o.DstMask = clipAffectedDestRect(adr, o.DstMask, o.DstMaskP)
if adr.Empty() || sr.Empty() {
return
}

View File

@ -408,6 +408,17 @@ func transformRect(s2d *f64.Aff3, sr *image.Rectangle) (dr image.Rectangle) {
return dr
}
func clipAffectedDestRect(adr image.Rectangle, dstMask image.Image, dstMaskP image.Point) (image.Rectangle, image.Image) {
if dstMask == nil {
return adr, nil
}
if r, ok := dstMask.(image.Rectangle); ok {
return adr.Intersect(r.Sub(dstMaskP)), nil
}
// TODO: clip to dstMask.Bounds() if the color model implies that out-of-bounds means 0 alpha?
return adr, dstMask
}
func transform_Uniform(dst Image, dr, adr image.Rectangle, d2s *f64.Aff3, src *image.Uniform, sr image.Rectangle, bias image.Point, op Op) {
switch op {
case Over:

View File

@ -373,30 +373,55 @@ func TestRectDstMask(t *testing.T) {
}
}
mk := func(q Transformer, dstMask image.Image) *image.RGBA {
mk := func(q Transformer, dstMask image.Image, dstMaskP image.Point) *image.RGBA {
m := image.NewRGBA(bounds)
Copy(m, bounds.Min, dstOutside, bounds, nil)
q.Transform(m, m00, src, src.Bounds(), &Options{DstMask: dstMask})
q.Transform(m, m00, src, src.Bounds(), &Options{
DstMask: dstMask,
DstMaskP: dstMaskP,
})
return m
}
rect := image.Rect(20, 10, 30, 40)
qs := []Interpolator{
NearestNeighbor,
ApproxBiLinear,
CatmullRom,
}
dstMaskPs := []image.Point{
{0, 0},
{5, 7},
{-3, 0},
}
rect := image.Rect(10, 10, 30, 40)
for _, q := range qs {
dstInside := mk(q, nil)
dst := mk(q, rect)
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
for x := bounds.Min.X; x < bounds.Max.X; x++ {
which := dstOutside
if (image.Point{x, y}).In(rect) {
which = dstInside
for _, dstMaskP := range dstMaskPs {
dstInside := mk(q, nil, image.Point{})
for _, wrap := range []bool{false, true} {
dstMask := image.Image(rect)
if wrap {
dstMask = srcWrapper{dstMask}
}
if got, want := dst.RGBAAt(x, y), which.RGBAAt(x, y); got != want {
t.Errorf("x=%3d y=%3d: got %v, want %v", x, y, got, want)
dst := mk(q, dstMask, dstMaskP)
nError := 0
loop:
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
for x := bounds.Min.X; x < bounds.Max.X; x++ {
which := dstOutside
if (image.Point{x, y}).Add(dstMaskP).In(rect) {
which = dstInside
}
if got, want := dst.RGBAAt(x, y), which.RGBAAt(x, y); got != want {
if nError == 10 {
t.Errorf("q=%T dmp=%v wrap=%v: ...and more errors", q, dstMaskP, wrap)
break loop
}
nError++
t.Errorf("q=%T dmp=%v wrap=%v: x=%3d y=%3d: got %v, want %v",
q, dstMaskP, wrap, x, y, got, want)
}
}
}
}
}