HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
otype_glyf.hpp
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
5#pragma once
6
7#include "otype_utilities.hpp"
8#include "font_char_map.hpp"
9#include "../macros.hpp"
10#include <variant>
11#include <coroutine>
12
13hi_export_module(hikogui.font.otype_glyf);
14
15hi_export namespace hi { inline namespace v1 {
16namespace detail {
17constexpr uint16_t otype_glyf_flag_arg1_and_arg2_are_words = 0x0001;
18constexpr uint16_t otype_glyf_flag_args_are_xy_values = 0x0002;
19constexpr uint16_t otype_glyf_flag_round_xy_to_grid = 0x0004;
20constexpr uint16_t otype_glyf_flag_has_scale = 0x0008;
21constexpr uint16_t otype_glyf_flag_more_components = 0x0020;
22constexpr uint16_t otype_glyf_flag_has_xy_scale = 0x0040;
23constexpr uint16_t otype_glyf_flag_has_2x2 = 0x0080;
24constexpr uint16_t otype_glyf_flag_instructions = 0x0100;
25constexpr uint16_t otype_glyf_flag_use_this_glyph_metrics = 0x0200;
26constexpr uint16_t otype_glyf_flag_overlap_compound = 0x0400;
27constexpr uint16_t otype_glyf_flag_scaled_component_offset = 0x0800;
28constexpr uint16_t otype_glyf_flag_unscaled_component_offset = 0x1000;
29
30constexpr uint8_t otype_glyf_flag_on_curve = 0x01;
31constexpr uint8_t otype_glyf_flag_x_short = 0x02;
32constexpr uint8_t otype_glyf_flag_y_short = 0x04;
33constexpr uint8_t otype_glyf_flag_repeat = 0x08;
34constexpr uint8_t otype_glyf_flag_x_same = 0x10;
35constexpr uint8_t otype_glyf_flag_y_same = 0x20;
36
38 big_int16_buf_t num_contours;
43};
44
45} // namespace detail
46
52[[nodiscard]] inline graphic_path
53otype_glyf_get_path(std::span<std::byte const> bytes, float em_scale)
54{
55 auto r = graphic_path{};
56
57 if (bytes.empty()) {
58 // Empty glyphs have no path, and therefor an empty bounding rectangle.
59 return r;
60 }
61
62 auto offset = 0_uz;
63 auto const& header = implicit_cast<detail::otype_glyf_header>(offset, bytes);
64 auto const num_contours = *header.num_contours;
65
66 hi_check(num_contours >= 0, "'glyph' path requested on a compound glyph.");
67
68 // Check includes instructionLength.
69 auto const end_points = implicit_cast<big_uint16_buf_t>(offset, bytes, num_contours);
70
71 r.contourEndPoints.reserve(end_points.size());
72 uint16_t max_end_point = 0;
73 for (auto const end_point : end_points) {
74 auto const end_point_ = *end_point;
75
76 hi_check(end_point_ >= max_end_point, "'glyf' end-point indices must be increasing");
77 max_end_point = end_point_;
78
79 r.contourEndPoints.push_back(end_point_);
80 }
81
82 auto const num_points = wide_cast<size_t>(max_end_point) + 1;
83
84 // Skip over the instructions.
85 auto const instruction_size = *implicit_cast<big_uint16_buf_t>(offset, bytes);
86 offset += instruction_size;
87
88 // Extract all the flags.
89 auto flags = std::vector<uint8_t>(num_points, uint8_t{0});
90 for (auto i = 0_uz; i != num_points; ++i) {
91 auto const flag = implicit_cast<uint8_t>(offset, bytes);
92
93 flags[i] = flag;
94 if (to_bool(flag & detail::otype_glyf_flag_repeat)) {
95 auto const repeat = implicit_cast<uint8_t>(offset, bytes);
96
97 hi_check(i + repeat <= num_points, "'glyf' repeating flags out-of-bound");
98 for (std::size_t j = 0; j != repeat; ++j) {
99 flags[++i] = flag;
100 }
101 }
102 }
103
104 // Get xCoordinates
105 auto x_deltas = std::vector<int16_t>(num_points, int16_t{0});
106 for (auto i = 0_uz; i != num_points; ++i) {
107 auto const flag = flags[i];
108
109 if (to_bool(flag & detail::otype_glyf_flag_x_short)) {
110 if (to_bool(flag & detail::otype_glyf_flag_x_same)) {
111 x_deltas[i] = wide_cast<int16_t>(implicit_cast<uint8_t>(offset, bytes));
112 } else {
113 // Negative short.
114 x_deltas[i] = -wide_cast<int16_t>(implicit_cast<uint8_t>(offset, bytes));
115 }
116 } else {
117 if (to_bool(flag & detail::otype_glyf_flag_x_same)) {
118 x_deltas[i] = 0;
119 } else {
120 // Long
121 x_deltas[i] = *implicit_cast<big_int16_buf_t>(offset, bytes);
122 }
123 }
124 }
125
126 // Get yCoordinates
127 auto y_deltas = std::vector<int16_t>(num_points, int16_t{0});
128 for (auto i = 0_uz; i != num_points; ++i) {
129 auto const flag = flags[i];
130
131 if (to_bool(flag & detail::otype_glyf_flag_y_short)) {
132 if (to_bool(flag & detail::otype_glyf_flag_y_same)) {
133 y_deltas[i] = wide_cast<int16_t>(implicit_cast<uint8_t>(offset, bytes));
134 } else {
135 // Negative short.
136 y_deltas[i] = -wide_cast<int16_t>(implicit_cast<uint8_t>(offset, bytes));
137 }
138 } else {
139 if (to_bool(flag & detail::otype_glyf_flag_y_same)) {
140 y_deltas[i] = 0;
141 } else {
142 // Long
143 y_deltas[i] = *implicit_cast<big_int16_buf_t>(offset, bytes);
144 }
145 }
146 }
147
148 // Create absolute points
149 int x = 0;
150 int y = 0;
151 r.points.reserve(num_points);
152 for (auto i = 0_uz; i != num_points; ++i) {
153 auto const flag = flags[i];
154 x += x_deltas[i];
155 y += y_deltas[i];
156
157 auto const type =
158 to_bool(flag & detail::otype_glyf_flag_on_curve) ? bezier_point::Type::Anchor : bezier_point::Type::QuadraticControl;
159
160 r.points.emplace_back(x * em_scale, y * em_scale, type);
161 }
162
163 return r;
164}
165
167 hi::glyph_id glyph_id = {};
168 vector2 offset = {};
169 matrix2 scale = {};
170
174
178
181 bool use_points = false;
182
185 bool use_for_metrics = false;
186};
187
193[[nodiscard]] inline generator<otype_glyf_component> otype_glyf_get_compound(std::span<std::byte const> bytes, float em_scale)
194{
195 if (bytes.empty()) {
196 // Empty glyphs have no path, and therefor an empty bounding rectangle.
197 co_return;
198 }
199
200 auto offset = 0_uz;
201 auto const& header = implicit_cast<detail::otype_glyf_header>(offset, bytes);
202
203 hi_check(*header.num_contours < 0, "'glyph' compound requested on a simple glyph.");
204
205 uint16_t flags;
206 do {
207 flags = *implicit_cast<big_uint16_buf_t>(offset, bytes);
208
209 auto component = otype_glyf_component{};
210 component.glyph_id = glyph_id{*implicit_cast<big_uint16_buf_t>(offset, bytes)};
211
212 if (to_bool(flags & detail::otype_glyf_flag_args_are_xy_values)) {
213 if (to_bool(flags & detail::otype_glyf_flag_arg1_and_arg2_are_words)) {
214 auto const tmp = implicit_cast<otype_fword_buf_t>(offset, bytes, 2);
215 component.offset = vector2{tmp[0] * em_scale, tmp[1] * em_scale};
216 } else {
217 auto const tmp = implicit_cast<otype_fbyte_buf_t>(offset, bytes, 2);
218 component.offset = vector2{tmp[0] * em_scale, tmp[1] * em_scale};
219 }
220 } else {
221 component.use_points = true;
222 if (to_bool(flags & detail::otype_glyf_flag_arg1_and_arg2_are_words)) {
223 auto const tmp = implicit_cast<big_uint16_buf_t>(offset, bytes, 2);
224 component.compound_point_index = *tmp[0];
225 component.component_point_index = *tmp[1];
226 } else {
227 auto const tmp = implicit_cast<uint8_t>(offset, bytes, 2);
228 component.compound_point_index = tmp[0];
229 component.component_point_index = tmp[1];
230 }
231 }
232
233 // Start with an identity matrix.
234 if (to_bool(flags & detail::otype_glyf_flag_has_scale)) {
235 component.scale = scale2(*implicit_cast<otype_fixed1_14_buf_t>(offset, bytes));
236
237 } else if (to_bool(flags & detail::otype_glyf_flag_has_xy_scale)) {
238 auto const tmp = implicit_cast<otype_fixed1_14_buf_t>(offset, bytes, 2);
239 component.scale = scale2(*tmp[0], *tmp[1]);
240
241 } else if (to_bool(flags & detail::otype_glyf_flag_has_2x2)) {
242 auto const tmp = implicit_cast<otype_fixed1_14_buf_t>(offset, bytes, 4);
243 component.scale = matrix2{vector2{*tmp[0], *tmp[1]}, vector2{*tmp[2], *tmp[3]}};
244 }
245
246 if (to_bool(flags & detail::otype_glyf_flag_scaled_component_offset)) {
247 component.offset = component.scale * component.offset;
248 }
249
250 if (to_bool(flags & detail::otype_glyf_flag_use_this_glyph_metrics)) {
251 component.use_for_metrics = true;
252 }
253
254 co_yield component;
255
256 } while (to_bool(flags & detail::otype_glyf_flag_more_components));
257 // Ignore trailing instructions.
258}
259
263[[nodiscard]] inline bool otype_glyf_is_compound(std::span<std::byte const> bytes)
264{
265 struct header_type {
266 big_int16_buf_t num_contours;
267 otype_fword_buf_t x_min;
268 otype_fword_buf_t y_min;
269 otype_fword_buf_t x_max;
270 otype_fword_buf_t y_max;
271 };
272
273 if (bytes.empty()) {
274 // Empty glyphs are simple, non-compound glyphs.
275 return false;
276 }
277
278 auto const& header = implicit_cast<detail::otype_glyf_header>(bytes);
279 return *header.num_contours < 0;
280}
281
287[[nodiscard]] inline aarectangle otype_glyf_get_bounding_box(std::span<std::byte const> bytes, float em_scale)
288{
289 struct header_type {
290 big_int16_buf_t num_contours;
291 otype_fword_buf_t x_min;
292 otype_fword_buf_t y_min;
293 otype_fword_buf_t x_max;
294 otype_fword_buf_t y_max;
295 };
296
297 if (bytes.empty()) {
298 // Empty glyphs have no path, and therefor an empty bounding rectangle.
299 return aarectangle{};
300 }
301
302 auto const& header = implicit_cast<detail::otype_glyf_header>(bytes);
303
304 auto const x_min = header.x_min * em_scale;
305 auto const y_min = header.y_min * em_scale;
306 auto const x_max = header.x_max * em_scale;
307 auto const y_max = header.y_max * em_scale;
308
309 hi_check(x_min <= x_max, "'glyf' bounding box is invalid.");
310 hi_check(y_min <= y_max, "'glyf' bounding box is invalid.");
311
312 return aarectangle{point2{x_min, y_min}, point2{x_max, y_max}};
313}
314
315}} // namespace hi::v1
Defined font_char_map type.
The HikoGUI namespace.
Definition array_generic.hpp:20
bool otype_glyf_is_compound(std::span< std::byte const > bytes)
Check if this glyph is a compound or simple glyph.
Definition otype_glyf.hpp:263
aarectangle otype_glyf_get_bounding_box(std::span< std::byte const > bytes, float em_scale)
Get the bounding box of a simple glyph.
Definition otype_glyf.hpp:287
graphic_path otype_glyf_get_path(std::span< std::byte const > bytes, float em_scale)
Get the graphic-path of a simple glyph.
Definition otype_glyf.hpp:53
generator< otype_glyf_component > otype_glyf_get_compound(std::span< std::byte const > bytes, float em_scale)
Get the components of a compound glyph.
Definition otype_glyf.hpp:193
DOXYGEN BUG.
Definition algorithm_misc.hpp:20
Definition otype_glyf.hpp:37
Definition otype_glyf.hpp:166
bool use_points
The component is positioned using anchor points.
Definition otype_glyf.hpp:181
size_t component_point_index
The point in the current component being added to the compound.
Definition otype_glyf.hpp:177
bool use_for_metrics
Use this glyph for the metrics of the compound.
Definition otype_glyf.hpp:185
size_t compound_point_index
The point-nr in the compound being assembled.
Definition otype_glyf.hpp:173
Open-type for 16 signed integer that must be scaled by the EM-scale.
Definition otype_utilities.hpp:40
Class which represents an axis-aligned rectangle.
Definition aarectangle.hpp:33
A 2D or 3D homogenius matrix for transforming homogenious vectors and points.
Definition matrix2.hpp:39
Definition scale2.hpp:18
A high-level geometric vector Part of the high-level vector, point, mat and color types.
Definition vector2.hpp:27
A path is a vector graphics object.
Definition graphic_path.hpp:29