💿🐜 Antkeeper source code https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

178 lines
6.4 KiB

  1. /*
  2. * Copyright (C) 2021 Christopher J. Howard
  3. *
  4. * This file is part of Antkeeper source code.
  5. *
  6. * Antkeeper source code is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * Antkeeper source code is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include "game/fonts.hpp"
  20. #include "application.hpp"
  21. #include "type/type.hpp"
  22. #include "resources/resource-manager.hpp"
  23. #include "gl/texture-wrapping.hpp"
  24. #include "gl/texture-filter.hpp"
  25. #include "render/material.hpp"
  26. #include "render/material-flags.hpp"
  27. #include <codecvt>
  28. namespace game {
  29. static void build_bitmap_font(const type::typeface& typeface, float size, const std::unordered_set<char32_t>& charset, type::bitmap_font& font, render::material& material, gl::shader_program* shader_program)
  30. {
  31. // Get font metrics for given size
  32. if (type::font_metrics metrics; typeface.get_metrics(size, metrics))
  33. font.set_font_metrics(metrics);
  34. // Format font bitmap
  35. image& font_bitmap = font.get_bitmap();
  36. font_bitmap.format(sizeof(std::byte), 1);
  37. // For each UTF-32 character code in the character set
  38. for (char32_t code: charset)
  39. {
  40. // Skip missing glyphs
  41. if (!typeface.has_glyph(code))
  42. continue;
  43. // Add glyph to font
  44. type::bitmap_glyph& glyph = font[code];
  45. typeface.get_metrics(size, code, glyph.metrics);
  46. typeface.get_bitmap(size, code, glyph.bitmap);
  47. }
  48. // Pack glyph bitmaps into the font bitmap
  49. font.pack();
  50. // Create font texture from bitmap
  51. gl::texture_2d* font_texture = new gl::texture_2d(font_bitmap.get_width(), font_bitmap.get_height(), gl::pixel_type::uint_8, gl::pixel_format::r, gl::color_space::linear, font_bitmap.get_pixels());
  52. font_texture->set_wrapping(gl::texture_wrapping::extend, gl::texture_wrapping::extend);
  53. font_texture->set_filters(gl::texture_min_filter::linear, gl::texture_mag_filter::linear);
  54. // Create font material
  55. material.set_flags(MATERIAL_FLAG_TRANSLUCENT);
  56. material.add_property<const gl::texture_2d*>("font_bitmap")->set_value(font_texture);
  57. material.set_shader_program(shader_program);
  58. }
  59. void load_fonts(game::context* ctx)
  60. {
  61. // Load dyslexia-friendly typeface (if enabled)
  62. bool dyslexia_font = false;
  63. if (ctx->config->contains("dyslexia_font"))
  64. {
  65. dyslexia_font = (*ctx->config)["dyslexia_font"].get<bool>();
  66. if (dyslexia_font)
  67. {
  68. if (auto it = ctx->strings->find("font_dyslexia"); it != ctx->strings->end() && !it->second.empty() && it->second[0] != '#')
  69. {
  70. ctx->logger->log(it->second);
  71. ctx->typefaces["dyslexia"] = ctx->resource_manager->load<type::typeface>(it->second);
  72. }
  73. else
  74. {
  75. dyslexia_font = false;
  76. }
  77. }
  78. }
  79. // Load typefaces
  80. if (dyslexia_font)
  81. {
  82. // Override standard typefaces with dyslexia-friendly typeface
  83. ctx->typefaces["serif"] = ctx->typefaces["dyslexia"];
  84. ctx->typefaces["sans_serif"] = ctx->typefaces["dyslexia"];
  85. ctx->typefaces["monospace"] = ctx->typefaces["dyslexia"];
  86. }
  87. else
  88. {
  89. // Load standard typefaces
  90. if (auto it = ctx->strings->find("font_serif"); it != ctx->strings->end())
  91. ctx->typefaces["serif"] = ctx->resource_manager->load<type::typeface>(it->second);
  92. if (auto it = ctx->strings->find("font_sans_serif"); it != ctx->strings->end())
  93. ctx->typefaces["sans_serif"] = ctx->resource_manager->load<type::typeface>(it->second);
  94. if (auto it = ctx->strings->find("font_monospace"); it != ctx->strings->end())
  95. ctx->typefaces["monospace"] = ctx->resource_manager->load<type::typeface>(it->second);
  96. }
  97. // Build character set
  98. std::unordered_set<char32_t> charset;
  99. {
  100. // Add all character codes from the basic Latin unicode block
  101. for (char32_t code = type::unicode::block::basic_latin.first; code <= type::unicode::block::basic_latin.last; ++code)
  102. charset.insert(code);
  103. // Add all character codes from game strings
  104. for (auto it = ctx->strings->begin(); it != ctx->strings->end(); ++it)
  105. {
  106. // Convert UTF-8 string to UTF-32
  107. std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> convert;
  108. std::u32string u32 = convert.from_bytes(it->second);
  109. /// Insert each character code from the UTF-32 string into the character set
  110. for (char32_t code: u32)
  111. charset.insert(code);
  112. }
  113. }
  114. // Load bitmap font shader
  115. gl::shader_program* bitmap_font_shader = ctx->resource_manager->load<gl::shader_program>("bitmap-font.glsl");
  116. // Determine font point sizes
  117. float debug_font_size_pt = 12.0f;
  118. float menu_font_size_pt = 12.0f;
  119. float title_font_size_pt = 12.0f;
  120. if (ctx->config->contains("debug_font_size"))
  121. debug_font_size_pt = (*ctx->config)["debug_font_size"].get<float>();
  122. if (ctx->config->contains("menu_font_size"))
  123. menu_font_size_pt = (*ctx->config)["menu_font_size"].get<float>();
  124. if (ctx->config->contains("title_font_size"))
  125. title_font_size_pt = (*ctx->config)["title_font_size"].get<float>();
  126. // Scale font point sizes
  127. if (ctx->config->contains("font_scale"))
  128. {
  129. const float font_scale = (*ctx->config)["font_scale"].get<float>();
  130. debug_font_size_pt *= font_scale;
  131. menu_font_size_pt *= font_scale;
  132. title_font_size_pt *= font_scale;
  133. }
  134. // Convert font point sizes to pixel sizes
  135. const float dpi = ctx->app->get_display_dpi();
  136. const float debug_font_size_px = (debug_font_size_pt * dpi) / 72.0f;
  137. const float menu_font_size_px = (menu_font_size_pt * dpi) / 72.0f;
  138. const float title_font_size_px = (title_font_size_pt * dpi) / 72.0f;
  139. // Build debug font
  140. if (auto it = ctx->typefaces.find("monospace"); it != ctx->typefaces.end())
  141. {
  142. build_bitmap_font(*it->second, debug_font_size_px, charset, ctx->debug_font, ctx->debug_font_material, bitmap_font_shader);
  143. }
  144. // Build menu font
  145. if (auto it = ctx->typefaces.find("sans_serif"); it != ctx->typefaces.end())
  146. {
  147. build_bitmap_font(*it->second, menu_font_size_px, charset, ctx->menu_font, ctx->menu_font_material, bitmap_font_shader);
  148. }
  149. // Build title font
  150. if (auto it = ctx->typefaces.find("serif"); it != ctx->typefaces.end())
  151. {
  152. build_bitmap_font(*it->second, title_font_size_px, charset, ctx->title_font, ctx->title_font_material, bitmap_font_shader);
  153. }
  154. }
  155. } // namespace game