diff --git a/ChangeLog b/ChangeLog index 1116d25ad..21599a68c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,9 +1,20 @@ 2002-06-26 David Turner - * src/truetype/ttgload.c (TT_Load_Composite_Glyph), - src/base/ftoutln.c (FT_Vector_Transform): fixed Werner's latest - fix. FT_Vector_Transform wasn't buggy, the TrueType composite loader - was... + * include/freetype/internal/ftobject.h: updating the object sub-system + definitions (still experimental) + + * src/base/fthash.c (ft_hash_remove): fixing a small reallocation bug + + * include/freetype/fttrigon.h, src/base/fttrigon.c: adding + FT_Vector_From_Polar and FT_Angle_Diff to the trigonometric functions + + * include/freetype/ftstroker.h, src/base/ftstroker.c: adding path stroker + component (work in progress) + + * src/truetype/ttgload.c (TT_Load_Composite_Glyph), + src/base/ftoutln.c (FT_Vector_Transform): fixed Werner's latest + fix. FT_Vector_Transform wasn't buggy, the TrueType composite loader + was... 2002-06-24 Werner Lemberg diff --git a/include/freetype/ftstroker.h b/include/freetype/ftstroker.h index 0f939e478..645f4f55e 100644 --- a/include/freetype/ftstroker.h +++ b/include/freetype/ftstroker.h @@ -44,7 +44,7 @@ FT_BEGIN_HEADER FT_STROKER_LINEJOIN_ROUND = 0, FT_STROKER_LINEJOIN_BEVEL, FT_STROKER_LINEJOIN_MITER - + } FT_Stroker_LineJoin; @@ -74,10 +74,10 @@ FT_BEGIN_HEADER 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 ); @@ -89,10 +89,15 @@ FT_BEGIN_HEADER FT_Stroker_LineJoin line_join, FT_Fixed miter_limit ); + + FT_EXPORT( FT_Error ) + FT_Stroker_ParseOutline( FT_Stroker stroker, + FT_Outline* outline, + FT_Bool opened ); + FT_EXPORT( FT_Error ) FT_Stroker_BeginSubPath( FT_Stroker stroker, - FT_Pos x, - FT_Pos y, + FT_Vector* to, FT_Bool open ); FT_EXPORT( FT_Error ) @@ -101,24 +106,18 @@ FT_BEGIN_HEADER FT_EXPORT( FT_Error ) FT_Stroker_LineTo( FT_Stroker stroker, - FT_Pos to_x, - FT_Pos to_y ); + FT_Vector* to ); 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_Vector* control, + FT_Vector* to ); 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_Vector* control1, + FT_Vector* control2, + FT_Vector* to ); FT_EXPORT( FT_Error ) @@ -134,11 +133,6 @@ FT_BEGIN_HEADER 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 54870bc43..07da694d5 100644 --- a/include/freetype/fttrigon.h +++ b/include/freetype/fttrigon.h @@ -291,6 +291,28 @@ FT_BEGIN_HEADER FT_Vector_Polarize( FT_Vector* vec, FT_Fixed *length, FT_Angle *angle ); + + + /*************************************************************************/ + /* */ + /* @function: */ + /* FT_Vector_From_Polar */ + /* */ + /* @description: */ + /* Compute vector coordinates from a length and angle. */ + /* */ + /* @output: */ + /* vec :: The address of source vector. */ + /* */ + /* @input: */ + /* length :: The vector length. */ + /* angle :: The vector angle. */ + /* */ + FT_EXPORT( void ) + FT_Vector_From_Polar( FT_Vector* vec, + FT_Fixed length, + FT_Angle angle ); + /* */ diff --git a/include/freetype/internal/fthash.h b/include/freetype/internal/fthash.h index 551f9f5dc..4aa62db83 100644 --- a/include/freetype/internal/fthash.h +++ b/include/freetype/internal/fthash.h @@ -59,7 +59,19 @@ FT_BEGIN_HEADER /*********************************************************** * - * @type: FT_Hash_CompareFunc + * @type: FT_HashLookup + * + * @description: + * handle to a @FT_HashNode pointer. This is returned by + * the @ft_hash_lookup function and can later be used by + * @ft_hash_add or @ft_hash_remove + */ + typedef FT_HashNode* FT_HashLookup; + + + /*********************************************************** + * + * @type: FT_Hash_EqualFunc * * @description: * a function used to compare two nodes of the hash table @@ -72,8 +84,8 @@ FT_BEGIN_HEADER * 1 iff the 'keys' in 'node1' and 'node2' are identical. * 0 otherwise. */ - typedef FT_Int (*FT_Hash_CompareFunc)( const FT_HashNode node1, - const FT_HashNode node2 ); + typedef FT_Int (*FT_Hash_EqualFunc)( FT_HashNode node1, + FT_HashNode node2 ); /*********************************************************** @@ -106,16 +118,12 @@ FT_BEGIN_HEADER */ typedef struct FT_HashRec_ { - FT_Memory memory; FT_HashNode* buckets; FT_UInt p; FT_UInt mask; /* really maxp-1 */ - FT_UInt slack; - - - FT_UInt node_size; - FT_Hash_CompareFunc node_compare; - FT_Hash_ComputeFunc node_hash; + FT_Long slack; + FT_Hash_EqualFunc node_equal; + FT_Memory memory; } FT_HashRec, *FT_Hash; @@ -168,36 +176,39 @@ FT_BEGIN_HEADER * initialize a dynamic hash table * * @input: - * table :: handle to target hash table structure - * compare :: node comparison function - * memory :: memory manager handle used to allocate the - * buckets array within the hash table + * table :: handle to target hash table structure + * node_equal :: node comparison function + * memory :: memory manager handle used to allocate the + * buckets array within the hash table + * + * @return: + * error code. 0 means success * * @note: * the node comparison function should only compare node _keys_ * and ignore values !! with good hashing computation (which the * user must perform itself), the comparison function should be - * pretty selfom called. + * pretty seldom called. * * here is a simple example: * * { - * static int my_compare( const MyNode node1, - * const MyNode node2 ) + * static int my_equal( MyNode node1, + * MyNode node2 ) * { * // compare keys of 'node1' and 'node2' - * return strcmp( node1->key, node2->key ); + * return (strcmp( node1->key, node2->key ) == 0); * } * * .... * - * ft_hash_init( &hash, (FT_Hash_CompareFunc) my_compare, memory ); + * ft_hash_init( &hash, (FT_Hash_EqualFunc) my_compare, memory ); * .... * } */ - FT_BASE( void ) + FT_BASE( FT_Error ) ft_hash_init( FT_Hash table, - FT_Hash_CompareFunc compare, + FT_Hash_EqualFunc compare, FT_Memory memory ); @@ -257,7 +268,7 @@ FT_BEGIN_HEADER * } * } */ - FT_BASE_DEF( FT_HashNode* ) + FT_BASE_DEF( FT_HashLookup ) ft_hash_lookup( FT_Hash table, FT_HashNode keynode ) @@ -273,10 +284,13 @@ FT_BEGIN_HEADER * * @input: * table :: hash table handle - * pnode :: pointer-to-hash-node value returned by @ft_hash_lookup + * lookup :: pointer-to-hash-node value returned by @ft_hash_lookup * new_node :: handle to new hash node. All its fields must be correctly * set, including 'hash'. * + * @return: + * error code. 0 means success + * * @note: * this function should always be used _after_ a call to @ft_hash_lookup * that returns a pointer to a NULL handle. Here's an example: @@ -308,18 +322,21 @@ FT_BEGIN_HEADER * * // allocate a new node - and set it up * node = (MyNode) malloc( sizeof(*node) ); + * if ( node == NULL ) ..... + * * node->hnode.hash = noderec.hnode.hash; * node->key = key; * node->value = value; * * // add it to the hash table - * ft_hash_add( table, pnode, node ); + * error = ft_hash_add( table, pnode, node ); + * if (error) .... * } */ - FT_BASE( void ) - ft_hash_add( FT_Hash table, - FT_HashNode* pnode, - FT_HashNode new_node ); + FT_BASE( FT_Error ) + ft_hash_add( FT_Hash table, + FT_HashLookup lookup, + FT_HashNode new_node ); /**************************************************************** @@ -332,7 +349,7 @@ FT_BEGIN_HEADER * * @input: * table :: hash table handle - * pnode :: pointer-to-hash-node value returned by @ft_hash_lookup + * lookup :: pointer-to-hash-node value returned by @ft_hash_lookup * * @note: * this function doesn't free the node itself !! Here's an example: @@ -354,15 +371,16 @@ FT_BEGIN_HEADER * node = *pnode; * if ( node != NULL ) * { - * ft_hash_remove( table, pnode ); - * free( node ); + * error = ft_hash_remove( table, pnode ); + * if ( !error ) + * free( node ); * } * } * } */ - FT_BASE( void ) - ft_hash_remove( FT_Hash table, - FT_HashNode* pnode ); + FT_BASE( FT_Error ) + ft_hash_remove( FT_Hash table, + FT_HashLookup lookup ) diff --git a/include/freetype/internal/ftobject.h b/include/freetype/internal/ftobject.h index e1faf545a..f050eb09b 100644 --- a/include/freetype/internal/ftobject.h +++ b/include/freetype/internal/ftobject.h @@ -140,14 +140,11 @@ FT_BEGIN_HEADER * object :: target object handle * init_data :: optional pointer to initialization data * - * @throws: any - * - * the object is _assumed_ to be reachable from the cleanup - * stack when the constructor is called. This means that - * any exception can be thrown safely in it. + * @return: + * error code. 0 means success */ - typedef void (*FT_Object_InitFunc)( FT_Object object, - FT_Pointer init_data ); + typedef FT_Error (*FT_Object_InitFunc)( FT_Object object, + FT_Pointer init_data ); /************************************************************** * @@ -158,8 +155,6 @@ FT_BEGIN_HEADER * * @input: * object :: handle to target object - * - * @throws: *never* !! */ typedef void (*FT_Object_DoneFunc)( FT_Object object ); @@ -179,12 +174,15 @@ FT_BEGIN_HEADER * object sub-system.) * * magic :: a 32-bit magic number used for decoding + * super :: pointer to super class * type :: the @FT_Type descriptor of this class * memory :: the current memory manager handle * library :: the current library handle * info :: an opaque pointer to class-specific information * managed by the FreeType object sub-system * + * class_done :: the class destructor function + * * obj_size :: size of class instances in bytes * obj_init :: class instance constructor * obj_done :: class instance destructor @@ -193,11 +191,14 @@ FT_BEGIN_HEADER { FT_ObjectRec object; FT_UInt32 magic; + FT_Class super; FT_Type type; FT_Memory memory; FT_Library library; FT_Pointer info; + FT_Object_DoneFunc class_done; + FT_UInt obj_size; FT_Object_InitFunc obj_init; FT_Object_DoneFunc obj_done; @@ -363,80 +364,193 @@ FT_BEGIN_HEADER /************************************************************** * - * @function: ft_object_new + * @function: ft_object_create * * @description: * create a new object (class instance) * + * @output: + * aobject :: new object handle. NULL in case of error + * * @input: * clazz :: object's class pointer * init_data :: optional pointer to initialization data * * @return: - * handle to new object. Cannot be NULL ! + * error code. 0 means success */ - FT_BASE_DEF( FT_Object ) - ft_object_new( FT_Class clazz, - FT_Pointer init_data ); + FT_BASE_DEF( FT_Error ) + ft_object_create( FT_Object *aobject, + FT_Class clazz, + FT_Pointer init_data ); /************************************************************** * - * @function: ft_object_create + * @function: ft_object_create_from_type * * @description: - * a variation of @ft_object_new that should be used when - * creating a new object that is owned by another object - * which is reachable from the cleanup stack. - * - * this function is a bit more akward to use but completely - * avoids push/pop pairs during object construction and is - * therefore faster. + * create a new object (class instance) from a @FT_Type * * @output: - * pobject :: new object handle + * aobject :: new object handle. NULL in case of error * * @input: - * clazz :: object's class pointer + * type :: object's type descriptor * init_data :: optional pointer to initialization data - * push :: boolean. If true, the new object is pushed - * on top of the cleanup stack. + * + * @return: + * error code. 0 means success + * + * @note: + * this function is slower than @ft_object_create + * + * this is equivalent to calling @ft_class_from_type followed by + * @ft_object_create */ - FT_BASE_DEF( void ) - ft_object_create( FT_Object *pobject, - FT_Class clazz, - FT_Pointer init_data ); + FT_BASE_DEF( FT_Error ) + ft_object_create_from_type( FT_Object *aobject, + FT_Type type, + FT_Pointer init_data, + FT_Library library ); + + + + /************************************************************** + * + * @macro FT_OBJ_CREATE (object,class,init) + * + * @description: + * a convenient macro used to create new objects. see + * @ft_object_create for details + */ +#define FT_OBJ_CREATE( _obj, _clazz, _init ) \ + ft_object_create( FT_OBJECT_P(&(_obj)), _clazz, _init ) + + + /************************************************************** + * + * @macro FT_CREATE (object,class,init) + * + * @description: + * a convenient macro used to create new objects. It also + * sets an _implicit_ local variable named "error" to the error + * code returned by the object constructor. + */ +#define FT_CREATE( _obj, _clazz, _init ) \ + FT_MEM_SET( FT_OBJ_CREATE( _obj, _clazz, _init ) ) + + /************************************************************** + * + * @macro FT_OBJ_CREATE_FROM_TYPE (object,type,init) + * + * @description: + * a convenient macro used to create new objects. see + * @ft_object_create_from_type for details + */ +#define FT_OBJ_CREATE_FROM_TYPE( _obj, _type, _init, _lib ) \ + ft_object_create_from_type( FT_OBJECT_P(&(_obj)), _type, _init, _lib ) + + + /************************************************************** + * + * @macro FT_CREATE_FROM_TYPE (object,type,init) + * + * @description: + * a convenient macro used to create new objects. It also + * sets an _implicit_ local variable named "error" to the error + * code returned by the object constructor. + */ +#define FT_CREATE_FROM_TYPE( _obj, _type, _init, _lib ) \ + FT_MEM_SET( FT_OBJ_CREATE( _obj, _type, _init, _lib ) ) + /* */ - FT_BASE_DEF( FT_Class ) - ft_class_find_by_type( FT_Type type, - FT_Memory memory ); + /************************************************************** + * + * @function: ft_class_from_type + * + * @description: + * retrieves the class object corresponding to a given type + * descriptor. The class is created when needed + * + * @output: + * aclass :: handle to corresponding class object. NULL in + * case of error + * + * @input: + * type :: type descriptor handle + * library :: library handle + * + * @return: + * error code. 0 means success + */ + FT_BASE_DEF( FT_Error ) + ft_class_from_type( FT_Class *aclass, + FT_Type type, + FT_Library library ); - FT_BASE_DEF( FT_Class ) - ft_class_find_by_name( FT_CString class_name, - FT_Memory memory ); - FT_BASE_DEF( FT_Object ) - ft_object_new_from_type( FT_Type type, - FT_Pointer data, - FT_Memory memory ); + /************************************************************** + * + * @function: ft_class_from_name + * + * @description: + * retrieves the class object corresponding to a given type + * name. The class is created when needed + * + * @output: + * aclass :: handle to corresponding class object. NULL + * in case of error + * + * @input: + * name :: class name + * library :: library handle + * + * @return: + * error code. 0 means success + * + * @note: + * this function is _very_ slow. You should only use it for + * debugging purposes.. + */ + FT_BASE_DEF( FT_Error ) + ft_class_from_name( FT_Class *aclass, + FT_CString class_name, + FT_Library library ); + /* */ +#include FT_INTERNAL_HASH_H + + typedef struct FT_ClassHNodeRec_* FT_ClassHNode; + + typedef struct FT_ClassHNodeRec_ + { + FT_HashNodeRec hnode; + FT_Type type; + FT_Class clazz; + + } FT_ClassHNodeRec; + + typedef struct FT_MetaClassRec_ + { + FT_ClassRec clazz; /* the meta-class is a class itself */ + FT_HashRec type_to_class; /* the type => class hash table */ + + } FT_MetaClassRec, *FT_MetaClass; + + + /* initialize meta class */ + FT_BASE_DEF( FT_Error ) + ft_metaclass_init( FT_MetaClass meta, + FT_Library library ); + + /* finalize meta class - destroy all registered class objects */ FT_BASE_DEF( void ) - ft_object_create_from_type( FT_Object *pobject, - FT_Type type, - FT_Pointer init_data, - FT_Memory memory ); - - FT_BASE_DEF( void ) - ft_object_push( FT_Object object ); - - FT_BASE_DEF( void ) - ft_object_pop( FT_Object object ); - - FT_BASE_DEF( void ) - ft_object_pop_destroy( FT_Object object ); + ft_metaclass_done( FT_MetaClass meta ); + /* */ FT_END_HEADER diff --git a/src/autohint/ahglyph.c b/src/autohint/ahglyph.c index 2fd219e32..da6325985 100644 --- a/src/autohint/ahglyph.c +++ b/src/autohint/ahglyph.c @@ -815,7 +815,7 @@ segment_dir = point->out_dir; /* clear all segment fields */ - FT_MEM_SET( segment, 0, sizeof ( *segment ) ); + FT_ZERO( segment ); segment->dir = segment_dir; segment->flags = ah_edge_normal; @@ -877,7 +877,7 @@ if ( min_point ) { /* clear all segment fields */ - FT_MEM_SET( segment, 0, sizeof ( *segment ) ); + FT_ZERO( segment ); segment->dir = segment_dir; segment->flags = ah_edge_normal; @@ -893,7 +893,7 @@ if ( max_point ) { /* clear all segment fields */ - FT_MEM_SET( segment, 0, sizeof ( *segment ) ); + FT_ZERO( segment ); segment->dir = segment_dir; segment->flags = ah_edge_normal; diff --git a/src/base/fthash.c b/src/base/fthash.c index bd3cb21ff..6a72a3ebe 100644 --- a/src/base/fthash.c +++ b/src/base/fthash.c @@ -45,7 +45,7 @@ - FT_BASE_DEF( void ) + FT_BASE_DEF( FT_Error ) ft_hash_init( FT_Hash table, FT_Hash_CompareFunc compare, FT_Memory memory ) @@ -196,7 +196,7 @@ table->mask >>= 1; p = table->mask; - FT_RENEW_ARRAY( hash->buckets, (mask+1)*2, (mask) ); + FT_RENEW_ARRAY( hash->buckets, (mask+1)*2, (mask+1) ); } else p--; diff --git a/src/base/ftstroker.c b/src/base/ftstroker.c index c7e74941e..ea913ca4e 100644 --- a/src/base/ftstroker.c +++ b/src/base/ftstroker.c @@ -2,6 +2,171 @@ #include FT_STROKER_H #include FT_TRIGONOMETRY_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_Bool 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_Vector_Angle( &d2 ); + } + else if (close2) + { + *angle_in = *angle_out = FT_Vector_Angle( &d1 ); + } + else + { + *angle_in = FT_Vector_Angle(&d1); + *angle_out = FT_Vector_Angle(&d2); + } + + theta = ft_pos_abs( FT_Angle_Diff( *angle_in, *angle_out ) ); + return ( 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_Bool 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_Vector_Angle( &d2 ); + *angle_out = FT_Vector_Angle( &d3 ); + } + else /* close2 */ + { + *angle_in = FT_Vector_Angle( &d1 ); + *angle_mid = *angle_out = FT_Vector_Angle( &d2 ); + } + } + else if (close2) + { + *angle_in = *angle_mid = FT_Vector_Angle( &d1 ); + *angle_out = FT_Vector_Angle( &d3 ); + } + else + { + *angle_in = FT_Vector_Angle(&d1); + *angle_mid = FT_Vector_Angle(&d2); + *angle_out = FT_Vector_Angle(&d3); + } + theta1 = ft_pos_abs( nv_angle_diff( *angle_in, *angle_mid ) ); + theta2 = ft_pos_abs( nv_angle_diff( *angle_mid, *angle_out ) ); + return ( theta1 < FT_SMALL_CUBIC_THRESHOLD && + theta2 < FT_SMALL_CUBIC_THRESHOLD ); + } + + + /***************************************************************************/ /***************************************************************************/ /***** *****/ @@ -16,7 +181,7 @@ 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; @@ -29,7 +194,7 @@ FT_Bool movable; FT_Int start; /* index of current sub-path start point */ FT_Memory memory; - + } FT_StrokeBorderRec, *FT_StrokeBorder; @@ -40,19 +205,19 @@ 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 = - + 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: @@ -63,35 +228,47 @@ 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; + + border->start = -1; + border->movable = 0; } static FT_Error ft_stroke_border_lineto( FT_StrokeBorder border, - FT_Vector* to ) + FT_Vector* to, + FT_Bool movable ) { FT_Error error; - + FT_ASSERT( border->start >= 0 ); - - error = ft_stroker_border_grow( border, 1 ); - if (!error) + + if ( border->movable ) { - 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; + /* 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 @@ -100,25 +277,26 @@ FT_Vector* to ) { FT_Error error; - + FT_ASSERT( border->start >= 0 ); - - error = ft_stroker_border_grow( border, 2 ); + + 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 @@ -128,25 +306,96 @@ FT_Vector* to ) { FT_Error error; - + FT_ASSERT( border->start >= 0 ); - - error = ft_stroker_border_grow( border, 3 ); + + 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_StrokerBorder 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, + (FT_ONE + 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; } @@ -156,17 +405,18 @@ 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 ); + 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 ) @@ -174,12 +424,12 @@ 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 ) @@ -187,16 +437,16 @@ 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; @@ -211,7 +461,7 @@ /***************************************************************************/ /***************************************************************************/ -#define FT_SIDE_TO_ROTATE(s) (FT_PI2 - (s)*FT_PI) +#define FT_SIDE_TO_ROTATE(s) (FT_ANGLE_PI2 - (s)*FT_ANGLE_PI) typedef struct FT_StrokerRec_ { @@ -230,7 +480,7 @@ FT_StrokeBorderRec borders[2]; FT_Memory memory; - + } FT_StrokerRec; @@ -240,11 +490,11 @@ { 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 ); } @@ -264,22 +514,22 @@ 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_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 ); } @@ -287,4 +537,684 @@ + /* 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 ); + + 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 ); + + Exit: + 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; + else + theta = theta/2; + + thcos = FT_Cos( theta ); + sigma = FT_MulFix( stroker->miter_limit, thcos ); + + if ( sigma >= FT_ONE ) + miter = 0; + + phi = stroker->angle_in + theta + rotate; + + 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_MulDiv( radius, FT_DivFix( FT_ONE - sigma, + ft_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 ); + } + else /* this is a bevel (intersection) */ + { + FT_Fixed length; + FT_Vector delta; + + length = FT_DivFix( stroker->radius, thcos ); + + FT_Vector_From_Polar( &delta, length, phi ); + delta.x += stroker->center.x; + delta.y += stroker->center.y; + + error = ft_stroke_border_lineto( border, &delta, 0 ); + if (error) goto Exit; + + /* now add end point */ + FT_Vector_From_Polar( &delta, stroker->radius, stroker->angle_out + rotate ); + delta.x += stroker->center.x; + delta.y += stroker->center.y; + + error = ft_stroke_border_lineto( border, &delta, 1 ); + } + } + Exit: + return error; + } + + + static FT_Error + ft_stroker_process_corner( FT_Stroker stroker ) + { + FT_Error error = 0; + FT_Angle turn; + FT_Int inside_side; + + turn = FT_Angle_Diff( stroker->angle_in, stroker->angle_out ); + + /* no specific corner processing is required if the turn is 0 */ + if (turn == 0) + goto Exit; + + /* when we turn to the right, the inside side is 0 */ + inside_side = 0; + + /* otherwise, the inside side is 1 */ + if (turn < 0) + inside_side = 1; + + /* process the inside side */ + error = ft_stroker_inside( stroker, inside_side ); + if (error) goto Exit; + + /* process the outside side */ + error = ft_stroker_outside( stroker, 1-inside_side ); + + Exit: + return error; + } + + + /* add two points to the left and right borders corresponding to the */ + /* start of the subpath.. */ + static FT_Error + ft_stroker_subpath_start( FT_Stroker stroker, + FT_Angle start_angle ) + { + FT_Vector delta; + FT_Vector point; + FT_Error error; + FT_StrokeBorder border; + + FT_Vector_From_Polar( &delta, stroker->radius, start_angle + FT_ANGLE_PI2 ); + + point.x = stroker->center.x + delta.x; + point.y = stroker->center.y + delta.y; + + border = stroker->borders; + error = ft_stroke_border_moveto( border, &point, stroker->subpath_open ); + if (error) goto Exit; + + point.x = stroker->center.x - delta.x; + point.y = stroker->center.y - delta.y; + + border++; + error = ft_stroke_border_moveto( border, &point, stroker->subpath_open ); + + /* save angle for last cap */ + stroker->subpath_angle = start_angle; + stroker->first_point = 0; + + Exit: + return error; + } + + + FT_EXPORT_DEF( FT_Error ) + FT_Stroker_LineTo( FT_Stroker stroker, + FT_Vector* to ) + { + FT_Error error = 0; + FT_StrokeBorder* border; + FT_Vector delta; + FT_Angle angle; + FT_Int side; + + delta.x = to->x - stroker->center.x; + delta.y = to->y - stroker->center.y; + + angle = FT_Atan2( delta.x, delta.y ); + FT_Vector_From_Polar( &delta, stroker->radius, angle + FT_ANGLE_PI2 ); + + /* process corner if necessary */ + if ( stroker->first_point ) + { + /* this is the first segment of a subpath. We need to */ + /* add a point to each border at their respective starting */ + /* point locations.. */ + error = ft_stroker_subpath_start( stroker, angle ); + if (error) goto Exit; + } + else + { + /* process the current corner */ + stroker->angle_out = angle; + error = ft_stroker_process_corner( stroker ); + if (error) goto Exit; + } + + /* now add a line segment to both the "inside" and "outside" paths */ + + for ( border = stroker->borders, side = 1; side >= 0; side--, border++ ) + { + FT_Vector point; + + point.x = to->x + delta.x; + point.y = to->y + delta.y; + + error = ft_stroke_border_lineto( border, &point, 1 ); + if (error) goto Exit; + + delta.x = -delta.x; + delta.y = -delta.y; + } + + stroker->angle_in = angle; + stroker->center = *to; + + Exit: + return error; + } + + + + FT_EXPORT_DEF( FT_Error ) + FT_Stroker_ConicTo( FT_Stroker stroker, + FT_Vector* control, + FT_Vector* to ) + { + FT_Error error = 0; + FT_Vector bez_stack[34]; + FT_Vector* arc; + FT_Vector* limit = bez_stack + 30; + FT_Angle start_angle; + FT_Bool first_arc = 1; + + arc = bez_stack; + arc[0] = *to; + arc[1] = *control; + arc[2] = stroker->center; + + while ( arc >= bez_stack ) + { + FT_Angle angle_in, angle_out; + + if ( arc < limit && + !ft_conic_is_small_enough( arc, &angle_in, &angle_out ) ) + { + ft_conic_split( arc ); + arc += 2; + continue; + } + + if ( first_arc ) + { + first_arc = 0; + + start_angle = angle_in; + + /* process corner if necessary */ + if ( stroker->first_point ) + error = ft_stroker_subpath_start( stroker, start_angle ); + else + { + stroker->angle_out = start_angle; + error = ft_stroker_process_corner( stroker ); + } + } + + /* the arc's angle is small enough, we can add it directly to each */ + /* border.. */ + { + FT_Vector ctrl, end; + FT_Angle theta, phi, rotate; + FT_Fixed length; + FT_Int side; + + theta = FT_Angle_Diff( angle_in, angle_out )/2; + phi = angle_in + theta; + length = FT_DivFix( stroker->radius, FT_Cos(theta) ); + + for ( side = 0; side <= 1; side++ ) + { + rotate = FT_SIDE_TO_ROTATE(side); + + /* compute control point */ + FT_Vector_From_Polar( &ctrl, length, phi + rotate ); + ctrl.x += arc[1].x; + ctrl.y += arc[1].y; + + /* compute end point */ + FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate ); + end.x += arc[0].x; + end.y += arc[0].y; + + error = ft_stroke_border_conicto( stroker->borders + side, &ctrl, &end ); + if (error) goto Exit; + } + } + + arc -= 2; + + if (arc < bez_stack) + stroker->angle_in = angle_out; + } + + stroker->center = *to; + + Exit: + return error; + } + + + + FT_EXPORT_DEF( FT_Error ) + FT_Stroker_CubicTo( FT_Stroker stroker, + FT_Vector* control1, + FT_Vector* control2, + FT_Vector* to ) + { + FT_Error error = 0; + FT_Vector bez_stack[37]; + FT_Vector* arc; + FT_Vector* limit = bez_stack + 32; + FT_Angle start_angle; + FT_Bool first_arc = 1; + + arc = bez_stack; + arc[0] = *to; + arc[1] = *control2; + arc[2] = *control1; + arc[3] = stroker->center; + + while ( arc >= bez_stack ) + { + FT_Angle angle_in, angle_mid, angle_out; + + if ( arc < limit && + !ft_cubic_is_small_enough( arc, &angle_in, &angle_mid, &angle_out ) ) + { + ft_cubic_split( arc ); + arc += 3; + continue; + } + + if ( first_arc ) + { + first_arc = 0; + + /* process corner if necessary */ + start_angle = angle_in; + + if ( stroker->first_point ) + error = ft_stroker_subpath_start( stroker, start_angle ); + else + { + stroker->angle_out = start_angle; + error = ft_stroker_process_corner( stroker ); + } + if (error) goto Exit; + } + + /* the arc's angle is small enough, we can add it directly to each */ + /* border.. */ + { + FT_Vector ctrl1, ctrl2, end; + FT_Angle theta1, phi1, theta2, phi2, rotate; + FT_Fixed length1, length2; + FT_Int side; + + theta1 = ft_pos_abs( angle_mid - angle_in )/2; + theta2 = ft_pos_abs( angle_out - angle_mid )/2; + phi1 = (angle_mid+angle_in)/2; + phi2 = (angle_mid+angle_out)/2; + length1 = FT_DivFix( stroker->radius, FT_Cos(theta1) ); + length2 = FT_DivFix( stroker->radius, FT_Cos(theta2) ); + + for ( side = 0; side <= 1; side++ ) + { + rotate = FT_SIDE_TO_ROTATE(side); + + /* compute control points */ + FT_Vector_From_Polar( &ctrl1, length1, phi1 + rotate ); + ft_vector_add( &ctrl1, &arc[2] ); + + FT_Vector_From_Polar( &ctrl2, length2, phi2 + rotate ); + ft_vector_add( &ctrl2, &arc[1] ); + + /* compute end point */ + FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate ); + ft_vector_add( &end, &arc[0] ); + + error = ft_stroke_border_cubicto( stroker->borders + side, &ctrl1, &ctrl2, &end ); + if (error) goto Exit; + } + } + + arc -= 3; + if (arc < bez_stack) + stroker->angle_in = angle_out; + } + + stroker->center = *to; + + Exit: + return error; + } + + + FT_EXPORT_DEF( FT_Error ) + FT_Stroker_BeginSubPath( FT_Stroker stroker, + FT_Vector* to, + FT_Bool open ) + { + /* we cannot process the first point, because there is not enough */ + /* information regarding its corner/cap. The latter will be processed */ + /* in the "end_subpath" routine */ + /* */ + stroker->first_point = 1; + stroker->center = *to; + stroker->subpath_open = open; + + /* record the subpath start point index for each border */ + stroker->subpath_start = *to; + return 0; + } + + + static + FT_Error ft_stroker_add_reverse_left( FT_Stroker stroker, + FT_Bool open ) + { + FT_StrokeBorder right = stroker->borders + 0; + FT_StrokeBorder left = stroker->borders + 1; + FT_Int new_points; + FT_Error error = 0; + + FT_ASSERT( left->start >= 0 ); + + new_points = left->num_points - left->start; + if ( new_points > 0 ) + { + error = ft_stroker_border_grow( right, (FT_UInt)new_points ); + if (error) goto Exit; + { + FT_Vector* dst_point = right->points + right->num_points; + FT_Path_Tag* dst_tag = right->tags + right->num_points; + FT_Vector* src_point = left->points + left->num_points - 1; + FT_Path_Tag* src_tag = left->tags + left->num_points - 1; + + while ( src_point >= left->points + left->start ) + { + *dst_point = *src_point; + *dst_tag = *src_tag; + + if (open) + dst_tag[0] &= ~(FT_STROKER_TAG_BEGIN | FT_STROKER_TAG_END); + else + { + /* switch begin/end tags if necessary.. */ + if (dst_tag[0] & (FT_STROKER_TAG_BEGIN | FT_STROKER_TAG_END)) + dst_tag[0] ^= (FT_STROKER_TAG_BEGIN | FT_STROKER_TAG_END); + } + + src_point--; + src_tag--; + dst_point++; + dst_tag++; + } + } + left->num_points = left->start; + right->num_points += new_points; + + right->movable = 0; + left->movable = 0; + } + Exit: + return error; + } + + + /* there's a lot of magic in this function !! */ + FT_EXPORT_DEF( FT_Error ) + FT_Stroker_EndSubPath( FT_Stroker stroker ) + { + FT_Error error = 0; + + if ( stroker->subpath_open ) + { + FT_StrokeBorder right = stroker->borders; + + /* all right, this is an opened path, we need to add a cap between */ + /* right & left, add the reverse of left, then add a final cap between */ + /* left & right.. */ + error = ft_stroker_cap( stroker, stroker->angle_in, 0 ); + if (error) goto Exit; + + /* add reversed points from "left" to "right" */ + error = ft_stroker_add_reverse_left( stroker, 1 ); + if (error) goto Exit; + + /* now add the final cap */ + stroker->center = stroker->subpath_start; + error = ft_stroker_cap( stroker, stroker->subpath_angle+FT_ANGLE_PI, 0 ); + if (error) goto Exit; + + /* now, end the right subpath accordingly. the left one is */ + /* rewind and doesn't need further processing.. */ + error = ft_stroke_border_close( right ); + } + else + { + FT_Angle turn; + FT_Int inside_side; + + /* process the corner ... */ + stroker->angle_out = stroker->subpath_angle; + turn = FT_Angle_Diff( stroker->angle_in, stroker->angle_out ); + + /* no specific corner processing is required if the turn is 0 */ + if (turn != 0) + { + /* when we turn to the right, the inside side is 0 */ + inside_side = 0; + + /* otherwise, the inside side is 1 */ + if (turn < 0) + inside_side = 1; + + /* IMPORTANT: WE DO NOT PROCESS THE INSIDE BORDER HERE !! */ + /* process the inside side */ + /* error = ft_stroker_inside( stroker, inside_side ); + if (error) goto Exit; */ + + /* process the outside side */ + error = ft_stroker_outside( stroker, 1-inside_side ); + if (error) goto Exit; + } + + /* we will first end our two subpaths */ + error = ft_stroker_border_close( stroker->borders + 0 ); + if (error) goto Exit; + + error = ft_stroker_border_close( stroker->borders + 1 ); + if (error) goto Exit; + + /* now, add the reversed left subpath to "right" */ + error = ft_stroker_add_reverse_left( stroker, 0 ); + if (error) goto Exit; + } + + Exit: + return error; + } + + \ No newline at end of file diff --git a/src/base/fttrigon.c b/src/base/fttrigon.c index 490a26ff1..321c6ec0d 100644 --- a/src/base/fttrigon.c +++ b/src/base/fttrigon.c @@ -451,6 +451,20 @@ } + /* documentation is in fttrigon.h */ + + FT_EXPORT_DEF( void ) + FT_Vector_From_Polar( FT_Vector* vec, + FT_Fixed length, + FT_Angle angle ) + { + vec->x = length; + vec->y = 0; + + FT_Vector_Rotate( vec, angle ); + } + + /* documentation is in fttrigon.h */ FT_EXPORT_DEF( FT_Angle ) @@ -458,14 +472,14 @@ FT_Angle angle2 ) { FT_Angle delta = angle2 - angle1; - + delta %= FT_ANGLE_2PI; if ( delta > FT_ANGLE_PI ) delta -= FT_ANGLE_2PI; - + return delta; - } + } /* END */