14#include <unicode/ubrk.h>
15#include <unicode/utypes.h>
16#include <unicode/ubidi.h>
36 char const* first_int = txt.data();
37 char const* end = txt.data() + txt.size();
38 while(first_int != end && !isdigit(*first_int))
40 char const* last_int = first_int;
41 while(last_int != end && isdigit(*last_int))
44 if(first_int == last_int) {
64 std::from_chars(first_int, last_int, rvalue);
95 std::string txt_copy = [&]() {
97 return std::string(txt.substr(0, txt.length() - 6));
100 return std::string(txt.substr(0, txt.length() - 11)) +
"_bold";
102 return std::string(txt);
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));
111 it =
state.font_collection.font_names.find(uint16_t(base_id | (individuator << 8)));
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);
120 auto index =
uint32_t(((
id >> 7) & 0x01) + 1);
122 return (int32_t(
id & 0x3F) * 3) / 4;
124 return (int32_t(
id & 0x3F) * 5) / 6;
128 return ((
id >> 6) & 0x01) != 0;
132 if(((
id >> 7) & 0x01) == 0)
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));
152 if(
state.user_settings.use_classic_fonts) {
167 hb_buffer_destroy(
hb_buf);
174 int bmp_x =
x - btmap_x_off;
175 int bmp_y =
y - btmap_y_off;
177 if((bmp_x < 0) || (bmp_x >= (int32_t)width) || (bmp_y < 0) || (bmp_y >= (int32_t)height))
180 return bmp_x + bmp_y * (int32_t)pitch;
184constexpr float rt_2 = 1.41421356237309504f;
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;
205 distance_map[i] = std::numeric_limits<float>::infinity();
207 for(int32_t j = 1; j <
dr_size - 1; ++j) {
208 for(int32_t i = 1; i <
dr_size - 1; ++i) {
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);
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)]) {
222 distance_map[(i) +
dr_size * (j)] = (float)std::sqrt((i - xborder[i +
dr_size * j]) * (i - xborder[i +
dr_size * j]) +
225 if(distance_map[(i) +
dr_size * (j - 1)] + 1.0f < distance_map[(i) +
dr_size * (j)]) {
228 distance_map[(i) +
dr_size * (j)] = (float)std::sqrt((i - xborder[i +
dr_size * j]) * (i - xborder[i +
dr_size * j]) +
231 if(distance_map[(i + 1) +
dr_size * (j - 1)] +
rt_2 < distance_map[(i) +
dr_size * (j)]) {
234 distance_map[(i) +
dr_size * (j)] = (float)std::sqrt((i - xborder[i +
dr_size * j]) * (i - xborder[i +
dr_size * j]) +
237 if(distance_map[(i - 1) +
dr_size * (j)] + 1.0f < distance_map[(i) +
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]) +
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)]) {
251 distance_map[(i) +
dr_size * (j)] = (float)std::sqrt((i - xborder[i +
dr_size * j]) * (i - xborder[i +
dr_size * j]) +
254 if(distance_map[(i - 1) +
dr_size * (j + 1)] +
rt_2 < distance_map[(i) +
dr_size * (j)]) {
257 distance_map[(i) +
dr_size * (j)] = (float)std::sqrt((i - xborder[i +
dr_size * j]) * (i - xborder[i +
dr_size * j]) +
260 if(distance_map[(i) +
dr_size * (j + 1)] + 1.0f < distance_map[(i) +
dr_size * (j)]) {
263 distance_map[(i) +
dr_size * (j)] = (float)std::sqrt((i - xborder[i +
dr_size * j]) * (i - xborder[i +
dr_size * j]) +
266 if(distance_map[(i + 1) +
dr_size * (j + 1)] +
rt_2 < distance_map[(i) +
dr_size * (j)]) {
269 distance_map[(i) +
dr_size * (j)] = (float)std::sqrt((i - xborder[i +
dr_size * j]) * (i - xborder[i +
dr_size * j]) +
277 distance_map[i] *= -1.0f;
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] ==
'-')
293 std::string lang_str{ localename_sv .substr(0, end_language) };
295 state.world.locale_set_resolved_language(l, hb_language_from_string(localename_sv.data(),
int(end_language)));
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;
303 for(
auto& fnt : font_array) {
304 if(fnt.file_name == fname) {
320 font_array.emplace_back();
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());
328 state.world.locale_set_resolved_body_font(l,
count);
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;
336 for(
auto& fnt : font_array) {
337 if(fnt.file_name == fname) {
353 font_array.emplace_back();
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());
361 state.world.locale_set_resolved_header_font(l,
count);
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;
369 for(
auto& fnt : font_array) {
370 if(fnt.file_name == fname) {
386 font_array.emplace_back();
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());
394 state.world.locale_set_resolved_map_font(l,
count);
397 state.reset_locale_pool();
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);
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)) {
410 auto rule_size = ubrk_getBinaryRules(lb_it,
nullptr, 0, &errorCode);
411 if(rule_size == 0 || !U_SUCCESS(errorCode)) {
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);
420 state.load_locale_strings(localename_sv);
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)];
439 fnt.
file_data = std::unique_ptr<FT_Byte[]>(
new FT_Byte[file_size]);
441 memcpy(fnt.
file_data.get(), file_data, file_size);
443 FT_Select_Charmap(fnt.
font_face, FT_ENCODING_UNICODE);
446 fnt.
hb_buf = hb_buffer_create();
468 return FT_Get_Char_Index(
font_face, ch_in) != 0;
473 return it->second.x_advance;
484 FT_Load_Glyph(
font_face, ch_in, FT_LOAD_TARGET_NORMAL | FT_LOAD_RENDER);
487 auto err = FT_Get_Glyph(
font_face->glyph, &g_result);
498 FT_Bitmap
const& bitmap = ((FT_BitmapGlyphRec*)g_result)->bitmap;
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;
504 uint8_t pixel_buffer[64 * 64] = { 0 };
515 init_in_map(in_map, bitmap.buffer, btmap_x_off, btmap_y_off, bitmap.width, bitmap.rows,
uint32_t(bitmap.pitch));
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);
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;
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);
540 glClearTexImage(GL_TEXTURE_2D, 0, GL_RED, GL_UNSIGNED_BYTE, &clearvalue);
545 glBindTexture(GL_TEXTURE_2D, texid);
549 glTexSubImage2D(GL_TEXTURE_2D, 0, (sub_index & 7) * 64, ((sub_index >> 3) & 7) * 64, 64, 64, GL_RED, GL_UNSIGNED_BYTE, pixel_buffer);
551 FT_Done_Glyph(g_result);
597 auto locale =
state.font_collection.get_current_locale();
599 UErrorCode errorCode = U_ZERO_ERROR;
606 hb_feature_t feature_buffer[10];
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;
616 uint32_t hb_feature_count = std::min(features.size(),
uint32_t(std::extent_v<
decltype(feature_buffer)>));
618 ubidi_setPara(para, (UChar
const*)(
source.data()), int32_t(
source.size()),
state.world.locale_get_native_rtl(locale) ? 1 : 0,
nullptr, &errorCode);
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;
626 auto direction = ubidi_getVisualRun(para, i, &logical_start, &length);
629 hb_buffer_clear_contents(
hb_buf);
630 hb_buffer_add_utf16(
hb_buf,
source.data(), int32_t(
source.size()), logical_start, length);
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));
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);
642 for(
unsigned int j = 0; j < gcount; j++) {
644 txt.
glyph_info.emplace_back(glyph_info[j], glyph_pos[j]);
674 auto locale =
state.font_collection.get_current_locale();
676 hb_feature_t feature_buffer[10];
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;
686 uint32_t hb_feature_count = std::min(features.size(),
uint32_t(std::extent_v<
decltype(feature_buffer)>));
689 hb_buffer_clear_contents(
hb_buf);
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));
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);
702 for(
unsigned int j = 0; j < gcount; j++) {
704 txt.
glyph_info.emplace_back(glyph_info[j], glyph_pos[j]);
707 if(
state.world.locale_get_native_rtl(locale)) {
728 hb_buffer_clear_contents(
hb_buf);
729 hb_buffer_add_utf8(
hb_buf, s.c_str(),
int(s.length()), 0,
int(s.length()));
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));
735 hb_feature_t feature_buffer[10];
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++) {
743 txt.
glyph_info.emplace_back(glyph_info[i], glyph_pos[i]);
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()));
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));
772 hb_feature_t feature_buffer[10];
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;
782 uint32_t hb_feature_count = std::min(features.size(),
uint32_t(std::extent_v<
decltype(feature_buffer)>));
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++) {
790 txt.
glyph_info.emplace_back(glyph_info[i], glyph_pos[i]);
793 std::vector<uint16_t> temp_text;
794 std::vector<uint16_t> to_base_char;
796 auto start = s.c_str();
797 auto end = start + s.length();
798 int32_t base_index = 0;
799 while(start + base_index < end) {
802 temp_text.push_back(
char16_t(c));
803 to_base_char.push_back(uint16_t(base_index));
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));
814 UErrorCode errorCode = U_ZERO_ERROR;
815 UBiDi* para = ubidi_open();
820 hb_feature_t feature_buffer[10];
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;
830 uint32_t hb_feature_count = std::min(features.size(),
uint32_t(std::extent_v<
decltype(feature_buffer)>));
832 ubidi_setPara(para, (UChar
const*)temp_text.data(), int32_t(temp_text.size()), 1,
nullptr, &errorCode);
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;
840 auto direction = ubidi_getVisualRun(para, i, &logical_start, &length);
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);
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));
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);
856 for(
unsigned int j = 0; j < gcount; j++) {
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];
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;
880 x_total += x_advance *
size / 64.f;
886 for(
auto& fnt : font_array) {
887 fnt.only_raw_codepoints = v;
float get_string_width(sys::state &state, char const *, uint32_t) const
void change_locale(sys::state &state, dcon::locale_id l)
void load_font(font &fnt, char const *file_data, uint32_t file_size)
float line_height(sys::state &state, uint16_t font_id)
float text_extent(sys::state &state, stored_glyphs const &txt, uint32_t starting_offset, uint32_t count, uint16_t font_id)
font & get_font(sys::state &state, font_selection s=font_selection::body_font)
void set_classic_fonts(bool v)
float top_adjustment(int32_t size) const
float descender(int32_t size) const
float line_height(int32_t size) const
float text_extent(sys::state &state, stored_glyphs const &txt, uint32_t starting_offset, uint32_t count, int32_t size)
ankerl::unordered_dense::map< char32_t, glyph_sub_offset > glyph_positions
float ascender(int32_t size) const
std::unique_ptr< FT_Byte[]> file_data
void remake_cache(sys::state &state, font_selection type, stored_glyphs &txt, std::string const &source)
void remake_bidiless_cache(sys::state &state, font_selection type, stored_glyphs &txt, std::span< uint16_t > source)
float base_glyph_width(char32_t ch_in)
void make_glyph(char32_t ch_in)
bool can_display(char32_t ch_in) const
float internal_line_height
std::vector< uint32_t > textures
#define assert(condition)
bool has_fixed_suffix_ci(char const *start, char const *end, char const (&t)[N])
bool has_fixed_prefix_ci(char const *start, char const *end, char const (&t)[N])
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)
constexpr int magnification_factor
int32_t size_from_font_id(uint16_t id)
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)
bm_font const & get_bm_font(sys::state &state, uint16_t font_handle)
bool is_black_from_font_id(uint16_t id)
uint32_t codepoint_from_utf8(char const *start, char const *end)
surrogate_pair make_surrogate_pair(uint32_t val) noexcept
uint32_t font_index(std::string_view txt)
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)
bool is_black_font(std::string_view txt)
size_t size_from_utf8(char const *start, char const *)
uint16_t name_into_font_id(sys::state &state, std::string_view txt)
bool requires_surrogate_pair(uint32_t codepoint)
void dead_reckoning(float distance_map[dr_size *dr_size], bool const in_map[dr_size *dr_size])
font_selection font_index_from_font_id(sys::state &state, uint16_t id)
constexpr uint16_t pack_font_handle(uint32_t font_index, bool black, uint32_t size)
uint32_t font_size(std::string_view txt)
std::vector< stored_glyph > glyph_info
void set_text(sys::state &state, font_selection type, std::string const &s)