-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathwriter.go
116 lines (88 loc) · 2.47 KB
/
writer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
package writer
import (
"image"
"image/draw"
"math"
"github.com/haashemi/go-harfbuzz/hb"
"github.com/mattn/go-pointer"
"golang.org/x/image/vector"
)
type Writer struct {
font *Font
opts Options
buf hb.Buffer
minX, minY float32
textBounds image.Rectangle
glyphs []hb.GlyphInfo
positions []hb.GlyphPosition
}
func NewWriter(font *Font, text string, opts Options) (w *Writer, err error) {
w = &Writer{
font: font,
opts: opts,
buf: hb.BufferCreate(),
}
if w.opts.Bidi {
text, err = bidiText(text)
if err != nil {
return nil, err
}
}
hb.BufferAddUTF8(w.buf, text)
hb.BufferGuessSegmentProperties(w.buf)
hb.Shape(w.font.font, w.buf, opts.Features)
w.glyphs = hb.BufferGetGlyphInfos(w.buf)
w.positions = hb.BufferGetGlyphPositions(w.buf)
return
}
// Advance returns how far the text will go after drawing.
func (w *Writer) Advance() int {
if w.textBounds.Empty() {
return w.Bounds().Dx()
}
return w.textBounds.Dx()
}
// Bounds returns the after-drawing text bounds.
func (w *Writer) Bounds() image.Rectangle {
if !w.textBounds.Empty() {
return w.textBounds
}
state := new(drawingState)
statePointer := pointer.Save(state)
defer pointer.Unref(statePointer)
for i, gi := range w.glyphs {
gp := w.positions[i]
state.offX = float32(gp.XOffset) / 64
state.offY = float32(gp.YOffset) / 64
w.font.draw(gi.Codepoint, drawFuncs, statePointer)
state.posX += float32(gp.XAdvance) / 64
state.posY += float32(gp.YAdvance) / 64
}
w.minY, w.minX = state.minY, state.minX
w.textBounds = image.Rect(0, 0, int(math.Ceil(float64(state.maxX-state.minX))), int(math.Ceil(float64(state.maxY-state.minY))))
return w.textBounds
}
// Write draws the text on the “image” at “at” with "color”.
func (w *Writer) Write(img draw.Image, at image.Point, color image.Image) {
bounds := w.Bounds()
state := &drawingState{
vec: vector.NewRasterizer(bounds.Dx(), bounds.Dy()),
minX: w.minX, minY: w.minY,
}
statePointer := pointer.Save(state)
defer pointer.Unref(statePointer)
for i, gi := range w.glyphs {
gp := w.positions[i]
state.offX = float32(gp.XOffset) / 64
state.offY = float32(gp.YOffset) / 64
w.font.draw(gi.Codepoint, drawFuncs, statePointer)
state.posX += float32(gp.XAdvance / 64)
state.posY += float32(gp.YAdvance / 64)
}
state.vec.Draw(img, state.vec.Bounds().Add(at), color, image.Point{})
}
// Close destroys the writer's buffer and frees the memory.
func (w *Writer) Close() error {
hb.BufferDestroy(w.buf)
return nil
}