1 /***************************************************************************/
5 /* FreeType path stroker (body). */
7 /* Copyright 2002, 2003, 2004, 2005, 2006, 2008, 2009, 2010 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 /***************************************************************************/
233 typedef enum FT_StrokeTags_
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
;
264 FT_Error error
= FT_Err_Ok
;
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
;
289 ft_stroke_border_close( FT_StrokeBorder border
,
292 FT_UInt start
= border
->start
;
293 FT_UInt count
= border
->num_points
;
296 FT_ASSERT( border
->start
>= 0 );
298 /* don't record empty paths! */
299 if ( count
<= start
+ 1U )
300 border
->num_points
= start
;
303 /* copy the last point to the start of this sub-path, since */
304 /* it contains the `adjusted' starting coordinates */
305 border
->num_points
= --count
;
306 border
->points
[start
] = border
->points
[count
];
310 /* reverse the points */
312 FT_Vector
* vec1
= border
->points
+ start
+ 1;
313 FT_Vector
* vec2
= border
->points
+ count
- 1;
316 for ( ; vec1
< vec2
; vec1
++, vec2
-- )
329 FT_Byte
* tag1
= border
->tags
+ start
+ 1;
330 FT_Byte
* tag2
= border
->tags
+ count
- 1;
333 for ( ; tag1
< tag2
; tag1
++, tag2
-- )
345 border
->tags
[start
] |= FT_STROKE_TAG_BEGIN
;
346 border
->tags
[count
- 1] |= FT_STROKE_TAG_END
;
350 border
->movable
= FALSE
;
355 ft_stroke_border_lineto( FT_StrokeBorder border
,
359 FT_Error error
= FT_Err_Ok
;
362 FT_ASSERT( border
->start
>= 0 );
364 if ( border
->movable
)
366 /* move last point */
367 border
->points
[border
->num_points
- 1] = *to
;
372 error
= ft_stroke_border_grow( border
, 1 );
375 FT_Vector
* vec
= border
->points
+ border
->num_points
;
376 FT_Byte
* tag
= border
->tags
+ border
->num_points
;
380 tag
[0] = FT_STROKE_TAG_ON
;
382 border
->num_points
+= 1;
385 border
->movable
= movable
;
391 ft_stroke_border_conicto( FT_StrokeBorder border
,
398 FT_ASSERT( border
->start
>= 0 );
400 error
= ft_stroke_border_grow( border
, 2 );
403 FT_Vector
* vec
= border
->points
+ border
->num_points
;
404 FT_Byte
* tag
= border
->tags
+ border
->num_points
;
410 tag
[1] = FT_STROKE_TAG_ON
;
412 border
->num_points
+= 2;
414 border
->movable
= FALSE
;
420 ft_stroke_border_cubicto( FT_StrokeBorder border
,
428 FT_ASSERT( border
->start
>= 0 );
430 error
= ft_stroke_border_grow( border
, 3 );
433 FT_Vector
* vec
= border
->points
+ border
->num_points
;
434 FT_Byte
* tag
= border
->tags
+ border
->num_points
;
441 tag
[0] = FT_STROKE_TAG_CUBIC
;
442 tag
[1] = FT_STROKE_TAG_CUBIC
;
443 tag
[2] = FT_STROKE_TAG_ON
;
445 border
->num_points
+= 3;
447 border
->movable
= FALSE
;
452 #define FT_ARC_CUBIC_ANGLE ( FT_ANGLE_PI / 2 )
456 ft_stroke_border_arcto( FT_StrokeBorder border
,
459 FT_Angle angle_start
,
460 FT_Angle angle_diff
)
462 FT_Angle total
, angle
, step
, rotate
, next
, theta
;
463 FT_Vector a
, b
, a2
, b2
;
465 FT_Error error
= FT_Err_Ok
;
468 /* compute start point */
469 FT_Vector_From_Polar( &a
, radius
, angle_start
);
475 rotate
= ( angle_diff
>= 0 ) ? FT_ANGLE_PI2
: -FT_ANGLE_PI2
;
480 if ( step
> FT_ARC_CUBIC_ANGLE
)
481 step
= FT_ARC_CUBIC_ANGLE
;
483 else if ( step
< -FT_ARC_CUBIC_ANGLE
)
484 step
= -FT_ARC_CUBIC_ANGLE
;
493 /* compute end point */
494 FT_Vector_From_Polar( &b
, radius
, next
);
498 /* compute first and second control points */
499 length
= FT_MulDiv( radius
, FT_Sin( theta
) * 4,
500 ( 0x10000L
+ FT_Cos( theta
) ) * 3 );
502 FT_Vector_From_Polar( &a2
, length
, angle
+ rotate
);
506 FT_Vector_From_Polar( &b2
, length
, next
- rotate
);
511 error
= ft_stroke_border_cubicto( border
, &a2
, &b2
, &b
);
515 /* process the rest of the arc ?? */
526 ft_stroke_border_moveto( FT_StrokeBorder border
,
529 /* close current open path if any ? */
530 if ( border
->start
>= 0 )
531 ft_stroke_border_close( border
, FALSE
);
533 border
->start
= border
->num_points
;
534 border
->movable
= FALSE
;
536 return ft_stroke_border_lineto( border
, to
, FALSE
);
541 ft_stroke_border_init( FT_StrokeBorder border
,
544 border
->memory
= memory
;
545 border
->points
= NULL
;
548 border
->num_points
= 0;
549 border
->max_points
= 0;
551 border
->valid
= FALSE
;
556 ft_stroke_border_reset( FT_StrokeBorder border
)
558 border
->num_points
= 0;
560 border
->valid
= FALSE
;
565 ft_stroke_border_done( FT_StrokeBorder border
)
567 FT_Memory memory
= border
->memory
;
570 FT_FREE( border
->points
);
571 FT_FREE( border
->tags
);
573 border
->num_points
= 0;
574 border
->max_points
= 0;
576 border
->valid
= FALSE
;
581 ft_stroke_border_get_counts( FT_StrokeBorder border
,
582 FT_UInt
*anum_points
,
583 FT_UInt
*anum_contours
)
585 FT_Error error
= FT_Err_Ok
;
586 FT_UInt num_points
= 0;
587 FT_UInt num_contours
= 0;
589 FT_UInt count
= border
->num_points
;
590 FT_Vector
* point
= border
->points
;
591 FT_Byte
* tags
= border
->tags
;
592 FT_Int in_contour
= 0;
595 for ( ; count
> 0; count
--, num_points
++, point
++, tags
++ )
597 if ( tags
[0] & FT_STROKE_TAG_BEGIN
)
599 if ( in_contour
!= 0 )
604 else if ( in_contour
== 0 )
607 if ( tags
[0] & FT_STROKE_TAG_END
)
614 if ( in_contour
!= 0 )
617 border
->valid
= TRUE
;
620 *anum_points
= num_points
;
621 *anum_contours
= num_contours
;
632 ft_stroke_border_export( FT_StrokeBorder border
,
633 FT_Outline
* outline
)
635 /* copy point locations */
636 FT_ARRAY_COPY( outline
->points
+ outline
->n_points
,
638 border
->num_points
);
642 FT_UInt count
= border
->num_points
;
643 FT_Byte
* read
= border
->tags
;
644 FT_Byte
* write
= (FT_Byte
*)outline
->tags
+ outline
->n_points
;
647 for ( ; count
> 0; count
--, read
++, write
++ )
649 if ( *read
& FT_STROKE_TAG_ON
)
650 *write
= FT_CURVE_TAG_ON
;
651 else if ( *read
& FT_STROKE_TAG_CUBIC
)
652 *write
= FT_CURVE_TAG_CUBIC
;
654 *write
= FT_CURVE_TAG_CONIC
;
660 FT_UInt count
= border
->num_points
;
661 FT_Byte
* tags
= border
->tags
;
662 FT_Short
* write
= outline
->contours
+ outline
->n_contours
;
663 FT_Short idx
= (FT_Short
)outline
->n_points
;
666 for ( ; count
> 0; count
--, tags
++, idx
++ )
668 if ( *tags
& FT_STROKE_TAG_END
)
671 outline
->n_contours
++;
676 outline
->n_points
= (short)( outline
->n_points
+ border
->num_points
);
678 FT_ASSERT( FT_Outline_Check( outline
) == 0 );
682 /***************************************************************************/
683 /***************************************************************************/
685 /***** STROKER *****/
687 /***************************************************************************/
688 /***************************************************************************/
690 #define FT_SIDE_TO_ROTATE( s ) ( FT_ANGLE_PI2 - (s) * FT_ANGLE_PI )
692 typedef struct FT_StrokerRec_
698 FT_Bool subpath_open
;
699 FT_Angle subpath_angle
;
700 FT_Vector subpath_start
;
702 FT_Stroker_LineCap line_cap
;
703 FT_Stroker_LineJoin line_join
;
704 FT_Fixed miter_limit
;
708 FT_StrokeBorderRec borders
[2];
714 /* documentation is in ftstroke.h */
716 FT_EXPORT_DEF( FT_Error
)
717 FT_Stroker_New( FT_Library library
,
718 FT_Stroker
*astroker
)
726 return FT_Err_Invalid_Argument
;
728 memory
= library
->memory
;
730 if ( !FT_NEW( stroker
) )
732 stroker
->library
= library
;
734 ft_stroke_border_init( &stroker
->borders
[0], memory
);
735 ft_stroke_border_init( &stroker
->borders
[1], memory
);
742 /* documentation is in ftstroke.h */
744 FT_EXPORT_DEF( void )
745 FT_Stroker_Set( FT_Stroker stroker
,
747 FT_Stroker_LineCap line_cap
,
748 FT_Stroker_LineJoin line_join
,
749 FT_Fixed miter_limit
)
751 stroker
->radius
= radius
;
752 stroker
->line_cap
= line_cap
;
753 stroker
->line_join
= line_join
;
754 stroker
->miter_limit
= miter_limit
;
756 FT_Stroker_Rewind( stroker
);
760 /* documentation is in ftstroke.h */
762 FT_EXPORT_DEF( void )
763 FT_Stroker_Rewind( FT_Stroker stroker
)
767 ft_stroke_border_reset( &stroker
->borders
[0] );
768 ft_stroke_border_reset( &stroker
->borders
[1] );
773 /* documentation is in ftstroke.h */
775 FT_EXPORT_DEF( void )
776 FT_Stroker_Done( FT_Stroker stroker
)
780 FT_Memory memory
= stroker
->library
->memory
;
783 ft_stroke_border_done( &stroker
->borders
[0] );
784 ft_stroke_border_done( &stroker
->borders
[1] );
786 stroker
->library
= NULL
;
792 /* creates a circular arc at a corner or cap */
794 ft_stroker_arcto( FT_Stroker stroker
,
797 FT_Angle total
, rotate
;
798 FT_Fixed radius
= stroker
->radius
;
799 FT_Error error
= FT_Err_Ok
;
800 FT_StrokeBorder border
= stroker
->borders
+ side
;
803 rotate
= FT_SIDE_TO_ROTATE( side
);
805 total
= FT_Angle_Diff( stroker
->angle_in
, stroker
->angle_out
);
806 if ( total
== FT_ANGLE_PI
)
809 error
= ft_stroke_border_arcto( border
,
812 stroker
->angle_in
+ rotate
,
814 border
->movable
= FALSE
;
819 /* adds a cap at the end of an opened path */
821 ft_stroker_cap( FT_Stroker stroker
,
825 FT_Error error
= FT_Err_Ok
;
828 if ( stroker
->line_cap
== FT_STROKER_LINECAP_ROUND
)
830 /* add a round cap */
831 stroker
->angle_in
= angle
;
832 stroker
->angle_out
= angle
+ FT_ANGLE_PI
;
833 error
= ft_stroker_arcto( stroker
, side
);
835 else if ( stroker
->line_cap
== FT_STROKER_LINECAP_SQUARE
)
837 /* add a square cap */
838 FT_Vector delta
, delta2
;
839 FT_Angle rotate
= FT_SIDE_TO_ROTATE( side
);
840 FT_Fixed radius
= stroker
->radius
;
841 FT_StrokeBorder border
= stroker
->borders
+ side
;
844 FT_Vector_From_Polar( &delta2
, radius
, angle
+ rotate
);
845 FT_Vector_From_Polar( &delta
, radius
, angle
);
847 delta
.x
+= stroker
->center
.x
+ delta2
.x
;
848 delta
.y
+= stroker
->center
.y
+ delta2
.y
;
850 error
= ft_stroke_border_lineto( border
, &delta
, FALSE
);
854 FT_Vector_From_Polar( &delta2
, radius
, angle
- rotate
);
855 FT_Vector_From_Polar( &delta
, radius
, angle
);
857 delta
.x
+= delta2
.x
+ stroker
->center
.x
;
858 delta
.y
+= delta2
.y
+ stroker
->center
.y
;
860 error
= ft_stroke_border_lineto( border
, &delta
, FALSE
);
862 else if ( stroker
->line_cap
== FT_STROKER_LINECAP_BUTT
)
864 /* add a butt ending */
866 FT_Angle rotate
= FT_SIDE_TO_ROTATE( side
);
867 FT_Fixed radius
= stroker
->radius
;
868 FT_StrokeBorder border
= stroker
->borders
+ side
;
871 FT_Vector_From_Polar( &delta
, radius
, angle
+ rotate
);
873 delta
.x
+= stroker
->center
.x
;
874 delta
.y
+= stroker
->center
.y
;
876 error
= ft_stroke_border_lineto( border
, &delta
, FALSE
);
880 FT_Vector_From_Polar( &delta
, radius
, angle
- rotate
);
882 delta
.x
+= stroker
->center
.x
;
883 delta
.y
+= stroker
->center
.y
;
885 error
= ft_stroke_border_lineto( border
, &delta
, FALSE
);
893 /* process an inside corner, i.e. compute intersection */
895 ft_stroker_inside( FT_Stroker stroker
,
898 FT_StrokeBorder border
= stroker
->borders
+ side
;
899 FT_Angle phi
, theta
, rotate
;
900 FT_Fixed length
, thcos
, sigma
;
902 FT_Error error
= FT_Err_Ok
;
905 rotate
= FT_SIDE_TO_ROTATE( side
);
907 /* compute median angle */
908 theta
= FT_Angle_Diff( stroker
->angle_in
, stroker
->angle_out
);
909 if ( theta
== FT_ANGLE_PI
)
914 phi
= stroker
->angle_in
+ theta
;
916 thcos
= FT_Cos( theta
);
917 sigma
= FT_MulFix( stroker
->miter_limit
, thcos
);
919 /* TODO: find better criterion to switch off the optimization */
920 if ( sigma
< 0x10000L
)
922 FT_Vector_From_Polar( &delta
, stroker
->radius
,
923 stroker
->angle_out
+ rotate
);
924 delta
.x
+= stroker
->center
.x
;
925 delta
.y
+= stroker
->center
.y
;
926 border
->movable
= FALSE
;
930 length
= FT_DivFix( stroker
->radius
, thcos
);
932 FT_Vector_From_Polar( &delta
, length
, phi
+ rotate
);
933 delta
.x
+= stroker
->center
.x
;
934 delta
.y
+= stroker
->center
.y
;
937 error
= ft_stroke_border_lineto( border
, &delta
, FALSE
);
943 /* process an outside corner, i.e. compute bevel/miter/round */
945 ft_stroker_outside( FT_Stroker stroker
,
948 FT_StrokeBorder border
= stroker
->borders
+ side
;
953 if ( stroker
->line_join
== FT_STROKER_LINEJOIN_ROUND
)
954 error
= ft_stroker_arcto( stroker
, side
);
957 /* this is a mitered or beveled corner */
958 FT_Fixed sigma
, radius
= stroker
->radius
;
964 rotate
= FT_SIDE_TO_ROTATE( side
);
965 miter
= FT_BOOL( stroker
->line_join
== FT_STROKER_LINEJOIN_MITER
);
967 theta
= FT_Angle_Diff( stroker
->angle_in
, stroker
->angle_out
);
968 if ( theta
== FT_ANGLE_PI
)
971 phi
= stroker
->angle_in
;
976 phi
= stroker
->angle_in
+ theta
+ rotate
;
979 thcos
= FT_Cos( theta
);
980 sigma
= FT_MulFix( stroker
->miter_limit
, thcos
);
982 /* FT_Sin(x) = 0 for x <= 57 */
983 if ( sigma
>= 0x10000L
|| ft_pos_abs( theta
) <= 57 )
986 if ( miter
) /* this is a miter (broken angle) */
988 FT_Vector middle
, delta
;
992 /* compute middle point */
993 FT_Vector_From_Polar( &middle
,
994 FT_MulFix( radius
, stroker
->miter_limit
),
996 middle
.x
+= stroker
->center
.x
;
997 middle
.y
+= stroker
->center
.y
;
999 /* compute first angle point */
1000 length
= FT_MulFix( radius
,
1001 FT_DivFix( 0x10000L
- sigma
,
1002 ft_pos_abs( FT_Sin( theta
) ) ) );
1004 FT_Vector_From_Polar( &delta
, length
, phi
+ rotate
);
1005 delta
.x
+= middle
.x
;
1006 delta
.y
+= middle
.y
;
1008 error
= ft_stroke_border_lineto( border
, &delta
, FALSE
);
1012 /* compute second angle point */
1013 FT_Vector_From_Polar( &delta
, length
, phi
- rotate
);
1014 delta
.x
+= middle
.x
;
1015 delta
.y
+= middle
.y
;
1017 error
= ft_stroke_border_lineto( border
, &delta
, FALSE
);
1021 /* finally, add a movable end point */
1022 FT_Vector_From_Polar( &delta
, radius
, stroker
->angle_out
+ rotate
);
1023 delta
.x
+= stroker
->center
.x
;
1024 delta
.y
+= stroker
->center
.y
;
1026 error
= ft_stroke_border_lineto( border
, &delta
, TRUE
);
1029 else /* this is a bevel (intersection) */
1035 length
= FT_DivFix( stroker
->radius
, thcos
);
1037 FT_Vector_From_Polar( &delta
, length
, phi
);
1038 delta
.x
+= stroker
->center
.x
;
1039 delta
.y
+= stroker
->center
.y
;
1041 error
= ft_stroke_border_lineto( border
, &delta
, FALSE
);
1045 /* now add end point */
1046 FT_Vector_From_Polar( &delta
, stroker
->radius
,
1047 stroker
->angle_out
+ rotate
);
1048 delta
.x
+= stroker
->center
.x
;
1049 delta
.y
+= stroker
->center
.y
;
1051 error
= ft_stroke_border_lineto( border
, &delta
, TRUE
);
1061 ft_stroker_process_corner( FT_Stroker stroker
)
1063 FT_Error error
= FT_Err_Ok
;
1068 turn
= FT_Angle_Diff( stroker
->angle_in
, stroker
->angle_out
);
1070 /* no specific corner processing is required if the turn is 0 */
1074 /* when we turn to the right, the inside side is 0 */
1077 /* otherwise, the inside side is 1 */
1081 /* process the inside side */
1082 error
= ft_stroker_inside( stroker
, inside_side
);
1086 /* process the outside side */
1087 error
= ft_stroker_outside( stroker
, 1 - inside_side
);
1094 /* add two points to the left and right borders corresponding to the */
1095 /* start of the subpath */
1097 ft_stroker_subpath_start( FT_Stroker stroker
,
1098 FT_Angle start_angle
)
1103 FT_StrokeBorder border
;
1106 FT_Vector_From_Polar( &delta
, stroker
->radius
,
1107 start_angle
+ FT_ANGLE_PI2
);
1109 point
.x
= stroker
->center
.x
+ delta
.x
;
1110 point
.y
= stroker
->center
.y
+ delta
.y
;
1112 border
= stroker
->borders
;
1113 error
= ft_stroke_border_moveto( border
, &point
);
1117 point
.x
= stroker
->center
.x
- delta
.x
;
1118 point
.y
= stroker
->center
.y
- delta
.y
;
1121 error
= ft_stroke_border_moveto( border
, &point
);
1123 /* save angle for last cap */
1124 stroker
->subpath_angle
= start_angle
;
1125 stroker
->first_point
= FALSE
;
1132 /* documentation is in ftstroke.h */
1134 FT_EXPORT_DEF( FT_Error
)
1135 FT_Stroker_LineTo( FT_Stroker stroker
,
1138 FT_Error error
= FT_Err_Ok
;
1139 FT_StrokeBorder border
;
1144 delta
.x
= to
->x
- stroker
->center
.x
;
1145 delta
.y
= to
->y
- stroker
->center
.y
;
1147 angle
= FT_Atan2( delta
.x
, delta
.y
);
1148 FT_Vector_From_Polar( &delta
, stroker
->radius
, angle
+ FT_ANGLE_PI2
);
1150 /* process corner if necessary */
1151 if ( stroker
->first_point
)
1153 /* This is the first segment of a subpath. We need to */
1154 /* add a point to each border at their respective starting */
1155 /* point locations. */
1156 error
= ft_stroker_subpath_start( stroker
, angle
);
1162 /* process the current corner */
1163 stroker
->angle_out
= angle
;
1164 error
= ft_stroker_process_corner( stroker
);
1169 /* now add a line segment to both the `inside' and `outside' paths */
1171 for ( border
= stroker
->borders
, side
= 1; side
>= 0; side
--, border
++ )
1176 point
.x
= to
->x
+ delta
.x
;
1177 point
.y
= to
->y
+ delta
.y
;
1179 error
= ft_stroke_border_lineto( border
, &point
, TRUE
);
1187 stroker
->angle_in
= angle
;
1188 stroker
->center
= *to
;
1195 /* documentation is in ftstroke.h */
1197 FT_EXPORT_DEF( FT_Error
)
1198 FT_Stroker_ConicTo( FT_Stroker stroker
,
1202 FT_Error error
= FT_Err_Ok
;
1203 FT_Vector bez_stack
[34];
1205 FT_Vector
* limit
= bez_stack
+ 30;
1206 FT_Angle start_angle
;
1207 FT_Bool first_arc
= TRUE
;
1213 arc
[2] = stroker
->center
;
1215 while ( arc
>= bez_stack
)
1217 FT_Angle angle_in
, angle_out
;
1220 angle_in
= angle_out
= 0; /* remove compiler warnings */
1223 !ft_conic_is_small_enough( arc
, &angle_in
, &angle_out
) )
1225 ft_conic_split( arc
);
1234 start_angle
= angle_in
;
1236 /* process corner if necessary */
1237 if ( stroker
->first_point
)
1238 error
= ft_stroker_subpath_start( stroker
, start_angle
);
1241 stroker
->angle_out
= start_angle
;
1242 error
= ft_stroker_process_corner( stroker
);
1246 /* the arc's angle is small enough; we can add it directly to each */
1249 FT_Vector ctrl
, end
;
1250 FT_Angle theta
, phi
, rotate
;
1255 theta
= FT_Angle_Diff( angle_in
, angle_out
) / 2;
1256 phi
= angle_in
+ theta
;
1257 length
= FT_DivFix( stroker
->radius
, FT_Cos( theta
) );
1259 for ( side
= 0; side
<= 1; side
++ )
1261 rotate
= FT_SIDE_TO_ROTATE( side
);
1263 /* compute control point */
1264 FT_Vector_From_Polar( &ctrl
, length
, phi
+ rotate
);
1268 /* compute end point */
1269 FT_Vector_From_Polar( &end
, stroker
->radius
, angle_out
+ rotate
);
1273 error
= ft_stroke_border_conicto( stroker
->borders
+ side
,
1282 if ( arc
< bez_stack
)
1283 stroker
->angle_in
= angle_out
;
1286 stroker
->center
= *to
;
1293 /* documentation is in ftstroke.h */
1295 FT_EXPORT_DEF( FT_Error
)
1296 FT_Stroker_CubicTo( FT_Stroker stroker
,
1297 FT_Vector
* control1
,
1298 FT_Vector
* control2
,
1301 FT_Error error
= FT_Err_Ok
;
1302 FT_Vector bez_stack
[37];
1304 FT_Vector
* limit
= bez_stack
+ 32;
1305 FT_Angle start_angle
;
1306 FT_Bool first_arc
= TRUE
;
1313 arc
[3] = stroker
->center
;
1315 while ( arc
>= bez_stack
)
1317 FT_Angle angle_in
, angle_mid
, angle_out
;
1320 /* remove compiler warnings */
1321 angle_in
= angle_out
= angle_mid
= 0;
1324 !ft_cubic_is_small_enough( arc
, &angle_in
,
1325 &angle_mid
, &angle_out
) )
1327 ft_cubic_split( arc
);
1336 /* process corner if necessary */
1337 start_angle
= angle_in
;
1339 if ( stroker
->first_point
)
1340 error
= ft_stroker_subpath_start( stroker
, start_angle
);
1343 stroker
->angle_out
= start_angle
;
1344 error
= ft_stroker_process_corner( stroker
);
1350 /* the arc's angle is small enough; we can add it directly to each */
1353 FT_Vector ctrl1
, ctrl2
, end
;
1354 FT_Angle theta1
, phi1
, theta2
, phi2
, rotate
;
1355 FT_Fixed length1
, length2
;
1359 theta1
= ft_pos_abs( angle_mid
- angle_in
) / 2;
1360 theta2
= ft_pos_abs( angle_out
- angle_mid
) / 2;
1361 phi1
= (angle_mid
+ angle_in
) / 2;
1362 phi2
= (angle_mid
+ angle_out
) / 2;
1363 length1
= FT_DivFix( stroker
->radius
, FT_Cos( theta1
) );
1364 length2
= FT_DivFix( stroker
->radius
, FT_Cos( theta2
) );
1366 for ( side
= 0; side
<= 1; side
++ )
1368 rotate
= FT_SIDE_TO_ROTATE( side
);
1370 /* compute control points */
1371 FT_Vector_From_Polar( &ctrl1
, length1
, phi1
+ rotate
);
1372 ctrl1
.x
+= arc
[2].x
;
1373 ctrl1
.y
+= arc
[2].y
;
1375 FT_Vector_From_Polar( &ctrl2
, length2
, phi2
+ rotate
);
1376 ctrl2
.x
+= arc
[1].x
;
1377 ctrl2
.y
+= arc
[1].y
;
1379 /* compute end point */
1380 FT_Vector_From_Polar( &end
, stroker
->radius
, angle_out
+ rotate
);
1384 error
= ft_stroke_border_cubicto( stroker
->borders
+ side
,
1385 &ctrl1
, &ctrl2
, &end
);
1392 if ( arc
< bez_stack
)
1393 stroker
->angle_in
= angle_out
;
1396 stroker
->center
= *to
;
1403 /* documentation is in ftstroke.h */
1405 FT_EXPORT_DEF( FT_Error
)
1406 FT_Stroker_BeginSubPath( FT_Stroker stroker
,
1410 /* We cannot process the first point, because there is not enough */
1411 /* information regarding its corner/cap. The latter will be processed */
1412 /* in the `FT_Stroker_EndSubPath' routine. */
1414 stroker
->first_point
= TRUE
;
1415 stroker
->center
= *to
;
1416 stroker
->subpath_open
= open
;
1418 /* record the subpath start point for each border */
1419 stroker
->subpath_start
= *to
;
1426 ft_stroker_add_reverse_left( FT_Stroker stroker
,
1429 FT_StrokeBorder right
= stroker
->borders
+ 0;
1430 FT_StrokeBorder left
= stroker
->borders
+ 1;
1432 FT_Error error
= FT_Err_Ok
;
1435 FT_ASSERT( left
->start
>= 0 );
1437 new_points
= left
->num_points
- left
->start
;
1438 if ( new_points
> 0 )
1440 error
= ft_stroke_border_grow( right
, (FT_UInt
)new_points
);
1445 FT_Vector
* dst_point
= right
->points
+ right
->num_points
;
1446 FT_Byte
* dst_tag
= right
->tags
+ right
->num_points
;
1447 FT_Vector
* src_point
= left
->points
+ left
->num_points
- 1;
1448 FT_Byte
* src_tag
= left
->tags
+ left
->num_points
- 1;
1450 while ( src_point
>= left
->points
+ left
->start
)
1452 *dst_point
= *src_point
;
1453 *dst_tag
= *src_tag
;
1456 dst_tag
[0] &= ~FT_STROKE_TAG_BEGIN_END
;
1459 FT_Byte ttag
= (FT_Byte
)( dst_tag
[0] & FT_STROKE_TAG_BEGIN_END
);
1462 /* switch begin/end tags if necessary */
1463 if ( ttag
== FT_STROKE_TAG_BEGIN
||
1464 ttag
== FT_STROKE_TAG_END
)
1465 dst_tag
[0] ^= FT_STROKE_TAG_BEGIN_END
;
1476 left
->num_points
= left
->start
;
1477 right
->num_points
+= new_points
;
1479 right
->movable
= FALSE
;
1480 left
->movable
= FALSE
;
1488 /* documentation is in ftstroke.h */
1490 /* there's a lot of magic in this function! */
1491 FT_EXPORT_DEF( FT_Error
)
1492 FT_Stroker_EndSubPath( FT_Stroker stroker
)
1494 FT_Error error
= FT_Err_Ok
;
1497 if ( stroker
->subpath_open
)
1499 FT_StrokeBorder right
= stroker
->borders
;
1501 /* All right, this is an opened path, we need to add a cap between */
1502 /* right & left, add the reverse of left, then add a final cap */
1503 /* between left & right. */
1504 error
= ft_stroker_cap( stroker
, stroker
->angle_in
, 0 );
1508 /* add reversed points from `left' to `right' */
1509 error
= ft_stroker_add_reverse_left( stroker
, TRUE
);
1513 /* now add the final cap */
1514 stroker
->center
= stroker
->subpath_start
;
1515 error
= ft_stroker_cap( stroker
,
1516 stroker
->subpath_angle
+ FT_ANGLE_PI
, 0 );
1520 /* Now end the right subpath accordingly. The left one is */
1521 /* rewind and doesn't need further processing. */
1522 ft_stroke_border_close( right
, FALSE
);
1529 /* close the path if needed */
1530 if ( stroker
->center
.x
!= stroker
->subpath_start
.x
||
1531 stroker
->center
.y
!= stroker
->subpath_start
.y
)
1533 error
= FT_Stroker_LineTo( stroker
, &stroker
->subpath_start
);
1538 /* process the corner */
1539 stroker
->angle_out
= stroker
->subpath_angle
;
1540 turn
= FT_Angle_Diff( stroker
->angle_in
,
1541 stroker
->angle_out
);
1543 /* no specific corner processing is required if the turn is 0 */
1546 /* when we turn to the right, the inside side is 0 */
1549 /* otherwise, the inside side is 1 */
1553 error
= ft_stroker_inside( stroker
, inside_side
);
1557 /* process the outside side */
1558 error
= ft_stroker_outside( stroker
, 1 - inside_side
);
1563 /* then end our two subpaths */
1564 ft_stroke_border_close( stroker
->borders
+ 0, TRUE
);
1565 ft_stroke_border_close( stroker
->borders
+ 1, FALSE
);
1573 /* documentation is in ftstroke.h */
1575 FT_EXPORT_DEF( FT_Error
)
1576 FT_Stroker_GetBorderCounts( FT_Stroker stroker
,
1577 FT_StrokerBorder border
,
1578 FT_UInt
*anum_points
,
1579 FT_UInt
*anum_contours
)
1581 FT_UInt num_points
= 0, num_contours
= 0;
1585 if ( !stroker
|| border
> 1 )
1587 error
= FT_Err_Invalid_Argument
;
1591 error
= ft_stroke_border_get_counts( stroker
->borders
+ border
,
1592 &num_points
, &num_contours
);
1595 *anum_points
= num_points
;
1597 if ( anum_contours
)
1598 *anum_contours
= num_contours
;
1604 /* documentation is in ftstroke.h */
1606 FT_EXPORT_DEF( FT_Error
)
1607 FT_Stroker_GetCounts( FT_Stroker stroker
,
1608 FT_UInt
*anum_points
,
1609 FT_UInt
*anum_contours
)
1611 FT_UInt count1
, count2
, num_points
= 0;
1612 FT_UInt count3
, count4
, num_contours
= 0;
1616 error
= ft_stroke_border_get_counts( stroker
->borders
+ 0,
1621 error
= ft_stroke_border_get_counts( stroker
->borders
+ 1,
1626 num_points
= count1
+ count3
;
1627 num_contours
= count2
+ count4
;
1630 *anum_points
= num_points
;
1631 *anum_contours
= num_contours
;
1636 /* documentation is in ftstroke.h */
1638 FT_EXPORT_DEF( void )
1639 FT_Stroker_ExportBorder( FT_Stroker stroker
,
1640 FT_StrokerBorder border
,
1641 FT_Outline
* outline
)
1643 if ( border
== FT_STROKER_BORDER_LEFT
||
1644 border
== FT_STROKER_BORDER_RIGHT
)
1646 FT_StrokeBorder sborder
= & stroker
->borders
[border
];
1649 if ( sborder
->valid
)
1650 ft_stroke_border_export( sborder
, outline
);
1655 /* documentation is in ftstroke.h */
1657 FT_EXPORT_DEF( void )
1658 FT_Stroker_Export( FT_Stroker stroker
,
1659 FT_Outline
* outline
)
1661 FT_Stroker_ExportBorder( stroker
, FT_STROKER_BORDER_LEFT
, outline
);
1662 FT_Stroker_ExportBorder( stroker
, FT_STROKER_BORDER_RIGHT
, outline
);
1666 /* documentation is in ftstroke.h */
1669 * The following is very similar to FT_Outline_Decompose, except
1670 * that we do support opened paths, and do not scale the outline.
1672 FT_EXPORT_DEF( FT_Error
)
1673 FT_Stroker_ParseOutline( FT_Stroker stroker
,
1674 FT_Outline
* outline
,
1678 FT_Vector v_control
;
1687 FT_Int n
; /* index of contour in outline */
1688 FT_UInt first
; /* index of first point in contour */
1689 FT_Int tag
; /* current point's state */
1692 if ( !outline
|| !stroker
)
1693 return FT_Err_Invalid_Argument
;
1695 FT_Stroker_Rewind( stroker
);
1699 for ( n
= 0; n
< outline
->n_contours
; n
++ )
1701 FT_UInt last
; /* index of last point in contour */
1704 last
= outline
->contours
[n
];
1705 limit
= outline
->points
+ last
;
1707 /* skip empty points; we don't stroke these */
1708 if ( last
<= first
)
1714 v_start
= outline
->points
[first
];
1715 v_last
= outline
->points
[last
];
1717 v_control
= v_start
;
1719 point
= outline
->points
+ first
;
1720 tags
= outline
->tags
+ first
;
1721 tag
= FT_CURVE_TAG( tags
[0] );
1723 /* A contour cannot start with a cubic control point! */
1724 if ( tag
== FT_CURVE_TAG_CUBIC
)
1725 goto Invalid_Outline
;
1727 /* check first point to determine origin */
1728 if ( tag
== FT_CURVE_TAG_CONIC
)
1730 /* First point is conic control. Yes, this happens. */
1731 if ( FT_CURVE_TAG( outline
->tags
[last
] ) == FT_CURVE_TAG_ON
)
1733 /* start at last point if it is on the curve */
1739 /* if both first and last points are conic, */
1740 /* start at their middle */
1741 v_start
.x
= ( v_start
.x
+ v_last
.x
) / 2;
1742 v_start
.y
= ( v_start
.y
+ v_last
.y
) / 2;
1748 error
= FT_Stroker_BeginSubPath( stroker
, &v_start
, opened
);
1752 while ( point
< limit
)
1757 tag
= FT_CURVE_TAG( tags
[0] );
1760 case FT_CURVE_TAG_ON
: /* emit a single line_to */
1768 error
= FT_Stroker_LineTo( stroker
, &vec
);
1774 case FT_CURVE_TAG_CONIC
: /* consume conic arcs */
1775 v_control
.x
= point
->x
;
1776 v_control
.y
= point
->y
;
1779 if ( point
< limit
)
1787 tag
= FT_CURVE_TAG( tags
[0] );
1791 if ( tag
== FT_CURVE_TAG_ON
)
1793 error
= FT_Stroker_ConicTo( stroker
, &v_control
, &vec
);
1799 if ( tag
!= FT_CURVE_TAG_CONIC
)
1800 goto Invalid_Outline
;
1802 v_middle
.x
= ( v_control
.x
+ vec
.x
) / 2;
1803 v_middle
.y
= ( v_control
.y
+ vec
.y
) / 2;
1805 error
= FT_Stroker_ConicTo( stroker
, &v_control
, &v_middle
);
1813 error
= FT_Stroker_ConicTo( stroker
, &v_control
, &v_start
);
1816 default: /* FT_CURVE_TAG_CUBIC */
1818 FT_Vector vec1
, vec2
;
1821 if ( point
+ 1 > limit
||
1822 FT_CURVE_TAG( tags
[1] ) != FT_CURVE_TAG_CUBIC
)
1823 goto Invalid_Outline
;
1831 if ( point
<= limit
)
1838 error
= FT_Stroker_CubicTo( stroker
, &vec1
, &vec2
, &vec
);
1844 error
= FT_Stroker_CubicTo( stroker
, &vec1
, &vec2
, &v_start
);
1854 error
= FT_Stroker_EndSubPath( stroker
);
1867 return FT_Err_Invalid_Outline
;
1870 /* declare an extern to access ft_outline_glyph_class global allocated
1871 in ftglyph.c, and use the FT_OUTLINE_GLYPH_CLASS_GET macro to access
1872 it when FT_CONFIG_OPTION_PIC is defined */
1873 #ifndef FT_CONFIG_OPTION_PIC
1874 extern const FT_Glyph_Class ft_outline_glyph_class
;
1876 #include "basepic.h"
1879 /* documentation is in ftstroke.h */
1881 FT_EXPORT_DEF( FT_Error
)
1882 FT_Glyph_Stroke( FT_Glyph
*pglyph
,
1886 FT_Error error
= FT_Err_Invalid_Argument
;
1887 FT_Glyph glyph
= NULL
;
1888 FT_Library library
= stroker
->library
;
1891 if ( pglyph
== NULL
)
1895 if ( glyph
== NULL
|| glyph
->clazz
!= FT_OUTLINE_GLYPH_CLASS_GET
)
1902 error
= FT_Glyph_Copy( glyph
, ©
);
1910 FT_OutlineGlyph oglyph
= (FT_OutlineGlyph
) glyph
;
1911 FT_Outline
* outline
= &oglyph
->outline
;
1912 FT_UInt num_points
, num_contours
;
1915 error
= FT_Stroker_ParseOutline( stroker
, outline
, FALSE
);
1919 (void)FT_Stroker_GetCounts( stroker
, &num_points
, &num_contours
);
1921 FT_Outline_Done( glyph
->library
, outline
);
1923 error
= FT_Outline_New( glyph
->library
,
1924 num_points
, num_contours
, outline
);
1928 outline
->n_points
= 0;
1929 outline
->n_contours
= 0;
1931 FT_Stroker_Export( stroker
, outline
);
1935 FT_Done_Glyph( *pglyph
);
1941 FT_Done_Glyph( glyph
);
1952 /* documentation is in ftstroke.h */
1954 FT_EXPORT_DEF( FT_Error
)
1955 FT_Glyph_StrokeBorder( FT_Glyph
*pglyph
,
1960 FT_Error error
= FT_Err_Invalid_Argument
;
1961 FT_Glyph glyph
= NULL
;
1962 FT_Library library
= stroker
->library
;
1965 if ( pglyph
== NULL
)
1969 if ( glyph
== NULL
|| glyph
->clazz
!= FT_OUTLINE_GLYPH_CLASS_GET
)
1976 error
= FT_Glyph_Copy( glyph
, ©
);
1984 FT_OutlineGlyph oglyph
= (FT_OutlineGlyph
) glyph
;
1985 FT_StrokerBorder border
;
1986 FT_Outline
* outline
= &oglyph
->outline
;
1987 FT_UInt num_points
, num_contours
;
1990 border
= FT_Outline_GetOutsideBorder( outline
);
1993 if ( border
== FT_STROKER_BORDER_LEFT
)
1994 border
= FT_STROKER_BORDER_RIGHT
;
1996 border
= FT_STROKER_BORDER_LEFT
;
1999 error
= FT_Stroker_ParseOutline( stroker
, outline
, FALSE
);
2003 (void)FT_Stroker_GetBorderCounts( stroker
, border
,
2004 &num_points
, &num_contours
);
2006 FT_Outline_Done( glyph
->library
, outline
);
2008 error
= FT_Outline_New( glyph
->library
,
2015 outline
->n_points
= 0;
2016 outline
->n_contours
= 0;
2018 FT_Stroker_ExportBorder( stroker
, border
, outline
);
2022 FT_Done_Glyph( *pglyph
);
2028 FT_Done_Glyph( glyph
);