4 * Copyright (C) 2011-2022 by Werner Lemberg.
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.
11 * The file `COPYING' mentioned in the previous paragraph is distributed
12 * with the ttfautohint library.
16 /* originally file `afhints.c' (2011-Mar-28) from FreeType */
18 /* heavily modified 2011 by Werner Lemberg <wl@gnu.org> */
27 /* get new segment for given axis */
30 ta_axis_hints_new_segment(TA_AxisHints axis
,
33 FT_Error error
= FT_Err_Ok
;
34 TA_Segment segment
= NULL
;
37 if (axis
->num_segments
< TA_SEGMENTS_EMBEDDED
)
41 axis
->segments
= axis
->embedded
.segments
;
42 axis
->max_segments
= TA_SEGMENTS_EMBEDDED
;
45 else if (axis
->num_segments
>= axis
->max_segments
)
47 TA_Segment segments_new
;
49 FT_Int old_max
= axis
->max_segments
;
50 FT_Int new_max
= old_max
;
51 FT_Int big_max
= (FT_Int
)(FT_INT_MAX
/ sizeof (*segment
));
54 if (old_max
>= big_max
)
56 error
= FT_Err_Out_Of_Memory
;
60 new_max
+= (new_max
>> 2) + 4;
65 if (axis
->segments
== axis
->embedded
.segments
)
67 axis
->segments
= (TA_Segment
)malloc(
68 (size_t)new_max
* sizeof (TA_SegmentRec
));
70 return FT_Err_Out_Of_Memory
;
72 memcpy(axis
->segments
, axis
->embedded
.segments
,
73 sizeof (axis
->embedded
.segments
));
77 segments_new
= (TA_Segment
)realloc(
79 (size_t)new_max
* sizeof (TA_SegmentRec
));
81 return FT_Err_Out_Of_Memory
;
82 axis
->segments
= segments_new
;
85 axis
->max_segments
= new_max
;
88 segment
= axis
->segments
+ axis
->num_segments
++;
96 /* get new edge for given axis, direction, and position, */
97 /* without initializing the edge itself */
100 ta_axis_hints_new_edge(TA_AxisHints axis
,
103 FT_Bool top_to_bottom_hinting
,
106 FT_Error error
= FT_Err_Ok
;
111 if (axis
->num_edges
< TA_EDGES_EMBEDDED
)
115 axis
->edges
= axis
->embedded
.edges
;
116 axis
->max_edges
= TA_EDGES_EMBEDDED
;
119 else if (axis
->num_edges
>= axis
->max_edges
)
123 FT_Int old_max
= axis
->max_edges
;
124 FT_Int new_max
= old_max
;
125 FT_Int big_max
= (FT_Int
)(FT_INT_MAX
/ sizeof (*edge
));
128 if (old_max
>= big_max
)
130 error
= FT_Err_Out_Of_Memory
;
134 new_max
+= (new_max
>> 2) + 4;
135 if (new_max
< old_max
136 || new_max
> big_max
)
139 if (axis
->edges
== axis
->embedded
.edges
)
141 axis
->edges
= (TA_Edge
)malloc((size_t)new_max
* sizeof (TA_EdgeRec
));
143 return FT_Err_Out_Of_Memory
;
145 memcpy(axis
->edges
, axis
->embedded
.edges
,
146 sizeof (axis
->embedded
.edges
));
150 edges_new
= (TA_Edge
)realloc(axis
->edges
,
151 (size_t)new_max
* sizeof (TA_EdgeRec
));
153 return FT_Err_Out_Of_Memory
;
154 axis
->edges
= edges_new
;
157 axis
->max_edges
= new_max
;
161 edge
= edges
+ axis
->num_edges
;
165 if (top_to_bottom_hinting
? (edge
[-1].fpos
> fpos
)
166 : (edge
[-1].fpos
< fpos
))
169 /* we want the edge with same position and minor direction */
170 /* to appear before those in the major one in the list */
171 if (edge
[-1].fpos
== fpos
172 && dir
== axis
->major_dir
)
195 _ta_message(const char* format
,
201 va_start(ap
, format
);
202 vfprintf(stderr
, format
, ap
);
208 ta_dir_str(TA_Direction dir
)
235 #define TA_INDEX_NUM(ptr, base) \
236 (int)((ptr) ? ((ptr) - (base)) \
241 ta_print_idx(char* p
,
251 sprintf(p
, "%d", idx
);
258 ta_get_segment_index(TA_GlyphHints hints
,
262 TA_AxisHints axis
= &hints
->axis
[dimension
];
263 TA_Point point
= hints
->points
+ point_idx
;
264 TA_Segment segments
= axis
->segments
;
265 TA_Segment limit
= segments
+ axis
->num_segments
;
269 for (segment
= segments
; segment
< limit
; segment
++)
271 if (segment
->first
<= segment
->last
)
273 if (point
>= segment
->first
&& point
<= segment
->last
)
278 TA_Point p
= segment
->first
;
286 if (p
== segment
->last
)
295 if (segment
== limit
)
298 return (int)(segment
- segments
);
303 ta_get_edge_index(TA_GlyphHints hints
,
307 TA_AxisHints axis
= &hints
->axis
[dimension
];
308 TA_Edge edges
= axis
->edges
;
309 TA_Segment segment
= axis
->segments
+ segment_idx
;
312 return segment_idx
== -1 ? -1 : TA_INDEX_NUM(segment
->edge
, edges
);
317 ta_get_strong_edge_index(TA_GlyphHints hints
,
318 TA_Edge
* strong_edges
,
321 TA_AxisHints axis
= &hints
->axis
[dimension
];
322 TA_Edge edges
= axis
->edges
;
325 return TA_INDEX_NUM(strong_edges
[dimension
], edges
);
330 ta_glyph_hints_dump_points(TA_GlyphHints hints
)
332 TA_Point points
= hints
->points
;
333 TA_Point limit
= points
+ hints
->num_points
;
334 TA_Point
* contour
= hints
->contours
;
335 TA_Point
* climit
= contour
+ hints
->num_contours
;
339 TA_LOG(("Table of points:\n"));
341 if (hints
->num_points
)
343 TA_LOG((" index hedge hseg flags"
344 /* " XXXXX XXXXX XXXXX XXXX" */
345 " xorg yorg xscale yscale xfit yfit "
346 /* " XXXXX XXXXX XXXX.XX XXXX.XX XXXX.XX XXXX.XX" */
351 TA_LOG((" (none)\n"));
353 for (point
= points
; point
< limit
; point
++)
355 int point_idx
= TA_INDEX_NUM(point
, points
);
356 int segment_idx_1
= ta_get_segment_index(hints
, point_idx
, 1);
358 char buf1
[16], buf2
[16];
359 char buf5
[16], buf6
[16];
362 /* insert extra newline at the beginning of a contour */
363 if (contour
< climit
&& *contour
== point
)
369 /* we don't show vertical edges since they are never used */
370 TA_LOG((" %5d %5s %5s %4s"
371 " %5d %5d %7.2f %7.2f %7.2f %7.2f"
375 ta_get_edge_index(hints
, segment_idx_1
, 1)),
376 ta_print_idx(buf2
, segment_idx_1
),
377 (point
->flags
& TA_FLAG_WEAK_INTERPOLATION
) ? "weak" : " -- ",
386 ta_print_idx(buf5
, ta_get_strong_edge_index(hints
,
389 ta_print_idx(buf6
, ta_get_strong_edge_index(hints
,
398 ta_edge_flags_to_string(FT_Byte flags
)
400 static char temp
[32];
404 if (flags
& TA_EDGE_ROUND
)
406 memcpy(temp
+ pos
, "round", 5);
409 if (flags
& TA_EDGE_SERIF
)
413 memcpy(temp
+ pos
, "serif", 5);
425 /* dump the array of linked segments */
428 ta_glyph_hints_dump_segments(TA_GlyphHints hints
)
433 for (dimension
= TA_DEBUG_STARTDIM
;
434 dimension
>= TA_DEBUG_ENDDIM
;
437 TA_AxisHints axis
= &hints
->axis
[dimension
];
438 TA_Point points
= hints
->points
;
439 TA_Edge edges
= axis
->edges
;
440 TA_Segment segments
= axis
->segments
;
441 TA_Segment limit
= segments
+ axis
->num_segments
;
444 char buf1
[16], buf2
[16], buf3
[16];
447 TA_LOG(("Table of %s segments:\n",
448 dimension
== TA_DIMENSION_HORZ
? "vertical"
450 if (axis
->num_segments
)
452 TA_LOG((" index pos delta dir from to "
453 /* " XXXXX XXXXX XXXXX XXXXX XXXX XXXX" */
455 /* " XXXX XXXXX XXXX" */
456 " height extra flags\n"));
457 /* " XXXXXX XXXXX XXXXXXXXXXX" */
460 TA_LOG((" (none)\n"));
462 for (seg
= segments
; seg
< limit
; seg
++)
463 TA_LOG((" %5d %5d %5d %5s %4d %4d"
466 TA_INDEX_NUM(seg
, segments
),
469 ta_dir_str((TA_Direction
)seg
->dir
),
470 TA_INDEX_NUM(seg
->first
, points
),
471 TA_INDEX_NUM(seg
->last
, points
),
473 ta_print_idx(buf1
, TA_INDEX_NUM(seg
->link
, segments
)),
474 ta_print_idx(buf2
, TA_INDEX_NUM(seg
->serif
, segments
)),
475 ta_print_idx(buf3
, TA_INDEX_NUM(seg
->edge
, edges
)),
478 seg
->height
- (seg
->max_coord
- seg
->min_coord
),
479 ta_edge_flags_to_string(seg
->flags
)));
485 /* dump the array of linked edges */
488 ta_glyph_hints_dump_edges(TA_GlyphHints hints
)
493 for (dimension
= TA_DEBUG_STARTDIM
;
494 dimension
>= TA_DEBUG_ENDDIM
;
497 TA_AxisHints axis
= &hints
->axis
[dimension
];
498 TA_Edge edges
= axis
->edges
;
499 TA_Edge limit
= edges
+ axis
->num_edges
;
502 char buf1
[16], buf2
[16];
505 /* note that TA_DIMENSION_HORZ corresponds to _vertical_ edges */
506 /* since they have a constant X coordinate */
507 if (dimension
== TA_DIMENSION_HORZ
)
508 TA_LOG(("Table of %s edges (1px=%.2fu, 10u=%.2fpx):\n",
510 65536.0 * 64.0 / hints
->x_scale
,
511 10.0 * hints
->x_scale
/ 65536.0 / 64.0));
513 TA_LOG(("Table of %s edges (1px=%.2fu, 10u=%.2fpx):\n",
515 65536.0 * 64.0 / hints
->y_scale
,
516 10.0 * hints
->y_scale
/ 65536.0 / 64.0));
520 TA_LOG((" index pos dir link serif"
521 /* " XXXXX XXXX.XX XXXXX XXXX XXXXX" */
522 " blue opos pos flags\n"));
523 /* " X XXXX.XX XXXX.XX XXXXXXXXXXX" */
526 TA_LOG((" (none)\n"));
528 for (edge
= edges
; edge
< limit
; edge
++)
529 TA_LOG((" %5d %7.2f %5s %4s %5s"
530 " %c %7.2f %7.2f %11s\n",
531 TA_INDEX_NUM(edge
, edges
),
532 (int)edge
->opos
/ 64.0,
533 ta_dir_str((TA_Direction
)edge
->dir
),
534 ta_print_idx(buf1
, TA_INDEX_NUM(edge
->link
, edges
)),
535 ta_print_idx(buf2
, TA_INDEX_NUM(edge
->serif
, edges
)),
537 edge
->blue_edge
? 'y' : 'n',
540 ta_edge_flags_to_string(edge
->flags
)));
545 #endif /* TA_DEBUG */
548 /* compute the direction value of a given vector */
551 ta_direction_compute(FT_Pos dx
,
554 FT_Pos ll
, ss
; /* long and short arm lengths */
555 TA_Direction dir
; /* candidate direction */
589 /* return no direction if arm lengths do not differ enough */
590 /* (value 14 is heuristic, corresponding to approx. 4.1 degrees); */
591 /* the long arm is never negative */
592 if (ll
<= 14 * TA_ABS(ss
))
600 ta_glyph_hints_init(TA_GlyphHints hints
)
602 /* no need to initialize the embedded items */
603 memset(hints
, 0, sizeof (*hints
) - sizeof (hints
->embedded
));
608 ta_glyph_hints_done(TA_GlyphHints hints
)
616 /* we don't need to free the segment and edge buffers */
617 /* since they are really within the hints->points array */
618 for (dim
= 0; dim
< TA_DIMENSION_MAX
; dim
++)
620 TA_AxisHints axis
= &hints
->axis
[dim
];
623 axis
->num_segments
= 0;
624 axis
->max_segments
= 0;
625 if (axis
->segments
!= axis
->embedded
.segments
)
627 free(axis
->segments
);
628 axis
->segments
= NULL
;
633 if (axis
->edges
!= axis
->embedded
.edges
)
640 if (hints
->contours
!= hints
->embedded
.contours
)
642 free(hints
->contours
);
643 hints
->contours
= NULL
;
645 hints
->max_contours
= 0;
646 hints
->num_contours
= 0;
648 if (hints
->points
!= hints
->embedded
.points
)
651 hints
->points
= NULL
;
653 hints
->max_points
= 0;
654 hints
->num_points
= 0;
661 ta_glyph_hints_rescale(TA_GlyphHints hints
,
662 TA_StyleMetrics metrics
)
664 hints
->metrics
= metrics
;
665 hints
->scaler_flags
= metrics
->scaler
.flags
;
669 /* from FreeType's ftcalc.c */
672 ta_corner_is_flat(FT_Pos in_x
,
680 FT_Pos d_in
, d_out
, d_corner
;
705 return (d_in
+ d_out
- d_corner
) < (d_corner
>> 4);
709 /* recompute all TA_Point in TA_GlyphHints */
710 /* from the definitions in a source outline */
713 ta_glyph_hints_reload(TA_GlyphHints hints
,
716 FT_Error error
= FT_Err_Ok
;
718 FT_UInt old_max
, new_max
;
720 FT_Fixed x_scale
= hints
->x_scale
;
721 FT_Fixed y_scale
= hints
->y_scale
;
722 FT_Pos x_delta
= hints
->x_delta
;
723 FT_Pos y_delta
= hints
->y_delta
;
726 hints
->num_points
= 0;
727 hints
->num_contours
= 0;
729 hints
->axis
[0].num_segments
= 0;
730 hints
->axis
[0].num_edges
= 0;
731 hints
->axis
[1].num_segments
= 0;
732 hints
->axis
[1].num_edges
= 0;
734 /* first of all, reallocate the contours array if necessary */
735 new_max
= (FT_UInt
)outline
->n_contours
;
736 old_max
= (FT_UInt
)hints
->max_contours
;
738 if (new_max
<= TA_CONTOURS_EMBEDDED
)
740 if (!hints
->contours
)
742 hints
->contours
= hints
->embedded
.contours
;
743 hints
->max_contours
= TA_CONTOURS_EMBEDDED
;
746 else if (new_max
> old_max
)
748 TA_Point
* contours_new
;
751 if (hints
->contours
== hints
->embedded
.contours
)
752 hints
->contours
= NULL
;
754 new_max
= (new_max
+ 3) & ~3U; /* round up to a multiple of 4 */
756 contours_new
= (TA_Point
*)realloc(hints
->contours
,
757 new_max
* sizeof (TA_Point
));
759 return FT_Err_Out_Of_Memory
;
761 hints
->contours
= contours_new
;
762 hints
->max_contours
= (FT_Int
)new_max
;
765 /* reallocate the points arrays if necessary -- we reserve */
766 /* two additional point positions, used to hint metrics appropriately */
767 new_max
= (FT_UInt
)(outline
->n_points
+ 2);
768 old_max
= (FT_UInt
)hints
->max_points
;
770 if (new_max
<= TA_POINTS_EMBEDDED
)
774 hints
->points
= hints
->embedded
.points
;
775 hints
->max_points
= TA_POINTS_EMBEDDED
;
778 else if (new_max
> old_max
)
783 if (hints
->points
== hints
->embedded
.points
)
784 hints
->points
= NULL
;
786 new_max
= (new_max
+ 2 + 7) & ~7U; /* round up to a multiple of 8 */
788 points_new
= (TA_Point
)realloc(hints
->points
,
789 new_max
* sizeof (TA_PointRec
));
791 return FT_Err_Out_Of_Memory
;
793 hints
->points
= points_new
;
794 hints
->max_points
= (FT_Int
)new_max
;
797 hints
->num_points
= outline
->n_points
;
798 hints
->num_contours
= outline
->n_contours
;
800 /* we can't rely on the value of `FT_Outline.flags' to know the fill */
801 /* direction used for a glyph, given that some fonts are broken */
802 /* (e.g. the Arphic ones); we thus recompute it each time we need to */
804 hints
->axis
[TA_DIMENSION_HORZ
].major_dir
= TA_DIR_UP
;
805 hints
->axis
[TA_DIMENSION_VERT
].major_dir
= TA_DIR_LEFT
;
807 if (FT_Outline_Get_Orientation(outline
) == FT_ORIENTATION_POSTSCRIPT
)
809 hints
->axis
[TA_DIMENSION_HORZ
].major_dir
= TA_DIR_DOWN
;
810 hints
->axis
[TA_DIMENSION_VERT
].major_dir
= TA_DIR_RIGHT
;
813 hints
->x_scale
= x_scale
;
814 hints
->y_scale
= y_scale
;
815 hints
->x_delta
= x_delta
;
816 hints
->y_delta
= y_delta
;
818 hints
->xmin_delta
= 0;
819 hints
->xmax_delta
= 0;
821 points
= hints
->points
;
822 if (hints
->num_points
== 0)
827 TA_Point point_limit
= points
+ hints
->num_points
;
830 /* compute coordinates & Bezier flags, next and prev */
832 FT_Vector
* vec
= outline
->points
;
833 char* tag
= outline
->tags
;
835 TA_Point end
= points
+ outline
->contours
[0];
838 FT_Int contour_index
= 0;
841 for (point
= points
; point
< point_limit
; point
++, vec
++, tag
++)
843 point
->in_dir
= (FT_Char
)TA_DIR_NONE
;
844 point
->out_dir
= (FT_Char
)TA_DIR_NONE
;
846 point
->fx
= (FT_Short
)vec
->x
;
847 point
->fy
= (FT_Short
)vec
->y
;
848 point
->ox
= point
->x
= FT_MulFix(vec
->x
, x_scale
) + x_delta
;
849 point
->oy
= point
->y
= FT_MulFix(vec
->y
, y_scale
) + y_delta
;
851 switch (FT_CURVE_TAG(*tag
))
853 case FT_CURVE_TAG_CONIC
:
854 point
->flags
= TA_FLAG_CONIC
;
856 case FT_CURVE_TAG_CUBIC
:
857 point
->flags
= TA_FLAG_CUBIC
;
860 point
->flags
= TA_FLAG_NONE
;
869 if (++contour_index
< outline
->n_contours
)
871 end
= points
+ outline
->contours
[contour_index
];
877 point
->before
[0] = NULL
;
878 point
->before
[1] = NULL
;
879 point
->after
[0] = NULL
;
880 point
->after
[1] = NULL
;
885 /* set up the contours array */
887 TA_Point
* contour
= hints
->contours
;
888 TA_Point
* contour_limit
= contour
+ hints
->num_contours
;
890 short* end
= outline
->contours
;
894 for (; contour
< contour_limit
; contour
++, end
++)
896 contour
[0] = points
+ idx
;
897 idx
= (short)(end
[0] + 1);
903 * Compute directions of `in' and `out' vectors.
905 * Note that distances between points that are very near to each
906 * other are accumulated. In other words, the auto-hinter
907 * prepends the small vectors between near points to the first
908 * non-near vector. All intermediate points are tagged as
909 * weak; the directions are adjusted also to be equal to the
913 /* value 20 in `near_limit' is heuristic */
914 FT_UInt units_per_em
= hints
->metrics
->scaler
.face
->units_per_EM
;
915 FT_Int near_limit
= 20 * units_per_em
/ 2048;
916 FT_Int near_limit2
= 2 * near_limit
- 1;
919 TA_Point
* contour_limit
= hints
->contours
+ hints
->num_contours
;
922 for (contour
= hints
->contours
; contour
< contour_limit
; contour
++)
924 TA_Point first
= *contour
;
925 TA_Point next
, prev
, curr
;
930 /* since the first point of a contour could be part of a */
931 /* series of near points, go backwards to find the first */
932 /* non-near point and adjust `first' */
937 while (prev
!= first
)
939 out_x
= point
->fx
- prev
->fx
;
940 out_y
= point
->fy
- prev
->fy
;
943 * We use Taxicab metrics to measure the vector length.
945 * Note that the accumulated distances so far could have the
946 * opposite direction of the distance measured here. For this
947 * reason we use `near_limit2' for the comparison to get a
948 * non-near point even in the worst case.
950 if (TA_ABS(out_x
) + TA_ABS(out_y
) >= near_limit2
)
957 /* adjust first point */
960 /* now loop over all points of the contour to get */
961 /* `in' and `out' vector directions */
966 * We abuse the `u' and `v' fields to store index deltas to the
967 * next and previous non-near point, respectively.
969 * To avoid problems with not having non-near points, we point to
970 * `first' by default as the next non-near point.
972 curr
->u
= (FT_Pos
)(first
- curr
);
981 TA_Direction out_dir
;
987 out_x
+= next
->fx
- point
->fx
;
988 out_y
+= next
->fy
- point
->fy
;
990 if (TA_ABS(out_x
) + TA_ABS(out_y
) < near_limit
)
992 next
->flags
|= TA_FLAG_WEAK_INTERPOLATION
;
996 curr
->u
= (FT_Pos
)(next
- curr
);
999 out_dir
= ta_direction_compute(out_x
, out_y
);
1001 /* adjust directions for all points inbetween; */
1002 /* the loop also updates position of `curr' */
1003 curr
->out_dir
= (FT_Char
)out_dir
;
1004 for (curr
= curr
->next
; curr
!= next
; curr
= curr
->next
)
1006 curr
->in_dir
= (FT_Char
)out_dir
;
1007 curr
->out_dir
= (FT_Char
)out_dir
;
1009 next
->in_dir
= (FT_Char
)out_dir
;
1011 curr
->u
= (FT_Pos
)(first
- curr
);
1012 first
->v
= -curr
->u
;
1017 } while (next
!= first
);
1021 * The next step is to `simplify' an outline's topology so that we
1022 * can identify local extrema more reliably: A series of
1023 * non-horizontal or non-vertical vectors pointing into the same
1024 * quadrant are handled as a single, long vector. From a
1025 * topological point of the view, the intermediate points are of no
1026 * interest and thus tagged as weak.
1029 for (point
= points
; point
< point_limit
; point
++)
1031 if (point
->flags
& TA_FLAG_WEAK_INTERPOLATION
)
1034 if (point
->in_dir
== TA_DIR_NONE
1035 && point
->out_dir
== TA_DIR_NONE
)
1037 /* check whether both vectors point into the same quadrant */
1040 FT_Pos out_x
, out_y
;
1042 TA_Point next_u
= point
+ point
->u
;
1043 TA_Point prev_v
= point
+ point
->v
;
1046 in_x
= point
->fx
- prev_v
->fx
;
1047 in_y
= point
->fy
- prev_v
->fy
;
1049 out_x
= next_u
->fx
- point
->fx
;
1050 out_y
= next_u
->fy
- point
->fy
;
1052 if ((in_x
^ out_x
) >= 0 && (in_y
^ out_y
) >= 0)
1054 /* yes, so tag current point as weak */
1055 /* and update index deltas */
1057 point
->flags
|= TA_FLAG_WEAK_INTERPOLATION
;
1059 prev_v
->u
= (FT_Pos
)(next_u
- prev_v
);
1060 next_u
->v
= -prev_v
->u
;
1066 * Finally, check for remaining weak points. Everything else not
1067 * collected in edges so far is then implicitly classified as strong
1071 for (point
= points
; point
< point_limit
; point
++)
1073 if (point
->flags
& TA_FLAG_WEAK_INTERPOLATION
)
1076 if (point
->flags
& TA_FLAG_CONTROL
)
1078 /* control points are always weak */
1080 point
->flags
|= TA_FLAG_WEAK_INTERPOLATION
;
1082 else if (point
->out_dir
== point
->in_dir
)
1084 if (point
->out_dir
!= TA_DIR_NONE
)
1086 /* current point lies on a horizontal or */
1087 /* vertical segment (but doesn't start or end it) */
1092 TA_Point next_u
= point
+ point
->u
;
1093 TA_Point prev_v
= point
+ point
->v
;
1096 if (ta_corner_is_flat(point
->fx
- prev_v
->fx
,
1097 point
->fy
- prev_v
->fy
,
1098 next_u
->fx
- point
->fx
,
1099 next_u
->fy
- point
->fy
))
1101 /* either the `in' or the `out' vector is much more */
1102 /* dominant than the other one, so tag current point */
1103 /* as weak and update index deltas */
1105 prev_v
->u
= (FT_Pos
)(next_u
- prev_v
);
1106 next_u
->v
= -prev_v
->u
;
1112 else if (point
->in_dir
== -point
->out_dir
)
1114 /* current point forms a spike */
1121 /* change some directions at the user's request */
1122 /* to make ttfautohint insert one-point segments */
1123 /* or remove points from segments */
1132 /* `globals' is not set up while initializing metrics, */
1133 /* so exit early in this case */
1134 if (!hints
->metrics
->globals
)
1137 font
= hints
->metrics
->globals
->font
;
1139 /* start conditions are set with `TA_control_segment_dir_collect' */
1140 while (TA_control_segment_dir_get_next(font
, &idx
, &dir
,
1141 &left_offset
, &right_offset
))
1143 TA_Point point
= &points
[idx
];
1146 point
->out_dir
= dir
;
1147 if (dir
== TA_DIR_NONE
)
1148 point
->flags
|= TA_FLAG_WEAK_INTERPOLATION
;
1150 point
->flags
&= ~TA_FLAG_WEAK_INTERPOLATION
;
1151 point
->left_offset
= (FT_Short
)left_offset
;
1152 point
->right_offset
= (FT_Short
)right_offset
;
1161 /* store the hinted outline in an FT_Outline structure */
1164 ta_glyph_hints_save(TA_GlyphHints hints
,
1165 FT_Outline
* outline
)
1167 TA_Point point
= hints
->points
;
1168 TA_Point limit
= point
+ hints
->num_points
;
1170 FT_Vector
* vec
= outline
->points
;
1171 char* tag
= outline
->tags
;
1174 for (; point
< limit
; point
++, vec
++, tag
++)
1179 if (point
->flags
& TA_FLAG_CONIC
)
1180 tag
[0] = FT_CURVE_TAG_CONIC
;
1181 else if (point
->flags
& TA_FLAG_CUBIC
)
1182 tag
[0] = FT_CURVE_TAG_CUBIC
;
1184 tag
[0] = FT_CURVE_TAG_ON
;
1189 /****************************************************************
1191 * EDGE POINT GRID-FITTING
1193 ****************************************************************/
1196 /* align all points of an edge to the same coordinate value, */
1197 /* either horizontally or vertically */
1200 ta_glyph_hints_align_edge_points(TA_GlyphHints hints
,
1203 TA_AxisHints axis
= &hints
->axis
[dim
];
1204 TA_Segment segments
= axis
->segments
;
1205 TA_Segment segment_limit
= segments
+ axis
->num_segments
;
1209 if (dim
== TA_DIMENSION_HORZ
)
1211 for (seg
= segments
; seg
< segment_limit
; seg
++)
1213 TA_Edge edge
= seg
->edge
;
1214 TA_Point point
, first
, last
;
1225 point
->x
= edge
->pos
;
1226 point
->flags
|= TA_FLAG_TOUCH_X
;
1231 point
= point
->next
;
1237 for (seg
= segments
; seg
< segment_limit
; seg
++)
1239 TA_Edge edge
= seg
->edge
;
1240 TA_Point point
, first
, last
;
1251 point
->y
= edge
->pos
;
1252 point
->flags
|= TA_FLAG_TOUCH_Y
;
1257 point
= point
->next
;
1264 /****************************************************************
1266 * STRONG POINT INTERPOLATION
1268 ****************************************************************/
1271 /* hint the strong points -- */
1272 /* this is equivalent to the TrueType `IP' hinting instruction */
1275 ta_glyph_hints_align_strong_points(TA_GlyphHints hints
,
1278 TA_Point points
= hints
->points
;
1279 TA_Point point_limit
= points
+ hints
->num_points
;
1281 TA_AxisHints axis
= &hints
->axis
[dim
];
1283 TA_Edge edges
= axis
->edges
;
1284 TA_Edge edge_limit
= edges
+ axis
->num_edges
;
1286 FT_UShort touch_flag
;
1289 if (dim
== TA_DIMENSION_HORZ
)
1290 touch_flag
= TA_FLAG_TOUCH_X
;
1292 touch_flag
= TA_FLAG_TOUCH_Y
;
1294 if (edges
< edge_limit
)
1300 for (point
= points
; point
< point_limit
; point
++)
1302 FT_Pos u
, ou
, fu
; /* point position */
1306 if (point
->flags
& touch_flag
)
1309 /* if this point is candidate to weak interpolation, we */
1310 /* interpolate it after all strong points have been processed */
1312 if ((point
->flags
& TA_FLAG_WEAK_INTERPOLATION
))
1315 if (dim
== TA_DIMENSION_VERT
)
1328 /* is the point before the first edge? */
1330 delta
= edge
->fpos
- u
;
1333 u
= edge
->pos
- (edge
->opos
- ou
);
1336 point
->before
[dim
] = edge
;
1337 point
->after
[dim
] = NULL
;
1340 if (hints
->recorder
)
1341 hints
->recorder(ta_ip_before
, hints
, dim
,
1342 point
, NULL
, NULL
, NULL
, NULL
);
1347 /* is the point after the last edge? */
1348 edge
= edge_limit
- 1;
1349 delta
= u
- edge
->fpos
;
1352 u
= edge
->pos
+ (ou
- edge
->opos
);
1355 point
->before
[dim
] = NULL
;
1356 point
->after
[dim
] = edge
;
1359 if (hints
->recorder
)
1360 hints
->recorder(ta_ip_after
, hints
, dim
,
1361 point
, NULL
, NULL
, NULL
, NULL
);
1367 FT_PtrDist min
, max
, mid
;
1371 /* find enclosing edges */
1373 max
= edge_limit
- edges
;
1375 /* for a small number of edges, a linear search is better */
1381 for (nn
= 0; nn
< max
; nn
++)
1382 if (edges
[nn
].fpos
>= u
)
1385 if (edges
[nn
].fpos
== u
)
1389 if (hints
->recorder
)
1390 hints
->recorder(ta_ip_on
, hints
, dim
,
1391 point
, &edges
[nn
], NULL
, NULL
, NULL
);
1400 mid
= (max
+ min
) >> 1;
1410 /* we are on the edge */
1414 point
->before
[dim
] = NULL
;
1415 point
->after
[dim
] = NULL
;
1418 if (hints
->recorder
)
1419 hints
->recorder(ta_ip_on
, hints
, dim
,
1420 point
, edge
, NULL
, NULL
, NULL
);
1426 /* point is not on an edge */
1428 TA_Edge before
= edges
+ min
- 1;
1429 TA_Edge after
= edges
+ min
+ 0;
1433 point
->before
[dim
] = before
;
1434 point
->after
[dim
] = after
;
1437 /* assert(before && after && before != after) */
1438 if (before
->scale
== 0)
1439 before
->scale
= FT_DivFix(after
->pos
- before
->pos
,
1440 after
->fpos
- before
->fpos
);
1442 u
= before
->pos
+ FT_MulFix(fu
- before
->fpos
,
1445 if (hints
->recorder
)
1446 hints
->recorder(ta_ip_between
, hints
, dim
,
1447 point
, before
, after
, NULL
, NULL
);
1452 /* save the point position */
1453 if (dim
== TA_DIMENSION_HORZ
)
1458 point
->flags
|= touch_flag
;
1464 /****************************************************************
1466 * WEAK POINT INTERPOLATION
1468 ****************************************************************/
1471 /* shift the original coordinates of all points between `p1' and */
1472 /* `p2' to get hinted coordinates, using the same difference as */
1473 /* given by `ref' */
1476 ta_iup_shift(TA_Point p1
,
1481 FT_Pos delta
= ref
->u
- ref
->v
;
1487 for (p
= p1
; p
< ref
; p
++)
1488 p
->u
= p
->v
+ delta
;
1490 for (p
= ref
+ 1; p
<= p2
; p
++)
1491 p
->u
= p
->v
+ delta
;
1495 /* interpolate the original coordinates of all points between `p1' and */
1496 /* `p2' to get hinted coordinates, using `ref1' and `ref2' as the */
1497 /* reference points; the `u' and `v' members are the current and */
1498 /* original coordinate values, respectively. */
1500 /* details can be found in the TrueType bytecode specification */
1503 ta_iup_interp(TA_Point p1
,
1509 FT_Pos u
, v1
, v2
, u1
, u2
, d1
, d2
;
1515 if (ref1
->v
> ref2
->v
)
1529 if (u1
== u2
|| v1
== v2
)
1531 for (p
= p1
; p
<= p2
; p
++)
1547 FT_Fixed scale
= FT_DivFix(u2
- u1
, v2
- v1
);
1550 for (p
= p1
; p
<= p2
; p
++)
1559 u
= u1
+ FT_MulFix(u
- v1
, scale
);
1567 /* hint the weak points -- */
1568 /* this is equivalent to the TrueType `IUP' hinting instruction */
1571 ta_glyph_hints_align_weak_points(TA_GlyphHints hints
,
1574 TA_Point points
= hints
->points
;
1575 TA_Point point_limit
= points
+ hints
->num_points
;
1577 TA_Point
* contour
= hints
->contours
;
1578 TA_Point
* contour_limit
= contour
+ hints
->num_contours
;
1580 FT_UShort touch_flag
;
1583 TA_Point first_point
;
1586 /* pass 1: move segment points to edge positions */
1588 if (dim
== TA_DIMENSION_HORZ
)
1590 touch_flag
= TA_FLAG_TOUCH_X
;
1592 for (point
= points
; point
< point_limit
; point
++)
1594 point
->u
= point
->x
;
1595 point
->v
= point
->ox
;
1600 touch_flag
= TA_FLAG_TOUCH_Y
;
1602 for (point
= points
; point
< point_limit
; point
++)
1604 point
->u
= point
->y
;
1605 point
->v
= point
->oy
;
1609 for (; contour
< contour_limit
; contour
++)
1611 TA_Point first_touched
, last_touched
;
1615 end_point
= point
->prev
;
1616 first_point
= point
;
1618 /* find first touched point */
1621 if (point
> end_point
) /* no touched point in contour */
1624 if (point
->flags
& touch_flag
)
1630 first_touched
= point
;
1634 /* skip any touched neighbours */
1635 while (point
< end_point
1636 && (point
[1].flags
& touch_flag
) != 0)
1639 last_touched
= point
;
1641 /* find the next touched point, if any */
1645 if (point
> end_point
)
1648 if ((point
->flags
& touch_flag
) != 0)
1654 /* interpolate between last_touched and point */
1655 ta_iup_interp(last_touched
+ 1, point
- 1,
1656 last_touched
, point
);
1660 /* special case: only one point was touched */
1661 if (last_touched
== first_touched
)
1662 ta_iup_shift(first_point
, end_point
, first_touched
);
1664 else /* interpolate the last part */
1666 if (last_touched
< end_point
)
1667 ta_iup_interp(last_touched
+ 1, end_point
,
1668 last_touched
, first_touched
);
1670 if (first_touched
> points
)
1671 ta_iup_interp(first_point
, first_touched
- 1,
1672 last_touched
, first_touched
);
1679 /* now save the interpolated values back to x/y */
1680 if (dim
== TA_DIMENSION_HORZ
)
1682 for (point
= points
; point
< point_limit
; point
++)
1683 point
->x
= point
->u
;
1687 for (point
= points
; point
< point_limit
; point
++)
1688 point
->y
= point
->u
;
1693 #ifdef TA_CONFIG_OPTION_USE_WARPER
1695 /* apply (small) warp scale and warp delta for given dimension */
1698 ta_glyph_hints_scale_dim(TA_GlyphHints hints
,
1703 TA_Point points
= hints
->points
;
1704 TA_Point points_limit
= points
+ hints
->num_points
;
1708 if (dim
== TA_DIMENSION_HORZ
)
1710 for (point
= points
; point
< points_limit
; point
++)
1711 point
->x
= FT_MulFix(point
->fx
, scale
) + delta
;
1715 for (point
= points
; point
< points_limit
; point
++)
1716 point
->y
= FT_MulFix(point
->fy
, scale
) + delta
;
1720 #endif /* TA_CONFIG_OPTION_USE_WARPER */
1722 /* end of tahints.c */