Commit 2deee293 authored by Nigel Tao's avatar Nigel Tao

exp/draw: clip destination rectangle to the image bounds.

image: introduce Intersect and Union rectangle methods.

R=r, rog, nigeltao
CC=golang-dev
https://golang.org/cl/2115043
parent 6c8b8527
......@@ -60,9 +60,10 @@ func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mas
if r.Dy() > dy {
r.Max.Y = r.Min.Y + dy
}
// TODO(nigeltao): Clip r to dst's bounding box, and handle the case when sp or mp has negative X or Y.
// TODO(nigeltao): Ensure that r is well formed, i.e. r.Max.X >= r.Min.X and likewise for Y.
r = r.Intersect(dst.Bounds())
if r.Empty() {
return
}
// Fast paths for special cases. If none of them apply, then we fall back to a general but slow implementation.
if dst0, ok := dst.(*image.RGBA); ok {
......
......@@ -37,6 +37,9 @@ func Pt(X, Y int) Point {
}
// A Rectangle contains the points with Min.X <= X < Max.X, Min.Y <= Y < Max.Y.
// It is well-formed if Min.X <= Max.X and likewise for Y. Points are always
// well-formed. A rectangle's methods always return well-formed outputs for
// well-formed inputs.
type Rectangle struct {
Min, Max Point
}
......@@ -72,12 +75,63 @@ func (r Rectangle) Sub(p Point) Rectangle {
}
}
// Inset returns the rectangle r inset by n, which may be negative.
// Inset returns the rectangle r inset by n, which may be negative. If either
// of r's dimensions is less than 2*n then an empty rectangle near the center
// of r will be returned.
func (r Rectangle) Inset(n int) Rectangle {
return Rectangle{
Point{r.Min.X + n, r.Min.Y + n},
Point{r.Max.X - n, r.Max.Y - n},
if r.Dx() < 2*n {
r.Min.X = (r.Min.X + r.Max.X) / 2
r.Max.X = r.Min.X
} else {
r.Min.X += n
r.Max.X -= n
}
if r.Dy() < 2*n {
r.Min.Y = (r.Min.Y + r.Max.Y) / 2
r.Max.Y = r.Min.Y
} else {
r.Min.Y += n
r.Max.Y -= n
}
return r
}
// Intersect returns the largest rectangle contained by both r and s. If the
// two rectangles do not overlap then the zero rectangle will be returned.
func (r Rectangle) Intersect(s Rectangle) Rectangle {
if r.Min.X < s.Min.X {
r.Min.X = s.Min.X
}
if r.Min.Y < s.Min.Y {
r.Min.Y = s.Min.Y
}
if r.Max.X > s.Max.X {
r.Max.X = s.Max.X
}
if r.Max.Y > s.Max.Y {
r.Max.Y = s.Max.Y
}
if r.Min.X > r.Max.X || r.Min.Y > r.Max.Y {
return ZR
}
return r
}
// Union returns the smallest rectangle that contains both r and s.
func (r Rectangle) Union(s Rectangle) Rectangle {
if r.Min.X > s.Min.X {
r.Min.X = s.Min.X
}
if r.Min.Y > s.Min.Y {
r.Min.Y = s.Min.Y
}
if r.Max.X < s.Max.X {
r.Max.X = s.Max.X
}
if r.Max.Y < s.Max.Y {
r.Max.Y = s.Max.Y
}
return r
}
// Empty returns whether the rectangle contains no points.
......@@ -103,9 +157,8 @@ func (r Rectangle) Contains(p Point) bool {
p.Y >= r.Min.Y && p.Y < r.Max.Y
}
// Canon returns the canonical version of r. The returned rectangle has
// minimum and maximum coordinates swapped if necessary so that Min.X <= Max.X
// and Min.Y <= Max.Y.
// Canon returns the canonical version of r. The returned rectangle has minimum
// and maximum coordinates swapped if necessary so that it is well-formed.
func (r Rectangle) Canon() Rectangle {
if r.Max.X < r.Min.X {
r.Min.X, r.Max.X = r.Max.X, r.Min.X
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment