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