1
/***************************************************************************/
5
/* FreeType path stroker (body). */
7
/* Copyright 2002, 2003, 2004 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
27
FT_EXPORT_DEF( FT_StrokerBorder )
28
FT_Outline_GetInsideBorder( FT_Outline* outline )
30
FT_Orientation o = FT_Outline_Get_Orientation( outline );
33
return o == FT_ORIENTATION_TRUETYPE ? FT_STROKER_BORDER_RIGHT
34
: FT_STROKER_BORDER_LEFT ;
38
FT_EXPORT_DEF( FT_StrokerBorder )
39
FT_Outline_GetOutsideBorder( FT_Outline* outline )
41
FT_Orientation o = FT_Outline_Get_Orientation( outline );
44
return o == FT_ORIENTATION_TRUETYPE ? FT_STROKER_BORDER_LEFT
45
: FT_STROKER_BORDER_RIGHT ;
49
/***************************************************************************/
50
/***************************************************************************/
52
/***** BEZIER COMPUTATIONS *****/
54
/***************************************************************************/
55
/***************************************************************************/
57
#define FT_SMALL_CONIC_THRESHOLD ( FT_ANGLE_PI / 6 )
58
#define FT_SMALL_CUBIC_THRESHOLD ( FT_ANGLE_PI / 6 )
61
#define FT_IS_SMALL( x ) ( (x) > -FT_EPSILON && (x) < FT_EPSILON )
65
ft_pos_abs( FT_Pos x )
67
return x >= 0 ? x : -x ;
72
ft_conic_split( FT_Vector* base )
77
base[4].x = base[2].x;
79
a = base[3].x = ( base[2].x + b ) / 2;
80
b = base[1].x = ( base[0].x + b ) / 2;
81
base[2].x = ( a + b ) / 2;
83
base[4].y = base[2].y;
85
a = base[3].y = ( base[2].y + b ) / 2;
86
b = base[1].y = ( base[0].y + b ) / 2;
87
base[2].y = ( a + b ) / 2;
92
ft_conic_is_small_enough( FT_Vector* base,
98
FT_Int close1, close2;
101
d1.x = base[1].x - base[2].x;
102
d1.y = base[1].y - base[2].y;
103
d2.x = base[0].x - base[1].x;
104
d2.y = base[0].y - base[1].y;
106
close1 = FT_IS_SMALL( d1.x ) && FT_IS_SMALL( d1.y );
107
close2 = FT_IS_SMALL( d2.x ) && FT_IS_SMALL( d2.y );
112
*angle_in = *angle_out = 0;
114
*angle_in = *angle_out = FT_Atan2( d2.x, d2.y );
118
*angle_in = *angle_out = FT_Atan2( d1.x, d1.y );
122
*angle_in = FT_Atan2( d1.x, d1.y );
123
*angle_out = FT_Atan2( d2.x, d2.y );
126
theta = ft_pos_abs( FT_Angle_Diff( *angle_in, *angle_out ) );
128
return FT_BOOL( theta < FT_SMALL_CONIC_THRESHOLD );
133
ft_cubic_split( FT_Vector* base )
138
base[6].x = base[3].x;
141
base[1].x = a = ( base[0].x + c ) / 2;
142
base[5].x = b = ( base[3].x + d ) / 2;
144
base[2].x = a = ( a + c ) / 2;
145
base[4].x = b = ( b + c ) / 2;
146
base[3].x = ( a + b ) / 2;
148
base[6].y = base[3].y;
151
base[1].y = a = ( base[0].y + c ) / 2;
152
base[5].y = b = ( base[3].y + d ) / 2;
154
base[2].y = a = ( a + c ) / 2;
155
base[4].y = b = ( b + c ) / 2;
156
base[3].y = ( a + b ) / 2;
161
ft_cubic_is_small_enough( FT_Vector* base,
164
FT_Angle *angle_out )
166
FT_Vector d1, d2, d3;
167
FT_Angle theta1, theta2;
168
FT_Int close1, close2, close3;
171
d1.x = base[2].x - base[3].x;
172
d1.y = base[2].y - base[3].y;
173
d2.x = base[1].x - base[2].x;
174
d2.y = base[1].y - base[2].y;
175
d3.x = base[0].x - base[1].x;
176
d3.y = base[0].y - base[1].y;
178
close1 = FT_IS_SMALL( d1.x ) && FT_IS_SMALL( d1.y );
179
close2 = FT_IS_SMALL( d2.x ) && FT_IS_SMALL( d2.y );
180
close3 = FT_IS_SMALL( d3.x ) && FT_IS_SMALL( d3.y );
182
if ( close1 || close3 )
186
/* basically a point */
187
*angle_in = *angle_out = *angle_mid = 0;
191
*angle_in = *angle_mid = FT_Atan2( d2.x, d2.y );
192
*angle_out = FT_Atan2( d3.x, d3.y );
196
*angle_in = FT_Atan2( d1.x, d1.y );
197
*angle_mid = *angle_out = FT_Atan2( d2.x, d2.y );
202
*angle_in = *angle_mid = FT_Atan2( d1.x, d1.y );
203
*angle_out = FT_Atan2( d3.x, d3.y );
207
*angle_in = FT_Atan2( d1.x, d1.y );
208
*angle_mid = FT_Atan2( d2.x, d2.y );
209
*angle_out = FT_Atan2( d3.x, d3.y );
212
theta1 = ft_pos_abs( FT_Angle_Diff( *angle_in, *angle_mid ) );
213
theta2 = ft_pos_abs( FT_Angle_Diff( *angle_mid, *angle_out ) );
215
return FT_BOOL( theta1 < FT_SMALL_CUBIC_THRESHOLD &&
216
theta2 < FT_SMALL_CUBIC_THRESHOLD );
220
/***************************************************************************/
221
/***************************************************************************/
223
/***** STROKE BORDERS *****/
225
/***************************************************************************/
226
/***************************************************************************/
230
FT_STROKE_TAG_ON = 1, /* on-curve point */
231
FT_STROKE_TAG_CUBIC = 2, /* cubic off-point */
232
FT_STROKE_TAG_BEGIN = 4, /* sub-path start */
233
FT_STROKE_TAG_END = 8 /* sub-path end */
238
typedef struct FT_StrokeBorderRec_
245
FT_Int start; /* index of current sub-path start point */
249
} FT_StrokeBorderRec, *FT_StrokeBorder;
253
ft_stroke_border_grow( FT_StrokeBorder border,
256
FT_UInt old_max = border->max_points;
257
FT_UInt new_max = border->num_points + new_points;
261
if ( new_max > old_max )
263
FT_UInt cur_max = old_max;
264
FT_Memory memory = border->memory;
267
while ( cur_max < new_max )
268
cur_max += ( cur_max >> 1 ) + 16;
270
if ( FT_RENEW_ARRAY( border->points, old_max, cur_max ) ||
271
FT_RENEW_ARRAY( border->tags, old_max, cur_max ) )
274
border->max_points = cur_max;
282
ft_stroke_border_close( FT_StrokeBorder border )
284
FT_ASSERT( border->start >= 0 );
286
/* don't record empty paths! */
287
if ( border->num_points > (FT_UInt)border->start )
289
border->tags[border->start ] |= FT_STROKE_TAG_BEGIN;
290
border->tags[border->num_points - 1] |= FT_STROKE_TAG_END;
299
ft_stroke_border_lineto( FT_StrokeBorder border,
306
FT_ASSERT( border->start >= 0 );
308
if ( border->movable )
310
/* move last point */
311
border->points[border->num_points - 1] = *to;
316
error = ft_stroke_border_grow( border, 1 );
319
FT_Vector* vec = border->points + border->num_points;
320
FT_Byte* tag = border->tags + border->num_points;
324
tag[0] = FT_STROKE_TAG_ON;
326
border->num_points += 1;
329
border->movable = movable;
335
ft_stroke_border_conicto( FT_StrokeBorder border,
342
FT_ASSERT( border->start >= 0 );
344
error = ft_stroke_border_grow( border, 2 );
347
FT_Vector* vec = border->points + border->num_points;
348
FT_Byte* tag = border->tags + border->num_points;
354
tag[1] = FT_STROKE_TAG_ON;
356
border->num_points += 2;
364
ft_stroke_border_cubicto( FT_StrokeBorder border,
372
FT_ASSERT( border->start >= 0 );
374
error = ft_stroke_border_grow( border, 3 );
377
FT_Vector* vec = border->points + border->num_points;
378
FT_Byte* tag = border->tags + border->num_points;
385
tag[0] = FT_STROKE_TAG_CUBIC;
386
tag[1] = FT_STROKE_TAG_CUBIC;
387
tag[2] = FT_STROKE_TAG_ON;
389
border->num_points += 3;
396
#define FT_ARC_CUBIC_ANGLE ( FT_ANGLE_PI / 2 )
400
ft_stroke_border_arcto( FT_StrokeBorder border,
403
FT_Angle angle_start,
404
FT_Angle angle_diff )
406
FT_Angle total, angle, step, rotate, next, theta;
407
FT_Vector a, b, a2, b2;
412
/* compute start point */
413
FT_Vector_From_Polar( &a, radius, angle_start );
419
rotate = ( angle_diff >= 0 ) ? FT_ANGLE_PI2 : -FT_ANGLE_PI2;
424
if ( step > FT_ARC_CUBIC_ANGLE )
425
step = FT_ARC_CUBIC_ANGLE;
427
else if ( step < -FT_ARC_CUBIC_ANGLE )
428
step = -FT_ARC_CUBIC_ANGLE;
437
/* compute end point */
438
FT_Vector_From_Polar( &b, radius, next );
442
/* compute first and second control points */
443
length = FT_MulDiv( radius, FT_Sin( theta ) * 4,
444
( 0x10000L + FT_Cos( theta ) ) * 3 );
446
FT_Vector_From_Polar( &a2, length, angle + rotate );
450
FT_Vector_From_Polar( &b2, length, next - rotate );
455
error = ft_stroke_border_cubicto( border, &a2, &b2, &b );
459
/* process the rest of the arc ?? */
470
ft_stroke_border_moveto( FT_StrokeBorder border,
473
/* close current open path if any ? */
474
if ( border->start >= 0 )
475
ft_stroke_border_close( border );
477
border->start = border->num_points;
480
return ft_stroke_border_lineto( border, to, 0 );
485
ft_stroke_border_init( FT_StrokeBorder border,
488
border->memory = memory;
489
border->points = NULL;
492
border->num_points = 0;
493
border->max_points = 0;
500
ft_stroke_border_reset( FT_StrokeBorder border )
502
border->num_points = 0;
509
ft_stroke_border_done( FT_StrokeBorder border )
511
FT_Memory memory = border->memory;
514
FT_FREE( border->points );
515
FT_FREE( border->tags );
517
border->num_points = 0;
518
border->max_points = 0;
525
ft_stroke_border_get_counts( FT_StrokeBorder border,
526
FT_UInt *anum_points,
527
FT_UInt *anum_contours )
530
FT_UInt num_points = 0;
531
FT_UInt num_contours = 0;
533
FT_UInt count = border->num_points;
534
FT_Vector* point = border->points;
535
FT_Byte* tags = border->tags;
536
FT_Int in_contour = 0;
539
for ( ; count > 0; count--, num_points++, point++, tags++ )
541
if ( tags[0] & FT_STROKE_TAG_BEGIN )
543
if ( in_contour != 0 )
548
else if ( in_contour == 0 )
551
if ( tags[0] & FT_STROKE_TAG_END )
553
if ( in_contour == 0 )
561
if ( in_contour != 0 )
567
*anum_points = num_points;
568
*anum_contours = num_contours;
579
ft_stroke_border_export( FT_StrokeBorder border,
580
FT_Outline* outline )
582
/* copy point locations */
583
FT_ARRAY_COPY( outline->points + outline->n_points,
585
border->num_points );
589
FT_UInt count = border->num_points;
590
FT_Byte* read = border->tags;
591
FT_Byte* write = (FT_Byte*)outline->tags + outline->n_points;
594
for ( ; count > 0; count--, read++, write++ )
596
if ( *read & FT_STROKE_TAG_ON )
597
*write = FT_CURVE_TAG_ON;
598
else if ( *read & FT_STROKE_TAG_CUBIC )
599
*write = FT_CURVE_TAG_CUBIC;
601
*write = FT_CURVE_TAG_CONIC;
607
FT_UInt count = border->num_points;
608
FT_Byte* tags = border->tags;
609
FT_Short* write = outline->contours + outline->n_contours;
610
FT_Short idx = (FT_Short)outline->n_points;
613
for ( ; count > 0; count--, tags++, idx++ )
615
if ( *tags & FT_STROKE_TAG_END )
618
outline->n_contours++;
623
outline->n_points = (short)( outline->n_points + border->num_points );
625
FT_ASSERT( FT_Outline_Check( outline ) == 0 );
629
/***************************************************************************/
630
/***************************************************************************/
632
/***** STROKER *****/
634
/***************************************************************************/
635
/***************************************************************************/
637
#define FT_SIDE_TO_ROTATE( s ) ( FT_ANGLE_PI2 - (s) * FT_ANGLE_PI )
639
typedef struct FT_StrokerRec_
645
FT_Bool subpath_open;
646
FT_Angle subpath_angle;
647
FT_Vector subpath_start;
649
FT_Stroker_LineCap line_cap;
650
FT_Stroker_LineJoin line_join;
651
FT_Fixed miter_limit;
655
FT_StrokeBorderRec borders[2];
661
FT_EXPORT_DEF( FT_Error )
662
FT_Stroker_New( FT_Memory memory,
663
FT_Stroker *astroker )
669
if ( !FT_NEW( stroker ) )
671
stroker->memory = memory;
673
ft_stroke_border_init( &stroker->borders[0], memory );
674
ft_stroke_border_init( &stroker->borders[1], memory );
681
FT_EXPORT_DEF( void )
682
FT_Stroker_Set( FT_Stroker stroker,
684
FT_Stroker_LineCap line_cap,
685
FT_Stroker_LineJoin line_join,
686
FT_Fixed miter_limit )
688
stroker->radius = radius;
689
stroker->line_cap = line_cap;
690
stroker->line_join = line_join;
691
stroker->miter_limit = miter_limit;
693
FT_Stroker_Rewind( stroker );
697
FT_EXPORT_DEF( void )
698
FT_Stroker_Rewind( FT_Stroker stroker )
702
ft_stroke_border_reset( &stroker->borders[0] );
703
ft_stroke_border_reset( &stroker->borders[1] );
708
FT_EXPORT_DEF( void )
709
FT_Stroker_Done( FT_Stroker stroker )
713
FT_Memory memory = stroker->memory;
716
ft_stroke_border_done( &stroker->borders[0] );
717
ft_stroke_border_done( &stroker->borders[1] );
719
stroker->memory = NULL;
725
/* creates a circular arc at a corner or cap */
727
ft_stroker_arcto( FT_Stroker stroker,
730
FT_Angle total, rotate;
731
FT_Fixed radius = stroker->radius;
733
FT_StrokeBorder border = stroker->borders + side;
736
rotate = FT_SIDE_TO_ROTATE( side );
738
total = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
739
if ( total == FT_ANGLE_PI )
742
error = ft_stroke_border_arcto( border,
745
stroker->angle_in + rotate,
752
/* adds a cap at the end of an opened path */
754
ft_stroker_cap( FT_Stroker stroker,
761
if ( stroker->line_cap == FT_STROKER_LINECAP_ROUND )
763
/* add a round cap */
764
stroker->angle_in = angle;
765
stroker->angle_out = angle + FT_ANGLE_PI;
766
error = ft_stroker_arcto( stroker, side );
768
else if ( stroker->line_cap == FT_STROKER_LINECAP_SQUARE )
770
/* add a square cap */
771
FT_Vector delta, delta2;
772
FT_Angle rotate = FT_SIDE_TO_ROTATE( side );
773
FT_Fixed radius = stroker->radius;
774
FT_StrokeBorder border = stroker->borders + side;
777
FT_Vector_From_Polar( &delta2, radius, angle + rotate );
778
FT_Vector_From_Polar( &delta, radius, angle );
780
delta.x += stroker->center.x + delta2.x;
781
delta.y += stroker->center.y + delta2.y;
783
error = ft_stroke_border_lineto( border, &delta, 0 );
787
FT_Vector_From_Polar( &delta2, radius, angle - rotate );
788
FT_Vector_From_Polar( &delta, radius, angle );
790
delta.x += delta2.x + stroker->center.x;
791
delta.y += delta2.y + stroker->center.y;
793
error = ft_stroke_border_lineto( border, &delta, 0 );
801
/* process an inside corner, i.e. compute intersection */
803
ft_stroker_inside( FT_Stroker stroker,
806
FT_StrokeBorder border = stroker->borders + side;
807
FT_Angle phi, theta, rotate;
808
FT_Fixed length, thcos, sigma;
813
rotate = FT_SIDE_TO_ROTATE( side );
815
/* compute median angle */
816
theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
817
if ( theta == FT_ANGLE_PI )
822
phi = stroker->angle_in + theta;
824
thcos = FT_Cos( theta );
825
sigma = FT_MulFix( stroker->miter_limit, thcos );
827
if ( sigma < 0x10000L )
829
FT_Vector_From_Polar( &delta, stroker->radius,
830
stroker->angle_out + rotate );
831
delta.x += stroker->center.x;
832
delta.y += stroker->center.y;
837
length = FT_DivFix( stroker->radius, thcos );
839
FT_Vector_From_Polar( &delta, length, phi + rotate );
840
delta.x += stroker->center.x;
841
delta.y += stroker->center.y;
844
error = ft_stroke_border_lineto( border, &delta, 0 );
850
/* process an outside corner, i.e. compute bevel/miter/round */
852
ft_stroker_outside( FT_Stroker stroker,
855
FT_StrokeBorder border = stroker->borders + side;
860
if ( stroker->line_join == FT_STROKER_LINEJOIN_ROUND )
862
error = ft_stroker_arcto( stroker, side );
866
/* this is a mitered or beveled corner */
867
FT_Fixed sigma, radius = stroker->radius;
873
rotate = FT_SIDE_TO_ROTATE( side );
874
miter = FT_BOOL( stroker->line_join == FT_STROKER_LINEJOIN_MITER );
876
theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
877
if ( theta == FT_ANGLE_PI )
880
phi = stroker->angle_in;
885
phi = stroker->angle_in + theta + rotate;
888
thcos = FT_Cos( theta );
889
sigma = FT_MulFix( stroker->miter_limit, thcos );
891
if ( sigma >= 0x10000L )
894
if ( miter ) /* this is a miter (broken angle) */
896
FT_Vector middle, delta;
900
/* compute middle point */
901
FT_Vector_From_Polar( &middle,
902
FT_MulFix( radius, stroker->miter_limit ),
904
middle.x += stroker->center.x;
905
middle.y += stroker->center.y;
907
/* compute first angle point */
908
length = FT_MulFix( radius,
909
FT_DivFix( 0x10000L - sigma,
910
ft_pos_abs( FT_Sin( theta ) ) ) );
912
FT_Vector_From_Polar( &delta, length, phi + rotate );
916
error = ft_stroke_border_lineto( border, &delta, 0 );
920
/* compute second angle point */
921
FT_Vector_From_Polar( &delta, length, phi - rotate );
925
error = ft_stroke_border_lineto( border, &delta, 0 );
929
/* finally, add a movable end point */
930
FT_Vector_From_Polar( &delta, radius, stroker->angle_out + rotate );
931
delta.x += stroker->center.x;
932
delta.y += stroker->center.y;
934
error = ft_stroke_border_lineto( border, &delta, 1 );
937
else /* this is a bevel (intersection) */
943
length = FT_DivFix( stroker->radius, thcos );
945
FT_Vector_From_Polar( &delta, length, phi );
946
delta.x += stroker->center.x;
947
delta.y += stroker->center.y;
949
error = ft_stroke_border_lineto( border, &delta, 0 );
950
if (error) goto Exit;
952
/* now add end point */
953
FT_Vector_From_Polar( &delta, stroker->radius,
954
stroker->angle_out + rotate );
955
delta.x += stroker->center.x;
956
delta.y += stroker->center.y;
958
error = ft_stroke_border_lineto( border, &delta, 1 );
968
ft_stroker_process_corner( FT_Stroker stroker )
975
turn = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
977
/* no specific corner processing is required if the turn is 0 */
981
/* when we turn to the right, the inside side is 0 */
984
/* otherwise, the inside side is 1 */
988
/* process the inside side */
989
error = ft_stroker_inside( stroker, inside_side );
993
/* process the outside side */
994
error = ft_stroker_outside( stroker, 1 - inside_side );
1001
/* add two points to the left and right borders corresponding to the */
1002
/* start of the subpath.. */
1004
ft_stroker_subpath_start( FT_Stroker stroker,
1005
FT_Angle start_angle )
1010
FT_StrokeBorder border;
1013
FT_Vector_From_Polar( &delta, stroker->radius,
1014
start_angle + FT_ANGLE_PI2 );
1016
point.x = stroker->center.x + delta.x;
1017
point.y = stroker->center.y + delta.y;
1019
border = stroker->borders;
1020
error = ft_stroke_border_moveto( border, &point );
1024
point.x = stroker->center.x - delta.x;
1025
point.y = stroker->center.y - delta.y;
1028
error = ft_stroke_border_moveto( border, &point );
1030
/* save angle for last cap */
1031
stroker->subpath_angle = start_angle;
1032
stroker->first_point = 0;
1039
FT_EXPORT_DEF( FT_Error )
1040
FT_Stroker_LineTo( FT_Stroker stroker,
1044
FT_StrokeBorder border;
1049
delta.x = to->x - stroker->center.x;
1050
delta.y = to->y - stroker->center.y;
1052
angle = FT_Atan2( delta.x, delta.y );
1053
FT_Vector_From_Polar( &delta, stroker->radius, angle + FT_ANGLE_PI2 );
1055
/* process corner if necessary */
1056
if ( stroker->first_point )
1058
/* This is the first segment of a subpath. We need to */
1059
/* add a point to each border at their respective starting */
1060
/* point locations. */
1061
error = ft_stroker_subpath_start( stroker, angle );
1067
/* process the current corner */
1068
stroker->angle_out = angle;
1069
error = ft_stroker_process_corner( stroker );
1074
/* now add a line segment to both the "inside" and "outside" paths */
1076
for ( border = stroker->borders, side = 1; side >= 0; side--, border++ )
1081
point.x = to->x + delta.x;
1082
point.y = to->y + delta.y;
1084
error = ft_stroke_border_lineto( border, &point, 1 );
1092
stroker->angle_in = angle;
1093
stroker->center = *to;
1100
FT_EXPORT_DEF( FT_Error )
1101
FT_Stroker_ConicTo( FT_Stroker stroker,
1106
FT_Vector bez_stack[34];
1108
FT_Vector* limit = bez_stack + 30;
1109
FT_Angle start_angle;
1110
FT_Bool first_arc = 1;
1116
arc[2] = stroker->center;
1118
while ( arc >= bez_stack )
1120
FT_Angle angle_in, angle_out;
1123
angle_in = angle_out = 0; /* remove compiler warnings */
1126
!ft_conic_is_small_enough( arc, &angle_in, &angle_out ) )
1128
ft_conic_split( arc );
1137
start_angle = angle_in;
1139
/* process corner if necessary */
1140
if ( stroker->first_point )
1141
error = ft_stroker_subpath_start( stroker, start_angle );
1144
stroker->angle_out = start_angle;
1145
error = ft_stroker_process_corner( stroker );
1149
/* the arc's angle is small enough; we can add it directly to each */
1152
FT_Vector ctrl, end;
1153
FT_Angle theta, phi, rotate;
1158
theta = FT_Angle_Diff( angle_in, angle_out ) / 2;
1159
phi = angle_in + theta;
1160
length = FT_DivFix( stroker->radius, FT_Cos( theta ) );
1162
for ( side = 0; side <= 1; side++ )
1164
rotate = FT_SIDE_TO_ROTATE( side );
1166
/* compute control point */
1167
FT_Vector_From_Polar( &ctrl, length, phi + rotate );
1171
/* compute end point */
1172
FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate );
1176
error = ft_stroke_border_conicto( stroker->borders + side,
1185
if ( arc < bez_stack )
1186
stroker->angle_in = angle_out;
1189
stroker->center = *to;
1196
FT_EXPORT_DEF( FT_Error )
1197
FT_Stroker_CubicTo( FT_Stroker stroker,
1198
FT_Vector* control1,
1199
FT_Vector* control2,
1203
FT_Vector bez_stack[37];
1205
FT_Vector* limit = bez_stack + 32;
1206
FT_Angle start_angle;
1207
FT_Bool first_arc = 1;
1214
arc[3] = stroker->center;
1216
while ( arc >= bez_stack )
1218
FT_Angle angle_in, angle_mid, angle_out;
1221
/* remove compiler warnings */
1222
angle_in = angle_out = angle_mid = 0;
1225
!ft_cubic_is_small_enough( arc, &angle_in,
1226
&angle_mid, &angle_out ) )
1228
ft_cubic_split( arc );
1237
/* process corner if necessary */
1238
start_angle = angle_in;
1240
if ( stroker->first_point )
1241
error = ft_stroker_subpath_start( stroker, start_angle );
1244
stroker->angle_out = start_angle;
1245
error = ft_stroker_process_corner( stroker );
1251
/* the arc's angle is small enough; we can add it directly to each */
1254
FT_Vector ctrl1, ctrl2, end;
1255
FT_Angle theta1, phi1, theta2, phi2, rotate;
1256
FT_Fixed length1, length2;
1260
theta1 = ft_pos_abs( angle_mid - angle_in ) / 2;
1261
theta2 = ft_pos_abs( angle_out - angle_mid ) / 2;
1262
phi1 = (angle_mid + angle_in ) / 2;
1263
phi2 = (angle_mid + angle_out ) / 2;
1264
length1 = FT_DivFix( stroker->radius, FT_Cos( theta1 ) );
1265
length2 = FT_DivFix( stroker->radius, FT_Cos(theta2) );
1267
for ( side = 0; side <= 1; side++ )
1269
rotate = FT_SIDE_TO_ROTATE( side );
1271
/* compute control points */
1272
FT_Vector_From_Polar( &ctrl1, length1, phi1 + rotate );
1273
ctrl1.x += arc[2].x;
1274
ctrl1.y += arc[2].y;
1276
FT_Vector_From_Polar( &ctrl2, length2, phi2 + rotate );
1277
ctrl2.x += arc[1].x;
1278
ctrl2.y += arc[1].y;
1280
/* compute end point */
1281
FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate );
1285
error = ft_stroke_border_cubicto( stroker->borders + side,
1286
&ctrl1, &ctrl2, &end );
1293
if ( arc < bez_stack )
1294
stroker->angle_in = angle_out;
1297
stroker->center = *to;
1304
FT_EXPORT_DEF( FT_Error )
1305
FT_Stroker_BeginSubPath( FT_Stroker stroker,
1309
/* We cannot process the first point, because there is not enough */
1310
/* information regarding its corner/cap. The latter will be processed */
1311
/* in the "end_subpath" routine. */
1313
stroker->first_point = 1;
1314
stroker->center = *to;
1315
stroker->subpath_open = open;
1317
/* record the subpath start point index for each border */
1318
stroker->subpath_start = *to;
1324
ft_stroker_add_reverse_left( FT_Stroker stroker,
1327
FT_StrokeBorder right = stroker->borders + 0;
1328
FT_StrokeBorder left = stroker->borders + 1;
1333
FT_ASSERT( left->start >= 0 );
1335
new_points = left->num_points - left->start;
1336
if ( new_points > 0 )
1338
error = ft_stroke_border_grow( right, (FT_UInt)new_points );
1343
FT_Vector* dst_point = right->points + right->num_points;
1344
FT_Byte* dst_tag = right->tags + right->num_points;
1345
FT_Vector* src_point = left->points + left->num_points - 1;
1346
FT_Byte* src_tag = left->tags + left->num_points - 1;
1348
while ( src_point >= left->points + left->start )
1350
*dst_point = *src_point;
1351
*dst_tag = *src_tag;
1354
dst_tag[0] &= ~( FT_STROKE_TAG_BEGIN | FT_STROKE_TAG_END );
1357
/* switch begin/end tags if necessary.. */
1358
if ( dst_tag[0] & ( FT_STROKE_TAG_BEGIN | FT_STROKE_TAG_END ) )
1359
dst_tag[0] ^= ( FT_STROKE_TAG_BEGIN | FT_STROKE_TAG_END );
1369
left->num_points = left->start;
1370
right->num_points += new_points;
1381
/* there's a lot of magic in this function! */
1382
FT_EXPORT_DEF( FT_Error )
1383
FT_Stroker_EndSubPath( FT_Stroker stroker )
1387
if ( stroker->subpath_open )
1389
FT_StrokeBorder right = stroker->borders;
1391
/* All right, this is an opened path, we need to add a cap between */
1392
/* right & left, add the reverse of left, then add a final cap */
1393
/* between left & right. */
1394
error = ft_stroker_cap( stroker, stroker->angle_in, 0 );
1398
/* add reversed points from "left" to "right" */
1399
error = ft_stroker_add_reverse_left( stroker, 1 );
1403
/* now add the final cap */
1404
stroker->center = stroker->subpath_start;
1405
error = ft_stroker_cap( stroker,
1406
stroker->subpath_angle + FT_ANGLE_PI, 0 );
1410
/* Now end the right subpath accordingly. The left one is */
1411
/* rewind and doesn't need further processing. */
1412
ft_stroke_border_close( right );
1419
/* close the path if needed */
1420
if ( stroker->center.x != stroker->subpath_start.x ||
1421
stroker->center.y != stroker->subpath_start.y )
1423
error = FT_Stroker_LineTo( stroker, &stroker->subpath_start );
1428
/* process the corner */
1429
stroker->angle_out = stroker->subpath_angle;
1430
turn = FT_Angle_Diff( stroker->angle_in,
1431
stroker->angle_out );
1433
/* no specific corner processing is required if the turn is 0 */
1436
/* when we turn to the right, the inside side is 0 */
1439
/* otherwise, the inside side is 1 */
1443
/* IMPORTANT: WE DO NOT PROCESS THE INSIDE BORDER HERE! */
1444
/* process the inside side */
1445
/* error = ft_stroker_inside( stroker, inside_side ); */
1449
/* process the outside side */
1450
error = ft_stroker_outside( stroker, 1 - inside_side );
1455
/* then end our two subpaths */
1456
ft_stroke_border_close( stroker->borders + 0 );
1457
ft_stroke_border_close( stroker->borders + 1 );
1465
FT_EXPORT_DEF( FT_Error )
1466
FT_Stroker_GetBorderCounts( FT_Stroker stroker,
1467
FT_StrokerBorder border,
1468
FT_UInt *anum_points,
1469
FT_UInt *anum_contours )
1471
FT_UInt num_points = 0, num_contours = 0;
1475
if ( !stroker || border > 1 )
1477
error = FT_Err_Invalid_Argument;
1481
error = ft_stroke_border_get_counts( stroker->borders + border,
1482
&num_points, &num_contours );
1485
*anum_points = num_points;
1487
if ( anum_contours )
1488
*anum_contours = num_contours;
1494
FT_EXPORT_DEF( FT_Error )
1495
FT_Stroker_GetCounts( FT_Stroker stroker,
1496
FT_UInt *anum_points,
1497
FT_UInt *anum_contours )
1499
FT_UInt count1, count2, num_points = 0;
1500
FT_UInt count3, count4, num_contours = 0;
1504
error = ft_stroke_border_get_counts( stroker->borders + 0,
1509
error = ft_stroke_border_get_counts( stroker->borders + 1,
1514
num_points = count1 + count3;
1515
num_contours = count2 + count4;
1518
*anum_points = num_points;
1519
*anum_contours = num_contours;
1524
FT_EXPORT_DEF( void )
1525
FT_Stroker_ExportBorder( FT_Stroker stroker,
1526
FT_StrokerBorder border,
1527
FT_Outline* outline )
1529
if ( border == FT_STROKER_BORDER_LEFT ||
1530
border == FT_STROKER_BORDER_RIGHT )
1532
FT_StrokeBorder sborder = & stroker->borders[border];
1535
if ( sborder->valid )
1536
ft_stroke_border_export( sborder, outline );
1541
FT_EXPORT_DEF( void )
1542
FT_Stroker_Export( FT_Stroker stroker,
1543
FT_Outline* outline )
1545
FT_Stroker_ExportBorder( stroker, FT_STROKER_BORDER_LEFT, outline );
1546
FT_Stroker_ExportBorder( stroker, FT_STROKER_BORDER_RIGHT, outline );
1551
* The following is very similar to FT_Outline_Decompose, except
1552
* that we do support opened paths, and do not scale the outline.
1554
FT_EXPORT_DEF( FT_Error )
1555
FT_Stroker_ParseOutline( FT_Stroker stroker,
1556
FT_Outline* outline,
1560
FT_Vector v_control;
1569
FT_Int n; /* index of contour in outline */
1570
FT_UInt first; /* index of first point in contour */
1571
FT_Int tag; /* current point's state */
1574
if ( !outline || !stroker )
1575
return FT_Err_Invalid_Argument;
1577
FT_Stroker_Rewind( stroker );
1581
for ( n = 0; n < outline->n_contours; n++ )
1583
FT_Int last; /* index of last point in contour */
1586
last = outline->contours[n];
1587
limit = outline->points + last;
1589
v_start = outline->points[first];
1590
v_last = outline->points[last];
1592
v_control = v_start;
1594
point = outline->points + first;
1595
tags = outline->tags + first;
1596
tag = FT_CURVE_TAG( tags[0] );
1598
/* A contour cannot start with a cubic control point! */
1599
if ( tag == FT_CURVE_TAG_CUBIC )
1600
goto Invalid_Outline;
1602
/* check first point to determine origin */
1603
if ( tag == FT_CURVE_TAG_CONIC )
1605
/* First point is conic control. Yes, this happens. */
1606
if ( FT_CURVE_TAG( outline->tags[last] ) == FT_CURVE_TAG_ON )
1608
/* start at last point if it is on the curve */
1614
/* if both first and last points are conic, */
1615
/* start at their middle and record its position */
1617
v_start.x = ( v_start.x + v_last.x ) / 2;
1618
v_start.y = ( v_start.y + v_last.y ) / 2;
1626
error = FT_Stroker_BeginSubPath( stroker, &v_start, opened );
1630
while ( point < limit )
1635
tag = FT_CURVE_TAG( tags[0] );
1638
case FT_CURVE_TAG_ON: /* emit a single line_to */
1646
error = FT_Stroker_LineTo( stroker, &vec );
1652
case FT_CURVE_TAG_CONIC: /* consume conic arcs */
1653
v_control.x = point->x;
1654
v_control.y = point->y;
1657
if ( point < limit )
1665
tag = FT_CURVE_TAG( tags[0] );
1669
if ( tag == FT_CURVE_TAG_ON )
1671
error = FT_Stroker_ConicTo( stroker, &v_control, &vec );
1677
if ( tag != FT_CURVE_TAG_CONIC )
1678
goto Invalid_Outline;
1680
v_middle.x = ( v_control.x + vec.x ) / 2;
1681
v_middle.y = ( v_control.y + vec.y ) / 2;
1683
error = FT_Stroker_ConicTo( stroker, &v_control, &v_middle );
1691
error = FT_Stroker_ConicTo( stroker, &v_control, &v_start );
1694
default: /* FT_CURVE_TAG_CUBIC */
1696
FT_Vector vec1, vec2;
1699
if ( point + 1 > limit ||
1700
FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC )
1701
goto Invalid_Outline;
1709
if ( point <= limit )
1716
error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &vec );
1722
error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &v_start );
1732
error = FT_Stroker_EndSubPath( stroker );
1745
return FT_Err_Invalid_Outline;
1749
extern const FT_Glyph_Class ft_outline_glyph_class;
1752
FT_EXPORT_DEF( FT_Error )
1753
FT_Glyph_Stroke( FT_Glyph *pglyph,
1757
FT_Error error = FT_Err_Invalid_Argument;
1758
FT_Glyph glyph = NULL;
1761
if ( pglyph == NULL )
1765
if ( glyph == NULL || glyph->clazz != &ft_outline_glyph_class )
1772
error = FT_Glyph_Copy( glyph, © );
1780
FT_OutlineGlyph oglyph = (FT_OutlineGlyph) glyph;
1781
FT_Outline* outline = &oglyph->outline;
1782
FT_UInt num_points, num_contours;
1785
error = FT_Stroker_ParseOutline( stroker, outline, 0 );
1789
(void)FT_Stroker_GetCounts( stroker, &num_points, &num_contours );
1791
FT_Outline_Done( glyph->library, outline );
1793
error = FT_Outline_New( glyph->library,
1794
num_points, num_contours, outline );
1798
outline->n_points = 0;
1799
outline->n_contours = 0;
1801
FT_Stroker_Export( stroker, outline );
1805
FT_Done_Glyph( *pglyph );
1811
FT_Done_Glyph( glyph );
1822
FT_EXPORT_DEF( FT_Error )
1823
FT_Glyph_StrokeBorder( FT_Glyph *pglyph,
1828
FT_Error error = FT_Err_Invalid_Argument;
1829
FT_Glyph glyph = NULL;
1832
if ( pglyph == NULL )
1836
if ( glyph == NULL || glyph->clazz != &ft_outline_glyph_class )
1843
error = FT_Glyph_Copy( glyph, © );
1851
FT_OutlineGlyph oglyph = (FT_OutlineGlyph) glyph;
1852
FT_StrokerBorder border;
1853
FT_Outline* outline = &oglyph->outline;
1854
FT_UInt num_points, num_contours;
1857
border = FT_Outline_GetOutsideBorder( outline );
1860
if ( border == FT_STROKER_BORDER_LEFT )
1861
border = FT_STROKER_BORDER_RIGHT;
1863
border = FT_STROKER_BORDER_LEFT;
1866
error = FT_Stroker_ParseOutline( stroker, outline, 0 );
1870
(void)FT_Stroker_GetBorderCounts( stroker, border,
1871
&num_points, &num_contours );
1873
FT_Outline_Done( glyph->library, outline );
1875
error = FT_Outline_New( glyph->library,
1882
outline->n_points = 0;
1883
outline->n_contours = 0;
1885
FT_Stroker_ExportBorder( stroker, border, outline );
1889
FT_Done_Glyph( *pglyph );
1895
FT_Done_Glyph( glyph );