HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
FontGlyphIDs.hpp
1// Copyright 2020 Pokitec
2// All rights reserved.
3
4#pragma once
5
6#include "TTauri/Text/GlyphID.hpp"
7#include "TTauri/Text/FontID.hpp"
8#include "TTauri/Foundation/hash.hpp"
9#include "TTauri/Foundation/tagged_id.hpp"
10#include "TTauri/Foundation/aarect.hpp"
11#include <tuple>
12
13namespace tt {
14struct Path;
15}
16
17namespace tt {
18
19// "Compatibility mappings are guaranteed to be no longer than 18 characters, although most consist of just a few characters."
20// https://unicode.org/reports/tr44/ (TR44 5.7.3)
22 int8_t nr_glyphs = 0;
23 std::array<GlyphID,18> glyph_ids;
24
25 FontGlyphIDs_long() noexcept = delete;
26 FontGlyphIDs_long(FontGlyphIDs_long const &rhs) noexcept = default;
27 FontGlyphIDs_long(FontGlyphIDs_long &&rhs) noexcept = default;
28 FontGlyphIDs_long &operator=(FontGlyphIDs_long const &rhs) noexcept = default;
29 FontGlyphIDs_long &operator=(FontGlyphIDs_long &&rhs) noexcept = default;
30
31 tt_force_inline FontGlyphIDs_long(GlyphID g1, GlyphID g2, GlyphID g3) noexcept {
32 (*this) += g1;
33 (*this) += g2;
34 (*this) += g3;
35 }
36
37 tt_force_inline FontGlyphIDs_long operator+=(GlyphID rhs) noexcept {
38 tt_assume(nr_glyphs >= 0);
39 tt_assume(nr_glyphs < ssize(glyph_ids));
40 glyph_ids[nr_glyphs++] = rhs;
41 return *this;
42 }
43
44 [[nodiscard]] size_t hash() const noexcept {
45 tt_assume(nr_glyphs > 3);
46 tt_assume(nr_glyphs < ssize(glyph_ids));
47
48 uint64_t r = 0;
49 for (int8_t i = 0; i != nr_glyphs; ++i) {
50 r = hash_mix_two(r, std::hash<GlyphID>{}(glyph_ids[i]));
51 }
52
53 return r;
54 }
55
56 [[nodiscard]] friend bool operator==(FontGlyphIDs_long const &lhs, FontGlyphIDs_long const &rhs) noexcept {
57 tt_assume(lhs.nr_glyphs > 3);
58 tt_assume(rhs.nr_glyphs > 3);
59 tt_assume(lhs.nr_glyphs < ssize(lhs.glyph_ids));
60 tt_assume(rhs.nr_glyphs < ssize(rhs.glyph_ids));
61
62 if (lhs.nr_glyphs == rhs.nr_glyphs) {
63 for (int8_t i = 0; i != lhs.nr_glyphs; ++i) {
64 if (lhs.glyph_ids[i] != rhs.glyph_ids[i]) {
65 return false;
66 }
67 }
68 return true;
69 } else {
70 return false;
71 }
72 }
73
74 friend class FontGlyphIDs;
75};
76
78 constexpr static uint64_t empty = 0xffff'ffff'ffff'ffff;
79
80 /*
81 * 0 to 3 glyph_ids, with or without a font_id.
82 * 63:48 glyph_id[2]
83 * 47:32 glyph_id[1]
84 * 31:16 glyph_id[0]
85 * 15 '1'
86 * 14: 0 font_id
87 *
88 * More than 3 glyphs
89 * 63:16 FontGlyphsIDs_long *
90 * 15 '0'
91 * 14: 0 font_id
92 */
93 uint64_t value;
94
95public:
96 tt_force_inline FontGlyphIDs() noexcept : value(empty) {}
97
98 FontGlyphIDs(FontGlyphIDs const &rhs) noexcept : value(rhs.value) {
99 if (rhs.has_pointer()) {
100 value = new_pointer(*(rhs.get_pointer()));
101 }
102 }
103
104 tt_force_inline FontGlyphIDs(FontGlyphIDs &&rhs) noexcept : value(rhs.value) {
105 rhs.value = empty;
106 }
107
108 FontGlyphIDs &operator=(FontGlyphIDs const &rhs) noexcept {
109 if (this != &rhs) {
110 delete_pointer();
111 value = rhs.value;
112 if (rhs.has_pointer()) {
113 value = new_pointer(*(rhs.get_pointer()));
114 }
115 }
116 return *this;
117 }
118
119 tt_force_inline FontGlyphIDs &operator=(FontGlyphIDs &&rhs) noexcept {
120 if (this != &rhs) {
121 using std::swap;
122 swap(value, rhs.value);
123 }
124 return *this;
125 }
126
127 ~FontGlyphIDs() noexcept {
128 delete_pointer();
129 }
130
131 void clear() noexcept {
132 delete_pointer();
133 value = empty;
134 }
135
136 tt_force_inline operator bool () const noexcept {
137 return size() > 0;
138 }
139
140 [[nodiscard]] tt_force_inline FontID font_id() const noexcept {
141 return FontID{value & FontID::mask};
142 }
143
144 tt_force_inline void set_font_id(FontID font_id) noexcept {
145 value = (value & ~static_cast<uint64_t>(FontID::mask)) | static_cast<uint64_t>(font_id);
146 }
147
148 FontGlyphIDs &operator+=(GlyphID rhs) noexcept {
149 switch (size()) {
150 case 0: value = (value & 0xffff'ffff'0000'ffff) | (static_cast<uint64_t>(rhs) << 16); break;
151 case 1: value = (value & 0xffff'0000'ffff'ffff) | (static_cast<uint64_t>(rhs) << 32); break;
152 case 2: value = (value & 0x0000'ffff'ffff'ffff) | (static_cast<uint64_t>(rhs) << 48); break;
153 case 3:
154 value = (value & FontID::mask) | new_pointer((*this)[0], (*this)[1], (*this)[2]);
155 [[fallthrough]];
156 default:
157 *(get_pointer()) += rhs;
158 }
159 return *this;
160 }
161
162 [[nodiscard]] tt_force_inline GlyphID front() const noexcept {
163 if (size() == 0) {
164 return GlyphID{};
165 } else {
166 return (*this)[0];
167 }
168 }
169
170 [[nodiscard]] tt_force_inline GlyphID operator[](size_t index) const noexcept {
171 if (has_pointer()) {
172 tt_assume(index < 18);
173 return get_pointer()->glyph_ids[index];
174 } else {
175 switch (index) {
176 case 0: return GlyphID{(value >> 16) & GlyphID::mask};
177 case 1: return GlyphID{(value >> 32) & GlyphID::mask};
178 case 2: return GlyphID{(value >> 48) & GlyphID::mask};
179 default: tt_no_default;
180 }
181 }
182 }
183
184 [[nodiscard]] tt_force_inline size_t size() const noexcept {
185 if (has_pointer()) {
186 return get_pointer()->nr_glyphs;
187 } else if (!(*this)[0]) {
188 return 0;
189 } else if (!(*this)[1]) {
190 return 1;
191 } else if (!(*this)[2]) {
192 return 2;
193 } else {
194 return 3;
195 }
196 }
197
198 [[nodiscard]] size_t hash() const noexcept {
199 if (has_pointer()) {
200 return get_pointer()->hash();
201 } else {
202 return std::hash<uint64_t>{}(value);
203 }
204 }
205
206 [[nodiscard]] std::pair<Path,aarect> getPathAndBoundingBox() const noexcept;
207 [[nodiscard]] aarect getBoundingBox() const noexcept;
208
209private:
210 [[nodiscard]] tt_force_inline bool has_pointer() const noexcept {
211 return (value & 0x8000) == 0;
212 }
213
214 [[nodiscard]] tt_force_inline FontGlyphIDs_long const *get_pointer() const noexcept {
215 tt_assume(has_pointer());
216 return std::launder(reinterpret_cast<FontGlyphIDs_long const *>(static_cast<ptrdiff_t>(value) >> 16));
217 }
218
219 [[nodiscard]] tt_force_inline FontGlyphIDs_long *get_pointer() noexcept {
220 tt_assume(has_pointer());
221 return std::launder(reinterpret_cast<FontGlyphIDs_long *>(static_cast<ptrdiff_t>(value) >> 16));
222 }
223
224 void delete_pointer() noexcept {
225 if (has_pointer()) {
226 delete get_pointer();
227 value = empty;
228 }
229 }
230
231 template<typename... Args>
232 [[nodiscard]] static uint64_t new_pointer(Args &&... args) noexcept {
233 auto *ptr = new FontGlyphIDs_long(std::forward<Args>(args)...);
234 return static_cast<uint64_t>(reinterpret_cast<ptrdiff_t>(ptr) << 16);
235 }
236
237public:
238 [[nodiscard]] friend bool operator==(FontGlyphIDs const &lhs, FontGlyphIDs const &rhs) noexcept {
239 if (lhs.has_pointer() == rhs.has_pointer()) {
240 if (lhs.has_pointer()) {
241 return *(lhs.get_pointer()) == *(rhs.get_pointer());
242 } else {
243 return lhs.value == rhs.value;
244 }
245 } else {
246 tt_assume(lhs.size() != rhs.size());
247 return false;
248 }
249 }
250};
251
252}
253
254namespace std {
255
256template<>
257struct hash<tt::FontGlyphIDs> {
258 [[nodiscard]] size_t operator()(tt::FontGlyphIDs const &rhs) const noexcept {
259 return rhs.hash();
260 }
261};
262
263}
STL namespace.
Class which represents an axis-aligned rectangle.
Definition aarect.hpp:13
Definition tagged_id.hpp:17
Definition FontGlyphIDs.hpp:21
Definition FontGlyphIDs.hpp:77
T operator()(T... args)
T swap(T... args)