1 /***************************************************************************/
5 /* Auto-fitter hinting routines (body). */
7 /* Copyright 2003-2016 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. */
16 /***************************************************************************/
21 #include FT_INTERNAL_CALC_H
22 #include FT_INTERNAL_DEBUG_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 trace_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
== NULL
)
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
)
113 if ( axis
->edges
== NULL
)
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
);
304 af_glyph_hints_dump_points( AF_GlyphHints hints
,
307 AF_Point points
= hints
->points
;
308 AF_Point limit
= points
+ hints
->num_points
;
309 AF_Point
* contour
= hints
->contours
;
310 AF_Point
* climit
= contour
+ hints
->num_contours
;
314 AF_DUMP(( "Table of points:\n" ));
316 if ( hints
->num_points
)
317 AF_DUMP(( " index hedge hseg vedge vseg flags "
318 " xorg yorg xscale yscale xfit yfit" ));
320 AF_DUMP(( " (none)\n" ));
322 for ( point
= points
; point
< limit
; point
++ )
324 int point_idx
= AF_INDEX_NUM( point
, points
);
325 int segment_idx_0
= af_get_segment_index( hints
, point_idx
, 0 );
326 int segment_idx_1
= af_get_segment_index( hints
, point_idx
, 1 );
328 char buf1
[16], buf2
[16], buf3
[16], buf4
[16];
331 /* insert extra newline at the beginning of a contour */
332 if ( contour
< climit
&& *contour
== point
)
338 AF_DUMP(( " %5d %5s %5s %5s %5s %s"
339 " %5d %5d %7.2f %7.2f %7.2f %7.2f\n",
342 af_get_edge_index( hints
, segment_idx_1
, 1 ) ),
343 af_print_idx( buf2
, segment_idx_1
),
345 af_get_edge_index( hints
, segment_idx_0
, 0 ) ),
346 af_print_idx( buf4
, segment_idx_0
),
347 ( point
->flags
& AF_FLAG_NEAR
)
349 : ( point
->flags
& AF_FLAG_WEAK_INTERPOLATION
)
368 af_edge_flags_to_string( FT_UInt flags
)
370 static char temp
[32];
374 if ( flags
& AF_EDGE_ROUND
)
376 ft_memcpy( temp
+ pos
, "round", 5 );
379 if ( flags
& AF_EDGE_SERIF
)
383 ft_memcpy( temp
+ pos
, "serif", 5 );
395 /* Dump the array of linked segments. */
401 af_glyph_hints_dump_segments( AF_GlyphHints hints
,
407 for ( dimension
= 1; dimension
>= 0; dimension
-- )
409 AF_AxisHints axis
= &hints
->axis
[dimension
];
410 AF_Point points
= hints
->points
;
411 AF_Edge edges
= axis
->edges
;
412 AF_Segment segments
= axis
->segments
;
413 AF_Segment limit
= segments
+ axis
->num_segments
;
416 char buf1
[16], buf2
[16], buf3
[16];
419 AF_DUMP(( "Table of %s segments:\n",
420 dimension
== AF_DIMENSION_HORZ
? "vertical"
422 if ( axis
->num_segments
)
423 AF_DUMP(( " index pos delta dir from to "
425 " height extra flags\n" ));
427 AF_DUMP(( " (none)\n" ));
429 for ( seg
= segments
; seg
< limit
; seg
++ )
430 AF_DUMP(( " %5d %5d %5d %5s %4d %4d"
433 AF_INDEX_NUM( seg
, segments
),
436 af_dir_str( (AF_Direction
)seg
->dir
),
437 AF_INDEX_NUM( seg
->first
, points
),
438 AF_INDEX_NUM( seg
->last
, points
),
440 af_print_idx( buf1
, AF_INDEX_NUM( seg
->link
, segments
) ),
441 af_print_idx( buf2
, AF_INDEX_NUM( seg
->serif
, segments
) ),
442 af_print_idx( buf3
, AF_INDEX_NUM( seg
->edge
, edges
) ),
445 seg
->height
- ( seg
->max_coord
- seg
->min_coord
),
446 af_edge_flags_to_string( seg
->flags
) ));
455 /* Fetch number of segments. */
461 af_glyph_hints_get_num_segments( AF_GlyphHints hints
,
463 FT_Int
* num_segments
)
469 dim
= ( dimension
== 0 ) ? AF_DIMENSION_HORZ
: AF_DIMENSION_VERT
;
471 axis
= &hints
->axis
[dim
];
472 *num_segments
= axis
->num_segments
;
481 /* Fetch offset of segments into user supplied offset array. */
487 af_glyph_hints_get_segment_offset( AF_GlyphHints hints
,
492 FT_Pos
*blue_offset
)
500 return FT_THROW( Invalid_Argument
);
502 dim
= ( dimension
== 0 ) ? AF_DIMENSION_HORZ
: AF_DIMENSION_VERT
;
504 axis
= &hints
->axis
[dim
];
506 if ( idx
< 0 || idx
>= axis
->num_segments
)
507 return FT_THROW( Invalid_Argument
);
509 seg
= &axis
->segments
[idx
];
510 *offset
= ( dim
== AF_DIMENSION_HORZ
) ? seg
->first
->ox
513 *is_blue
= (FT_Bool
)( seg
->edge
->blue_edge
!= 0 );
518 *blue_offset
= seg
->edge
->blue_edge
->cur
;
529 /* Dump the array of linked edges. */
535 af_glyph_hints_dump_edges( AF_GlyphHints hints
,
541 for ( dimension
= 1; dimension
>= 0; dimension
-- )
543 AF_AxisHints axis
= &hints
->axis
[dimension
];
544 AF_Edge edges
= axis
->edges
;
545 AF_Edge limit
= edges
+ axis
->num_edges
;
548 char buf1
[16], buf2
[16];
552 * note: AF_DIMENSION_HORZ corresponds to _vertical_ edges
553 * since they have a constant X coordinate.
555 if ( dimension
== AF_DIMENSION_HORZ
)
556 AF_DUMP(( "Table of %s edges (1px=%.2fu, 10u=%.2fpx):\n",
558 65536.0 * 64.0 / hints
->x_scale
,
559 10.0 * hints
->x_scale
/ 65536.0 / 64.0 ));
561 AF_DUMP(( "Table of %s edges (1px=%.2fu, 10u=%.2fpx):\n",
563 65536.0 * 64.0 / hints
->y_scale
,
564 10.0 * hints
->y_scale
/ 65536.0 / 64.0 ));
566 if ( axis
->num_edges
)
567 AF_DUMP(( " index pos dir link serif"
568 " blue opos pos flags\n" ));
570 AF_DUMP(( " (none)\n" ));
572 for ( edge
= edges
; edge
< limit
; edge
++ )
573 AF_DUMP(( " %5d %7.2f %5s %4s %5s"
574 " %c %7.2f %7.2f %11s\n",
575 AF_INDEX_NUM( edge
, edges
),
576 (int)edge
->opos
/ 64.0,
577 af_dir_str( (AF_Direction
)edge
->dir
),
578 af_print_idx( buf1
, AF_INDEX_NUM( edge
->link
, edges
) ),
579 af_print_idx( buf2
, AF_INDEX_NUM( edge
->serif
, edges
) ),
581 edge
->blue_edge
? 'y' : 'n',
584 af_edge_flags_to_string( edge
->flags
) ));
594 #endif /* !FT_DEBUG_AUTOFIT */
597 /* Compute the direction value of a given vector. */
599 FT_LOCAL_DEF( AF_Direction
)
600 af_direction_compute( FT_Pos dx
,
603 FT_Pos ll
, ss
; /* long and short arm lengths */
604 AF_Direction dir
; /* candidate direction */
638 /* return no direction if arm lengths do not differ enough */
639 /* (value 14 is heuristic, corresponding to approx. 4.1 degrees) */
640 /* the long arm is never negative */
641 if ( ll
<= 14 * FT_ABS( ss
) )
649 af_glyph_hints_init( AF_GlyphHints hints
,
652 /* no need to initialize the embedded items */
653 FT_MEM_ZERO( hints
, sizeof ( *hints
) - sizeof ( hints
->embedded
) );
654 hints
->memory
= memory
;
659 af_glyph_hints_done( AF_GlyphHints hints
)
665 if ( !( hints
&& hints
->memory
) )
668 memory
= hints
->memory
;
671 * note that we don't need to free the segment and edge
672 * buffers since they are really within the hints->points array
674 for ( dim
= 0; dim
< AF_DIMENSION_MAX
; dim
++ )
676 AF_AxisHints axis
= &hints
->axis
[dim
];
679 axis
->num_segments
= 0;
680 axis
->max_segments
= 0;
681 if ( axis
->segments
!= axis
->embedded
.segments
)
682 FT_FREE( axis
->segments
);
686 if ( axis
->edges
!= axis
->embedded
.edges
)
687 FT_FREE( axis
->edges
);
690 if ( hints
->contours
!= hints
->embedded
.contours
)
691 FT_FREE( hints
->contours
);
692 hints
->max_contours
= 0;
693 hints
->num_contours
= 0;
695 if ( hints
->points
!= hints
->embedded
.points
)
696 FT_FREE( hints
->points
);
697 hints
->max_points
= 0;
698 hints
->num_points
= 0;
700 hints
->memory
= NULL
;
707 af_glyph_hints_rescale( AF_GlyphHints hints
,
708 AF_StyleMetrics metrics
)
710 hints
->metrics
= metrics
;
711 hints
->scaler_flags
= metrics
->scaler
.flags
;
715 /* Recompute all AF_Point in AF_GlyphHints from the definitions */
716 /* in a source outline. */
718 FT_LOCAL_DEF( FT_Error
)
719 af_glyph_hints_reload( AF_GlyphHints hints
,
720 FT_Outline
* outline
)
722 FT_Error error
= FT_Err_Ok
;
724 FT_UInt old_max
, new_max
;
725 FT_Fixed x_scale
= hints
->x_scale
;
726 FT_Fixed y_scale
= hints
->y_scale
;
727 FT_Pos x_delta
= hints
->x_delta
;
728 FT_Pos y_delta
= hints
->y_delta
;
729 FT_Memory memory
= hints
->memory
;
732 hints
->num_points
= 0;
733 hints
->num_contours
= 0;
735 hints
->axis
[0].num_segments
= 0;
736 hints
->axis
[0].num_edges
= 0;
737 hints
->axis
[1].num_segments
= 0;
738 hints
->axis
[1].num_edges
= 0;
740 /* first of all, reallocate the contours array if necessary */
741 new_max
= (FT_UInt
)outline
->n_contours
;
742 old_max
= (FT_UInt
)hints
->max_contours
;
744 if ( new_max
<= AF_CONTOURS_EMBEDDED
)
746 if ( hints
->contours
== NULL
)
748 hints
->contours
= hints
->embedded
.contours
;
749 hints
->max_contours
= AF_CONTOURS_EMBEDDED
;
752 else if ( new_max
> old_max
)
754 if ( hints
->contours
== hints
->embedded
.contours
)
755 hints
->contours
= NULL
;
757 new_max
= ( new_max
+ 3 ) & ~3U; /* round up to a multiple of 4 */
759 if ( FT_RENEW_ARRAY( hints
->contours
, old_max
, new_max
) )
762 hints
->max_contours
= (FT_Int
)new_max
;
766 * then reallocate the points arrays if necessary --
767 * note that we reserve two additional point positions, used to
768 * hint metrics appropriately
770 new_max
= (FT_UInt
)( outline
->n_points
+ 2 );
771 old_max
= (FT_UInt
)hints
->max_points
;
773 if ( new_max
<= AF_POINTS_EMBEDDED
)
775 if ( hints
->points
== NULL
)
777 hints
->points
= hints
->embedded
.points
;
778 hints
->max_points
= AF_POINTS_EMBEDDED
;
781 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 if ( FT_RENEW_ARRAY( hints
->points
, old_max
, new_max
) )
791 hints
->max_points
= (FT_Int
)new_max
;
794 hints
->num_points
= outline
->n_points
;
795 hints
->num_contours
= outline
->n_contours
;
797 /* We can't rely on the value of `FT_Outline.flags' to know the fill */
798 /* direction used for a glyph, given that some fonts are broken (e.g., */
799 /* the Arphic ones). We thus recompute it each time we need to. */
801 hints
->axis
[AF_DIMENSION_HORZ
].major_dir
= AF_DIR_UP
;
802 hints
->axis
[AF_DIMENSION_VERT
].major_dir
= AF_DIR_LEFT
;
804 if ( FT_Outline_Get_Orientation( outline
) == FT_ORIENTATION_POSTSCRIPT
)
806 hints
->axis
[AF_DIMENSION_HORZ
].major_dir
= AF_DIR_DOWN
;
807 hints
->axis
[AF_DIMENSION_VERT
].major_dir
= AF_DIR_RIGHT
;
810 hints
->x_scale
= x_scale
;
811 hints
->y_scale
= y_scale
;
812 hints
->x_delta
= x_delta
;
813 hints
->y_delta
= y_delta
;
815 hints
->xmin_delta
= 0;
816 hints
->xmax_delta
= 0;
818 points
= hints
->points
;
819 if ( hints
->num_points
== 0 )
824 AF_Point point_limit
= points
+ hints
->num_points
;
826 /* value 20 in `near_limit' is heuristic */
827 FT_UInt units_per_em
= hints
->metrics
->scaler
.face
->units_per_EM
;
828 FT_Int near_limit
= 20 * units_per_em
/ 2048;
831 /* compute coordinates & Bezier flags, next and prev */
833 FT_Vector
* vec
= outline
->points
;
834 char* tag
= outline
->tags
;
835 FT_Short endpoint
= outline
->contours
[0];
836 AF_Point end
= points
+ endpoint
;
838 FT_Int contour_index
= 0;
841 for ( point
= points
; point
< point_limit
; point
++, vec
++, tag
++ )
846 point
->in_dir
= (FT_Char
)AF_DIR_NONE
;
847 point
->out_dir
= (FT_Char
)AF_DIR_NONE
;
849 point
->fx
= (FT_Short
)vec
->x
;
850 point
->fy
= (FT_Short
)vec
->y
;
851 point
->ox
= point
->x
= FT_MulFix( vec
->x
, x_scale
) + x_delta
;
852 point
->oy
= point
->y
= FT_MulFix( vec
->y
, y_scale
) + y_delta
;
854 end
->fx
= (FT_Short
)outline
->points
[endpoint
].x
;
855 end
->fy
= (FT_Short
)outline
->points
[endpoint
].y
;
857 switch ( FT_CURVE_TAG( *tag
) )
859 case FT_CURVE_TAG_CONIC
:
860 point
->flags
= AF_FLAG_CONIC
;
862 case FT_CURVE_TAG_CUBIC
:
863 point
->flags
= AF_FLAG_CUBIC
;
866 point
->flags
= AF_FLAG_NONE
;
869 out_x
= point
->fx
- prev
->fx
;
870 out_y
= point
->fy
- prev
->fy
;
872 if ( FT_ABS( out_x
) + FT_ABS( out_y
) < near_limit
)
873 prev
->flags
|= AF_FLAG_NEAR
;
881 if ( ++contour_index
< outline
->n_contours
)
883 endpoint
= outline
->contours
[contour_index
];
884 end
= points
+ endpoint
;
891 /* set up the contours array */
893 AF_Point
* contour
= hints
->contours
;
894 AF_Point
* contour_limit
= contour
+ hints
->num_contours
;
895 short* end
= outline
->contours
;
899 for ( ; contour
< contour_limit
; contour
++, end
++ )
901 contour
[0] = points
+ idx
;
902 idx
= (short)( end
[0] + 1 );
908 * Compute directions of `in' and `out' vectors.
910 * Note that distances between points that are very near to each
911 * other are accumulated. In other words, the auto-hinter either
912 * prepends the small vectors between near points to the first
913 * non-near vector, or the sum of small vector lengths exceeds a
914 * threshold, thus `grouping' the small vectors. All intermediate
915 * points are tagged as weak; the directions are adjusted also to
916 * be equal to the accumulated one.
919 FT_Int near_limit2
= 2 * near_limit
- 1;
922 AF_Point
* contour_limit
= hints
->contours
+ hints
->num_contours
;
925 for ( contour
= hints
->contours
; contour
< contour_limit
; contour
++ )
927 AF_Point first
= *contour
;
928 AF_Point next
, prev
, curr
;
933 /* since the first point of a contour could be part of a */
934 /* series of near points, go backwards to find the first */
935 /* non-near point and adjust `first' */
940 while ( prev
!= first
)
942 out_x
= point
->fx
- prev
->fx
;
943 out_y
= point
->fy
- prev
->fy
;
946 * We use Taxicab metrics to measure the vector length.
948 * Note that the accumulated distances so far could have the
949 * opposite direction of the distance measured here. For this
950 * reason we use `near_limit2' for the comparison to get a
951 * non-near point even in the worst case.
953 if ( FT_ABS( out_x
) + FT_ABS( out_y
) >= near_limit2
)
960 /* adjust first point */
963 /* now loop over all points of the contour to get */
964 /* `in' and `out' vector directions */
969 * We abuse the `u' and `v' fields to store index deltas to the
970 * next and previous non-near point, respectively.
972 * To avoid problems with not having non-near points, we point to
973 * `first' by default as the next non-near point.
976 curr
->u
= (FT_Pos
)( first
- curr
);
985 AF_Direction out_dir
;
991 out_x
+= next
->fx
- point
->fx
;
992 out_y
+= next
->fy
- point
->fy
;
994 if ( FT_ABS( out_x
) + FT_ABS( out_y
) < near_limit
)
996 next
->flags
|= AF_FLAG_WEAK_INTERPOLATION
;
1000 curr
->u
= (FT_Pos
)( next
- curr
);
1003 out_dir
= af_direction_compute( out_x
, out_y
);
1005 /* adjust directions for all points inbetween; */
1006 /* the loop also updates position of `curr' */
1007 curr
->out_dir
= (FT_Char
)out_dir
;
1008 for ( curr
= curr
->next
; curr
!= next
; curr
= curr
->next
)
1010 curr
->in_dir
= (FT_Char
)out_dir
;
1011 curr
->out_dir
= (FT_Char
)out_dir
;
1013 next
->in_dir
= (FT_Char
)out_dir
;
1015 curr
->u
= (FT_Pos
)( first
- curr
);
1016 first
->v
= -curr
->u
;
1021 } while ( next
!= first
);
1025 * The next step is to `simplify' an outline's topology so that we
1026 * can identify local extrema more reliably: A series of
1027 * non-horizontal or non-vertical vectors pointing into the same
1028 * quadrant are handled as a single, long vector. From a
1029 * topological point of the view, the intermediate points are of no
1030 * interest and thus tagged as weak.
1033 for ( point
= points
; point
< point_limit
; point
++ )
1035 if ( point
->flags
& AF_FLAG_WEAK_INTERPOLATION
)
1038 if ( point
->in_dir
== AF_DIR_NONE
&&
1039 point
->out_dir
== AF_DIR_NONE
)
1041 /* check whether both vectors point into the same quadrant */
1044 FT_Pos out_x
, out_y
;
1046 AF_Point next_u
= point
+ point
->u
;
1047 AF_Point prev_v
= point
+ point
->v
;
1050 in_x
= point
->fx
- prev_v
->fx
;
1051 in_y
= point
->fy
- prev_v
->fy
;
1053 out_x
= next_u
->fx
- point
->fx
;
1054 out_y
= next_u
->fy
- point
->fy
;
1056 if ( ( in_x
^ out_x
) >= 0 && ( in_y
^ out_y
) >= 0 )
1058 /* yes, so tag current point as weak */
1059 /* and update index deltas */
1061 point
->flags
|= AF_FLAG_WEAK_INTERPOLATION
;
1063 prev_v
->u
= (FT_Pos
)( next_u
- prev_v
);
1064 next_u
->v
= -prev_v
->u
;
1070 * Finally, check for remaining weak points. Everything else not
1071 * collected in edges so far is then implicitly classified as strong
1075 for ( point
= points
; point
< point_limit
; point
++ )
1077 if ( point
->flags
& AF_FLAG_WEAK_INTERPOLATION
)
1080 if ( point
->flags
& AF_FLAG_CONTROL
)
1082 /* control points are always weak */
1084 point
->flags
|= AF_FLAG_WEAK_INTERPOLATION
;
1086 else if ( point
->out_dir
== point
->in_dir
)
1088 if ( point
->out_dir
!= AF_DIR_NONE
)
1090 /* current point lies on a horizontal or */
1091 /* vertical segment (but doesn't start or end it) */
1096 AF_Point next_u
= point
+ point
->u
;
1097 AF_Point prev_v
= point
+ point
->v
;
1100 if ( ft_corner_is_flat( point
->fx
- prev_v
->fx
,
1101 point
->fy
- prev_v
->fy
,
1102 next_u
->fx
- point
->fx
,
1103 next_u
->fy
- point
->fy
) )
1105 /* either the `in' or the `out' vector is much more */
1106 /* dominant than the other one, so tag current point */
1107 /* as weak and update index deltas */
1109 prev_v
->u
= (FT_Pos
)( next_u
- prev_v
);
1110 next_u
->v
= -prev_v
->u
;
1116 else if ( point
->in_dir
== -point
->out_dir
)
1118 /* current point forms a spike */
1130 /* Store the hinted outline in an FT_Outline structure. */
1132 FT_LOCAL_DEF( void )
1133 af_glyph_hints_save( AF_GlyphHints hints
,
1134 FT_Outline
* outline
)
1136 AF_Point point
= hints
->points
;
1137 AF_Point limit
= point
+ hints
->num_points
;
1138 FT_Vector
* vec
= outline
->points
;
1139 char* tag
= outline
->tags
;
1142 for ( ; point
< limit
; point
++, vec
++, tag
++ )
1147 if ( point
->flags
& AF_FLAG_CONIC
)
1148 tag
[0] = FT_CURVE_TAG_CONIC
;
1149 else if ( point
->flags
& AF_FLAG_CUBIC
)
1150 tag
[0] = FT_CURVE_TAG_CUBIC
;
1152 tag
[0] = FT_CURVE_TAG_ON
;
1157 /****************************************************************
1159 * EDGE POINT GRID-FITTING
1161 ****************************************************************/
1164 /* Align all points of an edge to the same coordinate value, */
1165 /* either horizontally or vertically. */
1167 FT_LOCAL_DEF( void )
1168 af_glyph_hints_align_edge_points( AF_GlyphHints hints
,
1171 AF_AxisHints axis
= & hints
->axis
[dim
];
1172 AF_Segment segments
= axis
->segments
;
1173 AF_Segment segment_limit
= segments
+ axis
->num_segments
;
1177 if ( dim
== AF_DIMENSION_HORZ
)
1179 for ( seg
= segments
; seg
< segment_limit
; seg
++ )
1181 AF_Edge edge
= seg
->edge
;
1182 AF_Point point
, first
, last
;
1193 point
->x
= edge
->pos
;
1194 point
->flags
|= AF_FLAG_TOUCH_X
;
1196 if ( point
== last
)
1199 point
= point
->next
;
1205 for ( seg
= segments
; seg
< segment_limit
; seg
++ )
1207 AF_Edge edge
= seg
->edge
;
1208 AF_Point point
, first
, last
;
1219 point
->y
= edge
->pos
;
1220 point
->flags
|= AF_FLAG_TOUCH_Y
;
1222 if ( point
== last
)
1225 point
= point
->next
;
1232 /****************************************************************
1234 * STRONG POINT INTERPOLATION
1236 ****************************************************************/
1239 /* Hint the strong points -- this is equivalent to the TrueType `IP' */
1240 /* hinting instruction. */
1242 FT_LOCAL_DEF( void )
1243 af_glyph_hints_align_strong_points( AF_GlyphHints hints
,
1246 AF_Point points
= hints
->points
;
1247 AF_Point point_limit
= points
+ hints
->num_points
;
1248 AF_AxisHints axis
= &hints
->axis
[dim
];
1249 AF_Edge edges
= axis
->edges
;
1250 AF_Edge edge_limit
= edges
+ axis
->num_edges
;
1254 if ( dim
== AF_DIMENSION_HORZ
)
1255 touch_flag
= AF_FLAG_TOUCH_X
;
1257 touch_flag
= AF_FLAG_TOUCH_Y
;
1259 if ( edges
< edge_limit
)
1265 for ( point
= points
; point
< point_limit
; point
++ )
1267 FT_Pos u
, ou
, fu
; /* point position */
1271 if ( point
->flags
& touch_flag
)
1274 /* if this point is candidate to weak interpolation, we */
1275 /* interpolate it after all strong points have been processed */
1277 if ( ( point
->flags
& AF_FLAG_WEAK_INTERPOLATION
) )
1280 if ( dim
== AF_DIMENSION_VERT
)
1293 /* is the point before the first edge? */
1295 delta
= edge
->fpos
- u
;
1298 u
= edge
->pos
- ( edge
->opos
- ou
);
1302 /* is the point after the last edge? */
1303 edge
= edge_limit
- 1;
1304 delta
= u
- edge
->fpos
;
1307 u
= edge
->pos
+ ( ou
- edge
->opos
);
1312 FT_PtrDist min
, max
, mid
;
1316 /* find enclosing edges */
1318 max
= edge_limit
- edges
;
1321 /* for a small number of edges, a linear search is better */
1327 for ( nn
= 0; nn
< max
; nn
++ )
1328 if ( edges
[nn
].fpos
>= u
)
1331 if ( edges
[nn
].fpos
== u
)
1342 mid
= ( max
+ min
) >> 1;
1348 else if ( u
> fpos
)
1352 /* we are on the edge */
1358 /* point is not on an edge */
1360 AF_Edge before
= edges
+ min
- 1;
1361 AF_Edge after
= edges
+ min
+ 0;
1364 /* assert( before && after && before != after ) */
1365 if ( before
->scale
== 0 )
1366 before
->scale
= FT_DivFix( after
->pos
- before
->pos
,
1367 after
->fpos
- before
->fpos
);
1369 u
= before
->pos
+ FT_MulFix( fu
- before
->fpos
,
1375 /* save the point position */
1376 if ( dim
== AF_DIMENSION_HORZ
)
1381 point
->flags
|= touch_flag
;
1387 /****************************************************************
1389 * WEAK POINT INTERPOLATION
1391 ****************************************************************/
1394 /* Shift the original coordinates of all points between `p1' and */
1395 /* `p2' to get hinted coordinates, using the same difference as */
1396 /* given by `ref'. */
1399 af_iup_shift( AF_Point p1
,
1404 FT_Pos delta
= ref
->u
- ref
->v
;
1410 for ( p
= p1
; p
< ref
; p
++ )
1411 p
->u
= p
->v
+ delta
;
1413 for ( p
= ref
+ 1; p
<= p2
; p
++ )
1414 p
->u
= p
->v
+ delta
;
1418 /* Interpolate the original coordinates of all points between `p1' and */
1419 /* `p2' to get hinted coordinates, using `ref1' and `ref2' as the */
1420 /* reference points. The `u' and `v' members are the current and */
1421 /* original coordinate values, respectively. */
1423 /* Details can be found in the TrueType bytecode specification. */
1426 af_iup_interp( AF_Point p1
,
1432 FT_Pos u
, v1
, v2
, u1
, u2
, d1
, d2
;
1438 if ( ref1
->v
> ref2
->v
)
1452 if ( u1
== u2
|| v1
== v2
)
1454 for ( p
= p1
; p
<= p2
; p
++ )
1470 FT_Fixed scale
= FT_DivFix( u2
- u1
, v2
- v1
);
1473 for ( p
= p1
; p
<= p2
; p
++ )
1482 u
= u1
+ FT_MulFix( u
- v1
, scale
);
1490 /* Hint the weak points -- this is equivalent to the TrueType `IUP' */
1491 /* hinting instruction. */
1493 FT_LOCAL_DEF( void )
1494 af_glyph_hints_align_weak_points( AF_GlyphHints hints
,
1497 AF_Point points
= hints
->points
;
1498 AF_Point point_limit
= points
+ hints
->num_points
;
1499 AF_Point
* contour
= hints
->contours
;
1500 AF_Point
* contour_limit
= contour
+ hints
->num_contours
;
1504 AF_Point first_point
;
1507 /* PASS 1: Move segment points to edge positions */
1509 if ( dim
== AF_DIMENSION_HORZ
)
1511 touch_flag
= AF_FLAG_TOUCH_X
;
1513 for ( point
= points
; point
< point_limit
; point
++ )
1515 point
->u
= point
->x
;
1516 point
->v
= point
->ox
;
1521 touch_flag
= AF_FLAG_TOUCH_Y
;
1523 for ( point
= points
; point
< point_limit
; point
++ )
1525 point
->u
= point
->y
;
1526 point
->v
= point
->oy
;
1530 for ( ; contour
< contour_limit
; contour
++ )
1532 AF_Point first_touched
, last_touched
;
1536 end_point
= point
->prev
;
1537 first_point
= point
;
1539 /* find first touched point */
1542 if ( point
> end_point
) /* no touched point in contour */
1545 if ( point
->flags
& touch_flag
)
1551 first_touched
= point
;
1555 FT_ASSERT( point
<= end_point
&&
1556 ( point
->flags
& touch_flag
) != 0 );
1558 /* skip any touched neighbours */
1559 while ( point
< end_point
&&
1560 ( point
[1].flags
& touch_flag
) != 0 )
1563 last_touched
= point
;
1565 /* find the next touched point, if any */
1569 if ( point
> end_point
)
1572 if ( ( point
->flags
& touch_flag
) != 0 )
1578 /* interpolate between last_touched and point */
1579 af_iup_interp( last_touched
+ 1, point
- 1,
1580 last_touched
, point
);
1584 /* special case: only one point was touched */
1585 if ( last_touched
== first_touched
)
1586 af_iup_shift( first_point
, end_point
, first_touched
);
1588 else /* interpolate the last part */
1590 if ( last_touched
< end_point
)
1591 af_iup_interp( last_touched
+ 1, end_point
,
1592 last_touched
, first_touched
);
1594 if ( first_touched
> points
)
1595 af_iup_interp( first_point
, first_touched
- 1,
1596 last_touched
, first_touched
);
1603 /* now save the interpolated values back to x/y */
1604 if ( dim
== AF_DIMENSION_HORZ
)
1606 for ( point
= points
; point
< point_limit
; point
++ )
1607 point
->x
= point
->u
;
1611 for ( point
= points
; point
< point_limit
; point
++ )
1612 point
->y
= point
->u
;
1617 #ifdef AF_CONFIG_OPTION_USE_WARPER
1619 /* Apply (small) warp scale and warp delta for given dimension. */
1621 FT_LOCAL_DEF( void )
1622 af_glyph_hints_scale_dim( AF_GlyphHints hints
,
1627 AF_Point points
= hints
->points
;
1628 AF_Point points_limit
= points
+ hints
->num_points
;
1632 if ( dim
== AF_DIMENSION_HORZ
)
1634 for ( point
= points
; point
< points_limit
; point
++ )
1635 point
->x
= FT_MulFix( point
->fx
, scale
) + delta
;
1639 for ( point
= points
; point
< points_limit
; point
++ )
1640 point
->y
= FT_MulFix( point
->fy
, scale
) + delta
;
1644 #endif /* AF_CONFIG_OPTION_USE_WARPER */