1/***************************************************************************/
2/*                                                                         */
3/*  ftstroke.c                                                             */
4/*                                                                         */
5/*    FreeType path stroker (body).                                        */
6/*                                                                         */
7/*  Copyright 2002, 2003, 2004, 2005, 2006, 2008, 2009, 2010 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   = 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
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
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
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
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
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
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
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
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
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
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
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_Library           library;
710
711  } FT_StrokerRec;
712
713
714  /* documentation is in ftstroke.h */
715
716  FT_EXPORT_DEF( FT_Error )
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->library = library;
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 )
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 )
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 )
776  FT_Stroker_Done( FT_Stroker  stroker )
777  {
778    if ( stroker )
779    {
780      FT_Memory  memory = stroker->library->memory;
781
782
783      ft_stroke_border_done( &stroker->borders[0] );
784      ft_stroke_border_done( &stroker->borders[1] );
785
786      stroker->library = NULL;
787      FT_FREE( stroker );
788    }
789  }
790
791
792  /* creates a circular arc at a corner or cap */
793  static FT_Error
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
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    else if ( stroker->line_cap == FT_STROKER_LINECAP_BUTT )
863    {
864      /* add a butt ending */
865      FT_Vector        delta;
866      FT_Angle         rotate = FT_SIDE_TO_ROTATE( side );
867      FT_Fixed         radius = stroker->radius;
868      FT_StrokeBorder  border = stroker->borders + side;
869
870
871      FT_Vector_From_Polar( &delta, radius, angle + rotate );
872
873      delta.x += stroker->center.x;
874      delta.y += stroker->center.y;
875
876      error = ft_stroke_border_lineto( border, &delta, FALSE );
877      if ( error )
878        goto Exit;
879
880      FT_Vector_From_Polar( &delta, radius, angle - rotate );
881
882      delta.x += stroker->center.x;
883      delta.y += stroker->center.y;
884
885      error = ft_stroke_border_lineto( border, &delta, FALSE );
886    }
887
888  Exit:
889    return error;
890  }
891
892
893  /* process an inside corner, i.e. compute intersection */
894  static FT_Error
895  ft_stroker_inside( FT_Stroker  stroker,
896                     FT_Int      side)
897  {
898    FT_StrokeBorder  border = stroker->borders + side;
899    FT_Angle         phi, theta, rotate;
900    FT_Fixed         length, thcos, sigma;
901    FT_Vector        delta;
902    FT_Error         error = FT_Err_Ok;
903
904
905    rotate = FT_SIDE_TO_ROTATE( side );
906
907    /* compute median angle */
908    theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
909    if ( theta == FT_ANGLE_PI )
910      theta = rotate;
911    else
912      theta = theta / 2;
913
914    phi = stroker->angle_in + theta;
915
916    thcos = FT_Cos( theta );
917    sigma = FT_MulFix( stroker->miter_limit, thcos );
918
919    /* TODO: find better criterion to switch off the optimization */
920    if ( sigma < 0x10000L )
921    {
922      FT_Vector_From_Polar( &delta, stroker->radius,
923                            stroker->angle_out + rotate );
924      delta.x += stroker->center.x;
925      delta.y += stroker->center.y;
926      border->movable = FALSE;
927    }
928    else
929    {
930      length = FT_DivFix( stroker->radius, thcos );
931
932      FT_Vector_From_Polar( &delta, length, phi + rotate );
933      delta.x += stroker->center.x;
934      delta.y += stroker->center.y;
935    }
936
937    error = ft_stroke_border_lineto( border, &delta, FALSE );
938
939    return error;
940  }
941
942
943  /* process an outside corner, i.e. compute bevel/miter/round */
944  static FT_Error
945  ft_stroker_outside( FT_Stroker  stroker,
946                      FT_Int      side )
947  {
948    FT_StrokeBorder  border = stroker->borders + side;
949    FT_Error         error;
950    FT_Angle         rotate;
951
952
953    if ( stroker->line_join == FT_STROKER_LINEJOIN_ROUND )
954      error = ft_stroker_arcto( stroker, side );
955    else
956    {
957      /* this is a mitered or beveled corner */
958      FT_Fixed  sigma, radius = stroker->radius;
959      FT_Angle  theta, phi;
960      FT_Fixed  thcos;
961      FT_Bool   miter;
962
963
964      rotate = FT_SIDE_TO_ROTATE( side );
965      miter  = FT_BOOL( stroker->line_join == FT_STROKER_LINEJOIN_MITER );
966
967      theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
968      if ( theta == FT_ANGLE_PI )
969      {
970        theta = rotate;
971        phi   = stroker->angle_in;
972      }
973      else
974      {
975        theta = theta / 2;
976        phi   = stroker->angle_in + theta + rotate;
977      }
978
979      thcos = FT_Cos( theta );
980      sigma = FT_MulFix( stroker->miter_limit, thcos );
981
982      /* FT_Sin(x) = 0 for x <= 57 */
983      if ( sigma >= 0x10000L || ft_pos_abs( theta ) <= 57 )
984        miter = FALSE;
985
986      if ( miter )  /* this is a miter (broken angle) */
987      {
988        FT_Vector  middle, delta;
989        FT_Fixed   length;
990
991
992        /* compute middle point */
993        FT_Vector_From_Polar( &middle,
994                              FT_MulFix( radius, stroker->miter_limit ),
995                              phi );
996        middle.x += stroker->center.x;
997        middle.y += stroker->center.y;
998
999        /* compute first angle point */
1000        length = FT_MulFix( radius,
1001                            FT_DivFix( 0x10000L - sigma,
1002                                       ft_pos_abs( FT_Sin( theta ) ) ) );
1003
1004        FT_Vector_From_Polar( &delta, length, phi + rotate );
1005        delta.x += middle.x;
1006        delta.y += middle.y;
1007
1008        error = ft_stroke_border_lineto( border, &delta, FALSE );
1009        if ( error )
1010          goto Exit;
1011
1012        /* compute second angle point */
1013        FT_Vector_From_Polar( &delta, length, phi - rotate );
1014        delta.x += middle.x;
1015        delta.y += middle.y;
1016
1017        error = ft_stroke_border_lineto( border, &delta, FALSE );
1018        if ( error )
1019          goto Exit;
1020
1021        /* finally, add a movable end point */
1022        FT_Vector_From_Polar( &delta, radius, stroker->angle_out + rotate );
1023        delta.x += stroker->center.x;
1024        delta.y += stroker->center.y;
1025
1026        error = ft_stroke_border_lineto( border, &delta, TRUE );
1027      }
1028
1029      else /* this is a bevel (intersection) */
1030      {
1031        FT_Fixed   length;
1032        FT_Vector  delta;
1033
1034
1035        length = FT_DivFix( stroker->radius, thcos );
1036
1037        FT_Vector_From_Polar( &delta, length, phi );
1038        delta.x += stroker->center.x;
1039        delta.y += stroker->center.y;
1040
1041        error = ft_stroke_border_lineto( border, &delta, FALSE );
1042        if ( error )
1043          goto Exit;
1044
1045        /* now add end point */
1046        FT_Vector_From_Polar( &delta, stroker->radius,
1047                              stroker->angle_out + rotate );
1048        delta.x += stroker->center.x;
1049        delta.y += stroker->center.y;
1050
1051        error = ft_stroke_border_lineto( border, &delta, TRUE );
1052      }
1053    }
1054
1055  Exit:
1056    return error;
1057  }
1058
1059
1060  static FT_Error
1061  ft_stroker_process_corner( FT_Stroker  stroker )
1062  {
1063    FT_Error  error = FT_Err_Ok;
1064    FT_Angle  turn;
1065    FT_Int    inside_side;
1066
1067
1068    turn = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
1069
1070    /* no specific corner processing is required if the turn is 0 */
1071    if ( turn == 0 )
1072      goto Exit;
1073
1074    /* when we turn to the right, the inside side is 0 */
1075    inside_side = 0;
1076
1077    /* otherwise, the inside side is 1 */
1078    if ( turn < 0 )
1079      inside_side = 1;
1080
1081    /* process the inside side */
1082    error = ft_stroker_inside( stroker, inside_side );
1083    if ( error )
1084      goto Exit;
1085
1086    /* process the outside side */
1087    error = ft_stroker_outside( stroker, 1 - inside_side );
1088
1089  Exit:
1090    return error;
1091  }
1092
1093
1094  /* add two points to the left and right borders corresponding to the */
1095  /* start of the subpath                                              */
1096  static FT_Error
1097  ft_stroker_subpath_start( FT_Stroker  stroker,
1098                            FT_Angle    start_angle )
1099  {
1100    FT_Vector        delta;
1101    FT_Vector        point;
1102    FT_Error         error;
1103    FT_StrokeBorder  border;
1104
1105
1106    FT_Vector_From_Polar( &delta, stroker->radius,
1107                          start_angle + FT_ANGLE_PI2 );
1108
1109    point.x = stroker->center.x + delta.x;
1110    point.y = stroker->center.y + delta.y;
1111
1112    border = stroker->borders;
1113    error = ft_stroke_border_moveto( border, &point );
1114    if ( error )
1115      goto Exit;
1116
1117    point.x = stroker->center.x - delta.x;
1118    point.y = stroker->center.y - delta.y;
1119
1120    border++;
1121    error = ft_stroke_border_moveto( border, &point );
1122
1123    /* save angle for last cap */
1124    stroker->subpath_angle = start_angle;
1125    stroker->first_point   = FALSE;
1126
1127  Exit:
1128    return error;
1129  }
1130
1131
1132  /* documentation is in ftstroke.h */
1133
1134  FT_EXPORT_DEF( FT_Error )
1135  FT_Stroker_LineTo( FT_Stroker  stroker,
1136                     FT_Vector*  to )
1137  {
1138    FT_Error         error = FT_Err_Ok;
1139    FT_StrokeBorder  border;
1140    FT_Vector        delta;
1141    FT_Angle         angle;
1142    FT_Int           side;
1143
1144    delta.x = to->x - stroker->center.x;
1145    delta.y = to->y - stroker->center.y;
1146
1147    angle = FT_Atan2( delta.x, delta.y );
1148    FT_Vector_From_Polar( &delta, stroker->radius, angle + FT_ANGLE_PI2 );
1149
1150    /* process corner if necessary */
1151    if ( stroker->first_point )
1152    {
1153      /* This is the first segment of a subpath.  We need to     */
1154      /* add a point to each border at their respective starting */
1155      /* point locations.                                        */
1156      error = ft_stroker_subpath_start( stroker, angle );
1157      if ( error )
1158        goto Exit;
1159    }
1160    else
1161    {
1162      /* process the current corner */
1163      stroker->angle_out = angle;
1164      error = ft_stroker_process_corner( stroker );
1165      if ( error )
1166        goto Exit;
1167    }
1168
1169    /* now add a line segment to both the `inside' and `outside' paths */
1170
1171    for ( border = stroker->borders, side = 1; side >= 0; side--, border++ )
1172    {
1173      FT_Vector  point;
1174
1175
1176      point.x = to->x + delta.x;
1177      point.y = to->y + delta.y;
1178
1179      error = ft_stroke_border_lineto( border, &point, TRUE );
1180      if ( error )
1181        goto Exit;
1182
1183      delta.x = -delta.x;
1184      delta.y = -delta.y;
1185    }
1186
1187    stroker->angle_in = angle;
1188    stroker->center   = *to;
1189
1190  Exit:
1191    return error;
1192  }
1193
1194
1195  /* documentation is in ftstroke.h */
1196
1197  FT_EXPORT_DEF( FT_Error )
1198  FT_Stroker_ConicTo( FT_Stroker  stroker,
1199                      FT_Vector*  control,
1200                      FT_Vector*  to )
1201  {
1202    FT_Error    error = FT_Err_Ok;
1203    FT_Vector   bez_stack[34];
1204    FT_Vector*  arc;
1205    FT_Vector*  limit = bez_stack + 30;
1206    FT_Angle    start_angle;
1207    FT_Bool     first_arc = TRUE;
1208
1209
1210    arc    = bez_stack;
1211    arc[0] = *to;
1212    arc[1] = *control;
1213    arc[2] = stroker->center;
1214
1215    while ( arc >= bez_stack )
1216    {
1217      FT_Angle  angle_in, angle_out;
1218
1219
1220      angle_in = angle_out = 0;  /* remove compiler warnings */
1221
1222      if ( arc < limit                                             &&
1223           !ft_conic_is_small_enough( arc, &angle_in, &angle_out ) )
1224      {
1225        ft_conic_split( arc );
1226        arc += 2;
1227        continue;
1228      }
1229
1230      if ( first_arc )
1231      {
1232        first_arc = FALSE;
1233
1234        start_angle = angle_in;
1235
1236        /* process corner if necessary */
1237        if ( stroker->first_point )
1238          error = ft_stroker_subpath_start( stroker, start_angle );
1239        else
1240        {
1241          stroker->angle_out = start_angle;
1242          error = ft_stroker_process_corner( stroker );
1243        }
1244      }
1245
1246      /* the arc's angle is small enough; we can add it directly to each */
1247      /* border                                                          */
1248      {
1249        FT_Vector  ctrl, end;
1250        FT_Angle   theta, phi, rotate;
1251        FT_Fixed   length;
1252        FT_Int     side;
1253
1254
1255        theta  = FT_Angle_Diff( angle_in, angle_out ) / 2;
1256        phi    = angle_in + theta;
1257        length = FT_DivFix( stroker->radius, FT_Cos( theta ) );
1258
1259        for ( side = 0; side <= 1; side++ )
1260        {
1261          rotate = FT_SIDE_TO_ROTATE( side );
1262
1263          /* compute control point */
1264          FT_Vector_From_Polar( &ctrl, length, phi + rotate );
1265          ctrl.x += arc[1].x;
1266          ctrl.y += arc[1].y;
1267
1268          /* compute end point */
1269          FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate );
1270          end.x += arc[0].x;
1271          end.y += arc[0].y;
1272
1273          error = ft_stroke_border_conicto( stroker->borders + side,
1274                                            &ctrl, &end );
1275          if ( error )
1276            goto Exit;
1277        }
1278      }
1279
1280      arc -= 2;
1281
1282      if ( arc < bez_stack )
1283        stroker->angle_in = angle_out;
1284    }
1285
1286    stroker->center = *to;
1287
1288  Exit:
1289    return error;
1290  }
1291
1292
1293  /* documentation is in ftstroke.h */
1294
1295  FT_EXPORT_DEF( FT_Error )
1296  FT_Stroker_CubicTo( FT_Stroker  stroker,
1297                      FT_Vector*  control1,
1298                      FT_Vector*  control2,
1299                      FT_Vector*  to )
1300  {
1301    FT_Error    error = FT_Err_Ok;
1302    FT_Vector   bez_stack[37];
1303    FT_Vector*  arc;
1304    FT_Vector*  limit = bez_stack + 32;
1305    FT_Angle    start_angle;
1306    FT_Bool     first_arc = TRUE;
1307
1308
1309    arc    = bez_stack;
1310    arc[0] = *to;
1311    arc[1] = *control2;
1312    arc[2] = *control1;
1313    arc[3] = stroker->center;
1314
1315    while ( arc >= bez_stack )
1316    {
1317      FT_Angle  angle_in, angle_mid, angle_out;
1318
1319
1320      /* remove compiler warnings */
1321      angle_in = angle_out = angle_mid = 0;
1322
1323      if ( arc < limit                                         &&
1324           !ft_cubic_is_small_enough( arc, &angle_in,
1325                                      &angle_mid, &angle_out ) )
1326      {
1327        ft_cubic_split( arc );
1328        arc += 3;
1329        continue;
1330      }
1331
1332      if ( first_arc )
1333      {
1334        first_arc = FALSE;
1335
1336        /* process corner if necessary */
1337        start_angle = angle_in;
1338
1339        if ( stroker->first_point )
1340          error = ft_stroker_subpath_start( stroker, start_angle );
1341        else
1342        {
1343          stroker->angle_out = start_angle;
1344          error = ft_stroker_process_corner( stroker );
1345        }
1346        if ( error )
1347          goto Exit;
1348      }
1349
1350      /* the arc's angle is small enough; we can add it directly to each */
1351      /* border                                                          */
1352      {
1353        FT_Vector  ctrl1, ctrl2, end;
1354        FT_Angle   theta1, phi1, theta2, phi2, rotate;
1355        FT_Fixed   length1, length2;
1356        FT_Int     side;
1357
1358
1359        theta1  = ft_pos_abs( angle_mid - angle_in ) / 2;
1360        theta2  = ft_pos_abs( angle_out - angle_mid ) / 2;
1361        phi1    = (angle_mid + angle_in ) / 2;
1362        phi2    = (angle_mid + angle_out ) / 2;
1363        length1 = FT_DivFix( stroker->radius, FT_Cos( theta1 ) );
1364        length2 = FT_DivFix( stroker->radius, FT_Cos( theta2 ) );
1365
1366        for ( side = 0; side <= 1; side++ )
1367        {
1368          rotate = FT_SIDE_TO_ROTATE( side );
1369
1370          /* compute control points */
1371          FT_Vector_From_Polar( &ctrl1, length1, phi1 + rotate );
1372          ctrl1.x += arc[2].x;
1373          ctrl1.y += arc[2].y;
1374
1375          FT_Vector_From_Polar( &ctrl2, length2, phi2 + rotate );
1376          ctrl2.x += arc[1].x;
1377          ctrl2.y += arc[1].y;
1378
1379          /* compute end point */
1380          FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate );
1381          end.x += arc[0].x;
1382          end.y += arc[0].y;
1383
1384          error = ft_stroke_border_cubicto( stroker->borders + side,
1385                                            &ctrl1, &ctrl2, &end );
1386          if ( error )
1387            goto Exit;
1388        }
1389      }
1390
1391      arc -= 3;
1392      if ( arc < bez_stack )
1393        stroker->angle_in = angle_out;
1394    }
1395
1396    stroker->center = *to;
1397
1398  Exit:
1399    return error;
1400  }
1401
1402
1403  /* documentation is in ftstroke.h */
1404
1405  FT_EXPORT_DEF( FT_Error )
1406  FT_Stroker_BeginSubPath( FT_Stroker  stroker,
1407                           FT_Vector*  to,
1408                           FT_Bool     open )
1409  {
1410    /* We cannot process the first point, because there is not enough      */
1411    /* information regarding its corner/cap.  The latter will be processed */
1412    /* in the `FT_Stroker_EndSubPath' routine.                             */
1413    /*                                                                     */
1414    stroker->first_point  = TRUE;
1415    stroker->center       = *to;
1416    stroker->subpath_open = open;
1417
1418    /* record the subpath start point for each border */
1419    stroker->subpath_start = *to;
1420
1421    return FT_Err_Ok;
1422  }
1423
1424
1425  static FT_Error
1426  ft_stroker_add_reverse_left( FT_Stroker  stroker,
1427                               FT_Bool     open )
1428  {
1429    FT_StrokeBorder  right = stroker->borders + 0;
1430    FT_StrokeBorder  left  = stroker->borders + 1;
1431    FT_Int           new_points;
1432    FT_Error         error = FT_Err_Ok;
1433
1434
1435    FT_ASSERT( left->start >= 0 );
1436
1437    new_points = left->num_points - left->start;
1438    if ( new_points > 0 )
1439    {
1440      error = ft_stroke_border_grow( right, (FT_UInt)new_points );
1441      if ( error )
1442        goto Exit;
1443
1444      {
1445        FT_Vector*  dst_point = right->points + right->num_points;
1446        FT_Byte*    dst_tag   = right->tags   + right->num_points;
1447        FT_Vector*  src_point = left->points  + left->num_points - 1;
1448        FT_Byte*    src_tag   = left->tags    + left->num_points - 1;
1449
1450        while ( src_point >= left->points + left->start )
1451        {
1452          *dst_point = *src_point;
1453          *dst_tag   = *src_tag;
1454
1455          if ( open )
1456            dst_tag[0] &= ~FT_STROKE_TAG_BEGIN_END;
1457          else
1458          {
1459            FT_Byte  ttag = (FT_Byte)( dst_tag[0] & FT_STROKE_TAG_BEGIN_END );
1460
1461
1462            /* switch begin/end tags if necessary */
1463            if ( ttag == FT_STROKE_TAG_BEGIN ||
1464                 ttag == FT_STROKE_TAG_END   )
1465              dst_tag[0] ^= FT_STROKE_TAG_BEGIN_END;
1466
1467          }
1468
1469          src_point--;
1470          src_tag--;
1471          dst_point++;
1472          dst_tag++;
1473        }
1474      }
1475
1476      left->num_points   = left->start;
1477      right->num_points += new_points;
1478
1479      right->movable = FALSE;
1480      left->movable  = FALSE;
1481    }
1482
1483  Exit:
1484    return error;
1485  }
1486
1487
1488  /* documentation is in ftstroke.h */
1489
1490  /* there's a lot of magic in this function! */
1491  FT_EXPORT_DEF( FT_Error )
1492  FT_Stroker_EndSubPath( FT_Stroker  stroker )
1493  {
1494    FT_Error  error = FT_Err_Ok;
1495
1496
1497    if ( stroker->subpath_open )
1498    {
1499      FT_StrokeBorder  right = stroker->borders;
1500
1501      /* All right, this is an opened path, we need to add a cap between */
1502      /* right & left, add the reverse of left, then add a final cap     */
1503      /* between left & right.                                           */
1504      error = ft_stroker_cap( stroker, stroker->angle_in, 0 );
1505      if ( error )
1506        goto Exit;
1507
1508      /* add reversed points from `left' to `right' */
1509      error = ft_stroker_add_reverse_left( stroker, TRUE );
1510      if ( error )
1511        goto Exit;
1512
1513      /* now add the final cap */
1514      stroker->center = stroker->subpath_start;
1515      error = ft_stroker_cap( stroker,
1516                              stroker->subpath_angle + FT_ANGLE_PI, 0 );
1517      if ( error )
1518        goto Exit;
1519
1520      /* Now end the right subpath accordingly.  The left one is */
1521      /* rewind and doesn't need further processing.             */
1522      ft_stroke_border_close( right, FALSE );
1523    }
1524    else
1525    {
1526      FT_Angle  turn;
1527      FT_Int    inside_side;
1528
1529      /* close the path if needed */
1530      if ( stroker->center.x != stroker->subpath_start.x ||
1531           stroker->center.y != stroker->subpath_start.y )
1532      {
1533        error = FT_Stroker_LineTo( stroker, &stroker->subpath_start );
1534        if ( error )
1535          goto Exit;
1536      }
1537
1538      /* process the corner */
1539      stroker->angle_out = stroker->subpath_angle;
1540      turn               = FT_Angle_Diff( stroker->angle_in,
1541                                          stroker->angle_out );
1542
1543      /* no specific corner processing is required if the turn is 0 */
1544      if ( turn != 0 )
1545      {
1546        /* when we turn to the right, the inside side is 0 */
1547        inside_side = 0;
1548
1549        /* otherwise, the inside side is 1 */
1550        if ( turn < 0 )
1551          inside_side = 1;
1552
1553        error = ft_stroker_inside( stroker, inside_side );
1554        if ( error )
1555          goto Exit;
1556
1557        /* process the outside side */
1558        error = ft_stroker_outside( stroker, 1 - inside_side );
1559        if ( error )
1560          goto Exit;
1561      }
1562
1563      /* then end our two subpaths */
1564      ft_stroke_border_close( stroker->borders + 0, TRUE );
1565      ft_stroke_border_close( stroker->borders + 1, FALSE );
1566    }
1567
1568  Exit:
1569    return error;
1570  }
1571
1572
1573  /* documentation is in ftstroke.h */
1574
1575  FT_EXPORT_DEF( FT_Error )
1576  FT_Stroker_GetBorderCounts( FT_Stroker        stroker,
1577                              FT_StrokerBorder  border,
1578                              FT_UInt          *anum_points,
1579                              FT_UInt          *anum_contours )
1580  {
1581    FT_UInt   num_points = 0, num_contours = 0;
1582    FT_Error  error;
1583
1584
1585    if ( !stroker || border > 1 )
1586    {
1587      error = FT_Err_Invalid_Argument;
1588      goto Exit;
1589    }
1590
1591    error = ft_stroke_border_get_counts( stroker->borders + border,
1592                                         &num_points, &num_contours );
1593  Exit:
1594    if ( anum_points )
1595      *anum_points = num_points;
1596
1597    if ( anum_contours )
1598      *anum_contours = num_contours;
1599
1600    return error;
1601  }
1602
1603
1604  /* documentation is in ftstroke.h */
1605
1606  FT_EXPORT_DEF( FT_Error )
1607  FT_Stroker_GetCounts( FT_Stroker  stroker,
1608                        FT_UInt    *anum_points,
1609                        FT_UInt    *anum_contours )
1610  {
1611    FT_UInt   count1, count2, num_points   = 0;
1612    FT_UInt   count3, count4, num_contours = 0;
1613    FT_Error  error;
1614
1615
1616    error = ft_stroke_border_get_counts( stroker->borders + 0,
1617                                         &count1, &count2 );
1618    if ( error )
1619      goto Exit;
1620
1621    error = ft_stroke_border_get_counts( stroker->borders + 1,
1622                                         &count3, &count4 );
1623    if ( error )
1624      goto Exit;
1625
1626    num_points   = count1 + count3;
1627    num_contours = count2 + count4;
1628
1629  Exit:
1630    *anum_points   = num_points;
1631    *anum_contours = num_contours;
1632    return error;
1633  }
1634
1635
1636  /* documentation is in ftstroke.h */
1637
1638  FT_EXPORT_DEF( void )
1639  FT_Stroker_ExportBorder( FT_Stroker        stroker,
1640                           FT_StrokerBorder  border,
1641                           FT_Outline*       outline )
1642  {
1643    if ( border == FT_STROKER_BORDER_LEFT  ||
1644         border == FT_STROKER_BORDER_RIGHT )
1645    {
1646      FT_StrokeBorder  sborder = & stroker->borders[border];
1647
1648
1649      if ( sborder->valid )
1650        ft_stroke_border_export( sborder, outline );
1651    }
1652  }
1653
1654
1655  /* documentation is in ftstroke.h */
1656
1657  FT_EXPORT_DEF( void )
1658  FT_Stroker_Export( FT_Stroker   stroker,
1659                     FT_Outline*  outline )
1660  {
1661    FT_Stroker_ExportBorder( stroker, FT_STROKER_BORDER_LEFT, outline );
1662    FT_Stroker_ExportBorder( stroker, FT_STROKER_BORDER_RIGHT, outline );
1663  }
1664
1665
1666  /* documentation is in ftstroke.h */
1667
1668  /*
1669   *  The following is very similar to FT_Outline_Decompose, except
1670   *  that we do support opened paths, and do not scale the outline.
1671   */
1672  FT_EXPORT_DEF( FT_Error )
1673  FT_Stroker_ParseOutline( FT_Stroker   stroker,
1674                           FT_Outline*  outline,
1675                           FT_Bool      opened )
1676  {
1677    FT_Vector   v_last;
1678    FT_Vector   v_control;
1679    FT_Vector   v_start;
1680
1681    FT_Vector*  point;
1682    FT_Vector*  limit;
1683    char*       tags;
1684
1685    FT_Error    error;
1686
1687    FT_Int   n;         /* index of contour in outline     */
1688    FT_UInt  first;     /* index of first point in contour */
1689    FT_Int   tag;       /* current point's state           */
1690
1691
1692    if ( !outline || !stroker )
1693      return FT_Err_Invalid_Argument;
1694
1695    FT_Stroker_Rewind( stroker );
1696
1697    first = 0;
1698
1699    for ( n = 0; n < outline->n_contours; n++ )
1700    {
1701      FT_UInt  last;  /* index of last point in contour */
1702
1703
1704      last  = outline->contours[n];
1705      limit = outline->points + last;
1706
1707      /* skip empty points; we don't stroke these */
1708      if ( last <= first )
1709      {
1710        first = last + 1;
1711        continue;
1712      }
1713
1714      v_start = outline->points[first];
1715      v_last  = outline->points[last];
1716
1717      v_control = v_start;
1718
1719      point = outline->points + first;
1720      tags  = outline->tags   + first;
1721      tag   = FT_CURVE_TAG( tags[0] );
1722
1723      /* A contour cannot start with a cubic control point! */
1724      if ( tag == FT_CURVE_TAG_CUBIC )
1725        goto Invalid_Outline;
1726
1727      /* check first point to determine origin */
1728      if ( tag == FT_CURVE_TAG_CONIC )
1729      {
1730        /* First point is conic control.  Yes, this happens. */
1731        if ( FT_CURVE_TAG( outline->tags[last] ) == FT_CURVE_TAG_ON )
1732        {
1733          /* start at last point if it is on the curve */
1734          v_start = v_last;
1735          limit--;
1736        }
1737        else
1738        {
1739          /* if both first and last points are conic, */
1740          /* start at their middle                    */
1741          v_start.x = ( v_start.x + v_last.x ) / 2;
1742          v_start.y = ( v_start.y + v_last.y ) / 2;
1743        }
1744        point--;
1745        tags--;
1746      }
1747
1748      error = FT_Stroker_BeginSubPath( stroker, &v_start, opened );
1749      if ( error )
1750        goto Exit;
1751
1752      while ( point < limit )
1753      {
1754        point++;
1755        tags++;
1756
1757        tag = FT_CURVE_TAG( tags[0] );
1758        switch ( tag )
1759        {
1760        case FT_CURVE_TAG_ON:  /* emit a single line_to */
1761          {
1762            FT_Vector  vec;
1763
1764
1765            vec.x = point->x;
1766            vec.y = point->y;
1767
1768            error = FT_Stroker_LineTo( stroker, &vec );
1769            if ( error )
1770              goto Exit;
1771            continue;
1772          }
1773
1774        case FT_CURVE_TAG_CONIC:  /* consume conic arcs */
1775          v_control.x = point->x;
1776          v_control.y = point->y;
1777
1778        Do_Conic:
1779          if ( point < limit )
1780          {
1781            FT_Vector  vec;
1782            FT_Vector  v_middle;
1783
1784
1785            point++;
1786            tags++;
1787            tag = FT_CURVE_TAG( tags[0] );
1788
1789            vec = point[0];
1790
1791            if ( tag == FT_CURVE_TAG_ON )
1792            {
1793              error = FT_Stroker_ConicTo( stroker, &v_control, &vec );
1794              if ( error )
1795                goto Exit;
1796              continue;
1797            }
1798
1799            if ( tag != FT_CURVE_TAG_CONIC )
1800              goto Invalid_Outline;
1801
1802            v_middle.x = ( v_control.x + vec.x ) / 2;
1803            v_middle.y = ( v_control.y + vec.y ) / 2;
1804
1805            error = FT_Stroker_ConicTo( stroker, &v_control, &v_middle );
1806            if ( error )
1807              goto Exit;
1808
1809            v_control = vec;
1810            goto Do_Conic;
1811          }
1812
1813          error = FT_Stroker_ConicTo( stroker, &v_control, &v_start );
1814          goto Close;
1815
1816        default:  /* FT_CURVE_TAG_CUBIC */
1817          {
1818            FT_Vector  vec1, vec2;
1819
1820
1821            if ( point + 1 > limit                             ||
1822                 FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC )
1823              goto Invalid_Outline;
1824
1825            point += 2;
1826            tags  += 2;
1827
1828            vec1 = point[-2];
1829            vec2 = point[-1];
1830
1831            if ( point <= limit )
1832            {
1833              FT_Vector  vec;
1834
1835
1836              vec = point[0];
1837
1838              error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &vec );
1839              if ( error )
1840                goto Exit;
1841              continue;
1842            }
1843
1844            error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &v_start );
1845            goto Close;
1846          }
1847        }
1848      }
1849
1850    Close:
1851      if ( error )
1852        goto Exit;
1853
1854      error = FT_Stroker_EndSubPath( stroker );
1855      if ( error )
1856        goto Exit;
1857
1858      first = last + 1;
1859    }
1860
1861    return FT_Err_Ok;
1862
1863  Exit:
1864    return error;
1865
1866  Invalid_Outline:
1867    return FT_Err_Invalid_Outline;
1868  }
1869
1870/* declare an extern to access ft_outline_glyph_class global allocated
1871   in ftglyph.c, and use the FT_OUTLINE_GLYPH_CLASS_GET macro to access
1872   it when FT_CONFIG_OPTION_PIC is defined */
1873#ifndef FT_CONFIG_OPTION_PIC
1874  extern const FT_Glyph_Class  ft_outline_glyph_class;
1875#endif
1876#include "basepic.h"
1877
1878
1879  /* documentation is in ftstroke.h */
1880
1881  FT_EXPORT_DEF( FT_Error )
1882  FT_Glyph_Stroke( FT_Glyph    *pglyph,
1883                   FT_Stroker   stroker,
1884                   FT_Bool      destroy )
1885  {
1886    FT_Error  error = FT_Err_Invalid_Argument;
1887    FT_Glyph  glyph = NULL;
1888    FT_Library library = stroker->library;
1889    FT_UNUSED(library);
1890
1891    if ( pglyph == NULL )
1892      goto Exit;
1893
1894    glyph = *pglyph;
1895    if ( glyph == NULL || glyph->clazz != FT_OUTLINE_GLYPH_CLASS_GET )
1896      goto Exit;
1897
1898    {
1899      FT_Glyph  copy;
1900
1901
1902      error = FT_Glyph_Copy( glyph, &copy );
1903      if ( error )
1904        goto Exit;
1905
1906      glyph = copy;
1907    }
1908
1909    {
1910      FT_OutlineGlyph  oglyph  = (FT_OutlineGlyph) glyph;
1911      FT_Outline*      outline = &oglyph->outline;
1912      FT_UInt          num_points, num_contours;
1913
1914
1915      error = FT_Stroker_ParseOutline( stroker, outline, FALSE );
1916      if ( error )
1917        goto Fail;
1918
1919      (void)FT_Stroker_GetCounts( stroker, &num_points, &num_contours );
1920
1921      FT_Outline_Done( glyph->library, outline );
1922
1923      error = FT_Outline_New( glyph->library,
1924                              num_points, num_contours, outline );
1925      if ( error )
1926        goto Fail;
1927
1928      outline->n_points   = 0;
1929      outline->n_contours = 0;
1930
1931      FT_Stroker_Export( stroker, outline );
1932    }
1933
1934    if ( destroy )
1935      FT_Done_Glyph( *pglyph );
1936
1937    *pglyph = glyph;
1938    goto Exit;
1939
1940  Fail:
1941    FT_Done_Glyph( glyph );
1942    glyph = NULL;
1943
1944    if ( !destroy )
1945      *pglyph = NULL;
1946
1947  Exit:
1948    return error;
1949  }
1950
1951
1952  /* documentation is in ftstroke.h */
1953
1954  FT_EXPORT_DEF( FT_Error )
1955  FT_Glyph_StrokeBorder( FT_Glyph    *pglyph,
1956                         FT_Stroker   stroker,
1957                         FT_Bool      inside,
1958                         FT_Bool      destroy )
1959  {
1960    FT_Error  error = FT_Err_Invalid_Argument;
1961    FT_Glyph  glyph = NULL;
1962    FT_Library library = stroker->library;
1963    FT_UNUSED(library);
1964
1965    if ( pglyph == NULL )
1966      goto Exit;
1967
1968    glyph = *pglyph;
1969    if ( glyph == NULL || glyph->clazz != FT_OUTLINE_GLYPH_CLASS_GET )
1970      goto Exit;
1971
1972    {
1973      FT_Glyph  copy;
1974
1975
1976      error = FT_Glyph_Copy( glyph, &copy );
1977      if ( error )
1978        goto Exit;
1979
1980      glyph = copy;
1981    }
1982
1983    {
1984      FT_OutlineGlyph   oglyph  = (FT_OutlineGlyph) glyph;
1985      FT_StrokerBorder  border;
1986      FT_Outline*       outline = &oglyph->outline;
1987      FT_UInt           num_points, num_contours;
1988
1989
1990      border = FT_Outline_GetOutsideBorder( outline );
1991      if ( inside )
1992      {
1993        if ( border == FT_STROKER_BORDER_LEFT )
1994          border = FT_STROKER_BORDER_RIGHT;
1995        else
1996          border = FT_STROKER_BORDER_LEFT;
1997      }
1998
1999      error = FT_Stroker_ParseOutline( stroker, outline, FALSE );
2000      if ( error )
2001        goto Fail;
2002
2003      (void)FT_Stroker_GetBorderCounts( stroker, border,
2004                                        &num_points, &num_contours );
2005
2006      FT_Outline_Done( glyph->library, outline );
2007
2008      error = FT_Outline_New( glyph->library,
2009                              num_points,
2010                              num_contours,
2011                              outline );
2012      if ( error )
2013        goto Fail;
2014
2015      outline->n_points   = 0;
2016      outline->n_contours = 0;
2017
2018      FT_Stroker_ExportBorder( stroker, border, outline );
2019    }
2020
2021    if ( destroy )
2022      FT_Done_Glyph( *pglyph );
2023
2024    *pglyph = glyph;
2025    goto Exit;
2026
2027  Fail:
2028    FT_Done_Glyph( glyph );
2029    glyph = NULL;
2030
2031    if ( !destroy )
2032      *pglyph = NULL;
2033
2034  Exit:
2035    return error;
2036  }
2037
2038
2039/* END */
2040