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/axis_aligned_rectangle.hpp"
11#include "../utility.hpp"
12#include "../cast.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 [[nodiscard]] constexpr font const& font() const noexcept
193 {
194 hi_assert_not_null(_font);
195 return *_font;
196 }
197
200 void set_font(hi::font const& font) noexcept
201 {
202 _font = &font;
203 }
204
209 constexpr void clear() noexcept
210 {
211 if (is_long()) {
212 delete _ptr;
213 }
214 _ptr = make_ptr(1);
215 }
216
219 [[nodiscard]] constexpr bool empty() const noexcept
220 {
221 return _ptr == make_ptr(1);
222 }
223
226 constexpr operator bool() const noexcept
227 {
228 return not empty();
229 }
230
238 template<std::size_t N>
239 [[nodiscard]] constexpr bool has_num_glyphs() const noexcept
240 {
241 static_assert(N <= num_glyphs_mask);
242
243 constexpr std::size_t mask = (num_glyphs_mask << num_glyphs_shift) | 1;
244 constexpr std::size_t value = (N << num_glyphs_shift) | 1;
245 return (make_value(_ptr) & mask) == value;
246 }
247
250 [[nodiscard]] constexpr glyph_id get_single() const noexcept
251 {
252 hi_axiom(has_num_glyphs<1>());
253 return get_glyph(1);
254 }
255
258 [[nodiscard]] constexpr std::size_t num_glyphs() const noexcept
259 {
260 return is_long() ? _ptr->num_glyphs() : (make_value(_ptr) >> 1) & num_glyphs_mask;
261 }
262
263 [[nodiscard]] constexpr std::size_t num_graphemes() const noexcept
264 {
265 return is_long() ? _ptr->num_graphemes() : (make_value(_ptr) >> 4) & 0xf;
266 }
267
268 constexpr void set_num_graphemes(std::size_t num_graphemes) noexcept
269 {
270 if (is_long()) {
271 _ptr->set_num_graphemes(num_graphemes);
272 } else {
273 hi_axiom(num_graphemes <= num_graphemes_mask);
274 _ptr = make_ptr(
275 (make_value(_ptr) & ~(num_graphemes_mask << num_graphemes_shift)) | (num_graphemes << num_graphemes_shift));
276 }
277 }
278
281 [[nodiscard]] constexpr std::size_t hash() const noexcept
282 {
283 return is_long() ? _ptr->hash() : make_value(_ptr);
284 }
285
290 constexpr glyph_ids& operator+=(glyph_id id) noexcept
291 {
292 if (is_long()) {
293 *_ptr += id;
294
295 } else if (hilet index = short_num_glyphs(); index < num_glyphs_mask) {
296 increment_num_glyphs();
297 set_glyph(index, id);
298
299 } else {
300 _ptr = new detail::glyph_ids_long(make_value(_ptr), id);
301 }
302 return *this;
303 }
304
309 [[nodiscard]] constexpr glyph_id operator[](std::size_t index) const noexcept
310 {
311 hi_axiom(index < num_glyphs());
312
313 if (is_long()) {
314 return (*_ptr)[index];
315 } else {
316 return get_glyph(index);
317 }
318 }
319
320 template<std::size_t I>
321 [[nodiscard]] constexpr friend glyph_id get(glyph_ids const& rhs) noexcept
322 {
323 if (rhs.is_long()) {
324 return get<I>(*rhs._ptr);
325 } else {
326 constexpr std::size_t shift = (I + 1) * 16;
327 return glyph_id{(make_value(rhs._ptr) >> shift) & 0xffff};
328 }
329 }
330
331 [[nodiscard]] constexpr friend bool operator==(glyph_ids const& lhs, glyph_ids const& rhs) noexcept
332 {
333 hilet lhs_value = make_value(lhs._ptr);
334 hilet rhs_value = make_value(rhs._ptr);
335
336 if (lhs._font != rhs._font) {
337 return false;
338 } else if (lhs_value == rhs_value) {
339 return true;
340 } else {
341 return ((lhs_value | rhs_value) & 1) == 0 and *lhs._ptr == *rhs._ptr;
342 }
343 }
344
347 [[nodiscard]] glyph_atlas_info& atlas_info() const noexcept;
348
353 [[nodiscard]] std::pair<graphic_path, aarectangle> get_path_and_bounding_box() const noexcept;
354
359 [[nodiscard]] aarectangle get_bounding_box() const noexcept;
360
361private:
362 static_assert(sizeof(std::size_t) == sizeof(detail::glyph_ids_long *));
363
364 static constexpr std::size_t num_glyphs_shift = 1;
365 static constexpr std::size_t num_glyphs_mask = sizeof(std::size_t) == 4 ? 1 : 3;
366 static constexpr std::size_t num_graphemes_shift = 4;
367 static constexpr std::size_t num_graphemes_mask = 15;
368
369 hi::font const *_font;
370
382 detail::glyph_ids_long *_ptr;
383
384 constexpr void increment_num_glyphs() noexcept
385 {
386 hi_axiom(is_short());
387 hi_axiom(short_num_glyphs() < num_glyphs_mask);
388
389 _ptr = make_ptr(make_value(_ptr) + (1 << num_glyphs_shift));
390 }
391
392 [[nodiscard]] constexpr glyph_id get_glyph(std::size_t index) const noexcept
393 {
394 hi_axiom(is_short());
395
396 hilet shift = (index + 1) * 16;
397 return glyph_id{(make_value(_ptr) >> shift) & 0xffff};
398 }
399
400 constexpr void set_glyph(std::size_t i, glyph_id id) noexcept
401 {
402 hi_axiom(is_short());
403
404 hilet shift = (i + 1) * 16;
405 hilet mask = std::size_t{0xffff} << shift;
406 _ptr = make_ptr((make_value(_ptr) & ~mask) | (static_cast<std::size_t>(id) << shift));
407 }
408
409 [[nodiscard]] constexpr std::size_t short_num_glyphs() const noexcept
410 {
411 hi_axiom(is_short());
412 return (make_value(_ptr) >> num_glyphs_shift) & num_glyphs_mask;
413 }
414
415 [[nodiscard]] constexpr bool is_short() const noexcept
416 {
417 return to_bool(make_value(_ptr) & 1);
418 }
419
420 [[nodiscard]] constexpr bool is_long() const noexcept
421 {
422 return not is_short();
423 }
424
425 [[nodiscard]] static constexpr detail::glyph_ids_long *make_ptr(std::size_t value) noexcept
426 {
427 return std::bit_cast<detail::glyph_ids_long *>(value);
428 }
429
430 [[nodiscard]] static constexpr std::size_t make_value(detail::glyph_ids_long *ptr) noexcept
431 {
432 return std::bit_cast<std::size_t>(ptr);
433 }
434};
435
436} // namespace hi::inline v1
437
438template<>
439struct std::hash<hi::glyph_ids> {
440 [[nodiscard]] constexpr std::size_t operator()(hi::glyph_ids const& rhs) const noexcept
441 {
442 return rhs.hash();
443 }
444};
#define hi_axiom(expression)
Specify an axiom; an expression that is true.
Definition assert.hpp:133
#define hi_assert_not_null(x)
Assert if an expression is not nullptr.
Definition assert.hpp:118
Utilities used by the HikoGUI library itself.
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
STL namespace.
DOXYGEN BUG.
Definition algorithm.hpp:15
@ shift
The shift key is being held.
The HikoGUI namespace.
Definition ascii.hpp:19
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:32
Definition glyph_atlas_info.hpp:15
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:250
constexpr std::size_t num_glyphs() const noexcept
Get the number of glyphs.
Definition glyph_ids.hpp:258
constexpr font const & font() const noexcept
Get the font for this glyph_ids object.
Definition glyph_ids.hpp:192
constexpr std::size_t hash() const noexcept
Get the hash value.
Definition glyph_ids.hpp:281
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:290
constexpr bool has_num_glyphs() const noexcept
Check if this object contains a number of glyphs.
Definition glyph_ids.hpp:239
constexpr bool empty() const noexcept
Check if glyphs are attached.
Definition glyph_ids.hpp:219
constexpr glyph_id operator[](std::size_t index) const noexcept
Get a glyph.
Definition glyph_ids.hpp:309
constexpr void clear() noexcept
Clear the glyphs in this glyph_ids object.
Definition glyph_ids.hpp:209
void set_font(hi::font const &font) noexcept
Set the font for this glyph_ids object.
Definition glyph_ids.hpp:200
constexpr glyph_ids(hi::font const &font) noexcept
Create an empty glyph_ids for a font.
Definition glyph_ids.hpp:188
T operator()(T... args)
T swap(T... args)