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