./
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
0 / 102
0.0%
Branches:
0 / 98
0.0%
Functions:
0 / 8
0.0%

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