xref: /inferno-os/libfreetype/ftstroker.c (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
1*37da2899SCharles.Forsyth #include <ft2build.h>
2*37da2899SCharles.Forsyth #include FT_STROKER_H
3*37da2899SCharles.Forsyth #include FT_TRIGONOMETRY_H
4*37da2899SCharles.Forsyth #include FT_INTERNAL_MEMORY_H
5*37da2899SCharles.Forsyth #include FT_INTERNAL_DEBUG_H
6*37da2899SCharles.Forsyth 
7*37da2899SCharles.Forsyth  /***************************************************************************/
8*37da2899SCharles.Forsyth  /***************************************************************************/
9*37da2899SCharles.Forsyth  /*****                                                                 *****/
10*37da2899SCharles.Forsyth  /*****                       BEZIER COMPUTATIONS                       *****/
11*37da2899SCharles.Forsyth  /*****                                                                 *****/
12*37da2899SCharles.Forsyth  /***************************************************************************/
13*37da2899SCharles.Forsyth  /***************************************************************************/
14*37da2899SCharles.Forsyth 
15*37da2899SCharles.Forsyth #define FT_SMALL_CONIC_THRESHOLD   (FT_ANGLE_PI/6)
16*37da2899SCharles.Forsyth #define FT_SMALL_CUBIC_THRESHOLD   (FT_ANGLE_PI/6)
17*37da2899SCharles.Forsyth #define FT_EPSILON  2
18*37da2899SCharles.Forsyth 
19*37da2899SCharles.Forsyth #define FT_IS_SMALL(x)  ((x) > -FT_EPSILON && (x) < FT_EPSILON)
20*37da2899SCharles.Forsyth 
21*37da2899SCharles.Forsyth   static FT_Pos
ft_pos_abs(FT_Pos x)22*37da2899SCharles.Forsyth   ft_pos_abs( FT_Pos  x )
23*37da2899SCharles.Forsyth   {
24*37da2899SCharles.Forsyth     return  x >= 0 ? x : -x ;
25*37da2899SCharles.Forsyth   }
26*37da2899SCharles.Forsyth 
27*37da2899SCharles.Forsyth   static void
ft_conic_split(FT_Vector * base)28*37da2899SCharles.Forsyth   ft_conic_split( FT_Vector*  base )
29*37da2899SCharles.Forsyth   {
30*37da2899SCharles.Forsyth     FT_Pos  a, b;
31*37da2899SCharles.Forsyth 
32*37da2899SCharles.Forsyth 
33*37da2899SCharles.Forsyth     base[4].x = base[2].x;
34*37da2899SCharles.Forsyth     b = base[1].x;
35*37da2899SCharles.Forsyth     a = base[3].x = ( base[2].x + b )/2;
36*37da2899SCharles.Forsyth     b = base[1].x = ( base[0].x + b )/2;
37*37da2899SCharles.Forsyth     base[2].x = ( a + b )/2;
38*37da2899SCharles.Forsyth 
39*37da2899SCharles.Forsyth     base[4].y = base[2].y;
40*37da2899SCharles.Forsyth     b = base[1].y;
41*37da2899SCharles.Forsyth     a = base[3].y = ( base[2].y + b )/2;
42*37da2899SCharles.Forsyth     b = base[1].y = ( base[0].y + b )/2;
43*37da2899SCharles.Forsyth     base[2].y = ( a + b )/2;
44*37da2899SCharles.Forsyth   }
45*37da2899SCharles.Forsyth 
46*37da2899SCharles.Forsyth 
47*37da2899SCharles.Forsyth   static FT_Bool
ft_conic_is_small_enough(FT_Vector * base,FT_Angle * angle_in,FT_Angle * angle_out)48*37da2899SCharles.Forsyth   ft_conic_is_small_enough( FT_Vector*  base,
49*37da2899SCharles.Forsyth                             FT_Angle   *angle_in,
50*37da2899SCharles.Forsyth                             FT_Angle   *angle_out )
51*37da2899SCharles.Forsyth   {
52*37da2899SCharles.Forsyth     FT_Vector  d1, d2;
53*37da2899SCharles.Forsyth     FT_Angle   theta;
54*37da2899SCharles.Forsyth     FT_Int     close1, close2;
55*37da2899SCharles.Forsyth 
56*37da2899SCharles.Forsyth     d1.x = base[1].x - base[2].x;
57*37da2899SCharles.Forsyth     d1.y = base[1].y - base[2].y;
58*37da2899SCharles.Forsyth     d2.x = base[0].x - base[1].x;
59*37da2899SCharles.Forsyth     d2.y = base[0].y - base[1].y;
60*37da2899SCharles.Forsyth 
61*37da2899SCharles.Forsyth     close1 = FT_IS_SMALL(d1.x) && FT_IS_SMALL(d1.y);
62*37da2899SCharles.Forsyth     close2 = FT_IS_SMALL(d2.x) && FT_IS_SMALL(d2.y);
63*37da2899SCharles.Forsyth 
64*37da2899SCharles.Forsyth     if (close1)
65*37da2899SCharles.Forsyth     {
66*37da2899SCharles.Forsyth       if (close2)
67*37da2899SCharles.Forsyth         *angle_in = *angle_out = 0;
68*37da2899SCharles.Forsyth       else
69*37da2899SCharles.Forsyth         *angle_in = *angle_out = FT_Atan2( d2.x, d2.y );
70*37da2899SCharles.Forsyth     }
71*37da2899SCharles.Forsyth     else if (close2)
72*37da2899SCharles.Forsyth     {
73*37da2899SCharles.Forsyth       *angle_in = *angle_out = FT_Atan2( d1.x, d1.y );
74*37da2899SCharles.Forsyth     }
75*37da2899SCharles.Forsyth     else
76*37da2899SCharles.Forsyth     {
77*37da2899SCharles.Forsyth       *angle_in  = FT_Atan2( d1.x, d1.y );
78*37da2899SCharles.Forsyth       *angle_out = FT_Atan2( d2.x, d2.y );
79*37da2899SCharles.Forsyth     }
80*37da2899SCharles.Forsyth 
81*37da2899SCharles.Forsyth     theta = ft_pos_abs( FT_Angle_Diff( *angle_in, *angle_out ) );
82*37da2899SCharles.Forsyth 
83*37da2899SCharles.Forsyth     return FT_BOOL( theta < FT_SMALL_CONIC_THRESHOLD );
84*37da2899SCharles.Forsyth   }
85*37da2899SCharles.Forsyth 
86*37da2899SCharles.Forsyth 
87*37da2899SCharles.Forsyth   static void
ft_cubic_split(FT_Vector * base)88*37da2899SCharles.Forsyth   ft_cubic_split( FT_Vector*  base )
89*37da2899SCharles.Forsyth   {
90*37da2899SCharles.Forsyth     FT_Pos  a, b, c, d;
91*37da2899SCharles.Forsyth 
92*37da2899SCharles.Forsyth 
93*37da2899SCharles.Forsyth     base[6].x = base[3].x;
94*37da2899SCharles.Forsyth     c = base[1].x;
95*37da2899SCharles.Forsyth     d = base[2].x;
96*37da2899SCharles.Forsyth     base[1].x = a = ( base[0].x + c )/2;
97*37da2899SCharles.Forsyth     base[5].x = b = ( base[3].x + d )/2;
98*37da2899SCharles.Forsyth     c = ( c + d )/2;
99*37da2899SCharles.Forsyth     base[2].x = a = ( a + c )/2;
100*37da2899SCharles.Forsyth     base[4].x = b = ( b + c )/2;
101*37da2899SCharles.Forsyth     base[3].x = ( a + b )/2;
102*37da2899SCharles.Forsyth 
103*37da2899SCharles.Forsyth     base[6].y = base[3].y;
104*37da2899SCharles.Forsyth     c = base[1].y;
105*37da2899SCharles.Forsyth     d = base[2].y;
106*37da2899SCharles.Forsyth     base[1].y = a = ( base[0].y + c )/2;
107*37da2899SCharles.Forsyth     base[5].y = b = ( base[3].y + d )/2;
108*37da2899SCharles.Forsyth     c = ( c + d )/2;
109*37da2899SCharles.Forsyth     base[2].y = a = ( a + c )/2;
110*37da2899SCharles.Forsyth     base[4].y = b = ( b + c )/2;
111*37da2899SCharles.Forsyth     base[3].y = ( a + b )/2;
112*37da2899SCharles.Forsyth   }
113*37da2899SCharles.Forsyth 
114*37da2899SCharles.Forsyth 
115*37da2899SCharles.Forsyth   static FT_Bool
ft_cubic_is_small_enough(FT_Vector * base,FT_Angle * angle_in,FT_Angle * angle_mid,FT_Angle * angle_out)116*37da2899SCharles.Forsyth   ft_cubic_is_small_enough( FT_Vector*  base,
117*37da2899SCharles.Forsyth                             FT_Angle   *angle_in,
118*37da2899SCharles.Forsyth                             FT_Angle   *angle_mid,
119*37da2899SCharles.Forsyth                             FT_Angle   *angle_out )
120*37da2899SCharles.Forsyth   {
121*37da2899SCharles.Forsyth     FT_Vector  d1, d2, d3;
122*37da2899SCharles.Forsyth     FT_Angle   theta1, theta2;
123*37da2899SCharles.Forsyth     FT_Int     close1, close2, close3;
124*37da2899SCharles.Forsyth 
125*37da2899SCharles.Forsyth     d1.x = base[2].x - base[3].x;
126*37da2899SCharles.Forsyth     d1.y = base[2].y - base[3].y;
127*37da2899SCharles.Forsyth     d2.x = base[1].x - base[2].x;
128*37da2899SCharles.Forsyth     d2.y = base[1].y - base[2].y;
129*37da2899SCharles.Forsyth     d3.x = base[0].x - base[1].x;
130*37da2899SCharles.Forsyth     d3.y = base[0].y - base[1].y;
131*37da2899SCharles.Forsyth 
132*37da2899SCharles.Forsyth     close1 = FT_IS_SMALL(d1.x) && FT_IS_SMALL(d1.y);
133*37da2899SCharles.Forsyth     close2 = FT_IS_SMALL(d2.x) && FT_IS_SMALL(d2.y);
134*37da2899SCharles.Forsyth     close3 = FT_IS_SMALL(d3.x) && FT_IS_SMALL(d3.y);
135*37da2899SCharles.Forsyth 
136*37da2899SCharles.Forsyth     if (close1 || close3)
137*37da2899SCharles.Forsyth     {
138*37da2899SCharles.Forsyth       if (close2)
139*37da2899SCharles.Forsyth       {
140*37da2899SCharles.Forsyth         /* basically a point */
141*37da2899SCharles.Forsyth         *angle_in = *angle_out = *angle_mid = 0;
142*37da2899SCharles.Forsyth       }
143*37da2899SCharles.Forsyth       else if (close1)
144*37da2899SCharles.Forsyth       {
145*37da2899SCharles.Forsyth         *angle_in  = *angle_mid = FT_Atan2( d2.x, d2.y );
146*37da2899SCharles.Forsyth         *angle_out = FT_Atan2( d3.x, d3.y );
147*37da2899SCharles.Forsyth       }
148*37da2899SCharles.Forsyth       else  /* close2 */
149*37da2899SCharles.Forsyth       {
150*37da2899SCharles.Forsyth         *angle_in  = FT_Atan2( d1.x, d1.y );
151*37da2899SCharles.Forsyth         *angle_mid = *angle_out = FT_Atan2( d2.x, d2.y );
152*37da2899SCharles.Forsyth       }
153*37da2899SCharles.Forsyth     }
154*37da2899SCharles.Forsyth     else if (close2)
155*37da2899SCharles.Forsyth     {
156*37da2899SCharles.Forsyth       *angle_in  = *angle_mid = FT_Atan2( d1.x, d1.y );
157*37da2899SCharles.Forsyth       *angle_out = FT_Atan2( d3.x, d3.y );
158*37da2899SCharles.Forsyth     }
159*37da2899SCharles.Forsyth     else
160*37da2899SCharles.Forsyth     {
161*37da2899SCharles.Forsyth       *angle_in  = FT_Atan2( d1.x, d1.y );
162*37da2899SCharles.Forsyth       *angle_mid = FT_Atan2( d2.x, d2.y );
163*37da2899SCharles.Forsyth       *angle_out = FT_Atan2( d3.x, d3.y );
164*37da2899SCharles.Forsyth     }
165*37da2899SCharles.Forsyth     theta1 = ft_pos_abs( FT_Angle_Diff( *angle_in,  *angle_mid ) );
166*37da2899SCharles.Forsyth     theta2 = ft_pos_abs( FT_Angle_Diff( *angle_mid, *angle_out ) );
167*37da2899SCharles.Forsyth 
168*37da2899SCharles.Forsyth     return FT_BOOL( theta1 < FT_SMALL_CUBIC_THRESHOLD &&
169*37da2899SCharles.Forsyth                     theta2 < FT_SMALL_CUBIC_THRESHOLD );
170*37da2899SCharles.Forsyth   }
171*37da2899SCharles.Forsyth 
172*37da2899SCharles.Forsyth 
173*37da2899SCharles.Forsyth 
174*37da2899SCharles.Forsyth  /***************************************************************************/
175*37da2899SCharles.Forsyth  /***************************************************************************/
176*37da2899SCharles.Forsyth  /*****                                                                 *****/
177*37da2899SCharles.Forsyth  /*****                       STROKE BORDERS                            *****/
178*37da2899SCharles.Forsyth  /*****                                                                 *****/
179*37da2899SCharles.Forsyth  /***************************************************************************/
180*37da2899SCharles.Forsyth  /***************************************************************************/
181*37da2899SCharles.Forsyth 
182*37da2899SCharles.Forsyth   typedef enum
183*37da2899SCharles.Forsyth   {
184*37da2899SCharles.Forsyth     FT_STROKE_TAG_ON    = 1,   /* on-curve point  */
185*37da2899SCharles.Forsyth     FT_STROKE_TAG_CUBIC = 2,   /* cubic off-point */
186*37da2899SCharles.Forsyth     FT_STROKE_TAG_BEGIN = 4,   /* sub-path start  */
187*37da2899SCharles.Forsyth     FT_STROKE_TAG_END   = 8    /* sub-path end    */
188*37da2899SCharles.Forsyth 
189*37da2899SCharles.Forsyth   } FT_StrokeTags;
190*37da2899SCharles.Forsyth 
191*37da2899SCharles.Forsyth 
192*37da2899SCharles.Forsyth   typedef struct FT_StrokeBorderRec_
193*37da2899SCharles.Forsyth   {
194*37da2899SCharles.Forsyth     FT_UInt     num_points;
195*37da2899SCharles.Forsyth     FT_UInt     max_points;
196*37da2899SCharles.Forsyth     FT_Vector*  points;
197*37da2899SCharles.Forsyth     FT_Byte*    tags;
198*37da2899SCharles.Forsyth     FT_Bool     movable;
199*37da2899SCharles.Forsyth     FT_Int      start;    /* index of current sub-path start point */
200*37da2899SCharles.Forsyth     FT_Memory   memory;
201*37da2899SCharles.Forsyth 
202*37da2899SCharles.Forsyth   } FT_StrokeBorderRec, *FT_StrokeBorder;
203*37da2899SCharles.Forsyth 
204*37da2899SCharles.Forsyth 
205*37da2899SCharles.Forsyth   static FT_Error
ft_stroke_border_grow(FT_StrokeBorder border,FT_UInt new_points)206*37da2899SCharles.Forsyth   ft_stroke_border_grow( FT_StrokeBorder  border,
207*37da2899SCharles.Forsyth                          FT_UInt          new_points )
208*37da2899SCharles.Forsyth   {
209*37da2899SCharles.Forsyth     FT_UInt   old_max = border->max_points;
210*37da2899SCharles.Forsyth     FT_UInt   new_max = border->num_points + new_points;
211*37da2899SCharles.Forsyth     FT_Error  error   = 0;
212*37da2899SCharles.Forsyth 
213*37da2899SCharles.Forsyth     if ( new_max > old_max )
214*37da2899SCharles.Forsyth     {
215*37da2899SCharles.Forsyth       FT_UInt    cur_max = old_max;
216*37da2899SCharles.Forsyth       FT_Memory  memory  = border->memory;
217*37da2899SCharles.Forsyth 
218*37da2899SCharles.Forsyth       while ( cur_max < new_max )
219*37da2899SCharles.Forsyth         cur_max += (cur_max >> 1) + 16;
220*37da2899SCharles.Forsyth 
221*37da2899SCharles.Forsyth       if ( FT_RENEW_ARRAY( border->points, old_max, cur_max ) ||
222*37da2899SCharles.Forsyth            FT_RENEW_ARRAY( border->tags,   old_max, cur_max ) )
223*37da2899SCharles.Forsyth         goto Exit;
224*37da2899SCharles.Forsyth 
225*37da2899SCharles.Forsyth       border->max_points = cur_max;
226*37da2899SCharles.Forsyth     }
227*37da2899SCharles.Forsyth   Exit:
228*37da2899SCharles.Forsyth     return error;
229*37da2899SCharles.Forsyth   }
230*37da2899SCharles.Forsyth 
231*37da2899SCharles.Forsyth   static void
ft_stroke_border_close(FT_StrokeBorder border)232*37da2899SCharles.Forsyth   ft_stroke_border_close( FT_StrokeBorder  border )
233*37da2899SCharles.Forsyth   {
234*37da2899SCharles.Forsyth     FT_ASSERT( border->start >= 0 );
235*37da2899SCharles.Forsyth 
236*37da2899SCharles.Forsyth     border->tags[ border->start        ] |= FT_STROKE_TAG_BEGIN;
237*37da2899SCharles.Forsyth     border->tags[ border->num_points-1 ] |= FT_STROKE_TAG_END;
238*37da2899SCharles.Forsyth 
239*37da2899SCharles.Forsyth     border->start   = -1;
240*37da2899SCharles.Forsyth     border->movable = 0;
241*37da2899SCharles.Forsyth   }
242*37da2899SCharles.Forsyth 
243*37da2899SCharles.Forsyth 
244*37da2899SCharles.Forsyth   static FT_Error
ft_stroke_border_lineto(FT_StrokeBorder border,FT_Vector * to,FT_Bool movable)245*37da2899SCharles.Forsyth   ft_stroke_border_lineto( FT_StrokeBorder  border,
246*37da2899SCharles.Forsyth                            FT_Vector*       to,
247*37da2899SCharles.Forsyth                            FT_Bool          movable )
248*37da2899SCharles.Forsyth   {
249*37da2899SCharles.Forsyth     FT_Error  error = 0;
250*37da2899SCharles.Forsyth 
251*37da2899SCharles.Forsyth     FT_ASSERT( border->start >= 0 );
252*37da2899SCharles.Forsyth 
253*37da2899SCharles.Forsyth     if ( border->movable )
254*37da2899SCharles.Forsyth     {
255*37da2899SCharles.Forsyth       /* move last point */
256*37da2899SCharles.Forsyth       border->points[ border->num_points-1 ] = *to;
257*37da2899SCharles.Forsyth     }
258*37da2899SCharles.Forsyth     else
259*37da2899SCharles.Forsyth     {
260*37da2899SCharles.Forsyth       /* add one point */
261*37da2899SCharles.Forsyth       error = ft_stroke_border_grow( border, 1 );
262*37da2899SCharles.Forsyth       if (!error)
263*37da2899SCharles.Forsyth       {
264*37da2899SCharles.Forsyth         FT_Vector*  vec = border->points + border->num_points;
265*37da2899SCharles.Forsyth         FT_Byte*    tag = border->tags   + border->num_points;
266*37da2899SCharles.Forsyth 
267*37da2899SCharles.Forsyth         vec[0] = *to;
268*37da2899SCharles.Forsyth         tag[0] = FT_STROKE_TAG_ON;
269*37da2899SCharles.Forsyth 
270*37da2899SCharles.Forsyth         border->num_points += 1;
271*37da2899SCharles.Forsyth       }
272*37da2899SCharles.Forsyth     }
273*37da2899SCharles.Forsyth     border->movable = movable;
274*37da2899SCharles.Forsyth     return error;
275*37da2899SCharles.Forsyth   }
276*37da2899SCharles.Forsyth 
277*37da2899SCharles.Forsyth 
278*37da2899SCharles.Forsyth   static FT_Error
ft_stroke_border_conicto(FT_StrokeBorder border,FT_Vector * control,FT_Vector * to)279*37da2899SCharles.Forsyth   ft_stroke_border_conicto( FT_StrokeBorder  border,
280*37da2899SCharles.Forsyth                             FT_Vector*       control,
281*37da2899SCharles.Forsyth                             FT_Vector*       to )
282*37da2899SCharles.Forsyth   {
283*37da2899SCharles.Forsyth     FT_Error  error;
284*37da2899SCharles.Forsyth 
285*37da2899SCharles.Forsyth     FT_ASSERT( border->start >= 0 );
286*37da2899SCharles.Forsyth 
287*37da2899SCharles.Forsyth     error = ft_stroke_border_grow( border, 2 );
288*37da2899SCharles.Forsyth     if (!error)
289*37da2899SCharles.Forsyth     {
290*37da2899SCharles.Forsyth       FT_Vector*  vec = border->points + border->num_points;
291*37da2899SCharles.Forsyth       FT_Byte*    tag = border->tags   + border->num_points;
292*37da2899SCharles.Forsyth 
293*37da2899SCharles.Forsyth       vec[0] = *control;
294*37da2899SCharles.Forsyth       vec[1] = *to;
295*37da2899SCharles.Forsyth 
296*37da2899SCharles.Forsyth       tag[0] = 0;
297*37da2899SCharles.Forsyth       tag[1] = FT_STROKE_TAG_ON;
298*37da2899SCharles.Forsyth 
299*37da2899SCharles.Forsyth       border->num_points += 2;
300*37da2899SCharles.Forsyth     }
301*37da2899SCharles.Forsyth     border->movable = 0;
302*37da2899SCharles.Forsyth     return error;
303*37da2899SCharles.Forsyth   }
304*37da2899SCharles.Forsyth 
305*37da2899SCharles.Forsyth 
306*37da2899SCharles.Forsyth   static FT_Error
ft_stroke_border_cubicto(FT_StrokeBorder border,FT_Vector * control1,FT_Vector * control2,FT_Vector * to)307*37da2899SCharles.Forsyth   ft_stroke_border_cubicto( FT_StrokeBorder  border,
308*37da2899SCharles.Forsyth                             FT_Vector*       control1,
309*37da2899SCharles.Forsyth                             FT_Vector*       control2,
310*37da2899SCharles.Forsyth                             FT_Vector*       to )
311*37da2899SCharles.Forsyth   {
312*37da2899SCharles.Forsyth     FT_Error  error;
313*37da2899SCharles.Forsyth 
314*37da2899SCharles.Forsyth     FT_ASSERT( border->start >= 0 );
315*37da2899SCharles.Forsyth 
316*37da2899SCharles.Forsyth     error = ft_stroke_border_grow( border, 3 );
317*37da2899SCharles.Forsyth     if (!error)
318*37da2899SCharles.Forsyth     {
319*37da2899SCharles.Forsyth       FT_Vector*  vec = border->points + border->num_points;
320*37da2899SCharles.Forsyth       FT_Byte*    tag = border->tags   + border->num_points;
321*37da2899SCharles.Forsyth 
322*37da2899SCharles.Forsyth       vec[0] = *control1;
323*37da2899SCharles.Forsyth       vec[1] = *control2;
324*37da2899SCharles.Forsyth       vec[2] = *to;
325*37da2899SCharles.Forsyth 
326*37da2899SCharles.Forsyth       tag[0] = FT_STROKE_TAG_CUBIC;
327*37da2899SCharles.Forsyth       tag[1] = FT_STROKE_TAG_CUBIC;
328*37da2899SCharles.Forsyth       tag[2] = FT_STROKE_TAG_ON;
329*37da2899SCharles.Forsyth 
330*37da2899SCharles.Forsyth       border->num_points += 3;
331*37da2899SCharles.Forsyth     }
332*37da2899SCharles.Forsyth     border->movable = 0;
333*37da2899SCharles.Forsyth     return error;
334*37da2899SCharles.Forsyth   }
335*37da2899SCharles.Forsyth 
336*37da2899SCharles.Forsyth 
337*37da2899SCharles.Forsyth #define  FT_ARC_CUBIC_ANGLE  (FT_ANGLE_PI/2)
338*37da2899SCharles.Forsyth 
339*37da2899SCharles.Forsyth 
340*37da2899SCharles.Forsyth   static FT_Error
ft_stroke_border_arcto(FT_StrokeBorder border,FT_Vector * center,FT_Fixed radius,FT_Angle angle_start,FT_Angle angle_diff)341*37da2899SCharles.Forsyth   ft_stroke_border_arcto( FT_StrokeBorder  border,
342*37da2899SCharles.Forsyth                           FT_Vector*       center,
343*37da2899SCharles.Forsyth                           FT_Fixed         radius,
344*37da2899SCharles.Forsyth                           FT_Angle         angle_start,
345*37da2899SCharles.Forsyth                           FT_Angle         angle_diff )
346*37da2899SCharles.Forsyth   {
347*37da2899SCharles.Forsyth     FT_Angle   total, angle, step, rotate, next, theta;
348*37da2899SCharles.Forsyth     FT_Vector  a, b, a2, b2;
349*37da2899SCharles.Forsyth     FT_Fixed   length;
350*37da2899SCharles.Forsyth     FT_Error   error = 0;
351*37da2899SCharles.Forsyth 
352*37da2899SCharles.Forsyth     /* compute start point */
353*37da2899SCharles.Forsyth     FT_Vector_From_Polar( &a, radius, angle_start );
354*37da2899SCharles.Forsyth     a.x += center->x;
355*37da2899SCharles.Forsyth     a.y += center->y;
356*37da2899SCharles.Forsyth 
357*37da2899SCharles.Forsyth     total  = angle_diff;
358*37da2899SCharles.Forsyth     angle  = angle_start;
359*37da2899SCharles.Forsyth     rotate = ( angle_diff >= 0 ) ? FT_ANGLE_PI2 : -FT_ANGLE_PI2;
360*37da2899SCharles.Forsyth 
361*37da2899SCharles.Forsyth     while (total != 0)
362*37da2899SCharles.Forsyth     {
363*37da2899SCharles.Forsyth       step = total;
364*37da2899SCharles.Forsyth       if ( step > FT_ARC_CUBIC_ANGLE )
365*37da2899SCharles.Forsyth         step   = FT_ARC_CUBIC_ANGLE;
366*37da2899SCharles.Forsyth 
367*37da2899SCharles.Forsyth       else if ( step < -FT_ARC_CUBIC_ANGLE )
368*37da2899SCharles.Forsyth         step = -FT_ARC_CUBIC_ANGLE;
369*37da2899SCharles.Forsyth 
370*37da2899SCharles.Forsyth       next  = angle + step;
371*37da2899SCharles.Forsyth       theta = step;
372*37da2899SCharles.Forsyth       if ( theta < 0 )
373*37da2899SCharles.Forsyth         theta = -theta;
374*37da2899SCharles.Forsyth 
375*37da2899SCharles.Forsyth       theta >>= 1;
376*37da2899SCharles.Forsyth 
377*37da2899SCharles.Forsyth       /* compute end point */
378*37da2899SCharles.Forsyth       FT_Vector_From_Polar( &b, radius, next );
379*37da2899SCharles.Forsyth       b.x += center->x;
380*37da2899SCharles.Forsyth       b.y += center->y;
381*37da2899SCharles.Forsyth 
382*37da2899SCharles.Forsyth       /* compute first and second control points */
383*37da2899SCharles.Forsyth       length = FT_MulDiv( radius, FT_Sin(theta)*4,
384*37da2899SCharles.Forsyth                           (0x10000L + FT_Cos(theta))*3 );
385*37da2899SCharles.Forsyth 
386*37da2899SCharles.Forsyth       FT_Vector_From_Polar( &a2, length, angle + rotate );
387*37da2899SCharles.Forsyth       a2.x += a.x;
388*37da2899SCharles.Forsyth       a2.y += a.y;
389*37da2899SCharles.Forsyth 
390*37da2899SCharles.Forsyth       FT_Vector_From_Polar( &b2, length, next - rotate );
391*37da2899SCharles.Forsyth       b2.x += b.x;
392*37da2899SCharles.Forsyth       b2.y += b.y;
393*37da2899SCharles.Forsyth 
394*37da2899SCharles.Forsyth       /* add cubic arc */
395*37da2899SCharles.Forsyth       error = ft_stroke_border_cubicto( border, &a2, &b2, &b );
396*37da2899SCharles.Forsyth       if (error) break;
397*37da2899SCharles.Forsyth 
398*37da2899SCharles.Forsyth       /* process the rest of the arc ?? */
399*37da2899SCharles.Forsyth       a      = b;
400*37da2899SCharles.Forsyth       total -= step;
401*37da2899SCharles.Forsyth       angle  = next;
402*37da2899SCharles.Forsyth     }
403*37da2899SCharles.Forsyth     return error;
404*37da2899SCharles.Forsyth   }
405*37da2899SCharles.Forsyth 
406*37da2899SCharles.Forsyth 
407*37da2899SCharles.Forsyth   static FT_Error
ft_stroke_border_moveto(FT_StrokeBorder border,FT_Vector * to)408*37da2899SCharles.Forsyth   ft_stroke_border_moveto( FT_StrokeBorder  border,
409*37da2899SCharles.Forsyth                            FT_Vector*       to )
410*37da2899SCharles.Forsyth   {
411*37da2899SCharles.Forsyth     /* close current open path if any ? */
412*37da2899SCharles.Forsyth     if ( border->start >= 0 )
413*37da2899SCharles.Forsyth       ft_stroke_border_close( border );
414*37da2899SCharles.Forsyth 
415*37da2899SCharles.Forsyth     border->start   = border->num_points;
416*37da2899SCharles.Forsyth     border->movable = 0;
417*37da2899SCharles.Forsyth 
418*37da2899SCharles.Forsyth     return ft_stroke_border_lineto( border, to, 0 );
419*37da2899SCharles.Forsyth   }
420*37da2899SCharles.Forsyth 
421*37da2899SCharles.Forsyth 
422*37da2899SCharles.Forsyth  static void
ft_stroke_border_init(FT_StrokeBorder border,FT_Memory memory)423*37da2899SCharles.Forsyth  ft_stroke_border_init( FT_StrokeBorder  border,
424*37da2899SCharles.Forsyth                         FT_Memory        memory )
425*37da2899SCharles.Forsyth  {
426*37da2899SCharles.Forsyth    border->memory = memory;
427*37da2899SCharles.Forsyth    border->points = NULL;
428*37da2899SCharles.Forsyth    border->tags   = NULL;
429*37da2899SCharles.Forsyth 
430*37da2899SCharles.Forsyth    border->num_points = 0;
431*37da2899SCharles.Forsyth    border->max_points = 0;
432*37da2899SCharles.Forsyth    border->start      = -1;
433*37da2899SCharles.Forsyth  }
434*37da2899SCharles.Forsyth 
435*37da2899SCharles.Forsyth 
436*37da2899SCharles.Forsyth  static void
ft_stroke_border_reset(FT_StrokeBorder border)437*37da2899SCharles.Forsyth  ft_stroke_border_reset( FT_StrokeBorder  border )
438*37da2899SCharles.Forsyth  {
439*37da2899SCharles.Forsyth    border->num_points = 0;
440*37da2899SCharles.Forsyth    border->start      = -1;
441*37da2899SCharles.Forsyth  }
442*37da2899SCharles.Forsyth 
443*37da2899SCharles.Forsyth 
444*37da2899SCharles.Forsyth  static void
ft_stroke_border_done(FT_StrokeBorder border)445*37da2899SCharles.Forsyth  ft_stroke_border_done( FT_StrokeBorder  border )
446*37da2899SCharles.Forsyth  {
447*37da2899SCharles.Forsyth    FT_Memory  memory = border->memory;
448*37da2899SCharles.Forsyth 
449*37da2899SCharles.Forsyth    FT_FREE( border->points );
450*37da2899SCharles.Forsyth    FT_FREE( border->tags );
451*37da2899SCharles.Forsyth 
452*37da2899SCharles.Forsyth    border->num_points = 0;
453*37da2899SCharles.Forsyth    border->max_points = 0;
454*37da2899SCharles.Forsyth    border->start      = -1;
455*37da2899SCharles.Forsyth  }
456*37da2899SCharles.Forsyth 
457*37da2899SCharles.Forsyth 
458*37da2899SCharles.Forsyth  static FT_Error
ft_stroke_border_get_counts(FT_StrokeBorder border,FT_UInt * anum_points,FT_UInt * anum_contours)459*37da2899SCharles.Forsyth  ft_stroke_border_get_counts( FT_StrokeBorder  border,
460*37da2899SCharles.Forsyth                               FT_UInt         *anum_points,
461*37da2899SCharles.Forsyth                               FT_UInt         *anum_contours )
462*37da2899SCharles.Forsyth  {
463*37da2899SCharles.Forsyth    FT_Error  error        = 0;
464*37da2899SCharles.Forsyth    FT_UInt   num_points   = 0;
465*37da2899SCharles.Forsyth    FT_UInt   num_contours = 0;
466*37da2899SCharles.Forsyth 
467*37da2899SCharles.Forsyth    FT_UInt     count      = border->num_points;
468*37da2899SCharles.Forsyth    FT_Vector*  point      = border->points;
469*37da2899SCharles.Forsyth    FT_Byte*    tags       = border->tags;
470*37da2899SCharles.Forsyth    FT_Int      in_contour = 0;
471*37da2899SCharles.Forsyth 
472*37da2899SCharles.Forsyth    for ( ; count > 0; count--, point++, tags++ )
473*37da2899SCharles.Forsyth    {
474*37da2899SCharles.Forsyth      if ( tags[0] & FT_STROKE_TAG_BEGIN )
475*37da2899SCharles.Forsyth      {
476*37da2899SCharles.Forsyth        if ( in_contour != 0 )
477*37da2899SCharles.Forsyth          goto Fail;
478*37da2899SCharles.Forsyth 
479*37da2899SCharles.Forsyth        in_contour = 1;
480*37da2899SCharles.Forsyth      }
481*37da2899SCharles.Forsyth      else if ( in_contour == 0 )
482*37da2899SCharles.Forsyth        goto Fail;
483*37da2899SCharles.Forsyth 
484*37da2899SCharles.Forsyth      if ( tags[0] & FT_STROKE_TAG_END )
485*37da2899SCharles.Forsyth      {
486*37da2899SCharles.Forsyth        if ( in_contour == 0 )
487*37da2899SCharles.Forsyth          goto Fail;
488*37da2899SCharles.Forsyth 
489*37da2899SCharles.Forsyth        in_contour = 0;
490*37da2899SCharles.Forsyth        num_contours++;
491*37da2899SCharles.Forsyth      }
492*37da2899SCharles.Forsyth    }
493*37da2899SCharles.Forsyth    if ( in_contour != 0 )
494*37da2899SCharles.Forsyth      goto Fail;
495*37da2899SCharles.Forsyth 
496*37da2899SCharles.Forsyth  Exit:
497*37da2899SCharles.Forsyth    *anum_points   = num_points;
498*37da2899SCharles.Forsyth    *anum_contours = num_contours;
499*37da2899SCharles.Forsyth    return error;
500*37da2899SCharles.Forsyth 
501*37da2899SCharles.Forsyth  Fail:
502*37da2899SCharles.Forsyth    num_points   = 0;
503*37da2899SCharles.Forsyth    num_contours = 0;
504*37da2899SCharles.Forsyth    goto Exit;
505*37da2899SCharles.Forsyth  }
506*37da2899SCharles.Forsyth 
507*37da2899SCharles.Forsyth 
508*37da2899SCharles.Forsyth  static void
ft_stroke_border_export(FT_StrokeBorder border,FT_Outline * outline)509*37da2899SCharles.Forsyth  ft_stroke_border_export( FT_StrokeBorder  border,
510*37da2899SCharles.Forsyth                           FT_Outline*      outline )
511*37da2899SCharles.Forsyth  {
512*37da2899SCharles.Forsyth    /* copy point locations */
513*37da2899SCharles.Forsyth    FT_MEM_COPY( outline->points + outline->n_points,
514*37da2899SCharles.Forsyth                 border->points,
515*37da2899SCharles.Forsyth                 border->num_points * sizeof(FT_Vector) );
516*37da2899SCharles.Forsyth 
517*37da2899SCharles.Forsyth    /* copy tags */
518*37da2899SCharles.Forsyth    {
519*37da2899SCharles.Forsyth      FT_UInt   count = border->num_points;
520*37da2899SCharles.Forsyth      FT_Byte*  read  = border->tags;
521*37da2899SCharles.Forsyth      FT_Byte*  write = (FT_Byte*) outline->tags + outline->n_points;
522*37da2899SCharles.Forsyth 
523*37da2899SCharles.Forsyth      for ( ; count > 0; count--, read++, write++ )
524*37da2899SCharles.Forsyth      {
525*37da2899SCharles.Forsyth        if ( *read & FT_STROKE_TAG_ON )
526*37da2899SCharles.Forsyth          *write = FT_CURVE_TAG_ON;
527*37da2899SCharles.Forsyth        else if ( *read & FT_STROKE_TAG_CUBIC )
528*37da2899SCharles.Forsyth          *write = FT_CURVE_TAG_CUBIC;
529*37da2899SCharles.Forsyth        else
530*37da2899SCharles.Forsyth          *write = FT_CURVE_TAG_CONIC;
531*37da2899SCharles.Forsyth      }
532*37da2899SCharles.Forsyth    }
533*37da2899SCharles.Forsyth 
534*37da2899SCharles.Forsyth    /* copy contours */
535*37da2899SCharles.Forsyth    {
536*37da2899SCharles.Forsyth      FT_UInt    count = border->num_points;
537*37da2899SCharles.Forsyth      FT_Byte*   tags  = border->tags;
538*37da2899SCharles.Forsyth      FT_Short*  write = outline->contours + outline->n_contours;
539*37da2899SCharles.Forsyth      FT_Short   index = (FT_Short) outline->n_points;
540*37da2899SCharles.Forsyth 
541*37da2899SCharles.Forsyth      for ( ; count > 0; count--, tags++, write++, index++ )
542*37da2899SCharles.Forsyth      {
543*37da2899SCharles.Forsyth        if ( *tags & FT_STROKE_TAG_END )
544*37da2899SCharles.Forsyth        {
545*37da2899SCharles.Forsyth          *write++ = index;
546*37da2899SCharles.Forsyth          outline->n_contours++;
547*37da2899SCharles.Forsyth        }
548*37da2899SCharles.Forsyth      }
549*37da2899SCharles.Forsyth    }
550*37da2899SCharles.Forsyth 
551*37da2899SCharles.Forsyth    outline->n_points  = (short)( outline->n_points + border->num_points );
552*37da2899SCharles.Forsyth 
553*37da2899SCharles.Forsyth    FT_ASSERT( FT_Outline_Check( outline ) == 0 );
554*37da2899SCharles.Forsyth  }
555*37da2899SCharles.Forsyth 
556*37da2899SCharles.Forsyth 
557*37da2899SCharles.Forsyth  /***************************************************************************/
558*37da2899SCharles.Forsyth  /***************************************************************************/
559*37da2899SCharles.Forsyth  /*****                                                                 *****/
560*37da2899SCharles.Forsyth  /*****                           STROKER                               *****/
561*37da2899SCharles.Forsyth  /*****                                                                 *****/
562*37da2899SCharles.Forsyth  /***************************************************************************/
563*37da2899SCharles.Forsyth  /***************************************************************************/
564*37da2899SCharles.Forsyth 
565*37da2899SCharles.Forsyth #define  FT_SIDE_TO_ROTATE(s)   (FT_ANGLE_PI2 - (s)*FT_ANGLE_PI)
566*37da2899SCharles.Forsyth 
567*37da2899SCharles.Forsyth   typedef struct FT_StrokerRec_
568*37da2899SCharles.Forsyth   {
569*37da2899SCharles.Forsyth     FT_Angle             angle_in;
570*37da2899SCharles.Forsyth     FT_Angle             angle_out;
571*37da2899SCharles.Forsyth     FT_Vector            center;
572*37da2899SCharles.Forsyth     FT_Bool              first_point;
573*37da2899SCharles.Forsyth     FT_Bool              subpath_open;
574*37da2899SCharles.Forsyth     FT_Angle             subpath_angle;
575*37da2899SCharles.Forsyth     FT_Vector            subpath_start;
576*37da2899SCharles.Forsyth 
577*37da2899SCharles.Forsyth     FT_Stroker_LineCap   line_cap;
578*37da2899SCharles.Forsyth     FT_Stroker_LineJoin  line_join;
579*37da2899SCharles.Forsyth     FT_Fixed             miter_limit;
580*37da2899SCharles.Forsyth     FT_Fixed             radius;
581*37da2899SCharles.Forsyth 
582*37da2899SCharles.Forsyth     FT_Bool              valid;
583*37da2899SCharles.Forsyth     FT_StrokeBorderRec   borders[2];
584*37da2899SCharles.Forsyth     FT_Memory            memory;
585*37da2899SCharles.Forsyth 
586*37da2899SCharles.Forsyth   } FT_StrokerRec;
587*37da2899SCharles.Forsyth 
588*37da2899SCharles.Forsyth 
589*37da2899SCharles.Forsyth   FT_EXPORT_DEF( FT_Error )
FT_Stroker_New(FT_Memory memory,FT_Stroker * astroker)590*37da2899SCharles.Forsyth   FT_Stroker_New( FT_Memory    memory,
591*37da2899SCharles.Forsyth                   FT_Stroker  *astroker )
592*37da2899SCharles.Forsyth   {
593*37da2899SCharles.Forsyth     FT_Error    error;
594*37da2899SCharles.Forsyth     FT_Stroker  stroker;
595*37da2899SCharles.Forsyth 
596*37da2899SCharles.Forsyth     if ( !FT_NEW( stroker ) )
597*37da2899SCharles.Forsyth     {
598*37da2899SCharles.Forsyth       stroker->memory = memory;
599*37da2899SCharles.Forsyth 
600*37da2899SCharles.Forsyth       ft_stroke_border_init( &stroker->borders[0], memory );
601*37da2899SCharles.Forsyth       ft_stroke_border_init( &stroker->borders[1], memory );
602*37da2899SCharles.Forsyth     }
603*37da2899SCharles.Forsyth     *astroker = stroker;
604*37da2899SCharles.Forsyth     return error;
605*37da2899SCharles.Forsyth   }
606*37da2899SCharles.Forsyth 
607*37da2899SCharles.Forsyth 
608*37da2899SCharles.Forsyth   FT_EXPORT_DEF( void )
FT_Stroker_Set(FT_Stroker stroker,FT_Fixed radius,FT_Stroker_LineCap line_cap,FT_Stroker_LineJoin line_join,FT_Fixed miter_limit)609*37da2899SCharles.Forsyth   FT_Stroker_Set( FT_Stroker           stroker,
610*37da2899SCharles.Forsyth                   FT_Fixed             radius,
611*37da2899SCharles.Forsyth                   FT_Stroker_LineCap   line_cap,
612*37da2899SCharles.Forsyth                   FT_Stroker_LineJoin  line_join,
613*37da2899SCharles.Forsyth                   FT_Fixed             miter_limit )
614*37da2899SCharles.Forsyth   {
615*37da2899SCharles.Forsyth     stroker->radius      = radius;
616*37da2899SCharles.Forsyth     stroker->line_cap    = line_cap;
617*37da2899SCharles.Forsyth     stroker->line_join   = line_join;
618*37da2899SCharles.Forsyth     stroker->miter_limit = miter_limit;
619*37da2899SCharles.Forsyth 
620*37da2899SCharles.Forsyth     stroker->valid = 0;
621*37da2899SCharles.Forsyth 
622*37da2899SCharles.Forsyth     ft_stroke_border_reset( &stroker->borders[0] );
623*37da2899SCharles.Forsyth     ft_stroke_border_reset( &stroker->borders[1] );
624*37da2899SCharles.Forsyth   }
625*37da2899SCharles.Forsyth 
626*37da2899SCharles.Forsyth 
627*37da2899SCharles.Forsyth   FT_EXPORT_DEF( void )
FT_Stroker_Done(FT_Stroker stroker)628*37da2899SCharles.Forsyth   FT_Stroker_Done( FT_Stroker  stroker )
629*37da2899SCharles.Forsyth   {
630*37da2899SCharles.Forsyth     if ( stroker )
631*37da2899SCharles.Forsyth     {
632*37da2899SCharles.Forsyth       FT_Memory  memory = stroker->memory;
633*37da2899SCharles.Forsyth 
634*37da2899SCharles.Forsyth       ft_stroke_border_done( &stroker->borders[0] );
635*37da2899SCharles.Forsyth       ft_stroke_border_done( &stroker->borders[1] );
636*37da2899SCharles.Forsyth 
637*37da2899SCharles.Forsyth       stroker->memory = NULL;
638*37da2899SCharles.Forsyth       FT_FREE( stroker );
639*37da2899SCharles.Forsyth     }
640*37da2899SCharles.Forsyth   }
641*37da2899SCharles.Forsyth 
642*37da2899SCharles.Forsyth 
643*37da2899SCharles.Forsyth 
644*37da2899SCharles.Forsyth  /* creates a circular arc at a corner or cap */
645*37da2899SCharles.Forsyth   static FT_Error
ft_stroker_arcto(FT_Stroker stroker,FT_Int side)646*37da2899SCharles.Forsyth   ft_stroker_arcto( FT_Stroker  stroker,
647*37da2899SCharles.Forsyth                     FT_Int      side )
648*37da2899SCharles.Forsyth   {
649*37da2899SCharles.Forsyth     FT_Angle          total, rotate;
650*37da2899SCharles.Forsyth     FT_Fixed          radius = stroker->radius;
651*37da2899SCharles.Forsyth     FT_Error          error  = 0;
652*37da2899SCharles.Forsyth     FT_StrokeBorder   border = stroker->borders + side;
653*37da2899SCharles.Forsyth 
654*37da2899SCharles.Forsyth     rotate = FT_SIDE_TO_ROTATE(side);
655*37da2899SCharles.Forsyth 
656*37da2899SCharles.Forsyth     total = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
657*37da2899SCharles.Forsyth     if (total == FT_ANGLE_PI)
658*37da2899SCharles.Forsyth       total = -rotate*2;
659*37da2899SCharles.Forsyth 
660*37da2899SCharles.Forsyth     error = ft_stroke_border_arcto( border,
661*37da2899SCharles.Forsyth                                     &stroker->center,
662*37da2899SCharles.Forsyth                                     radius,
663*37da2899SCharles.Forsyth                                     stroker->angle_in + rotate,
664*37da2899SCharles.Forsyth                                     total );
665*37da2899SCharles.Forsyth     border->movable = 0;
666*37da2899SCharles.Forsyth     return error;
667*37da2899SCharles.Forsyth   }
668*37da2899SCharles.Forsyth 
669*37da2899SCharles.Forsyth 
670*37da2899SCharles.Forsyth   /* adds a cap at the end of an opened path */
671*37da2899SCharles.Forsyth   static FT_Error
ft_stroker_cap(FT_Stroker stroker,FT_Angle angle,FT_Int side)672*37da2899SCharles.Forsyth   ft_stroker_cap( FT_Stroker  stroker,
673*37da2899SCharles.Forsyth                   FT_Angle    angle,
674*37da2899SCharles.Forsyth                   FT_Int      side )
675*37da2899SCharles.Forsyth   {
676*37da2899SCharles.Forsyth     FT_Error  error  = 0;
677*37da2899SCharles.Forsyth 
678*37da2899SCharles.Forsyth     if ( stroker->line_cap == FT_STROKER_LINECAP_ROUND )
679*37da2899SCharles.Forsyth     {
680*37da2899SCharles.Forsyth       /* add a round cap */
681*37da2899SCharles.Forsyth       stroker->angle_in  = angle;
682*37da2899SCharles.Forsyth       stroker->angle_out = angle + FT_ANGLE_PI;
683*37da2899SCharles.Forsyth       error = ft_stroker_arcto( stroker, side );
684*37da2899SCharles.Forsyth     }
685*37da2899SCharles.Forsyth     else if ( stroker->line_cap == FT_STROKER_LINECAP_SQUARE )
686*37da2899SCharles.Forsyth     {
687*37da2899SCharles.Forsyth       /* add a square cap */
688*37da2899SCharles.Forsyth       FT_Vector         delta, delta2;
689*37da2899SCharles.Forsyth       FT_Angle          rotate = FT_SIDE_TO_ROTATE(side);
690*37da2899SCharles.Forsyth       FT_Fixed          radius = stroker->radius;
691*37da2899SCharles.Forsyth       FT_StrokeBorder   border = stroker->borders + side;
692*37da2899SCharles.Forsyth 
693*37da2899SCharles.Forsyth       FT_Vector_From_Polar( &delta2, radius, angle+rotate );
694*37da2899SCharles.Forsyth       FT_Vector_From_Polar( &delta,  radius, angle );
695*37da2899SCharles.Forsyth 
696*37da2899SCharles.Forsyth       delta.x += stroker->center.x + delta2.x;
697*37da2899SCharles.Forsyth       delta.y += stroker->center.y + delta2.y;
698*37da2899SCharles.Forsyth 
699*37da2899SCharles.Forsyth       error = ft_stroke_border_lineto( border, &delta, 0 );
700*37da2899SCharles.Forsyth       if (error) goto Exit;
701*37da2899SCharles.Forsyth 
702*37da2899SCharles.Forsyth       FT_Vector_From_Polar( &delta2, radius, angle-rotate );
703*37da2899SCharles.Forsyth       FT_Vector_From_Polar( &delta,  radius, angle );
704*37da2899SCharles.Forsyth 
705*37da2899SCharles.Forsyth       delta.x += delta2.x + stroker->center.x;
706*37da2899SCharles.Forsyth       delta.y += delta2.y + stroker->center.y;
707*37da2899SCharles.Forsyth 
708*37da2899SCharles.Forsyth       error = ft_stroke_border_lineto( border, &delta, 0 );
709*37da2899SCharles.Forsyth     }
710*37da2899SCharles.Forsyth   Exit:
711*37da2899SCharles.Forsyth     return error;
712*37da2899SCharles.Forsyth   }
713*37da2899SCharles.Forsyth 
714*37da2899SCharles.Forsyth 
715*37da2899SCharles.Forsyth 
716*37da2899SCharles.Forsyth  /* process an inside corner, i.e. compute intersection */
717*37da2899SCharles.Forsyth   static FT_Error
ft_stroker_inside(FT_Stroker stroker,FT_Int side)718*37da2899SCharles.Forsyth   ft_stroker_inside( FT_Stroker  stroker,
719*37da2899SCharles.Forsyth                      FT_Int      side)
720*37da2899SCharles.Forsyth   {
721*37da2899SCharles.Forsyth     FT_StrokeBorder  border = stroker->borders + side;
722*37da2899SCharles.Forsyth     FT_Angle         phi, theta, rotate;
723*37da2899SCharles.Forsyth     FT_Fixed         length, thcos, sigma;
724*37da2899SCharles.Forsyth     FT_Vector        delta;
725*37da2899SCharles.Forsyth     FT_Error         error = 0;
726*37da2899SCharles.Forsyth 
727*37da2899SCharles.Forsyth 
728*37da2899SCharles.Forsyth     rotate = FT_SIDE_TO_ROTATE(side);
729*37da2899SCharles.Forsyth 
730*37da2899SCharles.Forsyth     /* compute median angle */
731*37da2899SCharles.Forsyth     theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
732*37da2899SCharles.Forsyth     if ( theta == FT_ANGLE_PI )
733*37da2899SCharles.Forsyth       theta = rotate;
734*37da2899SCharles.Forsyth     else
735*37da2899SCharles.Forsyth       theta = theta/2;
736*37da2899SCharles.Forsyth 
737*37da2899SCharles.Forsyth     phi = stroker->angle_in + theta;
738*37da2899SCharles.Forsyth 
739*37da2899SCharles.Forsyth     thcos  = FT_Cos( theta );
740*37da2899SCharles.Forsyth     sigma  = FT_MulFix( stroker->miter_limit, thcos );
741*37da2899SCharles.Forsyth 
742*37da2899SCharles.Forsyth     if ( sigma < 0x10000L )
743*37da2899SCharles.Forsyth     {
744*37da2899SCharles.Forsyth       FT_Vector_From_Polar( &delta, stroker->radius, stroker->angle_out + rotate );
745*37da2899SCharles.Forsyth       delta.x += stroker->center.x;
746*37da2899SCharles.Forsyth       delta.y += stroker->center.y;
747*37da2899SCharles.Forsyth       border->movable = 0;
748*37da2899SCharles.Forsyth     }
749*37da2899SCharles.Forsyth     else
750*37da2899SCharles.Forsyth     {
751*37da2899SCharles.Forsyth       length = FT_DivFix( stroker->radius, thcos );
752*37da2899SCharles.Forsyth 
753*37da2899SCharles.Forsyth       FT_Vector_From_Polar( &delta, length, phi + rotate );
754*37da2899SCharles.Forsyth       delta.x += stroker->center.x;
755*37da2899SCharles.Forsyth       delta.y += stroker->center.y;
756*37da2899SCharles.Forsyth     }
757*37da2899SCharles.Forsyth 
758*37da2899SCharles.Forsyth     error = ft_stroke_border_lineto( border, &delta, 0 );
759*37da2899SCharles.Forsyth 
760*37da2899SCharles.Forsyth     return error;
761*37da2899SCharles.Forsyth   }
762*37da2899SCharles.Forsyth 
763*37da2899SCharles.Forsyth 
764*37da2899SCharles.Forsyth  /* process an outside corner, i.e. compute bevel/miter/round */
765*37da2899SCharles.Forsyth   static FT_Error
ft_stroker_outside(FT_Stroker stroker,FT_Int side)766*37da2899SCharles.Forsyth   ft_stroker_outside( FT_Stroker  stroker,
767*37da2899SCharles.Forsyth                       FT_Int      side )
768*37da2899SCharles.Forsyth   {
769*37da2899SCharles.Forsyth     FT_StrokeBorder  border = stroker->borders + side;
770*37da2899SCharles.Forsyth     FT_Error         error;
771*37da2899SCharles.Forsyth     FT_Angle         rotate;
772*37da2899SCharles.Forsyth 
773*37da2899SCharles.Forsyth     if ( stroker->line_join == FT_STROKER_LINEJOIN_ROUND )
774*37da2899SCharles.Forsyth     {
775*37da2899SCharles.Forsyth       error = ft_stroker_arcto( stroker, side );
776*37da2899SCharles.Forsyth     }
777*37da2899SCharles.Forsyth     else
778*37da2899SCharles.Forsyth     {
779*37da2899SCharles.Forsyth       /* this is a mitered or beveled corner */
780*37da2899SCharles.Forsyth       FT_Fixed   sigma, radius = stroker->radius;
781*37da2899SCharles.Forsyth       FT_Angle   theta, phi;
782*37da2899SCharles.Forsyth       FT_Fixed   thcos;
783*37da2899SCharles.Forsyth       FT_Bool    miter;
784*37da2899SCharles.Forsyth 
785*37da2899SCharles.Forsyth       rotate = FT_SIDE_TO_ROTATE(side);
786*37da2899SCharles.Forsyth       miter  = FT_BOOL( stroker->line_join == FT_STROKER_LINEJOIN_MITER );
787*37da2899SCharles.Forsyth 
788*37da2899SCharles.Forsyth       theta  = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
789*37da2899SCharles.Forsyth       if (theta == FT_ANGLE_PI)
790*37da2899SCharles.Forsyth         theta = rotate;
791*37da2899SCharles.Forsyth       else
792*37da2899SCharles.Forsyth         theta = theta/2;
793*37da2899SCharles.Forsyth 
794*37da2899SCharles.Forsyth       thcos  = FT_Cos( theta );
795*37da2899SCharles.Forsyth       sigma  = FT_MulFix( stroker->miter_limit, thcos );
796*37da2899SCharles.Forsyth 
797*37da2899SCharles.Forsyth       if ( sigma >= 0x10000L )
798*37da2899SCharles.Forsyth         miter = 0;
799*37da2899SCharles.Forsyth 
800*37da2899SCharles.Forsyth       phi = stroker->angle_in + theta + rotate;
801*37da2899SCharles.Forsyth 
802*37da2899SCharles.Forsyth       if (miter)  /* this is a miter (broken angle) */
803*37da2899SCharles.Forsyth       {
804*37da2899SCharles.Forsyth         FT_Vector  middle, delta;
805*37da2899SCharles.Forsyth         FT_Fixed  length;
806*37da2899SCharles.Forsyth 
807*37da2899SCharles.Forsyth         /* compute middle point */
808*37da2899SCharles.Forsyth         FT_Vector_From_Polar( &middle, FT_MulFix( radius, stroker->miter_limit ),
809*37da2899SCharles.Forsyth                               phi );
810*37da2899SCharles.Forsyth         middle.x += stroker->center.x;
811*37da2899SCharles.Forsyth         middle.y += stroker->center.y;
812*37da2899SCharles.Forsyth 
813*37da2899SCharles.Forsyth         /* compute first angle point */
814*37da2899SCharles.Forsyth         length = FT_MulFix( radius, FT_DivFix( 0x10000L - sigma,
815*37da2899SCharles.Forsyth                                     ft_pos_abs( FT_Sin( theta ) ) ) );
816*37da2899SCharles.Forsyth 
817*37da2899SCharles.Forsyth         FT_Vector_From_Polar( &delta, length, phi + rotate );
818*37da2899SCharles.Forsyth         delta.x += middle.x;
819*37da2899SCharles.Forsyth         delta.y += middle.y;
820*37da2899SCharles.Forsyth 
821*37da2899SCharles.Forsyth         error = ft_stroke_border_lineto( border, &delta, 0 );
822*37da2899SCharles.Forsyth         if (error) goto Exit;
823*37da2899SCharles.Forsyth 
824*37da2899SCharles.Forsyth         /* compute second angle point */
825*37da2899SCharles.Forsyth         FT_Vector_From_Polar( &delta, length, phi - rotate );
826*37da2899SCharles.Forsyth         delta.x += middle.x;
827*37da2899SCharles.Forsyth         delta.y += middle.y;
828*37da2899SCharles.Forsyth 
829*37da2899SCharles.Forsyth         error = ft_stroke_border_lineto( border, &delta, 0 );
830*37da2899SCharles.Forsyth         if (error) goto Exit;
831*37da2899SCharles.Forsyth 
832*37da2899SCharles.Forsyth         /* finally, add a movable end point */
833*37da2899SCharles.Forsyth         FT_Vector_From_Polar( &delta, radius, stroker->angle_out + rotate );
834*37da2899SCharles.Forsyth         delta.x += stroker->center.x;
835*37da2899SCharles.Forsyth         delta.y += stroker->center.y;
836*37da2899SCharles.Forsyth 
837*37da2899SCharles.Forsyth         error = ft_stroke_border_lineto( border, &delta, 1 );
838*37da2899SCharles.Forsyth       }
839*37da2899SCharles.Forsyth       else /* this is a bevel (intersection) */
840*37da2899SCharles.Forsyth       {
841*37da2899SCharles.Forsyth         FT_Fixed  length;
842*37da2899SCharles.Forsyth         FT_Vector  delta;
843*37da2899SCharles.Forsyth 
844*37da2899SCharles.Forsyth         length = FT_DivFix( stroker->radius, thcos );
845*37da2899SCharles.Forsyth 
846*37da2899SCharles.Forsyth         FT_Vector_From_Polar( &delta, length, phi );
847*37da2899SCharles.Forsyth         delta.x += stroker->center.x;
848*37da2899SCharles.Forsyth         delta.y += stroker->center.y;
849*37da2899SCharles.Forsyth 
850*37da2899SCharles.Forsyth         error = ft_stroke_border_lineto( border, &delta, 0 );
851*37da2899SCharles.Forsyth         if (error) goto Exit;
852*37da2899SCharles.Forsyth 
853*37da2899SCharles.Forsyth         /* now add end point */
854*37da2899SCharles.Forsyth         FT_Vector_From_Polar( &delta, stroker->radius, stroker->angle_out + rotate );
855*37da2899SCharles.Forsyth         delta.x += stroker->center.x;
856*37da2899SCharles.Forsyth         delta.y += stroker->center.y;
857*37da2899SCharles.Forsyth 
858*37da2899SCharles.Forsyth         error = ft_stroke_border_lineto( border, &delta, 1 );
859*37da2899SCharles.Forsyth       }
860*37da2899SCharles.Forsyth     }
861*37da2899SCharles.Forsyth   Exit:
862*37da2899SCharles.Forsyth     return error;
863*37da2899SCharles.Forsyth   }
864*37da2899SCharles.Forsyth 
865*37da2899SCharles.Forsyth 
866*37da2899SCharles.Forsyth   static FT_Error
ft_stroker_process_corner(FT_Stroker stroker)867*37da2899SCharles.Forsyth   ft_stroker_process_corner( FT_Stroker  stroker )
868*37da2899SCharles.Forsyth   {
869*37da2899SCharles.Forsyth     FT_Error  error = 0;
870*37da2899SCharles.Forsyth     FT_Angle  turn;
871*37da2899SCharles.Forsyth     FT_Int    inside_side;
872*37da2899SCharles.Forsyth 
873*37da2899SCharles.Forsyth     turn = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
874*37da2899SCharles.Forsyth 
875*37da2899SCharles.Forsyth     /* no specific corner processing is required if the turn is 0 */
876*37da2899SCharles.Forsyth     if (turn == 0)
877*37da2899SCharles.Forsyth       goto Exit;
878*37da2899SCharles.Forsyth 
879*37da2899SCharles.Forsyth     /* when we turn to the right, the inside side is 0 */
880*37da2899SCharles.Forsyth     inside_side = 0;
881*37da2899SCharles.Forsyth 
882*37da2899SCharles.Forsyth     /* otherwise, the inside side is 1 */
883*37da2899SCharles.Forsyth     if (turn < 0)
884*37da2899SCharles.Forsyth       inside_side = 1;
885*37da2899SCharles.Forsyth 
886*37da2899SCharles.Forsyth     /* process the inside side */
887*37da2899SCharles.Forsyth     error = ft_stroker_inside( stroker, inside_side );
888*37da2899SCharles.Forsyth     if (error) goto Exit;
889*37da2899SCharles.Forsyth 
890*37da2899SCharles.Forsyth     /* process the outside side */
891*37da2899SCharles.Forsyth     error = ft_stroker_outside( stroker, 1-inside_side );
892*37da2899SCharles.Forsyth 
893*37da2899SCharles.Forsyth   Exit:
894*37da2899SCharles.Forsyth     return error;
895*37da2899SCharles.Forsyth   }
896*37da2899SCharles.Forsyth 
897*37da2899SCharles.Forsyth 
898*37da2899SCharles.Forsyth  /* add two points to the left and right borders corresponding to the */
899*37da2899SCharles.Forsyth  /* start of the subpath..                                            */
900*37da2899SCharles.Forsyth   static FT_Error
ft_stroker_subpath_start(FT_Stroker stroker,FT_Angle start_angle)901*37da2899SCharles.Forsyth   ft_stroker_subpath_start( FT_Stroker  stroker,
902*37da2899SCharles.Forsyth                             FT_Angle    start_angle )
903*37da2899SCharles.Forsyth   {
904*37da2899SCharles.Forsyth     FT_Vector        delta;
905*37da2899SCharles.Forsyth     FT_Vector        point;
906*37da2899SCharles.Forsyth     FT_Error         error;
907*37da2899SCharles.Forsyth     FT_StrokeBorder  border;
908*37da2899SCharles.Forsyth 
909*37da2899SCharles.Forsyth     FT_Vector_From_Polar( &delta, stroker->radius, start_angle + FT_ANGLE_PI2 );
910*37da2899SCharles.Forsyth 
911*37da2899SCharles.Forsyth     point.x = stroker->center.x + delta.x;
912*37da2899SCharles.Forsyth     point.y = stroker->center.y + delta.y;
913*37da2899SCharles.Forsyth 
914*37da2899SCharles.Forsyth     border = stroker->borders;
915*37da2899SCharles.Forsyth     error = ft_stroke_border_moveto( border, &point );
916*37da2899SCharles.Forsyth     if (error) goto Exit;
917*37da2899SCharles.Forsyth 
918*37da2899SCharles.Forsyth     point.x = stroker->center.x - delta.x;
919*37da2899SCharles.Forsyth     point.y = stroker->center.y - delta.y;
920*37da2899SCharles.Forsyth 
921*37da2899SCharles.Forsyth     border++;
922*37da2899SCharles.Forsyth     error = ft_stroke_border_moveto( border, &point );
923*37da2899SCharles.Forsyth 
924*37da2899SCharles.Forsyth     /* save angle for last cap */
925*37da2899SCharles.Forsyth     stroker->subpath_angle = start_angle;
926*37da2899SCharles.Forsyth     stroker->first_point   = 0;
927*37da2899SCharles.Forsyth 
928*37da2899SCharles.Forsyth   Exit:
929*37da2899SCharles.Forsyth     return error;
930*37da2899SCharles.Forsyth   }
931*37da2899SCharles.Forsyth 
932*37da2899SCharles.Forsyth 
933*37da2899SCharles.Forsyth   FT_EXPORT_DEF( FT_Error )
FT_Stroker_LineTo(FT_Stroker stroker,FT_Vector * to)934*37da2899SCharles.Forsyth   FT_Stroker_LineTo( FT_Stroker  stroker,
935*37da2899SCharles.Forsyth                      FT_Vector*  to )
936*37da2899SCharles.Forsyth   {
937*37da2899SCharles.Forsyth     FT_Error         error = 0;
938*37da2899SCharles.Forsyth     FT_StrokeBorder  border;
939*37da2899SCharles.Forsyth     FT_Vector        delta;
940*37da2899SCharles.Forsyth     FT_Angle         angle;
941*37da2899SCharles.Forsyth     FT_Int           side;
942*37da2899SCharles.Forsyth 
943*37da2899SCharles.Forsyth     delta.x = to->x - stroker->center.x;
944*37da2899SCharles.Forsyth     delta.y = to->y - stroker->center.y;
945*37da2899SCharles.Forsyth 
946*37da2899SCharles.Forsyth     angle = FT_Atan2( delta.x, delta.y );
947*37da2899SCharles.Forsyth     FT_Vector_From_Polar( &delta, stroker->radius, angle + FT_ANGLE_PI2 );
948*37da2899SCharles.Forsyth 
949*37da2899SCharles.Forsyth     /* process corner if necessary */
950*37da2899SCharles.Forsyth     if ( stroker->first_point )
951*37da2899SCharles.Forsyth     {
952*37da2899SCharles.Forsyth       /* this is the first segment of a subpath. We need to      */
953*37da2899SCharles.Forsyth       /* add a point to each border at their respective starting */
954*37da2899SCharles.Forsyth       /* point locations..                                       */
955*37da2899SCharles.Forsyth       error = ft_stroker_subpath_start( stroker, angle );
956*37da2899SCharles.Forsyth       if (error) goto Exit;
957*37da2899SCharles.Forsyth     }
958*37da2899SCharles.Forsyth     else
959*37da2899SCharles.Forsyth     {
960*37da2899SCharles.Forsyth       /* process the current corner */
961*37da2899SCharles.Forsyth       stroker->angle_out = angle;
962*37da2899SCharles.Forsyth       error = ft_stroker_process_corner( stroker );
963*37da2899SCharles.Forsyth       if (error) goto Exit;
964*37da2899SCharles.Forsyth     }
965*37da2899SCharles.Forsyth 
966*37da2899SCharles.Forsyth     /* now add a line segment to both the "inside" and "outside" paths */
967*37da2899SCharles.Forsyth 
968*37da2899SCharles.Forsyth     for ( border = stroker->borders, side = 1; side >= 0; side--, border++ )
969*37da2899SCharles.Forsyth     {
970*37da2899SCharles.Forsyth       FT_Vector  point;
971*37da2899SCharles.Forsyth 
972*37da2899SCharles.Forsyth       point.x = to->x + delta.x;
973*37da2899SCharles.Forsyth       point.y = to->y + delta.y;
974*37da2899SCharles.Forsyth 
975*37da2899SCharles.Forsyth       error = ft_stroke_border_lineto( border, &point, 1 );
976*37da2899SCharles.Forsyth       if (error) goto Exit;
977*37da2899SCharles.Forsyth 
978*37da2899SCharles.Forsyth       delta.x = -delta.x;
979*37da2899SCharles.Forsyth       delta.y = -delta.y;
980*37da2899SCharles.Forsyth     }
981*37da2899SCharles.Forsyth 
982*37da2899SCharles.Forsyth     stroker->angle_in  = angle;
983*37da2899SCharles.Forsyth     stroker->center    = *to;
984*37da2899SCharles.Forsyth 
985*37da2899SCharles.Forsyth   Exit:
986*37da2899SCharles.Forsyth     return error;
987*37da2899SCharles.Forsyth   }
988*37da2899SCharles.Forsyth 
989*37da2899SCharles.Forsyth 
990*37da2899SCharles.Forsyth 
991*37da2899SCharles.Forsyth   FT_EXPORT_DEF( FT_Error )
FT_Stroker_ConicTo(FT_Stroker stroker,FT_Vector * control,FT_Vector * to)992*37da2899SCharles.Forsyth   FT_Stroker_ConicTo( FT_Stroker  stroker,
993*37da2899SCharles.Forsyth                       FT_Vector*  control,
994*37da2899SCharles.Forsyth                       FT_Vector*  to )
995*37da2899SCharles.Forsyth   {
996*37da2899SCharles.Forsyth     FT_Error    error = 0;
997*37da2899SCharles.Forsyth     FT_Vector   bez_stack[34];
998*37da2899SCharles.Forsyth     FT_Vector*  arc;
999*37da2899SCharles.Forsyth     FT_Vector*  limit = bez_stack + 30;
1000*37da2899SCharles.Forsyth     FT_Angle    start_angle;
1001*37da2899SCharles.Forsyth     FT_Bool     first_arc = 1;
1002*37da2899SCharles.Forsyth 
1003*37da2899SCharles.Forsyth     arc    = bez_stack;
1004*37da2899SCharles.Forsyth     arc[0] = *to;
1005*37da2899SCharles.Forsyth     arc[1] = *control;
1006*37da2899SCharles.Forsyth     arc[2] = stroker->center;
1007*37da2899SCharles.Forsyth 
1008*37da2899SCharles.Forsyth     while ( arc >= bez_stack )
1009*37da2899SCharles.Forsyth     {
1010*37da2899SCharles.Forsyth       FT_Angle  angle_in, angle_out;
1011*37da2899SCharles.Forsyth 
1012*37da2899SCharles.Forsyth       angle_in = angle_out = 0;  /* remove compiler warnings */
1013*37da2899SCharles.Forsyth 
1014*37da2899SCharles.Forsyth       if ( arc < limit &&
1015*37da2899SCharles.Forsyth            !ft_conic_is_small_enough( arc, &angle_in, &angle_out ) )
1016*37da2899SCharles.Forsyth       {
1017*37da2899SCharles.Forsyth         ft_conic_split( arc );
1018*37da2899SCharles.Forsyth         arc += 2;
1019*37da2899SCharles.Forsyth         continue;
1020*37da2899SCharles.Forsyth       }
1021*37da2899SCharles.Forsyth 
1022*37da2899SCharles.Forsyth       if ( first_arc )
1023*37da2899SCharles.Forsyth       {
1024*37da2899SCharles.Forsyth         first_arc = 0;
1025*37da2899SCharles.Forsyth 
1026*37da2899SCharles.Forsyth         start_angle = angle_in;
1027*37da2899SCharles.Forsyth 
1028*37da2899SCharles.Forsyth         /* process corner if necessary */
1029*37da2899SCharles.Forsyth         if ( stroker->first_point )
1030*37da2899SCharles.Forsyth           error = ft_stroker_subpath_start( stroker, start_angle );
1031*37da2899SCharles.Forsyth         else
1032*37da2899SCharles.Forsyth         {
1033*37da2899SCharles.Forsyth           stroker->angle_out = start_angle;
1034*37da2899SCharles.Forsyth           error = ft_stroker_process_corner( stroker );
1035*37da2899SCharles.Forsyth         }
1036*37da2899SCharles.Forsyth       }
1037*37da2899SCharles.Forsyth 
1038*37da2899SCharles.Forsyth       /* the arc's angle is small enough, we can add it directly to each */
1039*37da2899SCharles.Forsyth       /* border..                                                        */
1040*37da2899SCharles.Forsyth       {
1041*37da2899SCharles.Forsyth         FT_Vector  ctrl, end;
1042*37da2899SCharles.Forsyth         FT_Angle   theta, phi, rotate;
1043*37da2899SCharles.Forsyth         FT_Fixed  length;
1044*37da2899SCharles.Forsyth         FT_Int     side;
1045*37da2899SCharles.Forsyth 
1046*37da2899SCharles.Forsyth         theta  = FT_Angle_Diff( angle_in, angle_out )/2;
1047*37da2899SCharles.Forsyth         phi    = angle_in + theta;
1048*37da2899SCharles.Forsyth         length = FT_DivFix( stroker->radius, FT_Cos(theta) );
1049*37da2899SCharles.Forsyth 
1050*37da2899SCharles.Forsyth         for ( side = 0; side <= 1; side++ )
1051*37da2899SCharles.Forsyth         {
1052*37da2899SCharles.Forsyth           rotate = FT_SIDE_TO_ROTATE(side);
1053*37da2899SCharles.Forsyth 
1054*37da2899SCharles.Forsyth           /* compute control point */
1055*37da2899SCharles.Forsyth           FT_Vector_From_Polar( &ctrl, length, phi + rotate );
1056*37da2899SCharles.Forsyth           ctrl.x += arc[1].x;
1057*37da2899SCharles.Forsyth           ctrl.y += arc[1].y;
1058*37da2899SCharles.Forsyth 
1059*37da2899SCharles.Forsyth           /* compute end point */
1060*37da2899SCharles.Forsyth           FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate );
1061*37da2899SCharles.Forsyth           end.x += arc[0].x;
1062*37da2899SCharles.Forsyth           end.y += arc[0].y;
1063*37da2899SCharles.Forsyth 
1064*37da2899SCharles.Forsyth           error = ft_stroke_border_conicto( stroker->borders + side, &ctrl, &end );
1065*37da2899SCharles.Forsyth           if (error) goto Exit;
1066*37da2899SCharles.Forsyth         }
1067*37da2899SCharles.Forsyth       }
1068*37da2899SCharles.Forsyth 
1069*37da2899SCharles.Forsyth       arc -= 2;
1070*37da2899SCharles.Forsyth 
1071*37da2899SCharles.Forsyth       if (arc < bez_stack)
1072*37da2899SCharles.Forsyth         stroker->angle_in = angle_out;
1073*37da2899SCharles.Forsyth     }
1074*37da2899SCharles.Forsyth 
1075*37da2899SCharles.Forsyth     stroker->center = *to;
1076*37da2899SCharles.Forsyth 
1077*37da2899SCharles.Forsyth   Exit:
1078*37da2899SCharles.Forsyth     return error;
1079*37da2899SCharles.Forsyth   }
1080*37da2899SCharles.Forsyth 
1081*37da2899SCharles.Forsyth 
1082*37da2899SCharles.Forsyth 
1083*37da2899SCharles.Forsyth   FT_EXPORT_DEF( FT_Error )
FT_Stroker_CubicTo(FT_Stroker stroker,FT_Vector * control1,FT_Vector * control2,FT_Vector * to)1084*37da2899SCharles.Forsyth   FT_Stroker_CubicTo( FT_Stroker  stroker,
1085*37da2899SCharles.Forsyth                       FT_Vector*      control1,
1086*37da2899SCharles.Forsyth                       FT_Vector*      control2,
1087*37da2899SCharles.Forsyth                       FT_Vector*      to )
1088*37da2899SCharles.Forsyth   {
1089*37da2899SCharles.Forsyth     FT_Error    error = 0;
1090*37da2899SCharles.Forsyth     FT_Vector   bez_stack[37];
1091*37da2899SCharles.Forsyth     FT_Vector*  arc;
1092*37da2899SCharles.Forsyth     FT_Vector*  limit = bez_stack + 32;
1093*37da2899SCharles.Forsyth     FT_Angle    start_angle;
1094*37da2899SCharles.Forsyth     FT_Bool     first_arc = 1;
1095*37da2899SCharles.Forsyth 
1096*37da2899SCharles.Forsyth     arc    = bez_stack;
1097*37da2899SCharles.Forsyth     arc[0] = *to;
1098*37da2899SCharles.Forsyth     arc[1] = *control2;
1099*37da2899SCharles.Forsyth     arc[2] = *control1;
1100*37da2899SCharles.Forsyth     arc[3] = stroker->center;
1101*37da2899SCharles.Forsyth 
1102*37da2899SCharles.Forsyth     while ( arc >= bez_stack )
1103*37da2899SCharles.Forsyth     {
1104*37da2899SCharles.Forsyth       FT_Angle  angle_in, angle_mid, angle_out;
1105*37da2899SCharles.Forsyth 
1106*37da2899SCharles.Forsyth       /* remove compiler warnings */
1107*37da2899SCharles.Forsyth       angle_in = angle_out = angle_mid = 0;
1108*37da2899SCharles.Forsyth 
1109*37da2899SCharles.Forsyth       if ( arc < limit &&
1110*37da2899SCharles.Forsyth            !ft_cubic_is_small_enough( arc, &angle_in, &angle_mid, &angle_out ) )
1111*37da2899SCharles.Forsyth       {
1112*37da2899SCharles.Forsyth         ft_cubic_split( arc );
1113*37da2899SCharles.Forsyth         arc += 3;
1114*37da2899SCharles.Forsyth         continue;
1115*37da2899SCharles.Forsyth       }
1116*37da2899SCharles.Forsyth 
1117*37da2899SCharles.Forsyth       if ( first_arc )
1118*37da2899SCharles.Forsyth       {
1119*37da2899SCharles.Forsyth         first_arc = 0;
1120*37da2899SCharles.Forsyth 
1121*37da2899SCharles.Forsyth         /* process corner if necessary */
1122*37da2899SCharles.Forsyth         start_angle = angle_in;
1123*37da2899SCharles.Forsyth 
1124*37da2899SCharles.Forsyth         if ( stroker->first_point )
1125*37da2899SCharles.Forsyth           error = ft_stroker_subpath_start( stroker, start_angle );
1126*37da2899SCharles.Forsyth         else
1127*37da2899SCharles.Forsyth             {
1128*37da2899SCharles.Forsyth               stroker->angle_out = start_angle;
1129*37da2899SCharles.Forsyth               error = ft_stroker_process_corner( stroker );
1130*37da2899SCharles.Forsyth             }
1131*37da2899SCharles.Forsyth         if (error) goto Exit;
1132*37da2899SCharles.Forsyth       }
1133*37da2899SCharles.Forsyth 
1134*37da2899SCharles.Forsyth       /* the arc's angle is small enough, we can add it directly to each */
1135*37da2899SCharles.Forsyth       /* border..                                                        */
1136*37da2899SCharles.Forsyth       {
1137*37da2899SCharles.Forsyth         FT_Vector  ctrl1, ctrl2, end;
1138*37da2899SCharles.Forsyth         FT_Angle   theta1, phi1, theta2, phi2, rotate;
1139*37da2899SCharles.Forsyth         FT_Fixed   length1, length2;
1140*37da2899SCharles.Forsyth         FT_Int     side;
1141*37da2899SCharles.Forsyth 
1142*37da2899SCharles.Forsyth         theta1  = ft_pos_abs( angle_mid - angle_in )/2;
1143*37da2899SCharles.Forsyth         theta2  = ft_pos_abs( angle_out - angle_mid )/2;
1144*37da2899SCharles.Forsyth         phi1    = (angle_mid+angle_in)/2;
1145*37da2899SCharles.Forsyth         phi2    = (angle_mid+angle_out)/2;
1146*37da2899SCharles.Forsyth         length1 = FT_DivFix( stroker->radius, FT_Cos(theta1) );
1147*37da2899SCharles.Forsyth         length2 = FT_DivFix( stroker->radius, FT_Cos(theta2) );
1148*37da2899SCharles.Forsyth 
1149*37da2899SCharles.Forsyth         for ( side = 0; side <= 1; side++ )
1150*37da2899SCharles.Forsyth         {
1151*37da2899SCharles.Forsyth           rotate = FT_SIDE_TO_ROTATE(side);
1152*37da2899SCharles.Forsyth 
1153*37da2899SCharles.Forsyth           /* compute control points */
1154*37da2899SCharles.Forsyth           FT_Vector_From_Polar( &ctrl1, length1, phi1 + rotate );
1155*37da2899SCharles.Forsyth           ctrl1.x += arc[2].x;
1156*37da2899SCharles.Forsyth           ctrl1.y += arc[2].y;
1157*37da2899SCharles.Forsyth 
1158*37da2899SCharles.Forsyth           FT_Vector_From_Polar( &ctrl2, length2, phi2 + rotate );
1159*37da2899SCharles.Forsyth           ctrl2.x += arc[1].x;
1160*37da2899SCharles.Forsyth           ctrl2.y += arc[1].y;
1161*37da2899SCharles.Forsyth 
1162*37da2899SCharles.Forsyth           /* compute end point */
1163*37da2899SCharles.Forsyth           FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate );
1164*37da2899SCharles.Forsyth           end.x += arc[0].x;
1165*37da2899SCharles.Forsyth           end.y += arc[0].y;
1166*37da2899SCharles.Forsyth 
1167*37da2899SCharles.Forsyth           error = ft_stroke_border_cubicto( stroker->borders + side, &ctrl1, &ctrl2, &end );
1168*37da2899SCharles.Forsyth           if (error) goto Exit;
1169*37da2899SCharles.Forsyth         }
1170*37da2899SCharles.Forsyth       }
1171*37da2899SCharles.Forsyth 
1172*37da2899SCharles.Forsyth       arc -= 3;
1173*37da2899SCharles.Forsyth       if (arc < bez_stack)
1174*37da2899SCharles.Forsyth         stroker->angle_in = angle_out;
1175*37da2899SCharles.Forsyth     }
1176*37da2899SCharles.Forsyth 
1177*37da2899SCharles.Forsyth     stroker->center = *to;
1178*37da2899SCharles.Forsyth 
1179*37da2899SCharles.Forsyth   Exit:
1180*37da2899SCharles.Forsyth     return error;
1181*37da2899SCharles.Forsyth   }
1182*37da2899SCharles.Forsyth 
1183*37da2899SCharles.Forsyth 
1184*37da2899SCharles.Forsyth   FT_EXPORT_DEF( FT_Error )
FT_Stroker_BeginSubPath(FT_Stroker stroker,FT_Vector * to,FT_Bool open)1185*37da2899SCharles.Forsyth   FT_Stroker_BeginSubPath( FT_Stroker  stroker,
1186*37da2899SCharles.Forsyth                            FT_Vector*  to,
1187*37da2899SCharles.Forsyth                            FT_Bool     open )
1188*37da2899SCharles.Forsyth   {
1189*37da2899SCharles.Forsyth     /* we cannot process the first point, because there is not enough     */
1190*37da2899SCharles.Forsyth     /* information regarding its corner/cap. The latter will be processed */
1191*37da2899SCharles.Forsyth     /* in the "end_subpath" routine                                       */
1192*37da2899SCharles.Forsyth     /*                                                                    */
1193*37da2899SCharles.Forsyth     stroker->first_point   = 1;
1194*37da2899SCharles.Forsyth     stroker->center        = *to;
1195*37da2899SCharles.Forsyth     stroker->subpath_open  = open;
1196*37da2899SCharles.Forsyth 
1197*37da2899SCharles.Forsyth     /* record the subpath start point index for each border */
1198*37da2899SCharles.Forsyth     stroker->subpath_start = *to;
1199*37da2899SCharles.Forsyth     return 0;
1200*37da2899SCharles.Forsyth   }
1201*37da2899SCharles.Forsyth 
1202*37da2899SCharles.Forsyth 
1203*37da2899SCharles.Forsyth   static
ft_stroker_add_reverse_left(FT_Stroker stroker,FT_Bool open)1204*37da2899SCharles.Forsyth   FT_Error  ft_stroker_add_reverse_left( FT_Stroker  stroker,
1205*37da2899SCharles.Forsyth                                          FT_Bool     open )
1206*37da2899SCharles.Forsyth   {
1207*37da2899SCharles.Forsyth     FT_StrokeBorder  right  = stroker->borders + 0;
1208*37da2899SCharles.Forsyth     FT_StrokeBorder  left   = stroker->borders + 1;
1209*37da2899SCharles.Forsyth     FT_Int           new_points;
1210*37da2899SCharles.Forsyth     FT_Error         error  = 0;
1211*37da2899SCharles.Forsyth 
1212*37da2899SCharles.Forsyth     FT_ASSERT( left->start >= 0 );
1213*37da2899SCharles.Forsyth 
1214*37da2899SCharles.Forsyth     new_points = left->num_points - left->start;
1215*37da2899SCharles.Forsyth     if ( new_points > 0 )
1216*37da2899SCharles.Forsyth     {
1217*37da2899SCharles.Forsyth       error = ft_stroke_border_grow( right, (FT_UInt)new_points );
1218*37da2899SCharles.Forsyth       if (error) goto Exit;
1219*37da2899SCharles.Forsyth       {
1220*37da2899SCharles.Forsyth         FT_Vector*    dst_point = right->points + right->num_points;
1221*37da2899SCharles.Forsyth         FT_Byte*      dst_tag   = right->tags   + right->num_points;
1222*37da2899SCharles.Forsyth         FT_Vector*    src_point = left->points  + left->num_points - 1;
1223*37da2899SCharles.Forsyth         FT_Byte*      src_tag   = left->tags    + left->num_points - 1;
1224*37da2899SCharles.Forsyth 
1225*37da2899SCharles.Forsyth         while ( src_point >= left->points + left->start )
1226*37da2899SCharles.Forsyth         {
1227*37da2899SCharles.Forsyth           *dst_point = *src_point;
1228*37da2899SCharles.Forsyth           *dst_tag   = *src_tag;
1229*37da2899SCharles.Forsyth 
1230*37da2899SCharles.Forsyth           if (open)
1231*37da2899SCharles.Forsyth             dst_tag[0] &= ~(FT_STROKE_TAG_BEGIN | FT_STROKE_TAG_END);
1232*37da2899SCharles.Forsyth           else
1233*37da2899SCharles.Forsyth           {
1234*37da2899SCharles.Forsyth             /* switch begin/end tags if necessary.. */
1235*37da2899SCharles.Forsyth             if (dst_tag[0] & (FT_STROKE_TAG_BEGIN | FT_STROKE_TAG_END))
1236*37da2899SCharles.Forsyth               dst_tag[0] ^= (FT_STROKE_TAG_BEGIN | FT_STROKE_TAG_END);
1237*37da2899SCharles.Forsyth           }
1238*37da2899SCharles.Forsyth 
1239*37da2899SCharles.Forsyth           src_point--;
1240*37da2899SCharles.Forsyth           src_tag--;
1241*37da2899SCharles.Forsyth           dst_point++;
1242*37da2899SCharles.Forsyth           dst_tag++;
1243*37da2899SCharles.Forsyth         }
1244*37da2899SCharles.Forsyth       }
1245*37da2899SCharles.Forsyth       left->num_points   = left->start;
1246*37da2899SCharles.Forsyth       right->num_points += new_points;
1247*37da2899SCharles.Forsyth 
1248*37da2899SCharles.Forsyth       right->movable = 0;
1249*37da2899SCharles.Forsyth       left->movable  = 0;
1250*37da2899SCharles.Forsyth     }
1251*37da2899SCharles.Forsyth   Exit:
1252*37da2899SCharles.Forsyth     return error;
1253*37da2899SCharles.Forsyth   }
1254*37da2899SCharles.Forsyth 
1255*37da2899SCharles.Forsyth 
1256*37da2899SCharles.Forsyth  /* there's a lot of magic in this function !! */
1257*37da2899SCharles.Forsyth   FT_EXPORT_DEF( FT_Error )
FT_Stroker_EndSubPath(FT_Stroker stroker)1258*37da2899SCharles.Forsyth   FT_Stroker_EndSubPath( FT_Stroker  stroker )
1259*37da2899SCharles.Forsyth   {
1260*37da2899SCharles.Forsyth     FT_Error  error  = 0;
1261*37da2899SCharles.Forsyth 
1262*37da2899SCharles.Forsyth     if ( stroker->subpath_open )
1263*37da2899SCharles.Forsyth     {
1264*37da2899SCharles.Forsyth       FT_StrokeBorder  right = stroker->borders;
1265*37da2899SCharles.Forsyth 
1266*37da2899SCharles.Forsyth       /* all right, this is an opened path, we need to add a cap between     */
1267*37da2899SCharles.Forsyth       /* right & left, add the reverse of left, then add a final cap between */
1268*37da2899SCharles.Forsyth       /* left & right..                                                      */
1269*37da2899SCharles.Forsyth       error = ft_stroker_cap( stroker, stroker->angle_in, 0 );
1270*37da2899SCharles.Forsyth       if (error) goto Exit;
1271*37da2899SCharles.Forsyth 
1272*37da2899SCharles.Forsyth       /* add reversed points from "left" to "right" */
1273*37da2899SCharles.Forsyth       error = ft_stroker_add_reverse_left( stroker, 1 );
1274*37da2899SCharles.Forsyth       if (error) goto Exit;
1275*37da2899SCharles.Forsyth 
1276*37da2899SCharles.Forsyth       /* now add the final cap */
1277*37da2899SCharles.Forsyth       stroker->center = stroker->subpath_start;
1278*37da2899SCharles.Forsyth       error = ft_stroker_cap( stroker, stroker->subpath_angle+FT_ANGLE_PI, 0 );
1279*37da2899SCharles.Forsyth       if (error) goto Exit;
1280*37da2899SCharles.Forsyth 
1281*37da2899SCharles.Forsyth       /* now, end the right subpath accordingly. the left one is */
1282*37da2899SCharles.Forsyth       /* rewind and doesn't need further processing..            */
1283*37da2899SCharles.Forsyth       ft_stroke_border_close( right );
1284*37da2899SCharles.Forsyth     }
1285*37da2899SCharles.Forsyth     else
1286*37da2899SCharles.Forsyth     {
1287*37da2899SCharles.Forsyth       FT_Angle           turn;
1288*37da2899SCharles.Forsyth       FT_Int             inside_side;
1289*37da2899SCharles.Forsyth 
1290*37da2899SCharles.Forsyth       /* process the corner ... */
1291*37da2899SCharles.Forsyth       stroker->angle_out = stroker->subpath_angle;
1292*37da2899SCharles.Forsyth       turn               = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
1293*37da2899SCharles.Forsyth 
1294*37da2899SCharles.Forsyth       /* no specific corner processing is required if the turn is 0 */
1295*37da2899SCharles.Forsyth       if (turn != 0)
1296*37da2899SCharles.Forsyth       {
1297*37da2899SCharles.Forsyth         /* when we turn to the right, the inside side is 0 */
1298*37da2899SCharles.Forsyth         inside_side = 0;
1299*37da2899SCharles.Forsyth 
1300*37da2899SCharles.Forsyth         /* otherwise, the inside side is 1 */
1301*37da2899SCharles.Forsyth         if (turn < 0)
1302*37da2899SCharles.Forsyth           inside_side = 1;
1303*37da2899SCharles.Forsyth 
1304*37da2899SCharles.Forsyth         /* IMPORTANT: WE DO NOT PROCESS THE INSIDE BORDER HERE !! */
1305*37da2899SCharles.Forsyth         /* process the inside side */
1306*37da2899SCharles.Forsyth         /* error = ft_stroker_inside( stroker, inside_side );
1307*37da2899SCharles.Forsyth              if (error) goto Exit; */
1308*37da2899SCharles.Forsyth 
1309*37da2899SCharles.Forsyth         /* process the outside side */
1310*37da2899SCharles.Forsyth         error = ft_stroker_outside( stroker, 1-inside_side );
1311*37da2899SCharles.Forsyth         if (error) goto Exit;
1312*37da2899SCharles.Forsyth       }
1313*37da2899SCharles.Forsyth 
1314*37da2899SCharles.Forsyth       /* we will first end our two subpaths */
1315*37da2899SCharles.Forsyth       ft_stroke_border_close( stroker->borders + 0 );
1316*37da2899SCharles.Forsyth       ft_stroke_border_close( stroker->borders + 1 );
1317*37da2899SCharles.Forsyth 
1318*37da2899SCharles.Forsyth       /* now, add the reversed left subpath to "right" */
1319*37da2899SCharles.Forsyth       error = ft_stroker_add_reverse_left( stroker, 0 );
1320*37da2899SCharles.Forsyth       if (error) goto Exit;
1321*37da2899SCharles.Forsyth     }
1322*37da2899SCharles.Forsyth 
1323*37da2899SCharles.Forsyth   Exit:
1324*37da2899SCharles.Forsyth     return error;
1325*37da2899SCharles.Forsyth   }
1326*37da2899SCharles.Forsyth 
1327*37da2899SCharles.Forsyth 
1328*37da2899SCharles.Forsyth   FT_EXPORT_DEF( FT_Error )
FT_Stroker_GetCounts(FT_Stroker stroker,FT_UInt * anum_points,FT_UInt * anum_contours)1329*37da2899SCharles.Forsyth   FT_Stroker_GetCounts( FT_Stroker  stroker,
1330*37da2899SCharles.Forsyth                         FT_UInt    *anum_points,
1331*37da2899SCharles.Forsyth                         FT_UInt    *anum_contours )
1332*37da2899SCharles.Forsyth   {
1333*37da2899SCharles.Forsyth     FT_UInt   count1, count2, num_points   = 0;
1334*37da2899SCharles.Forsyth     FT_UInt   count3, count4, num_contours = 0;
1335*37da2899SCharles.Forsyth     FT_Error  error;
1336*37da2899SCharles.Forsyth 
1337*37da2899SCharles.Forsyth     error = ft_stroke_border_get_counts( stroker->borders+0, &count1, &count2 );
1338*37da2899SCharles.Forsyth     if (error) goto Exit;
1339*37da2899SCharles.Forsyth 
1340*37da2899SCharles.Forsyth     error = ft_stroke_border_get_counts( stroker->borders+1, &count3, &count4 );
1341*37da2899SCharles.Forsyth     if (error) goto Exit;
1342*37da2899SCharles.Forsyth 
1343*37da2899SCharles.Forsyth     num_points   = count1 + count3;
1344*37da2899SCharles.Forsyth     num_contours = count2 + count4;
1345*37da2899SCharles.Forsyth 
1346*37da2899SCharles.Forsyth     stroker->valid = 1;
1347*37da2899SCharles.Forsyth 
1348*37da2899SCharles.Forsyth   Exit:
1349*37da2899SCharles.Forsyth     *anum_points   = num_points;
1350*37da2899SCharles.Forsyth     *anum_contours = num_contours;
1351*37da2899SCharles.Forsyth     return error;
1352*37da2899SCharles.Forsyth   }
1353*37da2899SCharles.Forsyth 
1354*37da2899SCharles.Forsyth 
1355*37da2899SCharles.Forsyth   FT_EXPORT_DEF( void )
FT_Stroker_Export(FT_Stroker stroker,FT_Outline * outline)1356*37da2899SCharles.Forsyth   FT_Stroker_Export( FT_Stroker   stroker,
1357*37da2899SCharles.Forsyth                      FT_Outline*  outline )
1358*37da2899SCharles.Forsyth   {
1359*37da2899SCharles.Forsyth     if ( stroker->valid )
1360*37da2899SCharles.Forsyth     {
1361*37da2899SCharles.Forsyth       ft_stroke_border_export( stroker->borders+0, outline );
1362*37da2899SCharles.Forsyth       ft_stroke_border_export( stroker->borders+1, outline );
1363*37da2899SCharles.Forsyth     }
1364*37da2899SCharles.Forsyth   }
1365