📄 ftstroke.c
字号:
FT_Vector_From_Polar( &b2, length, next - rotate );
b2.x += b.x;
b2.y += b.y;
/* add cubic arc */
error = ft_stroke_border_cubicto( border, &a2, &b2, &b );
if ( error )
break;
/* process the rest of the arc ?? */
a = b;
total -= step;
angle = next;
}
return error;
}
static FT_Error
ft_stroke_border_moveto( FT_StrokeBorder border,
FT_Vector* to )
{
/* close current open path if any ? */
if ( border->start >= 0 )
ft_stroke_border_close( border, 0 );
border->start = border->num_points;
border->movable = 0;
return ft_stroke_border_lineto( border, to, 0 );
}
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;
border->valid = 0;
}
static void
ft_stroke_border_reset( FT_StrokeBorder border )
{
border->num_points = 0;
border->start = -1;
border->valid = 0;
}
static void
ft_stroke_border_done( FT_StrokeBorder border )
{
FT_Memory memory = border->memory;
FT_FREE( border->points );
FT_FREE( border->tags );
border->num_points = 0;
border->max_points = 0;
border->start = -1;
border->valid = 0;
}
static FT_Error
ft_stroke_border_get_counts( FT_StrokeBorder border,
FT_UInt *anum_points,
FT_UInt *anum_contours )
{
FT_Error error = 0;
FT_UInt num_points = 0;
FT_UInt num_contours = 0;
FT_UInt count = border->num_points;
FT_Vector* point = border->points;
FT_Byte* tags = border->tags;
FT_Int in_contour = 0;
for ( ; count > 0; count--, num_points++, point++, tags++ )
{
if ( tags[0] & FT_STROKE_TAG_BEGIN )
{
if ( in_contour != 0 )
goto Fail;
in_contour = 1;
}
else if ( in_contour == 0 )
goto Fail;
if ( tags[0] & FT_STROKE_TAG_END )
{
if ( in_contour == 0 )
goto Fail;
in_contour = 0;
num_contours++;
}
}
if ( in_contour != 0 )
goto Fail;
border->valid = 1;
Exit:
*anum_points = num_points;
*anum_contours = num_contours;
return error;
Fail:
num_points = 0;
num_contours = 0;
goto Exit;
}
static void
ft_stroke_border_export( FT_StrokeBorder border,
FT_Outline* outline )
{
/* copy point locations */
FT_ARRAY_COPY( outline->points + outline->n_points,
border->points,
border->num_points );
/* copy tags */
{
FT_UInt count = border->num_points;
FT_Byte* read = border->tags;
FT_Byte* write = (FT_Byte*)outline->tags + outline->n_points;
for ( ; count > 0; count--, read++, write++ )
{
if ( *read & FT_STROKE_TAG_ON )
*write = FT_CURVE_TAG_ON;
else if ( *read & FT_STROKE_TAG_CUBIC )
*write = FT_CURVE_TAG_CUBIC;
else
*write = FT_CURVE_TAG_CONIC;
}
}
/* copy contours */
{
FT_UInt count = border->num_points;
FT_Byte* tags = border->tags;
FT_Short* write = outline->contours + outline->n_contours;
FT_Short idx = (FT_Short)outline->n_points;
for ( ; count > 0; count--, tags++, idx++ )
{
if ( *tags & FT_STROKE_TAG_END )
{
*write++ = idx;
outline->n_contours++;
}
}
}
outline->n_points = (short)( outline->n_points + border->num_points );
FT_ASSERT( FT_Outline_Check( outline ) == 0 );
}
/***************************************************************************/
/***************************************************************************/
/***** *****/
/***** STROKER *****/
/***** *****/
/***************************************************************************/
/***************************************************************************/
#define FT_SIDE_TO_ROTATE( s ) ( FT_ANGLE_PI2 - (s) * FT_ANGLE_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_Bool valid;
FT_StrokeBorderRec borders[2];
FT_Memory memory;
} FT_StrokerRec;
/* documentation is in ftstroke.h */
FT_EXPORT_DEF( FT_Error )
FT_Stroker_New( FT_Library library,
FT_Stroker *astroker )
{
FT_Error error;
FT_Memory memory;
FT_Stroker stroker;
if ( !library )
return FT_Err_Invalid_Argument;
memory = library->memory;
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;
}
/* documentation is in ftstroke.h */
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_Stroker_Rewind( stroker );
}
/* documentation is in ftstroke.h */
FT_EXPORT_DEF( void )
FT_Stroker_Rewind( FT_Stroker stroker )
{
if ( stroker )
{
ft_stroke_border_reset( &stroker->borders[0] );
ft_stroke_border_reset( &stroker->borders[1] );
}
}
/* documentation is in ftstroke.h */
FT_EXPORT_DEF( 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 );
}
}
/* creates a circular arc at a corner or cap */
static FT_Error
ft_stroker_arcto( FT_Stroker stroker,
FT_Int side )
{
FT_Angle total, rotate;
FT_Fixed radius = stroker->radius;
FT_Error error = 0;
FT_StrokeBorder border = stroker->borders + side;
rotate = FT_SIDE_TO_ROTATE( side );
total = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
if ( total == FT_ANGLE_PI )
total = -rotate * 2;
error = ft_stroke_border_arcto( border,
&stroker->center,
radius,
stroker->angle_in + rotate,
total );
border->movable = 0;
return error;
}
/* adds a cap at the end of an opened path */
static FT_Error
ft_stroker_cap( FT_Stroker stroker,
FT_Angle angle,
FT_Int side )
{
FT_Error error = 0;
if ( stroker->line_cap == FT_STROKER_LINECAP_ROUND )
{
/* add a round cap */
stroker->angle_in = angle;
stroker->angle_out = angle + FT_ANGLE_PI;
error = ft_stroker_arcto( stroker, side );
}
else if ( stroker->line_cap == FT_STROKER_LINECAP_SQUARE )
{
/* add a square cap */
FT_Vector delta, delta2;
FT_Angle rotate = FT_SIDE_TO_ROTATE( side );
FT_Fixed radius = stroker->radius;
FT_StrokeBorder border = stroker->borders + side;
FT_Vector_From_Polar( &delta2, radius, angle + rotate );
FT_Vector_From_Polar( &delta, radius, angle );
delta.x += stroker->center.x + delta2.x;
delta.y += stroker->center.y + delta2.y;
error = ft_stroke_border_lineto( border, &delta, 0 );
if ( error )
goto Exit;
FT_Vector_From_Polar( &delta2, radius, angle - rotate );
FT_Vector_From_Polar( &delta, radius, angle );
delta.x += delta2.x + stroker->center.x;
delta.y += delta2.y + stroker->center.y;
error = ft_stroke_border_lineto( border, &delta, 0 );
}
Exit:
return error;
}
/* process an inside corner, i.e. compute intersection */
static FT_Error
ft_stroker_inside( FT_Stroker stroker,
FT_Int side)
{
FT_StrokeBorder border = stroker->borders + side;
FT_Angle phi, theta, rotate;
FT_Fixed length, thcos, sigma;
FT_Vector delta;
FT_Error error = 0;
rotate = FT_SIDE_TO_ROTATE( side );
/* compute median angle */
theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
if ( theta == FT_ANGLE_PI )
theta = rotate;
else
theta = theta / 2;
phi = stroker->angle_in + theta;
thcos = FT_Cos( theta );
sigma = FT_MulFix( stroker->miter_limit, thcos );
/* TODO: find better criterion to switch off the optimization */
if ( sigma < 0x10000L )
{
FT_Vector_From_Polar( &delta, stroker->radius,
stroker->angle_out + rotate );
delta.x += stroker->center.x;
delta.y += stroker->center.y;
border->movable = 0;
}
else
{
length = FT_DivFix( stroker->radius, thcos );
FT_Vector_From_Polar( &delta, length, phi + rotate );
delta.x += stroker->center.x;
delta.y += stroker->center.y;
}
error = ft_stroke_border_lineto( border, &delta, 0 );
return error;
}
/* process an outside corner, i.e. compute bevel/miter/round */
static FT_Error
ft_stroker_outside( FT_Stroker stroker,
FT_Int side )
{
FT_StrokeBorder border = stroker->borders + side;
FT_Error error;
FT_Angle rotate;
if ( stroker->line_join == FT_STROKER_LINEJOIN_ROUND )
{
error = ft_stroker_arcto( stroker, side );
}
else
{
/* this is a mitered or beveled corner */
FT_Fixed sigma, radius = stroker->radius;
FT_Angle theta, phi;
FT_Fixed thcos;
FT_Bool miter;
rotate = FT_SIDE_TO_ROTATE( side );
miter = FT_BOOL( stroker->line_join == FT_STROKER_LINEJOIN_MITER );
theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
if ( theta == FT_ANGLE_PI )
{
theta = rotate;
phi = stroker->angle_in;
}
else
{
theta = theta / 2;
phi = stroker->angle_in + theta + rotate;
}
thcos = FT_Cos( theta );
sigma = FT_MulFix( stroker->miter_limit, thcos );
if ( sigma >= 0x10000L )
miter = 0;
if ( miter ) /* this is a miter (broken angle) */
{
FT_Vector middle, delta;
FT_Fixed length;
/* compute middle point */
FT_Vector_From_Polar( &middle,
FT_MulFix( radius, stroker->miter_limit ),
phi );
middle.x += stroker->center.x;
middle.y += stroker->center.y;
/* compute first angle point */
length = FT_MulFix( radius,
FT_DivFix( 0x10000L - sigma,
ft_pos_abs( FT_Sin( theta ) ) ) );
FT_Vector_From_Polar( &delta, length, phi + rotate );
delta.x += middle.x;
delta.y += middle.y;
error = ft_stroke_border_lineto( border, &delta, 0 );
if ( error )
goto Exit;
/* compute second angle point */
FT_Vector_From_Polar( &delta, length, phi - rotate );
delta.x += middle.x;
delta.y += middle.y;
error = ft_stroke_border_lineto( border, &delta, 0 );
if ( error )
goto Exit;
/* finally, add a movable end point */
FT_Vector_From_Polar( &delta, radius, stroker->angle_out + rotate );
delta.x += stroker->center.x;
delta.y += stroker->center.y;
error = ft_stroke_border_lineto( border, &delta, 1 );
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -