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

View File

@ -16,12 +16,14 @@ func (z nnInterpolator) Scale(dst Image, dr image.Rectangle, src image.Image, sr
o = *opts o = *opts
} }
// adr is the affected destination pixels, relative to dr.Min. // adr is the affected destination pixels.
adr := dst.Bounds().Intersect(dr).Sub(dr.Min) 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() { if adr.Empty() || sr.Empty() {
return return
} }
// Make adr relative to dr.Min.
adr = adr.Sub(dr.Min)
if o.Op == Over && o.SrcMask == nil && opaque(src) { if o.Op == Over && o.SrcMask == nil && opaque(src) {
o.Op = 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) dr := transformRect(s2d, &sr)
// adr is the affected destination pixels. // adr is the affected destination pixels.
adr := dst.Bounds().Intersect(dr) 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() { if adr.Empty() || sr.Empty() {
return return
} }
@ -1003,12 +1005,14 @@ func (z ablInterpolator) Scale(dst Image, dr image.Rectangle, src image.Image, s
o = *opts o = *opts
} }
// adr is the affected destination pixels, relative to dr.Min. // adr is the affected destination pixels.
adr := dst.Bounds().Intersect(dr).Sub(dr.Min) 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() { if adr.Empty() || sr.Empty() {
return return
} }
// Make adr relative to dr.Min.
adr = adr.Sub(dr.Min)
if o.Op == Over && o.SrcMask == nil && opaque(src) { if o.Op == Over && o.SrcMask == nil && opaque(src) {
o.Op = 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) dr := transformRect(s2d, &sr)
// adr is the affected destination pixels. // adr is the affected destination pixels.
adr := dst.Bounds().Intersect(dr) 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() { if adr.Empty() || sr.Empty() {
return return
} }
@ -4257,12 +4261,14 @@ func (z *kernelScaler) Scale(dst Image, dr image.Rectangle, src image.Image, sr
o = *opts o = *opts
} }
// adr is the affected destination pixels, relative to dr.Min. // adr is the affected destination pixels.
adr := dst.Bounds().Intersect(dr).Sub(dr.Min) 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() { if adr.Empty() || sr.Empty() {
return return
} }
// Make adr relative to dr.Min.
adr = adr.Sub(dr.Min)
if o.Op == Over && o.SrcMask == nil && opaque(src) { if o.Op == Over && o.SrcMask == nil && opaque(src) {
o.Op = 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) dr := transformRect(s2d, &sr)
// adr is the affected destination pixels. // adr is the affected destination pixels.
adr := dst.Bounds().Intersect(dr) 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() { if adr.Empty() || sr.Empty() {
return return
} }

View File

@ -408,6 +408,17 @@ func transformRect(s2d *f64.Aff3, sr *image.Rectangle) (dr image.Rectangle) {
return dr 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) { 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 { switch op {
case Over: 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) m := image.NewRGBA(bounds)
Copy(m, bounds.Min, dstOutside, bounds, nil) 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 return m
} }
rect := image.Rect(20, 10, 30, 40)
qs := []Interpolator{ qs := []Interpolator{
NearestNeighbor, NearestNeighbor,
ApproxBiLinear, ApproxBiLinear,
CatmullRom, CatmullRom,
} }
dstMaskPs := []image.Point{
{0, 0},
{5, 7},
{-3, 0},
}
rect := image.Rect(10, 10, 30, 40)
for _, q := range qs { for _, q := range qs {
dstInside := mk(q, nil) for _, dstMaskP := range dstMaskPs {
dst := mk(q, rect) dstInside := mk(q, nil, image.Point{})
for y := bounds.Min.Y; y < bounds.Max.Y; y++ { for _, wrap := range []bool{false, true} {
for x := bounds.Min.X; x < bounds.Max.X; x++ { dstMask := image.Image(rect)
which := dstOutside if wrap {
if (image.Point{x, y}).In(rect) { dstMask = srcWrapper{dstMask}
which = dstInside
} }
if got, want := dst.RGBAAt(x, y), which.RGBAAt(x, y); got != want { dst := mk(q, dstMask, dstMaskP)
t.Errorf("x=%3d y=%3d: got %v, want %v", x, y, got, want)
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)
}
}
} }
} }
} }