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