-
Notifications
You must be signed in to change notification settings - Fork 34
/
truetype.go
178 lines (149 loc) · 4.72 KB
/
truetype.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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
package glfont
import (
"fmt"
"github.com/go-gl/gl/all-core/gl"
"github.com/golang/freetype"
"github.com/golang/freetype/truetype"
"golang.org/x/image/font"
"golang.org/x/image/math/fixed"
"image"
"image/draw"
"io"
"io/ioutil"
)
// A Font allows rendering of text to an OpenGL context.
type Font struct {
fontChar map[rune]*character
ttf *truetype.Font
scale int32
vao uint32
vbo uint32
program uint32
texture uint32 // Holds the glyph texture id.
color color
}
type character struct {
textureID uint32 // ID handle of the glyph texture
width int //glyph width
height int //glyph height
advance int //glyph advance
bearingH int //glyph bearing horizontal
bearingV int //glyph bearing vertical
}
//GenerateGlyphs builds a set of textures based on a ttf files gylphs
func (f *Font) GenerateGlyphs(low, high rune) error {
//create a freetype context for drawing
c := freetype.NewContext()
c.SetDPI(72)
c.SetFont(f.ttf)
c.SetFontSize(float64(f.scale))
c.SetHinting(font.HintingFull)
//create new face to measure glyph dimensions
ttfFace := truetype.NewFace(f.ttf, &truetype.Options{
Size: float64(f.scale),
DPI: 72,
Hinting: font.HintingFull,
})
//make each gylph
for ch := low; ch <= high; ch++ {
char := new(character)
gBnd, gAdv, ok := ttfFace.GlyphBounds(ch)
if ok != true {
return fmt.Errorf("ttf face glyphBounds error")
}
gh := int32((gBnd.Max.Y - gBnd.Min.Y) >> 6)
gw := int32((gBnd.Max.X - gBnd.Min.X) >> 6)
//if gylph has no dimensions set to a max value
if gw == 0 || gh == 0 {
gBnd = f.ttf.Bounds(fixed.Int26_6(f.scale))
gw = int32((gBnd.Max.X - gBnd.Min.X) >> 6)
gh = int32((gBnd.Max.Y - gBnd.Min.Y) >> 6)
//above can sometimes yield 0 for font smaller than 48pt, 1 is minimum
if gw == 0 || gh == 0 {
gw = 1
gh = 1
}
}
//The glyph's ascent and descent equal -bounds.Min.Y and +bounds.Max.Y.
gAscent := int(-gBnd.Min.Y) >> 6
gdescent := int(gBnd.Max.Y) >> 6
//set w,h and adv, bearing V and bearing H in char
char.width = int(gw)
char.height = int(gh)
char.advance = int(gAdv)
char.bearingV = gdescent
char.bearingH = (int(gBnd.Min.X) >> 6)
//create image to draw glyph
fg, bg := image.White, image.Black
rect := image.Rect(0, 0, int(gw), int(gh))
rgba := image.NewRGBA(rect)
draw.Draw(rgba, rgba.Bounds(), bg, image.ZP, draw.Src)
//set the glyph dot
px := 0 - (int(gBnd.Min.X) >> 6)
py := (gAscent)
pt := freetype.Pt(px, py)
// Draw the text from mask to image
c.SetClip(rgba.Bounds())
c.SetDst(rgba)
c.SetSrc(fg)
_, err := c.DrawString(string(ch), pt)
if err != nil {
return err
}
// Generate texture
var texture uint32
gl.GenTextures(1, &texture)
gl.BindTexture(gl.TEXTURE_2D, texture)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, int32(rgba.Rect.Dx()), int32(rgba.Rect.Dy()), 0,
gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(rgba.Pix))
char.textureID = texture
//add char to fontChar list
f.fontChar[ch] = char
}
gl.BindTexture(gl.TEXTURE_2D, 0)
return nil
}
//LoadTrueTypeFont builds OpenGL buffers and glyph textures based on a ttf file
func LoadTrueTypeFont(program uint32, r io.Reader, scale int32, low, high rune, dir Direction) (*Font, error) {
data, err := ioutil.ReadAll(r)
if err != nil {
return nil, err
}
// Read the truetype font.
ttf, err := truetype.Parse(data)
if err != nil {
return nil, err
}
//make Font stuct type
f := new(Font)
f.fontChar = make(map[rune]*character)
f.ttf = ttf
f.scale = scale
f.program = program //set shader program
f.SetColor(1.0, 1.0, 1.0, 1.0) //set default white
err = f.GenerateGlyphs(low, high)
if err != nil {
return nil, err
}
// Configure VAO/VBO for texture quads
gl.GenVertexArrays(1, &f.vao)
gl.GenBuffers(1, &f.vbo)
gl.BindVertexArray(f.vao)
gl.BindBuffer(gl.ARRAY_BUFFER, f.vbo)
gl.BufferData(gl.ARRAY_BUFFER, 6*4*4, nil, gl.STATIC_DRAW)
vertAttrib := uint32(gl.GetAttribLocation(f.program, gl.Str("vert\x00")))
gl.EnableVertexAttribArray(vertAttrib)
gl.VertexAttribPointer(vertAttrib, 2, gl.FLOAT, false, 4*4, gl.PtrOffset(0))
defer gl.DisableVertexAttribArray(vertAttrib)
texCoordAttrib := uint32(gl.GetAttribLocation(f.program, gl.Str("vertTexCoord\x00")))
gl.EnableVertexAttribArray(texCoordAttrib)
gl.VertexAttribPointer(texCoordAttrib, 2, gl.FLOAT, false, 4*4, gl.PtrOffset(2*4))
defer gl.DisableVertexAttribArray(texCoordAttrib)
gl.BindBuffer(gl.ARRAY_BUFFER, 0)
gl.BindVertexArray(0)
return f, nil
}