GCC Code Coverage Report


./
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
0 of 118, 0 excluded
0.0%
Functions:
0 of 9, 0 excluded
0.0%
Branches:
0 of 136, 0 excluded
0.0%

libs/core/src/core/loadedfont.cc
Line Branch Exec Source
1 #include "core/loadedfont.h"
2
3 #include <set>
4 #include <optional>
5 #include <map>
6
7 #include "eustb_truetype.h"
8
9 #include "log/log.h"
10
11 #include "assert/assert.h"
12
13 #include "base/cint.h"
14 #include "base/memorychunk.h"
15
16 #include "core/utf8.h"
17
18
19
20 namespace eu::core
21 {
22 struct FontData
23 {
24 stbtt_fontinfo font;
25 float size;
26 bool was_loaded;
27 int line_height = 0;
28 std::vector<stbtt_kerningentry> kernings;
29 std::map<int, int> index_to_unicode;
30
31 FontData(const unsigned char* ttf_buffer, float s)
32 : font{}
33 , size{s}
34 , was_loaded{stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0)) == 1}
35 {
36 if(was_loaded == false) {return;}
37
38 const int count = stbtt_GetKerningTableLength(&font);
39 kernings.reserve(count);
40 stbtt_GetKerningTable(&font, kernings.data(), count);
41
42 int ascent = 0;
43 int descent = 0;
44 int line_gap = 0;
45 stbtt_GetFontVMetrics(&font, &ascent, &descent, &line_gap);
46 line_height = ascent - descent + line_gap;
47 }
48
49 void define_codepoint(int cp)
50 {
51 const int index = stbtt_FindGlyphIndex(&font, cp);
52 index_to_unicode[index] = cp;
53 }
54
55 [[nodiscard]] std::optional<int> from_glyph_index_to_unicode(int glyph) const
56 {
57 auto found = index_to_unicode.find(glyph);
58 if(found == index_to_unicode.end())
59 {
60 return {};
61 }
62 else
63 {
64 return found->second;
65 }
66 }
67
68 [[nodiscard]] LoadedGlyph
69 load_glyph(int code_point) const
70 {
71 int width=0; int height=0;
72 int xoffset=0; int yoffset=0;
73 const auto scale = stbtt_ScaleForPixelHeight(&font, size);
74 unsigned char *bitmap = stbtt_GetCodepointBitmap
75 (
76 &font,
77 scale,
78 scale,
79 code_point,
80 &width,
81 &height,
82 &xoffset,
83 &yoffset
84 );
85
86 LoadedGlyph ch;
87 ch.code_point= code_point;
88 ch.size = size;
89 ch.bearing_x = xoffset;
90 ch.bearing_y = -yoffset;
91 ch.valid = true;
92 {
93 int advance_width = 0;
94 stbtt_GetCodepointHMetrics(&font, code_point, &advance_width, nullptr);
95 ch.advance = int_from_float(float_from_int(advance_width) * scale);
96 }
97 if(width == 0 && height == 0)
98 {
99 ch.image.make_invalid();
100 }
101 else
102 {
103 ch.image.setup_with_alpha_support(width, height);
104
105 for(int y = 0; y < ch.image.height; y += 1)
106 {
107 for(int x = 0; x < ch.image.width; x += 1)
108 {
109 const auto alpha = bitmap[ch.image.width * y + x];
110 ch.image.set_pixel
111 (
112 x, ch.image.height-y-1,
113 255, 255, 255,
114 alpha
115 );
116 }
117 }
118 }
119
120 if(bitmap != nullptr)
121 {
122 stbtt_FreeBitmap(bitmap, nullptr);
123 }
124
125 return ch;
126 }
127 };
128
129
130 int
131 LoadedFont::generate_new_index_from_private_use(const std::string& alias)
132 {
133 // detect existing private use alias!
134 const auto pu = next_private_use;
135 next_private_use += 1;
136 private_use_aliases[alias] = pu;
137 return pu;
138 }
139
140
141 void
142 LoadedFont::combine_with(const LoadedFont& fc)
143 {
144 std::map<int, int> pus;
145 for(const auto& [alias, id]: fc.private_use_aliases)
146 {
147 pus[id] = generate_new_index_from_private_use(alias);
148 }
149
150 for(const auto& glyph_iterator: fc.codepoint_to_glyph)
151 {
152 auto code_point = glyph_iterator.first;
153 auto found_pus = pus.find(code_point);
154 if(found_pus != pus.end())
155 {
156 // private use: move to new private use to avoid collisions
157 code_point = found_pus->second;
158 }
159
160 const auto found = codepoint_to_glyph.find(code_point);
161 if(found == codepoint_to_glyph.end())
162 {
163 codepoint_to_glyph[code_point] = glyph_iterator.second;
164 }
165 else
166 {
167 LOG_ERR("Multiple codepoints for {0} found when trying to combine", code_point);
168 }
169 }
170
171 for(const auto& e: fc.kerning)
172 {
173 const auto found = kerning.find(e.first);
174 if(found == kerning.end())
175 {
176 kerning.insert(e);
177 }
178 else
179 {
180 LOG_ERR("Multiple kernings found when trying to combine");
181 }
182 }
183
184 line_height = std::max(line_height, fc.line_height);
185 }
186
187
188 LoadedFont
189 get_characters_from_font
190 (
191 const MemoryChunk& file_memory,
192 int font_size,
193 const std::string& chars
194 )
195 {
196 auto f = FontData
197 {
198 reinterpret_cast<const unsigned char*>(file_memory.bytes),
199 float_from_int(font_size)
200 };
201
202 if(f.was_loaded == false) { return LoadedFont{}; }
203
204 std::vector<int> code_points;
205 calc_utf8_to_codepoints
206 (
207 chars,
208 [&](int cp){code_points.emplace_back(cp);}
209 );
210
211 LoadedFont fontchars {};
212 for(int code_point: code_points)
213 {
214 LoadedGlyph cc = f.load_glyph(code_point);
215 if(!cc.valid)
216 {
217 LOG_INFO("Invalid codepoint {0}", code_point);
218 continue;
219 }
220 fontchars.codepoint_to_glyph[code_point] = cc;
221 }
222
223 fontchars.line_height = f.line_height;
224
225 const float scale = 1 / static_cast<float>(font_size);
226
227 for(int cp: code_points)
228 {
229 f.define_codepoint(cp);
230 }
231
232 for(const auto& info: f.kernings)
233 {
234 const auto previous = f.from_glyph_index_to_unicode(info.glyph1);
235 const auto current = f.from_glyph_index_to_unicode(info.glyph2);
236 const int dx = info.advance;
237 if(previous && current)
238 {
239 fontchars.kerning.insert
240 (
241 KerningMap::value_type
242 (
243 KerningMap::key_type(*previous, *current),
244 static_cast<float>(dx) * scale
245 )
246 );
247 }
248 }
249
250 return fontchars;
251 }
252
253 #if 0
254 LoadedFont
255 get_characters_from_single_image
256 (
257 io::FileSystem* fs,
258 const io::FilePath& image_file,
259 const std::string& image_alias,
260 float image_scale,
261 float image_bearing_x,
262 float image_bearing_y,
263 float image_advance
264 )
265 {
266 const auto loaded = load_image
267 (
268 fs,
269 image_file,
270 AlphaLoad::keep
271 );
272
273 if(loaded.error.empty() == false)
274 {
275 LOG_ERR("Failed to load font image {}", image_file);
276 return LoadedFont{};
277 }
278
279 return get_characters_from_single_image
280 (
281 loaded.image,
282 image_alias,
283 image_scale,
284 image_bearing_x,
285 image_bearing_y,
286 image_advance
287 );
288 }
289 #endif
290
291
292 LoadedFont
293 get_characters_from_single_image
294 (
295 const Image& image,
296 const std::string& image_alias,
297 float image_scale,
298 float image_bearing_x,
299 float image_bearing_y,
300 float image_advance
301 )
302 {
303 LoadedFont font;
304
305 const auto s = 1 / image_scale;
306 LoadedGlyph glyph;
307 glyph.size = s * static_cast<float>(image.height);
308 glyph.bearing_y = int_from_float(s * static_cast<float>(image.height) + image_bearing_y);
309 glyph.bearing_x = int_from_float(image_bearing_x);
310 glyph.advance = int_from_float(s * static_cast<float>(image.width) + image_advance);
311 glyph.code_point= font.generate_new_index_from_private_use(image_alias);
312 // todo(Gustav): add ability to clip image
313 glyph.image = image;
314 font.codepoint_to_glyph[glyph.code_point] = glyph;
315
316 return font;
317 }
318
319 }
320
321