💿🐜 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.

219 lines
5.5 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 "type/bitmap-font.hpp"
  20. #include "geom/rect-pack.hpp"
  21. #include <stdexcept>
  22. namespace type {
  23. bitmap_font::bitmap_font(const font_metrics& metrics):
  24. font(metrics)
  25. {}
  26. bitmap_font::bitmap_font()
  27. {}
  28. bool bitmap_font::contains(char32_t code) const
  29. {
  30. return glyphs.count(code) != 0;
  31. }
  32. void bitmap_font::insert(char32_t code, const bitmap_glyph& glyph)
  33. {
  34. glyphs[code] = glyph;
  35. }
  36. void bitmap_font::remove(char32_t code)
  37. {
  38. if (auto it = glyphs.find(code); it != glyphs.end())
  39. glyphs.erase(it);
  40. }
  41. void bitmap_font::clear()
  42. {
  43. glyphs.clear();
  44. }
  45. bool bitmap_font::pack(bool resize)
  46. {
  47. // Returns the smallest power of two that is not smaller than @p x.
  48. auto ceil2 = [](unsigned int x) -> unsigned int
  49. {
  50. if (x <= 1)
  51. return 1;
  52. unsigned int y = 2;
  53. --x;
  54. while (x >>= 1)
  55. y <<= 1;
  56. return y;
  57. };
  58. // Calculate initial size of the font bitmap
  59. unsigned int bitmap_w;
  60. unsigned int bitmap_h;
  61. if (resize)
  62. {
  63. // Find the maximum glyph dimensions
  64. unsigned int max_glyph_w = 0;
  65. unsigned int max_glyph_h = 0;
  66. for (auto it = glyphs.begin(); it != glyphs.end(); ++it)
  67. {
  68. max_glyph_w = std::max(max_glyph_w, it->second.bitmap.get_width());
  69. max_glyph_h = std::max(max_glyph_h, it->second.bitmap.get_height());
  70. }
  71. // Find minimum power of two dimensions that can accommodate maximum glyph dimensions
  72. bitmap_w = ceil2(max_glyph_w);
  73. bitmap_h = ceil2(max_glyph_h);
  74. }
  75. else
  76. {
  77. bitmap_w = bitmap.get_width();
  78. bitmap_h = bitmap.get_height();
  79. }
  80. bool packed = false;
  81. geom::rect_pack<unsigned int> glyph_pack(bitmap_w, bitmap_h);
  82. std::unordered_map<char32_t, const typename geom::rect_pack<unsigned int>::node_type*> glyph_map;
  83. while (!packed)
  84. {
  85. // For each glyph
  86. for (auto it = glyphs.begin(); it != glyphs.end(); ++it)
  87. {
  88. // Attempt to pack glyph bitmap
  89. const auto* node = glyph_pack.pack(it->second.bitmap.get_width(), it->second.bitmap.get_height());
  90. // Abort if packing failed
  91. if (!node)
  92. break;
  93. // Map pack node to glyph character code
  94. glyph_map[it->first] = node;
  95. }
  96. // Check if not all glyphs were packed
  97. if (glyph_map.size() != glyphs.size())
  98. {
  99. if (!resize)
  100. {
  101. // No resize, packing failed
  102. packed = false;
  103. break;
  104. }
  105. // Clear glyph map
  106. glyph_map.clear();
  107. // Clear glyph pack
  108. glyph_pack.clear();
  109. // Resize glyph pack
  110. if (bitmap_w > bitmap_h)
  111. bitmap_h = ceil2(++bitmap_h);
  112. else
  113. bitmap_w = ceil2(++bitmap_w);
  114. glyph_pack.resize(bitmap_w, bitmap_h);
  115. }
  116. else
  117. {
  118. packed = true;
  119. }
  120. }
  121. // Copy glyph bitmaps into font bitmap
  122. if (packed)
  123. {
  124. // Resize font bitmap
  125. bitmap.resize(bitmap_w, bitmap_h);
  126. // For each glyph
  127. for (auto it = glyphs.begin(); it != glyphs.end(); ++it)
  128. {
  129. // Find rect pack node corresponding to the glyph
  130. const auto* node = glyph_map[it->first];
  131. // Copy glyph bitmap data into font bitmap
  132. image& glyph_bitmap = it->second.bitmap;
  133. bitmap.copy(glyph_bitmap, glyph_bitmap.get_width(), glyph_bitmap.get_height(), 0, 0, node->bounds.min.x, node->bounds.min.y);
  134. // Record coordinates of glyph bitmap within font bitmap
  135. it->second.position = {node->bounds.min.x, node->bounds.min.y};
  136. // Clear glyph bitmap data
  137. glyph_bitmap.resize(0, 0);
  138. }
  139. }
  140. return packed;
  141. }
  142. void bitmap_font::unpack(bool resize)
  143. {
  144. for (auto it = glyphs.begin(); it != glyphs.end(); ++it)
  145. {
  146. bitmap_glyph& glyph = it->second;
  147. // Get glyph dimensions
  148. unsigned int glyph_width = static_cast<unsigned int>(glyph.metrics.width + 0.5f);
  149. unsigned int glyph_height = static_cast<unsigned int>(glyph.metrics.height + 0.5f);
  150. // Reformat glyph bitmap if necessary
  151. if (!glyph.bitmap.compatible(bitmap))
  152. glyph.bitmap.format(bitmap.get_component_size(), bitmap.get_channel_count());
  153. // Resize glyph bitmap if necessary
  154. if (glyph.bitmap.get_width() != glyph_width || glyph.bitmap.get_height() != glyph_height)
  155. glyph.bitmap.resize(glyph_width, glyph_height);
  156. // Copy pixel data from font bitmap to glyph bitmap
  157. glyph.bitmap.copy(bitmap, glyph_width, glyph_height, glyph.position.x, glyph.position.y);
  158. }
  159. // Free font bitmap pixel data
  160. if (resize)
  161. {
  162. bitmap.resize(0, 0);
  163. }
  164. }
  165. const glyph_metrics& bitmap_font::get_glyph_metrics(char32_t code) const
  166. {
  167. if (auto it = glyphs.find(code); it != glyphs.end())
  168. return it->second.metrics;
  169. throw std::invalid_argument("Cannot fetch metrics of unknown bitmap glyph");
  170. }
  171. const bitmap_glyph& bitmap_font::get_glyph(char32_t code) const
  172. {
  173. if (auto it = glyphs.find(code); it != glyphs.end())
  174. return it->second;
  175. throw std::invalid_argument("Cannot get unknown bitmap glyph");
  176. }
  177. bitmap_glyph& bitmap_font::get_glyph(char32_t code)
  178. {
  179. if (auto it = glyphs.find(code); it != glyphs.end())
  180. return it->second;
  181. throw std::invalid_argument("Cannot get unknown bitmap glyph");
  182. }
  183. } // namespace type