1#ifndef OT_GLYF_PATH_BUILDER_HH
2#define OT_GLYF_PATH_BUILDER_HH
3
4
5#include "../../hb.hh"
6
7
8namespace OT {
9namespace glyf_impl {
10
11
12struct path_builder_t
13{
14 hb_font_t *font;
15 hb_draw_session_t *draw_session;
16
17 struct optional_point_t
18 {
19 optional_point_t () {}
20 optional_point_t (float x_, float y_) : has_data (true), x (x_), y (y_) {}
21 operator bool () const { return has_data; }
22
23 bool has_data = false;
24 float x;
25 float y;
26
27 optional_point_t mid (optional_point_t p)
28 { return optional_point_t ((x + p.x) * 0.5f, (y + p.y) * 0.5f); }
29 } first_oncurve, first_offcurve, first_offcurve2, last_offcurve, last_offcurve2;
30
31 path_builder_t (hb_font_t *font_, hb_draw_session_t &draw_session_) :
32 font (font_), draw_session (&draw_session_) {}
33
34 /* based on https://github.com/RazrFalcon/ttf-parser/blob/4f32821/src/glyf.rs#L287
35 See also:
36 * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM01/Chap1.html
37 * https://stackoverflow.com/a/20772557
38 *
39 * Cubic support added. */
40 HB_ALWAYS_INLINE
41 void consume_point (const contour_point_t &point)
42 {
43 bool is_on_curve = point.flag & glyf_impl::SimpleGlyph::FLAG_ON_CURVE;
44#ifdef HB_NO_CUBIC_GLYF
45 bool is_cubic = false;
46#else
47 bool is_cubic = !is_on_curve && (point.flag & glyf_impl::SimpleGlyph::FLAG_CUBIC);
48#endif
49 optional_point_t p (font->em_fscalef_x (point.x), font->em_fscalef_y (point.y));
50 if (unlikely (!first_oncurve))
51 {
52 if (is_on_curve)
53 {
54 first_oncurve = p;
55 draw_session->move_to (p.x, p.y);
56 }
57 else
58 {
59 if (is_cubic && !first_offcurve2)
60 {
61 first_offcurve2 = first_offcurve;
62 first_offcurve = p;
63 }
64 else if (first_offcurve)
65 {
66 optional_point_t mid = first_offcurve.mid (p);
67 first_oncurve = mid;
68 last_offcurve = p;
69 draw_session->move_to (mid.x, mid.y);
70 }
71 else
72 first_offcurve = p;
73 }
74 }
75 else
76 {
77 if (last_offcurve)
78 {
79 if (is_on_curve)
80 {
81 if (last_offcurve2)
82 {
83 draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y,
84 last_offcurve.x, last_offcurve.y,
85 p.x, p.y);
86 last_offcurve2 = optional_point_t ();
87 }
88 else
89 draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
90 p.x, p.y);
91 last_offcurve = optional_point_t ();
92 }
93 else
94 {
95 if (is_cubic && !last_offcurve2)
96 {
97 last_offcurve2 = last_offcurve;
98 last_offcurve = p;
99 }
100 else
101 {
102 optional_point_t mid = last_offcurve.mid (p);
103
104 if (is_cubic)
105 {
106 draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y,
107 last_offcurve.x, last_offcurve.y,
108 mid.x, mid.y);
109 last_offcurve2 = optional_point_t ();
110 }
111 else
112 draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
113 mid.x, mid.y);
114 last_offcurve = p;
115 }
116 }
117 }
118 else
119 {
120 if (is_on_curve)
121 draw_session->line_to (p.x, p.y);
122 else
123 last_offcurve = p;
124 }
125 }
126
127 if (unlikely (point.is_end_point))
128 {
129 if (first_offcurve && last_offcurve)
130 {
131 optional_point_t mid = last_offcurve.mid (first_offcurve2 ?
132 first_offcurve2 :
133 first_offcurve);
134 if (last_offcurve2)
135 draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y,
136 last_offcurve.x, last_offcurve.y,
137 mid.x, mid.y);
138 else
139 draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
140 mid.x, mid.y);
141 last_offcurve = optional_point_t ();
142 }
143 /* now check the rest */
144
145 if (first_offcurve && first_oncurve)
146 {
147 if (first_offcurve2)
148 draw_session->cubic_to (first_offcurve2.x, first_offcurve2.y,
149 first_offcurve.x, first_offcurve.y,
150 first_oncurve.x, first_oncurve.y);
151 else
152 draw_session->quadratic_to (first_offcurve.x, first_offcurve.y,
153 first_oncurve.x, first_oncurve.y);
154 }
155 else if (last_offcurve && first_oncurve)
156 {
157 if (last_offcurve2)
158 draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y,
159 last_offcurve.x, last_offcurve.y,
160 first_oncurve.x, first_oncurve.y);
161 else
162 draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
163 first_oncurve.x, first_oncurve.y);
164 }
165 else if (first_oncurve)
166 draw_session->line_to (first_oncurve.x, first_oncurve.y);
167 else if (first_offcurve)
168 {
169 float x = first_offcurve.x, y = first_offcurve.y;
170 draw_session->move_to (x, y);
171 draw_session->quadratic_to (x, y, x, y);
172 }
173
174 /* Getting ready for the next contour */
175 first_oncurve = first_offcurve = last_offcurve = last_offcurve2 = optional_point_t ();
176 draw_session->close_path ();
177 }
178 }
179 void points_end () {}
180
181 bool is_consuming_contour_points () { return true; }
182 contour_point_t *get_phantoms_sink () { return nullptr; }
183};
184
185
186} /* namespace glyf_impl */
187} /* namespace OT */
188
189
190#endif /* OT_GLYF_PATH_BUILDER_HH */
191