diff options
Diffstat (limited to 'nx-X11/extras/freetype2/src/autohint/ahglyph.c')
-rw-r--r-- | nx-X11/extras/freetype2/src/autohint/ahglyph.c | 1699 |
1 files changed, 1699 insertions, 0 deletions
diff --git a/nx-X11/extras/freetype2/src/autohint/ahglyph.c b/nx-X11/extras/freetype2/src/autohint/ahglyph.c new file mode 100644 index 000000000..2a0390be4 --- /dev/null +++ b/nx-X11/extras/freetype2/src/autohint/ahglyph.c @@ -0,0 +1,1699 @@ +/***************************************************************************/ +/* */ +/* ahglyph.c */ +/* */ +/* Routines used to load and analyze a given glyph before hinting */ +/* (body). */ +/* */ +/* Copyright 2000-2001, 2002, 2003, 2004 Catharon Productions Inc. */ +/* Author: David Turner */ +/* */ +/* This file is part of the Catharon Typography Project and shall only */ +/* be used, modified, and distributed under the terms of the Catharon */ +/* Open Source License that should come with this file under the name */ +/* `CatharonLicense.txt'. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/* Note that this license is compatible with the FreeType license. */ +/* */ +/***************************************************************************/ + + +#include <ft2build.h> +#include "ahglyph.h" +#include "ahangles.h" +#include "ahglobal.h" +#include "aherrors.h" + + +#ifdef AH_DEBUG + +#include <stdio.h> + + void + ah_dump_edges( AH_Outline outline ) + { + AH_Edge edges; + AH_Edge edge_limit; + AH_Segment segments; + FT_Int dimension; + + + edges = outline->horz_edges; + edge_limit = edges + outline->num_hedges; + segments = outline->horz_segments; + + for ( dimension = 1; dimension >= 0; dimension-- ) + { + AH_Edge edge; + + + printf ( "Table of %s edges:\n", + !dimension ? "vertical" : "horizontal" ); + printf ( " [ index | pos | dir | link |" + " serif | blue | opos | pos ]\n" ); + + for ( edge = edges; edge < edge_limit; edge++ ) + { + printf ( " [ %5d | %4d | %5s | %4d | %5d | %c | %5.2f | %5.2f ]\n", + edge - edges, + (int)edge->fpos, + edge->dir == AH_DIR_UP + ? "up" + : ( edge->dir == AH_DIR_DOWN + ? "down" + : ( edge->dir == AH_DIR_LEFT + ? "left" + : ( edge->dir == AH_DIR_RIGHT + ? "right" + : "none" ) ) ), + edge->link ? ( edge->link - edges ) : -1, + edge->serif ? ( edge->serif - edges ) : -1, + edge->blue_edge ? 'y' : 'n', + edge->opos / 64.0, + edge->pos / 64.0 ); + } + + edges = outline->vert_edges; + edge_limit = edges + outline->num_vedges; + segments = outline->vert_segments; + } + } + + + /* A function used to dump the array of linked segments */ + void + ah_dump_segments( AH_Outline outline ) + { + AH_Segment segments; + AH_Segment segment_limit; + AH_Point points; + FT_Int dimension; + + + points = outline->points; + segments = outline->horz_segments; + segment_limit = segments + outline->num_hsegments; + + for ( dimension = 1; dimension >= 0; dimension-- ) + { + AH_Segment seg; + + + printf ( "Table of %s segments:\n", + !dimension ? "vertical" : "horizontal" ); + printf ( " [ index | pos | dir | link | serif |" + " numl | first | start ]\n" ); + + for ( seg = segments; seg < segment_limit; seg++ ) + { + printf ( " [ %5d | %4d | %5s | %4d | %5d | %4d | %5d | %5d ]\n", + seg - segments, + (int)seg->pos, + seg->dir == AH_DIR_UP + ? "up" + : ( seg->dir == AH_DIR_DOWN + ? "down" + : ( seg->dir == AH_DIR_LEFT + ? "left" + : ( seg->dir == AH_DIR_RIGHT + ? "right" + : "none" ) ) ), + seg->link ? ( seg->link - segments ) : -1, + seg->serif ? ( seg->serif - segments ) : -1, + (int)seg->num_linked, + seg->first - points, + seg->last - points ); + } + + segments = outline->vert_segments; + segment_limit = segments + outline->num_vsegments; + } + } + +#endif /* AH_DEBUG */ + + + /* compute the direction value of a given vector */ + static AH_Direction + ah_compute_direction( FT_Pos dx, + FT_Pos dy ) + { + AH_Direction dir; + FT_Pos ax = FT_ABS( dx ); + FT_Pos ay = FT_ABS( dy ); + + + dir = AH_DIR_NONE; + + /* atan(1/12) == 4.7 degrees */ + + /* test for vertical direction */ + if ( ax * 12 < ay ) + { + dir = dy > 0 ? AH_DIR_UP : AH_DIR_DOWN; + } + /* test for horizontal direction */ + else if ( ay * 12 < ax ) + { + dir = dx > 0 ? AH_DIR_RIGHT : AH_DIR_LEFT; + } + + return dir; + } + + + /* this function is used by ah_get_orientation (see below) to test */ + /* the fill direction of given bbox extremum */ + static FT_Int + ah_test_extremum( FT_Outline* outline, + FT_Int n ) + { + FT_Vector *prev, *cur, *next; + FT_Pos product; + FT_Int first, last, c; + FT_Int retval; + + + /* we need to compute the `previous' and `next' point */ + /* for this extremum; we check whether the extremum */ + /* is start or end of a contour and providing */ + /* appropriate values if so */ + cur = outline->points + n; + prev = cur - 1; + next = cur + 1; + + first = 0; + for ( c = 0; c < outline->n_contours; c++ ) + { + last = outline->contours[c]; + + if ( n == first ) + prev = outline->points + last; + + if ( n == last ) + next = outline->points + first; + + first = last + 1; + } + + /* compute the vectorial product -- since we know that the angle */ + /* is <= 180 degrees (otherwise it wouldn't be an extremum) we */ + /* can determine the filling orientation if the product is */ + /* either positive or negative */ + product = FT_MulDiv( cur->x - prev->x, /* in.x */ + next->y - cur->y, /* out.y */ + 0x40 ) + - + FT_MulDiv( cur->y - prev->y, /* in.y */ + next->x - cur->x, /* out.x */ + 0x40 ); + + retval = 0; + if ( product ) + retval = product > 0 ? 2 : 1; + + return retval; + } + + + /* Compute the orientation of path filling. It differs between TrueType */ + /* and Type1 formats. We could use the `FT_OUTLINE_REVERSE_FILL' flag, */ + /* but it is better to re-compute it directly (it seems that this flag */ + /* isn't correctly set for some weird composite glyphs currently). */ + /* */ + /* We do this by computing bounding box points, and computing their */ + /* curvature. */ + /* */ + /* The function returns either 1 or 2. */ + /* */ + static FT_Int + ah_get_orientation( FT_Outline* outline ) + { + FT_BBox box; + FT_Int indices_xMin, indices_yMin, indices_xMax, indices_yMax; + FT_Int n, last; + + + indices_xMin = -1; + indices_yMin = -1; + indices_xMax = -1; + indices_yMax = -1; + + box.xMin = box.yMin = 32767L; + box.xMax = box.yMax = -32768L; + + /* is it empty? */ + if ( outline->n_contours < 1 ) + return 1; + + last = outline->contours[outline->n_contours - 1]; + + for ( n = 0; n <= last; n++ ) + { + FT_Pos x, y; + + + x = outline->points[n].x; + if ( x < box.xMin ) + { + box.xMin = x; + indices_xMin = n; + } + if ( x > box.xMax ) + { + box.xMax = x; + indices_xMax = n; + } + + y = outline->points[n].y; + if ( y < box.yMin ) + { + box.yMin = y; + indices_yMin = n; + } + if ( y > box.yMax ) + { + box.yMax = y; + indices_yMax = n; + } + } + + /* test orientation of the extrema */ + n = ah_test_extremum( outline, indices_xMin ); + if ( n ) + goto Exit; + + n = ah_test_extremum( outline, indices_yMin ); + if ( n ) + goto Exit; + + n = ah_test_extremum( outline, indices_xMax ); + if ( n ) + goto Exit; + + n = ah_test_extremum( outline, indices_yMax ); + if ( !n ) + n = 1; + + Exit: + return n; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* ah_outline_new */ + /* */ + /* <Description> */ + /* Creates a new and empty AH_OutlineRec object. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + ah_outline_new( FT_Memory memory, + AH_Outline* aoutline ) + { + FT_Error error; + AH_Outline outline; + + + if ( !FT_NEW( outline ) ) + { + outline->memory = memory; + *aoutline = outline; + } + + return error; + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* ah_outline_done */ + /* */ + /* <Description> */ + /* Destroys a given AH_OutlineRec object. */ + /* */ + FT_LOCAL_DEF( void ) + ah_outline_done( AH_Outline outline ) + { + FT_Memory memory = outline->memory; + + + FT_FREE( outline->horz_edges ); + FT_FREE( outline->horz_segments ); + FT_FREE( outline->contours ); + FT_FREE( outline->points ); + + FT_FREE( outline ); + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* ah_outline_save */ + /* */ + /* <Description> */ + /* Saves the contents of a given AH_OutlineRec object into a face's */ + /* glyph slot. */ + /* */ + FT_LOCAL_DEF( void ) + ah_outline_save( AH_Outline outline, + AH_Loader gloader ) + { + AH_Point point = outline->points; + AH_Point point_limit = point + outline->num_points; + FT_Vector* vec = gloader->current.outline.points; + char* tag = gloader->current.outline.tags; + + + /* we assume that the glyph loader has already been checked for storage */ + for ( ; point < point_limit; point++, vec++, tag++ ) + { + vec->x = point->x; + vec->y = point->y; + + if ( point->flags & AH_FLAG_CONIC ) + tag[0] = FT_CURVE_TAG_CONIC; + else if ( point->flags & AH_FLAG_CUBIC ) + tag[0] = FT_CURVE_TAG_CUBIC; + else + tag[0] = FT_CURVE_TAG_ON; + } + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* ah_outline_load */ + /* */ + /* <Description> */ + /* Loads an unscaled outline from a glyph slot into an AH_OutlineRec */ + /* object. */ + /* */ + FT_LOCAL_DEF( FT_Error ) + ah_outline_load( AH_Outline outline, + FT_Fixed x_scale, + FT_Fixed y_scale, + FT_Face face ) + { + FT_Memory memory = outline->memory; + FT_Error error = AH_Err_Ok; + FT_Outline* source = &face->glyph->outline; + FT_Int num_points = source->n_points; + FT_Int num_contours = source->n_contours; + AH_Point points; + + + /* check arguments */ + if ( !face || + !face->size || + face->glyph->format != FT_GLYPH_FORMAT_OUTLINE ) + return AH_Err_Invalid_Argument; + + /* first of all, reallocate the contours array if necessary */ + if ( num_contours > outline->max_contours ) + { + FT_Int new_contours = FT_PAD_CEIL( num_contours, 4 ); + + + if ( FT_RENEW_ARRAY( outline->contours, + outline->max_contours, + new_contours ) ) + goto Exit; + + outline->max_contours = new_contours; + } + + /* then, reallocate the points, segments & edges arrays if needed -- */ + /* note that we reserved two additional point positions, used to */ + /* hint metrics appropriately */ + /* */ + if ( num_points + 2 > outline->max_points ) + { + FT_Int news = FT_PAD_CEIL( num_points + 2, 8 ); + FT_Int max = outline->max_points; + + + if ( FT_RENEW_ARRAY( outline->points, max, news ) || + FT_RENEW_ARRAY( outline->horz_edges, max * 2, news * 2 ) || + FT_RENEW_ARRAY( outline->horz_segments, max * 2, news * 2 ) ) + goto Exit; + + /* readjust some pointers */ + outline->vert_edges = outline->horz_edges + news; + outline->vert_segments = outline->horz_segments + news; + outline->max_points = news; + } + + outline->num_points = num_points; + outline->num_contours = num_contours; + + outline->num_hedges = 0; + outline->num_vedges = 0; + outline->num_hsegments = 0; + outline->num_vsegments = 0; + + /* We can't rely on the value of `FT_Outline.flags' to know the fill */ + /* direction used for a glyph, given that some fonts are broken (e.g. */ + /* the Arphic ones). We thus recompute it each time we need to. */ + /* */ + outline->vert_major_dir = AH_DIR_UP; + outline->horz_major_dir = AH_DIR_LEFT; + + if ( ah_get_orientation( source ) > 1 ) + { + outline->vert_major_dir = AH_DIR_DOWN; + outline->horz_major_dir = AH_DIR_RIGHT; + } + + outline->x_scale = x_scale; + outline->y_scale = y_scale; + + points = outline->points; + if ( outline->num_points == 0 ) + goto Exit; + + { + /* do one thing at a time -- it is easier to understand, and */ + /* the code is clearer */ + AH_Point point; + AH_Point point_limit = points + outline->num_points; + + + /* compute coordinates */ + { + FT_Vector* vec = source->points; + + + for ( point = points; point < point_limit; vec++, point++ ) + { + point->fx = vec->x; + point->fy = vec->y; + point->ox = point->x = FT_MulFix( vec->x, x_scale ); + point->oy = point->y = FT_MulFix( vec->y, y_scale ); + + point->flags = 0; + } + } + + /* compute Bezier flags */ + { + char* tag = source->tags; + + + for ( point = points; point < point_limit; point++, tag++ ) + { + switch ( FT_CURVE_TAG( *tag ) ) + { + case FT_CURVE_TAG_CONIC: + point->flags = AH_FLAG_CONIC; + break; + case FT_CURVE_TAG_CUBIC: + point->flags = AH_FLAG_CUBIC; + break; + default: + ; + } + } + } + + /* compute `next' and `prev' */ + { + FT_Int contour_index; + AH_Point prev; + AH_Point first; + AH_Point end; + + + contour_index = 0; + + first = points; + end = points + source->contours[0]; + prev = end; + + for ( point = points; point < point_limit; point++ ) + { + point->prev = prev; + if ( point < end ) + { + point->next = point + 1; + prev = point; + } + else + { + point->next = first; + contour_index++; + if ( point + 1 < point_limit ) + { + end = points + source->contours[contour_index]; + first = point + 1; + prev = end; + } + } + } + } + + /* set-up the contours array */ + { + AH_Point* contour = outline->contours; + AH_Point* contour_limit = contour + outline->num_contours; + short* end = source->contours; + short idx = 0; + + + for ( ; contour < contour_limit; contour++, end++ ) + { + contour[0] = points + idx; + idx = (short)( end[0] + 1 ); + } + } + + /* compute directions of in & out vectors */ + { + for ( point = points; point < point_limit; point++ ) + { + AH_Point prev; + AH_Point next; + FT_Vector ivec, ovec; + + + prev = point->prev; + ivec.x = point->fx - prev->fx; + ivec.y = point->fy - prev->fy; + + point->in_dir = ah_compute_direction( ivec.x, ivec.y ); + + next = point->next; + ovec.x = next->fx - point->fx; + ovec.y = next->fy - point->fy; + + point->out_dir = ah_compute_direction( ovec.x, ovec.y ); + +#ifndef AH_OPTION_NO_WEAK_INTERPOLATION + if ( point->flags & ( AH_FLAG_CONIC | AH_FLAG_CUBIC ) ) + { + Is_Weak_Point: + point->flags |= AH_FLAG_WEAK_INTERPOLATION; + } + else if ( point->out_dir == point->in_dir ) + { + AH_Angle angle_in, angle_out, delta; + + + if ( point->out_dir != AH_DIR_NONE ) + goto Is_Weak_Point; + + angle_in = ah_angle( &ivec ); + angle_out = ah_angle( &ovec ); + delta = angle_in - angle_out; + + if ( delta > AH_PI ) + delta = AH_2PI - delta; + + if ( delta < 0 ) + delta = -delta; + + if ( delta < 2 ) + goto Is_Weak_Point; + } + else if ( point->in_dir == -point->out_dir ) + goto Is_Weak_Point; +#endif + } + } + } + + Exit: + return error; + } + + + FT_LOCAL_DEF( void ) + ah_setup_uv( AH_Outline outline, + AH_UV source ) + { + AH_Point point = outline->points; + AH_Point point_limit = point + outline->num_points; + + + switch ( source ) + { + case AH_UV_FXY: + for ( ; point < point_limit; point++ ) + { + point->u = point->fx; + point->v = point->fy; + } + break; + + case AH_UV_FYX: + for ( ; point < point_limit; point++ ) + { + point->u = point->fy; + point->v = point->fx; + } + break; + + case AH_UV_OXY: + for ( ; point < point_limit; point++ ) + { + point->u = point->ox; + point->v = point->oy; + } + break; + + case AH_UV_OYX: + for ( ; point < point_limit; point++ ) + { + point->u = point->oy; + point->v = point->ox; + } + break; + + case AH_UV_YX: + for ( ; point < point_limit; point++ ) + { + point->u = point->y; + point->v = point->x; + } + break; + + case AH_UV_OX: + for ( ; point < point_limit; point++ ) + { + point->u = point->x; + point->v = point->ox; + } + break; + + case AH_UV_OY: + for ( ; point < point_limit; point++ ) + { + point->u = point->y; + point->v = point->oy; + } + break; + + default: + for ( ; point < point_limit; point++ ) + { + point->u = point->x; + point->v = point->y; + } + } + } + + + /* compute all inflex points in a given glyph */ + static void + ah_outline_compute_inflections( AH_Outline outline ) + { + AH_Point* contour = outline->contours; + AH_Point* contour_limit = contour + outline->num_contours; + + + /* load original coordinates in (u,v) */ + ah_setup_uv( outline, AH_UV_FXY ); + + /* do each contour separately */ + for ( ; contour < contour_limit; contour++ ) + { + FT_Vector vec; + AH_Point point = contour[0]; + AH_Point first = point; + AH_Point start = point; + AH_Point end = point; + AH_Point before; + AH_Point after; + AH_Angle angle_in, angle_seg, angle_out; + AH_Angle diff_in, diff_out; + FT_Int finished = 0; + + + /* compute first segment in contour */ + first = point; + + start = end = first; + do + { + end = end->next; + if ( end == first ) + goto Skip; + + } while ( end->u == first->u && end->v == first->v ); + + vec.x = end->u - start->u; + vec.y = end->v - start->v; + angle_seg = ah_angle( &vec ); + + /* extend the segment start whenever possible */ + before = start; + do + { + do + { + start = before; + before = before->prev; + if ( before == first ) + goto Skip; + + } while ( before->u == start->u && before->v == start->v ); + + vec.x = start->u - before->u; + vec.y = start->v - before->v; + angle_in = ah_angle( &vec ); + + } while ( angle_in == angle_seg ); + + first = start; + diff_in = ah_angle_diff( angle_in, angle_seg ); + + /* now, process all segments in the contour */ + do + { + /* first, extend current segment's end whenever possible */ + after = end; + do + { + do + { + end = after; + after = after->next; + if ( after == first ) + finished = 1; + + } while ( end->u == after->u && end->v == after->v ); + + vec.x = after->u - end->u; + vec.y = after->v - end->v; + angle_out = ah_angle( &vec ); + + } while ( angle_out == angle_seg ); + + diff_out = ah_angle_diff( angle_seg, angle_out ); + + if ( ( diff_in ^ diff_out ) < 0 ) + { + /* diff_in and diff_out have different signs, we have */ + /* inflection points here... */ + do + { + start->flags |= AH_FLAG_INFLECTION; + start = start->next; + + } while ( start != end ); + + start->flags |= AH_FLAG_INFLECTION; + } + + start = end; + end = after; + angle_seg = angle_out; + diff_in = diff_out; + + } while ( !finished ); + + Skip: + ; + } + } + + + FT_LOCAL_DEF( void ) + ah_outline_compute_segments( AH_Outline outline ) + { + int dimension; + AH_Segment segments; + FT_Int* p_num_segments; + AH_Direction segment_dir; + AH_Direction major_dir; + + + segments = outline->horz_segments; + p_num_segments = &outline->num_hsegments; + major_dir = AH_DIR_RIGHT; /* This value must be positive! */ + segment_dir = major_dir; + + /* set up (u,v) in each point */ + ah_setup_uv( outline, AH_UV_FYX ); + + for ( dimension = 1; dimension >= 0; dimension-- ) + { + AH_Point* contour = outline->contours; + AH_Point* contour_limit = contour + outline->num_contours; + AH_Segment segment = segments; + FT_Int num_segments = 0; + +#ifdef AH_HINT_METRICS + AH_Point min_point = 0; + AH_Point max_point = 0; + FT_Pos min_coord = 32000; + FT_Pos max_coord = -32000; +#endif + + + /* do each contour separately */ + for ( ; contour < contour_limit; contour++ ) + { + AH_Point point = contour[0]; + AH_Point last = point->prev; + int on_edge = 0; + FT_Pos min_pos = 32000; /* minimum segment pos != min_coord */ + FT_Pos max_pos = -32000; /* maximum segment pos != max_coord */ + FT_Bool passed; + + +#ifdef AH_HINT_METRICS + if ( point->u < min_coord ) + { + min_coord = point->u; + min_point = point; + } + if ( point->u > max_coord ) + { + max_coord = point->u; + max_point = point; + } +#endif + + if ( point == last ) /* skip singletons -- just in case */ + continue; + + if ( FT_ABS( last->out_dir ) == major_dir && + FT_ABS( point->out_dir ) == major_dir ) + { + /* we are already on an edge, try to locate its start */ + last = point; + + for (;;) + { + point = point->prev; + if ( FT_ABS( point->out_dir ) != major_dir ) + { + point = point->next; + break; + } + if ( point == last ) + break; + } + } + + last = point; + passed = 0; + + for (;;) + { + FT_Pos u, v; + + + if ( on_edge ) + { + u = point->u; + if ( u < min_pos ) + min_pos = u; + if ( u > max_pos ) + max_pos = u; + + if ( point->out_dir != segment_dir || point == last ) + { + /* we are just leaving an edge; record a new segment! */ + segment->last = point; + segment->pos = ( min_pos + max_pos ) >> 1; + + /* a segment is round if either its first or last point */ + /* is a control point */ + if ( ( segment->first->flags | point->flags ) & + AH_FLAG_CONTROL ) + segment->flags |= AH_EDGE_ROUND; + + /* compute segment size */ + min_pos = max_pos = point->v; + + v = segment->first->v; + if ( v < min_pos ) + min_pos = v; + if ( v > max_pos ) + max_pos = v; + + segment->min_coord = min_pos; + segment->max_coord = max_pos; + + on_edge = 0; + num_segments++; + segment++; + /* fallthrough */ + } + } + + /* now exit if we are at the start/end point */ + if ( point == last ) + { + if ( passed ) + break; + passed = 1; + } + + if ( !on_edge && FT_ABS( point->out_dir ) == major_dir ) + { + /* this is the start of a new segment! */ + segment_dir = point->out_dir; + + /* clear all segment fields */ + FT_ZERO( segment ); + + segment->dir = segment_dir; + segment->flags = AH_EDGE_NORMAL; + min_pos = max_pos = point->u; + segment->first = point; + segment->last = point; + segment->contour = contour; + segment->score = 32000; + segment->link = NULL; + on_edge = 1; + +#ifdef AH_HINT_METRICS + if ( point == max_point ) + max_point = 0; + + if ( point == min_point ) + min_point = 0; +#endif + } + + point = point->next; + } + + } /* contours */ + +#ifdef AH_HINT_METRICS + /* we need to ensure that there are edges on the left-most and */ + /* right-most points of the glyph in order to hint the metrics; */ + /* we do this by inserting fake segments when needed */ + if ( dimension == 0 ) + { + AH_Point point = outline->points; + AH_Point point_limit = point + outline->num_points; + + FT_Pos min_pos = 32000; + FT_Pos max_pos = -32000; + + + min_point = 0; + max_point = 0; + + /* compute minimum and maximum points */ + for ( ; point < point_limit; point++ ) + { + FT_Pos x = point->fx; + + + if ( x < min_pos ) + { + min_pos = x; + min_point = point; + } + if ( x > max_pos ) + { + max_pos = x; + max_point = point; + } + } + + /* insert minimum segment */ + if ( min_point ) + { + /* clear all segment fields */ + FT_ZERO( segment ); + + segment->dir = segment_dir; + segment->flags = AH_EDGE_NORMAL; + segment->first = min_point; + segment->last = min_point; + segment->pos = min_pos; + segment->score = 32000; + segment->link = NULL; + + num_segments++; + segment++; + } + + /* insert maximum segment */ + if ( max_point ) + { + /* clear all segment fields */ + FT_ZERO( segment ); + + segment->dir = segment_dir; + segment->flags = AH_EDGE_NORMAL; + segment->first = max_point; + segment->last = max_point; + segment->pos = max_pos; + segment->score = 32000; + segment->link = NULL; + + num_segments++; + segment++; + } + } +#endif /* AH_HINT_METRICS */ + + *p_num_segments = num_segments; + + segments = outline->vert_segments; + major_dir = AH_DIR_UP; + p_num_segments = &outline->num_vsegments; + + ah_setup_uv( outline, AH_UV_FXY ); + } + } + + + FT_LOCAL_DEF( void ) + ah_outline_link_segments( AH_Outline outline ) + { + AH_Segment segments; + AH_Segment segment_limit; + AH_Direction major_dir; + int dimension; + + + segments = outline->horz_segments; + segment_limit = segments + outline->num_hsegments; + major_dir = outline->horz_major_dir; + + for ( dimension = 1; dimension >= 0; dimension-- ) + { + AH_Segment seg1; + AH_Segment seg2; + +#if 0 + /* now compare each segment to the others */ + for ( seg1 = segments; seg1 < segment_limit; seg1++ ) + { + FT_Pos best_score; + AH_Segment best_segment; + + + /* the fake segments are introduced to hint the metrics -- */ + /* we must never link them to anything */ + if ( seg1->first == seg1->last ) + continue; + + best_segment = seg1->link; + if ( best_segment ) + best_score = seg1->score; + else + best_score = +32000; + + for ( seg2 = segments; seg2 < segment_limit; seg2++ ) + if ( seg1 != seg2 && seg1->dir + seg2->dir == 0 ) + { + FT_Pos pos1 = seg1->pos; + FT_Pos pos2 = seg2->pos; + FT_Bool is_dir; + FT_Bool is_pos; + + + /* check that the segments are correctly oriented and */ + /* positioned to form a black distance */ + + is_dir = (FT_Bool)( seg1->dir == outline->horz_major_dir || + seg1->dir == outline->vert_major_dir ); + is_pos = (FT_Bool)( pos1 > pos2 ); + + if ( pos1 == pos2 || !(is_dir ^ is_pos) ) + continue; + + { + FT_Pos min = seg1->min_coord; + FT_Pos max = seg1->max_coord; + FT_Pos len, dist, score; + + + if ( min < seg2->min_coord ) + min = seg2->min_coord; + + if ( max > seg2->max_coord ) + max = seg2->max_coord; + + len = max - min; + if ( len >= 8 ) + { + dist = seg2->pos - seg1->pos; + if ( dist < 0 ) + dist = -dist; + + score = dist + 3000 / len; + + if ( score < best_score ) + { + best_score = score; + best_segment = seg2; + } + } + } + } + + if ( best_segment ) + { + seg1->link = best_segment; + seg1->score = best_score; + best_segment->num_linked++; + } + } +#endif /* 0 */ + +#if 1 + /* the following code does the same, but much faster! */ + + /* now compare each segment to the others */ + for ( seg1 = segments; seg1 < segment_limit; seg1++ ) + { + /* the fake segments are introduced to hint the metrics -- */ + /* we must never link them to anything */ + if ( seg1->first == seg1->last || seg1->dir != major_dir ) + continue; + + for ( seg2 = segments; seg2 < segment_limit; seg2++ ) + if ( seg2 != seg1 && seg1->dir + seg2->dir == 0 ) + { + FT_Pos pos1 = seg1->pos; + FT_Pos pos2 = seg2->pos; + FT_Pos dist = pos2 - pos1; + + + if ( dist < 0 ) + continue; + + { + FT_Pos min = seg1->min_coord; + FT_Pos max = seg1->max_coord; + FT_Pos len, score; + + + if ( min < seg2->min_coord ) + min = seg2->min_coord; + + if ( max > seg2->max_coord ) + max = seg2->max_coord; + + len = max - min; + if ( len >= 8 ) + { + score = dist + 3000 / len; + + if ( score < seg1->score ) + { + seg1->score = score; + seg1->link = seg2; + } + + if ( score < seg2->score ) + { + seg2->score = score; + seg2->link = seg1; + } + } + } + } + } +#endif /* 1 */ + + /* now, compute the `serif' segments */ + for ( seg1 = segments; seg1 < segment_limit; seg1++ ) + { + seg2 = seg1->link; + + if ( seg2 ) + { + seg2->num_linked++; + if ( seg2->link != seg1 ) + { + seg1->link = 0; + seg1->serif = seg2->link; + } + } + } + + segments = outline->vert_segments; + segment_limit = segments + outline->num_vsegments; + major_dir = outline->vert_major_dir; + } + } + + + static void + ah_outline_compute_edges( AH_Outline outline ) + { + AH_Edge edges; + AH_Segment segments; + AH_Segment segment_limit; + AH_Direction up_dir; + FT_Int* p_num_edges; + FT_Int dimension; + FT_Fixed scale; + FT_Pos edge_distance_threshold; + + + edges = outline->horz_edges; + segments = outline->horz_segments; + segment_limit = segments + outline->num_hsegments; + p_num_edges = &outline->num_hedges; + up_dir = AH_DIR_RIGHT; + scale = outline->y_scale; + + for ( dimension = 1; dimension >= 0; dimension-- ) + { + AH_Edge edge; + AH_Edge edge_limit; /* really == edge + num_edges */ + AH_Segment seg; + + + /*********************************************************************/ + /* */ + /* We will begin by generating a sorted table of edges for the */ + /* current direction. To do so, we simply scan each segment and try */ + /* to find an edge in our table that corresponds to its position. */ + /* */ + /* If no edge is found, we create and insert a new edge in the */ + /* sorted table. Otherwise, we simply add the segment to the edge's */ + /* list which will be processed in the second step to compute the */ + /* edge's properties. */ + /* */ + /* Note that the edges table is sorted along the segment/edge */ + /* position. */ + /* */ + /*********************************************************************/ + + edge_distance_threshold = FT_MulFix( outline->edge_distance_threshold, + scale ); + if ( edge_distance_threshold > 64 / 4 ) + edge_distance_threshold = 64 / 4; + + edge_distance_threshold = FT_DivFix( edge_distance_threshold, + scale ); + + edge_limit = edges; + for ( seg = segments; seg < segment_limit; seg++ ) + { + AH_Edge found = 0; + + + /* look for an edge corresponding to the segment */ + for ( edge = edges; edge < edge_limit; edge++ ) + { + FT_Pos dist; + + + dist = seg->pos - edge->fpos; + if ( dist < 0 ) + dist = -dist; + + if ( dist < edge_distance_threshold ) + { + found = edge; + break; + } + } + + if ( !found ) + { + /* insert a new edge in the list and */ + /* sort according to the position */ + while ( edge > edges && edge[-1].fpos > seg->pos ) + { + edge[0] = edge[-1]; + edge--; + } + edge_limit++; + + /* clear all edge fields */ + FT_MEM_ZERO( edge, sizeof ( *edge ) ); + + /* add the segment to the new edge's list */ + edge->first = seg; + edge->last = seg; + edge->fpos = seg->pos; + edge->opos = edge->pos = FT_MulFix( seg->pos, scale ); + seg->edge_next = seg; + } + else + { + /* if an edge was found, simply add the segment to the edge's */ + /* list */ + seg->edge_next = edge->first; + edge->last->edge_next = seg; + edge->last = seg; + } + } + *p_num_edges = (FT_Int)( edge_limit - edges ); + + + /*********************************************************************/ + /* */ + /* Good, we will now compute each edge's properties according to */ + /* segments found on its position. Basically, these are: */ + /* */ + /* - edge's main direction */ + /* - stem edge, serif edge or both (which defaults to stem then) */ + /* - rounded edge, straight or both (which defaults to straight) */ + /* - link for edge */ + /* */ + /*********************************************************************/ + + /* first of all, set the `edge' field in each segment -- this is */ + /* required in order to compute edge links */ + + /* Note that I've tried to remove this loop, setting + * the "edge" field of each segment directly in the + * code above. For some reason, it slows down execution + * speed -- on a Sun. + */ + for ( edge = edges; edge < edge_limit; edge++ ) + { + seg = edge->first; + if ( seg ) + do + { + seg->edge = edge; + seg = seg->edge_next; + } + while ( seg != edge->first ); + } + + /* now, compute each edge properties */ + for ( edge = edges; edge < edge_limit; edge++ ) + { + FT_Int is_round = 0; /* does it contain round segments? */ + FT_Int is_straight = 0; /* does it contain straight segments? */ + FT_Pos ups = 0; /* number of upwards segments */ + FT_Pos downs = 0; /* number of downwards segments */ + + + seg = edge->first; + + do + { + FT_Bool is_serif; + + + /* check for roundness of segment */ + if ( seg->flags & AH_EDGE_ROUND ) + is_round++; + else + is_straight++; + + /* check for segment direction */ + if ( seg->dir == up_dir ) + ups += seg->max_coord-seg->min_coord; + else + downs += seg->max_coord-seg->min_coord; + + /* check for links -- if seg->serif is set, then seg->link must */ + /* be ignored */ + is_serif = (FT_Bool)( seg->serif && seg->serif->edge != edge ); + + if ( seg->link || is_serif ) + { + AH_Edge edge2; + AH_Segment seg2; + + + edge2 = edge->link; + seg2 = seg->link; + + if ( is_serif ) + { + seg2 = seg->serif; + edge2 = edge->serif; + } + + if ( edge2 ) + { + FT_Pos edge_delta; + FT_Pos seg_delta; + + + edge_delta = edge->fpos - edge2->fpos; + if ( edge_delta < 0 ) + edge_delta = -edge_delta; + + seg_delta = seg->pos - seg2->pos; + if ( seg_delta < 0 ) + seg_delta = -seg_delta; + + if ( seg_delta < edge_delta ) + edge2 = seg2->edge; + } + else + edge2 = seg2->edge; + +#ifdef FT_CONFIG_CHESTER_SERIF + if ( is_serif ) + { + edge->serif = edge2; + edge2->flags |= AH_EDGE_SERIF; + } + else + edge->link = edge2; +#else /* !FT_CONFIG_CHESTER_SERIF */ + if ( is_serif ) + edge->serif = edge2; + else + edge->link = edge2; +#endif /* !FT_CONFIG_CHESTER_SERIF */ + } + + seg = seg->edge_next; + + } while ( seg != edge->first ); + + /* set the round/straight flags */ + edge->flags = AH_EDGE_NORMAL; + + if ( is_round > 0 && is_round >= is_straight ) + edge->flags |= AH_EDGE_ROUND; + + /* set the edge's main direction */ + edge->dir = AH_DIR_NONE; + + if ( ups > downs ) + edge->dir = up_dir; + + else if ( ups < downs ) + edge->dir = -up_dir; + + else if ( ups == downs ) + edge->dir = 0; /* both up and down! */ + + /* gets rid of serifs if link is set */ + /* XXX: This gets rid of many unpleasant artefacts! */ + /* Example: the `c' in cour.pfa at size 13 */ + + if ( edge->serif && edge->link ) + edge->serif = 0; + } + + edges = outline->vert_edges; + segments = outline->vert_segments; + segment_limit = segments + outline->num_vsegments; + p_num_edges = &outline->num_vedges; + up_dir = AH_DIR_UP; + scale = outline->x_scale; + } + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* ah_outline_detect_features */ + /* */ + /* <Description> */ + /* Performs feature detection on a given AH_OutlineRec object. */ + /* */ + FT_LOCAL_DEF( void ) + ah_outline_detect_features( AH_Outline outline ) + { + ah_outline_compute_segments ( outline ); + ah_outline_link_segments ( outline ); + ah_outline_compute_edges ( outline ); + ah_outline_compute_inflections( outline ); + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* ah_outline_compute_blue_edges */ + /* */ + /* <Description> */ + /* Computes the `blue edges' in a given outline (i.e. those that must */ + /* be snapped to a blue zone edge (top or bottom). */ + /* */ + FT_LOCAL_DEF( void ) + ah_outline_compute_blue_edges( AH_Outline outline, + AH_Face_Globals face_globals ) + { + AH_Edge edge = outline->horz_edges; + AH_Edge edge_limit = edge + outline->num_hedges; + AH_Globals globals = &face_globals->design; + FT_Fixed y_scale = outline->y_scale; + + FT_Bool blue_active[AH_BLUE_MAX]; + + + /* compute which blue zones are active, i.e. have their scaled */ + /* size < 3/4 pixels */ + { + AH_Blue blue; + FT_Bool check = 0; + + + for ( blue = AH_BLUE_CAPITAL_TOP; blue < AH_BLUE_MAX; blue++ ) + { + FT_Pos ref, shoot, dist; + + + ref = globals->blue_refs[blue]; + shoot = globals->blue_shoots[blue]; + dist = ref - shoot; + if ( dist < 0 ) + dist = -dist; + + blue_active[blue] = 0; + + if ( FT_MulFix( dist, y_scale ) < 48 ) + { + blue_active[blue] = 1; + check = 1; + } + } + + /* return immediately if no blue zone is active */ + if ( !check ) + return; + } + + /* for each horizontal edge search the blue zone which is closest */ + for ( ; edge < edge_limit; edge++ ) + { + AH_Blue blue; + FT_Pos* best_blue = 0; + FT_Pos best_dist; /* initial threshold */ + + + /* compute the initial threshold as a fraction of the EM size */ + best_dist = FT_MulFix( face_globals->face->units_per_EM / 40, y_scale ); + +#ifdef FT_CONFIG_CHESTER_SMALL_F + if ( best_dist > 64 / 2 ) + best_dist = 64 / 2; +#else + if ( best_dist > 64 / 4 ) + best_dist = 64 / 4; +#endif + + for ( blue = AH_BLUE_CAPITAL_TOP; blue < AH_BLUE_MAX; blue++ ) + { + /* if it is a top zone, check for right edges -- if it is a bottom */ + /* zone, check for left edges */ + /* */ + /* of course, that's for TrueType XXX */ + FT_Bool is_top_blue = + FT_BOOL( AH_IS_TOP_BLUE( blue ) ); + FT_Bool is_major_dir = + FT_BOOL( edge->dir == outline->horz_major_dir ); + + + if ( !blue_active[blue] ) + continue; + + /* if it is a top zone, the edge must be against the major */ + /* direction; if it is a bottom zone, it must be in the major */ + /* direction */ + if ( is_top_blue ^ is_major_dir ) + { + FT_Pos dist; + FT_Pos* blue_pos = globals->blue_refs + blue; + + + /* first of all, compare it to the reference position */ + dist = edge->fpos - *blue_pos; + if ( dist < 0 ) + dist = -dist; + + dist = FT_MulFix( dist, y_scale ); + if ( dist < best_dist ) + { + best_dist = dist; + best_blue = blue_pos; + } + + /* now, compare it to the overshoot position if the edge is */ + /* rounded, and if the edge is over the reference position of a */ + /* top zone, or under the reference position of a bottom zone */ + if ( edge->flags & AH_EDGE_ROUND && dist != 0 ) + { + FT_Bool is_under_ref = FT_BOOL( edge->fpos < *blue_pos ); + + + if ( is_top_blue ^ is_under_ref ) + { + blue_pos = globals->blue_shoots + blue; + dist = edge->fpos - *blue_pos; + if ( dist < 0 ) + dist = -dist; + + dist = FT_MulFix( dist, y_scale ); + if ( dist < best_dist ) + { + best_dist = dist; + best_blue = blue_pos; + } + } + } + } + } + + if ( best_blue ) + edge->blue_edge = best_blue; + } + } + + + /*************************************************************************/ + /* */ + /* <Function> */ + /* ah_outline_scale_blue_edges */ + /* */ + /* <Description> */ + /* This function must be called before hinting in order to re-adjust */ + /* the contents of the detected edges (basically change the `blue */ + /* edge' pointer from `design units' to `scaled ones'). */ + /* */ + FT_LOCAL_DEF( void ) + ah_outline_scale_blue_edges( AH_Outline outline, + AH_Face_Globals globals ) + { + AH_Edge edge = outline->horz_edges; + AH_Edge edge_limit = edge + outline->num_hedges; + FT_Pos delta; + + + delta = globals->scaled.blue_refs - globals->design.blue_refs; + + for ( ; edge < edge_limit; edge++ ) + { + if ( edge->blue_edge ) + edge->blue_edge += delta; + } + } + + +/* END */ |