1 /***************************************************************************/
5 /* FreeType path stroker (body). */
7 /* Copyright 2002, 2003, 2004, 2005, 2006 by */
8 /* David Turner, Robert Wilhelm, and Werner Lemberg. */
10 /* This file is part of the FreeType project, and may only be used, */
11 /* modified, and distributed under the terms of the FreeType project */
12 /* license, LICENSE.TXT. By continuing to use, modify, or distribute */
13 /* this file you indicate that you have read the license and */
14 /* understand and accept it fully. */
16 /***************************************************************************/
21 #include FT_TRIGONOMETRY_H
23 #include FT_INTERNAL_MEMORY_H
24 #include FT_INTERNAL_DEBUG_H
25 #include FT_INTERNAL_OBJECTS_H
28 /* documentation is in ftstroke.h */
30 FT_EXPORT_DEF( FT_StrokerBorder
)
31 FT_Outline_GetInsideBorder( FT_Outline
* outline
)
33 FT_Orientation o
= FT_Outline_Get_Orientation( outline
);
36 return o
== FT_ORIENTATION_TRUETYPE
? FT_STROKER_BORDER_RIGHT
37 : FT_STROKER_BORDER_LEFT
;
41 /* documentation is in ftstroke.h */
43 FT_EXPORT_DEF( FT_StrokerBorder
)
44 FT_Outline_GetOutsideBorder( FT_Outline
* outline
)
46 FT_Orientation o
= FT_Outline_Get_Orientation( outline
);
49 return o
== FT_ORIENTATION_TRUETYPE
? FT_STROKER_BORDER_LEFT
50 : FT_STROKER_BORDER_RIGHT
;
54 /***************************************************************************/
55 /***************************************************************************/
57 /***** BEZIER COMPUTATIONS *****/
59 /***************************************************************************/
60 /***************************************************************************/
62 #define FT_SMALL_CONIC_THRESHOLD ( FT_ANGLE_PI / 6 )
63 #define FT_SMALL_CUBIC_THRESHOLD ( FT_ANGLE_PI / 6 )
66 #define FT_IS_SMALL( x ) ( (x) > -FT_EPSILON && (x) < FT_EPSILON )
70 ft_pos_abs( FT_Pos x
)
72 return x
>= 0 ? x
: -x
;
77 ft_conic_split( FT_Vector
* base
)
82 base
[4].x
= base
[2].x
;
84 a
= base
[3].x
= ( base
[2].x
+ b
) / 2;
85 b
= base
[1].x
= ( base
[0].x
+ b
) / 2;
86 base
[2].x
= ( a
+ b
) / 2;
88 base
[4].y
= base
[2].y
;
90 a
= base
[3].y
= ( base
[2].y
+ b
) / 2;
91 b
= base
[1].y
= ( base
[0].y
+ b
) / 2;
92 base
[2].y
= ( a
+ b
) / 2;
97 ft_conic_is_small_enough( FT_Vector
* base
,
103 FT_Int close1
, close2
;
106 d1
.x
= base
[1].x
- base
[2].x
;
107 d1
.y
= base
[1].y
- base
[2].y
;
108 d2
.x
= base
[0].x
- base
[1].x
;
109 d2
.y
= base
[0].y
- base
[1].y
;
111 close1
= FT_IS_SMALL( d1
.x
) && FT_IS_SMALL( d1
.y
);
112 close2
= FT_IS_SMALL( d2
.x
) && FT_IS_SMALL( d2
.y
);
117 *angle_in
= *angle_out
= 0;
119 *angle_in
= *angle_out
= FT_Atan2( d2
.x
, d2
.y
);
123 *angle_in
= *angle_out
= FT_Atan2( d1
.x
, d1
.y
);
127 *angle_in
= FT_Atan2( d1
.x
, d1
.y
);
128 *angle_out
= FT_Atan2( d2
.x
, d2
.y
);
131 theta
= ft_pos_abs( FT_Angle_Diff( *angle_in
, *angle_out
) );
133 return FT_BOOL( theta
< FT_SMALL_CONIC_THRESHOLD
);
138 ft_cubic_split( FT_Vector
* base
)
143 base
[6].x
= base
[3].x
;
146 base
[1].x
= a
= ( base
[0].x
+ c
) / 2;
147 base
[5].x
= b
= ( base
[3].x
+ d
) / 2;
149 base
[2].x
= a
= ( a
+ c
) / 2;
150 base
[4].x
= b
= ( b
+ c
) / 2;
151 base
[3].x
= ( a
+ b
) / 2;
153 base
[6].y
= base
[3].y
;
156 base
[1].y
= a
= ( base
[0].y
+ c
) / 2;
157 base
[5].y
= b
= ( base
[3].y
+ d
) / 2;
159 base
[2].y
= a
= ( a
+ c
) / 2;
160 base
[4].y
= b
= ( b
+ c
) / 2;
161 base
[3].y
= ( a
+ b
) / 2;
166 ft_cubic_is_small_enough( FT_Vector
* base
,
169 FT_Angle
*angle_out
)
171 FT_Vector d1
, d2
, d3
;
172 FT_Angle theta1
, theta2
;
173 FT_Int close1
, close2
, close3
;
176 d1
.x
= base
[2].x
- base
[3].x
;
177 d1
.y
= base
[2].y
- base
[3].y
;
178 d2
.x
= base
[1].x
- base
[2].x
;
179 d2
.y
= base
[1].y
- base
[2].y
;
180 d3
.x
= base
[0].x
- base
[1].x
;
181 d3
.y
= base
[0].y
- base
[1].y
;
183 close1
= FT_IS_SMALL( d1
.x
) && FT_IS_SMALL( d1
.y
);
184 close2
= FT_IS_SMALL( d2
.x
) && FT_IS_SMALL( d2
.y
);
185 close3
= FT_IS_SMALL( d3
.x
) && FT_IS_SMALL( d3
.y
);
187 if ( close1
|| close3
)
191 /* basically a point */
192 *angle_in
= *angle_out
= *angle_mid
= 0;
196 *angle_in
= *angle_mid
= FT_Atan2( d2
.x
, d2
.y
);
197 *angle_out
= FT_Atan2( d3
.x
, d3
.y
);
201 *angle_in
= FT_Atan2( d1
.x
, d1
.y
);
202 *angle_mid
= *angle_out
= FT_Atan2( d2
.x
, d2
.y
);
207 *angle_in
= *angle_mid
= FT_Atan2( d1
.x
, d1
.y
);
208 *angle_out
= FT_Atan2( d3
.x
, d3
.y
);
212 *angle_in
= FT_Atan2( d1
.x
, d1
.y
);
213 *angle_mid
= FT_Atan2( d2
.x
, d2
.y
);
214 *angle_out
= FT_Atan2( d3
.x
, d3
.y
);
217 theta1
= ft_pos_abs( FT_Angle_Diff( *angle_in
, *angle_mid
) );
218 theta2
= ft_pos_abs( FT_Angle_Diff( *angle_mid
, *angle_out
) );
220 return FT_BOOL( theta1
< FT_SMALL_CUBIC_THRESHOLD
&&
221 theta2
< FT_SMALL_CUBIC_THRESHOLD
);
225 /***************************************************************************/
226 /***************************************************************************/
228 /***** STROKE BORDERS *****/
230 /***************************************************************************/
231 /***************************************************************************/
235 FT_STROKE_TAG_ON
= 1, /* on-curve point */
236 FT_STROKE_TAG_CUBIC
= 2, /* cubic off-point */
237 FT_STROKE_TAG_BEGIN
= 4, /* sub-path start */
238 FT_STROKE_TAG_END
= 8 /* sub-path end */
242 #define FT_STROKE_TAG_BEGIN_END (FT_STROKE_TAG_BEGIN|FT_STROKE_TAG_END)
244 typedef struct FT_StrokeBorderRec_
251 FT_Int start
; /* index of current sub-path start point */
255 } FT_StrokeBorderRec
, *FT_StrokeBorder
;
259 ft_stroke_border_grow( FT_StrokeBorder border
,
262 FT_UInt old_max
= border
->max_points
;
263 FT_UInt new_max
= border
->num_points
+ new_points
;
267 if ( new_max
> old_max
)
269 FT_UInt cur_max
= old_max
;
270 FT_Memory memory
= border
->memory
;
273 while ( cur_max
< new_max
)
274 cur_max
+= ( cur_max
>> 1 ) + 16;
276 if ( FT_RENEW_ARRAY( border
->points
, old_max
, cur_max
) ||
277 FT_RENEW_ARRAY( border
->tags
, old_max
, cur_max
) )
280 border
->max_points
= cur_max
;
288 ft_stroke_border_close( FT_StrokeBorder border
,
291 FT_UInt start
= border
->start
;
292 FT_UInt count
= border
->num_points
;
295 FT_ASSERT( border
->start
>= 0 );
297 /* don't record empty paths! */
298 if ( count
<= start
+ 1U )
299 border
->num_points
= start
;
302 /* copy the last point to the start of this sub-path, since */
303 /* it contains the `adjusted' starting coordinates */
304 border
->num_points
= --count
;
305 border
->points
[start
] = border
->points
[count
];
309 /* reverse the points */
311 FT_Vector
* vec1
= border
->points
+ start
+ 1;
312 FT_Vector
* vec2
= border
->points
+ count
- 1;
315 for ( ; vec1
< vec2
; vec1
++, vec2
-- )
328 FT_Byte
* tag1
= border
->tags
+ start
+ 1;
329 FT_Byte
* tag2
= border
->tags
+ count
- 1;
332 for ( ; tag1
< tag2
; tag1
++, tag2
-- )
344 border
->tags
[start
] |= FT_STROKE_TAG_BEGIN
;
345 border
->tags
[count
- 1] |= FT_STROKE_TAG_END
;
354 ft_stroke_border_lineto( FT_StrokeBorder border
,
361 FT_ASSERT( border
->start
>= 0 );
363 if ( border
->movable
)
365 /* move last point */
366 border
->points
[border
->num_points
- 1] = *to
;
371 error
= ft_stroke_border_grow( border
, 1 );
374 FT_Vector
* vec
= border
->points
+ border
->num_points
;
375 FT_Byte
* tag
= border
->tags
+ border
->num_points
;
379 tag
[0] = FT_STROKE_TAG_ON
;
381 border
->num_points
+= 1;
384 border
->movable
= movable
;
390 ft_stroke_border_conicto( FT_StrokeBorder border
,
397 FT_ASSERT( border
->start
>= 0 );
399 error
= ft_stroke_border_grow( border
, 2 );
402 FT_Vector
* vec
= border
->points
+ border
->num_points
;
403 FT_Byte
* tag
= border
->tags
+ border
->num_points
;
409 tag
[1] = FT_STROKE_TAG_ON
;
411 border
->num_points
+= 2;
419 ft_stroke_border_cubicto( FT_StrokeBorder border
,
427 FT_ASSERT( border
->start
>= 0 );
429 error
= ft_stroke_border_grow( border
, 3 );
432 FT_Vector
* vec
= border
->points
+ border
->num_points
;
433 FT_Byte
* tag
= border
->tags
+ border
->num_points
;
440 tag
[0] = FT_STROKE_TAG_CUBIC
;
441 tag
[1] = FT_STROKE_TAG_CUBIC
;
442 tag
[2] = FT_STROKE_TAG_ON
;
444 border
->num_points
+= 3;
451 #define FT_ARC_CUBIC_ANGLE ( FT_ANGLE_PI / 2 )
455 ft_stroke_border_arcto( FT_StrokeBorder border
,
458 FT_Angle angle_start
,
459 FT_Angle angle_diff
)
461 FT_Angle total
, angle
, step
, rotate
, next
, theta
;
462 FT_Vector a
, b
, a2
, b2
;
467 /* compute start point */
468 FT_Vector_From_Polar( &a
, radius
, angle_start
);
474 rotate
= ( angle_diff
>= 0 ) ? FT_ANGLE_PI2
: -FT_ANGLE_PI2
;
479 if ( step
> FT_ARC_CUBIC_ANGLE
)
480 step
= FT_ARC_CUBIC_ANGLE
;
482 else if ( step
< -FT_ARC_CUBIC_ANGLE
)
483 step
= -FT_ARC_CUBIC_ANGLE
;
492 /* compute end point */
493 FT_Vector_From_Polar( &b
, radius
, next
);
497 /* compute first and second control points */
498 length
= FT_MulDiv( radius
, FT_Sin( theta
) * 4,
499 ( 0x10000L
+ FT_Cos( theta
) ) * 3 );
501 FT_Vector_From_Polar( &a2
, length
, angle
+ rotate
);
505 FT_Vector_From_Polar( &b2
, length
, next
- rotate
);
510 error
= ft_stroke_border_cubicto( border
, &a2
, &b2
, &b
);
514 /* process the rest of the arc ?? */
525 ft_stroke_border_moveto( FT_StrokeBorder border
,
528 /* close current open path if any ? */
529 if ( border
->start
>= 0 )
530 ft_stroke_border_close( border
, 0 );
532 border
->start
= border
->num_points
;
535 return ft_stroke_border_lineto( border
, to
, 0 );
540 ft_stroke_border_init( FT_StrokeBorder border
,
543 border
->memory
= memory
;
544 border
->points
= NULL
;
547 border
->num_points
= 0;
548 border
->max_points
= 0;
555 ft_stroke_border_reset( FT_StrokeBorder border
)
557 border
->num_points
= 0;
564 ft_stroke_border_done( FT_StrokeBorder border
)
566 FT_Memory memory
= border
->memory
;
569 FT_FREE( border
->points
);
570 FT_FREE( border
->tags
);
572 border
->num_points
= 0;
573 border
->max_points
= 0;
580 ft_stroke_border_get_counts( FT_StrokeBorder border
,
581 FT_UInt
*anum_points
,
582 FT_UInt
*anum_contours
)
585 FT_UInt num_points
= 0;
586 FT_UInt num_contours
= 0;
588 FT_UInt count
= border
->num_points
;
589 FT_Vector
* point
= border
->points
;
590 FT_Byte
* tags
= border
->tags
;
591 FT_Int in_contour
= 0;
594 for ( ; count
> 0; count
--, num_points
++, point
++, tags
++ )
596 if ( tags
[0] & FT_STROKE_TAG_BEGIN
)
598 if ( in_contour
!= 0 )
603 else if ( in_contour
== 0 )
606 if ( tags
[0] & FT_STROKE_TAG_END
)
608 if ( in_contour
== 0 )
616 if ( in_contour
!= 0 )
622 *anum_points
= num_points
;
623 *anum_contours
= num_contours
;
634 ft_stroke_border_export( FT_StrokeBorder border
,
635 FT_Outline
* outline
)
637 /* copy point locations */
638 FT_ARRAY_COPY( outline
->points
+ outline
->n_points
,
640 border
->num_points
);
644 FT_UInt count
= border
->num_points
;
645 FT_Byte
* read
= border
->tags
;
646 FT_Byte
* write
= (FT_Byte
*)outline
->tags
+ outline
->n_points
;
649 for ( ; count
> 0; count
--, read
++, write
++ )
651 if ( *read
& FT_STROKE_TAG_ON
)
652 *write
= FT_CURVE_TAG_ON
;
653 else if ( *read
& FT_STROKE_TAG_CUBIC
)
654 *write
= FT_CURVE_TAG_CUBIC
;
656 *write
= FT_CURVE_TAG_CONIC
;
662 FT_UInt count
= border
->num_points
;
663 FT_Byte
* tags
= border
->tags
;
664 FT_Short
* write
= outline
->contours
+ outline
->n_contours
;
665 FT_Short idx
= (FT_Short
)outline
->n_points
;
668 for ( ; count
> 0; count
--, tags
++, idx
++ )
670 if ( *tags
& FT_STROKE_TAG_END
)
673 outline
->n_contours
++;
678 outline
->n_points
= (short)( outline
->n_points
+ border
->num_points
);
680 FT_ASSERT( FT_Outline_Check( outline
) == 0 );
684 /***************************************************************************/
685 /***************************************************************************/
687 /***** STROKER *****/
689 /***************************************************************************/
690 /***************************************************************************/
692 #define FT_SIDE_TO_ROTATE( s ) ( FT_ANGLE_PI2 - (s) * FT_ANGLE_PI )
694 typedef struct FT_StrokerRec_
700 FT_Bool subpath_open
;
701 FT_Angle subpath_angle
;
702 FT_Vector subpath_start
;
704 FT_Stroker_LineCap line_cap
;
705 FT_Stroker_LineJoin line_join
;
706 FT_Fixed miter_limit
;
710 FT_StrokeBorderRec borders
[2];
716 /* documentation is in ftstroke.h */
718 FT_EXPORT_DEF( FT_Error
)
719 FT_Stroker_New( FT_Library library
,
720 FT_Stroker
*astroker
)
728 return FT_Err_Invalid_Argument
;
730 memory
= library
->memory
;
732 if ( !FT_NEW( stroker
) )
734 stroker
->memory
= memory
;
736 ft_stroke_border_init( &stroker
->borders
[0], memory
);
737 ft_stroke_border_init( &stroker
->borders
[1], memory
);
744 /* documentation is in ftstroke.h */
746 FT_EXPORT_DEF( void )
747 FT_Stroker_Set( FT_Stroker stroker
,
749 FT_Stroker_LineCap line_cap
,
750 FT_Stroker_LineJoin line_join
,
751 FT_Fixed miter_limit
)
753 stroker
->radius
= radius
;
754 stroker
->line_cap
= line_cap
;
755 stroker
->line_join
= line_join
;
756 stroker
->miter_limit
= miter_limit
;
758 FT_Stroker_Rewind( stroker
);
762 /* documentation is in ftstroke.h */
764 FT_EXPORT_DEF( void )
765 FT_Stroker_Rewind( FT_Stroker stroker
)
769 ft_stroke_border_reset( &stroker
->borders
[0] );
770 ft_stroke_border_reset( &stroker
->borders
[1] );
775 /* documentation is in ftstroke.h */
777 FT_EXPORT_DEF( void )
778 FT_Stroker_Done( FT_Stroker stroker
)
782 FT_Memory memory
= stroker
->memory
;
785 ft_stroke_border_done( &stroker
->borders
[0] );
786 ft_stroke_border_done( &stroker
->borders
[1] );
788 stroker
->memory
= NULL
;
794 /* creates a circular arc at a corner or cap */
796 ft_stroker_arcto( FT_Stroker stroker
,
799 FT_Angle total
, rotate
;
800 FT_Fixed radius
= stroker
->radius
;
802 FT_StrokeBorder border
= stroker
->borders
+ side
;
805 rotate
= FT_SIDE_TO_ROTATE( side
);
807 total
= FT_Angle_Diff( stroker
->angle_in
, stroker
->angle_out
);
808 if ( total
== FT_ANGLE_PI
)
811 error
= ft_stroke_border_arcto( border
,
814 stroker
->angle_in
+ rotate
,
821 /* adds a cap at the end of an opened path */
823 ft_stroker_cap( FT_Stroker stroker
,
830 if ( stroker
->line_cap
== FT_STROKER_LINECAP_ROUND
)
832 /* add a round cap */
833 stroker
->angle_in
= angle
;
834 stroker
->angle_out
= angle
+ FT_ANGLE_PI
;
835 error
= ft_stroker_arcto( stroker
, side
);
837 else if ( stroker
->line_cap
== FT_STROKER_LINECAP_SQUARE
)
839 /* add a square cap */
840 FT_Vector delta
, delta2
;
841 FT_Angle rotate
= FT_SIDE_TO_ROTATE( side
);
842 FT_Fixed radius
= stroker
->radius
;
843 FT_StrokeBorder border
= stroker
->borders
+ side
;
846 FT_Vector_From_Polar( &delta2
, radius
, angle
+ rotate
);
847 FT_Vector_From_Polar( &delta
, radius
, angle
);
849 delta
.x
+= stroker
->center
.x
+ delta2
.x
;
850 delta
.y
+= stroker
->center
.y
+ delta2
.y
;
852 error
= ft_stroke_border_lineto( border
, &delta
, 0 );
856 FT_Vector_From_Polar( &delta2
, radius
, angle
- rotate
);
857 FT_Vector_From_Polar( &delta
, radius
, angle
);
859 delta
.x
+= delta2
.x
+ stroker
->center
.x
;
860 delta
.y
+= delta2
.y
+ stroker
->center
.y
;
862 error
= ft_stroke_border_lineto( border
, &delta
, 0 );
870 /* process an inside corner, i.e. compute intersection */
872 ft_stroker_inside( FT_Stroker stroker
,
875 FT_StrokeBorder border
= stroker
->borders
+ side
;
876 FT_Angle phi
, theta
, rotate
;
877 FT_Fixed length
, thcos
, sigma
;
882 rotate
= FT_SIDE_TO_ROTATE( side
);
884 /* compute median angle */
885 theta
= FT_Angle_Diff( stroker
->angle_in
, stroker
->angle_out
);
886 if ( theta
== FT_ANGLE_PI
)
891 phi
= stroker
->angle_in
+ theta
;
893 thcos
= FT_Cos( theta
);
894 sigma
= FT_MulFix( stroker
->miter_limit
, thcos
);
896 /* TODO: find better criterion to switch off the optimization */
897 if ( sigma
< 0x10000L
)
899 FT_Vector_From_Polar( &delta
, stroker
->radius
,
900 stroker
->angle_out
+ rotate
);
901 delta
.x
+= stroker
->center
.x
;
902 delta
.y
+= stroker
->center
.y
;
907 length
= FT_DivFix( stroker
->radius
, thcos
);
909 FT_Vector_From_Polar( &delta
, length
, phi
+ rotate
);
910 delta
.x
+= stroker
->center
.x
;
911 delta
.y
+= stroker
->center
.y
;
914 error
= ft_stroke_border_lineto( border
, &delta
, 0 );
920 /* process an outside corner, i.e. compute bevel/miter/round */
922 ft_stroker_outside( FT_Stroker stroker
,
925 FT_StrokeBorder border
= stroker
->borders
+ side
;
930 if ( stroker
->line_join
== FT_STROKER_LINEJOIN_ROUND
)
932 error
= ft_stroker_arcto( stroker
, side
);
936 /* this is a mitered or beveled corner */
937 FT_Fixed sigma
, radius
= stroker
->radius
;
943 rotate
= FT_SIDE_TO_ROTATE( side
);
944 miter
= FT_BOOL( stroker
->line_join
== FT_STROKER_LINEJOIN_MITER
);
946 theta
= FT_Angle_Diff( stroker
->angle_in
, stroker
->angle_out
);
947 if ( theta
== FT_ANGLE_PI
)
950 phi
= stroker
->angle_in
;
955 phi
= stroker
->angle_in
+ theta
+ rotate
;
958 thcos
= FT_Cos( theta
);
959 sigma
= FT_MulFix( stroker
->miter_limit
, thcos
);
961 if ( sigma
>= 0x10000L
)
964 if ( miter
) /* this is a miter (broken angle) */
966 FT_Vector middle
, delta
;
970 /* compute middle point */
971 FT_Vector_From_Polar( &middle
,
972 FT_MulFix( radius
, stroker
->miter_limit
),
974 middle
.x
+= stroker
->center
.x
;
975 middle
.y
+= stroker
->center
.y
;
977 /* compute first angle point */
978 length
= FT_MulFix( radius
,
979 FT_DivFix( 0x10000L
- sigma
,
980 ft_pos_abs( FT_Sin( theta
) ) ) );
982 FT_Vector_From_Polar( &delta
, length
, phi
+ rotate
);
986 error
= ft_stroke_border_lineto( border
, &delta
, 0 );
990 /* compute second angle point */
991 FT_Vector_From_Polar( &delta
, length
, phi
- rotate
);
995 error
= ft_stroke_border_lineto( border
, &delta
, 0 );
999 /* finally, add a movable end point */
1000 FT_Vector_From_Polar( &delta
, radius
, stroker
->angle_out
+ rotate
);
1001 delta
.x
+= stroker
->center
.x
;
1002 delta
.y
+= stroker
->center
.y
;
1004 error
= ft_stroke_border_lineto( border
, &delta
, 1 );
1007 else /* this is a bevel (intersection) */
1013 length
= FT_DivFix( stroker
->radius
, thcos
);
1015 FT_Vector_From_Polar( &delta
, length
, phi
);
1016 delta
.x
+= stroker
->center
.x
;
1017 delta
.y
+= stroker
->center
.y
;
1019 error
= ft_stroke_border_lineto( border
, &delta
, 0 );
1020 if (error
) goto Exit
;
1022 /* now add end point */
1023 FT_Vector_From_Polar( &delta
, stroker
->radius
,
1024 stroker
->angle_out
+ rotate
);
1025 delta
.x
+= stroker
->center
.x
;
1026 delta
.y
+= stroker
->center
.y
;
1028 error
= ft_stroke_border_lineto( border
, &delta
, 1 );
1038 ft_stroker_process_corner( FT_Stroker stroker
)
1045 turn
= FT_Angle_Diff( stroker
->angle_in
, stroker
->angle_out
);
1047 /* no specific corner processing is required if the turn is 0 */
1051 /* when we turn to the right, the inside side is 0 */
1054 /* otherwise, the inside side is 1 */
1058 /* process the inside side */
1059 error
= ft_stroker_inside( stroker
, inside_side
);
1063 /* process the outside side */
1064 error
= ft_stroker_outside( stroker
, 1 - inside_side
);
1071 /* add two points to the left and right borders corresponding to the */
1072 /* start of the subpath.. */
1074 ft_stroker_subpath_start( FT_Stroker stroker
,
1075 FT_Angle start_angle
)
1080 FT_StrokeBorder border
;
1083 FT_Vector_From_Polar( &delta
, stroker
->radius
,
1084 start_angle
+ FT_ANGLE_PI2
);
1086 point
.x
= stroker
->center
.x
+ delta
.x
;
1087 point
.y
= stroker
->center
.y
+ delta
.y
;
1089 border
= stroker
->borders
;
1090 error
= ft_stroke_border_moveto( border
, &point
);
1094 point
.x
= stroker
->center
.x
- delta
.x
;
1095 point
.y
= stroker
->center
.y
- delta
.y
;
1098 error
= ft_stroke_border_moveto( border
, &point
);
1100 /* save angle for last cap */
1101 stroker
->subpath_angle
= start_angle
;
1102 stroker
->first_point
= 0;
1109 /* documentation is in ftstroke.h */
1111 FT_EXPORT_DEF( FT_Error
)
1112 FT_Stroker_LineTo( FT_Stroker stroker
,
1116 FT_StrokeBorder border
;
1121 delta
.x
= to
->x
- stroker
->center
.x
;
1122 delta
.y
= to
->y
- stroker
->center
.y
;
1124 angle
= FT_Atan2( delta
.x
, delta
.y
);
1125 FT_Vector_From_Polar( &delta
, stroker
->radius
, angle
+ FT_ANGLE_PI2
);
1127 /* process corner if necessary */
1128 if ( stroker
->first_point
)
1130 /* This is the first segment of a subpath. We need to */
1131 /* add a point to each border at their respective starting */
1132 /* point locations. */
1133 error
= ft_stroker_subpath_start( stroker
, angle
);
1139 /* process the current corner */
1140 stroker
->angle_out
= angle
;
1141 error
= ft_stroker_process_corner( stroker
);
1146 /* now add a line segment to both the "inside" and "outside" paths */
1148 for ( border
= stroker
->borders
, side
= 1; side
>= 0; side
--, border
++ )
1153 point
.x
= to
->x
+ delta
.x
;
1154 point
.y
= to
->y
+ delta
.y
;
1156 error
= ft_stroke_border_lineto( border
, &point
, 1 );
1164 stroker
->angle_in
= angle
;
1165 stroker
->center
= *to
;
1172 /* documentation is in ftstroke.h */
1174 FT_EXPORT_DEF( FT_Error
)
1175 FT_Stroker_ConicTo( FT_Stroker stroker
,
1180 FT_Vector bez_stack
[34];
1182 FT_Vector
* limit
= bez_stack
+ 30;
1183 FT_Angle start_angle
;
1184 FT_Bool first_arc
= 1;
1190 arc
[2] = stroker
->center
;
1192 while ( arc
>= bez_stack
)
1194 FT_Angle angle_in
, angle_out
;
1197 angle_in
= angle_out
= 0; /* remove compiler warnings */
1200 !ft_conic_is_small_enough( arc
, &angle_in
, &angle_out
) )
1202 ft_conic_split( arc
);
1211 start_angle
= angle_in
;
1213 /* process corner if necessary */
1214 if ( stroker
->first_point
)
1215 error
= ft_stroker_subpath_start( stroker
, start_angle
);
1218 stroker
->angle_out
= start_angle
;
1219 error
= ft_stroker_process_corner( stroker
);
1223 /* the arc's angle is small enough; we can add it directly to each */
1226 FT_Vector ctrl
, end
;
1227 FT_Angle theta
, phi
, rotate
;
1232 theta
= FT_Angle_Diff( angle_in
, angle_out
) / 2;
1233 phi
= angle_in
+ theta
;
1234 length
= FT_DivFix( stroker
->radius
, FT_Cos( theta
) );
1236 for ( side
= 0; side
<= 1; side
++ )
1238 rotate
= FT_SIDE_TO_ROTATE( side
);
1240 /* compute control point */
1241 FT_Vector_From_Polar( &ctrl
, length
, phi
+ rotate
);
1245 /* compute end point */
1246 FT_Vector_From_Polar( &end
, stroker
->radius
, angle_out
+ rotate
);
1250 error
= ft_stroke_border_conicto( stroker
->borders
+ side
,
1259 if ( arc
< bez_stack
)
1260 stroker
->angle_in
= angle_out
;
1263 stroker
->center
= *to
;
1270 /* documentation is in ftstroke.h */
1272 FT_EXPORT_DEF( FT_Error
)
1273 FT_Stroker_CubicTo( FT_Stroker stroker
,
1274 FT_Vector
* control1
,
1275 FT_Vector
* control2
,
1279 FT_Vector bez_stack
[37];
1281 FT_Vector
* limit
= bez_stack
+ 32;
1282 FT_Angle start_angle
;
1283 FT_Bool first_arc
= 1;
1290 arc
[3] = stroker
->center
;
1292 while ( arc
>= bez_stack
)
1294 FT_Angle angle_in
, angle_mid
, angle_out
;
1297 /* remove compiler warnings */
1298 angle_in
= angle_out
= angle_mid
= 0;
1301 !ft_cubic_is_small_enough( arc
, &angle_in
,
1302 &angle_mid
, &angle_out
) )
1304 ft_cubic_split( arc
);
1313 /* process corner if necessary */
1314 start_angle
= angle_in
;
1316 if ( stroker
->first_point
)
1317 error
= ft_stroker_subpath_start( stroker
, start_angle
);
1320 stroker
->angle_out
= start_angle
;
1321 error
= ft_stroker_process_corner( stroker
);
1327 /* the arc's angle is small enough; we can add it directly to each */
1330 FT_Vector ctrl1
, ctrl2
, end
;
1331 FT_Angle theta1
, phi1
, theta2
, phi2
, rotate
;
1332 FT_Fixed length1
, length2
;
1336 theta1
= ft_pos_abs( angle_mid
- angle_in
) / 2;
1337 theta2
= ft_pos_abs( angle_out
- angle_mid
) / 2;
1338 phi1
= (angle_mid
+ angle_in
) / 2;
1339 phi2
= (angle_mid
+ angle_out
) / 2;
1340 length1
= FT_DivFix( stroker
->radius
, FT_Cos( theta1
) );
1341 length2
= FT_DivFix( stroker
->radius
, FT_Cos(theta2
) );
1343 for ( side
= 0; side
<= 1; side
++ )
1345 rotate
= FT_SIDE_TO_ROTATE( side
);
1347 /* compute control points */
1348 FT_Vector_From_Polar( &ctrl1
, length1
, phi1
+ rotate
);
1349 ctrl1
.x
+= arc
[2].x
;
1350 ctrl1
.y
+= arc
[2].y
;
1352 FT_Vector_From_Polar( &ctrl2
, length2
, phi2
+ rotate
);
1353 ctrl2
.x
+= arc
[1].x
;
1354 ctrl2
.y
+= arc
[1].y
;
1356 /* compute end point */
1357 FT_Vector_From_Polar( &end
, stroker
->radius
, angle_out
+ rotate
);
1361 error
= ft_stroke_border_cubicto( stroker
->borders
+ side
,
1362 &ctrl1
, &ctrl2
, &end
);
1369 if ( arc
< bez_stack
)
1370 stroker
->angle_in
= angle_out
;
1373 stroker
->center
= *to
;
1380 /* documentation is in ftstroke.h */
1382 FT_EXPORT_DEF( FT_Error
)
1383 FT_Stroker_BeginSubPath( FT_Stroker stroker
,
1387 /* We cannot process the first point, because there is not enough */
1388 /* information regarding its corner/cap. The latter will be processed */
1389 /* in the "end_subpath" routine. */
1391 stroker
->first_point
= 1;
1392 stroker
->center
= *to
;
1393 stroker
->subpath_open
= open
;
1395 /* record the subpath start point index for each border */
1396 stroker
->subpath_start
= *to
;
1402 ft_stroker_add_reverse_left( FT_Stroker stroker
,
1405 FT_StrokeBorder right
= stroker
->borders
+ 0;
1406 FT_StrokeBorder left
= stroker
->borders
+ 1;
1411 FT_ASSERT( left
->start
>= 0 );
1413 new_points
= left
->num_points
- left
->start
;
1414 if ( new_points
> 0 )
1416 error
= ft_stroke_border_grow( right
, (FT_UInt
)new_points
);
1421 FT_Vector
* dst_point
= right
->points
+ right
->num_points
;
1422 FT_Byte
* dst_tag
= right
->tags
+ right
->num_points
;
1423 FT_Vector
* src_point
= left
->points
+ left
->num_points
- 1;
1424 FT_Byte
* src_tag
= left
->tags
+ left
->num_points
- 1;
1426 while ( src_point
>= left
->points
+ left
->start
)
1428 *dst_point
= *src_point
;
1429 *dst_tag
= *src_tag
;
1432 dst_tag
[0] &= ~FT_STROKE_TAG_BEGIN_END
;
1435 FT_Byte ttag
= (FT_Byte
)( dst_tag
[0] & FT_STROKE_TAG_BEGIN_END
);
1438 /* switch begin/end tags if necessary */
1439 if ( ttag
== FT_STROKE_TAG_BEGIN
||
1440 ttag
== FT_STROKE_TAG_END
)
1441 dst_tag
[0] ^= FT_STROKE_TAG_BEGIN_END
;
1452 left
->num_points
= left
->start
;
1453 right
->num_points
+= new_points
;
1464 /* documentation is in ftstroke.h */
1466 /* there's a lot of magic in this function! */
1467 FT_EXPORT_DEF( FT_Error
)
1468 FT_Stroker_EndSubPath( FT_Stroker stroker
)
1472 if ( stroker
->subpath_open
)
1474 FT_StrokeBorder right
= stroker
->borders
;
1476 /* All right, this is an opened path, we need to add a cap between */
1477 /* right & left, add the reverse of left, then add a final cap */
1478 /* between left & right. */
1479 error
= ft_stroker_cap( stroker
, stroker
->angle_in
, 0 );
1483 /* add reversed points from "left" to "right" */
1484 error
= ft_stroker_add_reverse_left( stroker
, 1 );
1488 /* now add the final cap */
1489 stroker
->center
= stroker
->subpath_start
;
1490 error
= ft_stroker_cap( stroker
,
1491 stroker
->subpath_angle
+ FT_ANGLE_PI
, 0 );
1495 /* Now end the right subpath accordingly. The left one is */
1496 /* rewind and doesn't need further processing. */
1497 ft_stroke_border_close( right
, 0 );
1504 /* close the path if needed */
1505 if ( stroker
->center
.x
!= stroker
->subpath_start
.x
||
1506 stroker
->center
.y
!= stroker
->subpath_start
.y
)
1508 error
= FT_Stroker_LineTo( stroker
, &stroker
->subpath_start
);
1513 /* process the corner */
1514 stroker
->angle_out
= stroker
->subpath_angle
;
1515 turn
= FT_Angle_Diff( stroker
->angle_in
,
1516 stroker
->angle_out
);
1518 /* no specific corner processing is required if the turn is 0 */
1521 /* when we turn to the right, the inside side is 0 */
1524 /* otherwise, the inside side is 1 */
1528 error
= ft_stroker_inside( stroker
, inside_side
);
1532 /* process the outside side */
1533 error
= ft_stroker_outside( stroker
, 1 - inside_side
);
1538 /* then end our two subpaths */
1539 ft_stroke_border_close( stroker
->borders
+ 0, 1 );
1540 ft_stroke_border_close( stroker
->borders
+ 1, 0 );
1548 /* documentation is in ftstroke.h */
1550 FT_EXPORT_DEF( FT_Error
)
1551 FT_Stroker_GetBorderCounts( FT_Stroker stroker
,
1552 FT_StrokerBorder border
,
1553 FT_UInt
*anum_points
,
1554 FT_UInt
*anum_contours
)
1556 FT_UInt num_points
= 0, num_contours
= 0;
1560 if ( !stroker
|| border
> 1 )
1562 error
= FT_Err_Invalid_Argument
;
1566 error
= ft_stroke_border_get_counts( stroker
->borders
+ border
,
1567 &num_points
, &num_contours
);
1570 *anum_points
= num_points
;
1572 if ( anum_contours
)
1573 *anum_contours
= num_contours
;
1579 /* documentation is in ftstroke.h */
1581 FT_EXPORT_DEF( FT_Error
)
1582 FT_Stroker_GetCounts( FT_Stroker stroker
,
1583 FT_UInt
*anum_points
,
1584 FT_UInt
*anum_contours
)
1586 FT_UInt count1
, count2
, num_points
= 0;
1587 FT_UInt count3
, count4
, num_contours
= 0;
1591 error
= ft_stroke_border_get_counts( stroker
->borders
+ 0,
1596 error
= ft_stroke_border_get_counts( stroker
->borders
+ 1,
1601 num_points
= count1
+ count3
;
1602 num_contours
= count2
+ count4
;
1605 *anum_points
= num_points
;
1606 *anum_contours
= num_contours
;
1611 /* documentation is in ftstroke.h */
1613 FT_EXPORT_DEF( void )
1614 FT_Stroker_ExportBorder( FT_Stroker stroker
,
1615 FT_StrokerBorder border
,
1616 FT_Outline
* outline
)
1618 if ( border
== FT_STROKER_BORDER_LEFT
||
1619 border
== FT_STROKER_BORDER_RIGHT
)
1621 FT_StrokeBorder sborder
= & stroker
->borders
[border
];
1624 if ( sborder
->valid
)
1625 ft_stroke_border_export( sborder
, outline
);
1630 /* documentation is in ftstroke.h */
1632 FT_EXPORT_DEF( void )
1633 FT_Stroker_Export( FT_Stroker stroker
,
1634 FT_Outline
* outline
)
1636 FT_Stroker_ExportBorder( stroker
, FT_STROKER_BORDER_LEFT
, outline
);
1637 FT_Stroker_ExportBorder( stroker
, FT_STROKER_BORDER_RIGHT
, outline
);
1641 /* documentation is in ftstroke.h */
1644 * The following is very similar to FT_Outline_Decompose, except
1645 * that we do support opened paths, and do not scale the outline.
1647 FT_EXPORT_DEF( FT_Error
)
1648 FT_Stroker_ParseOutline( FT_Stroker stroker
,
1649 FT_Outline
* outline
,
1653 FT_Vector v_control
;
1662 FT_Int n
; /* index of contour in outline */
1663 FT_UInt first
; /* index of first point in contour */
1664 FT_Int tag
; /* current point's state */
1667 if ( !outline
|| !stroker
)
1668 return FT_Err_Invalid_Argument
;
1670 FT_Stroker_Rewind( stroker
);
1674 for ( n
= 0; n
< outline
->n_contours
; n
++ )
1676 FT_UInt last
; /* index of last point in contour */
1679 last
= outline
->contours
[n
];
1680 limit
= outline
->points
+ last
;
1682 /* skip empty points; we don't stroke these */
1683 if ( last
<= first
)
1689 v_start
= outline
->points
[first
];
1690 v_last
= outline
->points
[last
];
1692 v_control
= v_start
;
1694 point
= outline
->points
+ first
;
1695 tags
= outline
->tags
+ first
;
1696 tag
= FT_CURVE_TAG( tags
[0] );
1698 /* A contour cannot start with a cubic control point! */
1699 if ( tag
== FT_CURVE_TAG_CUBIC
)
1700 goto Invalid_Outline
;
1702 /* check first point to determine origin */
1703 if ( tag
== FT_CURVE_TAG_CONIC
)
1705 /* First point is conic control. Yes, this happens. */
1706 if ( FT_CURVE_TAG( outline
->tags
[last
] ) == FT_CURVE_TAG_ON
)
1708 /* start at last point if it is on the curve */
1714 /* if both first and last points are conic, */
1715 /* start at their middle and record its position */
1717 v_start
.x
= ( v_start
.x
+ v_last
.x
) / 2;
1718 v_start
.y
= ( v_start
.y
+ v_last
.y
) / 2;
1726 error
= FT_Stroker_BeginSubPath( stroker
, &v_start
, opened
);
1730 while ( point
< limit
)
1735 tag
= FT_CURVE_TAG( tags
[0] );
1738 case FT_CURVE_TAG_ON
: /* emit a single line_to */
1746 error
= FT_Stroker_LineTo( stroker
, &vec
);
1752 case FT_CURVE_TAG_CONIC
: /* consume conic arcs */
1753 v_control
.x
= point
->x
;
1754 v_control
.y
= point
->y
;
1757 if ( point
< limit
)
1765 tag
= FT_CURVE_TAG( tags
[0] );
1769 if ( tag
== FT_CURVE_TAG_ON
)
1771 error
= FT_Stroker_ConicTo( stroker
, &v_control
, &vec
);
1777 if ( tag
!= FT_CURVE_TAG_CONIC
)
1778 goto Invalid_Outline
;
1780 v_middle
.x
= ( v_control
.x
+ vec
.x
) / 2;
1781 v_middle
.y
= ( v_control
.y
+ vec
.y
) / 2;
1783 error
= FT_Stroker_ConicTo( stroker
, &v_control
, &v_middle
);
1791 error
= FT_Stroker_ConicTo( stroker
, &v_control
, &v_start
);
1794 default: /* FT_CURVE_TAG_CUBIC */
1796 FT_Vector vec1
, vec2
;
1799 if ( point
+ 1 > limit
||
1800 FT_CURVE_TAG( tags
[1] ) != FT_CURVE_TAG_CUBIC
)
1801 goto Invalid_Outline
;
1809 if ( point
<= limit
)
1816 error
= FT_Stroker_CubicTo( stroker
, &vec1
, &vec2
, &vec
);
1822 error
= FT_Stroker_CubicTo( stroker
, &vec1
, &vec2
, &v_start
);
1832 error
= FT_Stroker_EndSubPath( stroker
);
1845 return FT_Err_Invalid_Outline
;
1849 extern const FT_Glyph_Class ft_outline_glyph_class
;
1852 /* documentation is in ftstroke.h */
1854 FT_EXPORT_DEF( FT_Error
)
1855 FT_Glyph_Stroke( FT_Glyph
*pglyph
,
1859 FT_Error error
= FT_Err_Invalid_Argument
;
1860 FT_Glyph glyph
= NULL
;
1863 if ( pglyph
== NULL
)
1867 if ( glyph
== NULL
|| glyph
->clazz
!= &ft_outline_glyph_class
)
1874 error
= FT_Glyph_Copy( glyph
, ©
);
1882 FT_OutlineGlyph oglyph
= (FT_OutlineGlyph
) glyph
;
1883 FT_Outline
* outline
= &oglyph
->outline
;
1884 FT_UInt num_points
, num_contours
;
1887 error
= FT_Stroker_ParseOutline( stroker
, outline
, 0 );
1891 (void)FT_Stroker_GetCounts( stroker
, &num_points
, &num_contours
);
1893 FT_Outline_Done( glyph
->library
, outline
);
1895 error
= FT_Outline_New( glyph
->library
,
1896 num_points
, num_contours
, outline
);
1900 outline
->n_points
= 0;
1901 outline
->n_contours
= 0;
1903 FT_Stroker_Export( stroker
, outline
);
1907 FT_Done_Glyph( *pglyph
);
1913 FT_Done_Glyph( glyph
);
1924 /* documentation is in ftstroke.h */
1926 FT_EXPORT_DEF( FT_Error
)
1927 FT_Glyph_StrokeBorder( FT_Glyph
*pglyph
,
1932 FT_Error error
= FT_Err_Invalid_Argument
;
1933 FT_Glyph glyph
= NULL
;
1936 if ( pglyph
== NULL
)
1940 if ( glyph
== NULL
|| glyph
->clazz
!= &ft_outline_glyph_class
)
1947 error
= FT_Glyph_Copy( glyph
, ©
);
1955 FT_OutlineGlyph oglyph
= (FT_OutlineGlyph
) glyph
;
1956 FT_StrokerBorder border
;
1957 FT_Outline
* outline
= &oglyph
->outline
;
1958 FT_UInt num_points
, num_contours
;
1961 border
= FT_Outline_GetOutsideBorder( outline
);
1964 if ( border
== FT_STROKER_BORDER_LEFT
)
1965 border
= FT_STROKER_BORDER_RIGHT
;
1967 border
= FT_STROKER_BORDER_LEFT
;
1970 error
= FT_Stroker_ParseOutline( stroker
, outline
, 0 );
1974 (void)FT_Stroker_GetBorderCounts( stroker
, border
,
1975 &num_points
, &num_contours
);
1977 FT_Outline_Done( glyph
->library
, outline
);
1979 error
= FT_Outline_New( glyph
->library
,
1986 outline
->n_points
= 0;
1987 outline
->n_contours
= 0;
1989 FT_Stroker_ExportBorder( stroker
, border
, outline
);
1993 FT_Done_Glyph( *pglyph
);
1999 FT_Done_Glyph( glyph
);