font: add bounds measurement methods
Fixes golang/go#18297
Change-Id: Ib873b18c9bc5cac0d820c6129d9a28cb44f2a8da
Reviewed-on: https://go-review.googlesource.com/34514
Reviewed-by: Nigel Tao <nigeltao@golang.org>
diff --git a/font/font.go b/font/font.go
index 36a1638..d523baf 100644
--- a/font/font.go
+++ b/font/font.go
@@ -169,6 +169,26 @@
}
}
+// BoundBytes returns the bounding box of s, drawn at the drawer dot, as well as
+// the advance.
+//
+// It is equivalent to BoundBytes(string(s)) but may be more efficient.
+func (d *Drawer) BoundBytes(s []byte) (bounds fixed.Rectangle26_6, advance fixed.Int26_6) {
+ bounds, advance = BoundBytes(d.Face, s)
+ bounds.Min = bounds.Min.Add(d.Dot)
+ bounds.Max = bounds.Max.Add(d.Dot)
+ return
+}
+
+// BoundString returns the bounding box of s, drawn at the drawer dot, as well
+// as the advance.
+func (d *Drawer) BoundString(s string) (bounds fixed.Rectangle26_6, advance fixed.Int26_6) {
+ bounds, advance = BoundString(d.Face, s)
+ bounds.Min = bounds.Min.Add(d.Dot)
+ bounds.Max = bounds.Max.Add(d.Dot)
+ return
+}
+
// MeasureBytes returns how far dot would advance by drawing s.
//
// It is equivalent to MeasureString(string(s)) but may be more efficient.
@@ -181,6 +201,73 @@
return MeasureString(d.Face, s)
}
+// BoundBytes returns the bounding box of s with f, drawn at a dot equal to the
+// origin, as well as the advance.
+//
+// It is equivalent to BoundString(string(s)) but may be more efficient.
+func BoundBytes(f Face, s []byte) (bounds fixed.Rectangle26_6, advance fixed.Int26_6) {
+ prevC := rune(-1)
+ for len(s) > 0 {
+ c, size := utf8.DecodeRune(s)
+ s = s[size:]
+ if prevC >= 0 {
+ advance += f.Kern(prevC, c)
+ }
+ b, a, ok := f.GlyphBounds(c)
+ if !ok {
+ // TODO: is falling back on the U+FFFD glyph the responsibility of
+ // the Drawer or the Face?
+ // TODO: set prevC = '\ufffd'?
+ continue
+ }
+ bounds = grow(bounds, b, advance)
+ advance += a
+ prevC = c
+ }
+ return
+}
+
+// BoundString returns the bounding box of s with f, drawn at a dot equal to the
+// origin, as well as the advance.
+func BoundString(f Face, s string) (bounds fixed.Rectangle26_6, advance fixed.Int26_6) {
+ prevC := rune(-1)
+ for _, c := range s {
+ if prevC >= 0 {
+ advance += f.Kern(prevC, c)
+ }
+ b, a, ok := f.GlyphBounds(c)
+ if !ok {
+ // TODO: is falling back on the U+FFFD glyph the responsibility of
+ // the Drawer or the Face?
+ // TODO: set prevC = '\ufffd'?
+ continue
+ }
+ bounds = grow(bounds, b, advance)
+ advance += a
+ prevC = c
+ }
+ return
+}
+
+// grow returns the smallest rectangle containing both b and b2+shift.
+func grow(b, b2 fixed.Rectangle26_6, shift fixed.Int26_6) fixed.Rectangle26_6 {
+ x := b2.Min.X + shift
+ if b.Min.X > x {
+ b.Min.X = x
+ }
+ if b.Min.Y > b2.Min.Y {
+ b.Min.Y = b2.Min.Y
+ }
+ x = b2.Max.X + shift
+ if b.Max.X < x {
+ b.Max.X = x
+ }
+ if b.Max.Y < b2.Max.Y {
+ b.Max.Y = b2.Max.Y
+ }
+ return b
+}
+
// MeasureBytes returns how far dot would advance by drawing s with f.
//
// It is equivalent to MeasureString(string(s)) but may be more efficient.