GCC Code Coverage Report


./
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
0 of 102, 0 excluded
0.0%
Functions:
0 of 8, 0 excluded
0.0%
Branches:
0 of 98, 0 excluded
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 if(was_loaded == false) {return;}
40
41 const int count = stbtt_GetKerningTableLength(&font);
42 kernings.reserve(count);
43 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 stbtt_GetFontVMetrics(&font, &ascent_in_points, &descent_in_points, &line_gap_in_points);
49 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 const int index = stbtt_FindGlyphIndex(&font, cp);
58 index_to_unicode[index] = cp;
59 }
60
61 [[nodiscard]] std::optional<int> from_glyph_index_to_unicode(int glyph) const
62 {
63 auto found = index_to_unicode.find(glyph);
64 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 const auto scale = stbtt_ScaleForPixelHeight(&font, size);
80 unsigned char *bitmap = stbtt_GetCodepointBitmap
81 (
82 &font,
83 scale,
84 scale,
85 code_point,
86 &width,
87 &height,
88 &xoffset,
89 &yoffset
90 );
91
92 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 stbtt_GetCodepointHMetrics(&font, code_point, &advance_width, nullptr);
101 ch.advance = int_from_float(float_from_int(advance_width) * scale);
102 }
103 if(width == 0 && height == 0)
104 {
105 ch.image.make_invalid();
106 }
107 else
108 {
109 ch.image.setup_with_alpha_support(width, height);
110
111 for(int y = 0; y < ch.image.height; y += 1)
112 {
113 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 x, ch.image.height-y-1,
119 255, 255, 255,
120 alpha
121 );
122 }
123 }
124 }
125
126 if(bitmap != nullptr)
127 {
128 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 };
159
160 if(f.was_loaded == false) { return false; }
161
162 std::vector<int> code_points;
163 calc_utf8_to_codepoints
164 (
165 chars,
166 [&](int cp){code_points.emplace_back(cp);}
167 );
168
169 for(int code_point: code_points)
170 {
171 const LoadedGlyph cc = f.load_glyph(code_point);
172 if(!cc.valid)
173 {
174 LOG_INFO("Invalid codepoint {0}", code_point);
175 continue;
176 }
177 this->codepoint_to_glyph[code_point] = cc;
178 }
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 for(const auto& cp: code_points)
187 {
188 f.define_codepoint(cp);
189 }
190
191 for(const auto& info: f.kernings)
192 {
193 const auto previous = f.from_glyph_index_to_unicode(info.glyph1);
194 const auto current = f.from_glyph_index_to_unicode(info.glyph2);
195 const int dx = info.advance;
196 if(previous && current)
197 {
198 this->kerning.insert
199 (
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 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 glyph.code_point= font.generate_new_index_from_private_use(image_alias);
271 // todo(Gustav): add ability to clip image
272 glyph.image = image;
273 font.codepoint_to_glyph[glyph.code_point] = glyph;
274
275 return font;
276 }
277
278 }
279
280