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.hpp"
13#include "../utility/module.hpp"
14#include <bitset>
15#include <cstdint>
16#include <vector>
17#include <tuple>
18#include <algorithm>
19
20namespace hi { inline namespace v1 {
21
31public:
32 constexpr font_char_map() noexcept = default;
33 constexpr font_char_map(font_char_map const&) noexcept = default;
34 constexpr font_char_map(font_char_map&&) noexcept = default;
35 constexpr font_char_map& operator=(font_char_map const&) noexcept = default;
36 constexpr font_char_map& operator=(font_char_map&&) noexcept = default;
37
38 [[nodiscard]] constexpr bool empty() const noexcept
39 {
40 return _map.empty();
41 }
42
46 constexpr void reserve(size_t n)
47 {
48 return _map.reserve(n);
49 }
50
53 constexpr size_t count() const noexcept
54 {
55 return _count;
56 }
57
63 constexpr size_t update_mask(std::bitset<0x11'0000>& mask) const noexcept
64 {
65 auto r = 0_uz;
66 for (hilet &entry: _map) {
67 // Make sure this loop is inclusive.
68 for (auto cp = entry.start_code_point(); cp <= entry.end_code_point; ++cp) {
69 if (not mask.test(cp)) {
70 ++r;
71 mask.set(cp);
72 }
73 }
74 }
75 return r;
76 }
77
84 [[nodiscard]] constexpr void add(char32_t start_code_point, char32_t end_code_point, uint16_t start_glyph) noexcept
85 {
86#ifndef NDEBUG
87 _prepared = false;
88#endif
89 hi_axiom(start_code_point <= end_code_point);
90 auto todo = wide_cast<size_t>(end_code_point - start_code_point + 1);
91 _count += todo;
92 hi_axiom(start_glyph + todo < 0xffff, "Only glyph_ids 0 through 0xfffe are valid");
93
94 while (todo != 0) {
95 hilet doing = std::min(todo, entry_type::max_count);
96 hi_axiom(doing != 0);
97
98 _map.emplace_back(start_code_point, char_cast<char32_t>(start_code_point + doing - 1), start_glyph);
99
100 todo -= doing;
101 start_code_point += narrow_cast<char32_t>(doing);
102 start_glyph += narrow_cast<uint16_t>(doing);
103 }
104 }
105
111 void prepare() noexcept
112 {
113 if (_map.empty()) {
114 return;
115 }
116
117 // Sort the entries in reverse order so that the lower_bound search becomes upper_bound.
118 std::sort(_map.begin(), _map.end(), [](hilet& a, hilet& b) {
119 return a.end_code_point < b.end_code_point;
120 });
121
122 auto it = _map.begin();
123 auto prev_it = it++;
124 while (it != _map.end()) {
125 hi_axiom(prev_it->end_code_point < it->start_code_point());
126
127 if (mergable(*prev_it, *it)) {
128 hilet merged_count = std::min(prev_it->count() + it->count(), entry_type::max_count);
129 hilet move_count = merged_count - prev_it->count();
130 hi_axiom(move_count <= entry_type::max_count);
131
132 prev_it->end_code_point += narrow_cast<char32_t>(move_count);
133 prev_it->set_count(prev_it->count() + move_count);
134
135 if (move_count == it->count()) {
136 it = _map.erase(it);
137 // Don't change prev or it; since we just deleted instead of advanced.
138 continue;
139
140 } else {
141 hi_axiom(move_count < it->count());
142 it->start_glyph += narrow_cast<uint16_t>(move_count);
143 it->set_count(it->count() - move_count);
144 }
145 }
146
147 prev_it = it++;
148 }
149
150 _map.shrink_to_fit();
151
152#ifndef NDEBUG
153 _prepared = true;
154#endif
155 }
156
162 [[nodiscard]] inline glyph_id find(char32_t code_point) const noexcept
163 {
164#ifndef NDEBUG
165 hi_assert(_prepared);
166#endif
167
168 if (hilet item_ptr = fast_lower_bound(std::span{_map}, char_cast<uint32_t>(code_point))) {
169 return item_ptr->get(code_point);
170 }
171 return {};
172 }
173
174private:
175 struct entry_type {
176 constexpr static size_t max_count = 0x1'0000;
177
178 char32_t end_code_point;
179 uint16_t start_glyph;
180 uint16_t _count;
181
182 constexpr entry_type(char32_t start_code_point, char32_t end_code_point, uint16_t start_glyph) noexcept :
183 end_code_point(end_code_point),
184 start_glyph(start_glyph),
185 _count(narrow_cast<uint16_t>(end_code_point - start_code_point))
186 {
187 hi_axiom(start_code_point <= end_code_point);
188 }
189
190 [[nodiscard]] constexpr size_t count() const noexcept
191 {
192 return wide_cast<size_t>(_count) + 1;
193 }
194
195 [[nodiscard]] constexpr void set_count(size_t new_count) noexcept
196 {
197 hi_axiom(new_count > 0);
198 hi_axiom(new_count <= max_count);
199 _count = narrow_cast<uint16_t>(new_count - 1);
200 }
201
202 [[nodiscard]] constexpr char32_t start_code_point() const noexcept
203 {
204 return end_code_point - _count;
205 }
206
207 [[nodiscard]] constexpr uint16_t end_glyph() const noexcept
208 {
209 return narrow_cast<uint16_t>(start_glyph + _count);
210 }
211
212 [[nodiscard]] constexpr friend bool mergable(entry_type const& lhs, entry_type const& rhs) noexcept
213 {
214 return lhs.end_code_point + 1 == rhs.start_code_point() and lhs.end_glyph() + 1 == rhs.start_glyph;
215 }
216
217 [[nodiscard]] constexpr glyph_id get(char32_t code_point) const noexcept
218 {
219 auto diff = wide_cast<ptrdiff_t>(code_point) - wide_cast<ptrdiff_t>(end_code_point);
220 if (diff > 0) {
221 return {};
222 }
223 diff += _count;
224 if (diff < 0) {
225 return {};
226 }
227 diff += start_glyph;
228 return glyph_id{truncate<uint16_t>(diff)};
229 }
230 };
231
232 std::vector<entry_type> _map = {};
233
236 size_t _count = 0;
237
238#ifndef NDEBUG
239 bool _prepared = false;
240#endif
241};
242
243}} // namespace hi::v1
#define hi_assert(expression,...)
Assert if expression is true.
Definition assert.hpp:199
#define hi_axiom(expression,...)
Specify an axiom; an expression that is true.
Definition assert.hpp:253
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
DOXYGEN BUG.
Definition algorithm.hpp:13
geometry/margins.hpp
Definition cache.hpp:11
Character map of a font.
Definition font_char_map.hpp:30
constexpr void reserve(size_t n)
Reserve space for a set of ranges to be added.
Definition font_char_map.hpp:46
glyph_id find(char32_t code_point) const noexcept
Find a glyph for a code_point.
Definition font_char_map.hpp:162
constexpr size_t count() const noexcept
Get the number of code-points supported by the char-map.
Definition font_char_map.hpp:53
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:84
constexpr size_t update_mask(std::bitset< 0x11 '0000 > &mask) const noexcept
Update a code-point mask.
Definition font_char_map.hpp:63
void prepare() noexcept
Prepare the map for searching.
Definition font_char_map.hpp:111
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)