1 /* tahints.h */ 2 3 /* 4 * Copyright (C) 2011-2021 by Werner Lemberg. 5 * 6 * This file is part of the ttfautohint library, and may only be used, 7 * modified, and distributed under the terms given in `COPYING'. By 8 * continuing to use, modify, or distribute this file you indicate that you 9 * have read `COPYING' and understand and accept it fully. 10 * 11 * The file `COPYING' mentioned in the previous paragraph is distributed 12 * with the ttfautohint library. 13 */ 14 15 16 /* originally file `afhints.h' (2011-Mar-28) from FreeType */ 17 18 /* heavily modified 2011 by Werner Lemberg <wl@gnu.org> */ 19 20 #ifndef TAHINTS_H_ 21 #define TAHINTS_H_ 22 23 #include "tatypes.h" 24 25 #ifdef __cplusplus 26 extern "C" { 27 #endif 28 29 #define xxTA_SORT_SEGMENTS 30 31 32 /* the definition of outline glyph hints; these are shared */ 33 /* by all writing system analysis routines (until now) */ 34 35 typedef enum TA_Dimension_ 36 { 37 TA_DIMENSION_HORZ = 0, /* x coordinates, i.e. vert. segments & edges */ 38 TA_DIMENSION_VERT = 1, /* y coordinates, i.e. horz. segments & edges */ 39 TA_DIMENSION_MAX /* do not remove */ 40 } TA_Dimension; 41 42 43 /* hint directions -- the values are computed so that two vectors */ 44 /* are in opposite directions iff `dir1 + dir2 == 0' */ 45 46 typedef enum TA_Direction_ 47 { 48 TA_DIR_NONE = 4, 49 TA_DIR_RIGHT = 1, 50 TA_DIR_LEFT= -1, 51 TA_DIR_UP = 2, 52 TA_DIR_DOWN = -2 53 } TA_Direction; 54 55 56 /* 57 * The following explanations are mostly taken from the article 58 * 59 * Real-Time Grid Fitting of Typographic Outlines 60 * 61 * by David Turner and Werner Lemberg 62 * 63 * https://www.tug.org/TUGboat/Articles/tb24-3/lemberg.pdf 64 * 65 * with appropriate updates. 66 * 67 * 68 * Segments 69 * 70 * `ta_{cjk,latin,...}_hints_compute_segments' are the functions to 71 * find segments in an outline. 72 * 73 * A segment is a series of at least two consecutive points that are 74 * approximately aligned along a coordinate axis. The analysis to do 75 * so is specific to a writing system. 76 * 77 * 78 * Edges 79 * 80 * `ta_{cjk,latin,...}_hints_compute_edges' are the functions to find 81 * edges. 82 * 83 * As soon as segments are defined, the auto-hinter groups them into 84 * edges. An edge corresponds to a single position on the main 85 * dimension that collects one or more segments (allowing for a small 86 * threshold). 87 * 88 * As an example, the `latin' writing system first tries to grid-fit 89 * edges, then to align segments on the edges unless it detects that 90 * they form a serif. 91 * 92 * 93 * A H 94 * | | 95 * | | 96 * | | 97 * | | 98 * C | | F 99 * +------<-----+ +-----<------+ 100 * | B G | 101 * | | 102 * | | 103 * +--------------->------------------+ 104 * D E 105 * 106 * 107 * Stems 108 * 109 * Stems are detected by `ta_{cjk,latin,...}_hint_edges'. 110 * 111 * Segments need to be `linked' to other ones in order to detect stems. 112 * A stem is made of two segments that face each other in opposite 113 * directions and that are sufficiently close to each other. Using 114 * vocabulary from the TrueType specification, stem segments form a 115 * `black distance'. 116 * 117 * In the above ASCII drawing, the horizontal segments are BC, DE, and 118 * FG; the vertical segments are AB, CD, EF, and GH. 119 * 120 * Each segment has at most one `best' candidate to form a black 121 * distance, or no candidate at all. Notice that two distinct segments 122 * can have the same candidate, which frequently means a serif. 123 * 124 * A stem is recognized by the following condition: 125 * 126 * best segment_1 = segment_2 && best segment_2 = segment_1 127 * 128 * The best candidate is stored in field `link' in structure 129 * `TA_Segment'. 130 * 131 * In the above ASCII drawing, the best candidate for both AB and CD is 132 * GH, while the best candidate for GH is AB. Similarly, the best 133 * candidate for EF and GH is AB, while the best candidate for AB is 134 * GH. 135 * 136 * The detection and handling of stems is dependent on the writing 137 * system. 138 * 139 * 140 * Serifs 141 * 142 * Serifs are detected by `ta_{cjk,latin,...}_hint_edges'. 143 * 144 * In comparison to a stem, a serif (as handled by the auto-hinter 145 * module that takes care of the `latin' writing system) has 146 * 147 * best segment_1 = segment_2 && best segment_2 != segment_1 148 * 149 * where segment_1 corresponds to the serif segment (CD and EF in the 150 * above ASCII drawing). 151 * 152 * The best candidate is stored in field `serif' in structure 153 * `TA_Segment' (and `link' is set to NULL). 154 * 155 * 156 * Touched points 157 * 158 * A point is called `touched' if it has been processed somehow by the 159 * auto-hinter. It basically means that it shouldn't be moved again 160 * (or moved only under certain constraints to preserve the already 161 * applied processing). 162 * 163 * 164 * Flat and round segments 165 * 166 * Segments are `round' or `flat', depending on the series of points 167 * that define them. A segment is round if the next and previous point 168 * of an extremum (which can be either a single point or sequence of 169 * points) are both conic or cubic control points. Otherwise, a 170 * segment with an extremum is flat. 171 * 172 * 173 * Strong Points 174 * 175 * Experience has shown that points not part of an edge need to be 176 * interpolated linearly between their two closest edges, even if these 177 * are not part of the contour of those particular points. Typical 178 * candidates for this are 179 * 180 * - angle points (i.e., points where the `in' and `out' direction 181 * differ greatly) 182 * 183 * - inflection points (i.e., where the `in' and `out' angles are the 184 * same, but the curvature changes sign) [currently, such points 185 * aren't handled specially in the auto-hinter] 186 * 187 * `ta_glyph_hints_align_strong_points' is the function that takes 188 * care of such situations; it is equivalent to the TrueType `IP' 189 * hinting instruction. 190 * 191 * 192 * Weak Points 193 * 194 * Other points in the outline must be interpolated using the 195 * coordinates of their previous and next unfitted contour neighbours. 196 * These are called `weak points' and are touched by the function 197 * `ta_glyph_hints_align_weak_points', equivalent to the TrueType `IUP' 198 * hinting instruction. Typical candidates are control points and 199 * points on the contour without a major direction. 200 * 201 * The major effect is to reduce possible distortion caused by 202 * alignment of edges and strong points, thus weak points are processed 203 * after strong points. 204 */ 205 206 207 /* point hint flags */ 208 #define TA_FLAG_NONE 0 209 210 /* point type flags */ 211 #define TA_FLAG_CONIC (1U << 0) 212 #define TA_FLAG_CUBIC (1U << 1) 213 #define TA_FLAG_CONTROL (TA_FLAG_CONIC | TA_FLAG_CUBIC) 214 215 /* point touch flags */ 216 #define TA_FLAG_TOUCH_X (1U << 2) 217 #define TA_FLAG_TOUCH_Y (1U << 3) 218 219 /* candidates for weak interpolation have this flag set */ 220 #define TA_FLAG_WEAK_INTERPOLATION (1U << 4) 221 222 223 /* edge hint flags */ 224 #define TA_EDGE_NORMAL 0 225 #define TA_EDGE_ROUND (1U << 0) 226 #define TA_EDGE_SERIF (1U << 1) 227 #define TA_EDGE_DONE (1U << 2) 228 #define TA_EDGE_NEUTRAL (1U << 3) 229 230 231 typedef struct TA_PointRec_* TA_Point; 232 typedef struct TA_SegmentRec_* TA_Segment; 233 typedef struct TA_EdgeRec_* TA_Edge; 234 235 236 typedef struct TA_PointRec_ 237 { 238 FT_UShort flags; /* point flags used by hinter */ 239 FT_Char in_dir; /* direction of inwards vector */ 240 FT_Char out_dir; /* direction of outwards vector */ 241 242 FT_Pos ox, oy; /* original, scaled position */ 243 FT_Short fx, fy; /* original, unscaled position (in font units) */ 244 FT_Pos x, y; /* current position */ 245 FT_Pos u, v; /* current (x,y) or (y,x) depending on context */ 246 247 FT_Short left_offset; /* left offset in one-point segments */ 248 FT_Short right_offset; /* right offset in one-point segments */ 249 250 TA_Point next; /* next point in contour */ 251 TA_Point prev; /* previous point in contour */ 252 253 #ifdef TA_DEBUG 254 /* track `before' and `after' edges for strong points */ 255 TA_Edge before[2]; 256 TA_Edge after[2]; 257 #endif 258 } TA_PointRec; 259 260 261 typedef struct TA_SegmentRec_ 262 { 263 FT_Byte flags; /* edge/segment flags for this segment */ 264 FT_Char dir; /* segment direction */ 265 FT_Short pos; /* position of segment */ 266 FT_Short delta; /* deviation from segment position */ 267 FT_Short min_coord; /* minimum coordinate of segment */ 268 FT_Short max_coord; /* maximum coordinate of segment */ 269 FT_Short height; /* the hinted segment height */ 270 271 TA_Edge edge; /* the segment's parent edge */ 272 TA_Segment edge_next; /* link to next segment in parent edge */ 273 274 TA_Segment link; /* (stem) link segment */ 275 TA_Segment serif; /* primary segment for serifs */ 276 FT_Pos score; /* used during stem matching */ 277 FT_Pos len; /* used during stem matching */ 278 279 TA_Point first; /* first point in edge segment */ 280 TA_Point last; /* last point in edge segment */ 281 } TA_SegmentRec; 282 283 284 typedef struct TA_EdgeRec_ 285 { 286 FT_Short fpos; /* original, unscaled position (in font units) */ 287 FT_Pos opos; /* original, scaled position */ 288 FT_Pos pos; /* current position */ 289 290 FT_Byte flags; /* edge flags */ 291 FT_Char dir; /* edge direction */ 292 FT_Fixed scale; /* used to speed up interpolation between edges */ 293 294 TA_Width blue_edge; /* non-NULL if this is a blue edge */ 295 FT_UInt best_blue_idx; /* for the hinting recorder callback */ 296 FT_Bool best_blue_is_shoot; /* for the hinting recorder callback */ 297 298 TA_Edge link; /* link edge */ 299 TA_Edge serif; /* primary edge for serifs */ 300 FT_Int score; /* used during stem matching */ 301 302 TA_Segment first; /* first segment in edge */ 303 TA_Segment last; /* last segment in edge */ 304 } TA_EdgeRec; 305 306 307 #define TA_SEGMENTS_EMBEDDED 18 /* number of embedded segments */ 308 #define TA_EDGES_EMBEDDED 12 /* number of embedded edges */ 309 310 typedef struct TA_AxisHintsRec_ 311 { 312 FT_Int num_segments; /* number of used segments */ 313 FT_Int max_segments; /* number of allocated segments */ 314 TA_Segment segments; /* segments array */ 315 #ifdef TA_SORT_SEGMENTS 316 FT_Int mid_segments; 317 #endif 318 319 FT_Int num_edges; /* number of used edges */ 320 FT_Int max_edges; /* number of allocated edges */ 321 TA_Edge edges; /* edges array */ 322 323 TA_Direction major_dir; /* either vertical or horizontal */ 324 325 /* two arrays to avoid allocation penalty */ 326 struct 327 { 328 TA_SegmentRec segments[TA_SEGMENTS_EMBEDDED]; 329 TA_EdgeRec edges[TA_EDGES_EMBEDDED]; 330 } embedded; 331 } TA_AxisHintsRec, *TA_AxisHints; 332 333 334 typedef enum TA_Action_ 335 { 336 /* point actions */ 337 338 ta_ip_before, 339 ta_ip_after, 340 ta_ip_on, 341 ta_ip_between, 342 343 /* edge actions */ 344 345 ta_blue, 346 ta_blue_anchor, 347 348 ta_anchor, 349 ta_anchor_serif, 350 ta_anchor_round, 351 ta_anchor_round_serif, 352 353 ta_adjust, 354 ta_adjust_serif, 355 ta_adjust_round, 356 ta_adjust_round_serif, 357 ta_adjust_bound, 358 ta_adjust_bound_serif, 359 ta_adjust_bound_round, 360 ta_adjust_bound_round_serif, 361 ta_adjust_down_bound, 362 ta_adjust_down_bound_serif, 363 ta_adjust_down_bound_round, 364 ta_adjust_down_bound_round_serif, 365 366 ta_link, 367 ta_link_serif, 368 ta_link_round, 369 ta_link_round_serif, 370 371 ta_stem, 372 ta_stem_serif, 373 ta_stem_round, 374 ta_stem_round_serif, 375 ta_stem_bound, 376 ta_stem_bound_serif, 377 ta_stem_bound_round, 378 ta_stem_bound_round_serif, 379 ta_stem_down_bound, 380 ta_stem_down_bound_serif, 381 ta_stem_down_bound_round, 382 ta_stem_down_bound_round_serif, 383 384 ta_serif, 385 ta_serif_lower_bound, 386 ta_serif_upper_bound, 387 ta_serif_upper_lower_bound, 388 ta_serif_down_lower_bound, 389 ta_serif_down_upper_bound, 390 ta_serif_down_upper_lower_bound, 391 392 ta_serif_anchor, 393 ta_serif_anchor_lower_bound, 394 ta_serif_anchor_upper_bound, 395 ta_serif_anchor_upper_lower_bound, 396 ta_serif_anchor_down_lower_bound, 397 ta_serif_anchor_down_upper_bound, 398 ta_serif_anchor_down_upper_lower_bound, 399 400 ta_serif_link1, 401 ta_serif_link1_lower_bound, 402 ta_serif_link1_upper_bound, 403 ta_serif_link1_upper_lower_bound, 404 ta_serif_link1_down_lower_bound, 405 ta_serif_link1_down_upper_bound, 406 ta_serif_link1_down_upper_lower_bound, 407 408 ta_serif_link2, 409 ta_serif_link2_lower_bound, 410 ta_serif_link2_upper_bound, 411 ta_serif_link2_upper_lower_bound, 412 ta_serif_link2_down_lower_bound, 413 ta_serif_link2_down_upper_bound, 414 ta_serif_link2_down_upper_lower_bound, 415 416 ta_bound 417 } TA_Action; 418 419 420 typedef void 421 (*TA_Hints_Recorder)(TA_Action action, 422 TA_GlyphHints hints, 423 TA_Dimension dim, 424 void* arg1, /* TA_Point or TA_Edge */ 425 TA_Edge arg2, 426 TA_Edge arg3, 427 TA_Edge lower_bound, 428 TA_Edge upper_bound); 429 430 431 #define TA_POINTS_EMBEDDED 96 /* number of embedded points */ 432 #define TA_CONTOURS_EMBEDDED 8 /* number of embedded contours */ 433 434 typedef struct TA_GlyphHintsRec_ 435 { 436 FT_Fixed x_scale; 437 FT_Pos x_delta; 438 439 FT_Fixed y_scale; 440 FT_Pos y_delta; 441 442 FT_Int max_points; /* number of allocated points */ 443 FT_Int num_points; /* number of used points */ 444 TA_Point points; /* points array */ 445 446 FT_Int max_contours; /* number of allocated contours */ 447 FT_Int num_contours; /* number of used contours */ 448 TA_Point* contours; /* contours array */ 449 450 TA_AxisHintsRec axis[TA_DIMENSION_MAX]; 451 452 FT_UInt32 scaler_flags; /* copy of scaler flags */ 453 FT_UInt32 other_flags; /* free for style-specific implementations */ 454 TA_StyleMetrics metrics; 455 456 FT_Pos xmin_delta; /* used for warping */ 457 FT_Pos xmax_delta; 458 459 TA_Hints_Recorder recorder; 460 void* user; 461 462 /* two arrays to avoid allocation penalty; */ 463 /* the `embedded' structure must be the last element! */ 464 struct 465 { 466 TA_Point contours[TA_CONTOURS_EMBEDDED]; 467 TA_PointRec points[TA_POINTS_EMBEDDED]; 468 } embedded; 469 } TA_GlyphHintsRec; 470 471 472 #define TA_HINTS_TEST_SCALER(h, f) \ 473 ((h)->scaler_flags & (f)) 474 #define TA_HINTS_TEST_OTHER(h, f) \ 475 ((h)->other_flags & (f)) 476 477 478 #ifdef TA_DEBUG 479 480 #define TA_HINTS_DO_HORIZONTAL(h) \ 481 (!_ta_debug_disable_horz_hints \ 482 && !TA_HINTS_TEST_SCALER(h, TA_SCALER_FLAG_NO_HORIZONTAL)) 483 484 #define TA_HINTS_DO_VERTICAL(h) \ 485 (!_ta_debug_disable_vert_hints \ 486 && !TA_HINTS_TEST_SCALER(h, TA_SCALER_FLAG_NO_VERTICAL)) 487 488 #define TA_HINTS_DO_BLUES(h) \ 489 (!_ta_debug_disable_blue_hints) 490 491 #else /* !TA_DEBUG */ 492 493 #define TA_HINTS_DO_HORIZONTAL(h) \ 494 !TA_HINTS_TEST_SCALER(h, TA_SCALER_FLAG_NO_HORIZONTAL) 495 496 #define TA_HINTS_DO_VERTICAL(h) \ 497 !TA_HINTS_TEST_SCALER(h, TA_SCALER_FLAG_NO_VERTICAL) 498 499 #define TA_HINTS_DO_BLUES(h) \ 500 1 501 502 #endif /* !TA_DEBUG */ 503 504 505 #define TA_HINTS_DO_ADVANCE(h) \ 506 !TA_HINTS_TEST_SCALER(h, TA_SCALER_FLAG_NO_ADVANCE) 507 508 #define TA_HINTS_DO_WARP(h) \ 509 !TA_HINTS_TEST_SCALER(h, TA_SCALER_FLAG_NO_WARPER) 510 511 512 TA_Direction 513 ta_direction_compute(FT_Pos dx, 514 FT_Pos dy); 515 516 517 FT_Error 518 ta_axis_hints_new_segment(TA_AxisHints axis, 519 TA_Segment* asegment); 520 521 FT_Error 522 ta_axis_hints_new_edge(TA_AxisHints axis, 523 FT_Int fpos, 524 TA_Direction dir, 525 FT_Bool top_to_bottom_hinting, 526 TA_Edge* edge); 527 528 #ifdef TA_DEBUG 529 void 530 ta_glyph_hints_dump_points(TA_GlyphHints hints); 531 532 void 533 ta_glyph_hints_dump_segments(TA_GlyphHints hints); 534 535 void 536 ta_glyph_hints_dump_edges(TA_GlyphHints hints); 537 #endif 538 539 void 540 ta_glyph_hints_init(TA_GlyphHints hints); 541 542 void 543 ta_glyph_hints_rescale(TA_GlyphHints hints, 544 TA_StyleMetrics metrics); 545 546 FT_Error 547 ta_glyph_hints_reload(TA_GlyphHints hints, 548 FT_Outline* outline); 549 550 void 551 ta_glyph_hints_save(TA_GlyphHints hints, 552 FT_Outline* outline); 553 554 void 555 ta_glyph_hints_align_edge_points(TA_GlyphHints hints, 556 TA_Dimension dim); 557 558 void 559 ta_glyph_hints_align_strong_points(TA_GlyphHints hints, 560 TA_Dimension dim); 561 562 void 563 ta_glyph_hints_align_weak_points(TA_GlyphHints hints, 564 TA_Dimension dim); 565 566 #ifdef TA_CONFIG_OPTION_USE_WARPER 567 void 568 ta_glyph_hints_scale_dim(TA_GlyphHints hints, 569 TA_Dimension dim, 570 FT_Fixed scale, 571 FT_Pos delta); 572 #endif 573 574 void 575 ta_glyph_hints_done(TA_GlyphHints hints); 576 577 578 #define TA_SEGMENT_LEN(seg) \ 579 ((seg)->max_coord - (seg)->min_coord) 580 581 #define TA_SEGMENT_DIST(seg1, seg2) \ 582 (((seg1)->pos > (seg2)->pos) ? (seg1)->pos - (seg2)->pos \ 583 : (seg2)->pos - (seg1)->pos) 584 585 #ifdef __cplusplus 586 } /* extern "C" */ 587 #endif 588 589 #endif /* TAHINTS_H_ */ 590 591 592 /* end of tahints.h */ 593