1/*
2 * Copyright © 2020 Ebrahim Byagowi
3 *
4 * This is part of HarfBuzz, a text shaping library.
5 *
6 * Permission is hereby granted, without written agreement and without
7 * license or royalty fees, to use, copy, modify, and distribute this
8 * software and its documentation for any purpose, provided that the
9 * above copyright notice and the following two paragraphs appear in
10 * all copies of this software.
11 *
12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
16 * DAMAGE.
17 *
18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
23 */
24
25#ifndef HB_DRAW_HH
26#define HB_DRAW_HH
27
28#include "hb.hh"
29
30
31/*
32 * hb_draw_funcs_t
33 */
34
35#define HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS \
36 HB_DRAW_FUNC_IMPLEMENT (move_to) \
37 HB_DRAW_FUNC_IMPLEMENT (line_to) \
38 HB_DRAW_FUNC_IMPLEMENT (quadratic_to) \
39 HB_DRAW_FUNC_IMPLEMENT (cubic_to) \
40 HB_DRAW_FUNC_IMPLEMENT (close_path) \
41 /* ^--- Add new callbacks here */
42
43struct hb_draw_funcs_t
44{
45 hb_object_header_t header;
46
47 struct {
48#define HB_DRAW_FUNC_IMPLEMENT(name) hb_draw_##name##_func_t name;
49 HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS
50#undef HB_DRAW_FUNC_IMPLEMENT
51 } func;
52
53 struct {
54#define HB_DRAW_FUNC_IMPLEMENT(name) void *name;
55 HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS
56#undef HB_DRAW_FUNC_IMPLEMENT
57 } *user_data;
58
59 struct {
60#define HB_DRAW_FUNC_IMPLEMENT(name) hb_destroy_func_t name;
61 HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS
62#undef HB_DRAW_FUNC_IMPLEMENT
63 } *destroy;
64
65 void emit_move_to (void *draw_data, hb_draw_state_t &st,
66 float to_x, float to_y)
67 { func.move_to (this, draw_data, &st,
68 to_x, to_y,
69 !user_data ? nullptr : user_data->move_to); }
70 void emit_line_to (void *draw_data, hb_draw_state_t &st,
71 float to_x, float to_y)
72 { func.line_to (this, draw_data, &st,
73 to_x, to_y,
74 !user_data ? nullptr : user_data->line_to); }
75 void emit_quadratic_to (void *draw_data, hb_draw_state_t &st,
76 float control_x, float control_y,
77 float to_x, float to_y)
78 { func.quadratic_to (this, draw_data, &st,
79 control_x, control_y,
80 to_x, to_y,
81 !user_data ? nullptr : user_data->quadratic_to); }
82 void emit_cubic_to (void *draw_data, hb_draw_state_t &st,
83 float control1_x, float control1_y,
84 float control2_x, float control2_y,
85 float to_x, float to_y)
86 { func.cubic_to (this, draw_data, &st,
87 control1_x, control1_y,
88 control2_x, control2_y,
89 to_x, to_y,
90 !user_data ? nullptr : user_data->cubic_to); }
91 void emit_close_path (void *draw_data, hb_draw_state_t &st)
92 { func.close_path (this, draw_data, &st,
93 !user_data ? nullptr : user_data->close_path); }
94
95
96 void
97 HB_ALWAYS_INLINE
98 move_to (void *draw_data, hb_draw_state_t &st,
99 float to_x, float to_y)
100 {
101 if (unlikely (st.path_open)) close_path (draw_data, st);
102 st.current_x = to_x;
103 st.current_y = to_y;
104 }
105
106 void
107 HB_ALWAYS_INLINE
108 line_to (void *draw_data, hb_draw_state_t &st,
109 float to_x, float to_y)
110 {
111 if (unlikely (!st.path_open)) start_path (draw_data, st);
112 emit_line_to (draw_data, st, to_x, to_y);
113 st.current_x = to_x;
114 st.current_y = to_y;
115 }
116
117 void
118 HB_ALWAYS_INLINE
119 quadratic_to (void *draw_data, hb_draw_state_t &st,
120 float control_x, float control_y,
121 float to_x, float to_y)
122 {
123 if (unlikely (!st.path_open)) start_path (draw_data, st);
124 emit_quadratic_to (draw_data, st, control_x, control_y, to_x, to_y);
125 st.current_x = to_x;
126 st.current_y = to_y;
127 }
128
129 void
130 HB_ALWAYS_INLINE
131 cubic_to (void *draw_data, hb_draw_state_t &st,
132 float control1_x, float control1_y,
133 float control2_x, float control2_y,
134 float to_x, float to_y)
135 {
136 if (unlikely (!st.path_open)) start_path (draw_data, st);
137 emit_cubic_to (draw_data, st, control1_x, control1_y, control2_x, control2_y, to_x, to_y);
138 st.current_x = to_x;
139 st.current_y = to_y;
140 }
141
142 void
143 HB_ALWAYS_INLINE
144 close_path (void *draw_data, hb_draw_state_t &st)
145 {
146 if (likely (st.path_open))
147 {
148 if ((st.path_start_x != st.current_x) || (st.path_start_y != st.current_y))
149 emit_line_to (draw_data, st, st.path_start_x, st.path_start_y);
150 emit_close_path (draw_data, st);
151 }
152 st.path_open = false;
153 st.path_start_x = st.current_x = st.path_start_y = st.current_y = 0;
154 }
155
156 protected:
157
158 void start_path (void *draw_data, hb_draw_state_t &st)
159 {
160 assert (!st.path_open);
161 emit_move_to (draw_data, st, st.current_x, st.current_y);
162 st.path_open = true;
163 st.path_start_x = st.current_x;
164 st.path_start_y = st.current_y;
165 }
166};
167DECLARE_NULL_INSTANCE (hb_draw_funcs_t);
168
169struct hb_draw_session_t
170{
171 hb_draw_session_t (hb_draw_funcs_t *funcs_, void *draw_data_, float slant_ = 0.f)
172 : slant {slant_}, not_slanted {slant == 0.f},
173 funcs {funcs_}, draw_data {draw_data_}, st HB_DRAW_STATE_DEFAULT
174 {}
175
176 ~hb_draw_session_t () { close_path (); }
177
178 HB_ALWAYS_INLINE
179 void move_to (float to_x, float to_y)
180 {
181 if (likely (not_slanted))
182 funcs->move_to (draw_data, st,
183 to_x, to_y);
184 else
185 funcs->move_to (draw_data, st,
186 to_x + to_y * slant, to_y);
187 }
188 HB_ALWAYS_INLINE
189 void line_to (float to_x, float to_y)
190 {
191 if (likely (not_slanted))
192 funcs->line_to (draw_data, st,
193 to_x, to_y);
194 else
195 funcs->line_to (draw_data, st,
196 to_x + to_y * slant, to_y);
197 }
198 void
199 HB_ALWAYS_INLINE
200 quadratic_to (float control_x, float control_y,
201 float to_x, float to_y)
202 {
203 if (likely (not_slanted))
204 funcs->quadratic_to (draw_data, st,
205 control_x, control_y,
206 to_x, to_y);
207 else
208 funcs->quadratic_to (draw_data, st,
209 control_x + control_y * slant, control_y,
210 to_x + to_y * slant, to_y);
211 }
212 void
213 HB_ALWAYS_INLINE
214 cubic_to (float control1_x, float control1_y,
215 float control2_x, float control2_y,
216 float to_x, float to_y)
217 {
218 if (likely (not_slanted))
219 funcs->cubic_to (draw_data, st,
220 control1_x, control1_y,
221 control2_x, control2_y,
222 to_x, to_y);
223 else
224 funcs->cubic_to (draw_data, st,
225 control1_x + control1_y * slant, control1_y,
226 control2_x + control2_y * slant, control2_y,
227 to_x + to_y * slant, to_y);
228 }
229 HB_ALWAYS_INLINE
230 void close_path ()
231 {
232 funcs->close_path (draw_data, st);
233 }
234
235 protected:
236 float slant;
237 bool not_slanted;
238 hb_draw_funcs_t *funcs;
239 void *draw_data;
240 hb_draw_state_t st;
241};
242
243#endif /* HB_DRAW_HH */
244