1 /***************************************************************************/
5 /* Routines used to load and analyze a given glyph before hinting */
8 /* Copyright 2000-2001, 2002 Catharon Productions Inc. */
9 /* Author: David Turner */
11 /* This file is part of the Catharon Typography Project and shall only */
12 /* be used, modified, and distributed under the terms of the Catharon */
13 /* Open Source License that should come with this file under the name */
14 /* `CatharonLicense.txt'. By continuing to use, modify, or distribute */
15 /* this file you indicate that you have read the license and */
16 /* understand and accept it fully. */
18 /* Note that this license is compatible with the FreeType license. */
20 /***************************************************************************/
35 ah_dump_edges( AH_Outline outline )
43 edges = outline->horz_edges;
44 edge_limit = edges + outline->num_hedges;
45 segments = outline->horz_segments;
47 for ( dimension = 1; dimension >= 0; dimension-- )
52 printf ( "Table of %s edges:\n",
53 !dimension ? "vertical" : "horizontal" );
54 printf ( " [ index | pos | dir | link |"
55 " serif | blue | opos | pos ]\n" );
57 for ( edge = edges; edge < edge_limit; edge++ )
59 printf ( " [ %5d | %4d | %5s | %4d | %5d | %c | %5.2f | %5.2f ]\n",
62 edge->dir == AH_DIR_UP
64 : ( edge->dir == AH_DIR_DOWN
66 : ( edge->dir == AH_DIR_LEFT
68 : ( edge->dir == AH_DIR_RIGHT
71 edge->link ? ( edge->link - edges ) : -1,
72 edge->serif ? ( edge->serif - edges ) : -1,
73 edge->blue_edge ? 'y' : 'n',
78 edges = outline->vert_edges;
79 edge_limit = edges + outline->num_vedges;
80 segments = outline->vert_segments;
85 /* A function used to dump the array of linked segments */
87 ah_dump_segments( AH_Outline outline )
90 AH_Segment segment_limit;
95 points = outline->points;
96 segments = outline->horz_segments;
97 segment_limit = segments + outline->num_hsegments;
99 for ( dimension = 1; dimension >= 0; dimension-- )
104 printf ( "Table of %s segments:\n",
105 !dimension ? "vertical" : "horizontal" );
106 printf ( " [ index | pos | dir | link | serif |"
107 " numl | first | start ]\n" );
109 for ( seg = segments; seg < segment_limit; seg++ )
111 printf ( " [ %5d | %4d | %5s | %4d | %5d | %4d | %5d | %5d ]\n",
114 seg->dir == AH_DIR_UP
116 : ( seg->dir == AH_DIR_DOWN
118 : ( seg->dir == AH_DIR_LEFT
120 : ( seg->dir == AH_DIR_RIGHT
123 seg->link ? (seg->link-segments) : -1,
124 seg->serif ? (seg->serif-segments) : -1,
125 (int)seg->num_linked,
127 seg->last - points );
130 segments = outline->vert_segments;
131 segment_limit = segments + outline->num_vsegments;
135 #endif /* AH_DEBUG */
138 /* compute the direction value of a given vector.. */
140 ah_compute_direction( FT_Pos dx,
144 FT_Pos ax = ABS( dx );
145 FT_Pos ay = ABS( dy );
150 /* test for vertical direction */
153 dir = dy > 0 ? AH_DIR_UP : AH_DIR_DOWN;
155 /* test for horizontal direction */
156 else if ( ay * 12 < ax )
158 dir = dx > 0 ? AH_DIR_RIGHT : AH_DIR_LEFT;
165 /* this function is used by ah_get_orientation (see below) to test */
166 /* the fill direction of a given bbox extrema */
168 ah_test_extrema( FT_Outline* outline,
171 FT_Vector *prev, *cur, *next;
173 FT_Int first, last, c;
177 /* we need to compute the `previous' and `next' point */
178 /* for these extrema */
179 cur = outline->points + n;
184 for ( c = 0; c < outline->n_contours; c++ )
186 last = outline->contours[c];
189 prev = outline->points + last;
192 next = outline->points + first;
197 product = FT_MulDiv( cur->x - prev->x, /* in.x */
198 next->y - cur->y, /* out.y */
201 FT_MulDiv( cur->y - prev->y, /* in.y */
202 next->x - cur->x, /* out.x */
207 retval = product > 0 ? 2 : 1;
213 /* Compute the orientation of path filling. It differs between TrueType */
214 /* and Type1 formats. We could use the `FT_OUTLINE_REVERSE_FILL' flag, */
215 /* but it is better to re-compute it directly (it seems that this flag */
216 /* isn't correctly set for some weird composite glyphs currently). */
218 /* We do this by computing bounding box points, and computing their */
221 /* The function returns either 1 or -1. */
224 ah_get_orientation( FT_Outline* outline )
227 FT_Int indices_xMin, indices_yMin, indices_xMax, indices_yMax;
236 box.xMin = box.yMin = 32767L;
237 box.xMax = box.yMax = -32768L;
240 if ( outline->n_contours < 1 )
243 last = outline->contours[outline->n_contours - 1];
245 for ( n = 0; n <= last; n++ )
250 x = outline->points[n].x;
262 y = outline->points[n].y;
275 /* test orientation of the xmin */
276 n = ah_test_extrema( outline, indices_xMin );
280 n = ah_test_extrema( outline, indices_yMin );
284 n = ah_test_extrema( outline, indices_xMax );
288 n = ah_test_extrema( outline, indices_yMax );
297 /*************************************************************************/
303 /* Creates a new and empty AH_OutlineRec object. */
305 FT_LOCAL_DEF( FT_Error )
306 ah_outline_new( FT_Memory memory,
307 AH_Outline* aoutline )
313 if ( !FT_NEW( outline ) )
315 outline->memory = memory;
323 /*************************************************************************/
326 /* ah_outline_done */
329 /* Destroys a given AH_OutlineRec object. */
332 ah_outline_done( AH_Outline outline )
334 FT_Memory memory = outline->memory;
337 FT_FREE( outline->horz_edges );
338 FT_FREE( outline->horz_segments );
339 FT_FREE( outline->contours );
340 FT_FREE( outline->points );
346 /*************************************************************************/
349 /* ah_outline_save */
352 /* Saves the contents of a given AH_OutlineRec object into a face's */
356 ah_outline_save( AH_Outline outline,
359 AH_Point point = outline->points;
360 AH_Point point_limit = point + outline->num_points;
361 FT_Vector* vec = gloader->current.outline.points;
362 char* tag = gloader->current.outline.tags;
365 /* we assume that the glyph loader has already been checked for storage */
366 for ( ; point < point_limit; point++, vec++, tag++ )
371 if ( point->flags & AH_FLAG_CONIC )
372 tag[0] = FT_CURVE_TAG_CONIC;
373 else if ( point->flags & AH_FLAG_CUBIC )
374 tag[0] = FT_CURVE_TAG_CUBIC;
376 tag[0] = FT_CURVE_TAG_ON;
381 /*************************************************************************/
384 /* ah_outline_load */
387 /* Loads an unscaled outline from a glyph slot into an AH_OutlineRec */
390 FT_LOCAL_DEF( FT_Error )
391 ah_outline_load( AH_Outline outline,
396 FT_Memory memory = outline->memory;
397 FT_Error error = AH_Err_Ok;
398 FT_Outline* source = &face->glyph->outline;
399 FT_Int num_points = source->n_points;
400 FT_Int num_contours = source->n_contours;
404 /* check arguments */
407 face->glyph->format != FT_GLYPH_FORMAT_OUTLINE )
408 return AH_Err_Invalid_Argument;
410 /* first of all, reallocate the contours array if necessary */
411 if ( num_contours > outline->max_contours )
413 FT_Int new_contours = ( num_contours + 3 ) & -4;
416 if ( FT_RENEW_ARRAY( outline->contours,
417 outline->max_contours,
421 outline->max_contours = new_contours;
424 /* then, reallocate the points, segments & edges arrays if needed -- */
425 /* note that we reserved two additional point positions, used to */
426 /* hint metrics appropriately */
428 if ( num_points + 2 > outline->max_points )
430 FT_Int news = ( num_points + 2 + 7 ) & -8;
431 FT_Int max = outline->max_points;
434 if ( FT_RENEW_ARRAY( outline->points, max, news ) ||
435 FT_RENEW_ARRAY( outline->horz_edges, max * 2, news * 2 ) ||
436 FT_RENEW_ARRAY( outline->horz_segments, max * 2, news * 2 ) )
439 /* readjust some pointers */
440 outline->vert_edges = outline->horz_edges + news;
441 outline->vert_segments = outline->horz_segments + news;
442 outline->max_points = news;
445 outline->num_points = num_points;
446 outline->num_contours = num_contours;
448 outline->num_hedges = 0;
449 outline->num_vedges = 0;
450 outline->num_hsegments = 0;
451 outline->num_vsegments = 0;
453 /* We can't rely on the value of `FT_Outline.flags' to know the fill */
454 /* direction used for a glyph, given that some fonts are broken (e.g. */
455 /* the Arphic ones). We thus recompute it each time we need to. */
457 outline->vert_major_dir = AH_DIR_UP;
458 outline->horz_major_dir = AH_DIR_LEFT;
460 if ( ah_get_orientation( source ) > 1 )
462 outline->vert_major_dir = AH_DIR_DOWN;
463 outline->horz_major_dir = AH_DIR_RIGHT;
466 outline->x_scale = x_scale;
467 outline->y_scale = y_scale;
469 points = outline->points;
470 if ( outline->num_points == 0 )
474 /* do one thing at a time -- it is easier to understand, and */
475 /* the code is clearer */
477 AH_Point point_limit = points + outline->num_points;
480 /* compute coordinates */
482 FT_Vector* vec = source->points;
485 for ( point = points; point < point_limit; vec++, point++ )
489 point->ox = point->x = FT_MulFix( vec->x, x_scale );
490 point->oy = point->y = FT_MulFix( vec->y, y_scale );
496 /* compute Bezier flags */
498 char* tag = source->tags;
501 for ( point = points; point < point_limit; point++, tag++ )
503 switch ( FT_CURVE_TAG( *tag ) )
505 case FT_CURVE_TAG_CONIC:
506 point->flags = AH_FLAG_CONIC; break;
507 case FT_CURVE_TAG_CUBIC:
508 point->flags = AH_FLAG_CUBIC; break;
515 /* compute `next' and `prev' */
517 FT_Int contour_index;
526 end = points + source->contours[0];
529 for ( point = points; point < point_limit; point++ )
534 point->next = point + 1;
541 if ( point + 1 < point_limit )
543 end = points + source->contours[contour_index];
551 /* set-up the contours array */
553 AH_Point* contour = outline->contours;
554 AH_Point* contour_limit = contour + outline->num_contours;
555 short* end = source->contours;
559 for ( ; contour < contour_limit; contour++, end++ )
561 contour[0] = points + idx;
562 idx = (short)( end[0] + 1 );
566 /* compute directions of in & out vectors */
568 for ( point = points; point < point_limit; point++ )
572 FT_Vector ivec, ovec;
576 ivec.x = point->fx - prev->fx;
577 ivec.y = point->fy - prev->fy;
579 point->in_dir = ah_compute_direction( ivec.x, ivec.y );
582 ovec.x = next->fx - point->fx;
583 ovec.y = next->fy - point->fy;
585 point->out_dir = ah_compute_direction( ovec.x, ovec.y );
587 #ifndef AH_OPTION_NO_WEAK_INTERPOLATION
588 if ( point->flags & (AH_FLAG_CONIC | AH_FLAG_CUBIC) )
591 point->flags |= AH_FLAG_WEAK_INTERPOLATION;
593 else if ( point->out_dir == point->in_dir )
595 AH_Angle angle_in, angle_out, delta;
598 if ( point->out_dir != AH_DIR_NONE )
601 angle_in = ah_angle( &ivec );
602 angle_out = ah_angle( &ovec );
603 delta = angle_in - angle_out;
606 delta = AH_2PI - delta;
614 else if ( point->in_dir == -point->out_dir )
627 ah_setup_uv( AH_Outline outline,
630 AH_Point point = outline->points;
631 AH_Point point_limit = point + outline->num_points;
634 for ( ; point < point_limit; point++ )
680 /* compute all inflex points in a given glyph */
682 ah_outline_compute_inflections( AH_Outline outline )
684 AH_Point* contour = outline->contours;
685 AH_Point* contour_limit = contour + outline->num_contours;
688 /* load original coordinates in (u,v) */
689 ah_setup_uv( outline, AH_UV_FXY );
691 /* do each contour separately */
692 for ( ; contour < contour_limit; contour++ )
695 AH_Point point = contour[0];
696 AH_Point first = point;
697 AH_Point start = point;
698 AH_Point end = point;
701 AH_Angle angle_in, angle_seg, angle_out;
702 AH_Angle diff_in, diff_out;
706 /* compute first segment in contour */
716 } while ( end->u == first->u && end->v == first->v );
718 vec.x = end->u - start->u;
719 vec.y = end->v - start->v;
720 angle_seg = ah_angle( &vec );
722 /* extend the segment start whenever possible */
729 before = before->prev;
730 if ( before == first )
733 } while ( before->u == start->u && before->v == start->v );
735 vec.x = start->u - before->u;
736 vec.y = start->v - before->v;
737 angle_in = ah_angle( &vec );
739 } while ( angle_in == angle_seg );
742 diff_in = ah_angle_diff( angle_in, angle_seg );
744 /* now, process all segments in the contour */
747 /* first, extend current segment's end whenever possible */
755 if ( after == first )
758 } while ( end->u == after->u && end->v == after->v );
760 vec.x = after->u - end->u;
761 vec.y = after->v - end->v;
762 angle_out = ah_angle( &vec );
764 } while ( angle_out == angle_seg );
766 diff_out = ah_angle_diff( angle_seg, angle_out );
768 if ( ( diff_in ^ diff_out ) < 0 )
770 /* diff_in and diff_out have different signs, we have */
771 /* inflection points here... */
775 start->flags |= AH_FLAG_INFLECTION;
778 } while ( start != end );
780 start->flags |= AH_FLAG_INFLECTION;
785 angle_seg = angle_out;
788 } while ( !finished );
797 ah_outline_compute_segments( AH_Outline outline )
801 FT_Int* p_num_segments;
802 AH_Direction segment_dir;
803 AH_Direction major_dir;
806 segments = outline->horz_segments;
807 p_num_segments = &outline->num_hsegments;
808 major_dir = AH_DIR_RIGHT; /* This value must be positive! */
809 segment_dir = major_dir;
811 /* set up (u,v) in each point */
812 ah_setup_uv( outline, AH_UV_FYX );
814 for ( dimension = 1; dimension >= 0; dimension-- )
816 AH_Point* contour = outline->contours;
817 AH_Point* contour_limit = contour + outline->num_contours;
818 AH_Segment segment = segments;
819 FT_Int num_segments = 0;
821 #ifdef AH_HINT_METRICS
822 AH_Point min_point = 0;
823 AH_Point max_point = 0;
824 FT_Pos min_coord = 32000;
825 FT_Pos max_coord = -32000;
829 /* do each contour separately */
830 for ( ; contour < contour_limit; contour++ )
832 AH_Point point = contour[0];
833 AH_Point last = point->prev;
835 FT_Pos min_pos = +32000; /* minimum segment pos != min_coord */
836 FT_Pos max_pos = -32000; /* maximum segment pos != max_coord */
840 #ifdef AH_HINT_METRICS
841 if ( point->u < min_coord )
843 min_coord = point->u;
846 if ( point->u > max_coord )
848 max_coord = point->u;
853 if ( point == last ) /* skip singletons -- just in case? */
856 if ( ABS( last->out_dir ) == major_dir &&
857 ABS( point->out_dir ) == major_dir )
859 /* we are already on an edge, try to locate its start */
865 if ( ABS( point->out_dir ) != major_dir )
891 if ( point->out_dir != segment_dir || point == last )
893 /* we are just leaving an edge; record a new segment! */
894 segment->last = point;
895 segment->pos = ( min_pos + max_pos ) >> 1;
897 /* a segment is round if either its first or last point */
898 /* is a control point */
899 if ( ( segment->first->flags | point->flags ) &
901 segment->flags |= AH_EDGE_ROUND;
903 /* compute segment size */
904 min_pos = max_pos = point->v;
906 v = segment->first->v;
912 segment->min_coord = min_pos;
913 segment->max_coord = max_pos;
922 /* now exit if we are at the start/end point */
930 if ( !on_edge && ABS( point->out_dir ) == major_dir )
932 /* this is the start of a new segment! */
933 segment_dir = point->out_dir;
935 /* clear all segment fields */
938 segment->dir = segment_dir;
939 segment->flags = AH_EDGE_NORMAL;
940 min_pos = max_pos = point->u;
941 segment->first = point;
942 segment->last = point;
943 segment->contour = contour;
946 #ifdef AH_HINT_METRICS
947 if ( point == max_point )
950 if ( point == min_point )
960 #ifdef AH_HINT_METRICS
961 /* we need to ensure that there are edges on the left-most and */
962 /* right-most points of the glyph in order to hint the metrics; */
963 /* we do this by inserting fake segments when needed */
964 if ( dimension == 0 )
966 AH_Point point = outline->points;
967 AH_Point point_limit = point + outline->num_points;
969 FT_Pos min_pos = 32000;
970 FT_Pos max_pos = -32000;
976 /* compute minimum and maximum points */
977 for ( ; point < point_limit; point++ )
979 FT_Pos x = point->fx;
994 /* insert minimum segment */
997 /* clear all segment fields */
1000 segment->dir = segment_dir;
1001 segment->flags = AH_EDGE_NORMAL;
1002 segment->first = min_point;
1003 segment->last = min_point;
1004 segment->pos = min_pos;
1010 /* insert maximum segment */
1013 /* clear all segment fields */
1016 segment->dir = segment_dir;
1017 segment->flags = AH_EDGE_NORMAL;
1018 segment->first = max_point;
1019 segment->last = max_point;
1020 segment->pos = max_pos;
1026 #endif /* AH_HINT_METRICS */
1028 *p_num_segments = num_segments;
1030 segments = outline->vert_segments;
1031 major_dir = AH_DIR_UP;
1032 p_num_segments = &outline->num_vsegments;
1033 ah_setup_uv( outline, AH_UV_FXY );
1038 FT_LOCAL_DEF( void )
1039 ah_outline_link_segments( AH_Outline outline )
1041 AH_Segment segments;
1042 AH_Segment segment_limit;
1046 ah_setup_uv( outline, AH_UV_FYX );
1048 segments = outline->horz_segments;
1049 segment_limit = segments + outline->num_hsegments;
1051 for ( dimension = 1; dimension >= 0; dimension-- )
1057 /* now compare each segment to the others */
1058 for ( seg1 = segments; seg1 < segment_limit; seg1++ )
1061 AH_Segment best_segment;
1064 /* the fake segments are introduced to hint the metrics -- */
1065 /* we must never link them to anything */
1066 if ( seg1->first == seg1->last )
1069 best_segment = seg1->link;
1071 best_score = seg1->score;
1075 for ( seg2 = segments; seg2 < segment_limit; seg2++ )
1076 if ( seg1 != seg2 && seg1->dir + seg2->dir == 0 )
1078 FT_Pos pos1 = seg1->pos;
1079 FT_Pos pos2 = seg2->pos;
1084 /* check that the segments are correctly oriented and */
1085 /* positioned to form a black distance */
1087 is_dir = (FT_Bool)( seg1->dir == outline->horz_major_dir ||
1088 seg1->dir == outline->vert_major_dir );
1089 is_pos = (FT_Bool)( pos1 > pos2 );
1091 if ( pos1 == pos2 || !(is_dir ^ is_pos) )
1095 FT_Pos min = seg1->min_coord;
1096 FT_Pos max = seg1->max_coord;
1097 FT_Pos len, dist, score;
1100 if ( min < seg2->min_coord )
1101 min = seg2->min_coord;
1103 if ( max > seg2->max_coord )
1104 max = seg2->max_coord;
1109 dist = seg2->pos - seg1->pos;
1113 score = dist + 3000 / len;
1115 if ( score < best_score )
1118 best_segment = seg2;
1126 seg1->link = best_segment;
1127 seg1->score = best_score;
1129 best_segment->num_linked++;
1134 /* now, compute the `serif' segments */
1135 for ( seg1 = segments; seg1 < segment_limit; seg1++ )
1139 if ( seg2 && seg2->link != seg1 )
1142 seg1->serif = seg2->link;
1146 ah_setup_uv( outline, AH_UV_FXY );
1148 segments = outline->vert_segments;
1149 segment_limit = segments + outline->num_vsegments;
1155 ah_outline_compute_edges( AH_Outline outline )
1158 AH_Segment segments;
1159 AH_Segment segment_limit;
1160 AH_Direction up_dir;
1161 FT_Int* p_num_edges;
1164 FT_Pos edge_distance_threshold;
1167 edges = outline->horz_edges;
1168 segments = outline->horz_segments;
1169 segment_limit = segments + outline->num_hsegments;
1170 p_num_edges = &outline->num_hedges;
1171 up_dir = AH_DIR_RIGHT;
1172 scale = outline->y_scale;
1174 for ( dimension = 1; dimension >= 0; dimension-- )
1177 AH_Edge edge_limit; /* really == edge + num_edges */
1181 /*********************************************************************/
1183 /* We will begin by generating a sorted table of edges for the */
1184 /* current direction. To do so, we simply scan each segment and try */
1185 /* to find an edge in our table that corresponds to its position. */
1187 /* If no edge is found, we create and insert a new edge in the */
1188 /* sorted table. Otherwise, we simply add the segment to the edge's */
1189 /* list which will be processed in the second step to compute the */
1190 /* edge's properties. */
1192 /* Note that the edges table is sorted along the segment/edge */
1195 /*********************************************************************/
1197 edge_distance_threshold = FT_MulFix( outline->edge_distance_threshold,
1199 if ( edge_distance_threshold > 64 / 4 )
1200 edge_distance_threshold = 64 / 4;
1203 for ( seg = segments; seg < segment_limit; seg++ )
1208 /* look for an edge corresponding to the segment */
1209 for ( edge = edges; edge < edge_limit; edge++ )
1214 dist = seg->pos - edge->fpos;
1218 dist = FT_MulFix( dist, scale );
1219 if ( dist < edge_distance_threshold )
1228 /* insert a new edge in the list and */
1229 /* sort according to the position */
1230 while ( edge > edges && edge[-1].fpos > seg->pos )
1237 /* clear all edge fields */
1238 FT_MEM_ZERO( edge, sizeof ( *edge ) );
1240 /* add the segment to the new edge's list */
1243 edge->fpos = seg->pos;
1244 edge->opos = edge->pos = FT_MulFix( seg->pos, scale );
1245 seg->edge_next = seg;
1249 /* if an edge was found, simply add the segment to the edge's */
1251 seg->edge_next = edge->first;
1252 edge->last->edge_next = seg;
1257 *p_num_edges = (FT_Int)( edge_limit - edges );
1260 /*********************************************************************/
1262 /* Good, we will now compute each edge's properties according to */
1263 /* segments found on its position. Basically, these are: */
1265 /* - edge's main direction */
1266 /* - stem edge, serif edge or both (which defaults to stem then) */
1267 /* - rounded edge, straigth or both (which defaults to straight) */
1268 /* - link for edge */
1270 /*********************************************************************/
1272 /* first of all, set the `edge' field in each segment -- this is */
1273 /* required in order to compute edge links */
1274 for ( edge = edges; edge < edge_limit; edge++ )
1281 seg = seg->edge_next;
1283 while ( seg != edge->first );
1286 /* now, compute each edge properties */
1287 for ( edge = edges; edge < edge_limit; edge++ )
1289 FT_Int is_round = 0; /* does it contain round segments? */
1290 FT_Int is_straight = 0; /* does it contain straight segments? */
1291 FT_Pos ups = 0; /* number of upwards segments */
1292 FT_Pos downs = 0; /* number of downwards segments */
1302 /* check for roundness of segment */
1303 if ( seg->flags & AH_EDGE_ROUND )
1308 /* check for segment direction */
1309 if ( seg->dir == up_dir )
1310 ups += seg->max_coord-seg->min_coord;
1312 downs += seg->max_coord-seg->min_coord;
1314 /* check for links -- if seg->serif is set, then seg->link must */
1316 is_serif = (FT_Bool)( seg->serif && seg->serif->edge != edge );
1318 if ( seg->link || is_serif )
1330 edge2 = edge->serif;
1339 edge_delta = edge->fpos - edge2->fpos;
1340 if ( edge_delta < 0 )
1341 edge_delta = -edge_delta;
1343 seg_delta = seg->pos - seg2->pos;
1344 if ( seg_delta < 0 )
1345 seg_delta = -seg_delta;
1347 if ( seg_delta < edge_delta )
1353 #ifdef FT_CONFIG_CHESTER_SERIF
1356 edge->serif = edge2;
1357 edge2->flags |= AH_EDGE_SERIF;
1361 #else /* !CHESTER_SERIF */
1363 edge->serif = edge2;
1369 seg = seg->edge_next;
1371 } while ( seg != edge->first );
1373 /* set the round/straight flags */
1374 edge->flags = AH_EDGE_NORMAL;
1376 if ( is_round > 0 && is_round >= is_straight )
1377 edge->flags |= AH_EDGE_ROUND;
1379 /* set the edge's main direction */
1380 edge->dir = AH_DIR_NONE;
1385 else if ( ups < downs )
1386 edge->dir = - up_dir;
1388 else if ( ups == downs )
1389 edge->dir = 0; /* both up and down !! */
1391 /* gets rid of serifs if link is set */
1392 /* XXX: This gets rid of many unpleasant artefacts! */
1393 /* Example: the `c' in cour.pfa at size 13 */
1395 if ( edge->serif && edge->link )
1399 edges = outline->vert_edges;
1400 segments = outline->vert_segments;
1401 segment_limit = segments + outline->num_vsegments;
1402 p_num_edges = &outline->num_vedges;
1404 scale = outline->x_scale;
1409 /*************************************************************************/
1412 /* ah_outline_detect_features */
1415 /* Performs feature detection on a given AH_OutlineRec object. */
1417 FT_LOCAL_DEF( void )
1418 ah_outline_detect_features( AH_Outline outline )
1420 ah_outline_compute_segments ( outline );
1421 ah_outline_link_segments ( outline );
1422 ah_outline_compute_edges ( outline );
1423 ah_outline_compute_inflections( outline );
1427 /*************************************************************************/
1430 /* ah_outline_compute_blue_edges */
1433 /* Computes the `blue edges' in a given outline (i.e. those that must */
1434 /* be snapped to a blue zone edge (top or bottom). */
1436 FT_LOCAL_DEF( void )
1437 ah_outline_compute_blue_edges( AH_Outline outline,
1438 AH_Face_Globals face_globals )
1440 AH_Edge edge = outline->horz_edges;
1441 AH_Edge edge_limit = edge + outline->num_hedges;
1442 AH_Globals globals = &face_globals->design;
1443 FT_Fixed y_scale = outline->y_scale;
1445 FT_Bool blue_active[AH_BLUE_MAX];
1448 /* compute which blue zones are active, i.e. have their scaled */
1449 /* size < 3/4 pixels */
1455 for ( blue = AH_BLUE_CAPITAL_TOP; blue < AH_BLUE_MAX; blue++ )
1457 FT_Pos ref, shoot, dist;
1460 ref = globals->blue_refs[blue];
1461 shoot = globals->blue_shoots[blue];
1466 blue_active[blue] = 0;
1468 if ( FT_MulFix( dist, y_scale ) < 48 )
1470 blue_active[blue] = 1;
1475 /* return immediately if no blue zone is active */
1480 /* compute for each horizontal edge, which blue zone is closer */
1481 for ( ; edge < edge_limit; edge++ )
1484 FT_Pos* best_blue = 0;
1485 FT_Pos best_dist; /* initial threshold */
1488 /* compute the initial threshold as a fraction of the EM size */
1489 best_dist = FT_MulFix( face_globals->face->units_per_EM / 40, y_scale );
1491 #ifdef FT_CONFIG_CHESTER_SMALL_F
1492 if ( best_dist > 64 / 2 )
1495 if ( best_dist > 64 / 4 )
1499 for ( blue = AH_BLUE_CAPITAL_TOP; blue < AH_BLUE_MAX; blue++ )
1501 /* if it is a top zone, check for right edges -- if it is a bottom */
1502 /* zone, check for left edges */
1504 /* of course, that's for TrueType XXX */
1505 FT_Bool is_top_blue =
1506 FT_BOOL( AH_IS_TOP_BLUE( blue ) );
1507 FT_Bool is_major_dir =
1508 FT_BOOL( edge->dir == outline->horz_major_dir );
1510 if ( !blue_active[blue] )
1513 /* if it is a top zone, the edge must be against the major */
1514 /* direction; if it is a bottom zone, it must be in the major */
1516 if ( is_top_blue ^ is_major_dir )
1519 FT_Pos* blue_pos = globals->blue_refs + blue;
1522 /* first of all, compare it to the reference position */
1523 dist = edge->fpos - *blue_pos;
1527 dist = FT_MulFix( dist, y_scale );
1528 if ( dist < best_dist )
1531 best_blue = blue_pos;
1534 /* now, compare it to the overshoot position if the edge is */
1535 /* rounded, and if the edge is over the reference position of a */
1536 /* top zone, or under the reference position of a bottom zone */
1537 if ( edge->flags & AH_EDGE_ROUND && dist != 0 )
1539 FT_Bool is_under_ref = FT_BOOL( edge->fpos < *blue_pos );
1542 if ( is_top_blue ^ is_under_ref )
1544 blue_pos = globals->blue_shoots + blue;
1545 dist = edge->fpos - *blue_pos;
1549 dist = FT_MulFix( dist, y_scale );
1550 if ( dist < best_dist )
1553 best_blue = blue_pos;
1561 edge->blue_edge = best_blue;
1566 /*************************************************************************/
1569 /* ah_outline_scale_blue_edges */
1572 /* This functions must be called before hinting in order to re-adjust */
1573 /* the contents of the detected edges (basically change the `blue */
1574 /* edge' pointer from `design units' to `scaled ones'). */
1576 FT_LOCAL_DEF( void )
1577 ah_outline_scale_blue_edges( AH_Outline outline,
1578 AH_Face_Globals globals )
1580 AH_Edge edge = outline->horz_edges;
1581 AH_Edge edge_limit = edge + outline->num_hedges;
1585 delta = globals->scaled.blue_refs - globals->design.blue_refs;
1587 for ( ; edge < edge_limit; edge++ )
1589 if ( edge->blue_edge )
1590 edge->blue_edge += delta;