• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***************************************************************************/
2 /*                                                                         */
3 /*  ftstroke.c                                                             */
4 /*                                                                         */
5 /*    FreeType path stroker (body).                                        */
6 /*                                                                         */
7 /*  Copyright 2002, 2003, 2004, 2005, 2006, 2008, 2009 by                  */
8 /*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
9 /*                                                                         */
10 /*  This file is part of the FreeType project, and may only be used,       */
11 /*  modified, and distributed under the terms of the FreeType project      */
12 /*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
13 /*  this file you indicate that you have read the license and              */
14 /*  understand and accept it fully.                                        */
15 /*                                                                         */
16 /***************************************************************************/
17 
18 
19 #include <ft2build.h>
20 #include FT_STROKER_H
21 #include FT_TRIGONOMETRY_H
22 #include FT_OUTLINE_H
23 #include FT_INTERNAL_MEMORY_H
24 #include FT_INTERNAL_DEBUG_H
25 #include FT_INTERNAL_OBJECTS_H
26 
27 
28   /* documentation is in ftstroke.h */
29 
30   FT_EXPORT_DEF( FT_StrokerBorder )
FT_Outline_GetInsideBorder(FT_Outline * outline)31   FT_Outline_GetInsideBorder( FT_Outline*  outline )
32   {
33     FT_Orientation  o = FT_Outline_Get_Orientation( outline );
34 
35 
36     return o == FT_ORIENTATION_TRUETYPE ? FT_STROKER_BORDER_RIGHT
37                                         : FT_STROKER_BORDER_LEFT ;
38   }
39 
40 
41   /* documentation is in ftstroke.h */
42 
43   FT_EXPORT_DEF( FT_StrokerBorder )
FT_Outline_GetOutsideBorder(FT_Outline * outline)44   FT_Outline_GetOutsideBorder( FT_Outline*  outline )
45   {
46     FT_Orientation  o = FT_Outline_Get_Orientation( outline );
47 
48 
49     return o == FT_ORIENTATION_TRUETYPE ? FT_STROKER_BORDER_LEFT
50                                         : FT_STROKER_BORDER_RIGHT ;
51   }
52 
53 
54  /***************************************************************************/
55  /***************************************************************************/
56  /*****                                                                 *****/
57  /*****                       BEZIER COMPUTATIONS                       *****/
58  /*****                                                                 *****/
59  /***************************************************************************/
60  /***************************************************************************/
61 
62 #define FT_SMALL_CONIC_THRESHOLD  ( FT_ANGLE_PI / 6 )
63 #define FT_SMALL_CUBIC_THRESHOLD  ( FT_ANGLE_PI / 6 )
64 #define FT_EPSILON  2
65 
66 #define FT_IS_SMALL( x )  ( (x) > -FT_EPSILON && (x) < FT_EPSILON )
67 
68 
69   static FT_Pos
ft_pos_abs(FT_Pos x)70   ft_pos_abs( FT_Pos  x )
71   {
72     return x >= 0 ? x : -x ;
73   }
74 
75 
76   static void
ft_conic_split(FT_Vector * base)77   ft_conic_split( FT_Vector*  base )
78   {
79     FT_Pos  a, b;
80 
81 
82     base[4].x = base[2].x;
83     b = base[1].x;
84     a = base[3].x = ( base[2].x + b ) / 2;
85     b = base[1].x = ( base[0].x + b ) / 2;
86     base[2].x = ( a + b ) / 2;
87 
88     base[4].y = base[2].y;
89     b = base[1].y;
90     a = base[3].y = ( base[2].y + b ) / 2;
91     b = base[1].y = ( base[0].y + b ) / 2;
92     base[2].y = ( a + b ) / 2;
93   }
94 
95 
96   static FT_Bool
ft_conic_is_small_enough(FT_Vector * base,FT_Angle * angle_in,FT_Angle * angle_out)97   ft_conic_is_small_enough( FT_Vector*  base,
98                             FT_Angle   *angle_in,
99                             FT_Angle   *angle_out )
100   {
101     FT_Vector  d1, d2;
102     FT_Angle   theta;
103     FT_Int     close1, close2;
104 
105 
106     d1.x = base[1].x - base[2].x;
107     d1.y = base[1].y - base[2].y;
108     d2.x = base[0].x - base[1].x;
109     d2.y = base[0].y - base[1].y;
110 
111     close1 = FT_IS_SMALL( d1.x ) && FT_IS_SMALL( d1.y );
112     close2 = FT_IS_SMALL( d2.x ) && FT_IS_SMALL( d2.y );
113 
114     if ( close1 )
115     {
116       if ( close2 )
117         *angle_in = *angle_out = 0;
118       else
119         *angle_in = *angle_out = FT_Atan2( d2.x, d2.y );
120     }
121     else if ( close2 )
122     {
123       *angle_in = *angle_out = FT_Atan2( d1.x, d1.y );
124     }
125     else
126     {
127       *angle_in  = FT_Atan2( d1.x, d1.y );
128       *angle_out = FT_Atan2( d2.x, d2.y );
129     }
130 
131     theta = ft_pos_abs( FT_Angle_Diff( *angle_in, *angle_out ) );
132 
133     return FT_BOOL( theta < FT_SMALL_CONIC_THRESHOLD );
134   }
135 
136 
137   static void
ft_cubic_split(FT_Vector * base)138   ft_cubic_split( FT_Vector*  base )
139   {
140     FT_Pos  a, b, c, d;
141 
142 
143     base[6].x = base[3].x;
144     c = base[1].x;
145     d = base[2].x;
146     base[1].x = a = ( base[0].x + c ) / 2;
147     base[5].x = b = ( base[3].x + d ) / 2;
148     c = ( c + d ) / 2;
149     base[2].x = a = ( a + c ) / 2;
150     base[4].x = b = ( b + c ) / 2;
151     base[3].x = ( a + b ) / 2;
152 
153     base[6].y = base[3].y;
154     c = base[1].y;
155     d = base[2].y;
156     base[1].y = a = ( base[0].y + c ) / 2;
157     base[5].y = b = ( base[3].y + d ) / 2;
158     c = ( c + d ) / 2;
159     base[2].y = a = ( a + c ) / 2;
160     base[4].y = b = ( b + c ) / 2;
161     base[3].y = ( a + b ) / 2;
162   }
163 
164 
165   static FT_Bool
ft_cubic_is_small_enough(FT_Vector * base,FT_Angle * angle_in,FT_Angle * angle_mid,FT_Angle * angle_out)166   ft_cubic_is_small_enough( FT_Vector*  base,
167                             FT_Angle   *angle_in,
168                             FT_Angle   *angle_mid,
169                             FT_Angle   *angle_out )
170   {
171     FT_Vector  d1, d2, d3;
172     FT_Angle   theta1, theta2;
173     FT_Int     close1, close2, close3;
174 
175 
176     d1.x = base[2].x - base[3].x;
177     d1.y = base[2].y - base[3].y;
178     d2.x = base[1].x - base[2].x;
179     d2.y = base[1].y - base[2].y;
180     d3.x = base[0].x - base[1].x;
181     d3.y = base[0].y - base[1].y;
182 
183     close1 = FT_IS_SMALL( d1.x ) && FT_IS_SMALL( d1.y );
184     close2 = FT_IS_SMALL( d2.x ) && FT_IS_SMALL( d2.y );
185     close3 = FT_IS_SMALL( d3.x ) && FT_IS_SMALL( d3.y );
186 
187     if ( close1 || close3 )
188     {
189       if ( close2 )
190       {
191         /* basically a point */
192         *angle_in = *angle_out = *angle_mid = 0;
193       }
194       else if ( close1 )
195       {
196         *angle_in  = *angle_mid = FT_Atan2( d2.x, d2.y );
197         *angle_out = FT_Atan2( d3.x, d3.y );
198       }
199       else  /* close2 */
200       {
201         *angle_in  = FT_Atan2( d1.x, d1.y );
202         *angle_mid = *angle_out = FT_Atan2( d2.x, d2.y );
203       }
204     }
205     else if ( close2 )
206     {
207       *angle_in  = *angle_mid = FT_Atan2( d1.x, d1.y );
208       *angle_out = FT_Atan2( d3.x, d3.y );
209     }
210     else
211     {
212       *angle_in  = FT_Atan2( d1.x, d1.y );
213       *angle_mid = FT_Atan2( d2.x, d2.y );
214       *angle_out = FT_Atan2( d3.x, d3.y );
215     }
216 
217     theta1 = ft_pos_abs( FT_Angle_Diff( *angle_in,  *angle_mid ) );
218     theta2 = ft_pos_abs( FT_Angle_Diff( *angle_mid, *angle_out ) );
219 
220     return FT_BOOL( theta1 < FT_SMALL_CUBIC_THRESHOLD &&
221                     theta2 < FT_SMALL_CUBIC_THRESHOLD );
222   }
223 
224 
225  /***************************************************************************/
226  /***************************************************************************/
227  /*****                                                                 *****/
228  /*****                       STROKE BORDERS                            *****/
229  /*****                                                                 *****/
230  /***************************************************************************/
231  /***************************************************************************/
232 
233   typedef enum  FT_StrokeTags_
234   {
235     FT_STROKE_TAG_ON    = 1,   /* on-curve point  */
236     FT_STROKE_TAG_CUBIC = 2,   /* cubic off-point */
237     FT_STROKE_TAG_BEGIN = 4,   /* sub-path start  */
238     FT_STROKE_TAG_END   = 8    /* sub-path end    */
239 
240   } FT_StrokeTags;
241 
242 #define  FT_STROKE_TAG_BEGIN_END  (FT_STROKE_TAG_BEGIN|FT_STROKE_TAG_END)
243 
244   typedef struct  FT_StrokeBorderRec_
245   {
246     FT_UInt     num_points;
247     FT_UInt     max_points;
248     FT_Vector*  points;
249     FT_Byte*    tags;
250     FT_Bool     movable;
251     FT_Int      start;    /* index of current sub-path start point */
252     FT_Memory   memory;
253     FT_Bool     valid;
254 
255   } FT_StrokeBorderRec, *FT_StrokeBorder;
256 
257 
258   static FT_Error
ft_stroke_border_grow(FT_StrokeBorder border,FT_UInt new_points)259   ft_stroke_border_grow( FT_StrokeBorder  border,
260                          FT_UInt          new_points )
261   {
262     FT_UInt   old_max = border->max_points;
263     FT_UInt   new_max = border->num_points + new_points;
264     FT_Error  error   = FT_Err_Ok;
265 
266 
267     if ( new_max > old_max )
268     {
269       FT_UInt    cur_max = old_max;
270       FT_Memory  memory  = border->memory;
271 
272 
273       while ( cur_max < new_max )
274         cur_max += ( cur_max >> 1 ) + 16;
275 
276       if ( FT_RENEW_ARRAY( border->points, old_max, cur_max ) ||
277            FT_RENEW_ARRAY( border->tags,   old_max, cur_max ) )
278         goto Exit;
279 
280       border->max_points = cur_max;
281     }
282 
283   Exit:
284     return error;
285   }
286 
287 
288   static void
ft_stroke_border_close(FT_StrokeBorder border,FT_Bool reverse)289   ft_stroke_border_close( FT_StrokeBorder  border,
290                           FT_Bool          reverse )
291   {
292     FT_UInt  start = border->start;
293     FT_UInt  count = border->num_points;
294 
295 
296     FT_ASSERT( border->start >= 0 );
297 
298     /* don't record empty paths! */
299     if ( count <= start + 1U )
300       border->num_points = start;
301     else
302     {
303       /* copy the last point to the start of this sub-path, since */
304       /* it contains the `adjusted' starting coordinates          */
305       border->num_points    = --count;
306       border->points[start] = border->points[count];
307 
308       if ( reverse )
309       {
310         /* reverse the points */
311         {
312           FT_Vector*  vec1 = border->points + start + 1;
313           FT_Vector*  vec2 = border->points + count - 1;
314 
315 
316           for ( ; vec1 < vec2; vec1++, vec2-- )
317           {
318             FT_Vector  tmp;
319 
320 
321             tmp   = *vec1;
322             *vec1 = *vec2;
323             *vec2 = tmp;
324           }
325         }
326 
327         /* then the tags */
328         {
329           FT_Byte*  tag1 = border->tags + start + 1;
330           FT_Byte*  tag2 = border->tags + count - 1;
331 
332 
333           for ( ; tag1 < tag2; tag1++, tag2-- )
334           {
335             FT_Byte  tmp;
336 
337 
338             tmp   = *tag1;
339             *tag1 = *tag2;
340             *tag2 = tmp;
341           }
342         }
343       }
344 
345       border->tags[start    ] |= FT_STROKE_TAG_BEGIN;
346       border->tags[count - 1] |= FT_STROKE_TAG_END;
347     }
348 
349     border->start   = -1;
350     border->movable = FALSE;
351   }
352 
353 
354   static FT_Error
ft_stroke_border_lineto(FT_StrokeBorder border,FT_Vector * to,FT_Bool movable)355   ft_stroke_border_lineto( FT_StrokeBorder  border,
356                            FT_Vector*       to,
357                            FT_Bool          movable )
358   {
359     FT_Error  error = FT_Err_Ok;
360 
361 
362     FT_ASSERT( border->start >= 0 );
363 
364     if ( border->movable )
365     {
366       /* move last point */
367       border->points[border->num_points - 1] = *to;
368     }
369     else
370     {
371       /* add one point */
372       error = ft_stroke_border_grow( border, 1 );
373       if ( !error )
374       {
375         FT_Vector*  vec = border->points + border->num_points;
376         FT_Byte*    tag = border->tags   + border->num_points;
377 
378 
379         vec[0] = *to;
380         tag[0] = FT_STROKE_TAG_ON;
381 
382         border->num_points += 1;
383       }
384     }
385     border->movable = movable;
386     return error;
387   }
388 
389 
390   static FT_Error
ft_stroke_border_conicto(FT_StrokeBorder border,FT_Vector * control,FT_Vector * to)391   ft_stroke_border_conicto( FT_StrokeBorder  border,
392                             FT_Vector*       control,
393                             FT_Vector*       to )
394   {
395     FT_Error  error;
396 
397 
398     FT_ASSERT( border->start >= 0 );
399 
400     error = ft_stroke_border_grow( border, 2 );
401     if ( !error )
402     {
403       FT_Vector*  vec = border->points + border->num_points;
404       FT_Byte*    tag = border->tags   + border->num_points;
405 
406       vec[0] = *control;
407       vec[1] = *to;
408 
409       tag[0] = 0;
410       tag[1] = FT_STROKE_TAG_ON;
411 
412       border->num_points += 2;
413     }
414     border->movable = FALSE;
415     return error;
416   }
417 
418 
419   static FT_Error
ft_stroke_border_cubicto(FT_StrokeBorder border,FT_Vector * control1,FT_Vector * control2,FT_Vector * to)420   ft_stroke_border_cubicto( FT_StrokeBorder  border,
421                             FT_Vector*       control1,
422                             FT_Vector*       control2,
423                             FT_Vector*       to )
424   {
425     FT_Error  error;
426 
427 
428     FT_ASSERT( border->start >= 0 );
429 
430     error = ft_stroke_border_grow( border, 3 );
431     if ( !error )
432     {
433       FT_Vector*  vec = border->points + border->num_points;
434       FT_Byte*    tag = border->tags   + border->num_points;
435 
436 
437       vec[0] = *control1;
438       vec[1] = *control2;
439       vec[2] = *to;
440 
441       tag[0] = FT_STROKE_TAG_CUBIC;
442       tag[1] = FT_STROKE_TAG_CUBIC;
443       tag[2] = FT_STROKE_TAG_ON;
444 
445       border->num_points += 3;
446     }
447     border->movable = FALSE;
448     return error;
449   }
450 
451 
452 #define FT_ARC_CUBIC_ANGLE  ( FT_ANGLE_PI / 2 )
453 
454 
455   static FT_Error
ft_stroke_border_arcto(FT_StrokeBorder border,FT_Vector * center,FT_Fixed radius,FT_Angle angle_start,FT_Angle angle_diff)456   ft_stroke_border_arcto( FT_StrokeBorder  border,
457                           FT_Vector*       center,
458                           FT_Fixed         radius,
459                           FT_Angle         angle_start,
460                           FT_Angle         angle_diff )
461   {
462     FT_Angle   total, angle, step, rotate, next, theta;
463     FT_Vector  a, b, a2, b2;
464     FT_Fixed   length;
465     FT_Error   error = FT_Err_Ok;
466 
467 
468     /* compute start point */
469     FT_Vector_From_Polar( &a, radius, angle_start );
470     a.x += center->x;
471     a.y += center->y;
472 
473     total  = angle_diff;
474     angle  = angle_start;
475     rotate = ( angle_diff >= 0 ) ? FT_ANGLE_PI2 : -FT_ANGLE_PI2;
476 
477     while ( total != 0 )
478     {
479       step = total;
480       if ( step > FT_ARC_CUBIC_ANGLE )
481         step = FT_ARC_CUBIC_ANGLE;
482 
483       else if ( step < -FT_ARC_CUBIC_ANGLE )
484         step = -FT_ARC_CUBIC_ANGLE;
485 
486       next  = angle + step;
487       theta = step;
488       if ( theta < 0 )
489         theta = -theta;
490 
491       theta >>= 1;
492 
493       /* compute end point */
494       FT_Vector_From_Polar( &b, radius, next );
495       b.x += center->x;
496       b.y += center->y;
497 
498       /* compute first and second control points */
499       length = FT_MulDiv( radius, FT_Sin( theta ) * 4,
500                           ( 0x10000L + FT_Cos( theta ) ) * 3 );
501 
502       FT_Vector_From_Polar( &a2, length, angle + rotate );
503       a2.x += a.x;
504       a2.y += a.y;
505 
506       FT_Vector_From_Polar( &b2, length, next - rotate );
507       b2.x += b.x;
508       b2.y += b.y;
509 
510       /* add cubic arc */
511       error = ft_stroke_border_cubicto( border, &a2, &b2, &b );
512       if ( error )
513         break;
514 
515       /* process the rest of the arc ?? */
516       a      = b;
517       total -= step;
518       angle  = next;
519     }
520 
521     return error;
522   }
523 
524 
525   static FT_Error
ft_stroke_border_moveto(FT_StrokeBorder border,FT_Vector * to)526   ft_stroke_border_moveto( FT_StrokeBorder  border,
527                            FT_Vector*       to )
528   {
529     /* close current open path if any ? */
530     if ( border->start >= 0 )
531       ft_stroke_border_close( border, FALSE );
532 
533     border->start   = border->num_points;
534     border->movable = FALSE;
535 
536     return ft_stroke_border_lineto( border, to, FALSE );
537   }
538 
539 
540   static void
ft_stroke_border_init(FT_StrokeBorder border,FT_Memory memory)541   ft_stroke_border_init( FT_StrokeBorder  border,
542                          FT_Memory        memory )
543   {
544     border->memory = memory;
545     border->points = NULL;
546     border->tags   = NULL;
547 
548     border->num_points = 0;
549     border->max_points = 0;
550     border->start      = -1;
551     border->valid      = FALSE;
552   }
553 
554 
555   static void
ft_stroke_border_reset(FT_StrokeBorder border)556   ft_stroke_border_reset( FT_StrokeBorder  border )
557   {
558     border->num_points = 0;
559     border->start      = -1;
560     border->valid      = FALSE;
561   }
562 
563 
564   static void
ft_stroke_border_done(FT_StrokeBorder border)565   ft_stroke_border_done( FT_StrokeBorder  border )
566   {
567     FT_Memory  memory = border->memory;
568 
569 
570     FT_FREE( border->points );
571     FT_FREE( border->tags );
572 
573     border->num_points = 0;
574     border->max_points = 0;
575     border->start      = -1;
576     border->valid      = FALSE;
577   }
578 
579 
580   static FT_Error
ft_stroke_border_get_counts(FT_StrokeBorder border,FT_UInt * anum_points,FT_UInt * anum_contours)581   ft_stroke_border_get_counts( FT_StrokeBorder  border,
582                                FT_UInt         *anum_points,
583                                FT_UInt         *anum_contours )
584   {
585     FT_Error  error        = FT_Err_Ok;
586     FT_UInt   num_points   = 0;
587     FT_UInt   num_contours = 0;
588 
589     FT_UInt     count      = border->num_points;
590     FT_Vector*  point      = border->points;
591     FT_Byte*    tags       = border->tags;
592     FT_Int      in_contour = 0;
593 
594 
595     for ( ; count > 0; count--, num_points++, point++, tags++ )
596     {
597       if ( tags[0] & FT_STROKE_TAG_BEGIN )
598       {
599         if ( in_contour != 0 )
600           goto Fail;
601 
602         in_contour = 1;
603       }
604       else if ( in_contour == 0 )
605         goto Fail;
606 
607       if ( tags[0] & FT_STROKE_TAG_END )
608       {
609         in_contour = 0;
610         num_contours++;
611       }
612     }
613 
614     if ( in_contour != 0 )
615       goto Fail;
616 
617     border->valid = TRUE;
618 
619   Exit:
620     *anum_points   = num_points;
621     *anum_contours = num_contours;
622     return error;
623 
624   Fail:
625     num_points   = 0;
626     num_contours = 0;
627     goto Exit;
628   }
629 
630 
631   static void
ft_stroke_border_export(FT_StrokeBorder border,FT_Outline * outline)632   ft_stroke_border_export( FT_StrokeBorder  border,
633                            FT_Outline*      outline )
634   {
635     /* copy point locations */
636     FT_ARRAY_COPY( outline->points + outline->n_points,
637                    border->points,
638                    border->num_points );
639 
640     /* copy tags */
641     {
642       FT_UInt   count = border->num_points;
643       FT_Byte*  read  = border->tags;
644       FT_Byte*  write = (FT_Byte*)outline->tags + outline->n_points;
645 
646 
647       for ( ; count > 0; count--, read++, write++ )
648       {
649         if ( *read & FT_STROKE_TAG_ON )
650           *write = FT_CURVE_TAG_ON;
651         else if ( *read & FT_STROKE_TAG_CUBIC )
652           *write = FT_CURVE_TAG_CUBIC;
653         else
654           *write = FT_CURVE_TAG_CONIC;
655       }
656     }
657 
658     /* copy contours */
659     {
660       FT_UInt    count = border->num_points;
661       FT_Byte*   tags  = border->tags;
662       FT_Short*  write = outline->contours + outline->n_contours;
663       FT_Short   idx   = (FT_Short)outline->n_points;
664 
665 
666       for ( ; count > 0; count--, tags++, idx++ )
667       {
668         if ( *tags & FT_STROKE_TAG_END )
669         {
670           *write++ = idx;
671           outline->n_contours++;
672         }
673       }
674     }
675 
676     outline->n_points  = (short)( outline->n_points + border->num_points );
677 
678     FT_ASSERT( FT_Outline_Check( outline ) == 0 );
679   }
680 
681 
682  /***************************************************************************/
683  /***************************************************************************/
684  /*****                                                                 *****/
685  /*****                           STROKER                               *****/
686  /*****                                                                 *****/
687  /***************************************************************************/
688  /***************************************************************************/
689 
690 #define FT_SIDE_TO_ROTATE( s )   ( FT_ANGLE_PI2 - (s) * FT_ANGLE_PI )
691 
692   typedef struct  FT_StrokerRec_
693   {
694     FT_Angle             angle_in;
695     FT_Angle             angle_out;
696     FT_Vector            center;
697     FT_Bool              first_point;
698     FT_Bool              subpath_open;
699     FT_Angle             subpath_angle;
700     FT_Vector            subpath_start;
701 
702     FT_Stroker_LineCap   line_cap;
703     FT_Stroker_LineJoin  line_join;
704     FT_Fixed             miter_limit;
705     FT_Fixed             radius;
706 
707     FT_Bool              valid;
708     FT_StrokeBorderRec   borders[2];
709     FT_Memory            memory;
710 
711   } FT_StrokerRec;
712 
713 
714   /* documentation is in ftstroke.h */
715 
716   FT_EXPORT_DEF( FT_Error )
FT_Stroker_New(FT_Library library,FT_Stroker * astroker)717   FT_Stroker_New( FT_Library   library,
718                   FT_Stroker  *astroker )
719   {
720     FT_Error    error;
721     FT_Memory   memory;
722     FT_Stroker  stroker;
723 
724 
725     if ( !library )
726       return FT_Err_Invalid_Argument;
727 
728     memory = library->memory;
729 
730     if ( !FT_NEW( stroker ) )
731     {
732       stroker->memory = memory;
733 
734       ft_stroke_border_init( &stroker->borders[0], memory );
735       ft_stroke_border_init( &stroker->borders[1], memory );
736     }
737     *astroker = stroker;
738     return error;
739   }
740 
741 
742   /* documentation is in ftstroke.h */
743 
744   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)745   FT_Stroker_Set( FT_Stroker           stroker,
746                   FT_Fixed             radius,
747                   FT_Stroker_LineCap   line_cap,
748                   FT_Stroker_LineJoin  line_join,
749                   FT_Fixed             miter_limit )
750   {
751     stroker->radius      = radius;
752     stroker->line_cap    = line_cap;
753     stroker->line_join   = line_join;
754     stroker->miter_limit = miter_limit;
755 
756     FT_Stroker_Rewind( stroker );
757   }
758 
759 
760   /* documentation is in ftstroke.h */
761 
762   FT_EXPORT_DEF( void )
FT_Stroker_Rewind(FT_Stroker stroker)763   FT_Stroker_Rewind( FT_Stroker  stroker )
764   {
765     if ( stroker )
766     {
767       ft_stroke_border_reset( &stroker->borders[0] );
768       ft_stroke_border_reset( &stroker->borders[1] );
769     }
770   }
771 
772 
773   /* documentation is in ftstroke.h */
774 
775   FT_EXPORT_DEF( void )
FT_Stroker_Done(FT_Stroker stroker)776   FT_Stroker_Done( FT_Stroker  stroker )
777   {
778     if ( stroker )
779     {
780       FT_Memory  memory = stroker->memory;
781 
782 
783       ft_stroke_border_done( &stroker->borders[0] );
784       ft_stroke_border_done( &stroker->borders[1] );
785 
786       stroker->memory = NULL;
787       FT_FREE( stroker );
788     }
789   }
790 
791 
792   /* creates a circular arc at a corner or cap */
793   static FT_Error
ft_stroker_arcto(FT_Stroker stroker,FT_Int side)794   ft_stroker_arcto( FT_Stroker  stroker,
795                     FT_Int      side )
796   {
797     FT_Angle         total, rotate;
798     FT_Fixed         radius = stroker->radius;
799     FT_Error         error  = FT_Err_Ok;
800     FT_StrokeBorder  border = stroker->borders + side;
801 
802 
803     rotate = FT_SIDE_TO_ROTATE( side );
804 
805     total = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
806     if ( total == FT_ANGLE_PI )
807       total = -rotate * 2;
808 
809     error = ft_stroke_border_arcto( border,
810                                     &stroker->center,
811                                     radius,
812                                     stroker->angle_in + rotate,
813                                     total );
814     border->movable = FALSE;
815     return error;
816   }
817 
818 
819   /* adds a cap at the end of an opened path */
820   static FT_Error
ft_stroker_cap(FT_Stroker stroker,FT_Angle angle,FT_Int side)821   ft_stroker_cap( FT_Stroker  stroker,
822                   FT_Angle    angle,
823                   FT_Int      side )
824   {
825     FT_Error  error = FT_Err_Ok;
826 
827 
828     if ( stroker->line_cap == FT_STROKER_LINECAP_ROUND )
829     {
830       /* add a round cap */
831       stroker->angle_in  = angle;
832       stroker->angle_out = angle + FT_ANGLE_PI;
833       error = ft_stroker_arcto( stroker, side );
834     }
835     else if ( stroker->line_cap == FT_STROKER_LINECAP_SQUARE )
836     {
837       /* add a square cap */
838       FT_Vector        delta, delta2;
839       FT_Angle         rotate = FT_SIDE_TO_ROTATE( side );
840       FT_Fixed         radius = stroker->radius;
841       FT_StrokeBorder  border = stroker->borders + side;
842 
843 
844       FT_Vector_From_Polar( &delta2, radius, angle + rotate );
845       FT_Vector_From_Polar( &delta,  radius, angle );
846 
847       delta.x += stroker->center.x + delta2.x;
848       delta.y += stroker->center.y + delta2.y;
849 
850       error = ft_stroke_border_lineto( border, &delta, FALSE );
851       if ( error )
852         goto Exit;
853 
854       FT_Vector_From_Polar( &delta2, radius, angle - rotate );
855       FT_Vector_From_Polar( &delta,  radius, angle );
856 
857       delta.x += delta2.x + stroker->center.x;
858       delta.y += delta2.y + stroker->center.y;
859 
860       error = ft_stroke_border_lineto( border, &delta, FALSE );
861     }
862 
863   Exit:
864     return error;
865   }
866 
867 
868   /* process an inside corner, i.e. compute intersection */
869   static FT_Error
ft_stroker_inside(FT_Stroker stroker,FT_Int side)870   ft_stroker_inside( FT_Stroker  stroker,
871                      FT_Int      side)
872   {
873     FT_StrokeBorder  border = stroker->borders + side;
874     FT_Angle         phi, theta, rotate;
875     FT_Fixed         length, thcos, sigma;
876     FT_Vector        delta;
877     FT_Error         error = FT_Err_Ok;
878 
879 
880     rotate = FT_SIDE_TO_ROTATE( side );
881 
882     /* compute median angle */
883     theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
884     if ( theta == FT_ANGLE_PI )
885       theta = rotate;
886     else
887       theta = theta / 2;
888 
889     phi = stroker->angle_in + theta;
890 
891     thcos = FT_Cos( theta );
892     sigma = FT_MulFix( stroker->miter_limit, thcos );
893 
894     /* TODO: find better criterion to switch off the optimization */
895     if ( sigma < 0x10000L )
896     {
897       FT_Vector_From_Polar( &delta, stroker->radius,
898                             stroker->angle_out + rotate );
899       delta.x += stroker->center.x;
900       delta.y += stroker->center.y;
901       border->movable = FALSE;
902     }
903     else
904     {
905       length = FT_DivFix( stroker->radius, thcos );
906 
907       FT_Vector_From_Polar( &delta, length, phi + rotate );
908       delta.x += stroker->center.x;
909       delta.y += stroker->center.y;
910     }
911 
912     error = ft_stroke_border_lineto( border, &delta, FALSE );
913 
914     return error;
915   }
916 
917 
918   /* process an outside corner, i.e. compute bevel/miter/round */
919   static FT_Error
ft_stroker_outside(FT_Stroker stroker,FT_Int side)920   ft_stroker_outside( FT_Stroker  stroker,
921                       FT_Int      side )
922   {
923     FT_StrokeBorder  border = stroker->borders + side;
924     FT_Error         error;
925     FT_Angle         rotate;
926 
927 
928     if ( stroker->line_join == FT_STROKER_LINEJOIN_ROUND )
929       error = ft_stroker_arcto( stroker, side );
930     else
931     {
932       /* this is a mitered or beveled corner */
933       FT_Fixed  sigma, radius = stroker->radius;
934       FT_Angle  theta, phi;
935       FT_Fixed  thcos;
936       FT_Bool   miter;
937 
938 
939       rotate = FT_SIDE_TO_ROTATE( side );
940       miter  = FT_BOOL( stroker->line_join == FT_STROKER_LINEJOIN_MITER );
941 
942       theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
943       if ( theta == FT_ANGLE_PI )
944       {
945         theta = rotate;
946         phi   = stroker->angle_in;
947       }
948       else
949       {
950         theta = theta / 2;
951         phi   = stroker->angle_in + theta + rotate;
952       }
953 
954       thcos = FT_Cos( theta );
955       sigma = FT_MulFix( stroker->miter_limit, thcos );
956 
957       if ( sigma >= 0x10000L )
958         miter = FALSE;
959 
960       if ( miter )  /* this is a miter (broken angle) */
961       {
962         FT_Vector  middle, delta;
963         FT_Fixed   length;
964 
965 
966         /* compute middle point */
967         FT_Vector_From_Polar( &middle,
968                               FT_MulFix( radius, stroker->miter_limit ),
969                               phi );
970         middle.x += stroker->center.x;
971         middle.y += stroker->center.y;
972 
973         /* compute first angle point */
974         length = FT_MulFix( radius,
975                             FT_DivFix( 0x10000L - sigma,
976                                        ft_pos_abs( FT_Sin( theta ) ) ) );
977 
978         FT_Vector_From_Polar( &delta, length, phi + rotate );
979         delta.x += middle.x;
980         delta.y += middle.y;
981 
982         error = ft_stroke_border_lineto( border, &delta, FALSE );
983         if ( error )
984           goto Exit;
985 
986         /* compute second angle point */
987         FT_Vector_From_Polar( &delta, length, phi - rotate );
988         delta.x += middle.x;
989         delta.y += middle.y;
990 
991         error = ft_stroke_border_lineto( border, &delta, FALSE );
992         if ( error )
993           goto Exit;
994 
995         /* finally, add a movable end point */
996         FT_Vector_From_Polar( &delta, radius, stroker->angle_out + rotate );
997         delta.x += stroker->center.x;
998         delta.y += stroker->center.y;
999 
1000         error = ft_stroke_border_lineto( border, &delta, TRUE );
1001       }
1002 
1003       else /* this is a bevel (intersection) */
1004       {
1005         FT_Fixed   length;
1006         FT_Vector  delta;
1007 
1008 
1009         length = FT_DivFix( stroker->radius, thcos );
1010 
1011         FT_Vector_From_Polar( &delta, length, phi );
1012         delta.x += stroker->center.x;
1013         delta.y += stroker->center.y;
1014 
1015         error = ft_stroke_border_lineto( border, &delta, FALSE );
1016         if ( error )
1017           goto Exit;
1018 
1019         /* now add end point */
1020         FT_Vector_From_Polar( &delta, stroker->radius,
1021                               stroker->angle_out + rotate );
1022         delta.x += stroker->center.x;
1023         delta.y += stroker->center.y;
1024 
1025         error = ft_stroke_border_lineto( border, &delta, TRUE );
1026       }
1027     }
1028 
1029   Exit:
1030     return error;
1031   }
1032 
1033 
1034   static FT_Error
ft_stroker_process_corner(FT_Stroker stroker)1035   ft_stroker_process_corner( FT_Stroker  stroker )
1036   {
1037     FT_Error  error = FT_Err_Ok;
1038     FT_Angle  turn;
1039     FT_Int    inside_side;
1040 
1041 
1042     turn = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
1043 
1044     /* no specific corner processing is required if the turn is 0 */
1045     if ( turn == 0 )
1046       goto Exit;
1047 
1048     /* when we turn to the right, the inside side is 0 */
1049     inside_side = 0;
1050 
1051     /* otherwise, the inside side is 1 */
1052     if ( turn < 0 )
1053       inside_side = 1;
1054 
1055     /* process the inside side */
1056     error = ft_stroker_inside( stroker, inside_side );
1057     if ( error )
1058       goto Exit;
1059 
1060     /* process the outside side */
1061     error = ft_stroker_outside( stroker, 1 - inside_side );
1062 
1063   Exit:
1064     return error;
1065   }
1066 
1067 
1068   /* add two points to the left and right borders corresponding to the */
1069   /* start of the subpath                                              */
1070   static FT_Error
ft_stroker_subpath_start(FT_Stroker stroker,FT_Angle start_angle)1071   ft_stroker_subpath_start( FT_Stroker  stroker,
1072                             FT_Angle    start_angle )
1073   {
1074     FT_Vector        delta;
1075     FT_Vector        point;
1076     FT_Error         error;
1077     FT_StrokeBorder  border;
1078 
1079 
1080     FT_Vector_From_Polar( &delta, stroker->radius,
1081                           start_angle + FT_ANGLE_PI2 );
1082 
1083     point.x = stroker->center.x + delta.x;
1084     point.y = stroker->center.y + delta.y;
1085 
1086     border = stroker->borders;
1087     error = ft_stroke_border_moveto( border, &point );
1088     if ( error )
1089       goto Exit;
1090 
1091     point.x = stroker->center.x - delta.x;
1092     point.y = stroker->center.y - delta.y;
1093 
1094     border++;
1095     error = ft_stroke_border_moveto( border, &point );
1096 
1097     /* save angle for last cap */
1098     stroker->subpath_angle = start_angle;
1099     stroker->first_point   = FALSE;
1100 
1101   Exit:
1102     return error;
1103   }
1104 
1105 
1106   /* documentation is in ftstroke.h */
1107 
1108   FT_EXPORT_DEF( FT_Error )
FT_Stroker_LineTo(FT_Stroker stroker,FT_Vector * to)1109   FT_Stroker_LineTo( FT_Stroker  stroker,
1110                      FT_Vector*  to )
1111   {
1112     FT_Error         error = FT_Err_Ok;
1113     FT_StrokeBorder  border;
1114     FT_Vector        delta;
1115     FT_Angle         angle;
1116     FT_Int           side;
1117 
1118     delta.x = to->x - stroker->center.x;
1119     delta.y = to->y - stroker->center.y;
1120 
1121     angle = FT_Atan2( delta.x, delta.y );
1122     FT_Vector_From_Polar( &delta, stroker->radius, angle + FT_ANGLE_PI2 );
1123 
1124     /* process corner if necessary */
1125     if ( stroker->first_point )
1126     {
1127       /* This is the first segment of a subpath.  We need to     */
1128       /* add a point to each border at their respective starting */
1129       /* point locations.                                        */
1130       error = ft_stroker_subpath_start( stroker, angle );
1131       if ( error )
1132         goto Exit;
1133     }
1134     else
1135     {
1136       /* process the current corner */
1137       stroker->angle_out = angle;
1138       error = ft_stroker_process_corner( stroker );
1139       if ( error )
1140         goto Exit;
1141     }
1142 
1143     /* now add a line segment to both the `inside' and `outside' paths */
1144 
1145     for ( border = stroker->borders, side = 1; side >= 0; side--, border++ )
1146     {
1147       FT_Vector  point;
1148 
1149 
1150       point.x = to->x + delta.x;
1151       point.y = to->y + delta.y;
1152 
1153       error = ft_stroke_border_lineto( border, &point, TRUE );
1154       if ( error )
1155         goto Exit;
1156 
1157       delta.x = -delta.x;
1158       delta.y = -delta.y;
1159     }
1160 
1161     stroker->angle_in = angle;
1162     stroker->center   = *to;
1163 
1164   Exit:
1165     return error;
1166   }
1167 
1168 
1169   /* documentation is in ftstroke.h */
1170 
1171   FT_EXPORT_DEF( FT_Error )
FT_Stroker_ConicTo(FT_Stroker stroker,FT_Vector * control,FT_Vector * to)1172   FT_Stroker_ConicTo( FT_Stroker  stroker,
1173                       FT_Vector*  control,
1174                       FT_Vector*  to )
1175   {
1176     FT_Error    error = FT_Err_Ok;
1177     FT_Vector   bez_stack[34];
1178     FT_Vector*  arc;
1179     FT_Vector*  limit = bez_stack + 30;
1180     FT_Angle    start_angle;
1181     FT_Bool     first_arc = TRUE;
1182 
1183 
1184     arc    = bez_stack;
1185     arc[0] = *to;
1186     arc[1] = *control;
1187     arc[2] = stroker->center;
1188 
1189     while ( arc >= bez_stack )
1190     {
1191       FT_Angle  angle_in, angle_out;
1192 
1193 
1194       angle_in = angle_out = 0;  /* remove compiler warnings */
1195 
1196       if ( arc < limit                                             &&
1197            !ft_conic_is_small_enough( arc, &angle_in, &angle_out ) )
1198       {
1199         ft_conic_split( arc );
1200         arc += 2;
1201         continue;
1202       }
1203 
1204       if ( first_arc )
1205       {
1206         first_arc = FALSE;
1207 
1208         start_angle = angle_in;
1209 
1210         /* process corner if necessary */
1211         if ( stroker->first_point )
1212           error = ft_stroker_subpath_start( stroker, start_angle );
1213         else
1214         {
1215           stroker->angle_out = start_angle;
1216           error = ft_stroker_process_corner( stroker );
1217         }
1218       }
1219 
1220       /* the arc's angle is small enough; we can add it directly to each */
1221       /* border                                                          */
1222       {
1223         FT_Vector  ctrl, end;
1224         FT_Angle   theta, phi, rotate;
1225         FT_Fixed   length;
1226         FT_Int     side;
1227 
1228 
1229         theta  = FT_Angle_Diff( angle_in, angle_out ) / 2;
1230         phi    = angle_in + theta;
1231         length = FT_DivFix( stroker->radius, FT_Cos( theta ) );
1232 
1233         for ( side = 0; side <= 1; side++ )
1234         {
1235           rotate = FT_SIDE_TO_ROTATE( side );
1236 
1237           /* compute control point */
1238           FT_Vector_From_Polar( &ctrl, length, phi + rotate );
1239           ctrl.x += arc[1].x;
1240           ctrl.y += arc[1].y;
1241 
1242           /* compute end point */
1243           FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate );
1244           end.x += arc[0].x;
1245           end.y += arc[0].y;
1246 
1247           error = ft_stroke_border_conicto( stroker->borders + side,
1248                                             &ctrl, &end );
1249           if ( error )
1250             goto Exit;
1251         }
1252       }
1253 
1254       arc -= 2;
1255 
1256       if ( arc < bez_stack )
1257         stroker->angle_in = angle_out;
1258     }
1259 
1260     stroker->center = *to;
1261 
1262   Exit:
1263     return error;
1264   }
1265 
1266 
1267   /* documentation is in ftstroke.h */
1268 
1269   FT_EXPORT_DEF( FT_Error )
FT_Stroker_CubicTo(FT_Stroker stroker,FT_Vector * control1,FT_Vector * control2,FT_Vector * to)1270   FT_Stroker_CubicTo( FT_Stroker  stroker,
1271                       FT_Vector*  control1,
1272                       FT_Vector*  control2,
1273                       FT_Vector*  to )
1274   {
1275     FT_Error    error = FT_Err_Ok;
1276     FT_Vector   bez_stack[37];
1277     FT_Vector*  arc;
1278     FT_Vector*  limit = bez_stack + 32;
1279     FT_Angle    start_angle;
1280     FT_Bool     first_arc = TRUE;
1281 
1282 
1283     arc    = bez_stack;
1284     arc[0] = *to;
1285     arc[1] = *control2;
1286     arc[2] = *control1;
1287     arc[3] = stroker->center;
1288 
1289     while ( arc >= bez_stack )
1290     {
1291       FT_Angle  angle_in, angle_mid, angle_out;
1292 
1293 
1294       /* remove compiler warnings */
1295       angle_in = angle_out = angle_mid = 0;
1296 
1297       if ( arc < limit                                         &&
1298            !ft_cubic_is_small_enough( arc, &angle_in,
1299                                       &angle_mid, &angle_out ) )
1300       {
1301         ft_cubic_split( arc );
1302         arc += 3;
1303         continue;
1304       }
1305 
1306       if ( first_arc )
1307       {
1308         first_arc = FALSE;
1309 
1310         /* process corner if necessary */
1311         start_angle = angle_in;
1312 
1313         if ( stroker->first_point )
1314           error = ft_stroker_subpath_start( stroker, start_angle );
1315         else
1316         {
1317           stroker->angle_out = start_angle;
1318           error = ft_stroker_process_corner( stroker );
1319         }
1320         if ( error )
1321           goto Exit;
1322       }
1323 
1324       /* the arc's angle is small enough; we can add it directly to each */
1325       /* border                                                          */
1326       {
1327         FT_Vector  ctrl1, ctrl2, end;
1328         FT_Angle   theta1, phi1, theta2, phi2, rotate;
1329         FT_Fixed   length1, length2;
1330         FT_Int     side;
1331 
1332 
1333         theta1  = ft_pos_abs( angle_mid - angle_in ) / 2;
1334         theta2  = ft_pos_abs( angle_out - angle_mid ) / 2;
1335         phi1    = (angle_mid + angle_in ) / 2;
1336         phi2    = (angle_mid + angle_out ) / 2;
1337         length1 = FT_DivFix( stroker->radius, FT_Cos( theta1 ) );
1338         length2 = FT_DivFix( stroker->radius, FT_Cos(theta2) );
1339 
1340         for ( side = 0; side <= 1; side++ )
1341         {
1342           rotate = FT_SIDE_TO_ROTATE( side );
1343 
1344           /* compute control points */
1345           FT_Vector_From_Polar( &ctrl1, length1, phi1 + rotate );
1346           ctrl1.x += arc[2].x;
1347           ctrl1.y += arc[2].y;
1348 
1349           FT_Vector_From_Polar( &ctrl2, length2, phi2 + rotate );
1350           ctrl2.x += arc[1].x;
1351           ctrl2.y += arc[1].y;
1352 
1353           /* compute end point */
1354           FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate );
1355           end.x += arc[0].x;
1356           end.y += arc[0].y;
1357 
1358           error = ft_stroke_border_cubicto( stroker->borders + side,
1359                                             &ctrl1, &ctrl2, &end );
1360           if ( error )
1361             goto Exit;
1362         }
1363       }
1364 
1365       arc -= 3;
1366       if ( arc < bez_stack )
1367         stroker->angle_in = angle_out;
1368     }
1369 
1370     stroker->center = *to;
1371 
1372   Exit:
1373     return error;
1374   }
1375 
1376 
1377   /* documentation is in ftstroke.h */
1378 
1379   FT_EXPORT_DEF( FT_Error )
FT_Stroker_BeginSubPath(FT_Stroker stroker,FT_Vector * to,FT_Bool open)1380   FT_Stroker_BeginSubPath( FT_Stroker  stroker,
1381                            FT_Vector*  to,
1382                            FT_Bool     open )
1383   {
1384     /* We cannot process the first point, because there is not enough      */
1385     /* information regarding its corner/cap.  The latter will be processed */
1386     /* in the `FT_Stroker_EndSubPath' routine.                             */
1387     /*                                                                     */
1388     stroker->first_point  = TRUE;
1389     stroker->center       = *to;
1390     stroker->subpath_open = open;
1391 
1392     /* record the subpath start point for each border */
1393     stroker->subpath_start = *to;
1394 
1395     return FT_Err_Ok;
1396   }
1397 
1398 
1399   static FT_Error
ft_stroker_add_reverse_left(FT_Stroker stroker,FT_Bool open)1400   ft_stroker_add_reverse_left( FT_Stroker  stroker,
1401                                FT_Bool     open )
1402   {
1403     FT_StrokeBorder  right = stroker->borders + 0;
1404     FT_StrokeBorder  left  = stroker->borders + 1;
1405     FT_Int           new_points;
1406     FT_Error         error = FT_Err_Ok;
1407 
1408 
1409     FT_ASSERT( left->start >= 0 );
1410 
1411     new_points = left->num_points - left->start;
1412     if ( new_points > 0 )
1413     {
1414       error = ft_stroke_border_grow( right, (FT_UInt)new_points );
1415       if ( error )
1416         goto Exit;
1417 
1418       {
1419         FT_Vector*  dst_point = right->points + right->num_points;
1420         FT_Byte*    dst_tag   = right->tags   + right->num_points;
1421         FT_Vector*  src_point = left->points  + left->num_points - 1;
1422         FT_Byte*    src_tag   = left->tags    + left->num_points - 1;
1423 
1424         while ( src_point >= left->points + left->start )
1425         {
1426           *dst_point = *src_point;
1427           *dst_tag   = *src_tag;
1428 
1429           if ( open )
1430             dst_tag[0] &= ~FT_STROKE_TAG_BEGIN_END;
1431           else
1432           {
1433             FT_Byte  ttag = (FT_Byte)( dst_tag[0] & FT_STROKE_TAG_BEGIN_END );
1434 
1435 
1436             /* switch begin/end tags if necessary */
1437             if ( ttag == FT_STROKE_TAG_BEGIN ||
1438                  ttag == FT_STROKE_TAG_END   )
1439               dst_tag[0] ^= FT_STROKE_TAG_BEGIN_END;
1440 
1441           }
1442 
1443           src_point--;
1444           src_tag--;
1445           dst_point++;
1446           dst_tag++;
1447         }
1448       }
1449 
1450       left->num_points   = left->start;
1451       right->num_points += new_points;
1452 
1453       right->movable = FALSE;
1454       left->movable  = FALSE;
1455     }
1456 
1457   Exit:
1458     return error;
1459   }
1460 
1461 
1462   /* documentation is in ftstroke.h */
1463 
1464   /* there's a lot of magic in this function! */
1465   FT_EXPORT_DEF( FT_Error )
FT_Stroker_EndSubPath(FT_Stroker stroker)1466   FT_Stroker_EndSubPath( FT_Stroker  stroker )
1467   {
1468     FT_Error  error = FT_Err_Ok;
1469 
1470 
1471     if ( stroker->subpath_open )
1472     {
1473       FT_StrokeBorder  right = stroker->borders;
1474 
1475       /* All right, this is an opened path, we need to add a cap between */
1476       /* right & left, add the reverse of left, then add a final cap     */
1477       /* between left & right.                                           */
1478       error = ft_stroker_cap( stroker, stroker->angle_in, 0 );
1479       if ( error )
1480         goto Exit;
1481 
1482       /* add reversed points from `left' to `right' */
1483       error = ft_stroker_add_reverse_left( stroker, TRUE );
1484       if ( error )
1485         goto Exit;
1486 
1487       /* now add the final cap */
1488       stroker->center = stroker->subpath_start;
1489       error = ft_stroker_cap( stroker,
1490                               stroker->subpath_angle + FT_ANGLE_PI, 0 );
1491       if ( error )
1492         goto Exit;
1493 
1494       /* Now end the right subpath accordingly.  The left one is */
1495       /* rewind and doesn't need further processing.             */
1496       ft_stroke_border_close( right, FALSE );
1497     }
1498     else
1499     {
1500       FT_Angle  turn;
1501       FT_Int    inside_side;
1502 
1503       /* close the path if needed */
1504       if ( stroker->center.x != stroker->subpath_start.x ||
1505            stroker->center.y != stroker->subpath_start.y )
1506       {
1507         error = FT_Stroker_LineTo( stroker, &stroker->subpath_start );
1508         if ( error )
1509           goto Exit;
1510       }
1511 
1512       /* process the corner */
1513       stroker->angle_out = stroker->subpath_angle;
1514       turn               = FT_Angle_Diff( stroker->angle_in,
1515                                           stroker->angle_out );
1516 
1517       /* no specific corner processing is required if the turn is 0 */
1518       if ( turn != 0 )
1519       {
1520         /* when we turn to the right, the inside side is 0 */
1521         inside_side = 0;
1522 
1523         /* otherwise, the inside side is 1 */
1524         if ( turn < 0 )
1525           inside_side = 1;
1526 
1527         error = ft_stroker_inside( stroker, inside_side );
1528         if ( error )
1529           goto Exit;
1530 
1531         /* process the outside side */
1532         error = ft_stroker_outside( stroker, 1 - inside_side );
1533         if ( error )
1534           goto Exit;
1535       }
1536 
1537       /* then end our two subpaths */
1538       ft_stroke_border_close( stroker->borders + 0, TRUE );
1539       ft_stroke_border_close( stroker->borders + 1, FALSE );
1540     }
1541 
1542   Exit:
1543     return error;
1544   }
1545 
1546 
1547   /* documentation is in ftstroke.h */
1548 
1549   FT_EXPORT_DEF( FT_Error )
FT_Stroker_GetBorderCounts(FT_Stroker stroker,FT_StrokerBorder border,FT_UInt * anum_points,FT_UInt * anum_contours)1550   FT_Stroker_GetBorderCounts( FT_Stroker        stroker,
1551                               FT_StrokerBorder  border,
1552                               FT_UInt          *anum_points,
1553                               FT_UInt          *anum_contours )
1554   {
1555     FT_UInt   num_points = 0, num_contours = 0;
1556     FT_Error  error;
1557 
1558 
1559     if ( !stroker || border > 1 )
1560     {
1561       error = FT_Err_Invalid_Argument;
1562       goto Exit;
1563     }
1564 
1565     error = ft_stroke_border_get_counts( stroker->borders + border,
1566                                          &num_points, &num_contours );
1567   Exit:
1568     if ( anum_points )
1569       *anum_points = num_points;
1570 
1571     if ( anum_contours )
1572       *anum_contours = num_contours;
1573 
1574     return error;
1575   }
1576 
1577 
1578   /* documentation is in ftstroke.h */
1579 
1580   FT_EXPORT_DEF( FT_Error )
FT_Stroker_GetCounts(FT_Stroker stroker,FT_UInt * anum_points,FT_UInt * anum_contours)1581   FT_Stroker_GetCounts( FT_Stroker  stroker,
1582                         FT_UInt    *anum_points,
1583                         FT_UInt    *anum_contours )
1584   {
1585     FT_UInt   count1, count2, num_points   = 0;
1586     FT_UInt   count3, count4, num_contours = 0;
1587     FT_Error  error;
1588 
1589 
1590     error = ft_stroke_border_get_counts( stroker->borders + 0,
1591                                          &count1, &count2 );
1592     if ( error )
1593       goto Exit;
1594 
1595     error = ft_stroke_border_get_counts( stroker->borders + 1,
1596                                          &count3, &count4 );
1597     if ( error )
1598       goto Exit;
1599 
1600     num_points   = count1 + count3;
1601     num_contours = count2 + count4;
1602 
1603   Exit:
1604     *anum_points   = num_points;
1605     *anum_contours = num_contours;
1606     return error;
1607   }
1608 
1609 
1610   /* documentation is in ftstroke.h */
1611 
1612   FT_EXPORT_DEF( void )
FT_Stroker_ExportBorder(FT_Stroker stroker,FT_StrokerBorder border,FT_Outline * outline)1613   FT_Stroker_ExportBorder( FT_Stroker        stroker,
1614                            FT_StrokerBorder  border,
1615                            FT_Outline*       outline )
1616   {
1617     if ( border == FT_STROKER_BORDER_LEFT  ||
1618          border == FT_STROKER_BORDER_RIGHT )
1619     {
1620       FT_StrokeBorder  sborder = & stroker->borders[border];
1621 
1622 
1623       if ( sborder->valid )
1624         ft_stroke_border_export( sborder, outline );
1625     }
1626   }
1627 
1628 
1629   /* documentation is in ftstroke.h */
1630 
1631   FT_EXPORT_DEF( void )
FT_Stroker_Export(FT_Stroker stroker,FT_Outline * outline)1632   FT_Stroker_Export( FT_Stroker   stroker,
1633                      FT_Outline*  outline )
1634   {
1635     FT_Stroker_ExportBorder( stroker, FT_STROKER_BORDER_LEFT, outline );
1636     FT_Stroker_ExportBorder( stroker, FT_STROKER_BORDER_RIGHT, outline );
1637   }
1638 
1639 
1640   /* documentation is in ftstroke.h */
1641 
1642   /*
1643    *  The following is very similar to FT_Outline_Decompose, except
1644    *  that we do support opened paths, and do not scale the outline.
1645    */
1646   FT_EXPORT_DEF( FT_Error )
FT_Stroker_ParseOutline(FT_Stroker stroker,FT_Outline * outline,FT_Bool opened)1647   FT_Stroker_ParseOutline( FT_Stroker   stroker,
1648                            FT_Outline*  outline,
1649                            FT_Bool      opened )
1650   {
1651     FT_Vector   v_last;
1652     FT_Vector   v_control;
1653     FT_Vector   v_start;
1654 
1655     FT_Vector*  point;
1656     FT_Vector*  limit;
1657     char*       tags;
1658 
1659     FT_Error    error;
1660 
1661     FT_Int   n;         /* index of contour in outline     */
1662     FT_UInt  first;     /* index of first point in contour */
1663     FT_Int   tag;       /* current point's state           */
1664 
1665 
1666     if ( !outline || !stroker )
1667       return FT_Err_Invalid_Argument;
1668 
1669     FT_Stroker_Rewind( stroker );
1670 
1671     first = 0;
1672 
1673     for ( n = 0; n < outline->n_contours; n++ )
1674     {
1675       FT_UInt  last;  /* index of last point in contour */
1676 
1677 
1678       last  = outline->contours[n];
1679       limit = outline->points + last;
1680 
1681       /* skip empty points; we don't stroke these */
1682       if ( last <= first )
1683       {
1684         first = last + 1;
1685         continue;
1686       }
1687 
1688       v_start = outline->points[first];
1689       v_last  = outline->points[last];
1690 
1691       v_control = v_start;
1692 
1693       point = outline->points + first;
1694       tags  = outline->tags   + first;
1695       tag   = FT_CURVE_TAG( tags[0] );
1696 
1697       /* A contour cannot start with a cubic control point! */
1698       if ( tag == FT_CURVE_TAG_CUBIC )
1699         goto Invalid_Outline;
1700 
1701       /* check first point to determine origin */
1702       if ( tag == FT_CURVE_TAG_CONIC )
1703       {
1704         /* First point is conic control.  Yes, this happens. */
1705         if ( FT_CURVE_TAG( outline->tags[last] ) == FT_CURVE_TAG_ON )
1706         {
1707           /* start at last point if it is on the curve */
1708           v_start = v_last;
1709           limit--;
1710         }
1711         else
1712         {
1713           /* if both first and last points are conic,         */
1714           /* start at their middle and record its position    */
1715           /* for closure                                      */
1716           v_start.x = ( v_start.x + v_last.x ) / 2;
1717           v_start.y = ( v_start.y + v_last.y ) / 2;
1718 
1719           v_last = v_start;
1720         }
1721         point--;
1722         tags--;
1723       }
1724 
1725       error = FT_Stroker_BeginSubPath( stroker, &v_start, opened );
1726       if ( error )
1727         goto Exit;
1728 
1729       while ( point < limit )
1730       {
1731         point++;
1732         tags++;
1733 
1734         tag = FT_CURVE_TAG( tags[0] );
1735         switch ( tag )
1736         {
1737         case FT_CURVE_TAG_ON:  /* emit a single line_to */
1738           {
1739             FT_Vector  vec;
1740 
1741 
1742             vec.x = point->x;
1743             vec.y = point->y;
1744 
1745             error = FT_Stroker_LineTo( stroker, &vec );
1746             if ( error )
1747               goto Exit;
1748             continue;
1749           }
1750 
1751         case FT_CURVE_TAG_CONIC:  /* consume conic arcs */
1752           v_control.x = point->x;
1753           v_control.y = point->y;
1754 
1755         Do_Conic:
1756           if ( point < limit )
1757           {
1758             FT_Vector  vec;
1759             FT_Vector  v_middle;
1760 
1761 
1762             point++;
1763             tags++;
1764             tag = FT_CURVE_TAG( tags[0] );
1765 
1766             vec = point[0];
1767 
1768             if ( tag == FT_CURVE_TAG_ON )
1769             {
1770               error = FT_Stroker_ConicTo( stroker, &v_control, &vec );
1771               if ( error )
1772                 goto Exit;
1773               continue;
1774             }
1775 
1776             if ( tag != FT_CURVE_TAG_CONIC )
1777               goto Invalid_Outline;
1778 
1779             v_middle.x = ( v_control.x + vec.x ) / 2;
1780             v_middle.y = ( v_control.y + vec.y ) / 2;
1781 
1782             error = FT_Stroker_ConicTo( stroker, &v_control, &v_middle );
1783             if ( error )
1784               goto Exit;
1785 
1786             v_control = vec;
1787             goto Do_Conic;
1788           }
1789 
1790           error = FT_Stroker_ConicTo( stroker, &v_control, &v_start );
1791           goto Close;
1792 
1793         default:  /* FT_CURVE_TAG_CUBIC */
1794           {
1795             FT_Vector  vec1, vec2;
1796 
1797 
1798             if ( point + 1 > limit                             ||
1799                  FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC )
1800               goto Invalid_Outline;
1801 
1802             point += 2;
1803             tags  += 2;
1804 
1805             vec1 = point[-2];
1806             vec2 = point[-1];
1807 
1808             if ( point <= limit )
1809             {
1810               FT_Vector  vec;
1811 
1812 
1813               vec = point[0];
1814 
1815               error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &vec );
1816               if ( error )
1817                 goto Exit;
1818               continue;
1819             }
1820 
1821             error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &v_start );
1822             goto Close;
1823           }
1824         }
1825       }
1826 
1827     Close:
1828       if ( error )
1829         goto Exit;
1830 
1831       error = FT_Stroker_EndSubPath( stroker );
1832       if ( error )
1833         goto Exit;
1834 
1835       first = last + 1;
1836     }
1837 
1838     return FT_Err_Ok;
1839 
1840   Exit:
1841     return error;
1842 
1843   Invalid_Outline:
1844     return FT_Err_Invalid_Outline;
1845   }
1846 
1847 
1848   extern const FT_Glyph_Class  ft_outline_glyph_class;
1849 
1850 
1851   /* documentation is in ftstroke.h */
1852 
1853   FT_EXPORT_DEF( FT_Error )
FT_Glyph_Stroke(FT_Glyph * pglyph,FT_Stroker stroker,FT_Bool destroy)1854   FT_Glyph_Stroke( FT_Glyph    *pglyph,
1855                    FT_Stroker   stroker,
1856                    FT_Bool      destroy )
1857   {
1858     FT_Error  error = FT_Err_Invalid_Argument;
1859     FT_Glyph  glyph = NULL;
1860 
1861 
1862     if ( pglyph == NULL )
1863       goto Exit;
1864 
1865     glyph = *pglyph;
1866     if ( glyph == NULL || glyph->clazz != &ft_outline_glyph_class )
1867       goto Exit;
1868 
1869     {
1870       FT_Glyph  copy;
1871 
1872 
1873       error = FT_Glyph_Copy( glyph, &copy );
1874       if ( error )
1875         goto Exit;
1876 
1877       glyph = copy;
1878     }
1879 
1880     {
1881       FT_OutlineGlyph  oglyph  = (FT_OutlineGlyph) glyph;
1882       FT_Outline*      outline = &oglyph->outline;
1883       FT_UInt          num_points, num_contours;
1884 
1885 
1886       error = FT_Stroker_ParseOutline( stroker, outline, FALSE );
1887       if ( error )
1888         goto Fail;
1889 
1890       (void)FT_Stroker_GetCounts( stroker, &num_points, &num_contours );
1891 
1892       FT_Outline_Done( glyph->library, outline );
1893 
1894       error = FT_Outline_New( glyph->library,
1895                               num_points, num_contours, outline );
1896       if ( error )
1897         goto Fail;
1898 
1899       outline->n_points   = 0;
1900       outline->n_contours = 0;
1901 
1902       FT_Stroker_Export( stroker, outline );
1903     }
1904 
1905     if ( destroy )
1906       FT_Done_Glyph( *pglyph );
1907 
1908     *pglyph = glyph;
1909     goto Exit;
1910 
1911   Fail:
1912     FT_Done_Glyph( glyph );
1913     glyph = NULL;
1914 
1915     if ( !destroy )
1916       *pglyph = NULL;
1917 
1918   Exit:
1919     return error;
1920   }
1921 
1922 
1923   /* documentation is in ftstroke.h */
1924 
1925   FT_EXPORT_DEF( FT_Error )
FT_Glyph_StrokeBorder(FT_Glyph * pglyph,FT_Stroker stroker,FT_Bool inside,FT_Bool destroy)1926   FT_Glyph_StrokeBorder( FT_Glyph    *pglyph,
1927                          FT_Stroker   stroker,
1928                          FT_Bool      inside,
1929                          FT_Bool      destroy )
1930   {
1931     FT_Error  error = FT_Err_Invalid_Argument;
1932     FT_Glyph  glyph = NULL;
1933 
1934 
1935     if ( pglyph == NULL )
1936       goto Exit;
1937 
1938     glyph = *pglyph;
1939     if ( glyph == NULL || glyph->clazz != &ft_outline_glyph_class )
1940       goto Exit;
1941 
1942     {
1943       FT_Glyph  copy;
1944 
1945 
1946       error = FT_Glyph_Copy( glyph, &copy );
1947       if ( error )
1948         goto Exit;
1949 
1950       glyph = copy;
1951     }
1952 
1953     {
1954       FT_OutlineGlyph   oglyph  = (FT_OutlineGlyph) glyph;
1955       FT_StrokerBorder  border;
1956       FT_Outline*       outline = &oglyph->outline;
1957       FT_UInt           num_points, num_contours;
1958 
1959 
1960       border = FT_Outline_GetOutsideBorder( outline );
1961       if ( inside )
1962       {
1963         if ( border == FT_STROKER_BORDER_LEFT )
1964           border = FT_STROKER_BORDER_RIGHT;
1965         else
1966           border = FT_STROKER_BORDER_LEFT;
1967       }
1968 
1969       error = FT_Stroker_ParseOutline( stroker, outline, FALSE );
1970       if ( error )
1971         goto Fail;
1972 
1973       (void)FT_Stroker_GetBorderCounts( stroker, border,
1974                                         &num_points, &num_contours );
1975 
1976       FT_Outline_Done( glyph->library, outline );
1977 
1978       error = FT_Outline_New( glyph->library,
1979                               num_points,
1980                               num_contours,
1981                               outline );
1982       if ( error )
1983         goto Fail;
1984 
1985       outline->n_points   = 0;
1986       outline->n_contours = 0;
1987 
1988       FT_Stroker_ExportBorder( stroker, border, outline );
1989     }
1990 
1991     if ( destroy )
1992       FT_Done_Glyph( *pglyph );
1993 
1994     *pglyph = glyph;
1995     goto Exit;
1996 
1997   Fail:
1998     FT_Done_Glyph( glyph );
1999     glyph = NULL;
2000 
2001     if ( !destroy )
2002       *pglyph = NULL;
2003 
2004   Exit:
2005     return error;
2006   }
2007 
2008 
2009 /* END */
2010