aboutsummaryrefslogtreecommitdiffstats
path: root/gui/text
diff options
context:
space:
mode:
Diffstat (limited to 'gui/text')
-rw-r--r--gui/text/concurrent_face.go51
-rw-r--r--gui/text/text.go78
2 files changed, 129 insertions, 0 deletions
diff --git a/gui/text/concurrent_face.go b/gui/text/concurrent_face.go
new file mode 100644
index 0000000..c865251
--- /dev/null
+++ b/gui/text/concurrent_face.go
@@ -0,0 +1,51 @@
+package text
+
+import (
+ "image"
+ "sync"
+
+ "golang.org/x/image/font"
+ "golang.org/x/image/math/fixed"
+)
+
+type concurrentFace struct {
+ mu sync.Mutex
+ face font.Face
+}
+
+func (cf *concurrentFace) Close() error {
+ cf.mu.Lock()
+ defer cf.mu.Unlock()
+ return cf.face.Close()
+}
+
+func (cf *concurrentFace) Glyph(dot fixed.Point26_6, r rune) (
+ dr image.Rectangle, mask image.Image, maskp image.Point, advance fixed.Int26_6, ok bool) {
+ cf.mu.Lock()
+ defer cf.mu.Unlock()
+ return cf.face.Glyph(dot, r)
+}
+
+func (cf *concurrentFace) GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) {
+ cf.mu.Lock()
+ defer cf.mu.Unlock()
+ return cf.face.GlyphBounds(r)
+}
+
+func (cf *concurrentFace) GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool) {
+ cf.mu.Lock()
+ defer cf.mu.Unlock()
+ return cf.face.GlyphAdvance(r)
+}
+
+func (cf *concurrentFace) Kern(r0, r1 rune) fixed.Int26_6 {
+ cf.mu.Lock()
+ defer cf.mu.Unlock()
+ return cf.face.Kern(r0, r1)
+}
+
+func (cf *concurrentFace) Metrics() font.Metrics {
+ cf.mu.Lock()
+ defer cf.mu.Unlock()
+ return cf.face.Metrics()
+}
diff --git a/gui/text/text.go b/gui/text/text.go
new file mode 100644
index 0000000..dfe60e1
--- /dev/null
+++ b/gui/text/text.go
@@ -0,0 +1,78 @@
+package text
+
+import (
+ "log"
+ "sync"
+
+ "image"
+ "image/color"
+ "image/draw"
+
+ "golang.org/x/image/font"
+ "golang.org/x/image/font/gofont/goregular"
+ "golang.org/x/image/font/opentype"
+ "golang.org/x/image/math/fixed"
+)
+
+var (
+ FONT = goregular.TTF
+ FONT_SIZE = 15
+ DPI = 72
+ PAD = 3
+)
+
+var face *concurrentFace
+
+func init() {
+ fnt, err := opentype.Parse(FONT)
+ if err != nil {
+ log.Fatal(err)
+ }
+ fce, err := opentype.NewFace(fnt, &opentype.FaceOptions{
+ Size: float64(FONT_SIZE),
+ DPI: float64(DPI),
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+ face = &concurrentFace{sync.Mutex{}, fce}
+}
+
+func Size(text string) image.Point {
+ bounds := textBounds([]byte(text), font.Drawer{Face: face})
+ return image.Point{bounds.Max.X - bounds.Min.X + 2*PAD, bounds.Max.Y - bounds.Min.Y + 2*PAD}
+}
+
+func Draw(text []byte, dst draw.Image, r image.Rectangle, fg, bg color.Color) {
+ drawer := font.Drawer{
+ Src: &image.Uniform{fg},
+ Face: face,
+ Dot: fixed.P(0, 0),
+ }
+
+ // background
+ draw.Draw(dst, r, &image.Uniform{bg}, image.ZP, draw.Src)
+
+ // text image
+ bounds := textBounds(text, drawer)
+ textImg := image.NewRGBA(bounds)
+ draw.Draw(textImg, bounds, &image.Uniform{bg}, image.ZP, draw.Src)
+ drawer.Dst = textImg
+ drawer.DrawBytes(text)
+
+ // draw text image over background
+ leftCentre := image.Pt(bounds.Min.X, (bounds.Min.Y+bounds.Max.Y)/2)
+ target := image.Pt(r.Max.X-bounds.Max.X-PAD, (r.Min.Y+r.Max.Y)/2)
+ delta := target.Sub(leftCentre)
+ draw.Draw(dst, bounds.Add(delta).Intersect(r), drawer.Dst, bounds.Min, draw.Src)
+}
+
+func textBounds(text []byte, drawer font.Drawer) image.Rectangle {
+ b, _ := drawer.BoundBytes(text)
+ return image.Rect(
+ b.Min.X.Floor(),
+ b.Min.Y.Floor(),
+ b.Max.X.Ceil(),
+ b.Max.Y.Ceil(),
+ )
+}