Project Alice
Loading...
Searching...
No Matches
fonts.cpp
Go to the documentation of this file.
1#include <cmath>
2#include <bit>
3
4#include "hb.h"
5#include "hb-ft.h"
6
7#include "fonts.hpp"
8#include "parsers.hpp"
9#include "simple_fs.hpp"
10#include "system_state.hpp"
11#ifdef _WIN32
12#include <icu.h>
13#else
14#include <unicode/ubrk.h>
15#include <unicode/utypes.h>
16#include <unicode/ubidi.h>
17#endif
18
19namespace text {
20
22 return uint16_t(uint32_t((font_index - 1) << 7) | uint32_t(black ? (1 << 6) : 0) | uint32_t(size & 0x3F));
23}
24
25bool is_black_font(std::string_view txt) {
26 if(parsers::has_fixed_suffix_ci(txt.data(), txt.data() + txt.length(), "_bl") ||
27 parsers::has_fixed_suffix_ci(txt.data(), txt.data() + txt.length(), "black") ||
28 parsers::has_fixed_suffix_ci(txt.data(), txt.data() + txt.length(), "black_bold")) {
29 return true;
30 } else {
31 return false;
32 }
33}
34
35uint32_t font_size(std::string_view txt) {
36 char const* first_int = txt.data();
37 char const* end = txt.data() + txt.size();
38 while(first_int != end && !isdigit(*first_int))
39 ++first_int;
40 char const* last_int = first_int;
41 while(last_int != end && isdigit(*last_int))
42 ++last_int;
43
44 if(first_int == last_int) {
45 if(parsers::has_fixed_prefix_ci(txt.data(), txt.data() + txt.size(), "fps_font"))
46 return uint32_t(14);
47 else if(parsers::has_fixed_prefix_ci(txt.data(), txt.data() + txt.size(), "tooltip_font"))
48 return uint32_t(16);
49 else if(parsers::has_fixed_prefix_ci(txt.data(), txt.data() + txt.size(), "frangoth_bold"))
50 return uint32_t(18);
51 else if(parsers::has_fixed_prefix_ci(txt.data(), txt.data() + txt.size(), "impact_small"))
52 return uint32_t(24);
53 else if(parsers::has_fixed_prefix_ci(txt.data(), txt.data() + txt.size(), "old_english"))
54 return uint32_t(50);
55 else if(parsers::has_fixed_prefix_ci(txt.data(), txt.data() + txt.size(), "timefont"))
56 return uint32_t(24);
57 else if(parsers::has_fixed_prefix_ci(txt.data(), txt.data() + txt.size(), "vic_title"))
58 return uint32_t(42);
59 else
60 return uint32_t(14);
61 }
62
63 uint32_t rvalue = 0;
64 std::from_chars(first_int, last_int, rvalue);
65 return rvalue;
66}
67
68uint32_t font_index(std::string_view txt) {
69 if(parsers::has_fixed_prefix_ci(txt.data(), txt.data() + txt.size(), "arial"))
70 return 1;
71 else if(parsers::has_fixed_prefix_ci(txt.data(), txt.data() + txt.size(), "fps"))
72 return 1;
73 else if(parsers::has_fixed_prefix_ci(txt.data(), txt.data() + txt.size(), "main"))
74 return 2;
75 else if(parsers::has_fixed_prefix_ci(txt.data(), txt.data() + txt.size(), "tooltip"))
76 return 1;
77 else if(parsers::has_fixed_prefix_ci(txt.data(), txt.data() + txt.size(), "frangoth"))
78 return 2;
79 else if(parsers::has_fixed_prefix_ci(txt.data(), txt.data() + txt.size(), "garamond"))
80 return 2;
81 else if(parsers::has_fixed_prefix_ci(txt.data(), txt.data() + txt.size(), "impact"))
82 return 2;
83 else if(parsers::has_fixed_prefix_ci(txt.data(), txt.data() + txt.size(), "old"))
84 return 2;
85 else if(parsers::has_fixed_prefix_ci(txt.data(), txt.data() + txt.size(), "timefont"))
86 return 1;
87 else if(parsers::has_fixed_prefix_ci(txt.data(), txt.data() + txt.size(), "vic"))
88 return 2;
89 else
90 return 1;
91}
92
93uint16_t name_into_font_id(sys::state& state, std::string_view txt) {
94 auto base_id = pack_font_handle(font_index(txt), is_black_font(txt), font_size(txt));
95 std::string txt_copy = [&]() {
96 if(parsers::has_fixed_suffix_ci(txt.data(), txt.data() + txt.length(), "_black")) {
97 return std::string(txt.substr(0, txt.length() - 6));
98 }
99 if(parsers::has_fixed_suffix_ci(txt.data(), txt.data() + txt.length(), "_black_bold")) {
100 return std::string(txt.substr(0, txt.length() - 11)) + "_bold";
101 }
102 return std::string(txt);
103 }();
104 uint16_t individuator = 0;
105 auto it = state.font_collection.font_names.find(uint16_t(base_id | (individuator << 8)));
106 while(it != state.font_collection.font_names.end()) {
107 if(state.to_string_view(it->second) == txt_copy) {
108 return uint16_t(base_id | (individuator << 8));
109 }
110 ++individuator;
111 it = state.font_collection.font_names.find(uint16_t(base_id | (individuator << 8)));
112 }
113 auto new_key = state.add_key_utf8(txt_copy);
114 auto new_handle = uint16_t(base_id | (individuator << 8));
115 state.font_collection.font_names.insert_or_assign(new_handle, new_key);
116 return new_handle;
117}
118
119int32_t size_from_font_id(uint16_t id) {
120 auto index = uint32_t(((id >> 7) & 0x01) + 1);
121 if(index == 2)
122 return (int32_t(id & 0x3F) * 3) / 4;
123 else
124 return (int32_t(id & 0x3F) * 5) / 6;
125}
126
127bool is_black_from_font_id(uint16_t id) {
128 return ((id >> 6) & 0x01) != 0;
129}
131 uint32_t offset = 0;
132 if(((id >> 7) & 0x01) == 0)
134 else
136}
137
138float font_manager::text_extent(sys::state& state, stored_glyphs const& txt, uint32_t starting_offset, uint32_t count, uint16_t font_id) {
140 auto size = text::size_from_font_id(font_id);
141 if(state.user_settings.use_classic_fonts) {
142 std::string codepoints = "";
143 for(uint32_t i = starting_offset; i < starting_offset + count; i++) {
144 codepoints.push_back(char(txt.glyph_info[i].codepoint));
145 }
146 return text::get_bm_font(state, font_id).get_string_width(state, codepoints.c_str(), uint32_t(codepoints.size()));
147 }
148 return float(font.text_extent(state, txt, starting_offset, count, size));
149}
150
151float font_manager::line_height(sys::state& state, uint16_t font_id) {
152 if(state.user_settings.use_classic_fonts) {
153 return text::get_bm_font(state, font_id).get_height();
154 }
156}
157
159 FT_Init_FreeType(&ft_library);
160}
162 FT_Done_FreeType(ft_library);
163}
164
166 if(hb_buf)
167 hb_buffer_destroy(hb_buf);
168 // if(loaded)
169 // FT_Done_Face(font_face);
170}
171
172int32_t transform_offset_b(int32_t x, int32_t y, int32_t btmap_x_off, int32_t btmap_y_off, uint32_t width, uint32_t height,
173 uint32_t pitch) {
174 int bmp_x = x - btmap_x_off;
175 int bmp_y = y - btmap_y_off;
176
177 if((bmp_x < 0) || (bmp_x >= (int32_t)width) || (bmp_y < 0) || (bmp_y >= (int32_t)height))
178 return -1;
179 else
180 return bmp_x + bmp_y * (int32_t)pitch;
181}
182
183
184constexpr float rt_2 = 1.41421356237309504f;
185
186void init_in_map(bool in_map[dr_size * dr_size], uint8_t const* bmp_data, int32_t btmap_x_off, int32_t btmap_y_off, uint32_t width, uint32_t height, uint32_t pitch) {
187 for(int32_t j = 0; j < dr_size; ++j) {
188 for(int32_t i = 0; i < dr_size; ++i) {
189 auto const boff = transform_offset_b(i, j, btmap_x_off, btmap_y_off, width, height, pitch);
190 in_map[i + dr_size * j] = (boff != -1) ? (bmp_data[boff] > 127) : false;
191 }
192 }
193}
194
195//
196// based on The "dead reckoning" signed distance transform
197// Grevera, George J. (2004) Computer Vision and Image Understanding 95 pages 317–333
198//
199
200void dead_reckoning(float distance_map[dr_size * dr_size], bool const in_map[dr_size * dr_size]) {
201 int16_t yborder[dr_size * dr_size] = {0};
202 int16_t xborder[dr_size * dr_size] = {0};
203
204 for(uint32_t i = 0; i < dr_size * dr_size; ++i) {
205 distance_map[i] = std::numeric_limits<float>::infinity();
206 }
207 for(int32_t j = 1; j < dr_size - 1; ++j) {
208 for(int32_t i = 1; i < dr_size - 1; ++i) {
209 if(in_map[i - 1 + dr_size * j] != in_map[i + dr_size * j] || in_map[i + 1 + dr_size * j] != in_map[i + dr_size * j] ||
210 in_map[i + dr_size * (j + 1)] != in_map[i + dr_size * j] || in_map[i + dr_size * (j - 1)] != in_map[i + dr_size * j]) {
211 distance_map[i + dr_size * j] = 0.0f;
212 yborder[i + dr_size * j] = static_cast<int16_t>(j);
213 xborder[i + dr_size * j] = static_cast<int16_t>(i);
214 }
215 }
216 }
217 for(int32_t j = 1; j < dr_size - 1; ++j) {
218 for(int32_t i = 1; i < dr_size - 1; ++i) {
219 if(distance_map[(i - 1) + dr_size * (j - 1)] + rt_2 < distance_map[(i) + dr_size * (j)]) {
220 yborder[i + dr_size * j] = yborder[(i - 1) + dr_size * (j - 1)];
221 xborder[i + dr_size * j] = xborder[(i - 1) + dr_size * (j - 1)];
222 distance_map[(i) + dr_size * (j)] = (float)std::sqrt((i - xborder[i + dr_size * j]) * (i - xborder[i + dr_size * j]) +
223 (j - yborder[i + dr_size * j]) * (j - yborder[i + dr_size * j]));
224 }
225 if(distance_map[(i) + dr_size * (j - 1)] + 1.0f < distance_map[(i) + dr_size * (j)]) {
226 yborder[i + dr_size * j] = yborder[(i) + dr_size * (j - 1)];
227 xborder[i + dr_size * j] = xborder[(i) + dr_size * (j - 1)];
228 distance_map[(i) + dr_size * (j)] = (float)std::sqrt((i - xborder[i + dr_size * j]) * (i - xborder[i + dr_size * j]) +
229 (j - yborder[i + dr_size * j]) * (j - yborder[i + dr_size * j]));
230 }
231 if(distance_map[(i + 1) + dr_size * (j - 1)] + rt_2 < distance_map[(i) + dr_size * (j)]) {
232 yborder[i + dr_size * j] = yborder[(i + 1) + dr_size * (j - 1)];
233 xborder[i + dr_size * j] = xborder[(i + 1) + dr_size * (j - 1)];
234 distance_map[(i) + dr_size * (j)] = (float)std::sqrt((i - xborder[i + dr_size * j]) * (i - xborder[i + dr_size * j]) +
235 (j - yborder[i + dr_size * j]) * (j - yborder[i + dr_size * j]));
236 }
237 if(distance_map[(i - 1) + dr_size * (j)] + 1.0f < distance_map[(i) + dr_size * (j)]) {
238 yborder[i + dr_size * j] = yborder[(i - 1) + dr_size * (j)];
239 xborder[i + dr_size * j] = xborder[(i - 1) + dr_size * (j)];
240 distance_map[(i) + dr_size * (j)] = (float)std::sqrt((i - xborder[i + dr_size * j]) * (i - xborder[i + dr_size * j]) +
241 (j - yborder[i + dr_size * j]) * (j - yborder[i + dr_size * j]));
242 }
243 }
244 }
245
246 for(int32_t j = dr_size - 2; j > 0; --j) {
247 for(int32_t i = dr_size - 2; i > 0; --i) {
248 if(distance_map[(i + 1) + dr_size * (j)] + 1.0f < distance_map[(i) + dr_size * (j)]) {
249 yborder[i + dr_size * j] = yborder[(i + 1) + dr_size * (j)];
250 xborder[i + dr_size * j] = xborder[(i + 1) + dr_size * (j)];
251 distance_map[(i) + dr_size * (j)] = (float)std::sqrt((i - xborder[i + dr_size * j]) * (i - xborder[i + dr_size * j]) +
252 (j - yborder[i + dr_size * j]) * (j - yborder[i + dr_size * j]));
253 }
254 if(distance_map[(i - 1) + dr_size * (j + 1)] + rt_2 < distance_map[(i) + dr_size * (j)]) {
255 yborder[i + dr_size * j] = yborder[(i - 1) + dr_size * (j + 1)];
256 xborder[i + dr_size * j] = xborder[(i - 1) + dr_size * (j + 1)];
257 distance_map[(i) + dr_size * (j)] = (float)std::sqrt((i - xborder[i + dr_size * j]) * (i - xborder[i + dr_size * j]) +
258 (j - yborder[i + dr_size * j]) * (j - yborder[i + dr_size * j]));
259 }
260 if(distance_map[(i) + dr_size * (j + 1)] + 1.0f < distance_map[(i) + dr_size * (j)]) {
261 yborder[i + dr_size * j] = yborder[(i) + dr_size * (j + 1)];
262 xborder[i + dr_size * j] = xborder[(i) + dr_size * (j + 1)];
263 distance_map[(i) + dr_size * (j)] = (float)std::sqrt((i - xborder[i + dr_size * j]) * (i - xborder[i + dr_size * j]) +
264 (j - yborder[i + dr_size * j]) * (j - yborder[i + dr_size * j]));
265 }
266 if(distance_map[(i + 1) + dr_size * (j + 1)] + rt_2 < distance_map[(i) + dr_size * (j)]) {
267 yborder[i + dr_size * j] = yborder[(i + 1) + dr_size * (j + 1)];
268 xborder[i + dr_size * j] = xborder[(i + 1) + dr_size * (j + 1)];
269 distance_map[(i) + dr_size * (j)] = (float)std::sqrt((i - xborder[i + dr_size * j]) * (i - xborder[i + dr_size * j]) +
270 (j - yborder[i + dr_size * j]) * (j - yborder[i + dr_size * j]));
271 }
272 }
273 }
274
275 for(uint32_t i = 0; i < dr_size * dr_size; ++i) {
276 if(in_map[i])
277 distance_map[i] *= -1.0f;
278 }
279}
280
282 current_locale = l;
283
284 uint32_t end_language = 0;
285 auto locale_name = state.world.locale_get_locale_name(l);
286 std::string_view localename_sv((char const*)locale_name.begin(), locale_name.size());
287 while(end_language < locale_name.size()) {
288 if(localename_sv[end_language] == '-')
289 break;
290 ++end_language;
291 }
292
293 std::string lang_str{ localename_sv .substr(0, end_language) };
294
295 state.world.locale_set_resolved_language(l, hb_language_from_string(localename_sv.data(), int(end_language)));
296
297 {
298 auto f = state.world.locale_get_body_font(l);
299 std::string fname((char const*)f.begin(), (char const*)f.end());
300 font* resolved = nullptr;
301 uint16_t count = 0;
302
303 for(auto& fnt : font_array) {
304 if(fnt.file_name == fname) {
305 resolved = &fnt;
306 break;
307 }
308 ++count;
309 }
310
311 if(!resolved) {
312 auto r = simple_fs::get_root(state.common_fs);
313 auto assets = simple_fs::open_directory(r, NATIVE("assets"));
314 auto fonts = simple_fs::open_directory(assets, NATIVE("fonts"));
315 auto ff = simple_fs::open_file(fonts, simple_fs::utf8_to_native(fname));
316 if(!ff) {
317 std::abort();
318 }
319
320 font_array.emplace_back();
321 auto content = simple_fs::view_contents(*ff);
322 load_font(font_array.back(), content.data, content.file_size);
323 font_array.back().only_raw_codepoints = state.user_settings.use_classic_fonts;
324 font_array.back().file_name = fname;
325 resolved = &(font_array.back());
326 }
327
328 state.world.locale_set_resolved_body_font(l, count);
329 }
330 {
331 auto f = state.world.locale_get_header_font(l);
332 std::string fname((char const*)f.begin(), (char const*)f.end());
333 font* resolved = nullptr;
334 uint16_t count = 0;
335
336 for(auto& fnt : font_array) {
337 if(fnt.file_name == fname) {
338 resolved = &fnt;
339 break;
340 }
341 ++count;
342 }
343
344 if(!resolved) {
345 auto r = simple_fs::get_root(state.common_fs);
346 auto assets = simple_fs::open_directory(r, NATIVE("assets"));
347 auto fonts = simple_fs::open_directory(assets, NATIVE("fonts"));
348 auto ff = simple_fs::open_file(fonts, simple_fs::utf8_to_native(fname));
349 if(!ff) {
350 std::abort();
351 }
352
353 font_array.emplace_back();
354 auto content = simple_fs::view_contents(*ff);
355 load_font(font_array.back(), content.data, content.file_size);
356 font_array.back().only_raw_codepoints = state.user_settings.use_classic_fonts;
357 font_array.back().file_name = fname;
358 resolved = &(font_array.back());
359 }
360
361 state.world.locale_set_resolved_header_font(l, count);
362 }
363 {
364 auto f = state.world.locale_get_map_font(l);
365 std::string fname((char const*)f.begin(), (char const*)f.end());
366 font* resolved = nullptr;
367 uint16_t count = 0;
368
369 for(auto& fnt : font_array) {
370 if(fnt.file_name == fname) {
371 resolved = &fnt;
372 break;
373 }
374 ++count;
375 }
376
377 if(!resolved) {
378 auto r = simple_fs::get_root(state.common_fs);
379 auto assets = simple_fs::open_directory(r, NATIVE("assets"));
380 auto fonts = simple_fs::open_directory(assets, NATIVE("fonts"));
381 auto ff = simple_fs::open_file(fonts, simple_fs::utf8_to_native(fname));
382 if(!ff) {
383 std::abort();
384 }
385
386 font_array.emplace_back();
387 auto content = simple_fs::view_contents(*ff);
388 load_font(font_array.back(), content.data, content.file_size);
389 font_array.back().only_raw_codepoints = state.user_settings.use_classic_fonts;
390 font_array.back().file_name = fname;
391 resolved = &(font_array.back());
392 }
393
394 state.world.locale_set_resolved_map_font(l, count);
395 }
396
397 state.reset_locale_pool();
398
399 auto fb_name = state.world.locale_get_fallback(l);
400 if(fb_name.size() > 0) {
401 std::string_view fb_name_sv((char const*)fb_name.begin(), fb_name.size());
402 state.load_locale_strings(fb_name_sv);
403 }
404
405 UErrorCode errorCode = U_ZERO_ERROR;
406 UBreakIterator* lb_it = ubrk_open(UBreakIteratorType::UBRK_LINE, lang_str.c_str(), nullptr, 0, &errorCode);
407 if(!lb_it || !U_SUCCESS(errorCode)) {
408 std::abort(); // couldn't create iterator
409 }
410 auto rule_size = ubrk_getBinaryRules(lb_it, nullptr, 0, &errorCode);
411 if(rule_size == 0 || !U_SUCCESS(errorCode)) {
412 std::abort(); // couldn't get_rules
413 }
414
415 state.font_collection.compiled_ubrk_rules.resize(uint32_t(rule_size));
416 ubrk_getBinaryRules(lb_it, state.font_collection.compiled_ubrk_rules.data(), rule_size, &errorCode);
417
418 ubrk_close(lb_it);
419
420 state.load_locale_strings(localename_sv);
421}
422
424 if(!current_locale)
425 std::abort();
426 switch(s) {
428 default:
429 return font_array[state.world.locale_get_resolved_body_font(current_locale)];
431 return font_array[state.world.locale_get_resolved_header_font(current_locale)];
433 return font_array[state.world.locale_get_resolved_map_font(current_locale)];
434 }
435
436}
437
438void font_manager::load_font(font& fnt, char const* file_data, uint32_t file_size) {
439 fnt.file_data = std::unique_ptr<FT_Byte[]>(new FT_Byte[file_size]);
440
441 memcpy(fnt.file_data.get(), file_data, file_size);
442 FT_New_Memory_Face(ft_library, fnt.file_data.get(), file_size, 0, &fnt.font_face);
443 FT_Select_Charmap(fnt.font_face, FT_ENCODING_UNICODE);
444 FT_Set_Pixel_Sizes(fnt.font_face, dr_size, dr_size);
445 fnt.hb_font_face = hb_ft_font_create(fnt.font_face, nullptr);
446 fnt.hb_buf = hb_buffer_create();
447
448 fnt.internal_line_height = float(fnt.font_face->size->metrics.height) / float((1 << 6) * magnification_factor);
449 fnt.internal_ascender = float(fnt.font_face->size->metrics.ascender) / float((1 << 6) * magnification_factor);
450 fnt.internal_descender = -float(fnt.font_face->size->metrics.descender) / float((1 << 6) * magnification_factor);
452}
453
454float font::line_height(int32_t size) const {
455 return internal_line_height * size / 64.0f;
456}
457float font::ascender(int32_t size) const {
458 return internal_ascender * size / 64.0f;
459}
460float font::descender(int32_t size) const {
461 return internal_descender * size / 64.0f;
462}
463float font::top_adjustment(int32_t size) const {
464 return internal_top_adj * size / 64.0f;
465}
466
467bool font::can_display(char32_t ch_in) const {
468 return FT_Get_Char_Index(font_face, ch_in) != 0;
469}
470
471float font::base_glyph_width(char32_t ch_in) {
472 if(auto it = glyph_positions.find(ch_in); it != glyph_positions.end())
473 return it->second.x_advance;
474
475 make_glyph(ch_in);
476 return glyph_positions[ch_in].x_advance;
477}
478void font::make_glyph(char32_t ch_in) {
479 if(glyph_positions.find(ch_in) != glyph_positions.end())
480 return;
481
482 // load all glyph metrics
483 if(ch_in) {
484 FT_Load_Glyph(font_face, ch_in, FT_LOAD_TARGET_NORMAL | FT_LOAD_RENDER);
485
486 FT_Glyph g_result;
487 auto err = FT_Get_Glyph(font_face->glyph, &g_result);
488 if(err != 0) {
490 gso.x = 0.f;
491 gso.y = 0.f;
492 gso.x_advance = 0.f;
493 gso.texture_slot = 0;
494 glyph_positions.insert_or_assign(ch_in, gso);
495 return;
496 }
497
498 FT_Bitmap const& bitmap = ((FT_BitmapGlyphRec*)g_result)->bitmap;
499
500 float const hb_x = float(font_face->glyph->metrics.horiBearingX) / 64.f;
501 float const hb_y = float(font_face->glyph->metrics.horiBearingY) / 64.f;
502
503
504 uint8_t pixel_buffer[64 * 64] = { 0 };
505 int const btmap_x_off = 32 * magnification_factor - bitmap.width / 2;
506 int const btmap_y_off = 32 * magnification_factor - bitmap.rows / 2;
507
509 gso.x = (hb_x - float(btmap_x_off)) * 1.0f / float(magnification_factor);
510 gso.y = (-hb_y - float(btmap_y_off)) * 1.0f / float(magnification_factor);
511 gso.x_advance = float(font_face->glyph->metrics.horiAdvance) / float((1 << 6) * magnification_factor);
512
513 bool in_map[dr_size * dr_size] = {false};
514 float distance_map[dr_size * dr_size] = {0.0f};
515 init_in_map(in_map, bitmap.buffer, btmap_x_off, btmap_y_off, bitmap.width, bitmap.rows, uint32_t(bitmap.pitch));
516 dead_reckoning(distance_map, in_map);
517 for(int y = 0; y < 64; ++y) {
518 for(int x = 0; x < 64; ++x) {
519 const size_t index = size_t(x + y * 64);
520 float const distance_value = distance_map[(x * magnification_factor + magnification_factor / 2) + (y * magnification_factor + magnification_factor / 2) * dr_size] / float(magnification_factor * 64);
521 int const int_value = int(distance_value * -255.0f + 128.0f);
522 const uint8_t small_value = uint8_t(std::min(255, std::max(0, int_value)));
523 pixel_buffer[index] = small_value;
524 }
525 }
526 //The array
528 GLuint texid = 0;
529 if((first_free_slot & 63) == 0) {
530 GLuint new_text = 0;
531 glGenTextures(1, &texid);
532 glBindTexture(GL_TEXTURE_2D, texid);
533 glTexStorage2D(GL_TEXTURE_2D, 1, GL_R8, 64 * 8, 64 * 8);
534 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
535 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
536 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
537 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
538 textures.push_back(texid);
539 uint32_t clearvalue = 0;
540 glClearTexImage(GL_TEXTURE_2D, 0, GL_RED, GL_UNSIGNED_BYTE, &clearvalue);
541 } else {
542 assert(textures.size() > 0);
543 texid = textures.back();
544 assert(texid);
545 glBindTexture(GL_TEXTURE_2D, texid);
546 }
547 if(texid) {
548 auto sub_index = first_free_slot & 63;
549 glTexSubImage2D(GL_TEXTURE_2D, 0, (sub_index & 7) * 64, ((sub_index >> 3) & 7) * 64, 64, 64, GL_RED, GL_UNSIGNED_BYTE, pixel_buffer);
550 }
551 FT_Done_Glyph(g_result);
552 //after texture slot
553 glyph_positions.insert_or_assign(ch_in, gso);
555 }
556}
557
559 state.font_collection.get_font(state, type).remake_cache(state, type, *this, s);
560}
561stored_glyphs::stored_glyphs(std::string const& s, font& f) {
562 f.remake_cache(*this, s);
563}
564
566 state.font_collection.get_font(state, type).remake_cache(state, type, *this, s);
567}
569 state.font_collection.get_font(state, type).remake_bidiless_cache(state, type, *this, s);
570}
571
573 state.font_collection.get_font(state, type).remake_cache(state, type, *this, s);
574}
575
577 glyph_info.resize(count);
578 std::copy_n(other.glyph_info.data() + offset, count, glyph_info.data());
579}
580
582 txt.glyph_info.clear();
583
584 if(source.size() == 0)
585 return;
586
588 for(uint32_t i = 0; i < uint32_t(source.size()); i++) {
589 text::stored_glyph glyph;
590 glyph.codepoint = source[i];
591 glyph.cluster = i;
592 txt.glyph_info.push_back(glyph);
593 }
594 return;
595 }
596
597 auto locale = state.font_collection.get_current_locale();
598 UBiDi* para;
599 UErrorCode errorCode = U_ZERO_ERROR;
600
601 para = ubidi_open();
602 //para = ubidi_openSized(int32_t(temp_text.size()), 64, pErrorCode);
603 if(!para)
604 std::abort();
605
606 hb_feature_t feature_buffer[10];
607 auto features = type == font_selection::body_font ? state.world.locale_get_body_font_features(locale)
608 : type == font_selection::header_font ? state.world.locale_get_header_font_features(locale)
609 : state.world.locale_get_map_font_features(locale);
610 for(uint32_t i = 0; i < uint32_t(std::extent_v<decltype(feature_buffer)>) && i < features.size(); ++i) {
611 feature_buffer[i].tag = features[i];
612 feature_buffer[i].start = 0;
613 feature_buffer[i].end = (unsigned int)-1;
614 feature_buffer[i].value = 1;
615 }
616 uint32_t hb_feature_count = std::min(features.size(), uint32_t(std::extent_v<decltype(feature_buffer)>));
617
618 ubidi_setPara(para, (UChar const*)(source.data()), int32_t(source.size()), state.world.locale_get_native_rtl(locale) ? 1 : 0, nullptr, &errorCode);
619
620 if(U_SUCCESS(errorCode)) {
621 auto runcount = ubidi_countRuns(para, &errorCode);
622 if(U_SUCCESS(errorCode)) {
623 for(int32_t i = 0; i < runcount; ++i) {
624 int32_t logical_start = 0;
625 int32_t length = 0;
626 auto direction = ubidi_getVisualRun(para, i, &logical_start, &length);
627
628 // shape run with harfbuzz
629 hb_buffer_clear_contents(hb_buf);
630 hb_buffer_add_utf16(hb_buf, source.data(), int32_t(source.size()), logical_start, length);
631
632 hb_buffer_set_direction(hb_buf, direction == UBIDI_RTL ? HB_DIRECTION_RTL : HB_DIRECTION_LTR);
633 hb_buffer_set_script(hb_buf, (hb_script_t)state.world.locale_get_hb_script(locale));
634 hb_buffer_set_language(hb_buf, state.world.locale_get_resolved_language(locale));
635
636 hb_shape(hb_font_face, hb_buf, feature_buffer, hb_feature_count);
637
638 uint32_t gcount = 0;
639 hb_glyph_info_t* glyph_info = hb_buffer_get_glyph_infos(hb_buf, &gcount);
640 hb_glyph_position_t* glyph_pos = hb_buffer_get_glyph_positions(hb_buf, &gcount);
641
642 for(unsigned int j = 0; j < gcount; j++) { // Preload glyphs
643 make_glyph(glyph_info[j].codepoint);
644 txt.glyph_info.emplace_back(glyph_info[j], glyph_pos[j]);
645 }
646 }
647 } else {
648 // failure to get number of runs
649 std::abort();
650 }
651 } else {
652 // failure to add text
653 std::abort();
654 }
655
656 ubidi_close(para);
657}
658
660 txt.glyph_info.clear();
661 if(source.size() == 0)
662 return;
663
665 for(uint32_t i = 0; i < uint32_t(source.size()); i++) {
666 text::stored_glyph glyph;
667 glyph.codepoint = source[i];
668 glyph.cluster = i;
669 txt.glyph_info.push_back(glyph);
670 }
671 return;
672 }
673
674 auto locale = state.font_collection.get_current_locale();
675
676 hb_feature_t feature_buffer[10];
677 auto features = type == font_selection::body_font ? state.world.locale_get_body_font_features(locale)
678 : type == font_selection::header_font ? state.world.locale_get_header_font_features(locale)
679 : state.world.locale_get_map_font_features(locale);
680 for(uint32_t i = 0; i < uint32_t(std::extent_v<decltype(feature_buffer)>) && i < features.size(); ++i) {
681 feature_buffer[i].tag = features[i];
682 feature_buffer[i].start = 0;
683 feature_buffer[i].end = (unsigned int)-1;
684 feature_buffer[i].value = 1;
685 }
686 uint32_t hb_feature_count = std::min(features.size(), uint32_t(std::extent_v<decltype(feature_buffer)>));
687
688 // shape run with harfbuzz
689 hb_buffer_clear_contents(hb_buf);
690 hb_buffer_add_utf16(hb_buf, source.data(), int32_t(source.size()), 0, int32_t(source.size()));
691
692 hb_buffer_set_direction(hb_buf, state.world.locale_get_native_rtl(locale) ? HB_DIRECTION_RTL : HB_DIRECTION_LTR);
693 hb_buffer_set_script(hb_buf, (hb_script_t)state.world.locale_get_hb_script(locale));
694 hb_buffer_set_language(hb_buf, state.world.locale_get_resolved_language(locale));
695
696 hb_shape(hb_font_face, hb_buf, feature_buffer, hb_feature_count);
697
698 uint32_t gcount = 0;
699 hb_glyph_info_t* glyph_info = hb_buffer_get_glyph_infos(hb_buf, &gcount);
700 hb_glyph_position_t* glyph_pos = hb_buffer_get_glyph_positions(hb_buf, &gcount);
701
702 for(unsigned int j = 0; j < gcount; j++) { // Preload glyphs
703 make_glyph(glyph_info[j].codepoint);
704 txt.glyph_info.emplace_back(glyph_info[j], glyph_pos[j]);
705 }
706
707 if(state.world.locale_get_native_rtl(locale)) {
708 std::reverse(txt.glyph_info.begin(), txt.glyph_info.end());
709 }
710}
711
712void font::remake_cache(stored_glyphs& txt, std::string const& s) {
713 txt.glyph_info.clear();
714 if(s.length() == 0)
715 return;
716
718 for(uint32_t i = 0; i < uint32_t(s.size());) {
719 text::stored_glyph glyph;
720 glyph.codepoint = text::codepoint_from_utf8(s.data() + i, s.data() + s.size());
721 glyph.cluster = i;
722 txt.glyph_info.push_back(glyph);
723 i += uint32_t(text::size_from_utf8(s.data() + i, s.data() + s.size()));
724 }
725 return;
726 }
727
728 hb_buffer_clear_contents(hb_buf);
729 hb_buffer_add_utf8(hb_buf, s.c_str(), int(s.length()), 0, int(s.length()));
730
731 hb_buffer_set_direction(hb_buf, HB_DIRECTION_LTR);
732 hb_buffer_set_script(hb_buf, HB_SCRIPT_LATIN);
733 hb_buffer_set_language(hb_buf, hb_language_from_string("en", -1));
734
735 hb_feature_t feature_buffer[10];
736 hb_shape(hb_font_face, hb_buf, feature_buffer, 0);
737
738 uint32_t gcount = 0;
739 hb_glyph_info_t* glyph_info = hb_buffer_get_glyph_infos(hb_buf, &gcount);
740 hb_glyph_position_t* glyph_pos = hb_buffer_get_glyph_positions(hb_buf, &gcount);
741 for(unsigned int i = 0; i < gcount; i++) { // Preload glyphs
742 make_glyph(glyph_info[i].codepoint);
743 txt.glyph_info.emplace_back(glyph_info[i], glyph_pos[i]);
744 }
745}
746
748 txt.glyph_info.clear();
749 if(s.length() == 0)
750 return;
751
753 for(uint32_t i = 0; i < uint32_t(s.size());) {
754 text::stored_glyph glyph;
755 glyph.codepoint = text::codepoint_from_utf8(s.data() + i, s.data() + s.size());
756 glyph.cluster = i;
757 txt.glyph_info.push_back(glyph);
758 i += uint32_t(text::size_from_utf8(s.data() + i, s.data() + s.size()));
759 }
760 return;
761 }
762
763 auto locale = state.font_collection.get_current_locale();
764 if(state.world.locale_get_native_rtl(locale) == false) {
765 hb_buffer_clear_contents(hb_buf);
766 hb_buffer_add_utf8(hb_buf, s.c_str(), int(s.length()), 0, int(s.length()));
767
768 hb_buffer_set_direction(hb_buf, HB_DIRECTION_LTR);
769 hb_buffer_set_script(hb_buf, (hb_script_t)state.world.locale_get_hb_script(locale));
770 hb_buffer_set_language(hb_buf, state.world.locale_get_resolved_language(locale));
771
772 hb_feature_t feature_buffer[10];
773 auto features = type == font_selection::body_font ? state.world.locale_get_body_font_features(locale)
774 : type == font_selection::header_font ? state.world.locale_get_header_font_features(locale)
775 : state.world.locale_get_map_font_features(locale);
776 for(uint32_t i = 0; i < uint32_t(std::extent_v<decltype(feature_buffer)>) && i < features.size(); ++i) {
777 feature_buffer[i].tag = features[i];
778 feature_buffer[i].start = 0;
779 feature_buffer[i].end = (unsigned int)-1;
780 feature_buffer[i].value = 1;
781 }
782 uint32_t hb_feature_count = std::min(features.size(), uint32_t(std::extent_v<decltype(feature_buffer)>));
783 hb_shape(hb_font_face, hb_buf, feature_buffer, hb_feature_count);
784
785 uint32_t gcount = 0;
786 hb_glyph_info_t* glyph_info = hb_buffer_get_glyph_infos(hb_buf, &gcount);
787 hb_glyph_position_t* glyph_pos = hb_buffer_get_glyph_positions(hb_buf, &gcount);
788 for(unsigned int i = 0; i < gcount; i++) { // Preload glyphs
789 make_glyph(glyph_info[i].codepoint);
790 txt.glyph_info.emplace_back(glyph_info[i], glyph_pos[i]);
791 }
792 } else {
793 std::vector<uint16_t> temp_text;
794 std::vector<uint16_t> to_base_char;
795
796 auto start = s.c_str();
797 auto end = start + s.length();
798 int32_t base_index = 0;
799 while(start + base_index < end) {
800 auto c = text::codepoint_from_utf8(start + base_index, end);
802 temp_text.push_back(char16_t(c));
803 to_base_char.push_back(uint16_t(base_index));
804 } else {
805 auto p = make_surrogate_pair(c);
806 temp_text.push_back(char16_t(p.high));
807 temp_text.push_back(char16_t(p.low));
808 to_base_char.push_back(uint16_t(base_index));
809 to_base_char.push_back(uint16_t(base_index));
810 }
811 base_index += int32_t(size_from_utf8(start + base_index, end));
812 }
813
814 UErrorCode errorCode = U_ZERO_ERROR;
815 UBiDi* para = ubidi_open();
816 //para = ubidi_openSized(int32_t(temp_text.size()), 64, pErrorCode);
817 if(!para)
818 std::abort();
819
820 hb_feature_t feature_buffer[10];
821 auto features = type == font_selection::body_font ? state.world.locale_get_body_font_features(locale)
822 : type == font_selection::header_font ? state.world.locale_get_header_font_features(locale)
823 : state.world.locale_get_map_font_features(locale);
824 for(uint32_t i = 0; i < uint32_t(std::extent_v<decltype(feature_buffer)>) && i < features.size(); ++i) {
825 feature_buffer[i].tag = features[i];
826 feature_buffer[i].start = 0;
827 feature_buffer[i].end = (unsigned int)-1;
828 feature_buffer[i].value = 1;
829 }
830 uint32_t hb_feature_count = std::min(features.size(), uint32_t(std::extent_v<decltype(feature_buffer)>));
831
832 ubidi_setPara(para, (UChar const*)temp_text.data(), int32_t(temp_text.size()), 1, nullptr, &errorCode);
833
834 if(U_SUCCESS(errorCode)) {
835 auto runcount = ubidi_countRuns(para, &errorCode);
836 if(U_SUCCESS(errorCode)) {
837 for(int32_t i = 0; i < runcount; ++i) {
838 int32_t logical_start = 0;
839 int32_t length = 0;
840 auto direction = ubidi_getVisualRun(para, i, &logical_start, &length);
841
842 // shape run with harfbuzz
843 hb_buffer_clear_contents(hb_buf);
844 hb_buffer_add_utf16(hb_buf, temp_text.data(), int32_t(temp_text.size()), logical_start, length);
845
846 hb_buffer_set_direction(hb_buf, direction == UBIDI_RTL ? HB_DIRECTION_RTL : HB_DIRECTION_LTR);
847 hb_buffer_set_script(hb_buf, (hb_script_t)state.world.locale_get_hb_script(locale));
848 hb_buffer_set_language(hb_buf, state.world.locale_get_resolved_language(locale));
849
850 hb_shape(hb_font_face, hb_buf, feature_buffer, hb_feature_count);
851
852 uint32_t gcount = 0;
853 hb_glyph_info_t* glyph_info = hb_buffer_get_glyph_infos(hb_buf, &gcount);
854 hb_glyph_position_t* glyph_pos = hb_buffer_get_glyph_positions(hb_buf, &gcount);
855
856 for(unsigned int j = 0; j < gcount; j++) { // Preload glyphs
857 make_glyph(glyph_info[j].codepoint);
858 txt.glyph_info.emplace_back(glyph_info[j], glyph_pos[j]);
859 txt.glyph_info.back().cluster = to_base_char[glyph_info[j].cluster];
860 }
861 }
862 } else {
863 // failure to get number of runs
864 std::abort();
865 }
866 } else {
867 // failure to add text
868 std::abort();
869 }
870
871 ubidi_close(para);
872 }
873}
874
875float font::text_extent(sys::state& state, stored_glyphs const& txt, uint32_t starting_offset, uint32_t count, int32_t size) {
876 float x_total = 0.0f;
877 for(uint32_t i = starting_offset; i < starting_offset + count; i++) {
878 hb_codepoint_t glyphid = txt.glyph_info[i].codepoint;
879 float x_advance = float(txt.glyph_info[i].x_advance) / (float((1 << 6) * text::magnification_factor));
880 x_total += x_advance * size / 64.f;
881 }
882 return x_total;
883}
884
886 for(auto& fnt : font_array) {
887 fnt.only_raw_codepoints = v;
888 }
889}
890
891} // namespace text
892
float get_string_width(sys::state &state, char const *, uint32_t) const
Definition: bmfont.cpp:139
float get_height() const
Definition: bmfont.hpp:98
void change_locale(sys::state &state, dcon::locale_id l)
Definition: fonts.cpp:281
void load_font(font &fnt, char const *file_data, uint32_t file_size)
Definition: fonts.cpp:438
FT_Library ft_library
Definition: fonts.hpp:180
float line_height(sys::state &state, uint16_t font_id)
Definition: fonts.cpp:151
float text_extent(sys::state &state, stored_glyphs const &txt, uint32_t starting_offset, uint32_t count, uint16_t font_id)
Definition: fonts.cpp:138
font & get_font(sys::state &state, font_selection s=font_selection::body_font)
Definition: fonts.cpp:423
void set_classic_fonts(bool v)
Definition: fonts.cpp:885
float top_adjustment(int32_t size) const
Definition: fonts.cpp:463
float descender(int32_t size) const
Definition: fonts.cpp:460
float line_height(int32_t size) const
Definition: fonts.cpp:454
bool only_raw_codepoints
Definition: fonts.hpp:123
hb_font_t * hb_font_face
Definition: fonts.hpp:110
float text_extent(sys::state &state, stored_glyphs const &txt, uint32_t starting_offset, uint32_t count, int32_t size)
Definition: fonts.cpp:875
ankerl::unordered_dense::map< char32_t, glyph_sub_offset > glyph_positions
Definition: fonts.hpp:117
float internal_ascender
Definition: fonts.hpp:114
uint16_t first_free_slot
Definition: fonts.hpp:121
float internal_descender
Definition: fonts.hpp:115
FT_Face font_face
Definition: fonts.hpp:109
float internal_top_adj
Definition: fonts.hpp:116
float ascender(int32_t size) const
Definition: fonts.cpp:457
std::unique_ptr< FT_Byte[]> file_data
Definition: fonts.hpp:122
void remake_cache(sys::state &state, font_selection type, stored_glyphs &txt, std::string const &source)
Definition: fonts.cpp:747
void remake_bidiless_cache(sys::state &state, font_selection type, stored_glyphs &txt, std::span< uint16_t > source)
Definition: fonts.cpp:659
float base_glyph_width(char32_t ch_in)
Definition: fonts.cpp:471
hb_buffer_t * hb_buf
Definition: fonts.hpp:111
void make_glyph(char32_t ch_in)
Definition: fonts.cpp:478
bool can_display(char32_t ch_in) const
Definition: fonts.cpp:467
float internal_line_height
Definition: fonts.hpp:113
std::vector< uint32_t > textures
Definition: fonts.hpp:118
#define assert(condition)
Definition: debug.h:74
bool has_fixed_suffix_ci(char const *start, char const *end, char const (&t)[N])
Definition: parsers.hpp:229
bool has_fixed_prefix_ci(char const *start, char const *end, char const (&t)[N])
Definition: parsers.hpp:207
directory open_directory(directory const &dir, native_string_view directory_name)
native_string utf8_to_native(std::string_view data_in)
directory get_root(file_system const &fs)
std::optional< file > open_file(directory const &dir, native_string_view file_name)
file_contents view_contents(file const &f)
Definition: bmfont.cpp:118
constexpr int magnification_factor
Definition: fonts.hpp:17
int32_t size_from_font_id(uint16_t id)
Definition: fonts.cpp:119
font_selection
Definition: fonts.hpp:20
int32_t transform_offset_b(int32_t x, int32_t y, int32_t btmap_x_off, int32_t btmap_y_off, uint32_t width, uint32_t height, uint32_t pitch)
Definition: fonts.cpp:172
constexpr int dr_size
Definition: fonts.hpp:18
bm_font const & get_bm_font(sys::state &state, uint16_t font_handle)
Definition: bmfont.cpp:162
bool is_black_from_font_id(uint16_t id)
Definition: fonts.cpp:127
uint32_t codepoint_from_utf8(char const *start, char const *end)
Definition: text.cpp:66
surrogate_pair make_surrogate_pair(uint32_t val) noexcept
Definition: fonts.hpp:55
uint32_t font_index(std::string_view txt)
Definition: fonts.cpp:68
void init_in_map(bool in_map[dr_size *dr_size], uint8_t const *bmp_data, int32_t btmap_x_off, int32_t btmap_y_off, uint32_t width, uint32_t height, uint32_t pitch)
Definition: fonts.cpp:186
bool is_black_font(std::string_view txt)
Definition: fonts.cpp:25
size_t size_from_utf8(char const *start, char const *)
Definition: text.cpp:82
uint16_t name_into_font_id(sys::state &state, std::string_view txt)
Definition: fonts.cpp:93
constexpr float rt_2
Definition: fonts.cpp:184
bool requires_surrogate_pair(uint32_t codepoint)
Definition: fonts.hpp:46
void dead_reckoning(float distance_map[dr_size *dr_size], bool const in_map[dr_size *dr_size])
Definition: fonts.cpp:200
font_selection font_index_from_font_id(sys::state &state, uint16_t id)
Definition: fonts.cpp:130
constexpr uint16_t pack_font_handle(uint32_t font_index, bool black, uint32_t size)
Definition: fonts.cpp:21
uint32_t font_size(std::string_view txt)
Definition: fonts.cpp:35
#define NATIVE(X)
uint uint32_t
uchar uint8_t
uint16_t texture_slot
Definition: fonts.hpp:35
uint32_t cluster
Definition: fonts.hpp:64
uint32_t codepoint
Definition: fonts.hpp:63
stored_glyphs()=default
std::vector< stored_glyph > glyph_info
Definition: fonts.hpp:82
void set_text(sys::state &state, font_selection type, std::string const &s)
Definition: fonts.cpp:572