ref: 0d73b0c49abc97cb89d05bb5295769f289d271e2
parent: b7e18efcd2d6a71ec1a4bdf167f78d707ac91593
author: David Turner <[email protected]>
date: Wed Jun 26 19:45:21 EDT 2002
* 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)
--- 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 <[email protected]>
--- a/include/freetype/ftstroker.h
+++ b/include/freetype/ftstroker.h
@@ -44,7 +44,7 @@
FT_STROKER_LINEJOIN_ROUND = 0,
FT_STROKER_LINEJOIN_BEVEL,
FT_STROKER_LINEJOIN_MITER
-
+
} FT_Stroker_LineJoin;
@@ -74,10 +74,10 @@
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_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_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 )
@@ -133,11 +132,6 @@
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
--- a/include/freetype/fttrigon.h
+++ b/include/freetype/fttrigon.h
@@ -291,6 +291,28 @@
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 );
+
/* */
--- a/include/freetype/internal/fthash.h
+++ b/include/freetype/internal/fthash.h
@@ -59,9 +59,21 @@
/***********************************************************
*
- * @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
*
* @input:
@@ -72,8 +84,8 @@
* 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,17 +118,13 @@
*/
typedef struct FT_HashRec_
{
- FT_Memory memory;
FT_HashNode* buckets;
FT_UInt p;
FT_UInt mask; /* really maxp-1 */
- FT_UInt slack;
+ FT_Long slack;
+ FT_Hash_EqualFunc node_equal;
+ FT_Memory memory;
-
- FT_UInt node_size;
- FT_Hash_CompareFunc node_compare;
- FT_Hash_ComputeFunc node_hash;
-
} FT_HashRec, *FT_Hash;
@@ -168,36 +176,39 @@
* 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_BASE_DEF( FT_HashNode* )
+ FT_BASE_DEF( FT_HashLookup )
ft_hash_lookup( FT_Hash table,
FT_HashNode keynode )
@@ -273,10 +284,13 @@
*
* @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 @@
*
* // 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 @@
*
* @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 @@
* 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 )
--- a/include/freetype/internal/ftobject.h
+++ b/include/freetype/internal/ftobject.h
@@ -140,14 +140,11 @@
* 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 @@
*
* @input:
* object :: handle to target object
- *
- * @throws: *never* !!
*/
typedef void (*FT_Object_DoneFunc)( FT_Object object );
@@ -179,6 +174,7 @@
* 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
@@ -185,6 +181,8 @@
* 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_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 @@
/**************************************************************
*
- * @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.
+ * create a new object (class instance) from a @FT_Type
*
- * this function is a bit more akward to use but completely
- * avoids push/pop pairs during object construction and is
- * therefore faster.
- *
* @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 );
+ /* */
- FT_BASE_DEF( void )
- ft_object_create_from_type( FT_Object *pobject,
- FT_Type type,
- FT_Pointer init_data,
- FT_Memory memory );
+#include FT_INTERNAL_HASH_H
- FT_BASE_DEF( void )
- ft_object_push( FT_Object object );
+ typedef struct FT_ClassHNodeRec_* FT_ClassHNode;
- FT_BASE_DEF( void )
- ft_object_pop( FT_Object object );
+ 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_pop_destroy( FT_Object object );
+ ft_metaclass_done( FT_MetaClass meta );
+ /* */
FT_END_HEADER
--- 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;
--- 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--;
--- a/src/base/ftstroker.c
+++ b/src/base/ftstroker.c
@@ -5,6 +5,171 @@
/***************************************************************************/
/***************************************************************************/
/***** *****/
+ /***** 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 );
+ }
+
+
+
+ /***************************************************************************/
+ /***************************************************************************/
+ /***** *****/
/***** STROKE BORDERS *****/
/***** *****/
/***************************************************************************/
@@ -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,12 +205,12 @@
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;
@@ -52,7 +217,7 @@
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,45 +306,117 @@
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;
+ }
+
+
+ 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 );
+ 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,13 +424,13 @@
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,27 +514,707 @@
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 );
}
}
+
+
+ /* 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
--- a/src/base/fttrigon.c
+++ b/src/base/fttrigon.c
@@ -453,19 +453,33 @@
/* 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 )
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 */