From d25ad56d787cd4ecb37a69c64bfb2714458df7fc Mon Sep 17 00:00:00 2001 From: David Turner Date: Thu, 2 Oct 2003 21:07:10 +0000 Subject: [PATCH] * src/autofit/*: adding first sources of the new multi-script "auto-fitter" * include/freetype/ftoutln.h, src/base/ftoutln.c: adding the definition of FT_Outline_Get_Orientation, used to compute the fill orientation of a given glyph outline. * include/freetype/internal/ftserv.h: fixed trivial bug which could crashed the font engine when a cached service pointer was retrieved with FT_FACE_LOOKUP_SERVICE --- ChangeLog | 17 +- include/freetype/ftoutln.h | 63 +++ include/freetype/internal/ftserv.h | 2 +- src/autofit/afangles.c | 164 +++++++ src/autofit/afhints.c | 626 ++++++++++++++++++++++++ src/autofit/afhints.h | 233 +++++++++ src/autofit/aflatin.c | 755 +++++++++++++++++++++++++++++ src/autofit/aflatin.h | 89 ++++ src/autofit/aftypes.h | 289 +++++++++++ src/base/ftoutln.c | 137 ++++++ 10 files changed, 2372 insertions(+), 3 deletions(-) create mode 100644 src/autofit/afangles.c create mode 100644 src/autofit/afhints.c create mode 100644 src/autofit/afhints.h create mode 100644 src/autofit/aflatin.c create mode 100644 src/autofit/aflatin.h create mode 100644 src/autofit/aftypes.h diff --git a/ChangeLog b/ChangeLog index 8a90a61e1..1a6e916e7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +2003-10-01 David Turner + + * src/autofit/*: adding first sources of the new multi-script + "auto-fitter" + + * include/freetype/ftoutln.h, src/base/ftoutln.c: adding the + definition of FT_Outline_Get_Orientation, used to compute the + fill orientation of a given glyph outline. + + * include/freetype/internal/ftserv.h: fixed trivial bug which + could crashed the font engine when a cached service pointer was + retrieved with FT_FACE_LOOKUP_SERVICE + 2003-09-30 Werner Lemberg * src/cid/cidload.c (cid_parse_dict): Skip token if no keyword is @@ -21,9 +34,9 @@ 2003-09-29 David Turner Added new service to handle glyph name dictionaries, replacing the - old internal header named `psnames.h' by `services/svpsname.h'. + old internal header named `psnames.h' by `services/svpsname.h'. Note that this is different from `services/svpostnm.h' which only - handles the retrieval of PostScript font names for a given face. + handles the retrieval of PostScript font names for a given face. (Should we merge these two services into a single header?) * include/freetype/internal/psnames.h: Removed. Most of its diff --git a/include/freetype/ftoutln.h b/include/freetype/ftoutln.h index 19b5724a5..daebae835 100644 --- a/include/freetype/ftoutln.h +++ b/include/freetype/ftoutln.h @@ -389,6 +389,69 @@ FT_BEGIN_HEADER FT_Raster_Params* params ); + /************************************************************************** + * + * @enum: FT_Orientation + * + * @description: + * a list of values used to describe an outline's contour orientation + * + * The TrueType and Postscript specifications used different conventions + * to determine wether outline contours should be filled or unfilled. + * + * @values: + * FT_ORIENTATION_TRUETYPE :: + * according to the TrueType specification, clockwise contours must + * be filled, and counter-clockwise ones must be unfilled + * + * FT_ORIENTATION_POSTSCRIPT :: + * according to the Postscript specification, counter-clockwise contours + * must be filled, and clockwise ones must be unfilled + * + * FT_ORIENTATION_FILL_RIGHT :: + * this is identical to @FT_ORIENTATION_TRUETYPE, but is used to + * remember that in TrueType, everything that is to the right of + * the drawing direction of a contour must be filled. + * + * FT_ORIENTATION_FILL_LEFT :: + * this is identical to @FT_ORIENTATION_POSTSCRIPT, but is used to + * remember that in Postscript, everything that is to the left of + * the drawing direction of a contour must be filled + */ + typedef enum + { + FT_ORIENTATION_TRUETYPE = 0, + FT_ORIENTATION_POSTSCRIPT = 1, + FT_ORIENTATION_FILL_RIGHT = FT_ORIENTATION_TRUETYPE, + FT_ORIENTATION_FILL_LEFT = FT_ORIENTATION_POSTSCRIPT + + } FT_Orientation; + + + /************************************************************************** + * + * @function: FT_Outline_Get_Orientation + * + * @description: + * this function analyzes a glyph outline and tries to compute its + * fill orientation (see @FT_Orientation). This is done by computing + * the direction of each global horizontal and/or vertical extrema + * within the outline. + * + * note that this will return @FT_ORIENTATION_TRUETYPE for empty + * outlines. + * + * @input: + * outline :: handle to source outline + * + * @return: + * orientation + * + */ + FT_EXPORT( FT_Orientation ) + FT_Outline_Get_Orientation( FT_Outline* outline ); + + /* */ diff --git a/include/freetype/internal/ftserv.h b/include/freetype/internal/ftserv.h index e0ee7cc10..561d6f584 100644 --- a/include/freetype/internal/ftserv.h +++ b/include/freetype/internal/ftserv.h @@ -216,8 +216,8 @@ FT_BEGIN_HEADER FT_FACE(face)->internal->services. service_ ## id = \ (FT_Pointer)( svc != NULL ? svc \ : FT_SERVICE_UNAVAILABLE ); \ - *pptr = svc; \ } \ + *pptr = svc; \ FT_END_STMNT diff --git a/src/autofit/afangles.c b/src/autofit/afangles.c new file mode 100644 index 000000000..66c7d7998 --- /dev/null +++ b/src/autofit/afangles.c @@ -0,0 +1,164 @@ +#include "aftypes.h" + + /* this table was generated for AF_ANGLE_PI = 256 */ +#define AF_ANGLE_MAX_ITERS 8 + + static const FT_Fixed + af_angle_arctan_table[9] = + { + 90, 64, 38, 20, 10, 5, 3, 1, 1 + }; + + static FT_Int + af_angle_prenorm( FT_Vector* vec ) + { + FT_Fixed x, y, z; + FT_Int shift; + + + x = vec->x; + y = vec->y; + + z = ( ( x >= 0 ) ? x : - x ) | ( (y >= 0) ? y : -y ); + shift = 0; + + if ( z < ( 1L << 27 ) ) + { + do + { + shift++; + z <<= 1; + } while ( z < ( 1L << 27 ) ); + + vec->x = x << shift; + vec->y = y << shift; + } + else if ( z > ( 1L << 28 ) ) + { + do + { + shift++; + z >>= 1; + } while ( z > ( 1L << 28 ) ); + + vec->x = x >> shift; + vec->y = y >> shift; + shift = -shift; + } + return shift; + } + + + static void + af_angle_pseudo_polarize( FT_Vector* vec ) + { + FT_Fixed theta; + FT_Fixed yi, i; + FT_Fixed x, y; + const FT_Fixed *arctanptr; + + + x = vec->x; + y = vec->y; + + /* Get the vector into the right half plane */ + theta = 0; + if ( x < 0 ) + { + x = -x; + y = -y; + theta = 2 * AF_ANGLE_PI2; + } + + if ( y > 0 ) + theta = - theta; + + arctanptr = af_angle_arctan_table; + + if ( y < 0 ) + { + /* Rotate positive */ + yi = y + ( x << 1 ); + x = x - ( y << 1 ); + y = yi; + theta -= *arctanptr++; /* Subtract angle */ + } + else + { + /* Rotate negative */ + yi = y - ( x << 1 ); + x = x + ( y << 1 ); + y = yi; + theta += *arctanptr++; /* Add angle */ + } + + i = 0; + do + { + if ( y < 0 ) + { + /* Rotate positive */ + yi = y + ( x >> i ); + x = x - ( y >> i ); + y = yi; + theta -= *arctanptr++; + } + else + { + /* Rotate negative */ + yi = y - ( x >> i ); + x = x + ( y >> i ); + y = yi; + theta += *arctanptr++; + } + } while ( ++i < AF_TRIG_MAX_ITERS ); + + /* round theta */ + if ( theta >= 0 ) + theta = ( theta + 2 ) & -4; + else + theta = - (( -theta + 2 ) & -4); + + vec->x = x; + vec->y = theta; + } + + + /* documentation is in fttrigon.h */ + + FT_LOCAL_DEF( AF_Angle ) + af_angle_atan( FT_Fixed dx, + FT_Fixed dy ) + { + FT_Vector v; + + + if ( dx == 0 && dy == 0 ) + return 0; + + v.x = dx; + v.y = dy; + af_angle_prenorm( &v ); + af_angle_pseudo_polarize( &v ); + + return v.y; + } + + + + FT_LOCAL_DEF( AF_Angle ) + af_angle_diff( AF_Angle angle1, + AF_Angle angle2 ) + { + AF_Angle delta = angle2 - angle1; + + delta %= AF_ANGLE_2PI; + if ( delta < 0 ) + delta += AF_ANGLE_2PI; + + if ( delta > AF_ANGLE_PI ) + delta -= AF_ANGLE_2PI; + + return delta; + } + diff --git a/src/autofit/afhints.c b/src/autofit/afhints.c new file mode 100644 index 000000000..b7d8efbbf --- /dev/null +++ b/src/autofit/afhints.c @@ -0,0 +1,626 @@ +#include "afhints.h" + +#ifdef AF_DEBUG + +#include + + void + af_outline_hints_dump_edges( AF_OutlineHints hints ) + { + AF_Edge edges; + AF_Edge edge_limit; + AF_Segment segments; + FT_Int dimension; + + + edges = hints->horz_edges; + edge_limit = edges + hints->num_hedges; + segments = hints->horz_segments; + + for ( dimension = 1; dimension >= 0; dimension-- ) + { + AF_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 == AF_DIR_UP + ? "up" + : ( edge->dir == AF_DIR_DOWN + ? "down" + : ( edge->dir == AF_DIR_LEFT + ? "left" + : ( edge->dir == AF_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 = hints->vert_edges; + edge_limit = edges + hints->num_vedges; + segments = hints->vert_segments; + } + } + + + /* A function used to dump the array of linked segments */ + void + af_outline_hints_dump_segments( AF_OutlineHints hints ) + { + AF_Segment segments; + AF_Segment segment_limit; + AF_Point points; + FT_Int dimension; + + + points = hints->points; + segments = hints->horz_segments; + segment_limit = segments + hints->num_hsegments; + + for ( dimension = 1; dimension >= 0; dimension-- ) + { + AF_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 == AF_DIR_UP + ? "up" + : ( seg->dir == AF_DIR_DOWN + ? "down" + : ( seg->dir == AF_DIR_LEFT + ? "left" + : ( seg->dir == AF_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 = hints->vert_segments; + segment_limit = segments + hints->num_vsegments; + } + } + +#endif /* AF_DEBUG */ + + + /* compute the direction value of a given vector */ + FT_LOCAL_DEF( AF_Direction ) + af_direction_compute( FT_Pos dx, + FT_Pos dy ) + { + AF_Direction dir; + FT_Pos ax = ABS( dx ); + FT_Pos ay = ABS( dy ); + + + dir = AF_DIR_NONE; + + /* atan(1/12) == 4.7 degrees */ + + /* test for vertical direction */ + if ( ax * 12 < ay ) + { + dir = dy > 0 ? AF_DIR_UP : AF_DIR_DOWN; + } + /* test for horizontal direction */ + else if ( ay * 12 < ax ) + { + dir = dx > 0 ? AF_DIR_RIGHT : AF_DIR_LEFT; + } + + return dir; + } + + + /* compute all inflex points in a given glyph */ + static void + af_outline_hints_compute_inflections( AF_OutlineHints hints ) + { + AF_Point* contour = hints->contours; + AF_Point* contour_limit = contour + hints->num_contours; + + + /* load original coordinates in (u,v) */ + af_outline_hints_setup_uv( hints, outline, AF_UV_FXY ); + + /* do each contour separately */ + for ( ; contour < contour_limit; contour++ ) + { + AF_Point point = contour[0]; + AF_Point first = point; + AF_Point start = point; + AF_Point end = point; + AF_Point before; + AF_Point after; + AF_Angle angle_in, angle_seg, angle_out; + AF_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 ); + + angle_seg = af_angle( end->u - start->u, + end->v - start->v ); + + /* 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 ); + + angle_in = af_angle( start->u - before->u, + start->v - before->v ); + + } while ( angle_in == angle_seg ); + + first = start; + diff_in = af_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 = af_angle( after->u - end->u, + after->v - end->v ); + + } while ( angle_out == angle_seg ); + + diff_out = af_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 |= AF_FLAG_INFLECTION; + start = start->next; + + } while ( start != end ); + + start->flags |= AF_FLAG_INFLECTION; + } + + start = end; + end = after; + angle_seg = angle_out; + diff_in = diff_out; + + } while ( !finished ); + + Skip: + ; + } + } + + + + FT_LOCAL_DEF( void ) + af_outline_hints_init( AF_OutlineHints hints, + FT_Memory memory ) + { + FT_ZERO( hints ); + hints->memory = memory; + } + + + + FT_LOCAL_DEF( void ) + af_outline_hints_done( AF_OutlineHints hints ) + { + if ( hints && hints->memory ) + { + FT_Memory memory = hints->memory; + AF_Dimension dim; + + /* note that we don't need to free the segment and edge + * buffers, since they're really within the hints->points array + */ + for ( dim = 0; dim < 2; dim++ ) + { + AF_AxisHints axis = &hints->axis[ dim ]; + + axis->num_segments = 0; + axis->num_edges = 0; + axis->segments = NULL; + axis->edges = NULL; + } + + FT_FREE( hints->contours ); + hints->max_contours = 0; + hints->num_contours = 0; + + FT_FREE( hints->points ); + hints->num_points = 0; + hints->max_points = 0; + + hints->memory = NULL; + } + } + + + + FT_LOCAL_DEF( FT_Error ) + af_outline_hints_reset( AF_OutlineHints hints, + FT_Outline* outline, + FT_Fixed x_scale, + FT_Fixed y_scale ) + { + FT_Error error = AF_Err_Ok; + + FT_UInt old_max, new_max; + + hints->num_points = 0; + hints->num_contours = 0; + + hints->axis[0].num_segments = 0; + hints->axis[0].num_edges = 0; + hints->axis[1].num_segments = 0; + hints->axis[1].num_edges = 0; + + /* first of all, reallocate the contours array when necessary + */ + new_max = (FT_UInt) outline->n_contours; + old_max = hints->max_contours; + if ( new_max > old_max ) + { + new_max = (new_max + 3) & ~3; + + if ( FT_RENEW_ARRAY( hints->contours, old_max, new_max ) ) + goto Exit; + + hints->max_contours = new_max; + } + + /* then, reallocate the points, segments & edges arrays if needed -- + * note that we reserved two additional point positions, used to + * hint metrics appropriately + */ + new_max = (FT_UInt)( outline->n_points + 2 ); + old_max = hints->max_points; + if ( new_max > old_max ) + { + FT_Byte* items; + FT_ULong off1, off2, off3; + + /* we store in a single buffer the following arrays: + * + * - an array of N AF_PointRec items + * - an array of 2*N AF_SegmentRec items + * - an array of 2*N AF_EdgeRec items + * + */ + + new_max = ( new_max + 2 + 7 ) & ~7; + +#undef OFF_INCREMENT +#define OFF_INCREMENT( _off, _type, _count ) \ + ((((_off) + sizeof(_type)) & ~(sizeof(_type)) + ((_count)*sizeof(_type))) + + off1 = OFF_INCREMENT( 0, AF_PointRec, new_max ); + off2 = OFF_INCREMENT( off1, AF_SegmentRec, new_max ); + off3 = OFF_INCREMENT( off2, AF_EdgeRec, new_max*2 ); + + FT_FREE( hints->points ); + + if ( FT_ALLOC( items, off3 ) ) + { + hints->max_points = 0; + hints->axis[0].segments = NULL; + hints->axis[0].edges = NULL; + hints->axis[1].segments = NULL; + hints->axis[1].edges = NULL; + goto Exit; + } + + /* readjust some pointers + */ + hints->max_points = new_max; + hints->points = (AF_Point) items; + + hints->axis[0].segments = (AF_Segment)( items + off1 ); + hints->axis[1].segments = hints->axis[0].segments + new_max; + + hints->axis[0].edges = (AF_Edge) ( items + off2 ); + hints->axis[1].edges = hints->axis[0].edges + new_max; + } + + hints->num_points = outline->n_points; + hints->num_contours = outline->n_contours; + + + /* 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. */ + /* */ + hints->axis[ AF_DIMENSION_HORZ ].major_dir = AF_DIR_UP; + hints->axis[ AF_DIMENSION_VERT ].major_dir = AF_DIR_LEFT; + + if ( FT_Outline_Get_Orientation( outline ) == FT_ORIENTATION_POSTSCRIPT ) + { + hints->axis[ AF_DIMENSION_HORZ ].major_dir = AF_DIR_DOWN; + hints->axis[ AF_DIMENSION_VERT ].major_dir = AF_DIR_RIGHT; + } + + hints->x_scale = x_scale; + hints->y_scale = y_scale; + + points = hints->points; + if ( hints->num_points == 0 ) + goto Exit; + + { + /* do one thing at a time -- it is easier to understand, and */ + /* the code is clearer */ + AF_Point point; + AF_Point point_limit = points + hints->num_points; + + + /* compute coordinates & bezier flags */ + { + FT_Vector* vec = outline->points; + char* tag = outline->tags; + + + for ( point = points; point < point_limit; point++, vec++, tag++ ) + { + 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 ); + + switch ( FT_CURVE_TAG( *tag ) ) + { + case FT_CURVE_TAG_CONIC: + point->flags = AF_FLAG_CONIC; + break; + case FT_CURVE_TAG_CUBIC: + point->flags = AF_FLAG_CUBIC; + break; + default: + point->flags = 0; + ; + } + } + } + + /* compute `next' and `prev' */ + { + FT_Int contour_index; + AF_Point prev; + AF_Point first; + AF_Point end; + + + contour_index = 0; + + first = points; + end = points + outline->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 */ + { + AF_Point* contour = hints->contours; + AF_Point* contour_limit = contour + hints->num_contours; + short* end = outline->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++ ) + { + AF_Point prev; + AF_Point next; + FT_Pos in_x, in_y, out_x, out_y; + + + prev = point->prev; + in_x = point->fx - prev->fx; + in_y = point->fy - prev->fy; + + point->in_dir = af_compute_direction( in_x, in_y ); + + next = point->next; + out_x = next->fx - point->fx; + out_y = next->fy - point->fy; + + point->out_dir = af_compute_direction( out_x, out_y ); + + if ( point->flags & ( AF_FLAG_CONIC | AF_FLAG_CUBIC ) ) + { + Is_Weak_Point: + point->flags |= AF_FLAG_WEAK_INTERPOLATION; + } + else if ( point->out_dir == point->in_dir ) + { + AF_Angle angle_in, angle_out, delta; + + + if ( point->out_dir != AF_DIR_NONE ) + goto Is_Weak_Point; + + angle_in = af_angle( in_x, in_y ); + angle_out = af_angle( out_x, out_y ); + delta = af_angle_diff( angle_in, angle_out ); + + if ( delta < 2 && delta > -2 ) + goto Is_Weak_Point; + } + else if ( point->in_dir == -point->out_dir ) + goto Is_Weak_Point; + } + } + } + + /* compute inflection points + */ + af_outline_hints_compute_inflections( hints ); + + Exit: + return error; + } + + + FT_LOCAL_DEF( void ) + af_outline_hints_setup_uv( AF_OutlineHints hints, + AF_UV source ) + { + AF_Point point = hints->points; + AF_Point point_limit = point + hints->num_points; + + + switch ( source ) + { + case AF_UV_FXY: + for ( ; point < point_limit; point++ ) + { + point->u = point->fx; + point->v = point->fy; + } + break; + + case AF_UV_FYX: + for ( ; point < point_limit; point++ ) + { + point->u = point->fy; + point->v = point->fx; + } + break; + + case AF_UV_OXY: + for ( ; point < point_limit; point++ ) + { + point->u = point->ox; + point->v = point->oy; + } + break; + + case AF_UV_OYX: + for ( ; point < point_limit; point++ ) + { + point->u = point->oy; + point->v = point->ox; + } + break; + + case AF_UV_YX: + for ( ; point < point_limit; point++ ) + { + point->u = point->y; + point->v = point->x; + } + break; + + case AF_UV_OX: + for ( ; point < point_limit; point++ ) + { + point->u = point->x; + point->v = point->ox; + } + break; + + case AF_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; + } + } + } + + + diff --git a/src/autofit/afhints.h b/src/autofit/afhints.h new file mode 100644 index 000000000..4d736514b --- /dev/null +++ b/src/autofit/afhints.h @@ -0,0 +1,233 @@ +#ifndef __AFHINTS_H__ +#define __AFHINTS_H__ + +#include "aftypes.h" + +FT_BEGIN_HEADER + + /* + * The definition of outline hints. These are shared by all + * script analysis routines + * + */ + + typedef enum + { + AF_DIMENSION_HORZ = 0, /* x coordinates, i.e. vertical segments & edges */ + AF_DIMENSION_VERT = 1, /* y coordinates, i.e. horizontal segments & edges */ + + AF_DIMENSION_MAX /* do not remove */ + + } AF_Dimension; + + + /* hint directions -- the values are computed so that two vectors are */ + /* in opposite directions iff `dir1+dir2 == 0' */ + typedef enum + { + AF_DIR_NONE = 4, + AF_DIR_RIGHT = 1, + AF_DIR_LEFT = -1, + AF_DIR_UP = 2, + AF_DIR_DOWN = -2 + + } AF_Direction; + + + /* point hint flags */ + typedef enum + { + AF_FLAG_NONE = 0, + + /* point type flags */ + AF_FLAG_CONIC = (1 << 0), + AF_FLAG_CUBIC = (1 << 1), + AF_FLAG_CONTROL = AF_FLAG_CONIC | AF_FLAG_CUBIC, + + /* point extremum flags */ + AF_FLAG_EXTREMA_X = (1 << 2), + AF_FLAG_EXTREMA_Y = (1 << 3), + + /* point roundness flags */ + AF_FLAG_ROUND_X = (1 << 4), + AF_FLAG_ROUND_Y = (1 << 5), + + /* point touch flags */ + AF_FLAG_TOUCH_X = (1 << 6), + AF_FLAG_TOUCH_Y = (1 << 7), + + /* candidates for weak interpolation have this flag set */ + AF_FLAG_WEAK_INTERPOLATION = (1 << 8), + + /* all inflection points in the outline have this flag set */ + AF_FLAG_INFLECTION = (1 << 9) + + } AF_Flags; + + + /* edge hint flags */ + typedef enum + { + AF_EDGE_NORMAL = 0, + AF_EDGE_ROUND = (1 << 0), + AF_EDGE_SERIF = (1 << 1), + AF_EDGE_DONE = (1 << 2) + + } AF_Edge_Flags; + + + + typedef struct AF_PointRec_* AF_Point; + typedef struct AF_SegmentRec_* AF_Segment; + typedef struct AF_EdgeRec_* AF_Edge; + + + typedef struct AF_PointRec_ + { + AF_Flags flags; /* point flags used by hinter */ + FT_Pos ox, oy; /* original, scaled position */ + FT_Pos fx, fy; /* original, unscaled position (font units) */ + FT_Pos x, y; /* current position */ + FT_Pos u, v; /* current (x,y) or (y,x) depending on context */ + + AF_Direction in_dir; /* direction of inwards vector */ + AF_Direction out_dir; /* direction of outwards vector */ + + AF_Point next; /* next point in contour */ + AF_Point prev; /* previous point in contour */ + + } AF_PointRec; + + + typedef struct AF_SegmentRec_ + { + AF_Edge_Flags flags; /* edge/segment flags for this segment */ + AF_Direction dir; /* segment direction */ + FT_Pos pos; /* position of segment */ + FT_Pos min_coord; /* minimum coordinate of segment */ + FT_Pos max_coord; /* maximum coordinate of segment */ + + AF_Edge edge; /* the segment's parent edge */ + AF_Segment edge_next; /* link to next segment in parent edge */ + + AF_Segment link; /* link segment */ + AF_Segment serif; /* primary segment for serifs */ + FT_Pos num_linked; /* number of linked segments */ + FT_Pos score; + + AF_Point first; /* first point in edge segment */ + AF_Point last; /* last point in edge segment */ + AF_Point* contour; /* ptr to first point of segment's contour */ + + } AF_SegmentRec; + + + typedef struct AF_EdgeRec_ + { + FT_Pos fpos; /* original, unscaled position (font units) */ + FT_Pos opos; /* original, scaled position */ + FT_Pos pos; /* current position */ + + AF_Edge_Flags flags; /* edge flags */ + AF_Direction dir; /* edge direction */ + FT_Fixed scale; /* used to speed up interpolation between edges */ + FT_Pos* blue_edge; /* non-NULL if this is a blue edge */ + + AF_Edge link; + AF_Edge serif; + FT_Int num_linked; + + FT_Int score; + + AF_Segment first; + AF_Segment last; + + + } AF_EdgeRec; + + + typedef struct AF_AxisHintsRec_ + { + FT_Int num_segments; + AF_Segment segments; + + FT_Int num_edges; + AF_Edge edges; + + AF_Direction major_dir; + + } AF_AxisHintsRec, *AF_AxisHints; + + + typedef struct AF_OutlineHintsRec_ + { + FT_Memory memory; + + FT_Fixed x_scale; + FT_Fixed y_scale; + FT_Pos edge_distance_threshold; + + FT_Int max_points; + FT_Int num_points; + AF_Point points; + + FT_Int max_contours; + FT_Int num_contours; + AF_Point* contours; + + AF_AxisHintsRec axis[ AF_DIMENSION_MAX ]; + + } AF_OutlineHintsRec; + + + + + FT_LOCAL( AF_Direction ) + af_direction_compute( FT_Pos dx, + FT_Pos dy ); + + + FT_LOCAL( void ) + af_outline_hints_init( AF_OutlineHints hints ); + + + /* used to set the (u,v) fields of each AF_Point in a AF_OutlineHints + * object. + */ + typedef enum AH_UV_ + { + AH_UV_FXY, /* (u,v) = (fx,fy) */ + AH_UV_FYX, /* (u,v) = (fy,fx) */ + AH_UV_OXY, /* (u,v) = (ox,oy) */ + AH_UV_OYX, /* (u,v) = (oy,ox) */ + AH_UV_OX, /* (u,v) = (ox,x) */ + AH_UV_OY, /* (u,v) = (oy,y) */ + AH_UV_YX, /* (u,v) = (y,x) */ + AH_UV_XY /* (u,v) = (x,y) * should always be last! */ + + } AH_UV; + + FT_LOCAL_DEF( void ) + af_outline_hints_setup_uv( AF_OutlineHints hints, + AF_UV source ); + + + /* recomputes all AF_Point in a AF_OutlineHints from the definitions + * in a source outline + */ + FT_LOCAL( FT_Error ) + af_outline_hints_reset( AF_OutlineHints hints, + FT_Outline* outline, + FT_Fixed x_scale, + FT_Fixed y_scale ); + + FT_LOCAL( void ) + af_outline_hints_done( AF_OutlineHints hints ); + + + +/* */ + +FT_END_HEADER + +#endif /* __AFHINTS_H__ */ diff --git a/src/autofit/aflatin.c b/src/autofit/aflatin.c new file mode 100644 index 000000000..f92f67a6a --- /dev/null +++ b/src/autofit/aflatin.c @@ -0,0 +1,755 @@ +#include "aflatin.h" + + FT_LOCAL_DEF( void ) + af_latin_hints_compute_segments( AF_OutlineHints hints, + AF_Dimension dim ) + { + AF_AxisHints axis = &hints->axis[dim]; + AF_Segment segments = axis->segments; + AF_Segment segment = segments; + FT_Int num_segments = 0; + AF_Point* contour = hints->contours; + AF_Point* contour_limit = contour + hints->num_contours; + AF_Direction major_dir; + +#ifdef AF_HINT_METRICS + AF_Point min_point = 0; + AF_Point max_point = 0; + FT_Pos min_coord = 32000; + FT_Pos max_coord = -32000; +#endif + + major_dir = ABS( axis->major_dir ); + segment_dir = major_dir; + + /* set up (u,v) in each point */ + af_setup_uv( outline, (dim == AF_DIMENSION_HORZ) + ? AF_UV_FXY, + : AF_UV_FYX ); + + + /* do each contour separately */ + for ( ; contour < contour_limit; contour++ ) + { + AF_Point point = contour[0]; + AF_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 AF_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 ( ABS( last->out_dir ) == major_dir && + ABS( point->out_dir ) == major_dir ) + { + /* we are already on an edge, try to locate its start */ + last = point; + + for (;;) + { + point = point->prev; + if ( 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 ) & + AF_FLAG_CONTROL ) + segment->flags |= AF_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 && 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 = AF_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 AF_HINT_METRICS + if ( point == max_point ) + max_point = 0; + + if ( point == min_point ) + min_point = 0; +#endif + } + + point = point->next; + } + + } /* contours */ + +#ifdef AF_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 ( dim == AF_DIMENSION_HORZ ) + { + AF_Point point = hints->points; + AF_Point point_limit = point + hints->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 = AF_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 = AF_EDGE_NORMAL; + segment->first = max_point; + segment->last = max_point; + segment->pos = max_pos; + segment->score = 32000; + segment->link = NULL; + + num_segments++; + segment++; + } + } +#endif /* AF_HINT_METRICS */ + + axis->num_segments = num_segments; + } + + + FT_LOCAL_DEF( void ) + af_latin_hints_link_segments( AF_OutlineHints hints, + AF_Dimension dim ) + { + AF_AxisHints axis = &hints->axis[dim]; + AF_Segment segments = axis->segments; + AF_Segment segment_limit = segments + axis->num_segments; + AF_Direction major_dir = axis->major_dir; + AF_Segment seg1, seg2; + + /* 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; + } + } + } + } + } + + /* 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; + } + } + } + } + + + FT_LOCAL_DEF( void ) + af_latin_hints_compute_edges( AF_OutlineHints hints, + AF_Dimension dim ) + { + AF_AxisHints axis = &hints->axis[dim]; + AF_Edge edges = axis->edges; + AF_Edge edge, edge_limit; + + AF_Segment segments = axis->segments; + AF_Segment segment_limit = segments + axis->num_segments; + AF_Segment seg; + + AF_Direction up_dir; + FT_Fixed scale; + FT_Pos edge_distance_threshold; + + + scale = ( dim == AF_DIMENSION_HORZ ) ? hints->x_scale + : hints->y_scale; + + up_dir = ( dim == AF_DIMENSION_HORZ ) ? AF_DIR_UP + : AF_DIR_RIGHT; + + /*********************************************************************/ + /* */ + /* 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++ ) + { + AF_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 & AF_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 ) + { + AF_Edge edge2; + AF_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 |= AF_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 = AF_EDGE_NORMAL; + + if ( is_round > 0 && is_round >= is_straight ) + edge->flags |= AF_EDGE_ROUND; + + /* set the edge's main direction */ + edge->dir = AF_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; + } + } + + + /*************************************************************************/ + /* */ + /* */ + /* af_outline_detect_features */ + /* */ + /* */ + /* Performs feature detection on a given AF_OutlineRec object. */ + /* */ + FT_LOCAL_DEF( void ) + af_latin_hints_detect_features( AF_OutlineHints hints, + AF_Dimension dim ) + { + af_latin_hints_compute_segments( hints, dim ); + af_latin_hints_link_segments ( hints, dim ); + af_latin_hints_compute_edges ( hints dim ); + } + + + /*************************************************************************/ + /* */ + /* */ + /* af_outline_compute_blue_edges */ + /* */ + /* */ + /* 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 ) + af_latin_hints_compute_blue_edges( AF_OutlineHints outline, + AF_Face_Globals face_globals ) + { + AF_Edge edge = outline->horz_edges; + AF_Edge edge_limit = edge + outline->num_hedges; + AF_Globals globals = &face_globals->design; + FT_Fixed y_scale = outline->y_scale; + + FT_Bool blue_active[AF_BLUE_MAX]; + + + /* compute which blue zones are active, i.e. have their scaled */ + /* size < 3/4 pixels */ + { + AF_Blue blue; + FT_Bool check = 0; + + + for ( blue = AF_BLUE_CAPITAL_TOP; blue < AF_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++ ) + { + AF_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 = AF_BLUE_CAPITAL_TOP; blue < AF_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( AF_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 & AF_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; + } + } + + + /*************************************************************************/ + /* */ + /* */ + /* af_outline_scale_blue_edges */ + /* */ + /* */ + /* 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 ) + af_outline_hints_scale_blue_edges( AF_OutlineHints hints ) outline, + { + AF_AxisHints axis = &hints->axis[ AF_DIMENSION_VERT ]; + AF_Edge edge = axis->edges; + AF_Edge edge_limit = edge + axis->num_edges; + 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; + } + } + diff --git a/src/autofit/aflatin.h b/src/autofit/aflatin.h new file mode 100644 index 000000000..5c716e87f --- /dev/null +++ b/src/autofit/aflatin.h @@ -0,0 +1,89 @@ +#ifndef __AFLATIN_H__ +#define __AFLATIN_H__ + +#include "afhints.h" + +FT_BEGIN_HEADER + + /* + * the latin-specific script class + * + */ + FT_LOCAL( const FT_ScriptClassRec ) af_latin_script_class; + + /* + * the following declarations could be embedded in the file "aflatin.c" + * they've been made semi-public to allow alternate script hinters to + * re-use some of them + */ + + /* + * Latin (global) metrics management + * + */ + +#define AF_LATIN_MAX_WIDTHS 16 +#define AF_LATIN_MAX_BLUES 32 + + typedef struct AF_LatinAxisRec_ + { + FT_Fixed scale; + FT_Pos delta; + + FT_UInt width_count; + AF_WidthRec widths[ AF_LATIN_MAX_WIDTHS ]; + + /* ignored for horizontal metrics */ + FT_Bool control_overshoot; + FT_UInt blue_count; + AF_WidthRec blue_refs [ AF_MAX_BLUES ]; + AF_WidthRec blue_shoots[ AF_MAX_BLUES ]; + + } AF_LatinAxisRec, *AF_LatinAxis; + + typedef struct AF_LatinMetricsRec_ + { + AF_OutlineMetricsRec root; + AF_LatinAxisRec axis[ AF_DIMENSION_MAX ]; + + } AF_LatinMetricsRec, *AF_LatinMetrics; + + + FT_LOCAL( FT_Error ) + af_latin_metrics_init( AF_LatinMetrics metrics, + FT_Face face ); + + FT_LOCAL( void ) + af_latin_metrics_scale( AF_LatinMetrics metrics, + AF_Scaler scaler ); + + + /* + * Latin (glyph) hints management + * + */ + + FT_LOCAL( + + FT_LOCAL( void ) + af_latin_hints_compute_segments( AF_OutlineHints hints, + AF_Dimension dim ); + + FT_LOCAL( void ) + af_latin_hints_link_segments( AF_OutlineHints hints, + AF_Dimension dim ); + + FT_LOCAL( void ) + af_latin_hints_compute_edges( AF_OutlineHints hints, + AF_Dimension dim ); + + FT_LOCAL( void ) + af_latin_hints_init( AF_OutlineHints hints, + AF_Dimension dim ); + + +/* */ + +FT_END_HEADER + +#endif /* __AFLATIN_H__ */ diff --git a/src/autofit/aftypes.h b/src/autofit/aftypes.h new file mode 100644 index 000000000..a8a2b8d89 --- /dev/null +++ b/src/autofit/aftypes.h @@ -0,0 +1,289 @@ +#ifndef __AFTYPES_H__ +#define __AFTYPES_H__ + +FT_BEGIN_HEADER + + /**************************************************************************/ + /**************************************************************************/ + /***** *****/ + /***** D E B U G G I N G *****/ + /***** *****/ + /**************************************************************************/ + /**************************************************************************/ + +#define xxAF_DEBUG + +#ifdef AF_DEBUG + +# include +# define AF_LOG( x ) printf ## x + +#else + +# define AF_LOG( x ) do ; while ( 0 ) /* nothing */ + +#endif /* AF_DEBUG */ + + /**************************************************************************/ + /**************************************************************************/ + /***** *****/ + /***** A N G L E T Y P E S *****/ + /***** *****/ + /**************************************************************************/ + /**************************************************************************/ + + /* + * Angle type. The auto-fitter doesn't need a very high angular accuracy, + * and this allows us to speed up some computations considerably with a + * light Cordic algorithm (see afangle.c) + * + */ + + typedef FT_Int AF_Angle; + +#define AF_ANGLE_PI 128 +#define AF_ANGLE_2PI (AF_ANGLE_PI*2) +#define AF_ANGLE_PI2 (AF_ANGLE_PI/2) +#define AF_ANGLE_PI4 (AF_ANGLE_PI/4) + + /* + * compute the angle of a given 2-D vector + * + */ + FT_LOCAL( AF_Angle ) + af_angle( FT_Pos dx, + FT_Pos dy ); + + + /* + * computes "angle2 - angle1", the result is always within + * the range [ -AF_ANGLE_PI .. AF_ANGLE_PI-1 ] + * + */ + FT_LOCAL( AF_Angle ) + af_angle_diff( AF_Angle angle1, + AF_Angle angle2 ); + + + /**************************************************************************/ + /**************************************************************************/ + /***** *****/ + /***** O U T L I N E S *****/ + /***** *****/ + /**************************************************************************/ + /**************************************************************************/ + + typedef struct AF_OutlineHintsRec_* AF_OutlineHints; + + typedef struct AF_GlobalHintsRec_* AF_GlobalHints; + + typedef struct AF_OutlineRec_ + { + FT_Memory memory; + FT_Face face; + FT_OutlineRec outline; + FT_UInt outline_resolution; + + FT_Int advance; + FT_UInt metrics_resolution; + + AF_OutlineHints hints; + + } AF_OutlineRec; + + /**************************************************************************/ + /**************************************************************************/ + /***** *****/ + /***** G L O B A L M E T R I C S *****/ + /***** *****/ + /**************************************************************************/ + /**************************************************************************/ + + /* + * the following define global metrics in a _single_ dimension + * + * the "blue_refs" and "blue_shoots" arrays are ignored in + * the horizontal dimension + */ + + typedef struct AF_WidthRec_ + { + FT_Pos org; /* original position/width in font units */ + FT_Pos cur; /* current/scaled position/width in device sub-pixels */ + FT_Pos fit; /* current/fitted position/width in device sub-pixels */ + + } AF_WidthRec, *AF_Width; + + +#define AF_MAX_WIDTHS 16 +#define AF_MAX_BLUES 32 + + typedef struct AF_GlobalMetricsRec_ + { + FT_Int num_widths; + AF_WidthRec widths[ AF_MAX_WIDTHS ]; + + FT_Fixed scale; /* used to scale from org to cur with: */ + FT_Pos delta; /* x_cur = x_org * scale + delta */ + + /* ignored for horizontal metrics */ + AF_WidthRec blue_refs [ AF_MAX_BLUES ]; + AF_WidthRec blue_shoots[ AF_MAX_BLUES ]; + + FT_Bool control_overshoot; + + } AF_GlobalMetricsRec, *AF_GlobalMetrics; + + + /**************************************************************************/ + /**************************************************************************/ + /***** *****/ + /***** S C A L E R S *****/ + /***** *****/ + /**************************************************************************/ + /**************************************************************************/ + + /* + * A scaler models the target pixel device that will receive the + * auto-hinted glyph image + * + */ + + typedef enum + { + AF_SCALER_FLAG_NO_HORIZONTAL = 1, /* disable horizontal hinting */ + AF_SCALER_FLAG_NO_VERTICAL = 2, /* disable vertical hinting */ + AF_SCALER_FLAG_NO_ADVANCE = 4 /* disable advance hinting */ + + } AF_ScalerFlags; + + + typedef struct AF_ScalerRec_ + { + FT_Face face; /* source font face */ + FT_Fixed x_scale; /* from font units to 1/64th device pixels */ + FT_Fixed y_scale; /* from font units to 1/64th device pixels */ + FT_Pos x_delta; /* in 1/64th device pixels */ + FT_Pos y_delta; /* in 1/64th device pixels */ + FT_Render_Mode render_mode; /* monochrome, anti-aliased, LCD, etc.. */ + FT_UInt32 flags; /* additionnal control flags, see above */ + + } AF_ScalerRec, *AF_Scaler; + + + + /**************************************************************************/ + /**************************************************************************/ + /***** *****/ + /***** S C R I P T S *****/ + /***** *****/ + /**************************************************************************/ + /**************************************************************************/ + + /* + * the list of know scripts. Each different script correspond to the + * following information: + * + * - a set of Unicode ranges to test wether the face supports the + * script + * + * - a specific global analyzer that will compute global metrics + * specific to the script. + * + * - a specific hinting routine + * + * all scripts should share the same analysis routine though + */ + typedef enum + { + AF_SCRIPT_LATIN = 0, + /* add new scripts here */ + + AF_SCRIPT_MAX /* do not remove */ + + } AF_Script; + + + typedef struct AF_ScriptClassRec_ const* AF_ScriptClass; + + /* + * root class for script-specific metrics + */ + typedef struct AF_ScriptMetricsRec_ + { + AF_ScriptClass script_class; + AF_GlobalMetricsRec horz_metrics; + AF_GlobalMetricsRec vert_metrics; + + } AF_ScriptMetricsRec, *AF_ScriptMetrics; + + + /* this function parses a FT_Face to compute global metrics for + * a specific script + */ + typedef FT_Error (*AF_Script_InitMetricsFunc)( AF_ScriptMetrics metrics, + FT_Face face ); + + typedef void (*AF_Script_ScaleMetricsFunc)( AF_ScriptMetrics metrics, + AF_Scaler scaler ); + + typedef void (*AF_Script_DoneMetricsFunc)( AF_ScriptMetrics metrics ); + + + typedef FT_Error (*AF_Script_InitHintsFunc)( AF_OutlineHints hints, + AF_Scaler scaler, + AF_ScriptMetrics metrics ); + + typedef void (*AF_Script_ApplyHintsFunc)( AF_OutlineHints hints ); + + + typedef struct AF_Script_UniRangeRec_ + { + FT_UInt32 first; + FT_UInt32 last; + + } AF_Script_UniRangeRec, *AF_Script_UniRange; + + + typedef struct AF_ScriptClassRec_ + { + AF_Script script; + AF_Scipt_UniRange script_uni_ranges; /* last must be { 0, 0 } */ + + FT_UInt script_metrics_size; + AF_Script_InitMetricsFunc script_metrics_init; + AF_Script_ScaleMetricsFunc script_metrics_scale; + AF_Script_DoneMetricsFunc script_metrics_done; + + } AF_ScriptClassRec; + + + + /**************************************************************************/ + /**************************************************************************/ + /***** *****/ + /***** F A C E G L O B A L S *****/ + /***** *****/ + /**************************************************************************/ + /**************************************************************************/ + + /* + * models the global hints data for a given face, decomposed into + * script-specific items.. + * + */ + typedef struct AF_FaceGlobalsRec_ + { + FT_Face face; + FT_UInt glyph_count; /* same as face->num_glyphs */ + FT_Byte* glyph_scripts; /* maps each gindex to a script */ + + FT_ScriptMetrics metrics[ AF_SCRIPT_MAX ]; + + } AF_FaceGlobalsRec, *AF_FaceGlobals; + +/* */ + +FT_END_HEADER + +#endif /* __AFTYPES_H__ */ diff --git a/src/base/ftoutln.c b/src/base/ftoutln.c index c59043bc0..4e49514ba 100644 --- a/src/base/ftoutln.c +++ b/src/base/ftoutln.c @@ -26,6 +26,7 @@ #include #include FT_OUTLINE_H #include FT_INTERNAL_OBJECTS_H +#include FT_TRIGONOMETRY_H /*************************************************************************/ @@ -655,4 +656,140 @@ } + + typedef FT_OrientationExtremumRec_ + { + FT_Int index; + FT_Int pos; + FT_Int first; + FT_Int last; + + } FT_OrientationExtremumRec; + + + static FT_Orientation + ft_orientation_extremum_compute( FT_OrientationExtremumRec* extremum, + FT_Outline* outline ) + { + FT_Vector *point, *first, *last, *prev, *next; + FT_Vector* points = outline->points; + FT_Angle angle_in, angle_out; + + /* compute the previous and next points in the same contour + */ + point = points + extremum->index; + first = points + extremum->first; + last = points + extremum->last; + + do + { + prev = ( point == first ) ? last : point-1; + + if ( prev == point ) + return FT_ORIENTATION_TRUETYPE; /* degenerate case */ + + } while ( prev->x != point->x || prev->y != point->y ); + + do + { + next = ( point == last ) ? first : point+1; + + if ( next == point ) + return FT_ORIENTATION_TRUETYPE; /* shouldn't happen */ + + } while ( next->x != point->x || next->y != point->y ); + + /* now, compute the orientation of the "out" vector relative + * to the "in" vector. + */ + angle_in = FT_Atan2( point->x - prev->x, point->y - prev->y ); + angle_out = FT_Atan2( next->x - point->x, next->y - point->y ); + + return ( FT_Angle_Diff( angle_in, angle_out ) >= 0 ) + ? FT_ORIENTATION_TRUETYPE + : FT_ORIENTATION_POSTSCRIPT; + } + + + FT_EXPORT_DEF( FT_Orientation ) + FT_Outline_Get_Orientation( FT_Outline* outline ) + { + FT_Orientation result = FT_ORIENTATION_TRUETYPE; + + if ( outline && outline->n_points > 0 ) + { + FT_OrientationExtremumRec xmin, ymin, xmax, ymax; + FT_Int n; + FT_Int first, last; + FT_Vector* points = outline->points; + + xmin.pos = ymin.pos = +32768L; + xmax.pos = ymax.pos = -32768L; + + xmin.index = ymin.index = xmax.index = ymax.index = -1; + + first = 0; + for ( n = 0; n < outline->n_contours; n++, first = last+1 ) + { + last = outline->contours[n]; + + /* skip single-point contours, these are degenerated cases + */ + if ( last > first+1 ) + { + FT_Int i; + + for ( i = first; i < last; i++ ) + { + x = points[i].x; + y = points[i].y; + + if ( x < xmin.pos ) + { + xmin.pos = x; + xmin.index = i; + xmin.first = first; + xmin.last = last; + } + if ( x > xmax.pos ) + { + xmax.pos = x; + xmax.index = i; + xmax.first = first; + xmax.last = last; + } + if ( y < ymin.pos ) + { + ymin.pos = y; + ymin.index = i; + ymin.first = first; + ymin.last = last; + } + if ( y > ymax.pos ) + { + ymax.pos = y; + ymax.index = i; + ymax.first = first; + ymax.last = last; + } + } + } + + if ( xmin.index >= 0 ) + result = ft_orientation_extremum_compute( &xmin, outline ); + + else if ( xmax.index >= 0 ) + result = ft_orientation_extremum_compute( &xmax, outline ); + + else if ( ymin.index >= 0 ) + result = ft_orientation_extremum_compute( &ymin, outline ); + + else if ( ymax.index >= 0 ) + result = ft_orientation_extremum_compute( &ymax, outline ); + } + } + + return result; + } + /* END */