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 "../unicode/unicode_mask.hpp"
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
57 [[nodiscard]] constexpr void add(char32_t start_code_point, char32_t end_code_point, uint16_t start_glyph) noexcept
58 {
59#ifndef NDEBUG
60 _prepared = false;
61#endif
62 hi_axiom(start_code_point <= end_code_point);
63 auto count = wide_cast<size_t>(end_code_point - start_code_point + 1);
64 hi_axiom(start_glyph + count < 0xffff, "Only glyph_ids 0 through 0xfffe are valid");
65
66 while (count != 0) {
67 hilet short_count = std::min(count, entry_type::max_count);
68 hi_axiom(short_count != 0);
69
70 _map.emplace_back(start_code_point, char_cast<char32_t>(start_code_point + short_count - 1), start_glyph);
71
72 count -= short_count;
73 start_code_point += narrow_cast<char32_t>(short_count);
74 start_glyph += narrow_cast<uint16_t>(short_count);
75 }
76 }
77
83 void prepare() noexcept
84 {
85 if (_map.empty()) {
86 return;
87 }
88
89 // Sort the entries in reverse order so that the lower_bound search becomes upper_bound.
90 std::sort(_map.begin(), _map.end(), [](hilet& a, hilet& b) {
91 return a.end_code_point < b.end_code_point;
92 });
93
94 auto it = _map.begin();
95 auto prev_it = it++;
96 while (it != _map.end()) {
97 hi_axiom(prev_it->end_code_point < it->start_code_point());
98
99 if (mergable(*prev_it, *it)) {
100 hilet merged_count = std::min(prev_it->count() + it->count(), entry_type::max_count);
101 hilet move_count = merged_count - prev_it->count();
102 hi_axiom(move_count <= entry_type::max_count);
103
104 prev_it->end_code_point += narrow_cast<char32_t>(move_count);
105 prev_it->set_count(prev_it->count() + move_count);
106
107 if (move_count == it->count()) {
108 it = _map.erase(it);
109 // Don't change prev or it; since we just deleted instead of advanced.
110 continue;
111
112 } else {
113 hi_axiom(move_count < it->count());
114 it->start_glyph += narrow_cast<uint16_t>(move_count);
115 it->set_count(it->count() - move_count);
116 }
117 }
118
119 prev_it = it++;
120 }
121
122 _map.shrink_to_fit();
123
124#ifndef NDEBUG
125 _prepared = true;
126#endif
127 }
128
134 [[nodiscard]] inline glyph_id find(char32_t code_point) const noexcept
135 {
136#ifndef NDEBUG
137 hi_assert(_prepared);
138#endif
139
140 if (hilet item_ptr = fast_lower_bound(std::span{_map}, char_cast<uint32_t>(code_point))) {
141 return item_ptr->get(code_point);
142 }
143 return {};
144 }
145
146 [[nodiscard]] unicode_mask make_mask() const noexcept
147 {
148 auto r = unicode_mask();
149
150 for (hilet& entry : _map) {
151 r.add(entry.start_code_point(), entry.end_code_point + 1);
152 }
153
154 r.optimize();
155 r.shrink_to_fit();
156 return r;
157 }
158
159private:
160 struct entry_type {
161 constexpr static size_t max_count = 0x1'0000;
162
163 char32_t end_code_point;
164 uint16_t start_glyph;
165 uint16_t _count;
166
167 constexpr entry_type(char32_t start_code_point, char32_t end_code_point, uint16_t start_glyph) noexcept :
168 end_code_point(end_code_point),
169 start_glyph(start_glyph),
170 _count(narrow_cast<uint16_t>(end_code_point - start_code_point))
171 {
172 hi_axiom(start_code_point <= end_code_point);
173 }
174
175 [[nodiscard]] constexpr size_t count() const noexcept
176 {
177 return wide_cast<size_t>(_count) + 1;
178 }
179
180 [[nodiscard]] constexpr void set_count(size_t new_count) noexcept
181 {
182 hi_axiom(new_count > 0);
183 hi_axiom(new_count <= max_count);
184 _count = narrow_cast<uint16_t>(new_count - 1);
185 }
186
187 [[nodiscard]] constexpr char32_t start_code_point() const noexcept
188 {
189 return end_code_point - _count;
190 }
191
192 [[nodiscard]] constexpr uint16_t end_glyph() const noexcept
193 {
194 return narrow_cast<uint16_t>(start_glyph + _count);
195 }
196
197 [[nodiscard]] constexpr friend bool mergable(entry_type const& lhs, entry_type const& rhs) noexcept
198 {
199 return lhs.end_code_point + 1 == rhs.start_code_point() and lhs.end_glyph() + 1 == rhs.start_glyph;
200 }
201
202 [[nodiscard]] constexpr glyph_id get(char32_t code_point) const noexcept
203 {
204 auto diff = wide_cast<ptrdiff_t>(code_point) - wide_cast<ptrdiff_t>(end_code_point);
205 if (diff > 0) {
206 return {};
207 }
208 diff += _count;
209 if (diff < 0) {
210 return {};
211 }
212 diff += start_glyph;
213 return glyph_id{truncate<uint16_t>(diff)};
214 }
215 };
216
217 std::vector<entry_type> _map = {};
218
219#ifndef NDEBUG
220 bool _prepared = false;
221#endif
222};
223
224}} // namespace hi::v1
#define hi_assert(expression,...)
Assert if expression is true.
Definition assert.hpp:184
#define hi_axiom(expression,...)
Specify an axiom; an expression that is true.
Definition assert.hpp:238
#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:134
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:57
void prepare() noexcept
Prepare the map for searching.
Definition font_char_map.hpp:83
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)