* 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
This commit is contained in:
David Turner 2003-10-02 21:07:10 +00:00
parent 6d1d122dbc
commit ccdd3bebdc
10 changed files with 2372 additions and 3 deletions

View File

@ -1,3 +1,16 @@
2003-10-01 David Turner <david@freetype.org>
* 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 <wl@gnu.org>
* src/cid/cidload.c (cid_parse_dict): Skip token if no keyword is
@ -21,9 +34,9 @@
2003-09-29 David Turner <david@freetype.org>
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

View File

@ -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 );
/* */

View File

@ -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

164
src/autofit/afangles.c Normal file
View File

@ -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;
}

626
src/autofit/afhints.c Normal file
View File

@ -0,0 +1,626 @@
#include "afhints.h"
#ifdef AF_DEBUG
#include <stdio.h>
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;
}
}
}

233
src/autofit/afhints.h Normal file
View File

@ -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__ */

755
src/autofit/aflatin.c Normal file
View File

@ -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;
}
}
/*************************************************************************/
/* */
/* <Function> */
/* af_outline_detect_features */
/* */
/* <Description> */
/* 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 );
}
/*************************************************************************/
/* */
/* <Function> */
/* af_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 )
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;
}
}
/*************************************************************************/
/* */
/* <Function> */
/* af_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 )
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;
}
}

89
src/autofit/aflatin.h Normal file
View File

@ -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__ */

289
src/autofit/aftypes.h Normal file
View File

@ -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 <stdio.h>
# 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__ */

View File

@ -26,6 +26,7 @@
#include <ft2build.h>
#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 */