HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
glyph_ids.hpp
1// Copyright Take Vos 2021.
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
5#pragma once
6
7#include "glyph_id.hpp"
8#include "glyph_atlas_info.hpp"
9#include "../graphic_path.hpp"
10#include "../geometry/axis_aligned_rectangle.hpp"
11#include "../required.hpp"
12#include "../cast.hpp"
13#include <bit>
14#include <memory>
15#include <cstddef>
16#include <array>
17#include <cstdint>
18#include <functional>
19
20namespace hi::inline v1 {
21class font;
22
23namespace detail {
24
26public:
27 constexpr glyph_ids_long(glyph_ids_long const &) noexcept = default;
28 constexpr glyph_ids_long(glyph_ids_long &&) noexcept = default;
29 constexpr glyph_ids_long &operator=(glyph_ids_long const &) noexcept = default;
30 constexpr glyph_ids_long &operator=(glyph_ids_long &&) noexcept = default;
31
37 constexpr glyph_ids_long(std::size_t value, glyph_id new_id) noexcept : _num_glyphs(), _num_graphemes(), _glyphs()
38 {
39 if constexpr (sizeof(std::size_t) == 4) {
40 _num_glyphs = 2;
41 _num_graphemes = (value >> 4) & 0xf;
42 std::get<0>(_glyphs) = glyph_id{(value >> 16) & 0xffff};
43 std::get<1>(_glyphs) = new_id;
44 } else {
45 _num_glyphs = 4;
46 _num_graphemes = (value >> 4) & 0xf;
47 std::get<0>(_glyphs) = glyph_id{(value >> 16) & 0xffff};
48 std::get<1>(_glyphs) = glyph_id{(value >> 32) & 0xffff};
49 std::get<2>(_glyphs) = glyph_id{(value >> 48) & 0xffff};
50 std::get<3>(_glyphs) = new_id;
51 }
52 }
53
54 [[nodiscard]] constexpr std::size_t num_glyphs() const noexcept
55 {
56 return _num_glyphs;
57 }
58
59 [[nodiscard]] constexpr std::size_t num_graphemes() const noexcept
60 {
61 return _num_graphemes;
62 }
63
64 constexpr void set_num_graphemes(std::size_t num_graphemes) noexcept
65 {
66 hi_axiom(num_graphemes <= 0xf);
67 _num_graphemes = static_cast<uint8_t>(num_graphemes);
68 }
69
70 [[nodiscard]] constexpr std::size_t hash() const noexcept
71 {
72 std::size_t r = 0;
73
74 for (auto i = 0_uz; i != _num_glyphs; ++i) {
75 r ^= *_glyphs[i];
76 r ^= std::rotl(r, 16);
77 }
78
79 return r;
80 }
81
82 constexpr glyph_ids_long &operator+=(glyph_id id) noexcept
83 {
84 // On overflow silently drop glyphs.
85 if (_num_glyphs < _glyphs.size()) {
86 _glyphs[_num_glyphs++] = id;
87 }
88 return *this;
89 }
90
91 [[nodiscard]] constexpr glyph_id const &operator[](std::size_t i) const noexcept
92 {
93 hi_axiom(i < _num_glyphs);
94 return _glyphs[i];
95 }
96
97 template<std::size_t I>
98 [[nodiscard]] constexpr friend glyph_id get(glyph_ids_long const &rhs) noexcept
99 {
100 hi_axiom(I < rhs._num_glyphs);
101 return std::get<I>(rhs._glyphs);
102 }
103
104 [[nodiscard]] constexpr friend bool operator==(glyph_ids_long const &, glyph_ids_long const &) noexcept = default;
105
106private:
107 uint8_t _num_glyphs;
108 uint8_t _num_graphemes;
109
110 // At least 18 glyphs to handle the maximum Unicode compatibility-decomposition.
111 // Given a 64 byte allocation chunk, 16 bytes overhead, 2 bytes for the _num_graphemes
112 // and _num_glyphs; we are left over with 46 bytes -> 23 glyphs.
114};
115
116} // namespace detail
117
128public:
129 constexpr ~glyph_ids()
130 {
131 if (is_long()) {
132 delete _ptr;
133 }
134 }
135
136 constexpr glyph_ids(glyph_ids const &other) noexcept : _font(other._font), _ptr(other._ptr)
137 {
138 if (is_long()) {
139 _ptr = new detail::glyph_ids_long(*_ptr);
140 }
141 }
142
143 constexpr glyph_ids &operator=(glyph_ids const &other) noexcept
144 {
145 hi_return_on_self_assignment(other);
146
147 _font = other._font;
148 if (is_long()) {
149 delete _ptr;
150 }
151 _ptr = other._ptr;
152 if (is_long()) {
153 _ptr = new detail::glyph_ids_long(*_ptr);
154 }
155 return *this;
156 }
157
158 constexpr glyph_ids(glyph_ids &&other) noexcept : _font(other._font), _ptr(std::exchange(other._ptr, make_ptr(1))) {}
159
160 constexpr glyph_ids &operator=(glyph_ids &&other) noexcept
161 {
162 _font = other._font;
163 std::swap(_ptr, other._ptr);
164 return *this;
165 }
166
172 constexpr glyph_ids() noexcept : _font(nullptr), _ptr(make_ptr(1)) {}
173
180 constexpr glyph_ids(hi::font const &font) noexcept : _font(&font), _ptr(make_ptr(1)) {}
181
184 [[nodiscard]] constexpr font const &font() const noexcept
185 {
186 hi_axiom(_font);
187 return *_font;
188 }
189
192 void set_font(hi::font const &font) noexcept
193 {
194 _font = &font;
195 }
196
201 constexpr void clear() noexcept
202 {
203 if (is_long()) {
204 delete _ptr;
205 }
206 _ptr = make_ptr(1);
207 }
208
211 [[nodiscard]] constexpr bool empty() const noexcept
212 {
213 return _ptr == make_ptr(1);
214 }
215
218 constexpr operator bool() const noexcept
219 {
220 return not empty();
221 }
222
230 template<std::size_t N>
231 [[nodiscard]] constexpr bool has_num_glyphs() const noexcept
232 {
233 static_assert(N <= num_glyphs_mask);
234
235 constexpr std::size_t mask = (num_glyphs_mask << num_glyphs_shift) | 1;
236 constexpr std::size_t value = (N << num_glyphs_shift) | 1;
237 return (make_value(_ptr) & mask) == value;
238 }
239
242 [[nodiscard]] constexpr glyph_id get_single() const noexcept
243 {
244 hi_axiom(has_num_glyphs<1>());
245 return get_glyph(1);
246 }
247
250 [[nodiscard]] constexpr std::size_t num_glyphs() const noexcept
251 {
252 return is_long() ? _ptr->num_glyphs() : (make_value(_ptr) >> 1) & num_glyphs_mask;
253 }
254
255 [[nodiscard]] constexpr std::size_t num_graphemes() const noexcept
256 {
257 return is_long() ? _ptr->num_graphemes() : (make_value(_ptr) >> 4) & 0xf;
258 }
259
260 constexpr void set_num_graphemes(std::size_t num_graphemes) noexcept
261 {
262 if (is_long()) {
263 _ptr->set_num_graphemes(num_graphemes);
264 } else {
265 hi_axiom(num_graphemes <= num_graphemes_mask);
266 _ptr = make_ptr(
267 (make_value(_ptr) & ~(num_graphemes_mask << num_graphemes_shift)) | (num_graphemes << num_graphemes_shift));
268 }
269 }
270
273 [[nodiscard]] constexpr std::size_t hash() const noexcept
274 {
275 return is_long() ? _ptr->hash() : make_value(_ptr);
276 }
277
282 constexpr glyph_ids &operator+=(glyph_id id) noexcept
283 {
284 if (is_long()) {
285 *_ptr += id;
286
287 } else if (auto index = short_num_glyphs(); index < num_glyphs_mask) {
288 increment_num_glyphs();
289 set_glyph(index, id);
290
291 } else {
292 _ptr = new detail::glyph_ids_long(make_value(_ptr), id);
293 }
294 return *this;
295 }
296
301 [[nodiscard]] constexpr glyph_id operator[](std::size_t index) const noexcept
302 {
303 hi_axiom(index < num_glyphs());
304
305 if (is_long()) {
306 return (*_ptr)[index];
307 } else {
308 return get_glyph(index);
309 }
310 }
311
312 template<std::size_t I>
313 [[nodiscard]] constexpr friend glyph_id get(glyph_ids const &rhs) noexcept
314 {
315 if (rhs.is_long()) {
316 return get<I>(*rhs._ptr);
317 } else {
318 constexpr std::size_t shift = (I + 1) * 16;
319 return glyph_id{(make_value(rhs._ptr) >> shift) & 0xffff};
320 }
321 }
322
323 [[nodiscard]] constexpr friend bool operator==(glyph_ids const &lhs, glyph_ids const &rhs) noexcept
324 {
325 hilet lhs_value = make_value(lhs._ptr);
326 hilet rhs_value = make_value(rhs._ptr);
327
328 if (lhs._font != rhs._font) {
329 return false;
330 } else if (lhs_value == rhs_value) {
331 return true;
332 } else {
333 return ((lhs_value | rhs_value) & 1) == 0 and *lhs._ptr == *rhs._ptr;
334 }
335 }
336
339 [[nodiscard]] glyph_atlas_info &atlas_info() const noexcept;
340
345 [[nodiscard]] std::pair<graphic_path, aarectangle> get_path_and_bounding_box() const noexcept;
346
351 [[nodiscard]] aarectangle get_bounding_box() const noexcept;
352
353private:
354 static_assert(sizeof(std::size_t) == sizeof(detail::glyph_ids_long *));
355
356 static constexpr std::size_t num_glyphs_shift = 1;
357 static constexpr std::size_t num_glyphs_mask = sizeof(std::size_t) == 4 ? 1 : 3;
358 static constexpr std::size_t num_graphemes_shift = 4;
359 static constexpr std::size_t num_graphemes_mask = 15;
360
361 hi::font const *_font;
362
374 detail::glyph_ids_long *_ptr;
375
376 [[nodiscard]] constexpr void increment_num_glyphs() noexcept
377 {
378 hi_axiom(is_short());
379 hi_axiom(short_num_glyphs() < num_glyphs_mask);
380
381 _ptr = make_ptr(make_value(_ptr) + (1 << num_glyphs_shift));
382 }
383
384 [[nodiscard]] constexpr glyph_id get_glyph(std::size_t index) const noexcept
385 {
386 hi_axiom(is_short());
387
388 auto shift = (index + 1) * 16;
389 return glyph_id{(make_value(_ptr) >> shift) & 0xffff};
390 }
391
392 constexpr void set_glyph(std::size_t i, glyph_id id) noexcept
393 {
394 hi_axiom(is_short());
395
396 auto shift = (i + 1) * 16;
397 auto mask = std::size_t{0xffff} << shift;
398 _ptr = make_ptr((make_value(_ptr) & ~mask) | (static_cast<std::size_t>(id) << shift));
399 }
400
401 [[nodiscard]] constexpr std::size_t short_num_glyphs() const noexcept
402 {
403 hi_axiom(is_short());
404 return (make_value(_ptr) >> num_glyphs_shift) & num_glyphs_mask;
405 }
406
407 [[nodiscard]] constexpr bool is_short() const noexcept
408 {
409 return static_cast<bool>(make_value(_ptr) & 1);
410 }
411
412 [[nodiscard]] constexpr bool is_long() const noexcept
413 {
414 return not is_short();
415 }
416
417 [[nodiscard]] static constexpr detail::glyph_ids_long *make_ptr(std::size_t value) noexcept
418 {
419 return std::bit_cast<detail::glyph_ids_long *>(value);
420 }
421
422 [[nodiscard]] static constexpr std::size_t make_value(detail::glyph_ids_long *ptr) noexcept
423 {
424 return std::bit_cast<std::size_t>(ptr);
425 }
426};
427
428} // namespace hi::inline v1
429
430template<>
431struct std::hash<hi::glyph_ids> {
432 [[nodiscard]] constexpr std::size_t operator()(hi::glyph_ids const &rhs) const noexcept
433 {
434 return rhs.hash();
435 }
436};
This file includes required definitions.
#define hilet
Invariant should be the default for variables.
Definition required.hpp:23
STL namespace.
Class which represents an axis-aligned rectangle.
Definition axis_aligned_rectangle.hpp:20
A path is a vector graphics object.
Definition graphic_path.hpp:29
Definition font.hpp:34
Definition glyph_atlas_info.hpp:12
Definition glyph_ids.hpp:25
constexpr glyph_ids_long(std::size_t value, glyph_id new_id) noexcept
Construct a list of glyphs starting with a packed set of glyphs.
Definition glyph_ids.hpp:37
A set of glyph-ids of a font which composites into a single glyph.
Definition glyph_ids.hpp:127
glyph_atlas_info & atlas_info() const noexcept
Get information where the glyph is drawn in the atlas.
constexpr glyph_id get_single() const noexcept
Get the value of the single glyph.
Definition glyph_ids.hpp:242
constexpr std::size_t num_glyphs() const noexcept
Get the number of glyphs.
Definition glyph_ids.hpp:250
constexpr font const & font() const noexcept
Get the font for this glyph_ids object.
Definition glyph_ids.hpp:184
constexpr std::size_t hash() const noexcept
Get the hash value.
Definition glyph_ids.hpp:273
constexpr glyph_ids() noexcept
Create an empty glyph_ids object.
Definition glyph_ids.hpp:172
constexpr glyph_ids & operator+=(glyph_id id) noexcept
Add a glyph to this object.
Definition glyph_ids.hpp:282
constexpr bool has_num_glyphs() const noexcept
Check if this object contains a number of glyphs.
Definition glyph_ids.hpp:231
constexpr bool empty() const noexcept
Check if glyphs are attached.
Definition glyph_ids.hpp:211
constexpr glyph_id operator[](std::size_t index) const noexcept
Get a glyph.
Definition glyph_ids.hpp:301
constexpr void clear() noexcept
Clear the glyphs in this glyph_ids object.
Definition glyph_ids.hpp:201
void set_font(hi::font const &font) noexcept
Set the font for this glyph_ids object.
Definition glyph_ids.hpp:192
constexpr glyph_ids(hi::font const &font) noexcept
Create an empty glyph_ids for a font.
Definition glyph_ids.hpp:180
T operator()(T... args)
T swap(T... args)