HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
font_char_map.hpp
Go to the documentation of this file.
1// Copyright Take Vos 2023.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
9#pragma once
10
11#include "glyph_id.hpp"
12#include "../algorithm/algorithm.hpp"
13#include "../utility/utility.hpp"
14#include "../macros.hpp"
15#include <bitset>
16#include <cstdint>
17#include <vector>
18#include <tuple>
19#include <algorithm>
20#include <string>
21
22hi_export_module(hikogui.font.font_char_map);
23
24hi_export namespace hi { inline namespace v1 {
25
34hi_export class font_char_map {
35public:
36 constexpr font_char_map() noexcept = default;
37 constexpr font_char_map(font_char_map const&) noexcept = default;
38 constexpr font_char_map(font_char_map&&) noexcept = default;
39 constexpr font_char_map& operator=(font_char_map const&) noexcept = default;
40 constexpr font_char_map& operator=(font_char_map&&) noexcept = default;
41
42 [[nodiscard]] constexpr bool empty() const noexcept
43 {
44 return _map.empty();
45 }
46
50 constexpr void reserve(size_t n)
51 {
52 return _map.reserve(n);
53 }
54
57 constexpr size_t count() const noexcept
58 {
59 return _count;
60 }
61
67 constexpr size_t update_mask(std::bitset<0x11'0000>& mask) const noexcept
68 {
69 auto r = 0_uz;
70 for (auto const& entry : _map) {
71 // Make sure this loop is inclusive.
72 for (auto cp = entry.start_code_point(); cp <= entry.end_code_point; ++cp) {
73 if (not mask.test(cp)) {
74 ++r;
75 mask.set(cp);
76 }
77 }
78 }
79 return r;
80 }
81
88 constexpr void add(char32_t start_code_point, char32_t end_code_point, uint16_t start_glyph) noexcept
89 {
90#ifndef NDEBUG
91 _prepared = false;
92#endif
93 hi_axiom(start_code_point <= end_code_point);
94 auto todo = wide_cast<size_t>(end_code_point - start_code_point + 1);
95 _count += todo;
96 hi_axiom(start_glyph + todo < 0xffff, "Only glyph-ids 0 through 0xfffe are valid");
97
98 while (todo != 0) {
99 auto const doing = std::min(todo, entry_type::max_count);
100 hi_axiom(doing != 0);
101
102 _map.emplace_back(start_code_point, char_cast<char32_t>(start_code_point + doing - 1), start_glyph);
103
104 todo -= doing;
105 start_code_point += narrow_cast<char32_t>(doing);
106 start_glyph += narrow_cast<uint16_t>(doing);
107 }
108 }
109
115 void prepare() noexcept
116 {
117 if (_map.empty()) {
118 return;
119 }
120
121 // Sort the entries in reverse order so that the lower_bound search becomes upper_bound.
122 std::sort(_map.begin(), _map.end(), [](auto const& a, auto const& b) {
123 return a.end_code_point < b.end_code_point;
124 });
125
126 auto it = _map.begin();
127 auto prev_it = it++;
128 while (it != _map.end()) {
129 hi_axiom(prev_it->end_code_point < it->start_code_point());
130
131 if (mergable(*prev_it, *it)) {
132 auto const merged_count = std::min(prev_it->count() + it->count(), entry_type::max_count);
133 auto const move_count = merged_count - prev_it->count();
134 hi_axiom(move_count <= entry_type::max_count);
135
136 prev_it->end_code_point += narrow_cast<char32_t>(move_count);
137 prev_it->set_count(prev_it->count() + move_count);
138
139 if (move_count == it->count()) {
140 it = _map.erase(it);
141 // Don't change prev or it; since we just deleted instead of advanced.
142 continue;
143
144 } else {
145 hi_axiom(move_count < it->count());
146 it->start_glyph += narrow_cast<uint16_t>(move_count);
147 it->set_count(it->count() - move_count);
148 }
149 }
150
151 prev_it = it++;
152 }
153
154 _map.shrink_to_fit();
155
156#ifndef NDEBUG
157 _prepared = true;
158#endif
159 }
160
166 [[nodiscard]] inline glyph_id find(char32_t code_point) const noexcept
167 {
168#ifndef NDEBUG
169 hi_assert(_prepared);
170#endif
171
172 if (auto const item_ptr = fast_lower_bound(std::span{_map}, char_cast<uint32_t>(code_point))) {
173 return item_ptr->get(code_point);
174 }
175 return {};
176 }
177
178private:
179 struct entry_type {
180 constexpr static size_t max_count = 0x1'0000;
181
182 char32_t end_code_point;
183 uint16_t start_glyph;
184 uint16_t _count;
185
186 constexpr entry_type(char32_t start_code_point, char32_t end_code_point, uint16_t start_glyph) noexcept :
187 end_code_point(end_code_point),
188 start_glyph(start_glyph),
189 _count(narrow_cast<uint16_t>(end_code_point - start_code_point))
190 {
191 hi_axiom(start_code_point <= end_code_point);
192 }
193
194 [[nodiscard]] constexpr size_t count() const noexcept
195 {
196 return wide_cast<size_t>(_count) + 1;
197 }
198
199 constexpr void set_count(size_t new_count) noexcept
200 {
201 hi_axiom(new_count > 0);
202 hi_axiom(new_count <= max_count);
203 _count = narrow_cast<uint16_t>(new_count - 1);
204 }
205
206 [[nodiscard]] constexpr char32_t start_code_point() const noexcept
207 {
208 return end_code_point - _count;
209 }
210
211 [[nodiscard]] constexpr uint16_t end_glyph() const noexcept
212 {
213 return narrow_cast<uint16_t>(start_glyph + _count);
214 }
215
216 [[nodiscard]] constexpr friend bool mergable(entry_type const& lhs, entry_type const& rhs) noexcept
217 {
218 return lhs.end_code_point + 1 == rhs.start_code_point() and lhs.end_glyph() + 1 == rhs.start_glyph;
219 }
220
221 [[nodiscard]] constexpr glyph_id get(char32_t code_point) const noexcept
222 {
223 auto diff = wide_cast<ptrdiff_t>(code_point) - wide_cast<ptrdiff_t>(end_code_point);
224 if (diff > 0) {
225 return {};
226 }
227 diff += _count;
228 if (diff < 0) {
229 return {};
230 }
231 diff += start_glyph;
232 return glyph_id{truncate<uint16_t>(diff)};
233 }
234 };
235
236 std::vector<entry_type> _map = {};
237
240 size_t _count = 0;
241
242#ifndef NDEBUG
243 bool _prepared = false;
244#endif
245};
246
247}} // namespace hi::v1
The HikoGUI namespace.
Definition array_generic.hpp:20
DOXYGEN BUG.
Definition algorithm_misc.hpp:20
Character map of a font.
Definition font_char_map.hpp:34
constexpr void reserve(size_t n)
Reserve space for a set of ranges to be added.
Definition font_char_map.hpp:50
glyph_id find(char32_t code_point) const noexcept
Find a glyph for a code_point.
Definition font_char_map.hpp:166
constexpr size_t count() const noexcept
Get the number of code-points supported by the char-map.
Definition font_char_map.hpp:57
constexpr void add(char32_t start_code_point, char32_t end_code_point, uint16_t start_glyph) noexcept
Add a range of code points.
Definition font_char_map.hpp:88
constexpr size_t update_mask(std::bitset< 0x11 '0000 > &mask) const noexcept
Update a code-point mask.
Definition font_char_map.hpp:67
void prepare() noexcept
Prepare the map for searching.
Definition font_char_map.hpp:115
T begin(T... args)
T emplace_back(T... args)
T empty(T... args)
T end(T... args)
T erase(T... args)
T min(T... args)
T reserve(T... args)
T shrink_to_fit(T... args)
T sort(T... args)