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