📄 ftstroke.c
字号:
/***************************************************************************//* *//* ftstroke.c *//* *//* FreeType path stroker (body). *//* *//* Copyright 2002, 2003 by *//* David Turner, Robert Wilhelm, and Werner Lemberg. *//* *//* This file is part of the FreeType project, and may only be used, *//* modified, and distributed under the terms of the FreeType project *//* license, LICENSE.TXT. By continuing to use, modify, or distribute *//* this file you indicate that you have read the license and *//* understand and accept it fully. *//* *//***************************************************************************/#include <ft2build.h>#include FT_STROKER_H#include FT_TRIGONOMETRY_H#include FT_INTERNAL_MEMORY_H#include FT_INTERNAL_DEBUG_H /***************************************************************************/ /***************************************************************************/ /***** *****/ /***** BEZIER COMPUTATIONS *****/ /***** *****/ /***************************************************************************/ /***************************************************************************/#define FT_SMALL_CONIC_THRESHOLD ( FT_ANGLE_PI / 6 )#define FT_SMALL_CUBIC_THRESHOLD ( FT_ANGLE_PI / 6 )#define FT_EPSILON 2#define FT_IS_SMALL( x ) ( (x) > -FT_EPSILON && (x) < FT_EPSILON ) static FT_Pos ft_pos_abs( FT_Pos x ) { return x >= 0 ? x : -x ; } static void ft_conic_split( FT_Vector* base ) { FT_Pos a, b; base[4].x = base[2].x; b = base[1].x; a = base[3].x = ( base[2].x + b ) / 2; b = base[1].x = ( base[0].x + b ) / 2; base[2].x = ( a + b ) / 2; base[4].y = base[2].y; b = base[1].y; a = base[3].y = ( base[2].y + b ) / 2; b = base[1].y = ( base[0].y + b ) / 2; base[2].y = ( a + b ) / 2; } static FT_Bool ft_conic_is_small_enough( FT_Vector* base, FT_Angle *angle_in, FT_Angle *angle_out ) { FT_Vector d1, d2; FT_Angle theta; FT_Int close1, close2; d1.x = base[1].x - base[2].x; d1.y = base[1].y - base[2].y; d2.x = base[0].x - base[1].x; d2.y = base[0].y - base[1].y; close1 = FT_IS_SMALL( d1.x ) && FT_IS_SMALL( d1.y ); close2 = FT_IS_SMALL( d2.x ) && FT_IS_SMALL( d2.y ); if ( close1 ) { if ( close2 ) *angle_in = *angle_out = 0; else *angle_in = *angle_out = FT_Atan2( d2.x, d2.y ); } else if ( close2 ) { *angle_in = *angle_out = FT_Atan2( d1.x, d1.y ); } else { *angle_in = FT_Atan2( d1.x, d1.y ); *angle_out = FT_Atan2( d2.x, d2.y ); } theta = ft_pos_abs( FT_Angle_Diff( *angle_in, *angle_out ) ); return FT_BOOL( theta < FT_SMALL_CONIC_THRESHOLD ); } static void ft_cubic_split( FT_Vector* base ) { FT_Pos a, b, c, d; base[6].x = base[3].x; c = base[1].x; d = base[2].x; base[1].x = a = ( base[0].x + c ) / 2; base[5].x = b = ( base[3].x + d ) / 2; c = ( c + d ) / 2; base[2].x = a = ( a + c ) / 2; base[4].x = b = ( b + c ) / 2; base[3].x = ( a + b ) / 2; base[6].y = base[3].y; c = base[1].y; d = base[2].y; base[1].y = a = ( base[0].y + c ) / 2; base[5].y = b = ( base[3].y + d ) / 2; c = ( c + d ) / 2; base[2].y = a = ( a + c ) / 2; base[4].y = b = ( b + c ) / 2; base[3].y = ( a + b ) / 2; } static FT_Bool ft_cubic_is_small_enough( FT_Vector* base, FT_Angle *angle_in, FT_Angle *angle_mid, FT_Angle *angle_out ) { FT_Vector d1, d2, d3; FT_Angle theta1, theta2; FT_Int close1, close2, close3; d1.x = base[2].x - base[3].x; d1.y = base[2].y - base[3].y; d2.x = base[1].x - base[2].x; d2.y = base[1].y - base[2].y; d3.x = base[0].x - base[1].x; d3.y = base[0].y - base[1].y; close1 = FT_IS_SMALL( d1.x ) && FT_IS_SMALL( d1.y ); close2 = FT_IS_SMALL( d2.x ) && FT_IS_SMALL( d2.y ); close3 = FT_IS_SMALL( d3.x ) && FT_IS_SMALL( d3.y ); if ( close1 || close3 ) { if ( close2 ) { /* basically a point */ *angle_in = *angle_out = *angle_mid = 0; } else if ( close1 ) { *angle_in = *angle_mid = FT_Atan2( d2.x, d2.y ); *angle_out = FT_Atan2( d3.x, d3.y ); } else /* close2 */ { *angle_in = FT_Atan2( d1.x, d1.y ); *angle_mid = *angle_out = FT_Atan2( d2.x, d2.y ); } } else if ( close2 ) { *angle_in = *angle_mid = FT_Atan2( d1.x, d1.y ); *angle_out = FT_Atan2( d3.x, d3.y ); } else { *angle_in = FT_Atan2( d1.x, d1.y ); *angle_mid = FT_Atan2( d2.x, d2.y ); *angle_out = FT_Atan2( d3.x, d3.y ); } theta1 = ft_pos_abs( FT_Angle_Diff( *angle_in, *angle_mid ) ); theta2 = ft_pos_abs( FT_Angle_Diff( *angle_mid, *angle_out ) ); return FT_BOOL( theta1 < FT_SMALL_CUBIC_THRESHOLD && theta2 < FT_SMALL_CUBIC_THRESHOLD ); } /***************************************************************************/ /***************************************************************************/ /***** *****/ /***** 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 = border->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 ); /* don't record empty paths! */ if ( border->num_points > (FT_UInt)border->start ) { border->tags[border->start ] |= FT_STROKE_TAG_BEGIN; border->tags[border->num_points - 1] |= FT_STROKE_TAG_END; } border->start = -1; border->movable = 0; } static FT_Error ft_stroke_border_lineto( FT_StrokeBorder border, FT_Vector* to, FT_Bool movable ) { FT_Error error = 0; FT_ASSERT( border->start >= 0 ); if ( border->movable ) { /* move last point */ border->points[border->num_points - 1] = *to; } else { /* add one point */ error = ft_stroke_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; } } border->movable = movable; 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_stroke_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; } border->movable = 0; 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_stroke_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; } border->movable = 0; return error; }#define FT_ARC_CUBIC_ANGLE ( FT_ANGLE_PI / 2 ) static FT_Error ft_stroke_border_arcto( FT_StrokeBorder border, FT_Vector* center, FT_Fixed radius, FT_Angle angle_start, FT_Angle angle_diff ) { FT_Angle total, angle, step, rotate, next, theta; FT_Vector a, b, a2, b2; FT_Fixed length; FT_Error error = 0; /* compute start point */ FT_Vector_From_Polar( &a, radius, angle_start ); a.x += center->x; a.y += center->y; total = angle_diff; angle = angle_start; rotate = ( angle_diff >= 0 ) ? FT_ANGLE_PI2 : -FT_ANGLE_PI2; while ( total != 0 ) { step = total; if ( step > FT_ARC_CUBIC_ANGLE ) step = FT_ARC_CUBIC_ANGLE; else if ( step < -FT_ARC_CUBIC_ANGLE ) step = -FT_ARC_CUBIC_ANGLE; next = angle + step; theta = step; if ( theta < 0 ) theta = -theta; theta >>= 1; /* compute end point */ FT_Vector_From_Polar( &b, radius, next ); b.x += center->x; b.y += center->y; /* compute first and second control points */ length = FT_MulDiv( radius, FT_Sin( theta ) * 4, ( 0x10000L + FT_Cos( theta ) ) * 3 ); FT_Vector_From_Polar( &a2, length, angle + rotate ); a2.x += a.x; a2.y += a.y; 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 ); 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; } 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 ) { FT_Memory memory = border->memory; FT_FREE( border->points ); FT_FREE( border->tags ); border->num_points = 0; border->max_points = 0; border->start = -1; } 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; 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_MEM_COPY( outline->points + outline->n_points, border->points, border->num_points * sizeof ( FT_Vector ) );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -