1
2
3
4
5
6
7
8
9 package freetype
10
11 import (
12 "errors"
13 "image"
14 "image/draw"
15
16 "github.com/golang/freetype/raster"
17 "github.com/golang/freetype/truetype"
18 "golang.org/x/image/font"
19 "golang.org/x/image/math/fixed"
20 )
21
22
23
24
25
26 const (
27 nGlyphs = 256
28 nXFractions = 4
29 nYFractions = 1
30 )
31
32
33
34
35 type cacheEntry struct {
36 valid bool
37 glyph truetype.Index
38 advanceWidth fixed.Int26_6
39 mask *image.Alpha
40 offset image.Point
41 }
42
43
44
45
46 func ParseFont(b []byte) (*truetype.Font, error) {
47 return truetype.Parse(b)
48 }
49
50
51
52 func Pt(x, y int) fixed.Point26_6 {
53 return fixed.Point26_6{
54 X: fixed.Int26_6(x << 6),
55 Y: fixed.Int26_6(y << 6),
56 }
57 }
58
59
60 type Context struct {
61 r *raster.Rasterizer
62 f *truetype.Font
63 glyphBuf truetype.GlyphBuf
64
65 clip image.Rectangle
66
67 dst draw.Image
68 src image.Image
69
70
71 fontSize, dpi float64
72 scale fixed.Int26_6
73 hinting font.Hinting
74
75 cache [nGlyphs * nXFractions * nYFractions]cacheEntry
76 }
77
78
79
80 func (c *Context) PointToFixed(x float64) fixed.Int26_6 {
81 return fixed.Int26_6(x * float64(c.dpi) * (64.0 / 72.0))
82 }
83
84
85 func (c *Context) drawContour(ps []truetype.Point, dx, dy fixed.Int26_6) {
86 if len(ps) == 0 {
87 return
88 }
89
90
91
92
93
94
95
96
97
98
99
100 start := fixed.Point26_6{
101 X: dx + ps[0].X,
102 Y: dy - ps[0].Y,
103 }
104 others := []truetype.Point(nil)
105 if ps[0].Flags&0x01 != 0 {
106 others = ps[1:]
107 } else {
108 last := fixed.Point26_6{
109 X: dx + ps[len(ps)-1].X,
110 Y: dy - ps[len(ps)-1].Y,
111 }
112 if ps[len(ps)-1].Flags&0x01 != 0 {
113 start = last
114 others = ps[:len(ps)-1]
115 } else {
116 start = fixed.Point26_6{
117 X: (start.X + last.X) / 2,
118 Y: (start.Y + last.Y) / 2,
119 }
120 others = ps
121 }
122 }
123 c.r.Start(start)
124 q0, on0 := start, true
125 for _, p := range others {
126 q := fixed.Point26_6{
127 X: dx + p.X,
128 Y: dy - p.Y,
129 }
130 on := p.Flags&0x01 != 0
131 if on {
132 if on0 {
133 c.r.Add1(q)
134 } else {
135 c.r.Add2(q0, q)
136 }
137 } else {
138 if on0 {
139
140 } else {
141 mid := fixed.Point26_6{
142 X: (q0.X + q.X) / 2,
143 Y: (q0.Y + q.Y) / 2,
144 }
145 c.r.Add2(q0, mid)
146 }
147 }
148 q0, on0 = q, on
149 }
150
151 if on0 {
152 c.r.Add1(start)
153 } else {
154 c.r.Add2(q0, start)
155 }
156 }
157
158
159
160
161 func (c *Context) rasterize(glyph truetype.Index, fx, fy fixed.Int26_6) (
162 fixed.Int26_6, *image.Alpha, image.Point, error) {
163
164 if err := c.glyphBuf.Load(c.f, c.scale, glyph, c.hinting); err != nil {
165 return 0, nil, image.Point{}, err
166 }
167
168 xmin := int(fx+c.glyphBuf.Bounds.Min.X) >> 6
169 ymin := int(fy-c.glyphBuf.Bounds.Max.Y) >> 6
170 xmax := int(fx+c.glyphBuf.Bounds.Max.X+0x3f) >> 6
171 ymax := int(fy-c.glyphBuf.Bounds.Min.Y+0x3f) >> 6
172 if xmin > xmax || ymin > ymax {
173 return 0, nil, image.Point{}, errors.New("freetype: negative sized glyph")
174 }
175
176
177
178
179
180 fx -= fixed.Int26_6(xmin << 6)
181 fy -= fixed.Int26_6(ymin << 6)
182
183 c.r.Clear()
184 e0 := 0
185 for _, e1 := range c.glyphBuf.Ends {
186 c.drawContour(c.glyphBuf.Points[e0:e1], fx, fy)
187 e0 = e1
188 }
189 a := image.NewAlpha(image.Rect(0, 0, xmax-xmin, ymax-ymin))
190 c.r.Rasterize(raster.NewAlphaSrcPainter(a))
191 return c.glyphBuf.AdvanceWidth, a, image.Point{xmin, ymin}, nil
192 }
193
194
195
196
197
198 func (c *Context) glyph(glyph truetype.Index, p fixed.Point26_6) (
199 fixed.Int26_6, *image.Alpha, image.Point, error) {
200
201
202 ix, fx := int(p.X>>6), p.X&0x3f
203 iy, fy := int(p.Y>>6), p.Y&0x3f
204
205 tg := int(glyph) % nGlyphs
206 tx := int(fx) / (64 / nXFractions)
207 ty := int(fy) / (64 / nYFractions)
208 t := ((tg*nXFractions)+tx)*nYFractions + ty
209
210 if e := c.cache[t]; e.valid && e.glyph == glyph {
211 return e.advanceWidth, e.mask, e.offset.Add(image.Point{ix, iy}), nil
212 }
213
214 advanceWidth, mask, offset, err := c.rasterize(glyph, fx, fy)
215 if err != nil {
216 return 0, nil, image.Point{}, err
217 }
218 c.cache[t] = cacheEntry{true, glyph, advanceWidth, mask, offset}
219 return advanceWidth, mask, offset.Add(image.Point{ix, iy}), nil
220 }
221
222
223
224
225
226
227
228
229
230 func (c *Context) DrawString(s string, p fixed.Point26_6) (fixed.Point26_6, error) {
231 if c.f == nil {
232 return fixed.Point26_6{}, errors.New("freetype: DrawText called with a nil font")
233 }
234 prev, hasPrev := truetype.Index(0), false
235 for _, rune := range s {
236 index := c.f.Index(rune)
237 if hasPrev {
238 kern := c.f.Kern(c.scale, prev, index)
239 if c.hinting != font.HintingNone {
240 kern = (kern + 32) &^ 63
241 }
242 p.X += kern
243 }
244 advanceWidth, mask, offset, err := c.glyph(index, p)
245 if err != nil {
246 return fixed.Point26_6{}, err
247 }
248 p.X += advanceWidth
249 glyphRect := mask.Bounds().Add(offset)
250 dr := c.clip.Intersect(glyphRect)
251 if !dr.Empty() {
252 mp := image.Point{0, dr.Min.Y - glyphRect.Min.Y}
253 draw.DrawMask(c.dst, dr, c.src, image.ZP, mask, mp, draw.Over)
254 }
255 prev, hasPrev = index, true
256 }
257 return p, nil
258 }
259
260
261
262 func (c *Context) recalc() {
263 c.scale = fixed.Int26_6(c.fontSize * c.dpi * (64.0 / 72.0))
264 if c.f == nil {
265 c.r.SetBounds(0, 0)
266 } else {
267
268 b := c.f.Bounds(c.scale)
269 xmin := +int(b.Min.X) >> 6
270 ymin := -int(b.Max.Y) >> 6
271 xmax := +int(b.Max.X+63) >> 6
272 ymax := -int(b.Min.Y-63) >> 6
273 c.r.SetBounds(xmax-xmin, ymax-ymin)
274 }
275 for i := range c.cache {
276 c.cache[i] = cacheEntry{}
277 }
278 }
279
280
281 func (c *Context) SetDPI(dpi float64) {
282 if c.dpi == dpi {
283 return
284 }
285 c.dpi = dpi
286 c.recalc()
287 }
288
289
290 func (c *Context) SetFont(f *truetype.Font) {
291 if c.f == f {
292 return
293 }
294 c.f = f
295 c.recalc()
296 }
297
298
299 func (c *Context) SetFontSize(fontSize float64) {
300 if c.fontSize == fontSize {
301 return
302 }
303 c.fontSize = fontSize
304 c.recalc()
305 }
306
307
308 func (c *Context) SetHinting(hinting font.Hinting) {
309 c.hinting = hinting
310 for i := range c.cache {
311 c.cache[i] = cacheEntry{}
312 }
313 }
314
315
316 func (c *Context) SetDst(dst draw.Image) {
317 c.dst = dst
318 }
319
320
321
322 func (c *Context) SetSrc(src image.Image) {
323 c.src = src
324 }
325
326
327 func (c *Context) SetClip(clip image.Rectangle) {
328 c.clip = clip
329 }
330
331
332
333
334 func NewContext() *Context {
335 return &Context{
336 r: raster.NewRasterizer(0, 0),
337 fontSize: 12,
338 dpi: 72,
339 scale: 12 << 6,
340 }
341 }
342
View as plain text