-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathpixfont.go
187 lines (166 loc) · 5.61 KB
/
pixfont.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
179
180
181
182
183
184
185
186
187
// Package pixfont is a simple pixel font library that allows for text drawing in the
// standard image and image/draw packages. It is very useful when heavyweight TrueType
// fonts and the freetype-go package are not available or desired. Since fonts are
// simple bitmap images, scaling is not possible and aliasing will occur.
//
// See the included fontgen tool if you wish to convert or include your own pixel font
// in your project.
package pixfont
import (
"bytes"
"image/color"
)
// DefaultFont is used by the convienence method DrawString, and is initialized
// to the Public Domain 8x8 fixed font with some unicode characters.
var DefaultFont = Font8x8
// Spacing is the pixel spacing to use between letters (1 px by default)
var Spacing = 1
// Drawable is an interface which supports setting an x,y coordinate to a color.
type Drawable interface {
Set(x, y int, c color.Color)
}
// PixFont represents a simple bitmap or pixel-based font that can be drawn using
// simple opaque-pixel operations (supported by image.Image and easily included
// in other packages).
type PixFont struct {
charWidth uint8
charHeight uint8
charmap map[rune]uint16
data []uint32
varCharWidth uint8
}
// NewPixFont creates a new PixFont with the provided character width/height and
// character map of offsets into a packed uint32 array of bits.
func NewPixFont(w, h uint8, cm map[rune]uint16, d []uint32) *PixFont {
return &PixFont{w, h, cm, d, w}
}
// GetHeight returns the height of the font in pixels.
func (p *PixFont) GetHeight() int {
return int(p.charHeight)
}
// SetVariableWidth toggles the PixFont between drawing using variable width
// per character or the default fixed-width representation.
func (p *PixFont) SetVariableWidth(isVar bool) {
if !isVar {
p.varCharWidth = p.charWidth
} else {
// spaces will be approx 1/3 em (but at least 3px)
p.varCharWidth = p.charWidth / 3
if p.varCharWidth < 3 {
p.varCharWidth = 3
}
}
}
// DrawRune uses this PixFont to display a single rune in the provided color and
// position in Drawable. The x,y position represents the top-left corner of the rune.
// Drawable.Set is called for each opaque pixel in the font, leaving all other pixels
// in the Drawable as-is. If the rune has no representation in the PixFont, then
// DrawRune returns false and no drawing is done. DrawRune always returns the number
// of pixels to advance before drawing another character.
func (p *PixFont) DrawRune(dr Drawable, x, y int, c rune, clr color.Color) (bool, int) {
poff, haveChar := p.charmap[c]
if !haveChar {
return false, int(p.varCharWidth)
}
w := int(p.charWidth)
if p.varCharWidth != p.charWidth {
w = 0
}
pindex := int(poff >> 2)
psub := (poff & 0x03) * 8
d := p.data[pindex : pindex+int(p.charHeight)]
for yy := 0; yy < int(p.charHeight); yy++ {
bitMask := uint32(1) << psub
for xx := 0; xx < int(p.charWidth); xx++ {
if (d[yy] & bitMask) != 0 {
dr.Set(x+xx, y+yy, clr)
if xx >= w {
w = xx + Spacing
}
}
bitMask <<= 1
}
}
return true, w
}
// DrawString uses this PixFont to display text in the provided color and the specified
// start position in Drawable. The x,y position represents the top-left corner of the
// first letter of s. Text is drawn by repeated calls to DrawRune for each character.
// DrawString returns the total pixel advance used by the string.
func (p *PixFont) DrawString(dr Drawable, x, y int, s string, clr color.Color) int {
for _, c := range s {
_, w := p.DrawRune(dr, x, y, c, clr)
x += w + Spacing
}
return x
}
// MeasureRune measures the advance of a rune drawn using this PixFont.
func (p *PixFont) MeasureRune(c rune) (bool, int) {
poff, haveChar := p.charmap[c]
if !haveChar {
return false, int(p.varCharWidth)
}
w := int(p.charWidth)
if p.varCharWidth != p.charWidth {
w = 0
}
pindex := int(poff >> 2)
psub := (poff & 0x03) * 8
d := p.data[pindex : pindex+int(p.charHeight)]
for yy := 0; yy < int(p.charHeight); yy++ {
bitMask := uint32(1) << psub
for xx := 0; xx < int(p.charWidth); xx++ {
if (d[yy]&bitMask) != 0 && xx >= w {
w = xx + Spacing
}
bitMask <<= 1
}
}
return true, w
}
// MeasureString measures the pixel advance of a string drawn using this PixFont.
func (p *PixFont) MeasureString(s string) int {
x := 0
for _, c := range s {
_, w := p.MeasureRune(c)
x += w + Spacing
}
return x
}
// DrawString is a convienence method that calls DrawString using the DefaultFont
func DrawString(dr Drawable, x, y int, s string, clr color.Color) int {
return DefaultFont.DrawString(dr, x, y, s, clr)
}
// MeasureString is a convienence method that calls MeasureString using the DefaultFont
func MeasureString(s string) int {
return DefaultFont.MeasureString(s)
}
///////
// StringDrawable implements Drawable so you can do FIGlet-inspired pixel fonts in
// text. Obviously it's much simpler though.
type StringDrawable struct {
lines [][]byte
}
func (s *StringDrawable) Set(x, y int, c color.Color) {
for len(s.lines) <= y {
s.lines = append(s.lines, make([]byte, x))
}
if len(s.lines[y]) <= x {
nb := make([]byte, 1+(x-len(s.lines[y])))
s.lines[y] = append(s.lines[y], nb...)
}
s.lines[y][x] = byte('X')
}
// String returns the current string representation of this Drawable.
func (s *StringDrawable) String() string {
return s.PrefixString("")
}
// PrefixString returns the current string representation of this Drawable with a
// user-provided prefix before each line. Useful for adding output in code comments.
func (s *StringDrawable) PrefixString(p string) string {
r := ""
for _, line := range s.lines {
r += p + string(bytes.Replace(line, []byte{0}, []byte(" "), -1)) + "\n"
}
return r
}