This repository has been archived by the owner on Feb 25, 2023. It is now read-only.
/
font.go
176 lines (145 loc) · 3.81 KB
/
font.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
// Copyright (c) 2018-2018 Laurent Moussault. All rights reserved.
// Licensed under a simplified BSD license (see LICENSE file).
package pixel
import (
"errors"
"image"
_ "image/png" // Activate PNG support
"os"
"path/filepath"
"github.com/cozely/cozely/color"
"github.com/cozely/cozely/internal"
)
////////////////////////////////////////////////////////////////////////////////
// FontID is the ID to handle font assets.
type FontID uint8
const (
maxFontID = 0xFF
noFont = FontID(maxFontID)
)
// Monozela10 is the default font (10 pixel high, monospace). This is the only
// font that is always loaded and doesn't need declaration.
const Monozela10 = FontID(0)
var fontPaths = []string{"builtin monozela 10"}
var fonts = []font{{}}
type font struct {
height int16
baseline int16
basecolor color.Index
first uint16 // index of the first glyph
}
////////////////////////////////////////////////////////////////////////////////
// Font declares a new font and returns its ID.
func Font(path string) FontID {
if internal.Running {
setErr(errors.New("pixel font declaration: declarations must happen before starting the framework"))
return noFont
}
if len(fonts) >= maxFontID {
setErr(errors.New("pixel font declaration: too many fonts"))
return noFont
}
fonts = append(fonts, font{})
fontPaths = append(fontPaths, path)
return FontID(len(fonts) - 1)
}
////////////////////////////////////////////////////////////////////////////////
func (f FontID) glyph(r rune) uint16 {
//TODO: add support for non-ascii runes
switch {
case r < ' ':
r = 0x7F - ' '
case r <= 0x7F:
r = r - ' '
default:
r = 0x7F - ' '
}
return fonts[f].first + uint16(r)
}
////////////////////////////////////////////////////////////////////////////////
// Height returns the height of the font, i.e. the height of the images used to
// store the glyphs.
func (f FontID) Height() int16 {
return fonts[f].height
}
////////////////////////////////////////////////////////////////////////////////
func (f FontID) load(frects *[]uint32) error {
//TODO: support other image formats?
var p *image.Paletted
if f == 0 {
p = &monozela10
} else {
n := fontPaths[f]
path := filepath.FromSlash(internal.Path + n + ".png")
path, err := filepath.EvalSymlinks(path)
if err != nil {
return internal.Wrap("in path while loading font", err)
}
fl, err := os.Open(path)
if err != nil {
return internal.Wrap(`while opening font file "`+path+`"`, err)
}
defer fl.Close() //TODO: error handling
img, _, err := image.Decode(fl)
switch err {
case nil:
case image.ErrFormat:
return nil
default:
return internal.Wrap("decoding font file", err)
}
var ok bool
p, ok = img.(*image.Paletted)
if !ok {
return errors.New("impossible to load font " + path + " (color model not supported)")
}
}
h := p.Bounds().Dy() - 1
fonts[f].height = int16(h)
g := uint16(len(pictures.mapping))
fonts[f].first = g
maxw := 0
for y := 0; y < p.Bounds().Dy(); y++ {
if p.Pix[0+y*p.Stride] != 0 {
fonts[f].baseline = int16(y)
break
}
}
fonts[f].basecolor = 255
for _, c := range p.Pix {
if c != 0 && color.Index(c) < fonts[f].basecolor {
fonts[f].basecolor = color.Index(c)
}
}
// Create images and reserve mapping for each rune
for x := 1; x < p.Bounds().Dx(); g++ {
w := 0
for x+w < p.Bounds().Dx() && p.Pix[x+w+h*p.Stride] != 0 {
w++
}
if w > maxw {
maxw = w
}
m := p.SubImage(image.Rect(x, 0, x+w, h))
mm, ok := m.(*image.Paletted)
if !ok {
return errors.New("unexpected subimage in Loadfont")
}
gg := picture(mm)
if gg != PictureID(g) {
//TODO:
}
x += w
for x < p.Bounds().Dx() && p.Pix[x+h*p.Stride] == 0 {
x++
}
}
internal.Debug.Printf(
"Loaded font %s (%d glyphs, %dx%d)",
fontPaths[f],
g-fonts[f].first,
maxw,
fonts[f].height,
)
return nil
}