1 /****************************************************************************
5 * Auto-fitter hinting routines (body).
7 * Copyright (C) 2003-2020 by
8 * David Turner, Robert Wilhelm, and Werner Lemberg.
10 * This file is part of the FreeType project, and may only be used,
11 * modified, and distributed under the terms of the FreeType project
12 * license, LICENSE.TXT. By continuing to use, modify, or distribute
13 * this file you indicate that you have read the license and
14 * understand and accept it fully.
21 #include <freetype/internal/ftcalc.h>
22 #include <freetype/internal/ftdebug.h>
25 /**************************************************************************
27 * The macro FT_COMPONENT is used in trace mode. It is an implicit
28 * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
29 * messages during execution.
32 #define FT_COMPONENT afhints
35 /* Get new segment for given axis. */
37 FT_LOCAL_DEF( FT_Error )
38 af_axis_hints_new_segment( AF_AxisHints axis,
40 AF_Segment *asegment )
42 FT_Error error = FT_Err_Ok;
43 AF_Segment segment = NULL;
46 if ( axis->num_segments < AF_SEGMENTS_EMBEDDED )
48 if ( !axis->segments )
50 axis->segments = axis->embedded.segments;
51 axis->max_segments = AF_SEGMENTS_EMBEDDED;
54 else if ( axis->num_segments >= axis->max_segments )
56 FT_Int old_max = axis->max_segments;
57 FT_Int new_max = old_max;
58 FT_Int big_max = (FT_Int)( FT_INT_MAX / sizeof ( *segment ) );
61 if ( old_max >= big_max )
63 error = FT_THROW( Out_Of_Memory );
67 new_max += ( new_max >> 2 ) + 4;
68 if ( new_max < old_max || new_max > big_max )
71 if ( axis->segments == axis->embedded.segments )
73 if ( FT_NEW_ARRAY( axis->segments, new_max ) )
75 ft_memcpy( axis->segments, axis->embedded.segments,
76 sizeof ( axis->embedded.segments ) );
80 if ( FT_RENEW_ARRAY( axis->segments, old_max, new_max ) )
84 axis->max_segments = new_max;
87 segment = axis->segments + axis->num_segments++;
95 /* Get new edge for given axis, direction, and position, */
96 /* without initializing the edge itself. */
99 af_axis_hints_new_edge( AF_AxisHints axis,
102 FT_Bool top_to_bottom_hinting,
106 FT_Error error = FT_Err_Ok;
111 if ( axis->num_edges < AF_EDGES_EMBEDDED )
115 axis->edges = axis->embedded.edges;
116 axis->max_edges = AF_EDGES_EMBEDDED;
119 else if ( axis->num_edges >= axis->max_edges )
121 FT_Int old_max = axis->max_edges;
122 FT_Int new_max = old_max;
123 FT_Int big_max = (FT_Int)( FT_INT_MAX / sizeof ( *edge ) );
126 if ( old_max >= big_max )
128 error = FT_THROW( Out_Of_Memory );
132 new_max += ( new_max >> 2 ) + 4;
133 if ( new_max < old_max || new_max > big_max )
136 if ( axis->edges == axis->embedded.edges )
138 if ( FT_NEW_ARRAY( axis->edges, new_max ) )
140 ft_memcpy( axis->edges, axis->embedded.edges,
141 sizeof ( axis->embedded.edges ) );
145 if ( FT_RENEW_ARRAY( axis->edges, old_max, new_max ) )
149 axis->max_edges = new_max;
153 edge = edges + axis->num_edges;
155 while ( edge > edges )
157 if ( top_to_bottom_hinting ? ( edge[-1].fpos > fpos )
158 : ( edge[-1].fpos < fpos ) )
161 /* we want the edge with same position and minor direction */
162 /* to appear before those in the major one in the list */
163 if ( edge[-1].fpos == fpos && dir == axis->major_dir )
178 #ifdef FT_DEBUG_AUTOFIT
180 #include FT_CONFIG_STANDARD_LIBRARY_H
182 /* The dump functions are used in the `ftgrid' demo program, too. */
183 #define AF_DUMP( varformat ) \
189 FT_TRACE7( varformat ); \
194 af_dir_str( AF_Direction dir )
221 #define AF_INDEX_NUM( ptr, base ) (int)( (ptr) ? ( (ptr) - (base) ) : -1 )
225 af_print_idx( char* p,
235 ft_sprintf( p, "%d", idx );
242 af_get_segment_index( AF_GlyphHints hints,
246 AF_AxisHints axis = &hints->axis[dimension];
247 AF_Point point = hints->points + point_idx;
248 AF_Segment segments = axis->segments;
249 AF_Segment limit = segments + axis->num_segments;
253 for ( segment = segments; segment < limit; segment++ )
255 if ( segment->first <= segment->last )
257 if ( point >= segment->first && point <= segment->last )
262 AF_Point p = segment->first;
270 if ( p == segment->last )
279 if ( segment == limit )
282 return (int)( segment - segments );
287 af_get_edge_index( AF_GlyphHints hints,
291 AF_AxisHints axis = &hints->axis[dimension];
292 AF_Edge edges = axis->edges;
293 AF_Segment segment = axis->segments + segment_idx;
296 return segment_idx == -1 ? -1 : AF_INDEX_NUM( segment->edge, edges );
301 af_get_strong_edge_index( AF_GlyphHints hints,
302 AF_Edge* strong_edges,
305 AF_AxisHints axis = &hints->axis[dimension];
306 AF_Edge edges = axis->edges;
309 return AF_INDEX_NUM( strong_edges[dimension], edges );
317 af_glyph_hints_dump_points( AF_GlyphHints hints,
320 AF_Point points = hints->points;
321 AF_Point limit = points + hints->num_points;
322 AF_Point* contour = hints->contours;
323 AF_Point* climit = contour + hints->num_contours;
327 AF_DUMP(( "Table of points:\n" ));
329 if ( hints->num_points )
331 AF_DUMP(( " index hedge hseg vedge vseg flags "
332 /* " XXXXX XXXXX XXXXX XXXXX XXXXX XXXXXX" */
333 " xorg yorg xscale yscale xfit yfit "
334 /* " XXXXX XXXXX XXXX.XX XXXX.XX XXXX.XX XXXX.XX" */
335 " hbef haft vbef vaft" ));
336 /* " XXXXX XXXXX XXXXX XXXXX" */
339 AF_DUMP(( " (none)\n" ));
341 for ( point = points; point < limit; point++ )
343 int point_idx = AF_INDEX_NUM( point, points );
344 int segment_idx_0 = af_get_segment_index( hints, point_idx, 0 );
345 int segment_idx_1 = af_get_segment_index( hints, point_idx, 1 );
347 char buf1[16], buf2[16], buf3[16], buf4[16];
348 char buf5[16], buf6[16], buf7[16], buf8[16];
351 /* insert extra newline at the beginning of a contour */
352 if ( contour < climit && *contour == point )
358 AF_DUMP(( " %5d %5s %5s %5s %5s %s"
359 " %5d %5d %7.2f %7.2f %7.2f %7.2f"
360 " %5s %5s %5s %5s\n",
363 af_get_edge_index( hints, segment_idx_1, 1 ) ),
364 af_print_idx( buf2, segment_idx_1 ),
366 af_get_edge_index( hints, segment_idx_0, 0 ) ),
367 af_print_idx( buf4, segment_idx_0 ),
368 ( point->flags & AF_FLAG_NEAR )
370 : ( point->flags & AF_FLAG_WEAK_INTERPOLATION )
381 af_print_idx( buf5, af_get_strong_edge_index( hints,
384 af_print_idx( buf6, af_get_strong_edge_index( hints,
387 af_print_idx( buf7, af_get_strong_edge_index( hints,
390 af_print_idx( buf8, af_get_strong_edge_index( hints,
402 af_edge_flags_to_string( FT_UInt flags )
404 static char temp[32];
408 if ( flags & AF_EDGE_ROUND )
410 ft_memcpy( temp + pos, "round", 5 );
413 if ( flags & AF_EDGE_SERIF )
417 ft_memcpy( temp + pos, "serif", 5 );
429 /* Dump the array of linked segments. */
435 af_glyph_hints_dump_segments( AF_GlyphHints hints,
441 for ( dimension = 1; dimension >= 0; dimension-- )
443 AF_AxisHints axis = &hints->axis[dimension];
444 AF_Point points = hints->points;
445 AF_Edge edges = axis->edges;
446 AF_Segment segments = axis->segments;
447 AF_Segment limit = segments + axis->num_segments;
450 char buf1[16], buf2[16], buf3[16];
453 AF_DUMP(( "Table of %s segments:\n",
454 dimension == AF_DIMENSION_HORZ ? "vertical"
456 if ( axis->num_segments )
458 AF_DUMP(( " index pos delta dir from to "
459 /* " XXXXX XXXXX XXXXX XXXXX XXXX XXXX" */
461 /* " XXXX XXXXX XXXX" */
462 " height extra flags\n" ));
463 /* " XXXXXX XXXXX XXXXXXXXXXX" */
466 AF_DUMP(( " (none)\n" ));
468 for ( seg = segments; seg < limit; seg++ )
469 AF_DUMP(( " %5d %5d %5d %5s %4d %4d"
472 AF_INDEX_NUM( seg, segments ),
475 af_dir_str( (AF_Direction)seg->dir ),
476 AF_INDEX_NUM( seg->first, points ),
477 AF_INDEX_NUM( seg->last, points ),
479 af_print_idx( buf1, AF_INDEX_NUM( seg->link, segments ) ),
480 af_print_idx( buf2, AF_INDEX_NUM( seg->serif, segments ) ),
481 af_print_idx( buf3, AF_INDEX_NUM( seg->edge, edges ) ),
484 seg->height - ( seg->max_coord - seg->min_coord ),
485 af_edge_flags_to_string( seg->flags ) ));
494 /* Fetch number of segments. */
500 af_glyph_hints_get_num_segments( AF_GlyphHints hints,
502 FT_Int* num_segments )
508 dim = ( dimension == 0 ) ? AF_DIMENSION_HORZ : AF_DIMENSION_VERT;
510 axis = &hints->axis[dim];
511 *num_segments = axis->num_segments;
520 /* Fetch offset of segments into user supplied offset array. */
526 af_glyph_hints_get_segment_offset( AF_GlyphHints hints,
531 FT_Pos *blue_offset )
539 return FT_THROW( Invalid_Argument );
541 dim = ( dimension == 0 ) ? AF_DIMENSION_HORZ : AF_DIMENSION_VERT;
543 axis = &hints->axis[dim];
545 if ( idx < 0 || idx >= axis->num_segments )
546 return FT_THROW( Invalid_Argument );
548 seg = &axis->segments[idx];
549 *offset = ( dim == AF_DIMENSION_HORZ ) ? seg->first->fx
552 *is_blue = FT_BOOL( seg->edge->blue_edge );
557 *blue_offset = seg->edge->blue_edge->org;
568 /* Dump the array of linked edges. */
574 af_glyph_hints_dump_edges( AF_GlyphHints hints,
580 for ( dimension = 1; dimension >= 0; dimension-- )
582 AF_AxisHints axis = &hints->axis[dimension];
583 AF_Edge edges = axis->edges;
584 AF_Edge limit = edges + axis->num_edges;
587 char buf1[16], buf2[16];
591 * note: AF_DIMENSION_HORZ corresponds to _vertical_ edges
592 * since they have a constant X coordinate.
594 if ( dimension == AF_DIMENSION_HORZ )
595 AF_DUMP(( "Table of %s edges (1px=%.2fu, 10u=%.2fpx):\n",
597 65536.0 * 64.0 / hints->x_scale,
598 10.0 * hints->x_scale / 65536.0 / 64.0 ));
600 AF_DUMP(( "Table of %s edges (1px=%.2fu, 10u=%.2fpx):\n",
602 65536.0 * 64.0 / hints->y_scale,
603 10.0 * hints->y_scale / 65536.0 / 64.0 ));
605 if ( axis->num_edges )
607 AF_DUMP(( " index pos dir link serif"
608 /* " XXXXX XXXX.XX XXXXX XXXX XXXXX" */
609 " blue opos pos flags\n" ));
610 /* " X XXXX.XX XXXX.XX XXXXXXXXXXX" */
613 AF_DUMP(( " (none)\n" ));
615 for ( edge = edges; edge < limit; edge++ )
616 AF_DUMP(( " %5d %7.2f %5s %4s %5s"
617 " %c %7.2f %7.2f %11s\n",
618 AF_INDEX_NUM( edge, edges ),
619 (int)edge->opos / 64.0,
620 af_dir_str( (AF_Direction)edge->dir ),
621 af_print_idx( buf1, AF_INDEX_NUM( edge->link, edges ) ),
622 af_print_idx( buf2, AF_INDEX_NUM( edge->serif, edges ) ),
624 edge->blue_edge ? 'y' : 'n',
627 af_edge_flags_to_string( edge->flags ) ));
637 #endif /* !FT_DEBUG_AUTOFIT */
640 /* Compute the direction value of a given vector. */
642 FT_LOCAL_DEF( AF_Direction )
643 af_direction_compute( FT_Pos dx,
646 FT_Pos ll, ss; /* long and short arm lengths */
647 AF_Direction dir; /* candidate direction */
681 /* return no direction if arm lengths do not differ enough */
682 /* (value 14 is heuristic, corresponding to approx. 4.1 degrees) */
683 /* the long arm is never negative */
684 if ( ll <= 14 * FT_ABS( ss ) )
692 af_glyph_hints_init( AF_GlyphHints hints,
695 /* no need to initialize the embedded items */
696 FT_MEM_ZERO( hints, sizeof ( *hints ) - sizeof ( hints->embedded ) );
697 hints->memory = memory;
702 af_glyph_hints_done( AF_GlyphHints hints )
708 if ( !( hints && hints->memory ) )
711 memory = hints->memory;
714 * note that we don't need to free the segment and edge
715 * buffers since they are really within the hints->points array
717 for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ )
719 AF_AxisHints axis = &hints->axis[dim];
722 axis->num_segments = 0;
723 axis->max_segments = 0;
724 if ( axis->segments != axis->embedded.segments )
725 FT_FREE( axis->segments );
729 if ( axis->edges != axis->embedded.edges )
730 FT_FREE( axis->edges );
733 if ( hints->contours != hints->embedded.contours )
734 FT_FREE( hints->contours );
735 hints->max_contours = 0;
736 hints->num_contours = 0;
738 if ( hints->points != hints->embedded.points )
739 FT_FREE( hints->points );
740 hints->max_points = 0;
741 hints->num_points = 0;
743 hints->memory = NULL;
750 af_glyph_hints_rescale( AF_GlyphHints hints,
751 AF_StyleMetrics metrics )
753 hints->metrics = metrics;
754 hints->scaler_flags = metrics->scaler.flags;
758 /* Recompute all AF_Point in AF_GlyphHints from the definitions */
759 /* in a source outline. */
761 FT_LOCAL_DEF( FT_Error )
762 af_glyph_hints_reload( AF_GlyphHints hints,
763 FT_Outline* outline )
765 FT_Error error = FT_Err_Ok;
767 FT_UInt old_max, new_max;
768 FT_Fixed x_scale = hints->x_scale;
769 FT_Fixed y_scale = hints->y_scale;
770 FT_Pos x_delta = hints->x_delta;
771 FT_Pos y_delta = hints->y_delta;
772 FT_Memory memory = hints->memory;
775 hints->num_points = 0;
776 hints->num_contours = 0;
778 hints->axis[0].num_segments = 0;
779 hints->axis[0].num_edges = 0;
780 hints->axis[1].num_segments = 0;
781 hints->axis[1].num_edges = 0;
783 /* first of all, reallocate the contours array if necessary */
784 new_max = (FT_UInt)outline->n_contours;
785 old_max = (FT_UInt)hints->max_contours;
787 if ( new_max <= AF_CONTOURS_EMBEDDED )
789 if ( !hints->contours )
791 hints->contours = hints->embedded.contours;
792 hints->max_contours = AF_CONTOURS_EMBEDDED;
795 else if ( new_max > old_max )
797 if ( hints->contours == hints->embedded.contours )
798 hints->contours = NULL;
800 new_max = ( new_max + 3 ) & ~3U; /* round up to a multiple of 4 */
802 if ( FT_RENEW_ARRAY( hints->contours, old_max, new_max ) )
805 hints->max_contours = (FT_Int)new_max;
809 * then reallocate the points arrays if necessary --
810 * note that we reserve two additional point positions, used to
811 * hint metrics appropriately
813 new_max = (FT_UInt)( outline->n_points + 2 );
814 old_max = (FT_UInt)hints->max_points;
816 if ( new_max <= AF_POINTS_EMBEDDED )
818 if ( !hints->points )
820 hints->points = hints->embedded.points;
821 hints->max_points = AF_POINTS_EMBEDDED;
824 else if ( new_max > old_max )
826 if ( hints->points == hints->embedded.points )
827 hints->points = NULL;
829 new_max = ( new_max + 2 + 7 ) & ~7U; /* round up to a multiple of 8 */
831 if ( FT_RENEW_ARRAY( hints->points, old_max, new_max ) )
834 hints->max_points = (FT_Int)new_max;
837 hints->num_points = outline->n_points;
838 hints->num_contours = outline->n_contours;
840 /* We can't rely on the value of `FT_Outline.flags' to know the fill */
841 /* direction used for a glyph, given that some fonts are broken (e.g., */
842 /* the Arphic ones). We thus recompute it each time we need to. */
844 hints->axis[AF_DIMENSION_HORZ].major_dir = AF_DIR_UP;
845 hints->axis[AF_DIMENSION_VERT].major_dir = AF_DIR_LEFT;
847 if ( FT_Outline_Get_Orientation( outline ) == FT_ORIENTATION_POSTSCRIPT )
849 hints->axis[AF_DIMENSION_HORZ].major_dir = AF_DIR_DOWN;
850 hints->axis[AF_DIMENSION_VERT].major_dir = AF_DIR_RIGHT;
853 hints->x_scale = x_scale;
854 hints->y_scale = y_scale;
855 hints->x_delta = x_delta;
856 hints->y_delta = y_delta;
858 hints->xmin_delta = 0;
859 hints->xmax_delta = 0;
861 points = hints->points;
862 if ( hints->num_points == 0 )
867 AF_Point point_limit = points + hints->num_points;
869 /* value 20 in `near_limit' is heuristic */
870 FT_UInt units_per_em = hints->metrics->scaler.face->units_per_EM;
871 FT_Int near_limit = 20 * units_per_em / 2048;
874 /* compute coordinates & Bezier flags, next and prev */
876 FT_Vector* vec = outline->points;
877 char* tag = outline->tags;
878 FT_Short endpoint = outline->contours[0];
879 AF_Point end = points + endpoint;
881 FT_Int contour_index = 0;
884 for ( point = points; point < point_limit; point++, vec++, tag++ )
889 point->in_dir = (FT_Char)AF_DIR_NONE;
890 point->out_dir = (FT_Char)AF_DIR_NONE;
892 point->fx = (FT_Short)vec->x;
893 point->fy = (FT_Short)vec->y;
894 point->ox = point->x = FT_MulFix( vec->x, x_scale ) + x_delta;
895 point->oy = point->y = FT_MulFix( vec->y, y_scale ) + y_delta;
897 end->fx = (FT_Short)outline->points[endpoint].x;
898 end->fy = (FT_Short)outline->points[endpoint].y;
900 switch ( FT_CURVE_TAG( *tag ) )
902 case FT_CURVE_TAG_CONIC:
903 point->flags = AF_FLAG_CONIC;
905 case FT_CURVE_TAG_CUBIC:
906 point->flags = AF_FLAG_CUBIC;
909 point->flags = AF_FLAG_NONE;
912 out_x = point->fx - prev->fx;
913 out_y = point->fy - prev->fy;
915 if ( FT_ABS( out_x ) + FT_ABS( out_y ) < near_limit )
916 prev->flags |= AF_FLAG_NEAR;
924 if ( ++contour_index < outline->n_contours )
926 endpoint = outline->contours[contour_index];
927 end = points + endpoint;
932 #ifdef FT_DEBUG_AUTOFIT
933 point->before[0] = NULL;
934 point->before[1] = NULL;
935 point->after[0] = NULL;
936 point->after[1] = NULL;
942 /* set up the contours array */
944 AF_Point* contour = hints->contours;
945 AF_Point* contour_limit = contour + hints->num_contours;
946 short* end = outline->contours;
950 for ( ; contour < contour_limit; contour++, end++ )
952 contour[0] = points + idx;
953 idx = (short)( end[0] + 1 );
959 * Compute directions of `in' and `out' vectors.
961 * Note that distances between points that are very near to each
962 * other are accumulated. In other words, the auto-hinter either
963 * prepends the small vectors between near points to the first
964 * non-near vector, or the sum of small vector lengths exceeds a
965 * threshold, thus `grouping' the small vectors. All intermediate
966 * points are tagged as weak; the directions are adjusted also to
967 * be equal to the accumulated one.
970 FT_Int near_limit2 = 2 * near_limit - 1;
973 AF_Point* contour_limit = hints->contours + hints->num_contours;
976 for ( contour = hints->contours; contour < contour_limit; contour++ )
978 AF_Point first = *contour;
979 AF_Point next, prev, curr;
984 /* since the first point of a contour could be part of a */
985 /* series of near points, go backwards to find the first */
986 /* non-near point and adjust `first' */
991 while ( prev != first )
993 out_x = point->fx - prev->fx;
994 out_y = point->fy - prev->fy;
997 * We use Taxicab metrics to measure the vector length.
999 * Note that the accumulated distances so far could have the
1000 * opposite direction of the distance measured here. For this
1001 * reason we use `near_limit2' for the comparison to get a
1002 * non-near point even in the worst case.
1004 if ( FT_ABS( out_x ) + FT_ABS( out_y ) >= near_limit2 )
1011 /* adjust first point */
1014 /* now loop over all points of the contour to get */
1015 /* `in' and `out' vector directions */
1020 * We abuse the `u' and `v' fields to store index deltas to the
1021 * next and previous non-near point, respectively.
1023 * To avoid problems with not having non-near points, we point to
1024 * `first' by default as the next non-near point.
1027 curr->u = (FT_Pos)( first - curr );
1028 first->v = -curr->u;
1036 AF_Direction out_dir;
1042 out_x += next->fx - point->fx;
1043 out_y += next->fy - point->fy;
1045 if ( FT_ABS( out_x ) + FT_ABS( out_y ) < near_limit )
1047 next->flags |= AF_FLAG_WEAK_INTERPOLATION;
1051 curr->u = (FT_Pos)( next - curr );
1054 out_dir = af_direction_compute( out_x, out_y );
1056 /* adjust directions for all points inbetween; */
1057 /* the loop also updates position of `curr' */
1058 curr->out_dir = (FT_Char)out_dir;
1059 for ( curr = curr->next; curr != next; curr = curr->next )
1061 curr->in_dir = (FT_Char)out_dir;
1062 curr->out_dir = (FT_Char)out_dir;
1064 next->in_dir = (FT_Char)out_dir;
1066 curr->u = (FT_Pos)( first - curr );
1067 first->v = -curr->u;
1072 } while ( next != first );
1076 * The next step is to `simplify' an outline's topology so that we
1077 * can identify local extrema more reliably: A series of
1078 * non-horizontal or non-vertical vectors pointing into the same
1079 * quadrant are handled as a single, long vector. From a
1080 * topological point of the view, the intermediate points are of no
1081 * interest and thus tagged as weak.
1084 for ( point = points; point < point_limit; point++ )
1086 if ( point->flags & AF_FLAG_WEAK_INTERPOLATION )
1089 if ( point->in_dir == AF_DIR_NONE &&
1090 point->out_dir == AF_DIR_NONE )
1092 /* check whether both vectors point into the same quadrant */
1095 FT_Pos out_x, out_y;
1097 AF_Point next_u = point + point->u;
1098 AF_Point prev_v = point + point->v;
1101 in_x = point->fx - prev_v->fx;
1102 in_y = point->fy - prev_v->fy;
1104 out_x = next_u->fx - point->fx;
1105 out_y = next_u->fy - point->fy;
1107 if ( ( in_x ^ out_x ) >= 0 && ( in_y ^ out_y ) >= 0 )
1109 /* yes, so tag current point as weak */
1110 /* and update index deltas */
1112 point->flags |= AF_FLAG_WEAK_INTERPOLATION;
1114 prev_v->u = (FT_Pos)( next_u - prev_v );
1115 next_u->v = -prev_v->u;
1121 * Finally, check for remaining weak points. Everything else not
1122 * collected in edges so far is then implicitly classified as strong
1126 for ( point = points; point < point_limit; point++ )
1128 if ( point->flags & AF_FLAG_WEAK_INTERPOLATION )
1131 if ( point->flags & AF_FLAG_CONTROL )
1133 /* control points are always weak */
1135 point->flags |= AF_FLAG_WEAK_INTERPOLATION;
1137 else if ( point->out_dir == point->in_dir )
1139 if ( point->out_dir != AF_DIR_NONE )
1141 /* current point lies on a horizontal or */
1142 /* vertical segment (but doesn't start or end it) */
1147 AF_Point next_u = point + point->u;
1148 AF_Point prev_v = point + point->v;
1151 if ( ft_corner_is_flat( point->fx - prev_v->fx,
1152 point->fy - prev_v->fy,
1153 next_u->fx - point->fx,
1154 next_u->fy - point->fy ) )
1156 /* either the `in' or the `out' vector is much more */
1157 /* dominant than the other one, so tag current point */
1158 /* as weak and update index deltas */
1160 prev_v->u = (FT_Pos)( next_u - prev_v );
1161 next_u->v = -prev_v->u;
1167 else if ( point->in_dir == -point->out_dir )
1169 /* current point forms a spike */
1181 /* Store the hinted outline in an FT_Outline structure. */
1183 FT_LOCAL_DEF( void )
1184 af_glyph_hints_save( AF_GlyphHints hints,
1185 FT_Outline* outline )
1187 AF_Point point = hints->points;
1188 AF_Point limit = point + hints->num_points;
1189 FT_Vector* vec = outline->points;
1190 char* tag = outline->tags;
1193 for ( ; point < limit; point++, vec++, tag++ )
1198 if ( point->flags & AF_FLAG_CONIC )
1199 tag[0] = FT_CURVE_TAG_CONIC;
1200 else if ( point->flags & AF_FLAG_CUBIC )
1201 tag[0] = FT_CURVE_TAG_CUBIC;
1203 tag[0] = FT_CURVE_TAG_ON;
1208 /****************************************************************
1210 * EDGE POINT GRID-FITTING
1212 ****************************************************************/
1215 /* Align all points of an edge to the same coordinate value, */
1216 /* either horizontally or vertically. */
1218 FT_LOCAL_DEF( void )
1219 af_glyph_hints_align_edge_points( AF_GlyphHints hints,
1222 AF_AxisHints axis = & hints->axis[dim];
1223 AF_Segment segments = axis->segments;
1224 AF_Segment segment_limit = segments + axis->num_segments;
1228 if ( dim == AF_DIMENSION_HORZ )
1230 for ( seg = segments; seg < segment_limit; seg++ )
1232 AF_Edge edge = seg->edge;
1233 AF_Point point, first, last;
1244 point->x = edge->pos;
1245 point->flags |= AF_FLAG_TOUCH_X;
1247 if ( point == last )
1250 point = point->next;
1256 for ( seg = segments; seg < segment_limit; seg++ )
1258 AF_Edge edge = seg->edge;
1259 AF_Point point, first, last;
1270 point->y = edge->pos;
1271 point->flags |= AF_FLAG_TOUCH_Y;
1273 if ( point == last )
1276 point = point->next;
1283 /****************************************************************
1285 * STRONG POINT INTERPOLATION
1287 ****************************************************************/
1290 /* Hint the strong points -- this is equivalent to the TrueType `IP' */
1291 /* hinting instruction. */
1293 FT_LOCAL_DEF( void )
1294 af_glyph_hints_align_strong_points( AF_GlyphHints hints,
1297 AF_Point points = hints->points;
1298 AF_Point point_limit = points + hints->num_points;
1299 AF_AxisHints axis = &hints->axis[dim];
1300 AF_Edge edges = axis->edges;
1301 AF_Edge edge_limit = edges + axis->num_edges;
1305 if ( dim == AF_DIMENSION_HORZ )
1306 touch_flag = AF_FLAG_TOUCH_X;
1308 touch_flag = AF_FLAG_TOUCH_Y;
1310 if ( edges < edge_limit )
1316 for ( point = points; point < point_limit; point++ )
1318 FT_Pos u, ou, fu; /* point position */
1322 if ( point->flags & touch_flag )
1325 /* if this point is candidate to weak interpolation, we */
1326 /* interpolate it after all strong points have been processed */
1328 if ( ( point->flags & AF_FLAG_WEAK_INTERPOLATION ) )
1331 if ( dim == AF_DIMENSION_VERT )
1344 /* is the point before the first edge? */
1346 delta = edge->fpos - u;
1349 u = edge->pos - ( edge->opos - ou );
1351 #ifdef FT_DEBUG_AUTOFIT
1352 point->before[dim] = edge;
1353 point->after[dim] = NULL;
1359 /* is the point after the last edge? */
1360 edge = edge_limit - 1;
1361 delta = u - edge->fpos;
1364 u = edge->pos + ( ou - edge->opos );
1366 #ifdef FT_DEBUG_AUTOFIT
1367 point->before[dim] = NULL;
1368 point->after[dim] = edge;
1375 FT_PtrDist min, max, mid;
1379 /* find enclosing edges */
1381 max = edge_limit - edges;
1384 /* for a small number of edges, a linear search is better */
1390 for ( nn = 0; nn < max; nn++ )
1391 if ( edges[nn].fpos >= u )
1394 if ( edges[nn].fpos == u )
1405 mid = ( max + min ) >> 1;
1411 else if ( u > fpos )
1415 /* we are on the edge */
1418 #ifdef FT_DEBUG_AUTOFIT
1419 point->before[dim] = NULL;
1420 point->after[dim] = NULL;
1427 /* point is not on an edge */
1429 AF_Edge before = edges + min - 1;
1430 AF_Edge after = edges + min + 0;
1433 #ifdef FT_DEBUG_AUTOFIT
1434 point->before[dim] = before;
1435 point->after[dim] = after;
1438 /* assert( before && after && before != after ) */
1439 if ( before->scale == 0 )
1440 before->scale = FT_DivFix( after->pos - before->pos,
1441 after->fpos - before->fpos );
1443 u = before->pos + FT_MulFix( fu - before->fpos,
1449 /* save the point position */
1450 if ( dim == AF_DIMENSION_HORZ )
1455 point->flags |= touch_flag;
1461 /****************************************************************
1463 * WEAK POINT INTERPOLATION
1465 ****************************************************************/
1468 /* Shift the original coordinates of all points between `p1' and */
1469 /* `p2' to get hinted coordinates, using the same difference as */
1470 /* given by `ref'. */
1473 af_iup_shift( AF_Point p1,
1478 FT_Pos delta = ref->u - ref->v;
1484 for ( p = p1; p < ref; p++ )
1485 p->u = p->v + delta;
1487 for ( p = ref + 1; p <= p2; p++ )
1488 p->u = p->v + delta;
1492 /* Interpolate the original coordinates of all points between `p1' and */
1493 /* `p2' to get hinted coordinates, using `ref1' and `ref2' as the */
1494 /* reference points. The `u' and `v' members are the current and */
1495 /* original coordinate values, respectively. */
1497 /* Details can be found in the TrueType bytecode specification. */
1500 af_iup_interp( AF_Point p1,
1506 FT_Pos u, v1, v2, u1, u2, d1, d2;
1512 if ( ref1->v > ref2->v )
1526 if ( u1 == u2 || v1 == v2 )
1528 for ( p = p1; p <= p2; p++ )
1544 FT_Fixed scale = FT_DivFix( u2 - u1, v2 - v1 );
1547 for ( p = p1; p <= p2; p++ )
1556 u = u1 + FT_MulFix( u - v1, scale );
1564 /* Hint the weak points -- this is equivalent to the TrueType `IUP' */
1565 /* hinting instruction. */
1567 FT_LOCAL_DEF( void )
1568 af_glyph_hints_align_weak_points( AF_GlyphHints hints,
1571 AF_Point points = hints->points;
1572 AF_Point point_limit = points + hints->num_points;
1573 AF_Point* contour = hints->contours;
1574 AF_Point* contour_limit = contour + hints->num_contours;
1578 AF_Point first_point;
1581 /* PASS 1: Move segment points to edge positions */
1583 if ( dim == AF_DIMENSION_HORZ )
1585 touch_flag = AF_FLAG_TOUCH_X;
1587 for ( point = points; point < point_limit; point++ )
1589 point->u = point->x;
1590 point->v = point->ox;
1595 touch_flag = AF_FLAG_TOUCH_Y;
1597 for ( point = points; point < point_limit; point++ )
1599 point->u = point->y;
1600 point->v = point->oy;
1604 for ( ; contour < contour_limit; contour++ )
1606 AF_Point first_touched, last_touched;
1610 end_point = point->prev;
1611 first_point = point;
1613 /* find first touched point */
1616 if ( point > end_point ) /* no touched point in contour */
1619 if ( point->flags & touch_flag )
1625 first_touched = point;
1629 FT_ASSERT( point <= end_point &&
1630 ( point->flags & touch_flag ) != 0 );
1632 /* skip any touched neighbours */
1633 while ( point < end_point &&
1634 ( point[1].flags & touch_flag ) != 0 )
1637 last_touched = point;
1639 /* find the next touched point, if any */
1643 if ( point > end_point )
1646 if ( ( point->flags & touch_flag ) != 0 )
1652 /* interpolate between last_touched and point */
1653 af_iup_interp( last_touched + 1, point - 1,
1654 last_touched, point );
1658 /* special case: only one point was touched */
1659 if ( last_touched == first_touched )
1660 af_iup_shift( first_point, end_point, first_touched );
1662 else /* interpolate the last part */
1664 if ( last_touched < end_point )
1665 af_iup_interp( last_touched + 1, end_point,
1666 last_touched, first_touched );
1668 if ( first_touched > points )
1669 af_iup_interp( first_point, first_touched - 1,
1670 last_touched, first_touched );
1677 /* now save the interpolated values back to x/y */
1678 if ( dim == AF_DIMENSION_HORZ )
1680 for ( point = points; point < point_limit; point++ )
1681 point->x = point->u;
1685 for ( point = points; point < point_limit; point++ )
1686 point->y = point->u;
1691 #ifdef AF_CONFIG_OPTION_USE_WARPER
1693 /* Apply (small) warp scale and warp delta for given dimension. */
1695 FT_LOCAL_DEF( void )
1696 af_glyph_hints_scale_dim( AF_GlyphHints hints,
1701 AF_Point points = hints->points;
1702 AF_Point points_limit = points + hints->num_points;
1706 if ( dim == AF_DIMENSION_HORZ )
1708 for ( point = points; point < points_limit; point++ )
1709 point->x = FT_MulFix( point->fx, scale ) + delta;
1713 for ( point = points; point < points_limit; point++ )
1714 point->y = FT_MulFix( point->fy, scale ) + delta;
1718 #endif /* AF_CONFIG_OPTION_USE_WARPER */