diff --git a/include/freetype/ftstroker.h b/include/freetype/ftstroker.h new file mode 100644 index 000000000..0f939e478 --- /dev/null +++ b/include/freetype/ftstroker.h @@ -0,0 +1,144 @@ +#ifndef __FT_STROKER_H__ +#define __FT_STROKER_H__ + +#include +#include FT_OUTLINE_H + +FT_BEGIN_HEADER + +/************************************************************** + * + * @type: FT_Stroker + * + * @description: + * opaque handler to a path stroker object + */ + typedef struct FT_StrokerRec_* FT_Stroker; + + +/************************************************************** + * + * @enum: FT_Stroker_LineJoin + * + * @description: + * these values determine how two joining lines are rendered + * in a stroker. + * + * @values: + * FT_STROKER_LINEJOIN_ROUND :: + * used to render rounded line joins. circular arcs are used + * to join two lines smoothly + * + * FT_STROKER_LINEJOIN_BEVEL :: + * used to render beveled line joins; i.e. the two joining lines + * are extended until they intersect + * + * FT_STROKER_LINEJOIN_MITER :: + * same as beveled rendering, except that an additional line + * break is added if the angle between the two joining lines + * is too closed (this is useful to avoid unpleasant spikes + * in beveled rendering). + */ + typedef enum + { + FT_STROKER_LINEJOIN_ROUND = 0, + FT_STROKER_LINEJOIN_BEVEL, + FT_STROKER_LINEJOIN_MITER + + } FT_Stroker_LineJoin; + + +/************************************************************** + * + * @enum: FT_Stroker_LineCap + * + * @description: + * these values determine how the end of opened sub-paths are + * rendered in a stroke + * + * @values: + * FT_STROKER_LINECAP_BUTT :: + * the end of lines is rendered as a full stop on the last + * point itself + * + * FT_STROKER_LINECAP_ROUND :: + * the end of lines is rendered as a half-circle around the + * last point + * + * FT_STROKER_LINEJOIN_MITER :: + * the end of lines is rendered as a square around the + * last point + */ + typedef enum + { + FT_STROKER_LINECAP_BUTT = 0, + FT_STROKER_LINECAP_ROUND, + FT_STROKER_LINECAP_SQUARE + + } FT_Stroker_LineCap; + + + FT_EXPORT( FT_Error ) + FT_Stroker_New( FT_Memory memory, + FT_Stroker *astroker ); + + FT_EXPORT( void ) + FT_Stroker_Set( FT_Stroker stroker, + FT_Fixed radius, + FT_Stroker_LineCap line_cap, + FT_Stroker_LineJoin line_join, + FT_Fixed miter_limit ); + + FT_EXPORT( FT_Error ) + FT_Stroker_BeginSubPath( FT_Stroker stroker, + FT_Pos x, + FT_Pos y, + FT_Bool open ); + + FT_EXPORT( FT_Error ) + FT_Stroker_EndSubPath( FT_Stroker stroker ); + + + FT_EXPORT( FT_Error ) + FT_Stroker_LineTo( FT_Stroker stroker, + FT_Pos to_x, + FT_Pos to_y ); + + FT_EXPORT( FT_Error ) + FT_Stroker_ConicTo( FT_Stroker stroker, + FT_Pos control_x, + FT_Pos control_y, + FT_Pos to_x, + FT_Pos to_y ); + + FT_EXPORT( FT_Error ) + FT_Stroker_CubicTo( FT_Stroker stroker, + FT_Pos control1_x, + FT_Pos control1_y, + FT_Pos control2_x, + FT_Pos control2_y, + FT_Pos to_x, + FT_Pos to_y ); + + + FT_EXPORT( FT_Error ) + FT_Stroker_GetCounts( FT_Stroker stroker, + FT_UInt *anum_points, + FT_UInt *anum_contours ); + + FT_EXPORT( void ) + FT_Stroker_Export( FT_Stroker stroker, + FT_Outliner* outline ); + + FT_EXPORT( void ) + FT_Stroker_Done( FT_Stroker stroker ); + + + FT_EXPORT( FT_Error ) + FT_Stroker_ParseOutline( FT_Stroker stroker, + FT_Outline* outline, + FT_Bool opened ); + +FT_END_HEADER + +#endif /* __FT_STROKER_H__ */ diff --git a/include/freetype/fttrigon.h b/include/freetype/fttrigon.h index 9762189e5..54870bc43 100644 --- a/include/freetype/fttrigon.h +++ b/include/freetype/fttrigon.h @@ -173,6 +173,27 @@ FT_BEGIN_HEADER FT_Fixed y ); + /*************************************************************************/ + /* */ + /* @function: */ + /* FT_Angle_Diff */ + /* */ + /* @description: */ + /* Returns the difference between two angles. The result is always */ + /* constrained to the ]-PI..PI] interval */ + /* */ + /* @input: */ + /* angle1 :: first angle */ + /* angle2 :: second angle */ + /* */ + /* @return: */ + /* contrainted value of 'value2-value1' */ + /* */ + FT_EXPORT_DEF( FT_Angle ) + FT_Angle_Dif( FT_Angle angle1, + FT_Angle angle2 ); + + /*************************************************************************/ /* */ /* @function: */ diff --git a/src/base/ftstroker.c b/src/base/ftstroker.c new file mode 100644 index 000000000..c7e74941e --- /dev/null +++ b/src/base/ftstroker.c @@ -0,0 +1,290 @@ +#include +#include FT_STROKER_H +#include FT_TRIGONOMETRY_H + + /***************************************************************************/ + /***************************************************************************/ + /***** *****/ + /***** STROKE BORDERS *****/ + /***** *****/ + /***************************************************************************/ + /***************************************************************************/ + + typedef enum + { + FT_STROKE_TAG_ON = 1, /* on-curve point */ + FT_STROKE_TAG_CUBIC = 2, /* cubic off-point */ + FT_STROKE_TAG_BEGIN = 4, /* sub-path start */ + FT_STROKE_TAG_END = 8 /* sub-path end */ + + } FT_StrokeTags; + + + typedef struct FT_StrokeBorderRec_ + { + FT_UInt num_points; + FT_UInt max_points; + FT_Vector* points; + FT_Byte* tags; + FT_Bool movable; + FT_Int start; /* index of current sub-path start point */ + FT_Memory memory; + + } FT_StrokeBorderRec, *FT_StrokeBorder; + + + static FT_Error + ft_stroke_border_grow( FT_StrokeBorder border, + FT_UInt new_points ) + { + FT_UInt old_max = border->max_points; + FT_UInt new_max = border->num_points + new_points; + FT_Error error = 0; + + if ( new_max > old_max ) + { + FT_UInt cur_max = old_max; + FT_Memory memory = + + while ( cur_max < new_max ) + cur_max += (cur_max >> 1) + 16; + + if ( FT_RENEW_ARRAY( border->points, old_max, cur_max ) || + FT_RENEW_ARRAY( border->tags, old_max, cur_max ) ) + goto Exit; + + border->max_points = cur_max; + } + Exit: + return error; + } + + static void + ft_stroke_border_close( FT_StrokeBorder border ) + { + FT_ASSERT( border->start >= 0 ); + + border->tags[ border->start ] |= FT_STROKE_TAG_BEGIN; + border->tags[ border->num_points-1 ] |= FT_STROKE_TAG_END; + + border->start = -1; + } + + + static FT_Error + ft_stroke_border_lineto( FT_StrokeBorder border, + FT_Vector* to ) + { + FT_Error error; + + FT_ASSERT( border->start >= 0 ); + + error = ft_stroker_border_grow( border, 1 ); + if (!error) + { + FT_Vector* vec = border->points + border->num_points; + FT_Byte* tag = border->tags + border->num_points; + + vec[0] = *to; + tag[0] = FT_STROKE_TAG_ON; + + border->num_points += 1; + } + return error; + } + + + static FT_Error + ft_stroke_border_conicto( FT_StrokeBorder border, + FT_Vector* control, + FT_Vector* to ) + { + FT_Error error; + + FT_ASSERT( border->start >= 0 ); + + error = ft_stroker_border_grow( border, 2 ); + if (!error) + { + FT_Vector* vec = border->points + border->num_points; + FT_Byte* tag = border->tags + border->num_points; + + vec[0] = *control; + vec[1] = *to; + + tag[0] = 0; + tag[1] = FT_STROKE_TAG_ON; + + border->num_points += 2; + } + return error; + } + + + static FT_Error + ft_stroke_border_cubicto( FT_StrokeBorder border, + FT_Vector* control1, + FT_Vector* control2, + FT_Vector* to ) + { + FT_Error error; + + FT_ASSERT( border->start >= 0 ); + + error = ft_stroker_border_grow( border, 3 ); + if (!error) + { + FT_Vector* vec = border->points + border->num_points; + FT_Byte* tag = border->tags + border->num_points; + + vec[0] = *control1; + vec[1] = *control2; + vec[2] = *to; + + tag[0] = FT_STROKE_TAG_CUBIC; + tag[1] = FT_STROKE_TAG_CUBIC; + tag[2] = FT_STROKE_TAG_ON; + + border->num_points += 3; + } + return error; + } + + + static FT_Error + ft_stroke_border_moveto( FT_StrokeBorder border, + FT_Vector* to ) + { + FT_Error error; + + /* close current open path if any ? */ + if ( border->start >= 0 ) + ft_stroke_border_close( border ); + + border->start = border->num_points; + + return ft_stroke_border_lineto( border, to ); + } + + + static void + ft_stroke_border_init( FT_StrokeBorder border, + FT_Memory memory ) + { + border->memory = memory; + border->points = NULL; + border->tags = NULL; + + border->num_points = 0; + border->max_points = 0; + border->start = -1; + } + + + static void + ft_stroke_border_reset( FT_StrokeBorder border ) + { + border->num_points = 0; + border->start = -1; + } + + + static void + ft_stroke_border_done( FT_StrokeBorder border ) + { + memory = border->memory; + + FT_FREE( border->points ); + FT_FREE( border->tags ); + + border->num_points = 0; + border->max_points = 0; + border->start = -1; + } + + + /***************************************************************************/ + /***************************************************************************/ + /***** *****/ + /***** STROKER *****/ + /***** *****/ + /***************************************************************************/ + /***************************************************************************/ + +#define FT_SIDE_TO_ROTATE(s) (FT_PI2 - (s)*FT_PI) + + typedef struct FT_StrokerRec_ + { + FT_Angle angle_in; + FT_Angle angle_out; + FT_Vector center; + FT_Bool first_point; + FT_Bool subpath_open; + FT_Angle subpath_angle; + FT_Vector subpath_start; + + FT_Stroker_LineCap line_cap; + FT_Stroker_LineJoin line_join; + FT_Fixed miter_limit; + FT_Fixed radius; + + FT_StrokeBorderRec borders[2]; + FT_Memory memory; + + } FT_StrokerRec; + + + FT_EXPORT_DEF( FT_Error ) + FT_Stroker_New( FT_Memory memory, + FT_Stroker *astroker ) + { + FT_Error error; + FT_Stroker stroker; + + if ( !FT_NEW( stroker ) ) + { + stroker->memory = memory; + + ft_stroke_border_init( &stroker->borders[0], memory ); + ft_stroke_border_init( &stroker->borders[1], memory ); + } + *astroker = stroker; + return error; + } + + + FT_EXPORT_DEF( void ) + FT_Stroker_Set( FT_Stroker stroker, + FT_Fixed radius, + FT_Stroker_LineCap line_cap, + FT_Stroker_LineJoin line_join, + FT_Fixed miter_limit ) + { + stroker->radius = radius; + stroker->line_cap = line_cap; + stroker->line_join = line_join; + stroker->miter_limit = miter_limit; + + ft_stroke_border_reset( &stroker->borders[0] ); + ft_stroke_border_reset( &stroker->borders[1] ); + } + + + FT_EXPORT( void ) + FT_Stroker_Done( FT_Stroker stroker ) + { + if ( stroker ) + { + FT_Memory memory = stroker->memory; + + ft_stroke_border_done( &stroker->borders[0] ); + ft_stroke_border_done( &stroker->borders[1] ); + + stroker->memory = NULL; + FT_FREE( stroker ); + } + } + + + + \ No newline at end of file diff --git a/src/base/fttrigon.c b/src/base/fttrigon.c index 155fc5ff2..490a26ff1 100644 --- a/src/base/fttrigon.c +++ b/src/base/fttrigon.c @@ -451,4 +451,21 @@ } + /* documentation is in fttrigon.h */ + + FT_EXPORT_DEF( FT_Angle ) + FT_Angle_Dif( FT_Angle angle1, + FT_Angle angle2 ) + { + FT_Angle delta = angle2 - angle1; + + delta %= FT_ANGLE_2PI; + + if ( delta > FT_ANGLE_PI ) + delta -= FT_ANGLE_2PI; + + return delta; + } + + /* END */