1/***************************************************************************/
2/*                                                                         */
3/*  ftstroke.c                                                             */
4/*                                                                         */
5/*    FreeType path stroker (body).                                        */
6/*                                                                         */
7/*  Copyright 2002-2015 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 = (FT_UInt)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 = (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    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)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 < 0x10000L )
838      stroker->miter_limit = 0x10000L;
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    /* Also avoid U-turns of nearly 180 degree.                */
1003    if ( !border->movable || line_length == 0  ||
1004         theta > 0x59C000 || theta < -0x59C000 )
1005      intersect = FALSE;
1006    else
1007    {
1008      /* compute minimum required length of lines */
1009      FT_Fixed  min_length = ft_pos_abs( FT_MulFix( stroker->radius,
1010                                                    FT_Tan( theta ) ) );
1011
1012
1013      intersect = FT_BOOL( min_length                         &&
1014                           stroker->line_length >= min_length &&
1015                           line_length          >= min_length );
1016    }
1017
1018    if ( !intersect )
1019    {
1020      FT_Vector_From_Polar( &delta, stroker->radius,
1021                            stroker->angle_out + rotate );
1022      delta.x += stroker->center.x;
1023      delta.y += stroker->center.y;
1024
1025      border->movable = FALSE;
1026    }
1027    else
1028    {
1029      /* compute median angle */
1030      phi = stroker->angle_in + theta;
1031
1032      thcos = FT_Cos( theta );
1033
1034      length = FT_DivFix( stroker->radius, thcos );
1035
1036      FT_Vector_From_Polar( &delta, length, phi + rotate );
1037      delta.x += stroker->center.x;
1038      delta.y += stroker->center.y;
1039    }
1040
1041    error = ft_stroke_border_lineto( border, &delta, FALSE );
1042
1043    return error;
1044  }
1045
1046
1047  /* process an outside corner, i.e. compute bevel/miter/round */
1048  static FT_Error
1049  ft_stroker_outside( FT_Stroker  stroker,
1050                      FT_Int      side,
1051                      FT_Fixed    line_length )
1052  {
1053    FT_StrokeBorder  border = stroker->borders + side;
1054    FT_Error         error;
1055    FT_Angle         rotate;
1056
1057
1058    if ( stroker->line_join == FT_STROKER_LINEJOIN_ROUND )
1059      error = ft_stroker_arcto( stroker, side );
1060    else
1061    {
1062      /* this is a mitered (pointed) or beveled (truncated) corner */
1063      FT_Fixed  sigma = 0, radius = stroker->radius;
1064      FT_Angle  theta = 0, phi = 0;
1065      FT_Fixed  thcos = 0;
1066      FT_Bool   bevel, fixed_bevel;
1067
1068
1069      rotate = FT_SIDE_TO_ROTATE( side );
1070
1071      bevel =
1072        FT_BOOL( stroker->line_join == FT_STROKER_LINEJOIN_BEVEL );
1073
1074      fixed_bevel =
1075        FT_BOOL( stroker->line_join != FT_STROKER_LINEJOIN_MITER_VARIABLE );
1076
1077      if ( !bevel )
1078      {
1079        theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
1080
1081        if ( theta == FT_ANGLE_PI )
1082        {
1083          theta = rotate;
1084          phi   = stroker->angle_in;
1085        }
1086        else
1087        {
1088          theta /= 2;
1089          phi    = stroker->angle_in + theta + rotate;
1090        }
1091
1092        thcos = FT_Cos( theta );
1093        sigma = FT_MulFix( stroker->miter_limit, thcos );
1094
1095        /* is miter limit exceeded? */
1096        if ( sigma < 0x10000L )
1097        {
1098          /* don't create variable bevels for very small deviations; */
1099          /* FT_Sin(x) = 0 for x <= 57                               */
1100          if ( fixed_bevel || ft_pos_abs( theta ) > 57 )
1101            bevel = TRUE;
1102        }
1103      }
1104
1105      if ( bevel )  /* this is a bevel (broken angle) */
1106      {
1107        if ( fixed_bevel )
1108        {
1109          /* the outer corners are simply joined together */
1110          FT_Vector  delta;
1111
1112
1113          /* add bevel */
1114          FT_Vector_From_Polar( &delta,
1115                                radius,
1116                                stroker->angle_out + rotate );
1117          delta.x += stroker->center.x;
1118          delta.y += stroker->center.y;
1119
1120          border->movable = FALSE;
1121          error = ft_stroke_border_lineto( border, &delta, FALSE );
1122        }
1123        else /* variable bevel */
1124        {
1125          /* the miter is truncated */
1126          FT_Vector  middle, delta;
1127          FT_Fixed   length;
1128
1129
1130          /* compute middle point */
1131          FT_Vector_From_Polar( &middle,
1132                                FT_MulFix( radius, stroker->miter_limit ),
1133                                phi );
1134          middle.x += stroker->center.x;
1135          middle.y += stroker->center.y;
1136
1137          /* compute first angle point */
1138          length = FT_MulDiv( radius, 0x10000L - sigma,
1139                              ft_pos_abs( FT_Sin( theta ) ) );
1140
1141          FT_Vector_From_Polar( &delta, length, phi + rotate );
1142          delta.x += middle.x;
1143          delta.y += middle.y;
1144
1145          error = ft_stroke_border_lineto( border, &delta, FALSE );
1146          if ( error )
1147            goto Exit;
1148
1149          /* compute second angle point */
1150          FT_Vector_From_Polar( &delta, length, phi - rotate );
1151          delta.x += middle.x;
1152          delta.y += middle.y;
1153
1154          error = ft_stroke_border_lineto( border, &delta, FALSE );
1155          if ( error )
1156            goto Exit;
1157
1158          /* finally, add an end point; only needed if not lineto */
1159          /* (line_length is zero for curves)                     */
1160          if ( line_length == 0 )
1161          {
1162            FT_Vector_From_Polar( &delta,
1163                                  radius,
1164                                  stroker->angle_out + rotate );
1165
1166            delta.x += stroker->center.x;
1167            delta.y += stroker->center.y;
1168
1169            error = ft_stroke_border_lineto( border, &delta, FALSE );
1170          }
1171        }
1172      }
1173      else /* this is a miter (intersection) */
1174      {
1175        FT_Fixed   length;
1176        FT_Vector  delta;
1177
1178
1179        length = FT_DivFix( stroker->radius, thcos );
1180
1181        FT_Vector_From_Polar( &delta, length, phi );
1182        delta.x += stroker->center.x;
1183        delta.y += stroker->center.y;
1184
1185        error = ft_stroke_border_lineto( border, &delta, FALSE );
1186        if ( error )
1187          goto Exit;
1188
1189        /* now add an end point; only needed if not lineto */
1190        /* (line_length is zero for curves)                */
1191        if ( line_length == 0 )
1192        {
1193          FT_Vector_From_Polar( &delta,
1194                                stroker->radius,
1195                                stroker->angle_out + rotate );
1196          delta.x += stroker->center.x;
1197          delta.y += stroker->center.y;
1198
1199          error = ft_stroke_border_lineto( border, &delta, FALSE );
1200        }
1201      }
1202    }
1203
1204  Exit:
1205    return error;
1206  }
1207
1208
1209  static FT_Error
1210  ft_stroker_process_corner( FT_Stroker  stroker,
1211                             FT_Fixed    line_length )
1212  {
1213    FT_Error  error = FT_Err_Ok;
1214    FT_Angle  turn;
1215    FT_Int    inside_side;
1216
1217
1218    turn = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
1219
1220    /* no specific corner processing is required if the turn is 0 */
1221    if ( turn == 0 )
1222      goto Exit;
1223
1224    /* when we turn to the right, the inside side is 0 */
1225    /* otherwise, the inside side is 1 */
1226    inside_side = ( turn < 0 );
1227
1228    /* process the inside side */
1229    error = ft_stroker_inside( stroker, inside_side, line_length );
1230    if ( error )
1231      goto Exit;
1232
1233    /* process the outside side */
1234    error = ft_stroker_outside( stroker, !inside_side, line_length );
1235
1236  Exit:
1237    return error;
1238  }
1239
1240
1241  /* add two points to the left and right borders corresponding to the */
1242  /* start of the subpath                                              */
1243  static FT_Error
1244  ft_stroker_subpath_start( FT_Stroker  stroker,
1245                            FT_Angle    start_angle,
1246                            FT_Fixed    line_length )
1247  {
1248    FT_Vector        delta;
1249    FT_Vector        point;
1250    FT_Error         error;
1251    FT_StrokeBorder  border;
1252
1253
1254    FT_Vector_From_Polar( &delta, stroker->radius,
1255                          start_angle + FT_ANGLE_PI2 );
1256
1257    point.x = stroker->center.x + delta.x;
1258    point.y = stroker->center.y + delta.y;
1259
1260    border = stroker->borders;
1261    error = ft_stroke_border_moveto( border, &point );
1262    if ( error )
1263      goto Exit;
1264
1265    point.x = stroker->center.x - delta.x;
1266    point.y = stroker->center.y - delta.y;
1267
1268    border++;
1269    error = ft_stroke_border_moveto( border, &point );
1270
1271    /* save angle, position, and line length for last join */
1272    /* (line_length is zero for curves)                    */
1273    stroker->subpath_angle       = start_angle;
1274    stroker->first_point         = FALSE;
1275    stroker->subpath_line_length = line_length;
1276
1277  Exit:
1278    return error;
1279  }
1280
1281
1282  /* documentation is in ftstroke.h */
1283
1284  FT_EXPORT_DEF( FT_Error )
1285  FT_Stroker_LineTo( FT_Stroker  stroker,
1286                     FT_Vector*  to )
1287  {
1288    FT_Error         error = FT_Err_Ok;
1289    FT_StrokeBorder  border;
1290    FT_Vector        delta;
1291    FT_Angle         angle;
1292    FT_Int           side;
1293    FT_Fixed         line_length;
1294
1295
1296    if ( !stroker || !to )
1297      return FT_THROW( Invalid_Argument );
1298
1299    delta.x = to->x - stroker->center.x;
1300    delta.y = to->y - stroker->center.y;
1301
1302    /* a zero-length lineto is a no-op; avoid creating a spurious corner */
1303    if ( delta.x == 0 && delta.y == 0 )
1304       goto Exit;
1305
1306    /* compute length of line */
1307    line_length = FT_Vector_Length( &delta );
1308
1309    angle = FT_Atan2( delta.x, delta.y );
1310    FT_Vector_From_Polar( &delta, stroker->radius, angle + FT_ANGLE_PI2 );
1311
1312    /* process corner if necessary */
1313    if ( stroker->first_point )
1314    {
1315      /* This is the first segment of a subpath.  We need to     */
1316      /* add a point to each border at their respective starting */
1317      /* point locations.                                        */
1318      error = ft_stroker_subpath_start( stroker, angle, line_length );
1319      if ( error )
1320        goto Exit;
1321    }
1322    else
1323    {
1324      /* process the current corner */
1325      stroker->angle_out = angle;
1326      error = ft_stroker_process_corner( stroker, line_length );
1327      if ( error )
1328        goto Exit;
1329    }
1330
1331    /* now add a line segment to both the `inside' and `outside' paths */
1332    for ( border = stroker->borders, side = 1; side >= 0; side--, border++ )
1333    {
1334      FT_Vector  point;
1335
1336
1337      point.x = to->x + delta.x;
1338      point.y = to->y + delta.y;
1339
1340      /* the ends of lineto borders are movable */
1341      error = ft_stroke_border_lineto( border, &point, TRUE );
1342      if ( error )
1343        goto Exit;
1344
1345      delta.x = -delta.x;
1346      delta.y = -delta.y;
1347    }
1348
1349    stroker->angle_in    = angle;
1350    stroker->center      = *to;
1351    stroker->line_length = line_length;
1352
1353  Exit:
1354    return error;
1355  }
1356
1357
1358  /* documentation is in ftstroke.h */
1359
1360  FT_EXPORT_DEF( FT_Error )
1361  FT_Stroker_ConicTo( FT_Stroker  stroker,
1362                      FT_Vector*  control,
1363                      FT_Vector*  to )
1364  {
1365    FT_Error    error = FT_Err_Ok;
1366    FT_Vector   bez_stack[34];
1367    FT_Vector*  arc;
1368    FT_Vector*  limit = bez_stack + 30;
1369    FT_Bool     first_arc = TRUE;
1370
1371
1372    if ( !stroker || !control || !to )
1373    {
1374      error = FT_THROW( Invalid_Argument );
1375      goto Exit;
1376    }
1377
1378    /* if all control points are coincident, this is a no-op; */
1379    /* avoid creating a spurious corner                       */
1380    if ( FT_IS_SMALL( stroker->center.x - control->x ) &&
1381         FT_IS_SMALL( stroker->center.y - control->y ) &&
1382         FT_IS_SMALL( control->x        - to->x      ) &&
1383         FT_IS_SMALL( control->y        - to->y      ) )
1384    {
1385       stroker->center = *to;
1386       goto Exit;
1387    }
1388
1389    arc    = bez_stack;
1390    arc[0] = *to;
1391    arc[1] = *control;
1392    arc[2] = stroker->center;
1393
1394    while ( arc >= bez_stack )
1395    {
1396      FT_Angle  angle_in, angle_out;
1397
1398
1399      /* initialize with current direction */
1400      angle_in = angle_out = stroker->angle_in;
1401
1402      if ( arc < limit                                             &&
1403           !ft_conic_is_small_enough( arc, &angle_in, &angle_out ) )
1404      {
1405        if ( stroker->first_point )
1406          stroker->angle_in = angle_in;
1407
1408        ft_conic_split( arc );
1409        arc += 2;
1410        continue;
1411      }
1412
1413      if ( first_arc )
1414      {
1415        first_arc = FALSE;
1416
1417        /* process corner if necessary */
1418        if ( stroker->first_point )
1419          error = ft_stroker_subpath_start( stroker, angle_in, 0 );
1420        else
1421        {
1422          stroker->angle_out = angle_in;
1423          error = ft_stroker_process_corner( stroker, 0 );
1424        }
1425      }
1426      else if ( ft_pos_abs( FT_Angle_Diff( stroker->angle_in, angle_in ) ) >
1427                  FT_SMALL_CONIC_THRESHOLD / 4                             )
1428      {
1429        /* if the deviation from one arc to the next is too great, */
1430        /* add a round corner                                      */
1431        stroker->center    = arc[2];
1432        stroker->angle_out = angle_in;
1433        stroker->line_join = FT_STROKER_LINEJOIN_ROUND;
1434
1435        error = ft_stroker_process_corner( stroker, 0 );
1436
1437        /* reinstate line join style */
1438        stroker->line_join = stroker->line_join_saved;
1439      }
1440
1441      if ( error )
1442        goto Exit;
1443
1444      /* the arc's angle is small enough; we can add it directly to each */
1445      /* border                                                          */
1446      {
1447        FT_Vector        ctrl, end;
1448        FT_Angle         theta, phi, rotate, alpha0 = 0;
1449        FT_Fixed         length;
1450        FT_StrokeBorder  border;
1451        FT_Int           side;
1452
1453
1454        theta  = FT_Angle_Diff( angle_in, angle_out ) / 2;
1455        phi    = angle_in + theta;
1456        length = FT_DivFix( stroker->radius, FT_Cos( theta ) );
1457
1458        /* compute direction of original arc */
1459        if ( stroker->handle_wide_strokes )
1460          alpha0 = FT_Atan2( arc[0].x - arc[2].x, arc[0].y - arc[2].y );
1461
1462        for ( border = stroker->borders, side = 0;
1463              side <= 1;
1464              side++, border++ )
1465        {
1466          rotate = FT_SIDE_TO_ROTATE( side );
1467
1468          /* compute control point */
1469          FT_Vector_From_Polar( &ctrl, length, phi + rotate );
1470          ctrl.x += arc[1].x;
1471          ctrl.y += arc[1].y;
1472
1473          /* compute end point */
1474          FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate );
1475          end.x += arc[0].x;
1476          end.y += arc[0].y;
1477
1478          if ( stroker->handle_wide_strokes )
1479          {
1480            FT_Vector  start;
1481            FT_Angle   alpha1;
1482
1483
1484            /* determine whether the border radius is greater than the */
1485            /* radius of curvature of the original arc                 */
1486            start = border->points[border->num_points - 1];
1487
1488            alpha1 = FT_Atan2( end.x - start.x, end.y - start.y );
1489
1490            /* is the direction of the border arc opposite to */
1491            /* that of the original arc? */
1492            if ( ft_pos_abs( FT_Angle_Diff( alpha0, alpha1 ) ) >
1493                   FT_ANGLE_PI / 2                             )
1494            {
1495              FT_Angle   beta, gamma;
1496              FT_Vector  bvec, delta;
1497              FT_Fixed   blen, sinA, sinB, alen;
1498
1499
1500              /* use the sine rule to find the intersection point */
1501              beta  = FT_Atan2( arc[2].x - start.x, arc[2].y - start.y );
1502              gamma = FT_Atan2( arc[0].x - end.x,   arc[0].y - end.y );
1503
1504              bvec.x = end.x - start.x;
1505              bvec.y = end.y - start.y;
1506
1507              blen = FT_Vector_Length( &bvec );
1508
1509              sinA = ft_pos_abs( FT_Sin( alpha1 - gamma ) );
1510              sinB = ft_pos_abs( FT_Sin( beta - gamma ) );
1511
1512              alen = FT_MulDiv( blen, sinA, sinB );
1513
1514              FT_Vector_From_Polar( &delta, alen, beta );
1515              delta.x += start.x;
1516              delta.y += start.y;
1517
1518              /* circumnavigate the negative sector backwards */
1519              border->movable = FALSE;
1520              error = ft_stroke_border_lineto( border, &delta, FALSE );
1521              if ( error )
1522                goto Exit;
1523              error = ft_stroke_border_lineto( border, &end, FALSE );
1524              if ( error )
1525                goto Exit;
1526              error = ft_stroke_border_conicto( border, &ctrl, &start );
1527              if ( error )
1528                goto Exit;
1529              /* and then move to the endpoint */
1530              error = ft_stroke_border_lineto( border, &end, FALSE );
1531              if ( error )
1532                goto Exit;
1533
1534              continue;
1535            }
1536
1537            /* else fall through */
1538          }
1539
1540          /* simply add an arc */
1541          error = ft_stroke_border_conicto( border, &ctrl, &end );
1542          if ( error )
1543            goto Exit;
1544        }
1545      }
1546
1547      arc -= 2;
1548
1549      stroker->angle_in = angle_out;
1550    }
1551
1552    stroker->center = *to;
1553
1554  Exit:
1555    return error;
1556  }
1557
1558
1559  /* documentation is in ftstroke.h */
1560
1561  FT_EXPORT_DEF( FT_Error )
1562  FT_Stroker_CubicTo( FT_Stroker  stroker,
1563                      FT_Vector*  control1,
1564                      FT_Vector*  control2,
1565                      FT_Vector*  to )
1566  {
1567    FT_Error    error = FT_Err_Ok;
1568    FT_Vector   bez_stack[37];
1569    FT_Vector*  arc;
1570    FT_Vector*  limit = bez_stack + 32;
1571    FT_Bool     first_arc = TRUE;
1572
1573
1574    if ( !stroker || !control1 || !control2 || !to )
1575    {
1576      error = FT_THROW( Invalid_Argument );
1577      goto Exit;
1578    }
1579
1580    /* if all control points are coincident, this is a no-op; */
1581    /* avoid creating a spurious corner */
1582    if ( FT_IS_SMALL( stroker->center.x - control1->x ) &&
1583         FT_IS_SMALL( stroker->center.y - control1->y ) &&
1584         FT_IS_SMALL( control1->x       - control2->x ) &&
1585         FT_IS_SMALL( control1->y       - control2->y ) &&
1586         FT_IS_SMALL( control2->x       - to->x       ) &&
1587         FT_IS_SMALL( control2->y       - to->y       ) )
1588    {
1589       stroker->center = *to;
1590       goto Exit;
1591    }
1592
1593    arc    = bez_stack;
1594    arc[0] = *to;
1595    arc[1] = *control2;
1596    arc[2] = *control1;
1597    arc[3] = stroker->center;
1598
1599    while ( arc >= bez_stack )
1600    {
1601      FT_Angle  angle_in, angle_mid, angle_out;
1602
1603
1604      /* initialize with current direction */
1605      angle_in = angle_out = angle_mid = stroker->angle_in;
1606
1607      if ( arc < limit                                         &&
1608           !ft_cubic_is_small_enough( arc, &angle_in,
1609                                      &angle_mid, &angle_out ) )
1610      {
1611        if ( stroker->first_point )
1612          stroker->angle_in = angle_in;
1613
1614        ft_cubic_split( arc );
1615        arc += 3;
1616        continue;
1617      }
1618
1619      if ( first_arc )
1620      {
1621        first_arc = FALSE;
1622
1623        /* process corner if necessary */
1624        if ( stroker->first_point )
1625          error = ft_stroker_subpath_start( stroker, angle_in, 0 );
1626        else
1627        {
1628          stroker->angle_out = angle_in;
1629          error = ft_stroker_process_corner( stroker, 0 );
1630        }
1631      }
1632      else if ( ft_pos_abs( FT_Angle_Diff( stroker->angle_in, angle_in ) ) >
1633                  FT_SMALL_CUBIC_THRESHOLD / 4                             )
1634      {
1635        /* if the deviation from one arc to the next is too great, */
1636        /* add a round corner                                      */
1637        stroker->center    = arc[3];
1638        stroker->angle_out = angle_in;
1639        stroker->line_join = FT_STROKER_LINEJOIN_ROUND;
1640
1641        error = ft_stroker_process_corner( stroker, 0 );
1642
1643        /* reinstate line join style */
1644        stroker->line_join = stroker->line_join_saved;
1645      }
1646
1647      if ( error )
1648        goto Exit;
1649
1650      /* the arc's angle is small enough; we can add it directly to each */
1651      /* border                                                          */
1652      {
1653        FT_Vector        ctrl1, ctrl2, end;
1654        FT_Angle         theta1, phi1, theta2, phi2, rotate, alpha0 = 0;
1655        FT_Fixed         length1, length2;
1656        FT_StrokeBorder  border;
1657        FT_Int           side;
1658
1659
1660        theta1  = FT_Angle_Diff( angle_in,  angle_mid ) / 2;
1661        theta2  = FT_Angle_Diff( angle_mid, angle_out ) / 2;
1662        phi1    = ft_angle_mean( angle_in,  angle_mid );
1663        phi2    = ft_angle_mean( angle_mid, angle_out );
1664        length1 = FT_DivFix( stroker->radius, FT_Cos( theta1 ) );
1665        length2 = FT_DivFix( stroker->radius, FT_Cos( theta2 ) );
1666
1667        /* compute direction of original arc */
1668        if ( stroker->handle_wide_strokes )
1669          alpha0 = FT_Atan2( arc[0].x - arc[3].x, arc[0].y - arc[3].y );
1670
1671        for ( border = stroker->borders, side = 0;
1672              side <= 1;
1673              side++, border++ )
1674        {
1675          rotate = FT_SIDE_TO_ROTATE( side );
1676
1677          /* compute control points */
1678          FT_Vector_From_Polar( &ctrl1, length1, phi1 + rotate );
1679          ctrl1.x += arc[2].x;
1680          ctrl1.y += arc[2].y;
1681
1682          FT_Vector_From_Polar( &ctrl2, length2, phi2 + rotate );
1683          ctrl2.x += arc[1].x;
1684          ctrl2.y += arc[1].y;
1685
1686          /* compute end point */
1687          FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate );
1688          end.x += arc[0].x;
1689          end.y += arc[0].y;
1690
1691          if ( stroker->handle_wide_strokes )
1692          {
1693            FT_Vector  start;
1694            FT_Angle   alpha1;
1695
1696
1697            /* determine whether the border radius is greater than the */
1698            /* radius of curvature of the original arc                 */
1699            start = border->points[border->num_points - 1];
1700
1701            alpha1 = FT_Atan2( end.x - start.x, end.y - start.y );
1702
1703            /* is the direction of the border arc opposite to */
1704            /* that of the original arc? */
1705            if ( ft_pos_abs( FT_Angle_Diff( alpha0, alpha1 ) ) >
1706                   FT_ANGLE_PI / 2                             )
1707            {
1708              FT_Angle   beta, gamma;
1709              FT_Vector  bvec, delta;
1710              FT_Fixed   blen, sinA, sinB, alen;
1711
1712
1713              /* use the sine rule to find the intersection point */
1714              beta  = FT_Atan2( arc[3].x - start.x, arc[3].y - start.y );
1715              gamma = FT_Atan2( arc[0].x - end.x,   arc[0].y - end.y );
1716
1717              bvec.x = end.x - start.x;
1718              bvec.y = end.y - start.y;
1719
1720              blen = FT_Vector_Length( &bvec );
1721
1722              sinA = ft_pos_abs( FT_Sin( alpha1 - gamma ) );
1723              sinB = ft_pos_abs( FT_Sin( beta - gamma ) );
1724
1725              alen = FT_MulDiv( blen, sinA, sinB );
1726
1727              FT_Vector_From_Polar( &delta, alen, beta );
1728              delta.x += start.x;
1729              delta.y += start.y;
1730
1731              /* circumnavigate the negative sector backwards */
1732              border->movable = FALSE;
1733              error = ft_stroke_border_lineto( border, &delta, FALSE );
1734              if ( error )
1735                goto Exit;
1736              error = ft_stroke_border_lineto( border, &end, FALSE );
1737              if ( error )
1738                goto Exit;
1739              error = ft_stroke_border_cubicto( border,
1740                                                &ctrl2,
1741                                                &ctrl1,
1742                                                &start );
1743              if ( error )
1744                goto Exit;
1745              /* and then move to the endpoint */
1746              error = ft_stroke_border_lineto( border, &end, FALSE );
1747              if ( error )
1748                goto Exit;
1749
1750              continue;
1751            }
1752
1753            /* else fall through */
1754          }
1755
1756          /* simply add an arc */
1757          error = ft_stroke_border_cubicto( border, &ctrl1, &ctrl2, &end );
1758          if ( error )
1759            goto Exit;
1760        }
1761      }
1762
1763      arc -= 3;
1764
1765      stroker->angle_in = angle_out;
1766    }
1767
1768    stroker->center = *to;
1769
1770  Exit:
1771    return error;
1772  }
1773
1774
1775  /* documentation is in ftstroke.h */
1776
1777  FT_EXPORT_DEF( FT_Error )
1778  FT_Stroker_BeginSubPath( FT_Stroker  stroker,
1779                           FT_Vector*  to,
1780                           FT_Bool     open )
1781  {
1782    if ( !stroker || !to )
1783      return FT_THROW( Invalid_Argument );
1784
1785    /* We cannot process the first point, because there is not enough      */
1786    /* information regarding its corner/cap.  The latter will be processed */
1787    /* in the `FT_Stroker_EndSubPath' routine.                             */
1788    /*                                                                     */
1789    stroker->first_point  = TRUE;
1790    stroker->center       = *to;
1791    stroker->subpath_open = open;
1792
1793    /* Determine if we need to check whether the border radius is greater */
1794    /* than the radius of curvature of a curve, to handle this case       */
1795    /* specially.  This is only required if bevel joins or butt caps may  */
1796    /* be created, because round & miter joins and round & square caps    */
1797    /* cover the negative sector created with wide strokes.               */
1798    stroker->handle_wide_strokes =
1799      FT_BOOL( stroker->line_join != FT_STROKER_LINEJOIN_ROUND  ||
1800               ( stroker->subpath_open                        &&
1801                 stroker->line_cap == FT_STROKER_LINECAP_BUTT ) );
1802
1803    /* record the subpath start point for each border */
1804    stroker->subpath_start = *to;
1805
1806    stroker->angle_in = 0;
1807
1808    return FT_Err_Ok;
1809  }
1810
1811
1812  static FT_Error
1813  ft_stroker_add_reverse_left( FT_Stroker  stroker,
1814                               FT_Bool     open )
1815  {
1816    FT_StrokeBorder  right = stroker->borders + 0;
1817    FT_StrokeBorder  left  = stroker->borders + 1;
1818    FT_Int           new_points;
1819    FT_Error         error = FT_Err_Ok;
1820
1821
1822    FT_ASSERT( left->start >= 0 );
1823
1824    new_points = (FT_Int)left->num_points - left->start;
1825    if ( new_points > 0 )
1826    {
1827      error = ft_stroke_border_grow( right, (FT_UInt)new_points );
1828      if ( error )
1829        goto Exit;
1830
1831      {
1832        FT_Vector*  dst_point = right->points + right->num_points;
1833        FT_Byte*    dst_tag   = right->tags   + right->num_points;
1834        FT_Vector*  src_point = left->points  + left->num_points - 1;
1835        FT_Byte*    src_tag   = left->tags    + left->num_points - 1;
1836
1837
1838        while ( src_point >= left->points + left->start )
1839        {
1840          *dst_point = *src_point;
1841          *dst_tag   = *src_tag;
1842
1843          if ( open )
1844            dst_tag[0] &= ~FT_STROKE_TAG_BEGIN_END;
1845          else
1846          {
1847            FT_Byte  ttag =
1848                       (FT_Byte)( dst_tag[0] & FT_STROKE_TAG_BEGIN_END );
1849
1850
1851            /* switch begin/end tags if necessary */
1852            if ( ttag == FT_STROKE_TAG_BEGIN ||
1853                 ttag == FT_STROKE_TAG_END   )
1854              dst_tag[0] ^= FT_STROKE_TAG_BEGIN_END;
1855          }
1856
1857          src_point--;
1858          src_tag--;
1859          dst_point++;
1860          dst_tag++;
1861        }
1862      }
1863
1864      left->num_points   = (FT_UInt)left->start;
1865      right->num_points += (FT_UInt)new_points;
1866
1867      right->movable = FALSE;
1868      left->movable  = FALSE;
1869    }
1870
1871  Exit:
1872    return error;
1873  }
1874
1875
1876  /* documentation is in ftstroke.h */
1877
1878  /* there's a lot of magic in this function! */
1879  FT_EXPORT_DEF( FT_Error )
1880  FT_Stroker_EndSubPath( FT_Stroker  stroker )
1881  {
1882    FT_Error  error = FT_Err_Ok;
1883
1884
1885    if ( !stroker )
1886    {
1887      error = FT_THROW( Invalid_Argument );
1888      goto Exit;
1889    }
1890
1891    if ( stroker->subpath_open )
1892    {
1893      FT_StrokeBorder  right = stroker->borders;
1894
1895
1896      /* All right, this is an opened path, we need to add a cap between */
1897      /* right & left, add the reverse of left, then add a final cap     */
1898      /* between left & right.                                           */
1899      error = ft_stroker_cap( stroker, stroker->angle_in, 0 );
1900      if ( error )
1901        goto Exit;
1902
1903      /* add reversed points from `left' to `right' */
1904      error = ft_stroker_add_reverse_left( stroker, TRUE );
1905      if ( error )
1906        goto Exit;
1907
1908      /* now add the final cap */
1909      stroker->center = stroker->subpath_start;
1910      error = ft_stroker_cap( stroker,
1911                              stroker->subpath_angle + FT_ANGLE_PI, 0 );
1912      if ( error )
1913        goto Exit;
1914
1915      /* Now end the right subpath accordingly.  The left one is */
1916      /* rewind and doesn't need further processing.             */
1917      ft_stroke_border_close( right, FALSE );
1918    }
1919    else
1920    {
1921      FT_Angle  turn;
1922      FT_Int    inside_side;
1923
1924
1925      /* close the path if needed */
1926      if ( stroker->center.x != stroker->subpath_start.x ||
1927           stroker->center.y != stroker->subpath_start.y )
1928      {
1929         error = FT_Stroker_LineTo( stroker, &stroker->subpath_start );
1930         if ( error )
1931           goto Exit;
1932      }
1933
1934      /* process the corner */
1935      stroker->angle_out = stroker->subpath_angle;
1936      turn               = FT_Angle_Diff( stroker->angle_in,
1937                                          stroker->angle_out );
1938
1939      /* no specific corner processing is required if the turn is 0 */
1940      if ( turn != 0 )
1941      {
1942        /* when we turn to the right, the inside side is 0 */
1943        /* otherwise, the inside side is 1 */
1944        inside_side = ( turn < 0 );
1945
1946        error = ft_stroker_inside( stroker,
1947                                   inside_side,
1948                                   stroker->subpath_line_length );
1949        if ( error )
1950          goto Exit;
1951
1952        /* process the outside side */
1953        error = ft_stroker_outside( stroker,
1954                                    !inside_side,
1955                                    stroker->subpath_line_length );
1956        if ( error )
1957          goto Exit;
1958      }
1959
1960      /* then end our two subpaths */
1961      ft_stroke_border_close( stroker->borders + 0, FALSE );
1962      ft_stroke_border_close( stroker->borders + 1, TRUE );
1963    }
1964
1965  Exit:
1966    return error;
1967  }
1968
1969
1970  /* documentation is in ftstroke.h */
1971
1972  FT_EXPORT_DEF( FT_Error )
1973  FT_Stroker_GetBorderCounts( FT_Stroker        stroker,
1974                              FT_StrokerBorder  border,
1975                              FT_UInt          *anum_points,
1976                              FT_UInt          *anum_contours )
1977  {
1978    FT_UInt   num_points = 0, num_contours = 0;
1979    FT_Error  error;
1980
1981
1982    if ( !stroker || border > 1 )
1983    {
1984      error = FT_THROW( Invalid_Argument );
1985      goto Exit;
1986    }
1987
1988    error = ft_stroke_border_get_counts( stroker->borders + border,
1989                                         &num_points, &num_contours );
1990  Exit:
1991    if ( anum_points )
1992      *anum_points = num_points;
1993
1994    if ( anum_contours )
1995      *anum_contours = num_contours;
1996
1997    return error;
1998  }
1999
2000
2001  /* documentation is in ftstroke.h */
2002
2003  FT_EXPORT_DEF( FT_Error )
2004  FT_Stroker_GetCounts( FT_Stroker  stroker,
2005                        FT_UInt    *anum_points,
2006                        FT_UInt    *anum_contours )
2007  {
2008    FT_UInt   count1, count2, num_points   = 0;
2009    FT_UInt   count3, count4, num_contours = 0;
2010    FT_Error  error;
2011
2012
2013    if ( !stroker )
2014    {
2015      error = FT_THROW( Invalid_Argument );
2016      goto Exit;
2017    }
2018
2019    error = ft_stroke_border_get_counts( stroker->borders + 0,
2020                                         &count1, &count2 );
2021    if ( error )
2022      goto Exit;
2023
2024    error = ft_stroke_border_get_counts( stroker->borders + 1,
2025                                         &count3, &count4 );
2026    if ( error )
2027      goto Exit;
2028
2029    num_points   = count1 + count3;
2030    num_contours = count2 + count4;
2031
2032  Exit:
2033    if ( anum_points )
2034      *anum_points   = num_points;
2035
2036    if ( anum_contours )
2037      *anum_contours = num_contours;
2038
2039    return error;
2040  }
2041
2042
2043  /* documentation is in ftstroke.h */
2044
2045  FT_EXPORT_DEF( void )
2046  FT_Stroker_ExportBorder( FT_Stroker        stroker,
2047                           FT_StrokerBorder  border,
2048                           FT_Outline*       outline )
2049  {
2050    if ( !stroker || !outline )
2051      return;
2052
2053    if ( border == FT_STROKER_BORDER_LEFT  ||
2054         border == FT_STROKER_BORDER_RIGHT )
2055    {
2056      FT_StrokeBorder  sborder = & stroker->borders[border];
2057
2058
2059      if ( sborder->valid )
2060        ft_stroke_border_export( sborder, outline );
2061    }
2062  }
2063
2064
2065  /* documentation is in ftstroke.h */
2066
2067  FT_EXPORT_DEF( void )
2068  FT_Stroker_Export( FT_Stroker   stroker,
2069                     FT_Outline*  outline )
2070  {
2071    FT_Stroker_ExportBorder( stroker, FT_STROKER_BORDER_LEFT, outline );
2072    FT_Stroker_ExportBorder( stroker, FT_STROKER_BORDER_RIGHT, outline );
2073  }
2074
2075
2076  /* documentation is in ftstroke.h */
2077
2078  /*
2079   *  The following is very similar to FT_Outline_Decompose, except
2080   *  that we do support opened paths, and do not scale the outline.
2081   */
2082  FT_EXPORT_DEF( FT_Error )
2083  FT_Stroker_ParseOutline( FT_Stroker   stroker,
2084                           FT_Outline*  outline,
2085                           FT_Bool      opened )
2086  {
2087    FT_Vector   v_last;
2088    FT_Vector   v_control;
2089    FT_Vector   v_start;
2090
2091    FT_Vector*  point;
2092    FT_Vector*  limit;
2093    char*       tags;
2094
2095    FT_Error    error;
2096
2097    FT_Int      n;         /* index of contour in outline     */
2098    FT_UInt     first;     /* index of first point in contour */
2099    FT_Int      tag;       /* current point's state           */
2100
2101
2102    if ( !outline )
2103      return FT_THROW( Invalid_Outline );
2104
2105    if ( !stroker )
2106      return FT_THROW( Invalid_Argument );
2107
2108    FT_Stroker_Rewind( stroker );
2109
2110    first = 0;
2111
2112    for ( n = 0; n < outline->n_contours; n++ )
2113    {
2114      FT_UInt  last;  /* index of last point in contour */
2115
2116
2117      last  = (FT_UInt)outline->contours[n];
2118      limit = outline->points + last;
2119
2120      /* skip empty points; we don't stroke these */
2121      if ( last <= first )
2122      {
2123        first = last + 1;
2124        continue;
2125      }
2126
2127      v_start = outline->points[first];
2128      v_last  = outline->points[last];
2129
2130      v_control = v_start;
2131
2132      point = outline->points + first;
2133      tags  = outline->tags   + first;
2134      tag   = FT_CURVE_TAG( tags[0] );
2135
2136      /* A contour cannot start with a cubic control point! */
2137      if ( tag == FT_CURVE_TAG_CUBIC )
2138        goto Invalid_Outline;
2139
2140      /* check first point to determine origin */
2141      if ( tag == FT_CURVE_TAG_CONIC )
2142      {
2143        /* First point is conic control.  Yes, this happens. */
2144        if ( FT_CURVE_TAG( outline->tags[last] ) == FT_CURVE_TAG_ON )
2145        {
2146          /* start at last point if it is on the curve */
2147          v_start = v_last;
2148          limit--;
2149        }
2150        else
2151        {
2152          /* if both first and last points are conic, */
2153          /* start at their middle                    */
2154          v_start.x = ( v_start.x + v_last.x ) / 2;
2155          v_start.y = ( v_start.y + v_last.y ) / 2;
2156        }
2157        point--;
2158        tags--;
2159      }
2160
2161      error = FT_Stroker_BeginSubPath( stroker, &v_start, opened );
2162      if ( error )
2163        goto Exit;
2164
2165      while ( point < limit )
2166      {
2167        point++;
2168        tags++;
2169
2170        tag = FT_CURVE_TAG( tags[0] );
2171        switch ( tag )
2172        {
2173        case FT_CURVE_TAG_ON:  /* emit a single line_to */
2174          {
2175            FT_Vector  vec;
2176
2177
2178            vec.x = point->x;
2179            vec.y = point->y;
2180
2181            error = FT_Stroker_LineTo( stroker, &vec );
2182            if ( error )
2183              goto Exit;
2184            continue;
2185          }
2186
2187        case FT_CURVE_TAG_CONIC:  /* consume conic arcs */
2188          v_control.x = point->x;
2189          v_control.y = point->y;
2190
2191        Do_Conic:
2192          if ( point < limit )
2193          {
2194            FT_Vector  vec;
2195            FT_Vector  v_middle;
2196
2197
2198            point++;
2199            tags++;
2200            tag = FT_CURVE_TAG( tags[0] );
2201
2202            vec = point[0];
2203
2204            if ( tag == FT_CURVE_TAG_ON )
2205            {
2206              error = FT_Stroker_ConicTo( stroker, &v_control, &vec );
2207              if ( error )
2208                goto Exit;
2209              continue;
2210            }
2211
2212            if ( tag != FT_CURVE_TAG_CONIC )
2213              goto Invalid_Outline;
2214
2215            v_middle.x = ( v_control.x + vec.x ) / 2;
2216            v_middle.y = ( v_control.y + vec.y ) / 2;
2217
2218            error = FT_Stroker_ConicTo( stroker, &v_control, &v_middle );
2219            if ( error )
2220              goto Exit;
2221
2222            v_control = vec;
2223            goto Do_Conic;
2224          }
2225
2226          error = FT_Stroker_ConicTo( stroker, &v_control, &v_start );
2227          goto Close;
2228
2229        default:  /* FT_CURVE_TAG_CUBIC */
2230          {
2231            FT_Vector  vec1, vec2;
2232
2233
2234            if ( point + 1 > limit                             ||
2235                 FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC )
2236              goto Invalid_Outline;
2237
2238            point += 2;
2239            tags  += 2;
2240
2241            vec1 = point[-2];
2242            vec2 = point[-1];
2243
2244            if ( point <= limit )
2245            {
2246              FT_Vector  vec;
2247
2248
2249              vec = point[0];
2250
2251              error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &vec );
2252              if ( error )
2253                goto Exit;
2254              continue;
2255            }
2256
2257            error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &v_start );
2258            goto Close;
2259          }
2260        }
2261      }
2262
2263    Close:
2264      if ( error )
2265        goto Exit;
2266
2267      /* don't try to end the path if no segments have been generated */
2268      if ( !stroker->first_point )
2269      {
2270        error = FT_Stroker_EndSubPath( stroker );
2271        if ( error )
2272          goto Exit;
2273      }
2274
2275      first = last + 1;
2276    }
2277
2278    return FT_Err_Ok;
2279
2280  Exit:
2281    return error;
2282
2283  Invalid_Outline:
2284    return FT_THROW( Invalid_Outline );
2285  }
2286
2287
2288  /* declare an extern to access `ft_outline_glyph_class' globally     */
2289  /* allocated  in `ftglyph.c', and use the FT_OUTLINE_GLYPH_CLASS_GET */
2290  /* macro to access it when FT_CONFIG_OPTION_PIC is defined           */
2291#ifndef FT_CONFIG_OPTION_PIC
2292  extern const FT_Glyph_Class  ft_outline_glyph_class;
2293#endif
2294#include "basepic.h"
2295
2296
2297  /* documentation is in ftstroke.h */
2298
2299  FT_EXPORT_DEF( FT_Error )
2300  FT_Glyph_Stroke( FT_Glyph    *pglyph,
2301                   FT_Stroker   stroker,
2302                   FT_Bool      destroy )
2303  {
2304    FT_Error  error = FT_ERR( Invalid_Argument );
2305    FT_Glyph  glyph = NULL;
2306
2307    /* for FT_OUTLINE_GLYPH_CLASS_GET (in PIC mode) */
2308    FT_Library  library = stroker->library;
2309
2310    FT_UNUSED( library );
2311
2312
2313    if ( !pglyph )
2314      goto Exit;
2315
2316    glyph = *pglyph;
2317    if ( !glyph || glyph->clazz != FT_OUTLINE_GLYPH_CLASS_GET )
2318      goto Exit;
2319
2320    {
2321      FT_Glyph  copy;
2322
2323
2324      error = FT_Glyph_Copy( glyph, &copy );
2325      if ( error )
2326        goto Exit;
2327
2328      glyph = copy;
2329    }
2330
2331    {
2332      FT_OutlineGlyph  oglyph  = (FT_OutlineGlyph)glyph;
2333      FT_Outline*      outline = &oglyph->outline;
2334      FT_UInt          num_points, num_contours;
2335
2336
2337      error = FT_Stroker_ParseOutline( stroker, outline, FALSE );
2338      if ( error )
2339        goto Fail;
2340
2341      FT_Stroker_GetCounts( stroker, &num_points, &num_contours );
2342
2343      FT_Outline_Done( glyph->library, outline );
2344
2345      error = FT_Outline_New( glyph->library,
2346                              num_points,
2347                              (FT_Int)num_contours,
2348                              outline );
2349      if ( error )
2350        goto Fail;
2351
2352      outline->n_points   = 0;
2353      outline->n_contours = 0;
2354
2355      FT_Stroker_Export( stroker, outline );
2356    }
2357
2358    if ( destroy )
2359      FT_Done_Glyph( *pglyph );
2360
2361    *pglyph = glyph;
2362    goto Exit;
2363
2364  Fail:
2365    FT_Done_Glyph( glyph );
2366    glyph = NULL;
2367
2368    if ( !destroy )
2369      *pglyph = NULL;
2370
2371  Exit:
2372    return error;
2373  }
2374
2375
2376  /* documentation is in ftstroke.h */
2377
2378  FT_EXPORT_DEF( FT_Error )
2379  FT_Glyph_StrokeBorder( FT_Glyph    *pglyph,
2380                         FT_Stroker   stroker,
2381                         FT_Bool      inside,
2382                         FT_Bool      destroy )
2383  {
2384    FT_Error  error = FT_ERR( Invalid_Argument );
2385    FT_Glyph  glyph = NULL;
2386
2387    /* for FT_OUTLINE_GLYPH_CLASS_GET (in PIC mode) */
2388    FT_Library  library = stroker->library;
2389
2390    FT_UNUSED( library );
2391
2392
2393    if ( !pglyph )
2394      goto Exit;
2395
2396    glyph = *pglyph;
2397    if ( !glyph || glyph->clazz != FT_OUTLINE_GLYPH_CLASS_GET )
2398      goto Exit;
2399
2400    {
2401      FT_Glyph  copy;
2402
2403
2404      error = FT_Glyph_Copy( glyph, &copy );
2405      if ( error )
2406        goto Exit;
2407
2408      glyph = copy;
2409    }
2410
2411    {
2412      FT_OutlineGlyph   oglyph  = (FT_OutlineGlyph)glyph;
2413      FT_StrokerBorder  border;
2414      FT_Outline*       outline = &oglyph->outline;
2415      FT_UInt           num_points, num_contours;
2416
2417
2418      border = FT_Outline_GetOutsideBorder( outline );
2419      if ( inside )
2420      {
2421        if ( border == FT_STROKER_BORDER_LEFT )
2422          border = FT_STROKER_BORDER_RIGHT;
2423        else
2424          border = FT_STROKER_BORDER_LEFT;
2425      }
2426
2427      error = FT_Stroker_ParseOutline( stroker, outline, FALSE );
2428      if ( error )
2429        goto Fail;
2430
2431      FT_Stroker_GetBorderCounts( stroker, border,
2432                                  &num_points, &num_contours );
2433
2434      FT_Outline_Done( glyph->library, outline );
2435
2436      error = FT_Outline_New( glyph->library,
2437                              num_points,
2438                              (FT_Int)num_contours,
2439                              outline );
2440      if ( error )
2441        goto Fail;
2442
2443      outline->n_points   = 0;
2444      outline->n_contours = 0;
2445
2446      FT_Stroker_ExportBorder( stroker, border, outline );
2447    }
2448
2449    if ( destroy )
2450      FT_Done_Glyph( *pglyph );
2451
2452    *pglyph = glyph;
2453    goto Exit;
2454
2455  Fail:
2456    FT_Done_Glyph( glyph );
2457    glyph = NULL;
2458
2459    if ( !destroy )
2460      *pglyph = NULL;
2461
2462  Exit:
2463    return error;
2464  }
2465
2466
2467/* END */
2468