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