xref: /aosp_15_r20/external/harfbuzz_ng/src/hb-outline.cc (revision 2d1272b857b1f7575e6e246373e1cb218663db8a)
1 /*
2  * Copyright © 2023  Behdad Esfahbod
3  * Copyright © 1999  David Turner
4  * Copyright © 2005  Werner Lemberg
5  * Copyright © 2013-2015  Alexei Podtelezhnikov
6  *
7  *  This is part of HarfBuzz, a text shaping library.
8  *
9  * Permission is hereby granted, without written agreement and without
10  * license or royalty fees, to use, copy, modify, and distribute this
11  * software and its documentation for any purpose, provided that the
12  * above copyright notice and the following two paragraphs appear in
13  * all copies of this software.
14  *
15  * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
16  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
17  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
18  * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
19  * DAMAGE.
20  *
21  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
22  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
23  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
24  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
25  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
26  */
27 
28 #include "hb.hh"
29 
30 #ifndef HB_NO_OUTLINE
31 
32 #include "hb-outline.hh"
33 
34 #include "hb-machinery.hh"
35 
36 
replay(hb_draw_funcs_t * pen,void * pen_data) const37 void hb_outline_t::replay (hb_draw_funcs_t *pen, void *pen_data) const
38 {
39   hb_draw_state_t st = HB_DRAW_STATE_DEFAULT;
40 
41   unsigned first = 0;
42   for (unsigned contour : contours)
43   {
44     auto it = points.as_array ().sub_array (first, contour - first);
45     while (it)
46     {
47       hb_outline_point_t p1 = *it++;
48       switch (p1.type)
49       {
50 	case hb_outline_point_t::type_t::MOVE_TO:
51 	{
52 	  pen->move_to (pen_data, st,
53 			   p1.x, p1.y);
54 	}
55 	break;
56 	case hb_outline_point_t::type_t::LINE_TO:
57 	{
58 	  pen->line_to (pen_data, st,
59 			   p1.x, p1.y);
60 	}
61 	break;
62 	case hb_outline_point_t::type_t::QUADRATIC_TO:
63 	{
64 	  hb_outline_point_t p2 = *it++;
65 	  pen->quadratic_to (pen_data, st,
66 				p1.x, p1.y,
67 				p2.x, p2.y);
68 	}
69 	break;
70 	case hb_outline_point_t::type_t::CUBIC_TO:
71 	{
72 	  hb_outline_point_t p2 = *it++;
73 	  hb_outline_point_t p3 = *it++;
74 	  pen->cubic_to (pen_data, st,
75 			    p1.x, p1.y,
76 			    p2.x, p2.y,
77 			    p3.x, p3.y);
78 	}
79 	break;
80       }
81     }
82     pen->close_path (pen_data, st);
83     first = contour;
84   }
85 }
86 
control_area() const87 float hb_outline_t::control_area () const
88 {
89   float a = 0;
90   unsigned first = 0;
91   for (unsigned contour : contours)
92   {
93     for (unsigned i = first; i < contour; i++)
94     {
95       unsigned j = i + 1 < contour ? i + 1 : first;
96 
97       auto &pi = points[i];
98       auto &pj = points[j];
99       a += pi.x * pj.y - pi.y * pj.x;
100     }
101 
102     first = contour;
103   }
104   return a * .5f;
105 }
106 
embolden(float x_strength,float y_strength,float x_shift,float y_shift)107 void hb_outline_t::embolden (float x_strength, float y_strength,
108 			     float x_shift, float y_shift)
109 {
110   /* This function is a straight port of FreeType's FT_Outline_EmboldenXY.
111    * Permission has been obtained from the FreeType authors of the code
112    * to relicense it under the HarfBuzz license. */
113 
114   if (!x_strength && !y_strength) return;
115   if (!points) return;
116 
117   x_strength /= 2.f;
118   y_strength /= 2.f;
119 
120   bool orientation_negative = control_area () < 0;
121 
122   signed first = 0;
123   for (unsigned c = 0; c < contours.length; c++)
124   {
125     hb_outline_vector_t in, out, anchor, shift;
126     float l_in, l_out, l_anchor = 0, l, q, d;
127 
128     l_in = 0;
129     signed last = (int) contours[c] - 1;
130 
131     /* pacify compiler */
132     in.x = in.y = anchor.x = anchor.y = 0;
133 
134     /* Counter j cycles though the points; counter i advances only  */
135     /* when points are moved; anchor k marks the first moved point. */
136     for ( signed i = last, j = first, k = -1;
137 	  j != i && i != k;
138 	  j = j < last ? j + 1 : first )
139     {
140       if ( j != k )
141       {
142 	out.x = points[j].x - points[i].x;
143 	out.y = points[j].y - points[i].y;
144 	l_out = out.normalize_len ();
145 
146 	if ( l_out == 0 )
147 	  continue;
148       }
149       else
150       {
151 	out   = anchor;
152 	l_out = l_anchor;
153       }
154 
155       if ( l_in != 0 )
156       {
157 	if ( k < 0 )
158 	{
159 	  k        = i;
160 	  anchor   = in;
161 	  l_anchor = l_in;
162 	}
163 
164 	d = in.x * out.x + in.y * out.y;
165 
166 	/* shift only if turn is less than ~160 degrees */
167 	if ( d > -15.f/16.f )
168 	{
169 	  d = d + 1.f;
170 
171 	  /* shift components along lateral bisector in proper orientation */
172 	  shift.x = in.y + out.y;
173 	  shift.y = in.x + out.x;
174 
175 	  if ( orientation_negative )
176 	    shift.x = -shift.x;
177 	  else
178 	    shift.y = -shift.y;
179 
180 	  /* restrict shift magnitude to better handle collapsing segments */
181 	  q = out.x * in.y - out.y * in.x;
182 	  if ( orientation_negative )
183 	    q = -q;
184 
185 	  l = hb_min (l_in, l_out);
186 
187 	  /* non-strict inequalities avoid divide-by-zero when q == l == 0 */
188 	  if (x_strength * q <= l * d)
189 	    shift.x = shift.x * x_strength / d;
190 	  else
191 	    shift.x = shift.x * l / q;
192 
193 
194 	  if (y_strength * q <= l * d)
195 	    shift.y = shift.y * y_strength / d;
196 	  else
197 	    shift.y = shift.y * l / q;
198 	}
199 	else
200 	  shift.x = shift.y = 0;
201 
202 	for ( ;
203 	      i != j;
204 	      i = i < last ? i + 1 : first )
205 	{
206 	  points[i].x += x_shift + shift.x;
207 	  points[i].y += y_shift + shift.y;
208 	}
209       }
210       else
211 	i = j;
212 
213       in   = out;
214       l_in = l_out;
215     }
216 
217     first = last + 1;
218   }
219 }
220 
221 static void
hb_outline_recording_pen_move_to(hb_draw_funcs_t * dfuncs HB_UNUSED,void * data,hb_draw_state_t * st,float to_x,float to_y,void * user_data HB_UNUSED)222 hb_outline_recording_pen_move_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
223 				  void *data,
224 				  hb_draw_state_t *st,
225 				  float to_x, float to_y,
226 				  void *user_data HB_UNUSED)
227 {
228   hb_outline_t *c = (hb_outline_t *) data;
229 
230   c->points.push (hb_outline_point_t {to_x, to_y, hb_outline_point_t::type_t::MOVE_TO});
231 }
232 
233 static void
hb_outline_recording_pen_line_to(hb_draw_funcs_t * dfuncs HB_UNUSED,void * data,hb_draw_state_t * st,float to_x,float to_y,void * user_data HB_UNUSED)234 hb_outline_recording_pen_line_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
235 				  void *data,
236 				  hb_draw_state_t *st,
237 				  float to_x, float to_y,
238 				  void *user_data HB_UNUSED)
239 {
240   hb_outline_t *c = (hb_outline_t *) data;
241 
242   c->points.push (hb_outline_point_t {to_x, to_y, hb_outline_point_t::type_t::LINE_TO});
243 }
244 
245 static void
hb_outline_recording_pen_quadratic_to(hb_draw_funcs_t * dfuncs HB_UNUSED,void * data,hb_draw_state_t * st,float control_x,float control_y,float to_x,float to_y,void * user_data HB_UNUSED)246 hb_outline_recording_pen_quadratic_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
247 				       void *data,
248 				       hb_draw_state_t *st,
249 				       float control_x, float control_y,
250 				       float to_x, float to_y,
251 				       void *user_data HB_UNUSED)
252 {
253   hb_outline_t *c = (hb_outline_t *) data;
254 
255   c->points.push (hb_outline_point_t {control_x, control_y, hb_outline_point_t::type_t::QUADRATIC_TO});
256   c->points.push (hb_outline_point_t {to_x, to_y, hb_outline_point_t::type_t::QUADRATIC_TO});
257 }
258 
259 static void
hb_outline_recording_pen_cubic_to(hb_draw_funcs_t * dfuncs HB_UNUSED,void * data,hb_draw_state_t * st,float control1_x,float control1_y,float control2_x,float control2_y,float to_x,float to_y,void * user_data HB_UNUSED)260 hb_outline_recording_pen_cubic_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
261 				   void *data,
262 				   hb_draw_state_t *st,
263 				   float control1_x, float control1_y,
264 				   float control2_x, float control2_y,
265 				   float to_x, float to_y,
266 				   void *user_data HB_UNUSED)
267 {
268   hb_outline_t *c = (hb_outline_t *) data;
269 
270   c->points.push (hb_outline_point_t {control1_x, control1_y, hb_outline_point_t::type_t::CUBIC_TO});
271   c->points.push (hb_outline_point_t {control2_x, control2_y, hb_outline_point_t::type_t::CUBIC_TO});
272   c->points.push (hb_outline_point_t {to_x, to_y, hb_outline_point_t::type_t::CUBIC_TO});
273 }
274 
275 static void
hb_outline_recording_pen_close_path(hb_draw_funcs_t * dfuncs HB_UNUSED,void * data,hb_draw_state_t * st,void * user_data HB_UNUSED)276 hb_outline_recording_pen_close_path (hb_draw_funcs_t *dfuncs HB_UNUSED,
277 				     void *data,
278 				     hb_draw_state_t *st,
279 				     void *user_data HB_UNUSED)
280 {
281   hb_outline_t *c = (hb_outline_t *) data;
282 
283   c->contours.push (c->points.length);
284 }
285 
286 static inline void free_static_outline_recording_pen_funcs ();
287 
288 static struct hb_outline_recording_pen_funcs_lazy_loader_t : hb_draw_funcs_lazy_loader_t<hb_outline_recording_pen_funcs_lazy_loader_t>
289 {
createhb_outline_recording_pen_funcs_lazy_loader_t290   static hb_draw_funcs_t *create ()
291   {
292     hb_draw_funcs_t *funcs = hb_draw_funcs_create ();
293 
294     hb_draw_funcs_set_move_to_func (funcs, hb_outline_recording_pen_move_to, nullptr, nullptr);
295     hb_draw_funcs_set_line_to_func (funcs, hb_outline_recording_pen_line_to, nullptr, nullptr);
296     hb_draw_funcs_set_quadratic_to_func (funcs, hb_outline_recording_pen_quadratic_to, nullptr, nullptr);
297     hb_draw_funcs_set_cubic_to_func (funcs, hb_outline_recording_pen_cubic_to, nullptr, nullptr);
298     hb_draw_funcs_set_close_path_func (funcs, hb_outline_recording_pen_close_path, nullptr, nullptr);
299 
300     hb_draw_funcs_make_immutable (funcs);
301 
302     hb_atexit (free_static_outline_recording_pen_funcs);
303 
304     return funcs;
305   }
306 } static_outline_recording_pen_funcs;
307 
308 static inline
free_static_outline_recording_pen_funcs()309 void free_static_outline_recording_pen_funcs ()
310 {
311   static_outline_recording_pen_funcs.free_instance ();
312 }
313 
314 hb_draw_funcs_t *
hb_outline_recording_pen_get_funcs()315 hb_outline_recording_pen_get_funcs ()
316 {
317   return static_outline_recording_pen_funcs.get_unconst ();
318 }
319 
320 
321 #endif
322