1 /***************************************************************************/
5 /* FreeType path stroker (body). */
7 /* Copyright 2002-2016 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
30 /* declare an extern to access `ft_outline_glyph_class' globally */
31 /* allocated in `ftglyph.c', and use the FT_OUTLINE_GLYPH_CLASS_GET */
32 /* macro to access it when FT_CONFIG_OPTION_PIC is defined */
33 #ifndef FT_CONFIG_OPTION_PIC
34 FT_CALLBACK_TABLE
const FT_Glyph_Class ft_outline_glyph_class
;
38 /* documentation is in ftstroke.h */
40 FT_EXPORT_DEF( FT_StrokerBorder
)
41 FT_Outline_GetInsideBorder( FT_Outline
* outline
)
43 FT_Orientation o
= FT_Outline_Get_Orientation( outline
);
46 return o
== FT_ORIENTATION_TRUETYPE
? FT_STROKER_BORDER_RIGHT
47 : FT_STROKER_BORDER_LEFT
;
51 /* documentation is in ftstroke.h */
53 FT_EXPORT_DEF( FT_StrokerBorder
)
54 FT_Outline_GetOutsideBorder( FT_Outline
* outline
)
56 FT_Orientation o
= FT_Outline_Get_Orientation( outline
);
59 return o
== FT_ORIENTATION_TRUETYPE
? FT_STROKER_BORDER_LEFT
60 : FT_STROKER_BORDER_RIGHT
;
64 /*************************************************************************/
65 /*************************************************************************/
67 /***** BEZIER COMPUTATIONS *****/
69 /*************************************************************************/
70 /*************************************************************************/
72 #define FT_SMALL_CONIC_THRESHOLD ( FT_ANGLE_PI / 6 )
73 #define FT_SMALL_CUBIC_THRESHOLD ( FT_ANGLE_PI / 8 )
77 #define FT_IS_SMALL( x ) ( (x) > -FT_EPSILON && (x) < FT_EPSILON )
81 ft_pos_abs( FT_Pos x
)
83 return x
>= 0 ? x
: -x
;
88 ft_conic_split( FT_Vector
* base
)
93 base
[4].x
= base
[2].x
;
95 a
= base
[3].x
= ( base
[2].x
+ b
) / 2;
96 b
= base
[1].x
= ( base
[0].x
+ b
) / 2;
97 base
[2].x
= ( a
+ b
) / 2;
99 base
[4].y
= base
[2].y
;
101 a
= base
[3].y
= ( base
[2].y
+ b
) / 2;
102 b
= base
[1].y
= ( base
[0].y
+ b
) / 2;
103 base
[2].y
= ( a
+ b
) / 2;
108 ft_conic_is_small_enough( FT_Vector
* base
,
110 FT_Angle
*angle_out
)
114 FT_Int close1
, close2
;
117 d1
.x
= base
[1].x
- base
[2].x
;
118 d1
.y
= base
[1].y
- base
[2].y
;
119 d2
.x
= base
[0].x
- base
[1].x
;
120 d2
.y
= base
[0].y
- base
[1].y
;
122 close1
= FT_IS_SMALL( d1
.x
) && FT_IS_SMALL( d1
.y
);
123 close2
= FT_IS_SMALL( d2
.x
) && FT_IS_SMALL( d2
.y
);
129 /* basically a point; */
130 /* do nothing to retain original direction */
135 *angle_out
= FT_Atan2( d2
.x
, d2
.y
);
143 *angle_out
= FT_Atan2( d1
.x
, d1
.y
);
147 *angle_in
= FT_Atan2( d1
.x
, d1
.y
);
148 *angle_out
= FT_Atan2( d2
.x
, d2
.y
);
152 theta
= ft_pos_abs( FT_Angle_Diff( *angle_in
, *angle_out
) );
154 return FT_BOOL( theta
< FT_SMALL_CONIC_THRESHOLD
);
159 ft_cubic_split( FT_Vector
* base
)
164 base
[6].x
= base
[3].x
;
167 base
[1].x
= a
= ( base
[0].x
+ c
) / 2;
168 base
[5].x
= b
= ( base
[3].x
+ d
) / 2;
170 base
[2].x
= a
= ( a
+ c
) / 2;
171 base
[4].x
= b
= ( b
+ c
) / 2;
172 base
[3].x
= ( a
+ b
) / 2;
174 base
[6].y
= base
[3].y
;
177 base
[1].y
= a
= ( base
[0].y
+ c
) / 2;
178 base
[5].y
= b
= ( base
[3].y
+ d
) / 2;
180 base
[2].y
= a
= ( a
+ c
) / 2;
181 base
[4].y
= b
= ( b
+ c
) / 2;
182 base
[3].y
= ( a
+ b
) / 2;
186 /* Return the average of `angle1' and `angle2'. */
187 /* This gives correct result even if `angle1' and `angle2' */
188 /* have opposite signs. */
190 ft_angle_mean( FT_Angle angle1
,
193 return angle1
+ FT_Angle_Diff( angle1
, angle2
) / 2;
198 ft_cubic_is_small_enough( FT_Vector
* base
,
201 FT_Angle
*angle_out
)
203 FT_Vector d1
, d2
, d3
;
204 FT_Angle theta1
, theta2
;
205 FT_Int close1
, close2
, close3
;
208 d1
.x
= base
[2].x
- base
[3].x
;
209 d1
.y
= base
[2].y
- base
[3].y
;
210 d2
.x
= base
[1].x
- base
[2].x
;
211 d2
.y
= base
[1].y
- base
[2].y
;
212 d3
.x
= base
[0].x
- base
[1].x
;
213 d3
.y
= base
[0].y
- base
[1].y
;
215 close1
= FT_IS_SMALL( d1
.x
) && FT_IS_SMALL( d1
.y
);
216 close2
= FT_IS_SMALL( d2
.x
) && FT_IS_SMALL( d2
.y
);
217 close3
= FT_IS_SMALL( d3
.x
) && FT_IS_SMALL( d3
.y
);
225 /* basically a point; */
226 /* do nothing to retain original direction */
232 *angle_out
= FT_Atan2( d3
.x
, d3
.y
);
241 *angle_out
= FT_Atan2( d2
.x
, d2
.y
);
246 *angle_mid
= FT_Atan2( d2
.x
, d2
.y
);
247 *angle_out
= FT_Atan2( d3
.x
, d3
.y
);
259 *angle_out
= FT_Atan2( d1
.x
, d1
.y
);
263 *angle_in
= FT_Atan2( d1
.x
, d1
.y
);
264 *angle_out
= FT_Atan2( d3
.x
, d3
.y
);
265 *angle_mid
= ft_angle_mean( *angle_in
, *angle_out
);
272 *angle_in
= FT_Atan2( d1
.x
, d1
.y
);
274 *angle_out
= FT_Atan2( d2
.x
, d2
.y
);
278 *angle_in
= FT_Atan2( d1
.x
, d1
.y
);
279 *angle_mid
= FT_Atan2( d2
.x
, d2
.y
);
280 *angle_out
= FT_Atan2( d3
.x
, d3
.y
);
285 theta1
= ft_pos_abs( FT_Angle_Diff( *angle_in
, *angle_mid
) );
286 theta2
= ft_pos_abs( FT_Angle_Diff( *angle_mid
, *angle_out
) );
288 return FT_BOOL( theta1
< FT_SMALL_CUBIC_THRESHOLD
&&
289 theta2
< FT_SMALL_CUBIC_THRESHOLD
);
293 /*************************************************************************/
294 /*************************************************************************/
296 /***** STROKE BORDERS *****/
298 /*************************************************************************/
299 /*************************************************************************/
301 typedef enum FT_StrokeTags_
303 FT_STROKE_TAG_ON
= 1, /* on-curve point */
304 FT_STROKE_TAG_CUBIC
= 2, /* cubic off-point */
305 FT_STROKE_TAG_BEGIN
= 4, /* sub-path start */
306 FT_STROKE_TAG_END
= 8 /* sub-path end */
310 #define FT_STROKE_TAG_BEGIN_END ( FT_STROKE_TAG_BEGIN | FT_STROKE_TAG_END )
312 typedef struct FT_StrokeBorderRec_
318 FT_Bool movable
; /* TRUE for ends of lineto borders */
319 FT_Int start
; /* index of current sub-path start point */
323 } FT_StrokeBorderRec
, *FT_StrokeBorder
;
327 ft_stroke_border_grow( FT_StrokeBorder border
,
330 FT_UInt old_max
= border
->max_points
;
331 FT_UInt new_max
= border
->num_points
+ new_points
;
332 FT_Error error
= FT_Err_Ok
;
335 if ( new_max
> old_max
)
337 FT_UInt cur_max
= old_max
;
338 FT_Memory memory
= border
->memory
;
341 while ( cur_max
< new_max
)
342 cur_max
+= ( cur_max
>> 1 ) + 16;
344 if ( FT_RENEW_ARRAY( border
->points
, old_max
, cur_max
) ||
345 FT_RENEW_ARRAY( border
->tags
, old_max
, cur_max
) )
348 border
->max_points
= cur_max
;
357 ft_stroke_border_close( FT_StrokeBorder border
,
360 FT_UInt start
= (FT_UInt
)border
->start
;
361 FT_UInt count
= border
->num_points
;
364 FT_ASSERT( border
->start
>= 0 );
366 /* don't record empty paths! */
367 if ( count
<= start
+ 1U )
368 border
->num_points
= start
;
371 /* copy the last point to the start of this sub-path, since */
372 /* it contains the `adjusted' starting coordinates */
373 border
->num_points
= --count
;
374 border
->points
[start
] = border
->points
[count
];
378 /* reverse the points */
380 FT_Vector
* vec1
= border
->points
+ start
+ 1;
381 FT_Vector
* vec2
= border
->points
+ count
- 1;
384 for ( ; vec1
< vec2
; vec1
++, vec2
-- )
397 FT_Byte
* tag1
= border
->tags
+ start
+ 1;
398 FT_Byte
* tag2
= border
->tags
+ count
- 1;
401 for ( ; tag1
< tag2
; tag1
++, tag2
-- )
413 border
->tags
[start
] |= FT_STROKE_TAG_BEGIN
;
414 border
->tags
[count
- 1] |= FT_STROKE_TAG_END
;
418 border
->movable
= FALSE
;
423 ft_stroke_border_lineto( FT_StrokeBorder border
,
427 FT_Error error
= FT_Err_Ok
;
430 FT_ASSERT( border
->start
>= 0 );
432 if ( border
->movable
)
434 /* move last point */
435 border
->points
[border
->num_points
- 1] = *to
;
439 /* don't add zero-length lineto */
440 if ( border
->num_points
> 0 &&
441 FT_IS_SMALL( border
->points
[border
->num_points
- 1].x
- to
->x
) &&
442 FT_IS_SMALL( border
->points
[border
->num_points
- 1].y
- to
->y
) )
446 error
= ft_stroke_border_grow( border
, 1 );
449 FT_Vector
* vec
= border
->points
+ border
->num_points
;
450 FT_Byte
* tag
= border
->tags
+ border
->num_points
;
454 tag
[0] = FT_STROKE_TAG_ON
;
456 border
->num_points
+= 1;
459 border
->movable
= movable
;
465 ft_stroke_border_conicto( FT_StrokeBorder border
,
472 FT_ASSERT( border
->start
>= 0 );
474 error
= ft_stroke_border_grow( border
, 2 );
477 FT_Vector
* vec
= border
->points
+ border
->num_points
;
478 FT_Byte
* tag
= border
->tags
+ border
->num_points
;
485 tag
[1] = FT_STROKE_TAG_ON
;
487 border
->num_points
+= 2;
490 border
->movable
= FALSE
;
497 ft_stroke_border_cubicto( FT_StrokeBorder border
,
505 FT_ASSERT( border
->start
>= 0 );
507 error
= ft_stroke_border_grow( border
, 3 );
510 FT_Vector
* vec
= border
->points
+ border
->num_points
;
511 FT_Byte
* tag
= border
->tags
+ border
->num_points
;
518 tag
[0] = FT_STROKE_TAG_CUBIC
;
519 tag
[1] = FT_STROKE_TAG_CUBIC
;
520 tag
[2] = FT_STROKE_TAG_ON
;
522 border
->num_points
+= 3;
525 border
->movable
= FALSE
;
531 #define FT_ARC_CUBIC_ANGLE ( FT_ANGLE_PI / 2 )
535 ft_stroke_border_arcto( FT_StrokeBorder border
,
538 FT_Angle angle_start
,
539 FT_Angle angle_diff
)
541 FT_Angle total
, angle
, step
, rotate
, next
, theta
;
542 FT_Vector a
, b
, a2
, b2
;
544 FT_Error error
= FT_Err_Ok
;
547 /* compute start point */
548 FT_Vector_From_Polar( &a
, radius
, angle_start
);
554 rotate
= ( angle_diff
>= 0 ) ? FT_ANGLE_PI2
: -FT_ANGLE_PI2
;
559 if ( step
> FT_ARC_CUBIC_ANGLE
)
560 step
= FT_ARC_CUBIC_ANGLE
;
562 else if ( step
< -FT_ARC_CUBIC_ANGLE
)
563 step
= -FT_ARC_CUBIC_ANGLE
;
572 /* compute end point */
573 FT_Vector_From_Polar( &b
, radius
, next
);
577 /* compute first and second control points */
578 length
= FT_MulDiv( radius
, FT_Sin( theta
) * 4,
579 ( 0x10000L
+ FT_Cos( theta
) ) * 3 );
581 FT_Vector_From_Polar( &a2
, length
, angle
+ rotate
);
585 FT_Vector_From_Polar( &b2
, length
, next
- rotate
);
590 error
= ft_stroke_border_cubicto( border
, &a2
, &b2
, &b
);
594 /* process the rest of the arc ?? */
605 ft_stroke_border_moveto( FT_StrokeBorder border
,
608 /* close current open path if any ? */
609 if ( border
->start
>= 0 )
610 ft_stroke_border_close( border
, FALSE
);
612 border
->start
= (FT_Int
)border
->num_points
;
613 border
->movable
= FALSE
;
615 return ft_stroke_border_lineto( border
, to
, FALSE
);
620 ft_stroke_border_init( FT_StrokeBorder border
,
623 border
->memory
= memory
;
624 border
->points
= NULL
;
627 border
->num_points
= 0;
628 border
->max_points
= 0;
630 border
->valid
= FALSE
;
635 ft_stroke_border_reset( FT_StrokeBorder border
)
637 border
->num_points
= 0;
639 border
->valid
= FALSE
;
644 ft_stroke_border_done( FT_StrokeBorder border
)
646 FT_Memory memory
= border
->memory
;
649 FT_FREE( border
->points
);
650 FT_FREE( border
->tags
);
652 border
->num_points
= 0;
653 border
->max_points
= 0;
655 border
->valid
= FALSE
;
660 ft_stroke_border_get_counts( FT_StrokeBorder border
,
661 FT_UInt
*anum_points
,
662 FT_UInt
*anum_contours
)
664 FT_Error error
= FT_Err_Ok
;
665 FT_UInt num_points
= 0;
666 FT_UInt num_contours
= 0;
668 FT_UInt count
= border
->num_points
;
669 FT_Vector
* point
= border
->points
;
670 FT_Byte
* tags
= border
->tags
;
671 FT_Int in_contour
= 0;
674 for ( ; count
> 0; count
--, num_points
++, point
++, tags
++ )
676 if ( tags
[0] & FT_STROKE_TAG_BEGIN
)
678 if ( in_contour
!= 0 )
683 else if ( in_contour
== 0 )
686 if ( tags
[0] & FT_STROKE_TAG_END
)
693 if ( in_contour
!= 0 )
696 border
->valid
= TRUE
;
699 *anum_points
= num_points
;
700 *anum_contours
= num_contours
;
711 ft_stroke_border_export( FT_StrokeBorder border
,
712 FT_Outline
* outline
)
714 /* copy point locations */
715 if ( border
->num_points
)
716 FT_ARRAY_COPY( outline
->points
+ outline
->n_points
,
718 border
->num_points
);
722 FT_UInt count
= border
->num_points
;
723 FT_Byte
* read
= border
->tags
;
724 FT_Byte
* write
= (FT_Byte
*)outline
->tags
+ outline
->n_points
;
727 for ( ; count
> 0; count
--, read
++, write
++ )
729 if ( *read
& FT_STROKE_TAG_ON
)
730 *write
= FT_CURVE_TAG_ON
;
731 else if ( *read
& FT_STROKE_TAG_CUBIC
)
732 *write
= FT_CURVE_TAG_CUBIC
;
734 *write
= FT_CURVE_TAG_CONIC
;
740 FT_UInt count
= border
->num_points
;
741 FT_Byte
* tags
= border
->tags
;
742 FT_Short
* write
= outline
->contours
+ outline
->n_contours
;
743 FT_Short idx
= (FT_Short
)outline
->n_points
;
746 for ( ; count
> 0; count
--, tags
++, idx
++ )
748 if ( *tags
& FT_STROKE_TAG_END
)
751 outline
->n_contours
++;
756 outline
->n_points
+= (short)border
->num_points
;
758 FT_ASSERT( FT_Outline_Check( outline
) == 0 );
762 /*************************************************************************/
763 /*************************************************************************/
765 /***** STROKER *****/
767 /*************************************************************************/
768 /*************************************************************************/
770 #define FT_SIDE_TO_ROTATE( s ) ( FT_ANGLE_PI2 - (s) * FT_ANGLE_PI )
772 typedef struct FT_StrokerRec_
774 FT_Angle angle_in
; /* direction into curr join */
775 FT_Angle angle_out
; /* direction out of join */
776 FT_Vector center
; /* current position */
777 FT_Fixed line_length
; /* length of last lineto */
778 FT_Bool first_point
; /* is this the start? */
779 FT_Bool subpath_open
; /* is the subpath open? */
780 FT_Angle subpath_angle
; /* subpath start direction */
781 FT_Vector subpath_start
; /* subpath start position */
782 FT_Fixed subpath_line_length
; /* subpath start lineto len */
783 FT_Bool handle_wide_strokes
; /* use wide strokes logic? */
785 FT_Stroker_LineCap line_cap
;
786 FT_Stroker_LineJoin line_join
;
787 FT_Stroker_LineJoin line_join_saved
;
788 FT_Fixed miter_limit
;
791 FT_StrokeBorderRec borders
[2];
797 /* documentation is in ftstroke.h */
799 FT_EXPORT_DEF( FT_Error
)
800 FT_Stroker_New( FT_Library library
,
801 FT_Stroker
*astroker
)
803 FT_Error error
; /* assigned in FT_NEW */
805 FT_Stroker stroker
= NULL
;
809 return FT_THROW( Invalid_Library_Handle
);
812 return FT_THROW( Invalid_Argument
);
814 memory
= library
->memory
;
816 if ( !FT_NEW( stroker
) )
818 stroker
->library
= library
;
820 ft_stroke_border_init( &stroker
->borders
[0], memory
);
821 ft_stroke_border_init( &stroker
->borders
[1], memory
);
830 /* documentation is in ftstroke.h */
832 FT_EXPORT_DEF( void )
833 FT_Stroker_Set( FT_Stroker stroker
,
835 FT_Stroker_LineCap line_cap
,
836 FT_Stroker_LineJoin line_join
,
837 FT_Fixed miter_limit
)
842 stroker
->radius
= radius
;
843 stroker
->line_cap
= line_cap
;
844 stroker
->line_join
= line_join
;
845 stroker
->miter_limit
= miter_limit
;
847 /* ensure miter limit has sensible value */
848 if ( stroker
->miter_limit
< 0x10000L
)
849 stroker
->miter_limit
= 0x10000L
;
851 /* save line join style: */
852 /* line join style can be temporarily changed when stroking curves */
853 stroker
->line_join_saved
= line_join
;
855 FT_Stroker_Rewind( stroker
);
859 /* documentation is in ftstroke.h */
861 FT_EXPORT_DEF( void )
862 FT_Stroker_Rewind( FT_Stroker stroker
)
866 ft_stroke_border_reset( &stroker
->borders
[0] );
867 ft_stroke_border_reset( &stroker
->borders
[1] );
872 /* documentation is in ftstroke.h */
874 FT_EXPORT_DEF( void )
875 FT_Stroker_Done( FT_Stroker stroker
)
879 FT_Memory memory
= stroker
->library
->memory
;
882 ft_stroke_border_done( &stroker
->borders
[0] );
883 ft_stroke_border_done( &stroker
->borders
[1] );
885 stroker
->library
= NULL
;
891 /* create a circular arc at a corner or cap */
893 ft_stroker_arcto( FT_Stroker stroker
,
896 FT_Angle total
, rotate
;
897 FT_Fixed radius
= stroker
->radius
;
898 FT_Error error
= FT_Err_Ok
;
899 FT_StrokeBorder border
= stroker
->borders
+ side
;
902 rotate
= FT_SIDE_TO_ROTATE( side
);
904 total
= FT_Angle_Diff( stroker
->angle_in
, stroker
->angle_out
);
905 if ( total
== FT_ANGLE_PI
)
908 error
= ft_stroke_border_arcto( border
,
911 stroker
->angle_in
+ rotate
,
913 border
->movable
= FALSE
;
918 /* add a cap at the end of an opened path */
920 ft_stroker_cap( FT_Stroker stroker
,
924 FT_Error error
= FT_Err_Ok
;
927 if ( stroker
->line_cap
== FT_STROKER_LINECAP_ROUND
)
929 /* add a round cap */
930 stroker
->angle_in
= angle
;
931 stroker
->angle_out
= angle
+ FT_ANGLE_PI
;
933 error
= ft_stroker_arcto( stroker
, side
);
935 else if ( stroker
->line_cap
== FT_STROKER_LINECAP_SQUARE
)
937 /* add a square cap */
938 FT_Vector delta
, delta2
;
939 FT_Angle rotate
= FT_SIDE_TO_ROTATE( side
);
940 FT_Fixed radius
= stroker
->radius
;
941 FT_StrokeBorder border
= stroker
->borders
+ side
;
944 FT_Vector_From_Polar( &delta2
, radius
, angle
+ rotate
);
945 FT_Vector_From_Polar( &delta
, radius
, angle
);
947 delta
.x
+= stroker
->center
.x
+ delta2
.x
;
948 delta
.y
+= stroker
->center
.y
+ delta2
.y
;
950 error
= ft_stroke_border_lineto( border
, &delta
, FALSE
);
954 FT_Vector_From_Polar( &delta2
, radius
, angle
- rotate
);
955 FT_Vector_From_Polar( &delta
, radius
, angle
);
957 delta
.x
+= delta2
.x
+ stroker
->center
.x
;
958 delta
.y
+= delta2
.y
+ stroker
->center
.y
;
960 error
= ft_stroke_border_lineto( border
, &delta
, FALSE
);
962 else if ( stroker
->line_cap
== FT_STROKER_LINECAP_BUTT
)
964 /* add a butt ending */
966 FT_Angle rotate
= FT_SIDE_TO_ROTATE( side
);
967 FT_Fixed radius
= stroker
->radius
;
968 FT_StrokeBorder border
= stroker
->borders
+ side
;
971 FT_Vector_From_Polar( &delta
, radius
, angle
+ rotate
);
973 delta
.x
+= stroker
->center
.x
;
974 delta
.y
+= stroker
->center
.y
;
976 error
= ft_stroke_border_lineto( border
, &delta
, FALSE
);
980 FT_Vector_From_Polar( &delta
, radius
, angle
- rotate
);
982 delta
.x
+= stroker
->center
.x
;
983 delta
.y
+= stroker
->center
.y
;
985 error
= ft_stroke_border_lineto( border
, &delta
, FALSE
);
993 /* process an inside corner, i.e. compute intersection */
995 ft_stroker_inside( FT_Stroker stroker
,
997 FT_Fixed line_length
)
999 FT_StrokeBorder border
= stroker
->borders
+ side
;
1000 FT_Angle phi
, theta
, rotate
;
1001 FT_Fixed length
, thcos
;
1003 FT_Error error
= FT_Err_Ok
;
1004 FT_Bool intersect
; /* use intersection of lines? */
1007 rotate
= FT_SIDE_TO_ROTATE( side
);
1009 theta
= FT_Angle_Diff( stroker
->angle_in
, stroker
->angle_out
) / 2;
1011 /* Only intersect borders if between two lineto's and both */
1012 /* lines are long enough (line_length is zero for curves). */
1013 /* Also avoid U-turns of nearly 180 degree. */
1014 if ( !border
->movable
|| line_length
== 0 ||
1015 theta
> 0x59C000 || theta
< -0x59C000 )
1019 /* compute minimum required length of lines */
1020 FT_Fixed min_length
= ft_pos_abs( FT_MulFix( stroker
->radius
,
1021 FT_Tan( theta
) ) );
1024 intersect
= FT_BOOL( min_length
&&
1025 stroker
->line_length
>= min_length
&&
1026 line_length
>= min_length
);
1031 FT_Vector_From_Polar( &delta
, stroker
->radius
,
1032 stroker
->angle_out
+ rotate
);
1033 delta
.x
+= stroker
->center
.x
;
1034 delta
.y
+= stroker
->center
.y
;
1036 border
->movable
= FALSE
;
1040 /* compute median angle */
1041 phi
= stroker
->angle_in
+ theta
;
1043 thcos
= FT_Cos( theta
);
1045 length
= FT_DivFix( stroker
->radius
, thcos
);
1047 FT_Vector_From_Polar( &delta
, length
, phi
+ rotate
);
1048 delta
.x
+= stroker
->center
.x
;
1049 delta
.y
+= stroker
->center
.y
;
1052 error
= ft_stroke_border_lineto( border
, &delta
, FALSE
);
1058 /* process an outside corner, i.e. compute bevel/miter/round */
1060 ft_stroker_outside( FT_Stroker stroker
,
1062 FT_Fixed line_length
)
1064 FT_StrokeBorder border
= stroker
->borders
+ side
;
1069 if ( stroker
->line_join
== FT_STROKER_LINEJOIN_ROUND
)
1070 error
= ft_stroker_arcto( stroker
, side
);
1073 /* this is a mitered (pointed) or beveled (truncated) corner */
1074 FT_Fixed sigma
= 0, radius
= stroker
->radius
;
1075 FT_Angle theta
= 0, phi
= 0;
1077 FT_Bool bevel
, fixed_bevel
;
1080 rotate
= FT_SIDE_TO_ROTATE( side
);
1083 FT_BOOL( stroker
->line_join
== FT_STROKER_LINEJOIN_BEVEL
);
1086 FT_BOOL( stroker
->line_join
!= FT_STROKER_LINEJOIN_MITER_VARIABLE
);
1090 theta
= FT_Angle_Diff( stroker
->angle_in
, stroker
->angle_out
);
1092 if ( theta
== FT_ANGLE_PI
)
1095 phi
= stroker
->angle_in
;
1100 phi
= stroker
->angle_in
+ theta
+ rotate
;
1103 thcos
= FT_Cos( theta
);
1104 sigma
= FT_MulFix( stroker
->miter_limit
, thcos
);
1106 /* is miter limit exceeded? */
1107 if ( sigma
< 0x10000L
)
1109 /* don't create variable bevels for very small deviations; */
1110 /* FT_Sin(x) = 0 for x <= 57 */
1111 if ( fixed_bevel
|| ft_pos_abs( theta
) > 57 )
1116 if ( bevel
) /* this is a bevel (broken angle) */
1120 /* the outer corners are simply joined together */
1125 FT_Vector_From_Polar( &delta
,
1127 stroker
->angle_out
+ rotate
);
1128 delta
.x
+= stroker
->center
.x
;
1129 delta
.y
+= stroker
->center
.y
;
1131 border
->movable
= FALSE
;
1132 error
= ft_stroke_border_lineto( border
, &delta
, FALSE
);
1134 else /* variable bevel */
1136 /* the miter is truncated */
1137 FT_Vector middle
, delta
;
1141 /* compute middle point */
1142 FT_Vector_From_Polar( &middle
,
1143 FT_MulFix( radius
, stroker
->miter_limit
),
1145 middle
.x
+= stroker
->center
.x
;
1146 middle
.y
+= stroker
->center
.y
;
1148 /* compute first angle point */
1149 length
= FT_MulDiv( radius
, 0x10000L
- sigma
,
1150 ft_pos_abs( FT_Sin( theta
) ) );
1152 FT_Vector_From_Polar( &delta
, length
, phi
+ rotate
);
1153 delta
.x
+= middle
.x
;
1154 delta
.y
+= middle
.y
;
1156 error
= ft_stroke_border_lineto( border
, &delta
, FALSE
);
1160 /* compute second angle point */
1161 FT_Vector_From_Polar( &delta
, length
, phi
- rotate
);
1162 delta
.x
+= middle
.x
;
1163 delta
.y
+= middle
.y
;
1165 error
= ft_stroke_border_lineto( border
, &delta
, FALSE
);
1169 /* finally, add an end point; only needed if not lineto */
1170 /* (line_length is zero for curves) */
1171 if ( line_length
== 0 )
1173 FT_Vector_From_Polar( &delta
,
1175 stroker
->angle_out
+ rotate
);
1177 delta
.x
+= stroker
->center
.x
;
1178 delta
.y
+= stroker
->center
.y
;
1180 error
= ft_stroke_border_lineto( border
, &delta
, FALSE
);
1184 else /* this is a miter (intersection) */
1190 length
= FT_DivFix( stroker
->radius
, thcos
);
1192 FT_Vector_From_Polar( &delta
, length
, phi
);
1193 delta
.x
+= stroker
->center
.x
;
1194 delta
.y
+= stroker
->center
.y
;
1196 error
= ft_stroke_border_lineto( border
, &delta
, FALSE
);
1200 /* now add an end point; only needed if not lineto */
1201 /* (line_length is zero for curves) */
1202 if ( line_length
== 0 )
1204 FT_Vector_From_Polar( &delta
,
1206 stroker
->angle_out
+ rotate
);
1207 delta
.x
+= stroker
->center
.x
;
1208 delta
.y
+= stroker
->center
.y
;
1210 error
= ft_stroke_border_lineto( border
, &delta
, FALSE
);
1221 ft_stroker_process_corner( FT_Stroker stroker
,
1222 FT_Fixed line_length
)
1224 FT_Error error
= FT_Err_Ok
;
1229 turn
= FT_Angle_Diff( stroker
->angle_in
, stroker
->angle_out
);
1231 /* no specific corner processing is required if the turn is 0 */
1235 /* when we turn to the right, the inside side is 0 */
1236 /* otherwise, the inside side is 1 */
1237 inside_side
= ( turn
< 0 );
1239 /* process the inside side */
1240 error
= ft_stroker_inside( stroker
, inside_side
, line_length
);
1244 /* process the outside side */
1245 error
= ft_stroker_outside( stroker
, !inside_side
, line_length
);
1252 /* add two points to the left and right borders corresponding to the */
1253 /* start of the subpath */
1255 ft_stroker_subpath_start( FT_Stroker stroker
,
1256 FT_Angle start_angle
,
1257 FT_Fixed line_length
)
1262 FT_StrokeBorder border
;
1265 FT_Vector_From_Polar( &delta
, stroker
->radius
,
1266 start_angle
+ FT_ANGLE_PI2
);
1268 point
.x
= stroker
->center
.x
+ delta
.x
;
1269 point
.y
= stroker
->center
.y
+ delta
.y
;
1271 border
= stroker
->borders
;
1272 error
= ft_stroke_border_moveto( border
, &point
);
1276 point
.x
= stroker
->center
.x
- delta
.x
;
1277 point
.y
= stroker
->center
.y
- delta
.y
;
1280 error
= ft_stroke_border_moveto( border
, &point
);
1282 /* save angle, position, and line length for last join */
1283 /* (line_length is zero for curves) */
1284 stroker
->subpath_angle
= start_angle
;
1285 stroker
->first_point
= FALSE
;
1286 stroker
->subpath_line_length
= line_length
;
1293 /* documentation is in ftstroke.h */
1295 FT_EXPORT_DEF( FT_Error
)
1296 FT_Stroker_LineTo( FT_Stroker stroker
,
1299 FT_Error error
= FT_Err_Ok
;
1300 FT_StrokeBorder border
;
1304 FT_Fixed line_length
;
1307 if ( !stroker
|| !to
)
1308 return FT_THROW( Invalid_Argument
);
1310 delta
.x
= to
->x
- stroker
->center
.x
;
1311 delta
.y
= to
->y
- stroker
->center
.y
;
1313 /* a zero-length lineto is a no-op; avoid creating a spurious corner */
1314 if ( delta
.x
== 0 && delta
.y
== 0 )
1317 /* compute length of line */
1318 line_length
= FT_Vector_Length( &delta
);
1320 angle
= FT_Atan2( delta
.x
, delta
.y
);
1321 FT_Vector_From_Polar( &delta
, stroker
->radius
, angle
+ FT_ANGLE_PI2
);
1323 /* process corner if necessary */
1324 if ( stroker
->first_point
)
1326 /* This is the first segment of a subpath. We need to */
1327 /* add a point to each border at their respective starting */
1328 /* point locations. */
1329 error
= ft_stroker_subpath_start( stroker
, angle
, line_length
);
1335 /* process the current corner */
1336 stroker
->angle_out
= angle
;
1337 error
= ft_stroker_process_corner( stroker
, line_length
);
1342 /* now add a line segment to both the `inside' and `outside' paths */
1343 for ( border
= stroker
->borders
, side
= 1; side
>= 0; side
--, border
++ )
1348 point
.x
= to
->x
+ delta
.x
;
1349 point
.y
= to
->y
+ delta
.y
;
1351 /* the ends of lineto borders are movable */
1352 error
= ft_stroke_border_lineto( border
, &point
, TRUE
);
1360 stroker
->angle_in
= angle
;
1361 stroker
->center
= *to
;
1362 stroker
->line_length
= line_length
;
1369 /* documentation is in ftstroke.h */
1371 FT_EXPORT_DEF( FT_Error
)
1372 FT_Stroker_ConicTo( FT_Stroker stroker
,
1376 FT_Error error
= FT_Err_Ok
;
1377 FT_Vector bez_stack
[34];
1379 FT_Vector
* limit
= bez_stack
+ 30;
1380 FT_Bool first_arc
= TRUE
;
1383 if ( !stroker
|| !control
|| !to
)
1385 error
= FT_THROW( Invalid_Argument
);
1389 /* if all control points are coincident, this is a no-op; */
1390 /* avoid creating a spurious corner */
1391 if ( FT_IS_SMALL( stroker
->center
.x
- control
->x
) &&
1392 FT_IS_SMALL( stroker
->center
.y
- control
->y
) &&
1393 FT_IS_SMALL( control
->x
- to
->x
) &&
1394 FT_IS_SMALL( control
->y
- to
->y
) )
1396 stroker
->center
= *to
;
1403 arc
[2] = stroker
->center
;
1405 while ( arc
>= bez_stack
)
1407 FT_Angle angle_in
, angle_out
;
1410 /* initialize with current direction */
1411 angle_in
= angle_out
= stroker
->angle_in
;
1414 !ft_conic_is_small_enough( arc
, &angle_in
, &angle_out
) )
1416 if ( stroker
->first_point
)
1417 stroker
->angle_in
= angle_in
;
1419 ft_conic_split( arc
);
1428 /* process corner if necessary */
1429 if ( stroker
->first_point
)
1430 error
= ft_stroker_subpath_start( stroker
, angle_in
, 0 );
1433 stroker
->angle_out
= angle_in
;
1434 error
= ft_stroker_process_corner( stroker
, 0 );
1437 else if ( ft_pos_abs( FT_Angle_Diff( stroker
->angle_in
, angle_in
) ) >
1438 FT_SMALL_CONIC_THRESHOLD
/ 4 )
1440 /* if the deviation from one arc to the next is too great, */
1441 /* add a round corner */
1442 stroker
->center
= arc
[2];
1443 stroker
->angle_out
= angle_in
;
1444 stroker
->line_join
= FT_STROKER_LINEJOIN_ROUND
;
1446 error
= ft_stroker_process_corner( stroker
, 0 );
1448 /* reinstate line join style */
1449 stroker
->line_join
= stroker
->line_join_saved
;
1455 /* the arc's angle is small enough; we can add it directly to each */
1458 FT_Vector ctrl
, end
;
1459 FT_Angle theta
, phi
, rotate
, alpha0
= 0;
1461 FT_StrokeBorder border
;
1465 theta
= FT_Angle_Diff( angle_in
, angle_out
) / 2;
1466 phi
= angle_in
+ theta
;
1467 length
= FT_DivFix( stroker
->radius
, FT_Cos( theta
) );
1469 /* compute direction of original arc */
1470 if ( stroker
->handle_wide_strokes
)
1471 alpha0
= FT_Atan2( arc
[0].x
- arc
[2].x
, arc
[0].y
- arc
[2].y
);
1473 for ( border
= stroker
->borders
, side
= 0;
1477 rotate
= FT_SIDE_TO_ROTATE( side
);
1479 /* compute control point */
1480 FT_Vector_From_Polar( &ctrl
, length
, phi
+ rotate
);
1484 /* compute end point */
1485 FT_Vector_From_Polar( &end
, stroker
->radius
, angle_out
+ rotate
);
1489 if ( stroker
->handle_wide_strokes
)
1495 /* determine whether the border radius is greater than the */
1496 /* radius of curvature of the original arc */
1497 start
= border
->points
[border
->num_points
- 1];
1499 alpha1
= FT_Atan2( end
.x
- start
.x
, end
.y
- start
.y
);
1501 /* is the direction of the border arc opposite to */
1502 /* that of the original arc? */
1503 if ( ft_pos_abs( FT_Angle_Diff( alpha0
, alpha1
) ) >
1506 FT_Angle beta
, gamma
;
1507 FT_Vector bvec
, delta
;
1508 FT_Fixed blen
, sinA
, sinB
, alen
;
1511 /* use the sine rule to find the intersection point */
1512 beta
= FT_Atan2( arc
[2].x
- start
.x
, arc
[2].y
- start
.y
);
1513 gamma
= FT_Atan2( arc
[0].x
- end
.x
, arc
[0].y
- end
.y
);
1515 bvec
.x
= end
.x
- start
.x
;
1516 bvec
.y
= end
.y
- start
.y
;
1518 blen
= FT_Vector_Length( &bvec
);
1520 sinA
= ft_pos_abs( FT_Sin( alpha1
- gamma
) );
1521 sinB
= ft_pos_abs( FT_Sin( beta
- gamma
) );
1523 alen
= FT_MulDiv( blen
, sinA
, sinB
);
1525 FT_Vector_From_Polar( &delta
, alen
, beta
);
1529 /* circumnavigate the negative sector backwards */
1530 border
->movable
= FALSE
;
1531 error
= ft_stroke_border_lineto( border
, &delta
, FALSE
);
1534 error
= ft_stroke_border_lineto( border
, &end
, FALSE
);
1537 error
= ft_stroke_border_conicto( border
, &ctrl
, &start
);
1540 /* and then move to the endpoint */
1541 error
= ft_stroke_border_lineto( border
, &end
, FALSE
);
1548 /* else fall through */
1551 /* simply add an arc */
1552 error
= ft_stroke_border_conicto( border
, &ctrl
, &end
);
1560 stroker
->angle_in
= angle_out
;
1563 stroker
->center
= *to
;
1570 /* documentation is in ftstroke.h */
1572 FT_EXPORT_DEF( FT_Error
)
1573 FT_Stroker_CubicTo( FT_Stroker stroker
,
1574 FT_Vector
* control1
,
1575 FT_Vector
* control2
,
1578 FT_Error error
= FT_Err_Ok
;
1579 FT_Vector bez_stack
[37];
1581 FT_Vector
* limit
= bez_stack
+ 32;
1582 FT_Bool first_arc
= TRUE
;
1585 if ( !stroker
|| !control1
|| !control2
|| !to
)
1587 error
= FT_THROW( Invalid_Argument
);
1591 /* if all control points are coincident, this is a no-op; */
1592 /* avoid creating a spurious corner */
1593 if ( FT_IS_SMALL( stroker
->center
.x
- control1
->x
) &&
1594 FT_IS_SMALL( stroker
->center
.y
- control1
->y
) &&
1595 FT_IS_SMALL( control1
->x
- control2
->x
) &&
1596 FT_IS_SMALL( control1
->y
- control2
->y
) &&
1597 FT_IS_SMALL( control2
->x
- to
->x
) &&
1598 FT_IS_SMALL( control2
->y
- to
->y
) )
1600 stroker
->center
= *to
;
1608 arc
[3] = stroker
->center
;
1610 while ( arc
>= bez_stack
)
1612 FT_Angle angle_in
, angle_mid
, angle_out
;
1615 /* initialize with current direction */
1616 angle_in
= angle_out
= angle_mid
= stroker
->angle_in
;
1619 !ft_cubic_is_small_enough( arc
, &angle_in
,
1620 &angle_mid
, &angle_out
) )
1622 if ( stroker
->first_point
)
1623 stroker
->angle_in
= angle_in
;
1625 ft_cubic_split( arc
);
1634 /* process corner if necessary */
1635 if ( stroker
->first_point
)
1636 error
= ft_stroker_subpath_start( stroker
, angle_in
, 0 );
1639 stroker
->angle_out
= angle_in
;
1640 error
= ft_stroker_process_corner( stroker
, 0 );
1643 else if ( ft_pos_abs( FT_Angle_Diff( stroker
->angle_in
, angle_in
) ) >
1644 FT_SMALL_CUBIC_THRESHOLD
/ 4 )
1646 /* if the deviation from one arc to the next is too great, */
1647 /* add a round corner */
1648 stroker
->center
= arc
[3];
1649 stroker
->angle_out
= angle_in
;
1650 stroker
->line_join
= FT_STROKER_LINEJOIN_ROUND
;
1652 error
= ft_stroker_process_corner( stroker
, 0 );
1654 /* reinstate line join style */
1655 stroker
->line_join
= stroker
->line_join_saved
;
1661 /* the arc's angle is small enough; we can add it directly to each */
1664 FT_Vector ctrl1
, ctrl2
, end
;
1665 FT_Angle theta1
, phi1
, theta2
, phi2
, rotate
, alpha0
= 0;
1666 FT_Fixed length1
, length2
;
1667 FT_StrokeBorder border
;
1671 theta1
= FT_Angle_Diff( angle_in
, angle_mid
) / 2;
1672 theta2
= FT_Angle_Diff( angle_mid
, angle_out
) / 2;
1673 phi1
= ft_angle_mean( angle_in
, angle_mid
);
1674 phi2
= ft_angle_mean( angle_mid
, angle_out
);
1675 length1
= FT_DivFix( stroker
->radius
, FT_Cos( theta1
) );
1676 length2
= FT_DivFix( stroker
->radius
, FT_Cos( theta2
) );
1678 /* compute direction of original arc */
1679 if ( stroker
->handle_wide_strokes
)
1680 alpha0
= FT_Atan2( arc
[0].x
- arc
[3].x
, arc
[0].y
- arc
[3].y
);
1682 for ( border
= stroker
->borders
, side
= 0;
1686 rotate
= FT_SIDE_TO_ROTATE( side
);
1688 /* compute control points */
1689 FT_Vector_From_Polar( &ctrl1
, length1
, phi1
+ rotate
);
1690 ctrl1
.x
+= arc
[2].x
;
1691 ctrl1
.y
+= arc
[2].y
;
1693 FT_Vector_From_Polar( &ctrl2
, length2
, phi2
+ rotate
);
1694 ctrl2
.x
+= arc
[1].x
;
1695 ctrl2
.y
+= arc
[1].y
;
1697 /* compute end point */
1698 FT_Vector_From_Polar( &end
, stroker
->radius
, angle_out
+ rotate
);
1702 if ( stroker
->handle_wide_strokes
)
1708 /* determine whether the border radius is greater than the */
1709 /* radius of curvature of the original arc */
1710 start
= border
->points
[border
->num_points
- 1];
1712 alpha1
= FT_Atan2( end
.x
- start
.x
, end
.y
- start
.y
);
1714 /* is the direction of the border arc opposite to */
1715 /* that of the original arc? */
1716 if ( ft_pos_abs( FT_Angle_Diff( alpha0
, alpha1
) ) >
1719 FT_Angle beta
, gamma
;
1720 FT_Vector bvec
, delta
;
1721 FT_Fixed blen
, sinA
, sinB
, alen
;
1724 /* use the sine rule to find the intersection point */
1725 beta
= FT_Atan2( arc
[3].x
- start
.x
, arc
[3].y
- start
.y
);
1726 gamma
= FT_Atan2( arc
[0].x
- end
.x
, arc
[0].y
- end
.y
);
1728 bvec
.x
= end
.x
- start
.x
;
1729 bvec
.y
= end
.y
- start
.y
;
1731 blen
= FT_Vector_Length( &bvec
);
1733 sinA
= ft_pos_abs( FT_Sin( alpha1
- gamma
) );
1734 sinB
= ft_pos_abs( FT_Sin( beta
- gamma
) );
1736 alen
= FT_MulDiv( blen
, sinA
, sinB
);
1738 FT_Vector_From_Polar( &delta
, alen
, beta
);
1742 /* circumnavigate the negative sector backwards */
1743 border
->movable
= FALSE
;
1744 error
= ft_stroke_border_lineto( border
, &delta
, FALSE
);
1747 error
= ft_stroke_border_lineto( border
, &end
, FALSE
);
1750 error
= ft_stroke_border_cubicto( border
,
1756 /* and then move to the endpoint */
1757 error
= ft_stroke_border_lineto( border
, &end
, FALSE
);
1764 /* else fall through */
1767 /* simply add an arc */
1768 error
= ft_stroke_border_cubicto( border
, &ctrl1
, &ctrl2
, &end
);
1776 stroker
->angle_in
= angle_out
;
1779 stroker
->center
= *to
;
1786 /* documentation is in ftstroke.h */
1788 FT_EXPORT_DEF( FT_Error
)
1789 FT_Stroker_BeginSubPath( FT_Stroker stroker
,
1793 if ( !stroker
|| !to
)
1794 return FT_THROW( Invalid_Argument
);
1796 /* We cannot process the first point, because there is not enough */
1797 /* information regarding its corner/cap. The latter will be processed */
1798 /* in the `FT_Stroker_EndSubPath' routine. */
1800 stroker
->first_point
= TRUE
;
1801 stroker
->center
= *to
;
1802 stroker
->subpath_open
= open
;
1804 /* Determine if we need to check whether the border radius is greater */
1805 /* than the radius of curvature of a curve, to handle this case */
1806 /* specially. This is only required if bevel joins or butt caps may */
1807 /* be created, because round & miter joins and round & square caps */
1808 /* cover the negative sector created with wide strokes. */
1809 stroker
->handle_wide_strokes
=
1810 FT_BOOL( stroker
->line_join
!= FT_STROKER_LINEJOIN_ROUND
||
1811 ( stroker
->subpath_open
&&
1812 stroker
->line_cap
== FT_STROKER_LINECAP_BUTT
) );
1814 /* record the subpath start point for each border */
1815 stroker
->subpath_start
= *to
;
1817 stroker
->angle_in
= 0;
1824 ft_stroker_add_reverse_left( FT_Stroker stroker
,
1827 FT_StrokeBorder right
= stroker
->borders
+ 0;
1828 FT_StrokeBorder left
= stroker
->borders
+ 1;
1830 FT_Error error
= FT_Err_Ok
;
1833 FT_ASSERT( left
->start
>= 0 );
1835 new_points
= (FT_Int
)left
->num_points
- left
->start
;
1836 if ( new_points
> 0 )
1838 error
= ft_stroke_border_grow( right
, (FT_UInt
)new_points
);
1843 FT_Vector
* dst_point
= right
->points
+ right
->num_points
;
1844 FT_Byte
* dst_tag
= right
->tags
+ right
->num_points
;
1845 FT_Vector
* src_point
= left
->points
+ left
->num_points
- 1;
1846 FT_Byte
* src_tag
= left
->tags
+ left
->num_points
- 1;
1849 while ( src_point
>= left
->points
+ left
->start
)
1851 *dst_point
= *src_point
;
1852 *dst_tag
= *src_tag
;
1855 dst_tag
[0] &= ~FT_STROKE_TAG_BEGIN_END
;
1859 (FT_Byte
)( dst_tag
[0] & FT_STROKE_TAG_BEGIN_END
);
1862 /* switch begin/end tags if necessary */
1863 if ( ttag
== FT_STROKE_TAG_BEGIN
||
1864 ttag
== FT_STROKE_TAG_END
)
1865 dst_tag
[0] ^= FT_STROKE_TAG_BEGIN_END
;
1875 left
->num_points
= (FT_UInt
)left
->start
;
1876 right
->num_points
+= (FT_UInt
)new_points
;
1878 right
->movable
= FALSE
;
1879 left
->movable
= FALSE
;
1887 /* documentation is in ftstroke.h */
1889 /* there's a lot of magic in this function! */
1890 FT_EXPORT_DEF( FT_Error
)
1891 FT_Stroker_EndSubPath( FT_Stroker stroker
)
1893 FT_Error error
= FT_Err_Ok
;
1898 error
= FT_THROW( Invalid_Argument
);
1902 if ( stroker
->subpath_open
)
1904 FT_StrokeBorder right
= stroker
->borders
;
1907 /* All right, this is an opened path, we need to add a cap between */
1908 /* right & left, add the reverse of left, then add a final cap */
1909 /* between left & right. */
1910 error
= ft_stroker_cap( stroker
, stroker
->angle_in
, 0 );
1914 /* add reversed points from `left' to `right' */
1915 error
= ft_stroker_add_reverse_left( stroker
, TRUE
);
1919 /* now add the final cap */
1920 stroker
->center
= stroker
->subpath_start
;
1921 error
= ft_stroker_cap( stroker
,
1922 stroker
->subpath_angle
+ FT_ANGLE_PI
, 0 );
1926 /* Now end the right subpath accordingly. The left one is */
1927 /* rewind and doesn't need further processing. */
1928 ft_stroke_border_close( right
, FALSE
);
1936 /* close the path if needed */
1937 if ( stroker
->center
.x
!= stroker
->subpath_start
.x
||
1938 stroker
->center
.y
!= stroker
->subpath_start
.y
)
1940 error
= FT_Stroker_LineTo( stroker
, &stroker
->subpath_start
);
1945 /* process the corner */
1946 stroker
->angle_out
= stroker
->subpath_angle
;
1947 turn
= FT_Angle_Diff( stroker
->angle_in
,
1948 stroker
->angle_out
);
1950 /* no specific corner processing is required if the turn is 0 */
1953 /* when we turn to the right, the inside side is 0 */
1954 /* otherwise, the inside side is 1 */
1955 inside_side
= ( turn
< 0 );
1957 error
= ft_stroker_inside( stroker
,
1959 stroker
->subpath_line_length
);
1963 /* process the outside side */
1964 error
= ft_stroker_outside( stroker
,
1966 stroker
->subpath_line_length
);
1971 /* then end our two subpaths */
1972 ft_stroke_border_close( stroker
->borders
+ 0, FALSE
);
1973 ft_stroke_border_close( stroker
->borders
+ 1, TRUE
);
1981 /* documentation is in ftstroke.h */
1983 FT_EXPORT_DEF( FT_Error
)
1984 FT_Stroker_GetBorderCounts( FT_Stroker stroker
,
1985 FT_StrokerBorder border
,
1986 FT_UInt
*anum_points
,
1987 FT_UInt
*anum_contours
)
1989 FT_UInt num_points
= 0, num_contours
= 0;
1993 if ( !stroker
|| border
> 1 )
1995 error
= FT_THROW( Invalid_Argument
);
1999 error
= ft_stroke_border_get_counts( stroker
->borders
+ border
,
2000 &num_points
, &num_contours
);
2003 *anum_points
= num_points
;
2005 if ( anum_contours
)
2006 *anum_contours
= num_contours
;
2012 /* documentation is in ftstroke.h */
2014 FT_EXPORT_DEF( FT_Error
)
2015 FT_Stroker_GetCounts( FT_Stroker stroker
,
2016 FT_UInt
*anum_points
,
2017 FT_UInt
*anum_contours
)
2019 FT_UInt count1
, count2
, num_points
= 0;
2020 FT_UInt count3
, count4
, num_contours
= 0;
2026 error
= FT_THROW( Invalid_Argument
);
2030 error
= ft_stroke_border_get_counts( stroker
->borders
+ 0,
2035 error
= ft_stroke_border_get_counts( stroker
->borders
+ 1,
2040 num_points
= count1
+ count3
;
2041 num_contours
= count2
+ count4
;
2045 *anum_points
= num_points
;
2047 if ( anum_contours
)
2048 *anum_contours
= num_contours
;
2054 /* documentation is in ftstroke.h */
2056 FT_EXPORT_DEF( void )
2057 FT_Stroker_ExportBorder( FT_Stroker stroker
,
2058 FT_StrokerBorder border
,
2059 FT_Outline
* outline
)
2061 if ( !stroker
|| !outline
)
2064 if ( border
== FT_STROKER_BORDER_LEFT
||
2065 border
== FT_STROKER_BORDER_RIGHT
)
2067 FT_StrokeBorder sborder
= & stroker
->borders
[border
];
2070 if ( sborder
->valid
)
2071 ft_stroke_border_export( sborder
, outline
);
2076 /* documentation is in ftstroke.h */
2078 FT_EXPORT_DEF( void )
2079 FT_Stroker_Export( FT_Stroker stroker
,
2080 FT_Outline
* outline
)
2082 FT_Stroker_ExportBorder( stroker
, FT_STROKER_BORDER_LEFT
, outline
);
2083 FT_Stroker_ExportBorder( stroker
, FT_STROKER_BORDER_RIGHT
, outline
);
2087 /* documentation is in ftstroke.h */
2090 * The following is very similar to FT_Outline_Decompose, except
2091 * that we do support opened paths, and do not scale the outline.
2093 FT_EXPORT_DEF( FT_Error
)
2094 FT_Stroker_ParseOutline( FT_Stroker stroker
,
2095 FT_Outline
* outline
,
2099 FT_Vector v_control
;
2108 FT_Int n
; /* index of contour in outline */
2109 FT_UInt first
; /* index of first point in contour */
2110 FT_Int tag
; /* current point's state */
2114 return FT_THROW( Invalid_Outline
);
2117 return FT_THROW( Invalid_Argument
);
2119 FT_Stroker_Rewind( stroker
);
2123 for ( n
= 0; n
< outline
->n_contours
; n
++ )
2125 FT_UInt last
; /* index of last point in contour */
2128 last
= (FT_UInt
)outline
->contours
[n
];
2129 limit
= outline
->points
+ last
;
2131 /* skip empty points; we don't stroke these */
2132 if ( last
<= first
)
2138 v_start
= outline
->points
[first
];
2139 v_last
= outline
->points
[last
];
2141 v_control
= v_start
;
2143 point
= outline
->points
+ first
;
2144 tags
= outline
->tags
+ first
;
2145 tag
= FT_CURVE_TAG( tags
[0] );
2147 /* A contour cannot start with a cubic control point! */
2148 if ( tag
== FT_CURVE_TAG_CUBIC
)
2149 goto Invalid_Outline
;
2151 /* check first point to determine origin */
2152 if ( tag
== FT_CURVE_TAG_CONIC
)
2154 /* First point is conic control. Yes, this happens. */
2155 if ( FT_CURVE_TAG( outline
->tags
[last
] ) == FT_CURVE_TAG_ON
)
2157 /* start at last point if it is on the curve */
2163 /* if both first and last points are conic, */
2164 /* start at their middle */
2165 v_start
.x
= ( v_start
.x
+ v_last
.x
) / 2;
2166 v_start
.y
= ( v_start
.y
+ v_last
.y
) / 2;
2172 error
= FT_Stroker_BeginSubPath( stroker
, &v_start
, opened
);
2176 while ( point
< limit
)
2181 tag
= FT_CURVE_TAG( tags
[0] );
2184 case FT_CURVE_TAG_ON
: /* emit a single line_to */
2192 error
= FT_Stroker_LineTo( stroker
, &vec
);
2198 case FT_CURVE_TAG_CONIC
: /* consume conic arcs */
2199 v_control
.x
= point
->x
;
2200 v_control
.y
= point
->y
;
2203 if ( point
< limit
)
2211 tag
= FT_CURVE_TAG( tags
[0] );
2215 if ( tag
== FT_CURVE_TAG_ON
)
2217 error
= FT_Stroker_ConicTo( stroker
, &v_control
, &vec
);
2223 if ( tag
!= FT_CURVE_TAG_CONIC
)
2224 goto Invalid_Outline
;
2226 v_middle
.x
= ( v_control
.x
+ vec
.x
) / 2;
2227 v_middle
.y
= ( v_control
.y
+ vec
.y
) / 2;
2229 error
= FT_Stroker_ConicTo( stroker
, &v_control
, &v_middle
);
2237 error
= FT_Stroker_ConicTo( stroker
, &v_control
, &v_start
);
2240 default: /* FT_CURVE_TAG_CUBIC */
2242 FT_Vector vec1
, vec2
;
2245 if ( point
+ 1 > limit
||
2246 FT_CURVE_TAG( tags
[1] ) != FT_CURVE_TAG_CUBIC
)
2247 goto Invalid_Outline
;
2255 if ( point
<= limit
)
2262 error
= FT_Stroker_CubicTo( stroker
, &vec1
, &vec2
, &vec
);
2268 error
= FT_Stroker_CubicTo( stroker
, &vec1
, &vec2
, &v_start
);
2278 /* don't try to end the path if no segments have been generated */
2279 if ( !stroker
->first_point
)
2281 error
= FT_Stroker_EndSubPath( stroker
);
2295 return FT_THROW( Invalid_Outline
);
2299 /* documentation is in ftstroke.h */
2301 FT_EXPORT_DEF( FT_Error
)
2302 FT_Glyph_Stroke( FT_Glyph
*pglyph
,
2306 FT_Error error
= FT_ERR( Invalid_Argument
);
2307 FT_Glyph glyph
= NULL
;
2309 /* for FT_OUTLINE_GLYPH_CLASS_GET (in PIC mode) */
2310 FT_Library library
= stroker
->library
;
2312 FT_UNUSED( library
);
2319 if ( !glyph
|| glyph
->clazz
!= FT_OUTLINE_GLYPH_CLASS_GET
)
2326 error
= FT_Glyph_Copy( glyph
, ©
);
2334 FT_OutlineGlyph oglyph
= (FT_OutlineGlyph
)glyph
;
2335 FT_Outline
* outline
= &oglyph
->outline
;
2336 FT_UInt num_points
, num_contours
;
2339 error
= FT_Stroker_ParseOutline( stroker
, outline
, FALSE
);
2343 FT_Stroker_GetCounts( stroker
, &num_points
, &num_contours
);
2345 FT_Outline_Done( glyph
->library
, outline
);
2347 error
= FT_Outline_New( glyph
->library
,
2349 (FT_Int
)num_contours
,
2354 outline
->n_points
= 0;
2355 outline
->n_contours
= 0;
2357 FT_Stroker_Export( stroker
, outline
);
2361 FT_Done_Glyph( *pglyph
);
2367 FT_Done_Glyph( glyph
);
2378 /* documentation is in ftstroke.h */
2380 FT_EXPORT_DEF( FT_Error
)
2381 FT_Glyph_StrokeBorder( FT_Glyph
*pglyph
,
2386 FT_Error error
= FT_ERR( Invalid_Argument
);
2387 FT_Glyph glyph
= NULL
;
2389 /* for FT_OUTLINE_GLYPH_CLASS_GET (in PIC mode) */
2390 FT_Library library
= stroker
->library
;
2392 FT_UNUSED( library
);
2399 if ( !glyph
|| glyph
->clazz
!= FT_OUTLINE_GLYPH_CLASS_GET
)
2406 error
= FT_Glyph_Copy( glyph
, ©
);
2414 FT_OutlineGlyph oglyph
= (FT_OutlineGlyph
)glyph
;
2415 FT_StrokerBorder border
;
2416 FT_Outline
* outline
= &oglyph
->outline
;
2417 FT_UInt num_points
, num_contours
;
2420 border
= FT_Outline_GetOutsideBorder( outline
);
2423 if ( border
== FT_STROKER_BORDER_LEFT
)
2424 border
= FT_STROKER_BORDER_RIGHT
;
2426 border
= FT_STROKER_BORDER_LEFT
;
2429 error
= FT_Stroker_ParseOutline( stroker
, outline
, FALSE
);
2433 FT_Stroker_GetBorderCounts( stroker
, border
,
2434 &num_points
, &num_contours
);
2436 FT_Outline_Done( glyph
->library
, outline
);
2438 error
= FT_Outline_New( glyph
->library
,
2440 (FT_Int
)num_contours
,
2445 outline
->n_points
= 0;
2446 outline
->n_contours
= 0;
2448 FT_Stroker_ExportBorder( stroker
, border
, outline
);
2452 FT_Done_Glyph( *pglyph
);
2458 FT_Done_Glyph( glyph
);