Imported Upstream version 2.10.4
[platform/upstream/freetype2.git] / src / base / ftstroke.c
1 /****************************************************************************
2  *
3  * ftstroke.c
4  *
5  *   FreeType path stroker (body).
6  *
7  * Copyright (C) 2002-2020 by
8  * David Turner, Robert Wilhelm, and Werner Lemberg.
9  *
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.
15  *
16  */
17
18
19 #include <freetype/ftstroke.h>
20 #include <freetype/fttrigon.h>
21 #include <freetype/ftoutln.h>
22 #include <freetype/internal/ftmemory.h>
23 #include <freetype/internal/ftdebug.h>
24 #include <freetype/internal/ftobjs.h>
25
26
27   /* declare an extern to access `ft_outline_glyph_class' globally */
28   /* allocated  in `ftglyph.c'                                     */
29   FT_CALLBACK_TABLE const FT_Glyph_Class  ft_outline_glyph_class;
30
31
32   /* documentation is in ftstroke.h */
33
34   FT_EXPORT_DEF( FT_StrokerBorder )
35   FT_Outline_GetInsideBorder( FT_Outline*  outline )
36   {
37     FT_Orientation  o = FT_Outline_Get_Orientation( outline );
38
39
40     return o == FT_ORIENTATION_TRUETYPE ? FT_STROKER_BORDER_RIGHT
41                                         : FT_STROKER_BORDER_LEFT;
42   }
43
44
45   /* documentation is in ftstroke.h */
46
47   FT_EXPORT_DEF( FT_StrokerBorder )
48   FT_Outline_GetOutsideBorder( FT_Outline*  outline )
49   {
50     FT_Orientation  o = FT_Outline_Get_Orientation( outline );
51
52
53     return o == FT_ORIENTATION_TRUETYPE ? FT_STROKER_BORDER_LEFT
54                                         : FT_STROKER_BORDER_RIGHT;
55   }
56
57
58   /*************************************************************************/
59   /*************************************************************************/
60   /*****                                                               *****/
61   /*****                      BEZIER COMPUTATIONS                      *****/
62   /*****                                                               *****/
63   /*************************************************************************/
64   /*************************************************************************/
65
66 #define FT_SMALL_CONIC_THRESHOLD  ( FT_ANGLE_PI / 6 )
67 #define FT_SMALL_CUBIC_THRESHOLD  ( FT_ANGLE_PI / 8 )
68
69 #define FT_EPSILON  2
70
71 #define FT_IS_SMALL( x )  ( (x) > -FT_EPSILON && (x) < FT_EPSILON )
72
73
74   static FT_Pos
75   ft_pos_abs( FT_Pos  x )
76   {
77     return x >= 0 ? x : -x;
78   }
79
80
81   static void
82   ft_conic_split( FT_Vector*  base )
83   {
84     FT_Pos  a, b;
85
86
87     base[4].x = base[2].x;
88     a = base[0].x + base[1].x;
89     b = base[1].x + base[2].x;
90     base[3].x = b >> 1;
91     base[2].x = ( a + b ) >> 2;
92     base[1].x = a >> 1;
93
94     base[4].y = base[2].y;
95     a = base[0].y + base[1].y;
96     b = base[1].y + base[2].y;
97     base[3].y = b >> 1;
98     base[2].y = ( a + b ) >> 2;
99     base[1].y = a >> 1;
100   }
101
102
103   static FT_Bool
104   ft_conic_is_small_enough( FT_Vector*  base,
105                             FT_Angle   *angle_in,
106                             FT_Angle   *angle_out )
107   {
108     FT_Vector  d1, d2;
109     FT_Angle   theta;
110     FT_Int     close1, close2;
111
112
113     d1.x = base[1].x - base[2].x;
114     d1.y = base[1].y - base[2].y;
115     d2.x = base[0].x - base[1].x;
116     d2.y = base[0].y - base[1].y;
117
118     close1 = FT_IS_SMALL( d1.x ) && FT_IS_SMALL( d1.y );
119     close2 = FT_IS_SMALL( d2.x ) && FT_IS_SMALL( d2.y );
120
121     if ( close1 )
122     {
123       if ( close2 )
124       {
125         /* basically a point;                      */
126         /* do nothing to retain original direction */
127       }
128       else
129       {
130         *angle_in  =
131         *angle_out = FT_Atan2( d2.x, d2.y );
132       }
133     }
134     else /* !close1 */
135     {
136       if ( close2 )
137       {
138         *angle_in  =
139         *angle_out = FT_Atan2( d1.x, d1.y );
140       }
141       else
142       {
143         *angle_in  = FT_Atan2( d1.x, d1.y );
144         *angle_out = FT_Atan2( d2.x, d2.y );
145       }
146     }
147
148     theta = ft_pos_abs( FT_Angle_Diff( *angle_in, *angle_out ) );
149
150     return FT_BOOL( theta < FT_SMALL_CONIC_THRESHOLD );
151   }
152
153
154   static void
155   ft_cubic_split( FT_Vector*  base )
156   {
157     FT_Pos  a, b, c;
158
159
160     base[6].x = base[3].x;
161     a = base[0].x + base[1].x;
162     b = base[1].x + base[2].x;
163     c = base[2].x + base[3].x;
164     base[5].x = c >> 1;
165     c += b;
166     base[4].x = c >> 2;
167     base[1].x = a >> 1;
168     a += b;
169     base[2].x = a >> 2;
170     base[3].x = ( a + c ) >> 3;
171
172     base[6].y = base[3].y;
173     a = base[0].y + base[1].y;
174     b = base[1].y + base[2].y;
175     c = base[2].y + base[3].y;
176     base[5].y = c >> 1;
177     c += b;
178     base[4].y = c >> 2;
179     base[1].y = a >> 1;
180     a += b;
181     base[2].y = a >> 2;
182     base[3].y = ( a + c ) >> 3;
183   }
184
185
186   /* Return the average of `angle1' and `angle2'.            */
187   /* This gives correct result even if `angle1' and `angle2' */
188   /* have opposite signs.                                    */
189   static FT_Angle
190   ft_angle_mean( FT_Angle  angle1,
191                  FT_Angle  angle2 )
192   {
193     return angle1 + FT_Angle_Diff( angle1, angle2 ) / 2;
194   }
195
196
197   static FT_Bool
198   ft_cubic_is_small_enough( FT_Vector*  base,
199                             FT_Angle   *angle_in,
200                             FT_Angle   *angle_mid,
201                             FT_Angle   *angle_out )
202   {
203     FT_Vector  d1, d2, d3;
204     FT_Angle   theta1, theta2;
205     FT_Int     close1, close2, close3;
206
207
208     d1.x = base[2].x - base[3].x;
209     d1.y = base[2].y - base[3].y;
210     d2.x = base[1].x - base[2].x;
211     d2.y = base[1].y - base[2].y;
212     d3.x = base[0].x - base[1].x;
213     d3.y = base[0].y - base[1].y;
214
215     close1 = FT_IS_SMALL( d1.x ) && FT_IS_SMALL( d1.y );
216     close2 = FT_IS_SMALL( d2.x ) && FT_IS_SMALL( d2.y );
217     close3 = FT_IS_SMALL( d3.x ) && FT_IS_SMALL( d3.y );
218
219     if ( close1 )
220     {
221       if ( close2 )
222       {
223         if ( close3 )
224         {
225           /* basically a point;                      */
226           /* do nothing to retain original direction */
227         }
228         else /* !close3 */
229         {
230           *angle_in  =
231           *angle_mid =
232           *angle_out = FT_Atan2( d3.x, d3.y );
233         }
234       }
235       else /* !close2 */
236       {
237         if ( close3 )
238         {
239           *angle_in  =
240           *angle_mid =
241           *angle_out = FT_Atan2( d2.x, d2.y );
242         }
243         else /* !close3 */
244         {
245           *angle_in  =
246           *angle_mid = FT_Atan2( d2.x, d2.y );
247           *angle_out = FT_Atan2( d3.x, d3.y );
248         }
249       }
250     }
251     else /* !close1 */
252     {
253       if ( close2 )
254       {
255         if ( close3 )
256         {
257           *angle_in  =
258           *angle_mid =
259           *angle_out = FT_Atan2( d1.x, d1.y );
260         }
261         else /* !close3 */
262         {
263           *angle_in  = FT_Atan2( d1.x, d1.y );
264           *angle_out = FT_Atan2( d3.x, d3.y );
265           *angle_mid = ft_angle_mean( *angle_in, *angle_out );
266         }
267       }
268       else /* !close2 */
269       {
270         if ( close3 )
271         {
272           *angle_in  = FT_Atan2( d1.x, d1.y );
273           *angle_mid =
274           *angle_out = FT_Atan2( d2.x, d2.y );
275         }
276         else /* !close3 */
277         {
278           *angle_in  = FT_Atan2( d1.x, d1.y );
279           *angle_mid = FT_Atan2( d2.x, d2.y );
280           *angle_out = FT_Atan2( d3.x, d3.y );
281         }
282       }
283     }
284
285     theta1 = ft_pos_abs( FT_Angle_Diff( *angle_in,  *angle_mid ) );
286     theta2 = ft_pos_abs( FT_Angle_Diff( *angle_mid, *angle_out ) );
287
288     return FT_BOOL( theta1 < FT_SMALL_CUBIC_THRESHOLD &&
289                     theta2 < FT_SMALL_CUBIC_THRESHOLD );
290   }
291
292
293   /*************************************************************************/
294   /*************************************************************************/
295   /*****                                                               *****/
296   /*****                       STROKE BORDERS                          *****/
297   /*****                                                               *****/
298   /*************************************************************************/
299   /*************************************************************************/
300
301   typedef enum  FT_StrokeTags_
302   {
303     FT_STROKE_TAG_ON    = 1,   /* on-curve point  */
304     FT_STROKE_TAG_CUBIC = 2,   /* cubic off-point */
305     FT_STROKE_TAG_BEGIN = 4,   /* sub-path start  */
306     FT_STROKE_TAG_END   = 8    /* sub-path end    */
307
308   } FT_StrokeTags;
309
310 #define  FT_STROKE_TAG_BEGIN_END  ( FT_STROKE_TAG_BEGIN | FT_STROKE_TAG_END )
311
312   typedef struct  FT_StrokeBorderRec_
313   {
314     FT_UInt     num_points;
315     FT_UInt     max_points;
316     FT_Vector*  points;
317     FT_Byte*    tags;
318     FT_Bool     movable;  /* TRUE for ends of lineto borders */
319     FT_Int      start;    /* index of current sub-path start point */
320     FT_Memory   memory;
321     FT_Bool     valid;
322
323   } FT_StrokeBorderRec, *FT_StrokeBorder;
324
325
326   static FT_Error
327   ft_stroke_border_grow( FT_StrokeBorder  border,
328                          FT_UInt          new_points )
329   {
330     FT_UInt   old_max = border->max_points;
331     FT_UInt   new_max = border->num_points + new_points;
332     FT_Error  error   = FT_Err_Ok;
333
334
335     if ( new_max > old_max )
336     {
337       FT_UInt    cur_max = old_max;
338       FT_Memory  memory  = border->memory;
339
340
341       while ( cur_max < new_max )
342         cur_max += ( cur_max >> 1 ) + 16;
343
344       if ( FT_RENEW_ARRAY( border->points, old_max, cur_max ) ||
345            FT_RENEW_ARRAY( border->tags,   old_max, cur_max ) )
346         goto Exit;
347
348       border->max_points = cur_max;
349     }
350
351   Exit:
352     return error;
353   }
354
355
356   static void
357   ft_stroke_border_close( FT_StrokeBorder  border,
358                           FT_Bool          reverse )
359   {
360     FT_UInt  start = (FT_UInt)border->start;
361     FT_UInt  count = border->num_points;
362
363
364     FT_ASSERT( border->start >= 0 );
365
366     /* don't record empty paths! */
367     if ( count <= start + 1U )
368       border->num_points = start;
369     else
370     {
371       /* copy the last point to the start of this sub-path, since */
372       /* it contains the `adjusted' starting coordinates          */
373       border->num_points    = --count;
374       border->points[start] = border->points[count];
375       border->tags[start]   = border->tags[count];
376
377       if ( reverse )
378       {
379         /* reverse the points */
380         {
381           FT_Vector*  vec1 = border->points + start + 1;
382           FT_Vector*  vec2 = border->points + count - 1;
383
384
385           for ( ; vec1 < vec2; vec1++, vec2-- )
386           {
387             FT_Vector  tmp;
388
389
390             tmp   = *vec1;
391             *vec1 = *vec2;
392             *vec2 = tmp;
393           }
394         }
395
396         /* then the tags */
397         {
398           FT_Byte*  tag1 = border->tags + start + 1;
399           FT_Byte*  tag2 = border->tags + count - 1;
400
401
402           for ( ; tag1 < tag2; tag1++, tag2-- )
403           {
404             FT_Byte  tmp;
405
406
407             tmp   = *tag1;
408             *tag1 = *tag2;
409             *tag2 = tmp;
410           }
411         }
412       }
413
414       border->tags[start    ] |= FT_STROKE_TAG_BEGIN;
415       border->tags[count - 1] |= FT_STROKE_TAG_END;
416     }
417
418     border->start   = -1;
419     border->movable = FALSE;
420   }
421
422
423   static FT_Error
424   ft_stroke_border_lineto( FT_StrokeBorder  border,
425                            FT_Vector*       to,
426                            FT_Bool          movable )
427   {
428     FT_Error  error = FT_Err_Ok;
429
430
431     FT_ASSERT( border->start >= 0 );
432
433     if ( border->movable )
434     {
435       /* move last point */
436       border->points[border->num_points - 1] = *to;
437     }
438     else
439     {
440       /* don't add zero-length lineto, but always add moveto */
441       if ( border->num_points > (FT_UInt)border->start                     &&
442            FT_IS_SMALL( border->points[border->num_points - 1].x - to->x ) &&
443            FT_IS_SMALL( border->points[border->num_points - 1].y - to->y ) )
444         return error;
445
446       /* add one point */
447       error = ft_stroke_border_grow( border, 1 );
448       if ( !error )
449       {
450         FT_Vector*  vec = border->points + border->num_points;
451         FT_Byte*    tag = border->tags   + border->num_points;
452
453
454         vec[0] = *to;
455         tag[0] = FT_STROKE_TAG_ON;
456
457         border->num_points += 1;
458       }
459     }
460     border->movable = movable;
461     return error;
462   }
463
464
465   static FT_Error
466   ft_stroke_border_conicto( FT_StrokeBorder  border,
467                             FT_Vector*       control,
468                             FT_Vector*       to )
469   {
470     FT_Error  error;
471
472
473     FT_ASSERT( border->start >= 0 );
474
475     error = ft_stroke_border_grow( border, 2 );
476     if ( !error )
477     {
478       FT_Vector*  vec = border->points + border->num_points;
479       FT_Byte*    tag = border->tags   + border->num_points;
480
481
482       vec[0] = *control;
483       vec[1] = *to;
484
485       tag[0] = 0;
486       tag[1] = FT_STROKE_TAG_ON;
487
488       border->num_points += 2;
489     }
490
491     border->movable = FALSE;
492
493     return error;
494   }
495
496
497   static FT_Error
498   ft_stroke_border_cubicto( FT_StrokeBorder  border,
499                             FT_Vector*       control1,
500                             FT_Vector*       control2,
501                             FT_Vector*       to )
502   {
503     FT_Error  error;
504
505
506     FT_ASSERT( border->start >= 0 );
507
508     error = ft_stroke_border_grow( border, 3 );
509     if ( !error )
510     {
511       FT_Vector*  vec = border->points + border->num_points;
512       FT_Byte*    tag = border->tags   + border->num_points;
513
514
515       vec[0] = *control1;
516       vec[1] = *control2;
517       vec[2] = *to;
518
519       tag[0] = FT_STROKE_TAG_CUBIC;
520       tag[1] = FT_STROKE_TAG_CUBIC;
521       tag[2] = FT_STROKE_TAG_ON;
522
523       border->num_points += 3;
524     }
525
526     border->movable = FALSE;
527
528     return error;
529   }
530
531
532 #define FT_ARC_CUBIC_ANGLE  ( FT_ANGLE_PI / 2 )
533
534
535   static FT_Error
536   ft_stroke_border_arcto( FT_StrokeBorder  border,
537                           FT_Vector*       center,
538                           FT_Fixed         radius,
539                           FT_Angle         angle_start,
540                           FT_Angle         angle_diff )
541   {
542     FT_Fixed   coef;
543     FT_Vector  a0, a1, a2, a3;
544     FT_Int     i, arcs = 1;
545     FT_Error   error = FT_Err_Ok;
546
547
548     /* number of cubic arcs to draw */
549     while (  angle_diff > FT_ARC_CUBIC_ANGLE * arcs ||
550             -angle_diff > FT_ARC_CUBIC_ANGLE * arcs )
551       arcs++;
552
553     /* control tangents */
554     coef  = FT_Tan( angle_diff / ( 4 * arcs ) );
555     coef += coef / 3;
556
557     /* compute start and first control point */
558     FT_Vector_From_Polar( &a0, radius, angle_start );
559     a1.x = FT_MulFix( -a0.y, coef );
560     a1.y = FT_MulFix(  a0.x, coef );
561
562     a0.x += center->x;
563     a0.y += center->y;
564     a1.x += a0.x;
565     a1.y += a0.y;
566
567     for ( i = 1; i <= arcs; i++ )
568     {
569       /* compute end and second control point */
570       FT_Vector_From_Polar( &a3, radius,
571                             angle_start + i * angle_diff / arcs );
572       a2.x = FT_MulFix(  a3.y, coef );
573       a2.y = FT_MulFix( -a3.x, coef );
574
575       a3.x += center->x;
576       a3.y += center->y;
577       a2.x += a3.x;
578       a2.y += a3.y;
579
580       /* add cubic arc */
581       error = ft_stroke_border_cubicto( border, &a1, &a2, &a3 );
582       if ( error )
583         break;
584
585       /* a0 = a3; */
586       a1.x = a3.x - a2.x + a3.x;
587       a1.y = a3.y - a2.y + a3.y;
588     }
589
590     return error;
591   }
592
593
594   static FT_Error
595   ft_stroke_border_moveto( FT_StrokeBorder  border,
596                            FT_Vector*       to )
597   {
598     /* close current open path if any ? */
599     if ( border->start >= 0 )
600       ft_stroke_border_close( border, FALSE );
601
602     border->start = (FT_Int)border->num_points;
603     border->movable = FALSE;
604
605     return ft_stroke_border_lineto( border, to, FALSE );
606   }
607
608
609   static void
610   ft_stroke_border_init( FT_StrokeBorder  border,
611                          FT_Memory        memory )
612   {
613     border->memory = memory;
614     border->points = NULL;
615     border->tags   = NULL;
616
617     border->num_points = 0;
618     border->max_points = 0;
619     border->start      = -1;
620     border->valid      = FALSE;
621   }
622
623
624   static void
625   ft_stroke_border_reset( FT_StrokeBorder  border )
626   {
627     border->num_points = 0;
628     border->start      = -1;
629     border->valid      = FALSE;
630   }
631
632
633   static void
634   ft_stroke_border_done( FT_StrokeBorder  border )
635   {
636     FT_Memory  memory = border->memory;
637
638
639     FT_FREE( border->points );
640     FT_FREE( border->tags );
641
642     border->num_points = 0;
643     border->max_points = 0;
644     border->start      = -1;
645     border->valid      = FALSE;
646   }
647
648
649   static FT_Error
650   ft_stroke_border_get_counts( FT_StrokeBorder  border,
651                                FT_UInt         *anum_points,
652                                FT_UInt         *anum_contours )
653   {
654     FT_Error  error        = FT_Err_Ok;
655     FT_UInt   num_points   = 0;
656     FT_UInt   num_contours = 0;
657
658     FT_UInt     count      = border->num_points;
659     FT_Vector*  point      = border->points;
660     FT_Byte*    tags       = border->tags;
661     FT_Int      in_contour = 0;
662
663
664     for ( ; count > 0; count--, num_points++, point++, tags++ )
665     {
666       if ( tags[0] & FT_STROKE_TAG_BEGIN )
667       {
668         if ( in_contour != 0 )
669           goto Fail;
670
671         in_contour = 1;
672       }
673       else if ( in_contour == 0 )
674         goto Fail;
675
676       if ( tags[0] & FT_STROKE_TAG_END )
677       {
678         in_contour = 0;
679         num_contours++;
680       }
681     }
682
683     if ( in_contour != 0 )
684       goto Fail;
685
686     border->valid = TRUE;
687
688   Exit:
689     *anum_points   = num_points;
690     *anum_contours = num_contours;
691     return error;
692
693   Fail:
694     num_points   = 0;
695     num_contours = 0;
696     goto Exit;
697   }
698
699
700   static void
701   ft_stroke_border_export( FT_StrokeBorder  border,
702                            FT_Outline*      outline )
703   {
704     /* copy point locations */
705     if ( border->num_points )
706       FT_ARRAY_COPY( outline->points + outline->n_points,
707                      border->points,
708                      border->num_points );
709
710     /* copy tags */
711     {
712       FT_UInt   count = border->num_points;
713       FT_Byte*  read  = border->tags;
714       FT_Byte*  write = (FT_Byte*)outline->tags + outline->n_points;
715
716
717       for ( ; count > 0; count--, read++, write++ )
718       {
719         if ( *read & FT_STROKE_TAG_ON )
720           *write = FT_CURVE_TAG_ON;
721         else if ( *read & FT_STROKE_TAG_CUBIC )
722           *write = FT_CURVE_TAG_CUBIC;
723         else
724           *write = FT_CURVE_TAG_CONIC;
725       }
726     }
727
728     /* copy contours */
729     {
730       FT_UInt    count = border->num_points;
731       FT_Byte*   tags  = border->tags;
732       FT_Short*  write = outline->contours + outline->n_contours;
733       FT_Short   idx   = (FT_Short)outline->n_points;
734
735
736       for ( ; count > 0; count--, tags++, idx++ )
737       {
738         if ( *tags & FT_STROKE_TAG_END )
739         {
740           *write++ = idx;
741           outline->n_contours++;
742         }
743       }
744     }
745
746     outline->n_points += (short)border->num_points;
747
748     FT_ASSERT( FT_Outline_Check( outline ) == 0 );
749   }
750
751
752   /*************************************************************************/
753   /*************************************************************************/
754   /*****                                                               *****/
755   /*****                           STROKER                             *****/
756   /*****                                                               *****/
757   /*************************************************************************/
758   /*************************************************************************/
759
760 #define FT_SIDE_TO_ROTATE( s )   ( FT_ANGLE_PI2 - (s) * FT_ANGLE_PI )
761
762   typedef struct  FT_StrokerRec_
763   {
764     FT_Angle             angle_in;             /* direction into curr join */
765     FT_Angle             angle_out;            /* direction out of join  */
766     FT_Vector            center;               /* current position */
767     FT_Fixed             line_length;          /* length of last lineto */
768     FT_Bool              first_point;          /* is this the start? */
769     FT_Bool              subpath_open;         /* is the subpath open? */
770     FT_Angle             subpath_angle;        /* subpath start direction */
771     FT_Vector            subpath_start;        /* subpath start position */
772     FT_Fixed             subpath_line_length;  /* subpath start lineto len */
773     FT_Bool              handle_wide_strokes;  /* use wide strokes logic? */
774
775     FT_Stroker_LineCap   line_cap;
776     FT_Stroker_LineJoin  line_join;
777     FT_Stroker_LineJoin  line_join_saved;
778     FT_Fixed             miter_limit;
779     FT_Fixed             radius;
780
781     FT_StrokeBorderRec   borders[2];
782     FT_Library           library;
783
784   } FT_StrokerRec;
785
786
787   /* documentation is in ftstroke.h */
788
789   FT_EXPORT_DEF( FT_Error )
790   FT_Stroker_New( FT_Library   library,
791                   FT_Stroker  *astroker )
792   {
793     FT_Error    error;           /* assigned in FT_NEW */
794     FT_Memory   memory;
795     FT_Stroker  stroker = NULL;
796
797
798     if ( !library )
799       return FT_THROW( Invalid_Library_Handle );
800
801     if ( !astroker )
802       return FT_THROW( Invalid_Argument );
803
804     memory = library->memory;
805
806     if ( !FT_NEW( stroker ) )
807     {
808       stroker->library = library;
809
810       ft_stroke_border_init( &stroker->borders[0], memory );
811       ft_stroke_border_init( &stroker->borders[1], memory );
812     }
813
814     *astroker = stroker;
815
816     return error;
817   }
818
819
820   /* documentation is in ftstroke.h */
821
822   FT_EXPORT_DEF( void )
823   FT_Stroker_Set( FT_Stroker           stroker,
824                   FT_Fixed             radius,
825                   FT_Stroker_LineCap   line_cap,
826                   FT_Stroker_LineJoin  line_join,
827                   FT_Fixed             miter_limit )
828   {
829     if ( !stroker )
830       return;
831
832     stroker->radius      = radius;
833     stroker->line_cap    = line_cap;
834     stroker->line_join   = line_join;
835     stroker->miter_limit = miter_limit;
836
837     /* ensure miter limit has sensible value */
838     if ( stroker->miter_limit < 0x10000L )
839       stroker->miter_limit = 0x10000L;
840
841     /* save line join style:                                           */
842     /* line join style can be temporarily changed when stroking curves */
843     stroker->line_join_saved = line_join;
844
845     FT_Stroker_Rewind( stroker );
846   }
847
848
849   /* documentation is in ftstroke.h */
850
851   FT_EXPORT_DEF( void )
852   FT_Stroker_Rewind( FT_Stroker  stroker )
853   {
854     if ( stroker )
855     {
856       ft_stroke_border_reset( &stroker->borders[0] );
857       ft_stroke_border_reset( &stroker->borders[1] );
858     }
859   }
860
861
862   /* documentation is in ftstroke.h */
863
864   FT_EXPORT_DEF( void )
865   FT_Stroker_Done( FT_Stroker  stroker )
866   {
867     if ( stroker )
868     {
869       FT_Memory  memory = stroker->library->memory;
870
871
872       ft_stroke_border_done( &stroker->borders[0] );
873       ft_stroke_border_done( &stroker->borders[1] );
874
875       stroker->library = NULL;
876       FT_FREE( stroker );
877     }
878   }
879
880
881   /* create a circular arc at a corner or cap */
882   static FT_Error
883   ft_stroker_arcto( FT_Stroker  stroker,
884                     FT_Int      side )
885   {
886     FT_Angle         total, rotate;
887     FT_Fixed         radius = stroker->radius;
888     FT_Error         error  = FT_Err_Ok;
889     FT_StrokeBorder  border = stroker->borders + side;
890
891
892     rotate = FT_SIDE_TO_ROTATE( side );
893
894     total = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
895     if ( total == FT_ANGLE_PI )
896       total = -rotate * 2;
897
898     error = ft_stroke_border_arcto( border,
899                                     &stroker->center,
900                                     radius,
901                                     stroker->angle_in + rotate,
902                                     total );
903     border->movable = FALSE;
904     return error;
905   }
906
907
908   /* add a cap at the end of an opened path */
909   static FT_Error
910   ft_stroker_cap( FT_Stroker  stroker,
911                   FT_Angle    angle,
912                   FT_Int      side )
913   {
914     FT_Error  error = FT_Err_Ok;
915
916
917     if ( stroker->line_cap == FT_STROKER_LINECAP_ROUND )
918     {
919       /* add a round cap */
920       stroker->angle_in  = angle;
921       stroker->angle_out = angle + FT_ANGLE_PI;
922
923       error = ft_stroker_arcto( stroker, side );
924     }
925     else
926     {
927       /* add a square or butt cap */
928       FT_Vector        middle, delta;
929       FT_Fixed         radius = stroker->radius;
930       FT_StrokeBorder  border = stroker->borders + side;
931
932
933       /* compute middle point and first angle point */
934       FT_Vector_From_Polar( &middle, radius, angle );
935       delta.x = side ?  middle.y : -middle.y;
936       delta.y = side ? -middle.x :  middle.x;
937
938       if ( stroker->line_cap == FT_STROKER_LINECAP_SQUARE )
939       {
940         middle.x += stroker->center.x;
941         middle.y += stroker->center.y;
942       }
943       else  /* FT_STROKER_LINECAP_BUTT */
944       {
945         middle.x  = stroker->center.x;
946         middle.y  = stroker->center.y;
947       }
948
949       delta.x  += middle.x;
950       delta.y  += middle.y;
951
952       error = ft_stroke_border_lineto( border, &delta, FALSE );
953       if ( error )
954         goto Exit;
955
956       /* compute second angle point */
957       delta.x = middle.x - delta.x + middle.x;
958       delta.y = middle.y - delta.y + middle.y;
959
960       error = ft_stroke_border_lineto( border, &delta, FALSE );
961     }
962
963   Exit:
964     return error;
965   }
966
967
968   /* process an inside corner, i.e. compute intersection */
969   static FT_Error
970   ft_stroker_inside( FT_Stroker  stroker,
971                      FT_Int      side,
972                      FT_Fixed    line_length )
973   {
974     FT_StrokeBorder  border = stroker->borders + side;
975     FT_Angle         phi, theta, rotate;
976     FT_Fixed         length;
977     FT_Vector        sigma, delta;
978     FT_Error         error = FT_Err_Ok;
979     FT_Bool          intersect;          /* use intersection of lines? */
980
981
982     rotate = FT_SIDE_TO_ROTATE( side );
983
984     theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out ) / 2;
985
986     /* Only intersect borders if between two lineto's and both */
987     /* lines are long enough (line_length is zero for curves). */
988     /* Also avoid U-turns of nearly 180 degree.                */
989     if ( !border->movable || line_length == 0  ||
990          theta > 0x59C000 || theta < -0x59C000 )
991       intersect = FALSE;
992     else
993     {
994       /* compute minimum required length of lines */
995       FT_Fixed  min_length;
996
997
998       FT_Vector_Unit( &sigma, theta );
999       min_length =
1000         ft_pos_abs( FT_MulDiv( stroker->radius, sigma.y, sigma.x ) );
1001
1002       intersect = FT_BOOL( min_length                         &&
1003                            stroker->line_length >= min_length &&
1004                            line_length          >= min_length );
1005     }
1006
1007     if ( !intersect )
1008     {
1009       FT_Vector_From_Polar( &delta, stroker->radius,
1010                             stroker->angle_out + rotate );
1011       delta.x += stroker->center.x;
1012       delta.y += stroker->center.y;
1013
1014       border->movable = FALSE;
1015     }
1016     else
1017     {
1018       /* compute median angle */
1019       phi = stroker->angle_in + theta + rotate;
1020
1021       length = FT_DivFix( stroker->radius, sigma.x );
1022
1023       FT_Vector_From_Polar( &delta, length, phi );
1024       delta.x += stroker->center.x;
1025       delta.y += stroker->center.y;
1026     }
1027
1028     error = ft_stroke_border_lineto( border, &delta, FALSE );
1029
1030     return error;
1031   }
1032
1033
1034   /* process an outside corner, i.e. compute bevel/miter/round */
1035   static FT_Error
1036   ft_stroker_outside( FT_Stroker  stroker,
1037                       FT_Int      side,
1038                       FT_Fixed    line_length )
1039   {
1040     FT_StrokeBorder  border = stroker->borders + side;
1041     FT_Error         error;
1042     FT_Angle         rotate;
1043
1044
1045     if ( stroker->line_join == FT_STROKER_LINEJOIN_ROUND )
1046       error = ft_stroker_arcto( stroker, side );
1047     else
1048     {
1049       /* this is a mitered (pointed) or beveled (truncated) corner */
1050       FT_Fixed   radius = stroker->radius;
1051       FT_Vector  sigma;
1052       FT_Angle   theta = 0, phi = 0;
1053       FT_Bool    bevel, fixed_bevel;
1054
1055
1056       rotate = FT_SIDE_TO_ROTATE( side );
1057
1058       bevel =
1059         FT_BOOL( stroker->line_join == FT_STROKER_LINEJOIN_BEVEL );
1060
1061       fixed_bevel =
1062         FT_BOOL( stroker->line_join != FT_STROKER_LINEJOIN_MITER_VARIABLE );
1063
1064       /* check miter limit first */
1065       if ( !bevel )
1066       {
1067         theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out ) / 2;
1068
1069         if ( theta == FT_ANGLE_PI2 )
1070           theta = -rotate;
1071
1072         phi    = stroker->angle_in + theta + rotate;
1073
1074         FT_Vector_From_Polar( &sigma, stroker->miter_limit, theta );
1075
1076         /* is miter limit exceeded? */
1077         if ( sigma.x < 0x10000L )
1078         {
1079           /* don't create variable bevels for very small deviations; */
1080           /* FT_Sin(x) = 0 for x <= 57                               */
1081           if ( fixed_bevel || ft_pos_abs( theta ) > 57 )
1082             bevel = TRUE;
1083         }
1084       }
1085
1086       if ( bevel )  /* this is a bevel (broken angle) */
1087       {
1088         if ( fixed_bevel )
1089         {
1090           /* the outer corners are simply joined together */
1091           FT_Vector  delta;
1092
1093
1094           /* add bevel */
1095           FT_Vector_From_Polar( &delta,
1096                                 radius,
1097                                 stroker->angle_out + rotate );
1098           delta.x += stroker->center.x;
1099           delta.y += stroker->center.y;
1100
1101           border->movable = FALSE;
1102           error = ft_stroke_border_lineto( border, &delta, FALSE );
1103         }
1104         else /* variable bevel or clipped miter */
1105         {
1106           /* the miter is truncated */
1107           FT_Vector  middle, delta;
1108           FT_Fixed   coef;
1109
1110
1111           /* compute middle point and first angle point */
1112           FT_Vector_From_Polar( &middle,
1113                                 FT_MulFix( radius, stroker->miter_limit ),
1114                                 phi );
1115
1116           coef    = FT_DivFix(  0x10000L - sigma.x, sigma.y );
1117           delta.x = FT_MulFix(  middle.y, coef );
1118           delta.y = FT_MulFix( -middle.x, coef );
1119
1120           middle.x += stroker->center.x;
1121           middle.y += stroker->center.y;
1122           delta.x  += middle.x;
1123           delta.y  += middle.y;
1124
1125           error = ft_stroke_border_lineto( border, &delta, FALSE );
1126           if ( error )
1127             goto Exit;
1128
1129           /* compute second angle point */
1130           delta.x = middle.x - delta.x + middle.x;
1131           delta.y = middle.y - delta.y + middle.y;
1132
1133           error = ft_stroke_border_lineto( border, &delta, FALSE );
1134           if ( error )
1135             goto Exit;
1136
1137           /* finally, add an end point; only needed if not lineto */
1138           /* (line_length is zero for curves)                     */
1139           if ( line_length == 0 )
1140           {
1141             FT_Vector_From_Polar( &delta,
1142                                   radius,
1143                                   stroker->angle_out + rotate );
1144
1145             delta.x += stroker->center.x;
1146             delta.y += stroker->center.y;
1147
1148             error = ft_stroke_border_lineto( border, &delta, FALSE );
1149           }
1150         }
1151       }
1152       else /* this is a miter (intersection) */
1153       {
1154         FT_Fixed   length;
1155         FT_Vector  delta;
1156
1157
1158         length = FT_MulDiv( stroker->radius, stroker->miter_limit, sigma.x );
1159
1160         FT_Vector_From_Polar( &delta, length, phi );
1161         delta.x += stroker->center.x;
1162         delta.y += stroker->center.y;
1163
1164         error = ft_stroke_border_lineto( border, &delta, FALSE );
1165         if ( error )
1166           goto Exit;
1167
1168         /* now add an end point; only needed if not lineto */
1169         /* (line_length is zero for curves)                */
1170         if ( line_length == 0 )
1171         {
1172           FT_Vector_From_Polar( &delta,
1173                                 stroker->radius,
1174                                 stroker->angle_out + rotate );
1175           delta.x += stroker->center.x;
1176           delta.y += stroker->center.y;
1177
1178           error = ft_stroke_border_lineto( border, &delta, FALSE );
1179         }
1180       }
1181     }
1182
1183   Exit:
1184     return error;
1185   }
1186
1187
1188   static FT_Error
1189   ft_stroker_process_corner( FT_Stroker  stroker,
1190                              FT_Fixed    line_length )
1191   {
1192     FT_Error  error = FT_Err_Ok;
1193     FT_Angle  turn;
1194     FT_Int    inside_side;
1195
1196
1197     turn = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
1198
1199     /* no specific corner processing is required if the turn is 0 */
1200     if ( turn == 0 )
1201       goto Exit;
1202
1203     /* when we turn to the right, the inside side is 0 */
1204     /* otherwise, the inside side is 1 */
1205     inside_side = ( turn < 0 );
1206
1207     /* process the inside side */
1208     error = ft_stroker_inside( stroker, inside_side, line_length );
1209     if ( error )
1210       goto Exit;
1211
1212     /* process the outside side */
1213     error = ft_stroker_outside( stroker, !inside_side, line_length );
1214
1215   Exit:
1216     return error;
1217   }
1218
1219
1220   /* add two points to the left and right borders corresponding to the */
1221   /* start of the subpath                                              */
1222   static FT_Error
1223   ft_stroker_subpath_start( FT_Stroker  stroker,
1224                             FT_Angle    start_angle,
1225                             FT_Fixed    line_length )
1226   {
1227     FT_Vector        delta;
1228     FT_Vector        point;
1229     FT_Error         error;
1230     FT_StrokeBorder  border;
1231
1232
1233     FT_Vector_From_Polar( &delta, stroker->radius,
1234                           start_angle + FT_ANGLE_PI2 );
1235
1236     point.x = stroker->center.x + delta.x;
1237     point.y = stroker->center.y + delta.y;
1238
1239     border = stroker->borders;
1240     error = ft_stroke_border_moveto( border, &point );
1241     if ( error )
1242       goto Exit;
1243
1244     point.x = stroker->center.x - delta.x;
1245     point.y = stroker->center.y - delta.y;
1246
1247     border++;
1248     error = ft_stroke_border_moveto( border, &point );
1249
1250     /* save angle, position, and line length for last join */
1251     /* (line_length is zero for curves)                    */
1252     stroker->subpath_angle       = start_angle;
1253     stroker->first_point         = FALSE;
1254     stroker->subpath_line_length = line_length;
1255
1256   Exit:
1257     return error;
1258   }
1259
1260
1261   /* documentation is in ftstroke.h */
1262
1263   FT_EXPORT_DEF( FT_Error )
1264   FT_Stroker_LineTo( FT_Stroker  stroker,
1265                      FT_Vector*  to )
1266   {
1267     FT_Error         error = FT_Err_Ok;
1268     FT_StrokeBorder  border;
1269     FT_Vector        delta;
1270     FT_Angle         angle;
1271     FT_Int           side;
1272     FT_Fixed         line_length;
1273
1274
1275     if ( !stroker || !to )
1276       return FT_THROW( Invalid_Argument );
1277
1278     delta.x = to->x - stroker->center.x;
1279     delta.y = to->y - stroker->center.y;
1280
1281     /* a zero-length lineto is a no-op; avoid creating a spurious corner */
1282     if ( delta.x == 0 && delta.y == 0 )
1283        goto Exit;
1284
1285     /* compute length of line */
1286     line_length = FT_Vector_Length( &delta );
1287
1288     angle = FT_Atan2( delta.x, delta.y );
1289     FT_Vector_From_Polar( &delta, stroker->radius, angle + FT_ANGLE_PI2 );
1290
1291     /* process corner if necessary */
1292     if ( stroker->first_point )
1293     {
1294       /* This is the first segment of a subpath.  We need to     */
1295       /* add a point to each border at their respective starting */
1296       /* point locations.                                        */
1297       error = ft_stroker_subpath_start( stroker, angle, line_length );
1298       if ( error )
1299         goto Exit;
1300     }
1301     else
1302     {
1303       /* process the current corner */
1304       stroker->angle_out = angle;
1305       error = ft_stroker_process_corner( stroker, line_length );
1306       if ( error )
1307         goto Exit;
1308     }
1309
1310     /* now add a line segment to both the `inside' and `outside' paths */
1311     for ( border = stroker->borders, side = 1; side >= 0; side--, border++ )
1312     {
1313       FT_Vector  point;
1314
1315
1316       point.x = to->x + delta.x;
1317       point.y = to->y + delta.y;
1318
1319       /* the ends of lineto borders are movable */
1320       error = ft_stroke_border_lineto( border, &point, TRUE );
1321       if ( error )
1322         goto Exit;
1323
1324       delta.x = -delta.x;
1325       delta.y = -delta.y;
1326     }
1327
1328     stroker->angle_in    = angle;
1329     stroker->center      = *to;
1330     stroker->line_length = line_length;
1331
1332   Exit:
1333     return error;
1334   }
1335
1336
1337   /* documentation is in ftstroke.h */
1338
1339   FT_EXPORT_DEF( FT_Error )
1340   FT_Stroker_ConicTo( FT_Stroker  stroker,
1341                       FT_Vector*  control,
1342                       FT_Vector*  to )
1343   {
1344     FT_Error    error = FT_Err_Ok;
1345     FT_Vector   bez_stack[34];
1346     FT_Vector*  arc;
1347     FT_Vector*  limit = bez_stack + 30;
1348     FT_Bool     first_arc = TRUE;
1349
1350
1351     if ( !stroker || !control || !to )
1352     {
1353       error = FT_THROW( Invalid_Argument );
1354       goto Exit;
1355     }
1356
1357     /* if all control points are coincident, this is a no-op; */
1358     /* avoid creating a spurious corner                       */
1359     if ( FT_IS_SMALL( stroker->center.x - control->x ) &&
1360          FT_IS_SMALL( stroker->center.y - control->y ) &&
1361          FT_IS_SMALL( control->x        - to->x      ) &&
1362          FT_IS_SMALL( control->y        - to->y      ) )
1363     {
1364        stroker->center = *to;
1365        goto Exit;
1366     }
1367
1368     arc    = bez_stack;
1369     arc[0] = *to;
1370     arc[1] = *control;
1371     arc[2] = stroker->center;
1372
1373     while ( arc >= bez_stack )
1374     {
1375       FT_Angle  angle_in, angle_out;
1376
1377
1378       /* initialize with current direction */
1379       angle_in = angle_out = stroker->angle_in;
1380
1381       if ( arc < limit                                             &&
1382            !ft_conic_is_small_enough( arc, &angle_in, &angle_out ) )
1383       {
1384         if ( stroker->first_point )
1385           stroker->angle_in = angle_in;
1386
1387         ft_conic_split( arc );
1388         arc += 2;
1389         continue;
1390       }
1391
1392       if ( first_arc )
1393       {
1394         first_arc = FALSE;
1395
1396         /* process corner if necessary */
1397         if ( stroker->first_point )
1398           error = ft_stroker_subpath_start( stroker, angle_in, 0 );
1399         else
1400         {
1401           stroker->angle_out = angle_in;
1402           error = ft_stroker_process_corner( stroker, 0 );
1403         }
1404       }
1405       else if ( ft_pos_abs( FT_Angle_Diff( stroker->angle_in, angle_in ) ) >
1406                   FT_SMALL_CONIC_THRESHOLD / 4                             )
1407       {
1408         /* if the deviation from one arc to the next is too great, */
1409         /* add a round corner                                      */
1410         stroker->center    = arc[2];
1411         stroker->angle_out = angle_in;
1412         stroker->line_join = FT_STROKER_LINEJOIN_ROUND;
1413
1414         error = ft_stroker_process_corner( stroker, 0 );
1415
1416         /* reinstate line join style */
1417         stroker->line_join = stroker->line_join_saved;
1418       }
1419
1420       if ( error )
1421         goto Exit;
1422
1423       /* the arc's angle is small enough; we can add it directly to each */
1424       /* border                                                          */
1425       {
1426         FT_Vector        ctrl, end;
1427         FT_Angle         theta, phi, rotate, alpha0 = 0;
1428         FT_Fixed         length;
1429         FT_StrokeBorder  border;
1430         FT_Int           side;
1431
1432
1433         theta  = FT_Angle_Diff( angle_in, angle_out ) / 2;
1434         phi    = angle_in + theta;
1435         length = FT_DivFix( stroker->radius, FT_Cos( theta ) );
1436
1437         /* compute direction of original arc */
1438         if ( stroker->handle_wide_strokes )
1439           alpha0 = FT_Atan2( arc[0].x - arc[2].x, arc[0].y - arc[2].y );
1440
1441         for ( border = stroker->borders, side = 0;
1442               side <= 1;
1443               side++, border++ )
1444         {
1445           rotate = FT_SIDE_TO_ROTATE( side );
1446
1447           /* compute control point */
1448           FT_Vector_From_Polar( &ctrl, length, phi + rotate );
1449           ctrl.x += arc[1].x;
1450           ctrl.y += arc[1].y;
1451
1452           /* compute end point */
1453           FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate );
1454           end.x += arc[0].x;
1455           end.y += arc[0].y;
1456
1457           if ( stroker->handle_wide_strokes )
1458           {
1459             FT_Vector  start;
1460             FT_Angle   alpha1;
1461
1462
1463             /* determine whether the border radius is greater than the */
1464             /* radius of curvature of the original arc                 */
1465             start = border->points[border->num_points - 1];
1466
1467             alpha1 = FT_Atan2( end.x - start.x, end.y - start.y );
1468
1469             /* is the direction of the border arc opposite to */
1470             /* that of the original arc? */
1471             if ( ft_pos_abs( FT_Angle_Diff( alpha0, alpha1 ) ) >
1472                    FT_ANGLE_PI / 2                             )
1473             {
1474               FT_Angle   beta, gamma;
1475               FT_Vector  bvec, delta;
1476               FT_Fixed   blen, sinA, sinB, alen;
1477
1478
1479               /* use the sine rule to find the intersection point */
1480               beta  = FT_Atan2( arc[2].x - start.x, arc[2].y - start.y );
1481               gamma = FT_Atan2( arc[0].x - end.x,   arc[0].y - end.y );
1482
1483               bvec.x = end.x - start.x;
1484               bvec.y = end.y - start.y;
1485
1486               blen = FT_Vector_Length( &bvec );
1487
1488               sinA = ft_pos_abs( FT_Sin( alpha1 - gamma ) );
1489               sinB = ft_pos_abs( FT_Sin( beta - gamma ) );
1490
1491               alen = FT_MulDiv( blen, sinA, sinB );
1492
1493               FT_Vector_From_Polar( &delta, alen, beta );
1494               delta.x += start.x;
1495               delta.y += start.y;
1496
1497               /* circumnavigate the negative sector backwards */
1498               border->movable = FALSE;
1499               error = ft_stroke_border_lineto( border, &delta, FALSE );
1500               if ( error )
1501                 goto Exit;
1502               error = ft_stroke_border_lineto( border, &end, FALSE );
1503               if ( error )
1504                 goto Exit;
1505               error = ft_stroke_border_conicto( border, &ctrl, &start );
1506               if ( error )
1507                 goto Exit;
1508               /* and then move to the endpoint */
1509               error = ft_stroke_border_lineto( border, &end, FALSE );
1510               if ( error )
1511                 goto Exit;
1512
1513               continue;
1514             }
1515
1516             /* else fall through */
1517           }
1518
1519           /* simply add an arc */
1520           error = ft_stroke_border_conicto( border, &ctrl, &end );
1521           if ( error )
1522             goto Exit;
1523         }
1524       }
1525
1526       arc -= 2;
1527
1528       stroker->angle_in = angle_out;
1529     }
1530
1531     stroker->center = *to;
1532
1533   Exit:
1534     return error;
1535   }
1536
1537
1538   /* documentation is in ftstroke.h */
1539
1540   FT_EXPORT_DEF( FT_Error )
1541   FT_Stroker_CubicTo( FT_Stroker  stroker,
1542                       FT_Vector*  control1,
1543                       FT_Vector*  control2,
1544                       FT_Vector*  to )
1545   {
1546     FT_Error    error = FT_Err_Ok;
1547     FT_Vector   bez_stack[37];
1548     FT_Vector*  arc;
1549     FT_Vector*  limit = bez_stack + 32;
1550     FT_Bool     first_arc = TRUE;
1551
1552
1553     if ( !stroker || !control1 || !control2 || !to )
1554     {
1555       error = FT_THROW( Invalid_Argument );
1556       goto Exit;
1557     }
1558
1559     /* if all control points are coincident, this is a no-op; */
1560     /* avoid creating a spurious corner */
1561     if ( FT_IS_SMALL( stroker->center.x - control1->x ) &&
1562          FT_IS_SMALL( stroker->center.y - control1->y ) &&
1563          FT_IS_SMALL( control1->x       - control2->x ) &&
1564          FT_IS_SMALL( control1->y       - control2->y ) &&
1565          FT_IS_SMALL( control2->x       - to->x       ) &&
1566          FT_IS_SMALL( control2->y       - to->y       ) )
1567     {
1568        stroker->center = *to;
1569        goto Exit;
1570     }
1571
1572     arc    = bez_stack;
1573     arc[0] = *to;
1574     arc[1] = *control2;
1575     arc[2] = *control1;
1576     arc[3] = stroker->center;
1577
1578     while ( arc >= bez_stack )
1579     {
1580       FT_Angle  angle_in, angle_mid, angle_out;
1581
1582
1583       /* initialize with current direction */
1584       angle_in = angle_out = angle_mid = stroker->angle_in;
1585
1586       if ( arc < limit                                         &&
1587            !ft_cubic_is_small_enough( arc, &angle_in,
1588                                       &angle_mid, &angle_out ) )
1589       {
1590         if ( stroker->first_point )
1591           stroker->angle_in = angle_in;
1592
1593         ft_cubic_split( arc );
1594         arc += 3;
1595         continue;
1596       }
1597
1598       if ( first_arc )
1599       {
1600         first_arc = FALSE;
1601
1602         /* process corner if necessary */
1603         if ( stroker->first_point )
1604           error = ft_stroker_subpath_start( stroker, angle_in, 0 );
1605         else
1606         {
1607           stroker->angle_out = angle_in;
1608           error = ft_stroker_process_corner( stroker, 0 );
1609         }
1610       }
1611       else if ( ft_pos_abs( FT_Angle_Diff( stroker->angle_in, angle_in ) ) >
1612                   FT_SMALL_CUBIC_THRESHOLD / 4                             )
1613       {
1614         /* if the deviation from one arc to the next is too great, */
1615         /* add a round corner                                      */
1616         stroker->center    = arc[3];
1617         stroker->angle_out = angle_in;
1618         stroker->line_join = FT_STROKER_LINEJOIN_ROUND;
1619
1620         error = ft_stroker_process_corner( stroker, 0 );
1621
1622         /* reinstate line join style */
1623         stroker->line_join = stroker->line_join_saved;
1624       }
1625
1626       if ( error )
1627         goto Exit;
1628
1629       /* the arc's angle is small enough; we can add it directly to each */
1630       /* border                                                          */
1631       {
1632         FT_Vector        ctrl1, ctrl2, end;
1633         FT_Angle         theta1, phi1, theta2, phi2, rotate, alpha0 = 0;
1634         FT_Fixed         length1, length2;
1635         FT_StrokeBorder  border;
1636         FT_Int           side;
1637
1638
1639         theta1  = FT_Angle_Diff( angle_in,  angle_mid ) / 2;
1640         theta2  = FT_Angle_Diff( angle_mid, angle_out ) / 2;
1641         phi1    = ft_angle_mean( angle_in,  angle_mid );
1642         phi2    = ft_angle_mean( angle_mid, angle_out );
1643         length1 = FT_DivFix( stroker->radius, FT_Cos( theta1 ) );
1644         length2 = FT_DivFix( stroker->radius, FT_Cos( theta2 ) );
1645
1646         /* compute direction of original arc */
1647         if ( stroker->handle_wide_strokes )
1648           alpha0 = FT_Atan2( arc[0].x - arc[3].x, arc[0].y - arc[3].y );
1649
1650         for ( border = stroker->borders, side = 0;
1651               side <= 1;
1652               side++, border++ )
1653         {
1654           rotate = FT_SIDE_TO_ROTATE( side );
1655
1656           /* compute control points */
1657           FT_Vector_From_Polar( &ctrl1, length1, phi1 + rotate );
1658           ctrl1.x += arc[2].x;
1659           ctrl1.y += arc[2].y;
1660
1661           FT_Vector_From_Polar( &ctrl2, length2, phi2 + rotate );
1662           ctrl2.x += arc[1].x;
1663           ctrl2.y += arc[1].y;
1664
1665           /* compute end point */
1666           FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate );
1667           end.x += arc[0].x;
1668           end.y += arc[0].y;
1669
1670           if ( stroker->handle_wide_strokes )
1671           {
1672             FT_Vector  start;
1673             FT_Angle   alpha1;
1674
1675
1676             /* determine whether the border radius is greater than the */
1677             /* radius of curvature of the original arc                 */
1678             start = border->points[border->num_points - 1];
1679
1680             alpha1 = FT_Atan2( end.x - start.x, end.y - start.y );
1681
1682             /* is the direction of the border arc opposite to */
1683             /* that of the original arc? */
1684             if ( ft_pos_abs( FT_Angle_Diff( alpha0, alpha1 ) ) >
1685                    FT_ANGLE_PI / 2                             )
1686             {
1687               FT_Angle   beta, gamma;
1688               FT_Vector  bvec, delta;
1689               FT_Fixed   blen, sinA, sinB, alen;
1690
1691
1692               /* use the sine rule to find the intersection point */
1693               beta  = FT_Atan2( arc[3].x - start.x, arc[3].y - start.y );
1694               gamma = FT_Atan2( arc[0].x - end.x,   arc[0].y - end.y );
1695
1696               bvec.x = end.x - start.x;
1697               bvec.y = end.y - start.y;
1698
1699               blen = FT_Vector_Length( &bvec );
1700
1701               sinA = ft_pos_abs( FT_Sin( alpha1 - gamma ) );
1702               sinB = ft_pos_abs( FT_Sin( beta - gamma ) );
1703
1704               alen = FT_MulDiv( blen, sinA, sinB );
1705
1706               FT_Vector_From_Polar( &delta, alen, beta );
1707               delta.x += start.x;
1708               delta.y += start.y;
1709
1710               /* circumnavigate the negative sector backwards */
1711               border->movable = FALSE;
1712               error = ft_stroke_border_lineto( border, &delta, FALSE );
1713               if ( error )
1714                 goto Exit;
1715               error = ft_stroke_border_lineto( border, &end, FALSE );
1716               if ( error )
1717                 goto Exit;
1718               error = ft_stroke_border_cubicto( border,
1719                                                 &ctrl2,
1720                                                 &ctrl1,
1721                                                 &start );
1722               if ( error )
1723                 goto Exit;
1724               /* and then move to the endpoint */
1725               error = ft_stroke_border_lineto( border, &end, FALSE );
1726               if ( error )
1727                 goto Exit;
1728
1729               continue;
1730             }
1731
1732             /* else fall through */
1733           }
1734
1735           /* simply add an arc */
1736           error = ft_stroke_border_cubicto( border, &ctrl1, &ctrl2, &end );
1737           if ( error )
1738             goto Exit;
1739         }
1740       }
1741
1742       arc -= 3;
1743
1744       stroker->angle_in = angle_out;
1745     }
1746
1747     stroker->center = *to;
1748
1749   Exit:
1750     return error;
1751   }
1752
1753
1754   /* documentation is in ftstroke.h */
1755
1756   FT_EXPORT_DEF( FT_Error )
1757   FT_Stroker_BeginSubPath( FT_Stroker  stroker,
1758                            FT_Vector*  to,
1759                            FT_Bool     open )
1760   {
1761     if ( !stroker || !to )
1762       return FT_THROW( Invalid_Argument );
1763
1764     /* We cannot process the first point, because there is not enough      */
1765     /* information regarding its corner/cap.  The latter will be processed */
1766     /* in the `FT_Stroker_EndSubPath' routine.                             */
1767     /*                                                                     */
1768     stroker->first_point  = TRUE;
1769     stroker->center       = *to;
1770     stroker->subpath_open = open;
1771
1772     /* Determine if we need to check whether the border radius is greater */
1773     /* than the radius of curvature of a curve, to handle this case       */
1774     /* specially.  This is only required if bevel joins or butt caps may  */
1775     /* be created, because round & miter joins and round & square caps    */
1776     /* cover the negative sector created with wide strokes.               */
1777     stroker->handle_wide_strokes =
1778       FT_BOOL( stroker->line_join != FT_STROKER_LINEJOIN_ROUND  ||
1779                ( stroker->subpath_open                        &&
1780                  stroker->line_cap == FT_STROKER_LINECAP_BUTT ) );
1781
1782     /* record the subpath start point for each border */
1783     stroker->subpath_start = *to;
1784
1785     stroker->angle_in = 0;
1786
1787     return FT_Err_Ok;
1788   }
1789
1790
1791   static FT_Error
1792   ft_stroker_add_reverse_left( FT_Stroker  stroker,
1793                                FT_Bool     open )
1794   {
1795     FT_StrokeBorder  right = stroker->borders + 0;
1796     FT_StrokeBorder  left  = stroker->borders + 1;
1797     FT_Int           new_points;
1798     FT_Error         error = FT_Err_Ok;
1799
1800
1801     FT_ASSERT( left->start >= 0 );
1802
1803     new_points = (FT_Int)left->num_points - left->start;
1804     if ( new_points > 0 )
1805     {
1806       error = ft_stroke_border_grow( right, (FT_UInt)new_points );
1807       if ( error )
1808         goto Exit;
1809
1810       {
1811         FT_Vector*  dst_point = right->points + right->num_points;
1812         FT_Byte*    dst_tag   = right->tags   + right->num_points;
1813         FT_Vector*  src_point = left->points  + left->num_points - 1;
1814         FT_Byte*    src_tag   = left->tags    + left->num_points - 1;
1815
1816
1817         while ( src_point >= left->points + left->start )
1818         {
1819           *dst_point = *src_point;
1820           *dst_tag   = *src_tag;
1821
1822           if ( open )
1823             dst_tag[0] &= ~FT_STROKE_TAG_BEGIN_END;
1824           else
1825           {
1826             FT_Byte  ttag =
1827                        (FT_Byte)( dst_tag[0] & FT_STROKE_TAG_BEGIN_END );
1828
1829
1830             /* switch begin/end tags if necessary */
1831             if ( ttag == FT_STROKE_TAG_BEGIN ||
1832                  ttag == FT_STROKE_TAG_END   )
1833               dst_tag[0] ^= FT_STROKE_TAG_BEGIN_END;
1834           }
1835
1836           src_point--;
1837           src_tag--;
1838           dst_point++;
1839           dst_tag++;
1840         }
1841       }
1842
1843       left->num_points   = (FT_UInt)left->start;
1844       right->num_points += (FT_UInt)new_points;
1845
1846       right->movable = FALSE;
1847       left->movable  = FALSE;
1848     }
1849
1850   Exit:
1851     return error;
1852   }
1853
1854
1855   /* documentation is in ftstroke.h */
1856
1857   /* there's a lot of magic in this function! */
1858   FT_EXPORT_DEF( FT_Error )
1859   FT_Stroker_EndSubPath( FT_Stroker  stroker )
1860   {
1861     FT_Error  error = FT_Err_Ok;
1862
1863
1864     if ( !stroker )
1865     {
1866       error = FT_THROW( Invalid_Argument );
1867       goto Exit;
1868     }
1869
1870     if ( stroker->subpath_open )
1871     {
1872       FT_StrokeBorder  right = stroker->borders;
1873
1874
1875       /* All right, this is an opened path, we need to add a cap between */
1876       /* right & left, add the reverse of left, then add a final cap     */
1877       /* between left & right.                                           */
1878       error = ft_stroker_cap( stroker, stroker->angle_in, 0 );
1879       if ( error )
1880         goto Exit;
1881
1882       /* add reversed points from `left' to `right' */
1883       error = ft_stroker_add_reverse_left( stroker, TRUE );
1884       if ( error )
1885         goto Exit;
1886
1887       /* now add the final cap */
1888       stroker->center = stroker->subpath_start;
1889       error = ft_stroker_cap( stroker,
1890                               stroker->subpath_angle + FT_ANGLE_PI, 0 );
1891       if ( error )
1892         goto Exit;
1893
1894       /* Now end the right subpath accordingly.  The left one is */
1895       /* rewind and doesn't need further processing.             */
1896       ft_stroke_border_close( right, FALSE );
1897     }
1898     else
1899     {
1900       FT_Angle  turn;
1901       FT_Int    inside_side;
1902
1903
1904       /* close the path if needed */
1905       if ( stroker->center.x != stroker->subpath_start.x ||
1906            stroker->center.y != stroker->subpath_start.y )
1907       {
1908          error = FT_Stroker_LineTo( stroker, &stroker->subpath_start );
1909          if ( error )
1910            goto Exit;
1911       }
1912
1913       /* process the corner */
1914       stroker->angle_out = stroker->subpath_angle;
1915       turn               = FT_Angle_Diff( stroker->angle_in,
1916                                           stroker->angle_out );
1917
1918       /* no specific corner processing is required if the turn is 0 */
1919       if ( turn != 0 )
1920       {
1921         /* when we turn to the right, the inside side is 0 */
1922         /* otherwise, the inside side is 1 */
1923         inside_side = ( turn < 0 );
1924
1925         error = ft_stroker_inside( stroker,
1926                                    inside_side,
1927                                    stroker->subpath_line_length );
1928         if ( error )
1929           goto Exit;
1930
1931         /* process the outside side */
1932         error = ft_stroker_outside( stroker,
1933                                     !inside_side,
1934                                     stroker->subpath_line_length );
1935         if ( error )
1936           goto Exit;
1937       }
1938
1939       /* then end our two subpaths */
1940       ft_stroke_border_close( stroker->borders + 0, FALSE );
1941       ft_stroke_border_close( stroker->borders + 1, TRUE );
1942     }
1943
1944   Exit:
1945     return error;
1946   }
1947
1948
1949   /* documentation is in ftstroke.h */
1950
1951   FT_EXPORT_DEF( FT_Error )
1952   FT_Stroker_GetBorderCounts( FT_Stroker        stroker,
1953                               FT_StrokerBorder  border,
1954                               FT_UInt          *anum_points,
1955                               FT_UInt          *anum_contours )
1956   {
1957     FT_UInt   num_points = 0, num_contours = 0;
1958     FT_Error  error;
1959
1960
1961     if ( !stroker || border > 1 )
1962     {
1963       error = FT_THROW( Invalid_Argument );
1964       goto Exit;
1965     }
1966
1967     error = ft_stroke_border_get_counts( stroker->borders + border,
1968                                          &num_points, &num_contours );
1969   Exit:
1970     if ( anum_points )
1971       *anum_points = num_points;
1972
1973     if ( anum_contours )
1974       *anum_contours = num_contours;
1975
1976     return error;
1977   }
1978
1979
1980   /* documentation is in ftstroke.h */
1981
1982   FT_EXPORT_DEF( FT_Error )
1983   FT_Stroker_GetCounts( FT_Stroker  stroker,
1984                         FT_UInt    *anum_points,
1985                         FT_UInt    *anum_contours )
1986   {
1987     FT_UInt   count1, count2, num_points   = 0;
1988     FT_UInt   count3, count4, num_contours = 0;
1989     FT_Error  error;
1990
1991
1992     if ( !stroker )
1993     {
1994       error = FT_THROW( Invalid_Argument );
1995       goto Exit;
1996     }
1997
1998     error = ft_stroke_border_get_counts( stroker->borders + 0,
1999                                          &count1, &count2 );
2000     if ( error )
2001       goto Exit;
2002
2003     error = ft_stroke_border_get_counts( stroker->borders + 1,
2004                                          &count3, &count4 );
2005     if ( error )
2006       goto Exit;
2007
2008     num_points   = count1 + count3;
2009     num_contours = count2 + count4;
2010
2011   Exit:
2012     if ( anum_points )
2013       *anum_points   = num_points;
2014
2015     if ( anum_contours )
2016       *anum_contours = num_contours;
2017
2018     return error;
2019   }
2020
2021
2022   /* documentation is in ftstroke.h */
2023
2024   FT_EXPORT_DEF( void )
2025   FT_Stroker_ExportBorder( FT_Stroker        stroker,
2026                            FT_StrokerBorder  border,
2027                            FT_Outline*       outline )
2028   {
2029     if ( !stroker || !outline )
2030       return;
2031
2032     if ( border == FT_STROKER_BORDER_LEFT  ||
2033          border == FT_STROKER_BORDER_RIGHT )
2034     {
2035       FT_StrokeBorder  sborder = & stroker->borders[border];
2036
2037
2038       if ( sborder->valid )
2039         ft_stroke_border_export( sborder, outline );
2040     }
2041   }
2042
2043
2044   /* documentation is in ftstroke.h */
2045
2046   FT_EXPORT_DEF( void )
2047   FT_Stroker_Export( FT_Stroker   stroker,
2048                      FT_Outline*  outline )
2049   {
2050     FT_Stroker_ExportBorder( stroker, FT_STROKER_BORDER_LEFT, outline );
2051     FT_Stroker_ExportBorder( stroker, FT_STROKER_BORDER_RIGHT, outline );
2052   }
2053
2054
2055   /* documentation is in ftstroke.h */
2056
2057   /*
2058    * The following is very similar to FT_Outline_Decompose, except
2059    * that we do support opened paths, and do not scale the outline.
2060    */
2061   FT_EXPORT_DEF( FT_Error )
2062   FT_Stroker_ParseOutline( FT_Stroker   stroker,
2063                            FT_Outline*  outline,
2064                            FT_Bool      opened )
2065   {
2066     FT_Vector   v_last;
2067     FT_Vector   v_control;
2068     FT_Vector   v_start;
2069
2070     FT_Vector*  point;
2071     FT_Vector*  limit;
2072     char*       tags;
2073
2074     FT_Error    error;
2075
2076     FT_Int      n;         /* index of contour in outline     */
2077     FT_UInt     first;     /* index of first point in contour */
2078     FT_Int      tag;       /* current point's state           */
2079
2080
2081     if ( !outline )
2082       return FT_THROW( Invalid_Outline );
2083
2084     if ( !stroker )
2085       return FT_THROW( Invalid_Argument );
2086
2087     FT_Stroker_Rewind( stroker );
2088
2089     first = 0;
2090
2091     for ( n = 0; n < outline->n_contours; n++ )
2092     {
2093       FT_UInt  last;  /* index of last point in contour */
2094
2095
2096       last  = (FT_UInt)outline->contours[n];
2097       limit = outline->points + last;
2098
2099       /* skip empty points; we don't stroke these */
2100       if ( last <= first )
2101       {
2102         first = last + 1;
2103         continue;
2104       }
2105
2106       v_start = outline->points[first];
2107       v_last  = outline->points[last];
2108
2109       v_control = v_start;
2110
2111       point = outline->points + first;
2112       tags  = outline->tags   + first;
2113       tag   = FT_CURVE_TAG( tags[0] );
2114
2115       /* A contour cannot start with a cubic control point! */
2116       if ( tag == FT_CURVE_TAG_CUBIC )
2117         goto Invalid_Outline;
2118
2119       /* check first point to determine origin */
2120       if ( tag == FT_CURVE_TAG_CONIC )
2121       {
2122         /* First point is conic control.  Yes, this happens. */
2123         if ( FT_CURVE_TAG( outline->tags[last] ) == FT_CURVE_TAG_ON )
2124         {
2125           /* start at last point if it is on the curve */
2126           v_start = v_last;
2127           limit--;
2128         }
2129         else
2130         {
2131           /* if both first and last points are conic, */
2132           /* start at their middle                    */
2133           v_start.x = ( v_start.x + v_last.x ) / 2;
2134           v_start.y = ( v_start.y + v_last.y ) / 2;
2135         }
2136         point--;
2137         tags--;
2138       }
2139
2140       error = FT_Stroker_BeginSubPath( stroker, &v_start, opened );
2141       if ( error )
2142         goto Exit;
2143
2144       while ( point < limit )
2145       {
2146         point++;
2147         tags++;
2148
2149         tag = FT_CURVE_TAG( tags[0] );
2150         switch ( tag )
2151         {
2152         case FT_CURVE_TAG_ON:  /* emit a single line_to */
2153           {
2154             FT_Vector  vec;
2155
2156
2157             vec.x = point->x;
2158             vec.y = point->y;
2159
2160             error = FT_Stroker_LineTo( stroker, &vec );
2161             if ( error )
2162               goto Exit;
2163             continue;
2164           }
2165
2166         case FT_CURVE_TAG_CONIC:  /* consume conic arcs */
2167           v_control.x = point->x;
2168           v_control.y = point->y;
2169
2170         Do_Conic:
2171           if ( point < limit )
2172           {
2173             FT_Vector  vec;
2174             FT_Vector  v_middle;
2175
2176
2177             point++;
2178             tags++;
2179             tag = FT_CURVE_TAG( tags[0] );
2180
2181             vec = point[0];
2182
2183             if ( tag == FT_CURVE_TAG_ON )
2184             {
2185               error = FT_Stroker_ConicTo( stroker, &v_control, &vec );
2186               if ( error )
2187                 goto Exit;
2188               continue;
2189             }
2190
2191             if ( tag != FT_CURVE_TAG_CONIC )
2192               goto Invalid_Outline;
2193
2194             v_middle.x = ( v_control.x + vec.x ) / 2;
2195             v_middle.y = ( v_control.y + vec.y ) / 2;
2196
2197             error = FT_Stroker_ConicTo( stroker, &v_control, &v_middle );
2198             if ( error )
2199               goto Exit;
2200
2201             v_control = vec;
2202             goto Do_Conic;
2203           }
2204
2205           error = FT_Stroker_ConicTo( stroker, &v_control, &v_start );
2206           goto Close;
2207
2208         default:  /* FT_CURVE_TAG_CUBIC */
2209           {
2210             FT_Vector  vec1, vec2;
2211
2212
2213             if ( point + 1 > limit                             ||
2214                  FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC )
2215               goto Invalid_Outline;
2216
2217             point += 2;
2218             tags  += 2;
2219
2220             vec1 = point[-2];
2221             vec2 = point[-1];
2222
2223             if ( point <= limit )
2224             {
2225               FT_Vector  vec;
2226
2227
2228               vec = point[0];
2229
2230               error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &vec );
2231               if ( error )
2232                 goto Exit;
2233               continue;
2234             }
2235
2236             error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &v_start );
2237             goto Close;
2238           }
2239         }
2240       }
2241
2242     Close:
2243       if ( error )
2244         goto Exit;
2245
2246       /* don't try to end the path if no segments have been generated */
2247       if ( !stroker->first_point )
2248       {
2249         error = FT_Stroker_EndSubPath( stroker );
2250         if ( error )
2251           goto Exit;
2252       }
2253
2254       first = last + 1;
2255     }
2256
2257     return FT_Err_Ok;
2258
2259   Exit:
2260     return error;
2261
2262   Invalid_Outline:
2263     return FT_THROW( Invalid_Outline );
2264   }
2265
2266
2267   /* documentation is in ftstroke.h */
2268
2269   FT_EXPORT_DEF( FT_Error )
2270   FT_Glyph_Stroke( FT_Glyph    *pglyph,
2271                    FT_Stroker   stroker,
2272                    FT_Bool      destroy )
2273   {
2274     FT_Error  error = FT_ERR( Invalid_Argument );
2275     FT_Glyph  glyph = NULL;
2276
2277
2278     if ( !pglyph )
2279       goto Exit;
2280
2281     glyph = *pglyph;
2282     if ( !glyph || glyph->clazz != &ft_outline_glyph_class )
2283       goto Exit;
2284
2285     {
2286       FT_Glyph  copy;
2287
2288
2289       error = FT_Glyph_Copy( glyph, &copy );
2290       if ( error )
2291         goto Exit;
2292
2293       glyph = copy;
2294     }
2295
2296     {
2297       FT_OutlineGlyph  oglyph  = (FT_OutlineGlyph)glyph;
2298       FT_Outline*      outline = &oglyph->outline;
2299       FT_UInt          num_points, num_contours;
2300
2301
2302       error = FT_Stroker_ParseOutline( stroker, outline, FALSE );
2303       if ( error )
2304         goto Fail;
2305
2306       FT_Stroker_GetCounts( stroker, &num_points, &num_contours );
2307
2308       FT_Outline_Done( glyph->library, outline );
2309
2310       error = FT_Outline_New( glyph->library,
2311                               num_points,
2312                               (FT_Int)num_contours,
2313                               outline );
2314       if ( error )
2315         goto Fail;
2316
2317       outline->n_points   = 0;
2318       outline->n_contours = 0;
2319
2320       FT_Stroker_Export( stroker, outline );
2321     }
2322
2323     if ( destroy )
2324       FT_Done_Glyph( *pglyph );
2325
2326     *pglyph = glyph;
2327     goto Exit;
2328
2329   Fail:
2330     FT_Done_Glyph( glyph );
2331     glyph = NULL;
2332
2333     if ( !destroy )
2334       *pglyph = NULL;
2335
2336   Exit:
2337     return error;
2338   }
2339
2340
2341   /* documentation is in ftstroke.h */
2342
2343   FT_EXPORT_DEF( FT_Error )
2344   FT_Glyph_StrokeBorder( FT_Glyph    *pglyph,
2345                          FT_Stroker   stroker,
2346                          FT_Bool      inside,
2347                          FT_Bool      destroy )
2348   {
2349     FT_Error  error = FT_ERR( Invalid_Argument );
2350     FT_Glyph  glyph = NULL;
2351
2352
2353     if ( !pglyph )
2354       goto Exit;
2355
2356     glyph = *pglyph;
2357     if ( !glyph || glyph->clazz != &ft_outline_glyph_class )
2358       goto Exit;
2359
2360     {
2361       FT_Glyph  copy;
2362
2363
2364       error = FT_Glyph_Copy( glyph, &copy );
2365       if ( error )
2366         goto Exit;
2367
2368       glyph = copy;
2369     }
2370
2371     {
2372       FT_OutlineGlyph   oglyph  = (FT_OutlineGlyph)glyph;
2373       FT_StrokerBorder  border;
2374       FT_Outline*       outline = &oglyph->outline;
2375       FT_UInt           num_points, num_contours;
2376
2377
2378       border = FT_Outline_GetOutsideBorder( outline );
2379       if ( inside )
2380       {
2381         if ( border == FT_STROKER_BORDER_LEFT )
2382           border = FT_STROKER_BORDER_RIGHT;
2383         else
2384           border = FT_STROKER_BORDER_LEFT;
2385       }
2386
2387       error = FT_Stroker_ParseOutline( stroker, outline, FALSE );
2388       if ( error )
2389         goto Fail;
2390
2391       FT_Stroker_GetBorderCounts( stroker, border,
2392                                   &num_points, &num_contours );
2393
2394       FT_Outline_Done( glyph->library, outline );
2395
2396       error = FT_Outline_New( glyph->library,
2397                               num_points,
2398                               (FT_Int)num_contours,
2399                               outline );
2400       if ( error )
2401         goto Fail;
2402
2403       outline->n_points   = 0;
2404       outline->n_contours = 0;
2405
2406       FT_Stroker_ExportBorder( stroker, border, outline );
2407     }
2408
2409     if ( destroy )
2410       FT_Done_Glyph( *pglyph );
2411
2412     *pglyph = glyph;
2413     goto Exit;
2414
2415   Fail:
2416     FT_Done_Glyph( glyph );
2417     glyph = NULL;
2418
2419     if ( !destroy )
2420       *pglyph = NULL;
2421
2422   Exit:
2423     return error;
2424   }
2425
2426
2427 /* END */