hb-outline.cc (8602B)
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 37 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 87 void hb_outline_t::translate (float dx, float dy) 88 { 89 for (auto &p : points) 90 { 91 p.x += dx; 92 p.y += dy; 93 } 94 } 95 96 void hb_outline_t::slant (float slant_xy) 97 { 98 for (auto &p : points) 99 p.x += slant_xy * p.y; 100 } 101 102 float hb_outline_t::control_area () const 103 { 104 float a = 0; 105 unsigned first = 0; 106 for (unsigned contour : contours) 107 { 108 for (unsigned i = first; i < contour; i++) 109 { 110 unsigned j = i + 1 < contour ? i + 1 : first; 111 112 auto &pi = points[i]; 113 auto &pj = points[j]; 114 a += pi.x * pj.y - pi.y * pj.x; 115 } 116 117 first = contour; 118 } 119 return a * .5f; 120 } 121 122 void hb_outline_t::embolden (float x_strength, float y_strength, 123 float x_shift, float y_shift) 124 { 125 /* This function is a straight port of FreeType's FT_Outline_EmboldenXY. 126 * Permission has been obtained from the FreeType authors of the code 127 * to relicense it under the HarfBuzz license. */ 128 129 if (!x_strength && !y_strength) return; 130 if (!points) return; 131 132 x_strength /= 2.f; 133 y_strength /= 2.f; 134 135 bool orientation_negative = control_area () < 0; 136 137 signed first = 0; 138 for (unsigned c = 0; c < contours.length; c++) 139 { 140 hb_outline_vector_t in, out, anchor, shift; 141 float l_in, l_out, l_anchor = 0, l, q, d; 142 143 l_in = 0; 144 signed last = (int) contours[c] - 1; 145 146 /* pacify compiler */ 147 in.x = in.y = anchor.x = anchor.y = 0; 148 149 /* Counter j cycles though the points; counter i advances only */ 150 /* when points are moved; anchor k marks the first moved point. */ 151 for ( signed i = last, j = first, k = -1; 152 j != i && i != k; 153 j = j < last ? j + 1 : first ) 154 { 155 if ( j != k ) 156 { 157 out.x = points[j].x - points[i].x; 158 out.y = points[j].y - points[i].y; 159 l_out = out.normalize_len (); 160 161 if ( l_out == 0 ) 162 continue; 163 } 164 else 165 { 166 out = anchor; 167 l_out = l_anchor; 168 } 169 170 if ( l_in != 0 ) 171 { 172 if ( k < 0 ) 173 { 174 k = i; 175 anchor = in; 176 l_anchor = l_in; 177 } 178 179 d = in.x * out.x + in.y * out.y; 180 181 /* shift only if turn is less than ~160 degrees */ 182 if ( d > -15.f/16.f ) 183 { 184 d = d + 1.f; 185 186 /* shift components along lateral bisector in proper orientation */ 187 shift.x = in.y + out.y; 188 shift.y = in.x + out.x; 189 190 if ( orientation_negative ) 191 shift.x = -shift.x; 192 else 193 shift.y = -shift.y; 194 195 /* restrict shift magnitude to better handle collapsing segments */ 196 q = out.x * in.y - out.y * in.x; 197 if ( orientation_negative ) 198 q = -q; 199 200 l = hb_min (l_in, l_out); 201 202 /* non-strict inequalities avoid divide-by-zero when q == l == 0 */ 203 if (x_strength * q <= l * d) 204 shift.x = shift.x * x_strength / d; 205 else 206 shift.x = shift.x * l / q; 207 208 209 if (y_strength * q <= l * d) 210 shift.y = shift.y * y_strength / d; 211 else 212 shift.y = shift.y * l / q; 213 } 214 else 215 shift.x = shift.y = 0; 216 217 for ( ; 218 i != j; 219 i = i < last ? i + 1 : first ) 220 { 221 points[i].x += x_shift + shift.x; 222 points[i].y += y_shift + shift.y; 223 } 224 } 225 else 226 i = j; 227 228 in = out; 229 l_in = l_out; 230 } 231 232 first = last + 1; 233 } 234 } 235 236 static void 237 hb_outline_recording_pen_move_to (hb_draw_funcs_t *dfuncs HB_UNUSED, 238 void *data, 239 hb_draw_state_t *st, 240 float to_x, float to_y, 241 void *user_data HB_UNUSED) 242 { 243 hb_outline_t *c = (hb_outline_t *) data; 244 245 c->points.push (hb_outline_point_t {to_x, to_y, hb_outline_point_t::type_t::MOVE_TO}); 246 } 247 248 static void 249 hb_outline_recording_pen_line_to (hb_draw_funcs_t *dfuncs HB_UNUSED, 250 void *data, 251 hb_draw_state_t *st, 252 float to_x, float to_y, 253 void *user_data HB_UNUSED) 254 { 255 hb_outline_t *c = (hb_outline_t *) data; 256 257 c->points.push (hb_outline_point_t {to_x, to_y, hb_outline_point_t::type_t::LINE_TO}); 258 } 259 260 static void 261 hb_outline_recording_pen_quadratic_to (hb_draw_funcs_t *dfuncs HB_UNUSED, 262 void *data, 263 hb_draw_state_t *st, 264 float control_x, float control_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 {control_x, control_y, hb_outline_point_t::type_t::QUADRATIC_TO}); 271 c->points.push (hb_outline_point_t {to_x, to_y, hb_outline_point_t::type_t::QUADRATIC_TO}); 272 } 273 274 static void 275 hb_outline_recording_pen_cubic_to (hb_draw_funcs_t *dfuncs HB_UNUSED, 276 void *data, 277 hb_draw_state_t *st, 278 float control1_x, float control1_y, 279 float control2_x, float control2_y, 280 float to_x, float to_y, 281 void *user_data HB_UNUSED) 282 { 283 hb_outline_t *c = (hb_outline_t *) data; 284 285 c->points.push (hb_outline_point_t {control1_x, control1_y, hb_outline_point_t::type_t::CUBIC_TO}); 286 c->points.push (hb_outline_point_t {control2_x, control2_y, hb_outline_point_t::type_t::CUBIC_TO}); 287 c->points.push (hb_outline_point_t {to_x, to_y, hb_outline_point_t::type_t::CUBIC_TO}); 288 } 289 290 static void 291 hb_outline_recording_pen_close_path (hb_draw_funcs_t *dfuncs HB_UNUSED, 292 void *data, 293 hb_draw_state_t *st, 294 void *user_data HB_UNUSED) 295 { 296 hb_outline_t *c = (hb_outline_t *) data; 297 298 c->contours.push (c->points.length); 299 } 300 301 static inline void free_static_outline_recording_pen_funcs (); 302 303 static struct hb_outline_recording_pen_funcs_lazy_loader_t : hb_draw_funcs_lazy_loader_t<hb_outline_recording_pen_funcs_lazy_loader_t> 304 { 305 static hb_draw_funcs_t *create () 306 { 307 hb_draw_funcs_t *funcs = hb_draw_funcs_create (); 308 309 hb_draw_funcs_set_move_to_func (funcs, hb_outline_recording_pen_move_to, nullptr, nullptr); 310 hb_draw_funcs_set_line_to_func (funcs, hb_outline_recording_pen_line_to, nullptr, nullptr); 311 hb_draw_funcs_set_quadratic_to_func (funcs, hb_outline_recording_pen_quadratic_to, nullptr, nullptr); 312 hb_draw_funcs_set_cubic_to_func (funcs, hb_outline_recording_pen_cubic_to, nullptr, nullptr); 313 hb_draw_funcs_set_close_path_func (funcs, hb_outline_recording_pen_close_path, nullptr, nullptr); 314 315 hb_draw_funcs_make_immutable (funcs); 316 317 hb_atexit (free_static_outline_recording_pen_funcs); 318 319 return funcs; 320 } 321 } static_outline_recording_pen_funcs; 322 323 static inline 324 void free_static_outline_recording_pen_funcs () 325 { 326 static_outline_recording_pen_funcs.free_instance (); 327 } 328 329 hb_draw_funcs_t * 330 hb_outline_recording_pen_get_funcs () 331 { 332 return static_outline_recording_pen_funcs.get_unconst (); 333 } 334 335 336 #endif