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