HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
glyph_ids.hpp
1// Copyright Take Vos 2021-2022.
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/module.hpp"
11#include "../utility/module.hpp"
12#include "../lean_vector.hpp"
13#include <bit>
14#include <memory>
15#include <cstddef>
16#include <array>
17#include <cstdint>
18#include <functional>
19
20hi_warning_push();
21// C26401: Do not delete a raw pointer that is not an owner<T> (i.11).
22// False positive, ~glyph_ids::glyph_ids() is owner of glyphs_ids::_ptr.
23hi_warning_ignore_msvc(26401);
24// C26409: Avoid calling new and delete explicitly, use std::make_unique<T> instead (r.11).
25// glyph_ids implements a container.
26hi_warning_ignore_msvc(26409);
27
28namespace hi::inline v1 {
29class font;
30
31namespace detail {
32
34public:
35 constexpr glyph_ids_long(glyph_ids_long const&) noexcept = default;
36 constexpr glyph_ids_long(glyph_ids_long&&) noexcept = default;
37 constexpr glyph_ids_long& operator=(glyph_ids_long const&) noexcept = default;
38 constexpr glyph_ids_long& operator=(glyph_ids_long&&) noexcept = default;
39
45 constexpr glyph_ids_long(std::size_t value, glyph_id new_id) noexcept : _num_glyphs(), _num_graphemes(), _glyphs()
46 {
47 if constexpr (sizeof(std::size_t) == 4) {
48 _num_glyphs = 2;
49 _num_graphemes = (value >> 4) & 0xf;
50 std::get<0>(_glyphs) = glyph_id{(value >> 16) & 0xffff};
51 std::get<1>(_glyphs) = new_id;
52 } else {
53 _num_glyphs = 4;
54 _num_graphemes = (value >> 4) & 0xf;
55 std::get<0>(_glyphs) = glyph_id{(value >> 16) & 0xffff};
56 std::get<1>(_glyphs) = glyph_id{(value >> 32) & 0xffff};
57 std::get<2>(_glyphs) = glyph_id{(value >> 48) & 0xffff};
58 std::get<3>(_glyphs) = new_id;
59 }
60 }
61
62 [[nodiscard]] constexpr std::size_t num_glyphs() const noexcept
63 {
64 return _num_glyphs;
65 }
66
67 [[nodiscard]] constexpr std::size_t num_graphemes() const noexcept
68 {
69 return _num_graphemes;
70 }
71
72 constexpr void set_num_graphemes(std::size_t num_graphemes) noexcept
73 {
74 hi_axiom(num_graphemes <= 0xf);
75 _num_graphemes = narrow_cast<uint8_t>(num_graphemes);
76 }
77
78 [[nodiscard]] constexpr std::size_t hash() const noexcept
79 {
80 std::size_t r = 0;
81
82 for (auto i = 0_uz; i != _num_glyphs; ++i) {
83 r ^= *_glyphs[i];
84 r ^= std::rotl(r, 16);
85 }
86
87 return r;
88 }
89
90 constexpr glyph_ids_long& operator+=(glyph_id id) noexcept
91 {
92 // On overflow silently drop glyphs.
93 if (_num_glyphs < _glyphs.size()) {
94 _glyphs[_num_glyphs++] = id;
95 }
96 return *this;
97 }
98
99 [[nodiscard]] constexpr glyph_id const& operator[](std::size_t i) const noexcept
100 {
101 hi_axiom(i < _num_glyphs);
102 return _glyphs[i];
103 }
104
105 template<std::size_t I>
106 [[nodiscard]] constexpr friend glyph_id get(glyph_ids_long const& rhs) noexcept
107 {
108 hi_axiom(I < rhs._num_glyphs);
109 return std::get<I>(rhs._glyphs);
110 }
111
112 [[nodiscard]] constexpr friend bool operator==(glyph_ids_long const&, glyph_ids_long const&) noexcept = default;
113
114private:
115 uint8_t _num_glyphs;
116 uint8_t _num_graphemes;
117
118 // At least 18 glyphs to handle the maximum Unicode compatibility-decomposition.
119 // Given a 64 byte allocation chunk, 16 bytes overhead, 2 bytes for the _num_graphemes
120 // and _num_glyphs; we are left over with 46 bytes -> 23 glyphs.
122};
123
124} // namespace detail
125
136public:
137 constexpr ~glyph_ids()
138 {
139 if (is_long()) {
140 delete _ptr;
141 }
142 }
143
144 constexpr glyph_ids(glyph_ids const& other) noexcept : _font(other._font), _ptr(other._ptr)
145 {
146 if (is_long()) {
147 _ptr = new detail::glyph_ids_long(*_ptr);
148 }
149 }
150
151 constexpr glyph_ids& operator=(glyph_ids const& other) noexcept
152 {
153 hi_return_on_self_assignment(other);
154
155 _font = other._font;
156 if (is_long()) {
157 delete _ptr;
158 }
159 _ptr = other._ptr;
160 if (is_long()) {
161 _ptr = new detail::glyph_ids_long(*_ptr);
162 }
163 return *this;
164 }
165
166 constexpr glyph_ids(glyph_ids&& other) noexcept : _font(other._font), _ptr(std::exchange(other._ptr, make_ptr(1))) {}
167
168 constexpr glyph_ids& operator=(glyph_ids&& other) noexcept
169 {
170 _font = other._font;
171 std::swap(_ptr, other._ptr);
172 return *this;
173 }
174
180 constexpr glyph_ids() noexcept : _font(nullptr), _ptr(make_ptr(1)) {}
181
188 constexpr glyph_ids(hi::font const& font) noexcept : _font(&font), _ptr(make_ptr(1)) {}
189
192 constexpr glyph_ids(hi::font const& font, hi::glyph_id glyph_id) noexcept : glyph_ids(font)
193 {
194 *this += glyph_id;
195 }
196
199 glyph_ids(hi::font const& font, lean_vector<glyph_id> const& glyph_ids) noexcept : glyph_ids(font)
200 {
201 for (hilet glyph_id : glyph_ids) {
202 *this += glyph_id;
203 }
204 }
205
208 [[nodiscard]] constexpr font const& font() const noexcept
209 {
210 hi_assert_not_null(_font);
211 return *_font;
212 }
213
216 void set_font(hi::font const& font) noexcept
217 {
218 _font = &font;
219 }
220
225 constexpr void clear() noexcept
226 {
227 if (is_long()) {
228 delete _ptr;
229 }
230 _ptr = make_ptr(1);
231 }
232
235 [[nodiscard]] constexpr bool empty() const noexcept
236 {
237 return _ptr == make_ptr(1);
238 }
239
242 constexpr operator bool() const noexcept
243 {
244 return not empty();
245 }
246
254 template<std::size_t N>
255 [[nodiscard]] constexpr bool has_num_glyphs() const noexcept
256 {
257 static_assert(N <= num_glyphs_mask);
258
259 constexpr std::size_t mask = (num_glyphs_mask << num_glyphs_shift) | 1;
260 constexpr std::size_t value = (N << num_glyphs_shift) | 1;
261 return (make_value(_ptr) & mask) == value;
262 }
263
266 [[nodiscard]] constexpr glyph_id get_single() const noexcept
267 {
268 hi_axiom(has_num_glyphs<1>());
269 return get_glyph(0);
270 }
271
274 [[nodiscard]] constexpr std::size_t num_glyphs() const noexcept
275 {
276 return is_long() ? _ptr->num_glyphs() : (make_value(_ptr) >> 1) & num_glyphs_mask;
277 }
278
279 [[nodiscard]] constexpr std::size_t num_graphemes() const noexcept
280 {
281 return is_long() ? _ptr->num_graphemes() : (make_value(_ptr) >> 4) & 0xf;
282 }
283
284 constexpr void set_num_graphemes(std::size_t num_graphemes) noexcept
285 {
286 if (is_long()) {
287 _ptr->set_num_graphemes(num_graphemes);
288 } else {
289 hi_axiom(num_graphemes <= num_graphemes_mask);
290 _ptr = make_ptr(
291 (make_value(_ptr) & ~(num_graphemes_mask << num_graphemes_shift)) | (num_graphemes << num_graphemes_shift));
292 }
293 }
294
297 [[nodiscard]] constexpr std::size_t hash() const noexcept
298 {
299 return is_long() ? _ptr->hash() : make_value(_ptr);
300 }
301
306 constexpr glyph_ids& operator+=(glyph_id id) noexcept
307 {
308 if (is_long()) {
309 *_ptr += id;
310
311 } else if (hilet index = short_num_glyphs(); index < num_glyphs_mask) {
312 increment_num_glyphs();
313 set_glyph(index, id);
314
315 } else {
316 _ptr = new detail::glyph_ids_long(make_value(_ptr), id);
317 }
318 return *this;
319 }
320
325 [[nodiscard]] constexpr glyph_id operator[](std::size_t index) const noexcept
326 {
327 hi_axiom(index < num_glyphs());
328
329 if (is_long()) {
330 return (*_ptr)[index];
331 } else {
332 return get_glyph(index);
333 }
334 }
335
336 template<std::size_t I>
337 [[nodiscard]] constexpr friend glyph_id get(glyph_ids const& rhs) noexcept
338 {
339 if (rhs.is_long()) {
340 return get<I>(*rhs._ptr);
341 } else {
342 constexpr std::size_t shift = (I + 1) * 16;
343 return glyph_id{(make_value(rhs._ptr) >> shift) & 0xffff};
344 }
345 }
346
347 [[nodiscard]] constexpr friend bool operator==(glyph_ids const& lhs, glyph_ids const& rhs) noexcept
348 {
349 hilet lhs_value = make_value(lhs._ptr);
350 hilet rhs_value = make_value(rhs._ptr);
351
352 if (lhs._font != rhs._font) {
353 return false;
354 } else if (lhs_value == rhs_value) {
355 return true;
356 } else {
357 return ((lhs_value | rhs_value) & 1) == 0 and *lhs._ptr == *rhs._ptr;
358 }
359 }
360
363 [[nodiscard]] glyph_atlas_info& atlas_info() const noexcept;
364
369 [[nodiscard]] std::pair<graphic_path, aarectangle> get_path_and_bounding_box() const noexcept;
370
375 [[nodiscard]] aarectangle get_bounding_box() const noexcept;
376
377private:
378 static_assert(sizeof(std::size_t) == sizeof(detail::glyph_ids_long *));
379
380 static constexpr std::size_t num_glyphs_shift = 1;
381 static constexpr std::size_t num_glyphs_mask = sizeof(std::size_t) == 4 ? 1 : 3;
382 static constexpr std::size_t num_graphemes_shift = 4;
383 static constexpr std::size_t num_graphemes_mask = 15;
384
385 hi::font const *_font;
386
398 detail::glyph_ids_long *_ptr;
399
400 constexpr void increment_num_glyphs() noexcept
401 {
402 hi_axiom(is_short());
403 hi_axiom(short_num_glyphs() < num_glyphs_mask);
404
405 _ptr = make_ptr(make_value(_ptr) + (1 << num_glyphs_shift));
406 }
407
408 [[nodiscard]] constexpr glyph_id get_glyph(std::size_t index) const noexcept
409 {
410 hi_axiom(is_short());
411
412 hilet shift = (index + 1) * 16;
413 return glyph_id{(make_value(_ptr) >> shift) & 0xffff};
414 }
415
416 constexpr void set_glyph(std::size_t i, glyph_id id) noexcept
417 {
418 hi_axiom(is_short());
419
420 hilet shift = (i + 1) * 16;
421 hilet mask = std::size_t{0xffff} << shift;
422 _ptr = make_ptr((make_value(_ptr) & ~mask) | (static_cast<std::size_t>(id) << shift));
423 }
424
425 [[nodiscard]] constexpr std::size_t short_num_glyphs() const noexcept
426 {
427 hi_axiom(is_short());
428 return (make_value(_ptr) >> num_glyphs_shift) & num_glyphs_mask;
429 }
430
431 [[nodiscard]] constexpr bool is_short() const noexcept
432 {
433 return to_bool(make_value(_ptr) & 1);
434 }
435
436 [[nodiscard]] constexpr bool is_long() const noexcept
437 {
438 return not is_short();
439 }
440
441 [[nodiscard]] static constexpr detail::glyph_ids_long *make_ptr(std::size_t value) noexcept
442 {
443 return std::bit_cast<detail::glyph_ids_long *>(value);
444 }
445
446 [[nodiscard]] static constexpr std::size_t make_value(detail::glyph_ids_long *ptr) noexcept
447 {
448 return std::bit_cast<std::size_t>(ptr);
449 }
450};
451
452} // namespace hi::inline v1
453
454template<>
455struct std::hash<hi::glyph_ids> {
456 [[nodiscard]] constexpr std::size_t operator()(hi::glyph_ids const& rhs) const noexcept
457 {
458 return rhs.hash();
459 }
460};
Defined lean_vector<>.
#define hi_axiom(expression,...)
Specify an axiom; an expression that is true.
Definition assert.hpp:238
#define hi_assert_not_null(x,...)
Assert if an expression is not nullptr.
Definition assert.hpp:223
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
STL namespace.
DOXYGEN BUG.
Definition algorithm.hpp:13
@ shift
The shift key is being held.
geometry/margins.hpp
Definition cache.hpp:11
Definition font.hpp:32
Definition glyph_atlas_info.hpp:12
Definition glyph_ids.hpp:33
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:45
A set of glyph-ids of a font which composites into a single glyph.
Definition glyph_ids.hpp:135
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:266
constexpr std::size_t num_glyphs() const noexcept
Get the number of glyphs.
Definition glyph_ids.hpp:274
constexpr font const & font() const noexcept
Get the font for this glyph_ids object.
Definition glyph_ids.hpp:208
constexpr std::size_t hash() const noexcept
Get the hash value.
Definition glyph_ids.hpp:297
constexpr glyph_ids() noexcept
Create an empty glyph_ids object.
Definition glyph_ids.hpp:180
constexpr glyph_ids & operator+=(glyph_id id) noexcept
Add a glyph to this object.
Definition glyph_ids.hpp:306
constexpr bool has_num_glyphs() const noexcept
Check if this object contains a number of glyphs.
Definition glyph_ids.hpp:255
constexpr bool empty() const noexcept
Check if glyphs are attached.
Definition glyph_ids.hpp:235
constexpr glyph_ids(hi::font const &font, hi::glyph_id glyph_id) noexcept
Create a glyph_ids object from a font and a single glyph_id.
Definition glyph_ids.hpp:192
constexpr glyph_id operator[](std::size_t index) const noexcept
Get a glyph.
Definition glyph_ids.hpp:325
constexpr void clear() noexcept
Clear the glyphs in this glyph_ids object.
Definition glyph_ids.hpp:225
void set_font(hi::font const &font) noexcept
Set the font for this glyph_ids object.
Definition glyph_ids.hpp:216
glyph_ids(hi::font const &font, lean_vector< glyph_id > const &glyph_ids) noexcept
Create a glyph_ids object from a font and a list of glyph_ids.
Definition glyph_ids.hpp:199
constexpr glyph_ids(hi::font const &font) noexcept
Create an empty glyph_ids for a font.
Definition glyph_ids.hpp:188
A path is a vector graphics object.
Definition graphic_path.hpp:26
Definition tagged_id.hpp:17
T operator()(T... args)
T swap(T... args)