source: trunk/poppler/freetype2/src/base/ftstroke.c @ 262

Last change on this file since 262 was 262, checked in by Eugene Romanenko, 13 years ago

PDF plugin: freetype library updated to version 2.3.8

File size: 52.5 KB
Line 
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   = 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        if ( in_contour == 0 )
610          goto Fail;
611
612        in_contour = 0;
613        num_contours++;
614      }
615    }
616
617    if ( in_contour != 0 )
618      goto Fail;
619
620    border->valid = TRUE;
621
622  Exit:
623    *anum_points   = num_points;
624    *anum_contours = num_contours;
625    return error;
626
627  Fail:
628    num_points   = 0;
629    num_contours = 0;
630    goto Exit;
631  }
632
633
634  static void
635  ft_stroke_border_export( FT_StrokeBorder  border,
636                           FT_Outline*      outline )
637  {
638    /* copy point locations */
639    FT_ARRAY_COPY( outline->points + outline->n_points,
640                   border->points,
641                   border->num_points );
642
643    /* copy tags */
644    {
645      FT_UInt   count = border->num_points;
646      FT_Byte*  read  = border->tags;
647      FT_Byte*  write = (FT_Byte*)outline->tags + outline->n_points;
648
649
650      for ( ; count > 0; count--, read++, write++ )
651      {
652        if ( *read & FT_STROKE_TAG_ON )
653          *write = FT_CURVE_TAG_ON;
654        else if ( *read & FT_STROKE_TAG_CUBIC )
655          *write = FT_CURVE_TAG_CUBIC;
656        else
657          *write = FT_CURVE_TAG_CONIC;
658      }
659    }
660
661    /* copy contours */
662    {
663      FT_UInt    count = border->num_points;
664      FT_Byte*   tags  = border->tags;
665      FT_Short*  write = outline->contours + outline->n_contours;
666      FT_Short   idx   = (FT_Short)outline->n_points;
667
668
669      for ( ; count > 0; count--, tags++, idx++ )
670      {
671        if ( *tags & FT_STROKE_TAG_END )
672        {
673          *write++ = idx;
674          outline->n_contours++;
675        }
676      }
677    }
678
679    outline->n_points  = (short)( outline->n_points + border->num_points );
680
681    FT_ASSERT( FT_Outline_Check( outline ) == 0 );
682  }
683
684
685 /***************************************************************************/
686 /***************************************************************************/
687 /*****                                                                 *****/
688 /*****                           STROKER                               *****/
689 /*****                                                                 *****/
690 /***************************************************************************/
691 /***************************************************************************/
692
693#define FT_SIDE_TO_ROTATE( s )   ( FT_ANGLE_PI2 - (s) * FT_ANGLE_PI )
694
695  typedef struct  FT_StrokerRec_
696  {
697    FT_Angle             angle_in;
698    FT_Angle             angle_out;
699    FT_Vector            center;
700    FT_Bool              first_point;
701    FT_Bool              subpath_open;
702    FT_Angle             subpath_angle;
703    FT_Vector            subpath_start;
704
705    FT_Stroker_LineCap   line_cap;
706    FT_Stroker_LineJoin  line_join;
707    FT_Fixed             miter_limit;
708    FT_Fixed             radius;
709
710    FT_Bool              valid;
711    FT_StrokeBorderRec   borders[2];
712    FT_Memory            memory;
713
714  } FT_StrokerRec;
715
716
717  /* documentation is in ftstroke.h */
718
719  FT_EXPORT_DEF( FT_Error )
720  FT_Stroker_New( FT_Library   library,
721                  FT_Stroker  *astroker )
722  {
723    FT_Error    error;
724    FT_Memory   memory;
725    FT_Stroker  stroker;
726
727
728    if ( !library )
729      return FT_Err_Invalid_Argument;
730
731    memory = library->memory;
732
733    if ( !FT_NEW( stroker ) )
734    {
735      stroker->memory = memory;
736
737      ft_stroke_border_init( &stroker->borders[0], memory );
738      ft_stroke_border_init( &stroker->borders[1], memory );
739    }
740    *astroker = stroker;
741    return error;
742  }
743
744
745  /* documentation is in ftstroke.h */
746
747  FT_EXPORT_DEF( void )
748  FT_Stroker_Set( FT_Stroker           stroker,
749                  FT_Fixed             radius,
750                  FT_Stroker_LineCap   line_cap,
751                  FT_Stroker_LineJoin  line_join,
752                  FT_Fixed             miter_limit )
753  {
754    stroker->radius      = radius;
755    stroker->line_cap    = line_cap;
756    stroker->line_join   = line_join;
757    stroker->miter_limit = miter_limit;
758
759    FT_Stroker_Rewind( stroker );
760  }
761
762
763  /* documentation is in ftstroke.h */
764
765  FT_EXPORT_DEF( void )
766  FT_Stroker_Rewind( FT_Stroker  stroker )
767  {
768    if ( stroker )
769    {
770      ft_stroke_border_reset( &stroker->borders[0] );
771      ft_stroke_border_reset( &stroker->borders[1] );
772    }
773  }
774
775
776  /* documentation is in ftstroke.h */
777
778  FT_EXPORT_DEF( void )
779  FT_Stroker_Done( FT_Stroker  stroker )
780  {
781    if ( stroker )
782    {
783      FT_Memory  memory = stroker->memory;
784
785
786      ft_stroke_border_done( &stroker->borders[0] );
787      ft_stroke_border_done( &stroker->borders[1] );
788
789      stroker->memory = NULL;
790      FT_FREE( stroker );
791    }
792  }
793
794
795  /* creates a circular arc at a corner or cap */
796  static FT_Error
797  ft_stroker_arcto( FT_Stroker  stroker,
798                    FT_Int      side )
799  {
800    FT_Angle         total, rotate;
801    FT_Fixed         radius = stroker->radius;
802    FT_Error         error  = FT_Err_Ok;
803    FT_StrokeBorder  border = stroker->borders + side;
804
805
806    rotate = FT_SIDE_TO_ROTATE( side );
807
808    total = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
809    if ( total == FT_ANGLE_PI )
810      total = -rotate * 2;
811
812    error = ft_stroke_border_arcto( border,
813                                    &stroker->center,
814                                    radius,
815                                    stroker->angle_in + rotate,
816                                    total );
817    border->movable = FALSE;
818    return error;
819  }
820
821
822  /* adds a cap at the end of an opened path */
823  static FT_Error
824  ft_stroker_cap( FT_Stroker  stroker,
825                  FT_Angle    angle,
826                  FT_Int      side )
827  {
828    FT_Error  error = FT_Err_Ok;
829
830
831    if ( stroker->line_cap == FT_STROKER_LINECAP_ROUND )
832    {
833      /* add a round cap */
834      stroker->angle_in  = angle;
835      stroker->angle_out = angle + FT_ANGLE_PI;
836      error = ft_stroker_arcto( stroker, side );
837    }
838    else if ( stroker->line_cap == FT_STROKER_LINECAP_SQUARE )
839    {
840      /* add a square cap */
841      FT_Vector        delta, delta2;
842      FT_Angle         rotate = FT_SIDE_TO_ROTATE( side );
843      FT_Fixed         radius = stroker->radius;
844      FT_StrokeBorder  border = stroker->borders + side;
845
846
847      FT_Vector_From_Polar( &delta2, radius, angle + rotate );
848      FT_Vector_From_Polar( &delta,  radius, angle );
849
850      delta.x += stroker->center.x + delta2.x;
851      delta.y += stroker->center.y + delta2.y;
852
853      error = ft_stroke_border_lineto( border, &delta, FALSE );
854      if ( error )
855        goto Exit;
856
857      FT_Vector_From_Polar( &delta2, radius, angle - rotate );
858      FT_Vector_From_Polar( &delta,  radius, angle );
859
860      delta.x += delta2.x + stroker->center.x;
861      delta.y += delta2.y + stroker->center.y;
862
863      error = ft_stroke_border_lineto( border, &delta, FALSE );
864    }
865
866  Exit:
867    return error;
868  }
869
870
871  /* process an inside corner, i.e. compute intersection */
872  static FT_Error
873  ft_stroker_inside( FT_Stroker  stroker,
874                     FT_Int      side)
875  {
876    FT_StrokeBorder  border = stroker->borders + side;
877    FT_Angle         phi, theta, rotate;
878    FT_Fixed         length, thcos, sigma;
879    FT_Vector        delta;
880    FT_Error         error = FT_Err_Ok;
881
882
883    rotate = FT_SIDE_TO_ROTATE( side );
884
885    /* compute median angle */
886    theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
887    if ( theta == FT_ANGLE_PI )
888      theta = rotate;
889    else
890      theta = theta / 2;
891
892    phi = stroker->angle_in + theta;
893
894    thcos = FT_Cos( theta );
895    sigma = FT_MulFix( stroker->miter_limit, thcos );
896
897    /* TODO: find better criterion to switch off the optimization */
898    if ( sigma < 0x10000L )
899    {
900      FT_Vector_From_Polar( &delta, stroker->radius,
901                            stroker->angle_out + rotate );
902      delta.x += stroker->center.x;
903      delta.y += stroker->center.y;
904      border->movable = FALSE;
905    }
906    else
907    {
908      length = FT_DivFix( stroker->radius, thcos );
909
910      FT_Vector_From_Polar( &delta, length, phi + rotate );
911      delta.x += stroker->center.x;
912      delta.y += stroker->center.y;
913    }
914
915    error = ft_stroke_border_lineto( border, &delta, FALSE );
916
917    return error;
918  }
919
920
921  /* process an outside corner, i.e. compute bevel/miter/round */
922  static FT_Error
923  ft_stroker_outside( FT_Stroker  stroker,
924                      FT_Int      side )
925  {
926    FT_StrokeBorder  border = stroker->borders + side;
927    FT_Error         error;
928    FT_Angle         rotate;
929
930
931    if ( stroker->line_join == FT_STROKER_LINEJOIN_ROUND )
932      error = ft_stroker_arcto( stroker, side );
933    else
934    {
935      /* this is a mitered or beveled corner */
936      FT_Fixed  sigma, radius = stroker->radius;
937      FT_Angle  theta, phi;
938      FT_Fixed  thcos;
939      FT_Bool   miter;
940
941
942      rotate = FT_SIDE_TO_ROTATE( side );
943      miter  = FT_BOOL( stroker->line_join == FT_STROKER_LINEJOIN_MITER );
944
945      theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
946      if ( theta == FT_ANGLE_PI )
947      {
948        theta = rotate;
949        phi   = stroker->angle_in;
950      }
951      else
952      {
953        theta = theta / 2;
954        phi   = stroker->angle_in + theta + rotate;
955      }
956
957      thcos = FT_Cos( theta );
958      sigma = FT_MulFix( stroker->miter_limit, thcos );
959
960      if ( sigma >= 0x10000L )
961        miter = FALSE;
962
963      if ( miter )  /* this is a miter (broken angle) */
964      {
965        FT_Vector  middle, delta;
966        FT_Fixed   length;
967
968
969        /* compute middle point */
970        FT_Vector_From_Polar( &middle,
971                              FT_MulFix( radius, stroker->miter_limit ),
972                              phi );
973        middle.x += stroker->center.x;
974        middle.y += stroker->center.y;
975
976        /* compute first angle point */
977        length = FT_MulFix( radius,
978                            FT_DivFix( 0x10000L - sigma,
979                                       ft_pos_abs( FT_Sin( theta ) ) ) );
980
981        FT_Vector_From_Polar( &delta, length, phi + rotate );
982        delta.x += middle.x;
983        delta.y += middle.y;
984
985        error = ft_stroke_border_lineto( border, &delta, FALSE );
986        if ( error )
987          goto Exit;
988
989        /* compute second angle point */
990        FT_Vector_From_Polar( &delta, length, phi - rotate );
991        delta.x += middle.x;
992        delta.y += middle.y;
993
994        error = ft_stroke_border_lineto( border, &delta, FALSE );
995        if ( error )
996          goto Exit;
997
998        /* finally, add a movable end point */
999        FT_Vector_From_Polar( &delta, radius, stroker->angle_out + rotate );
1000        delta.x += stroker->center.x;
1001        delta.y += stroker->center.y;
1002
1003        error = ft_stroke_border_lineto( border, &delta, TRUE );
1004      }
1005
1006      else /* this is a bevel (intersection) */
1007      {
1008        FT_Fixed   length;
1009        FT_Vector  delta;
1010
1011
1012        length = FT_DivFix( stroker->radius, thcos );
1013
1014        FT_Vector_From_Polar( &delta, length, phi );
1015        delta.x += stroker->center.x;
1016        delta.y += stroker->center.y;
1017
1018        error = ft_stroke_border_lineto( border, &delta, FALSE );
1019        if ( error )
1020          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, TRUE );
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 = FT_Err_Ok;
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   = FALSE;
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 = FT_Err_Ok;
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, TRUE );
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 = FT_Err_Ok;
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 = TRUE;
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 = FALSE;
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 = FT_Err_Ok;
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 = TRUE;
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 = FALSE;
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 `FT_Stroker_EndSubPath' routine.                             */
1390    /*                                                                     */
1391    stroker->first_point  = TRUE;
1392    stroker->center       = *to;
1393    stroker->subpath_open = open;
1394
1395    /* record the subpath start point for each border */
1396    stroker->subpath_start = *to;
1397
1398    return FT_Err_Ok;
1399  }
1400
1401
1402  static FT_Error
1403  ft_stroker_add_reverse_left( FT_Stroker  stroker,
1404                               FT_Bool     open )
1405  {
1406    FT_StrokeBorder  right = stroker->borders + 0;
1407    FT_StrokeBorder  left  = stroker->borders + 1;
1408    FT_Int           new_points;
1409    FT_Error         error = FT_Err_Ok;
1410
1411
1412    FT_ASSERT( left->start >= 0 );
1413
1414    new_points = left->num_points - left->start;
1415    if ( new_points > 0 )
1416    {
1417      error = ft_stroke_border_grow( right, (FT_UInt)new_points );
1418      if ( error )
1419        goto Exit;
1420
1421      {
1422        FT_Vector*  dst_point = right->points + right->num_points;
1423        FT_Byte*    dst_tag   = right->tags   + right->num_points;
1424        FT_Vector*  src_point = left->points  + left->num_points - 1;
1425        FT_Byte*    src_tag   = left->tags    + left->num_points - 1;
1426
1427        while ( src_point >= left->points + left->start )
1428        {
1429          *dst_point = *src_point;
1430          *dst_tag   = *src_tag;
1431
1432          if ( open )
1433            dst_tag[0] &= ~FT_STROKE_TAG_BEGIN_END;
1434          else
1435          {
1436            FT_Byte  ttag = (FT_Byte)( dst_tag[0] & FT_STROKE_TAG_BEGIN_END );
1437
1438
1439            /* switch begin/end tags if necessary */
1440            if ( ttag == FT_STROKE_TAG_BEGIN ||
1441                 ttag == FT_STROKE_TAG_END   )
1442              dst_tag[0] ^= FT_STROKE_TAG_BEGIN_END;
1443
1444          }
1445
1446          src_point--;
1447          src_tag--;
1448          dst_point++;
1449          dst_tag++;
1450        }
1451      }
1452
1453      left->num_points   = left->start;
1454      right->num_points += new_points;
1455
1456      right->movable = FALSE;
1457      left->movable  = FALSE;
1458    }
1459
1460  Exit:
1461    return error;
1462  }
1463
1464
1465  /* documentation is in ftstroke.h */
1466
1467  /* there's a lot of magic in this function! */
1468  FT_EXPORT_DEF( FT_Error )
1469  FT_Stroker_EndSubPath( FT_Stroker  stroker )
1470  {
1471    FT_Error  error = FT_Err_Ok;
1472
1473
1474    if ( stroker->subpath_open )
1475    {
1476      FT_StrokeBorder  right = stroker->borders;
1477
1478      /* All right, this is an opened path, we need to add a cap between */
1479      /* right & left, add the reverse of left, then add a final cap     */
1480      /* between left & right.                                           */
1481      error = ft_stroker_cap( stroker, stroker->angle_in, 0 );
1482      if ( error )
1483        goto Exit;
1484
1485      /* add reversed points from `left' to `right' */
1486      error = ft_stroker_add_reverse_left( stroker, TRUE );
1487      if ( error )
1488        goto Exit;
1489
1490      /* now add the final cap */
1491      stroker->center = stroker->subpath_start;
1492      error = ft_stroker_cap( stroker,
1493                              stroker->subpath_angle + FT_ANGLE_PI, 0 );
1494      if ( error )
1495        goto Exit;
1496
1497      /* Now end the right subpath accordingly.  The left one is */
1498      /* rewind and doesn't need further processing.             */
1499      ft_stroke_border_close( right, FALSE );
1500    }
1501    else
1502    {
1503      FT_Angle  turn;
1504      FT_Int    inside_side;
1505
1506      /* close the path if needed */
1507      if ( stroker->center.x != stroker->subpath_start.x ||
1508           stroker->center.y != stroker->subpath_start.y )
1509      {
1510        error = FT_Stroker_LineTo( stroker, &stroker->subpath_start );
1511        if ( error )
1512          goto Exit;
1513      }
1514
1515      /* process the corner */
1516      stroker->angle_out = stroker->subpath_angle;
1517      turn               = FT_Angle_Diff( stroker->angle_in,
1518                                          stroker->angle_out );
1519
1520      /* no specific corner processing is required if the turn is 0 */
1521      if ( turn != 0 )
1522      {
1523        /* when we turn to the right, the inside side is 0 */
1524        inside_side = 0;
1525
1526        /* otherwise, the inside side is 1 */
1527        if ( turn < 0 )
1528          inside_side = 1;
1529
1530        error = ft_stroker_inside( stroker, inside_side );
1531        if ( error )
1532          goto Exit;
1533
1534        /* process the outside side */
1535        error = ft_stroker_outside( stroker, 1 - inside_side );
1536        if ( error )
1537          goto Exit;
1538      }
1539
1540      /* then end our two subpaths */
1541      ft_stroke_border_close( stroker->borders + 0, TRUE );
1542      ft_stroke_border_close( stroker->borders + 1, FALSE );
1543    }
1544
1545  Exit:
1546    return error;
1547  }
1548
1549
1550  /* documentation is in ftstroke.h */
1551
1552  FT_EXPORT_DEF( FT_Error )
1553  FT_Stroker_GetBorderCounts( FT_Stroker        stroker,
1554                              FT_StrokerBorder  border,
1555                              FT_UInt          *anum_points,
1556                              FT_UInt          *anum_contours )
1557  {
1558    FT_UInt   num_points = 0, num_contours = 0;
1559    FT_Error  error;
1560
1561
1562    if ( !stroker || border > 1 )
1563    {
1564      error = FT_Err_Invalid_Argument;
1565      goto Exit;
1566    }
1567
1568    error = ft_stroke_border_get_counts( stroker->borders + border,
1569                                         &num_points, &num_contours );
1570  Exit:
1571    if ( anum_points )
1572      *anum_points = num_points;
1573
1574    if ( anum_contours )
1575      *anum_contours = num_contours;
1576
1577    return error;
1578  }
1579
1580
1581  /* documentation is in ftstroke.h */
1582
1583  FT_EXPORT_DEF( FT_Error )
1584  FT_Stroker_GetCounts( FT_Stroker  stroker,
1585                        FT_UInt    *anum_points,
1586                        FT_UInt    *anum_contours )
1587  {
1588    FT_UInt   count1, count2, num_points   = 0;
1589    FT_UInt   count3, count4, num_contours = 0;
1590    FT_Error  error;
1591
1592
1593    error = ft_stroke_border_get_counts( stroker->borders + 0,
1594                                         &count1, &count2 );
1595    if ( error )
1596      goto Exit;
1597
1598    error = ft_stroke_border_get_counts( stroker->borders + 1,
1599                                         &count3, &count4 );
1600    if ( error )
1601      goto Exit;
1602
1603    num_points   = count1 + count3;
1604    num_contours = count2 + count4;
1605
1606  Exit:
1607    *anum_points   = num_points;
1608    *anum_contours = num_contours;
1609    return error;
1610  }
1611
1612
1613  /* documentation is in ftstroke.h */
1614
1615  FT_EXPORT_DEF( void )
1616  FT_Stroker_ExportBorder( FT_Stroker        stroker,
1617                           FT_StrokerBorder  border,
1618                           FT_Outline*       outline )
1619  {
1620    if ( border == FT_STROKER_BORDER_LEFT  ||
1621         border == FT_STROKER_BORDER_RIGHT )
1622    {
1623      FT_StrokeBorder  sborder = & stroker->borders[border];
1624
1625
1626      if ( sborder->valid )
1627        ft_stroke_border_export( sborder, outline );
1628    }
1629  }
1630
1631
1632  /* documentation is in ftstroke.h */
1633
1634  FT_EXPORT_DEF( void )
1635  FT_Stroker_Export( FT_Stroker   stroker,
1636                     FT_Outline*  outline )
1637  {
1638    FT_Stroker_ExportBorder( stroker, FT_STROKER_BORDER_LEFT, outline );
1639    FT_Stroker_ExportBorder( stroker, FT_STROKER_BORDER_RIGHT, outline );
1640  }
1641
1642
1643  /* documentation is in ftstroke.h */
1644
1645  /*
1646   *  The following is very similar to FT_Outline_Decompose, except
1647   *  that we do support opened paths, and do not scale the outline.
1648   */
1649  FT_EXPORT_DEF( FT_Error )
1650  FT_Stroker_ParseOutline( FT_Stroker   stroker,
1651                           FT_Outline*  outline,
1652                           FT_Bool      opened )
1653  {
1654    FT_Vector   v_last;
1655    FT_Vector   v_control;
1656    FT_Vector   v_start;
1657
1658    FT_Vector*  point;
1659    FT_Vector*  limit;
1660    char*       tags;
1661
1662    FT_Error    error;
1663
1664    FT_Int   n;         /* index of contour in outline     */
1665    FT_UInt  first;     /* index of first point in contour */
1666    FT_Int   tag;       /* current point's state           */
1667
1668
1669    if ( !outline || !stroker )
1670      return FT_Err_Invalid_Argument;
1671
1672    FT_Stroker_Rewind( stroker );
1673
1674    first = 0;
1675
1676    for ( n = 0; n < outline->n_contours; n++ )
1677    {
1678      FT_UInt  last;  /* index of last point in contour */
1679
1680
1681      last  = outline->contours[n];
1682      limit = outline->points + last;
1683
1684      /* skip empty points; we don't stroke these */
1685      if ( last <= first )
1686      {
1687        first = last + 1;
1688        continue;
1689      }
1690
1691      v_start = outline->points[first];
1692      v_last  = outline->points[last];
1693
1694      v_control = v_start;
1695
1696      point = outline->points + first;
1697      tags  = outline->tags   + first;
1698      tag   = FT_CURVE_TAG( tags[0] );
1699
1700      /* A contour cannot start with a cubic control point! */
1701      if ( tag == FT_CURVE_TAG_CUBIC )
1702        goto Invalid_Outline;
1703
1704      /* check first point to determine origin */
1705      if ( tag == FT_CURVE_TAG_CONIC )
1706      {
1707        /* First point is conic control.  Yes, this happens. */
1708        if ( FT_CURVE_TAG( outline->tags[last] ) == FT_CURVE_TAG_ON )
1709        {
1710          /* start at last point if it is on the curve */
1711          v_start = v_last;
1712          limit--;
1713        }
1714        else
1715        {
1716          /* if both first and last points are conic,         */
1717          /* start at their middle and record its position    */
1718          /* for closure                                      */
1719          v_start.x = ( v_start.x + v_last.x ) / 2;
1720          v_start.y = ( v_start.y + v_last.y ) / 2;
1721
1722          v_last = v_start;
1723        }
1724        point--;
1725        tags--;
1726      }
1727
1728      error = FT_Stroker_BeginSubPath( stroker, &v_start, opened );
1729      if ( error )
1730        goto Exit;
1731
1732      while ( point < limit )
1733      {
1734        point++;
1735        tags++;
1736
1737        tag = FT_CURVE_TAG( tags[0] );
1738        switch ( tag )
1739        {
1740        case FT_CURVE_TAG_ON:  /* emit a single line_to */
1741          {
1742            FT_Vector  vec;
1743
1744
1745            vec.x = point->x;
1746            vec.y = point->y;
1747
1748            error = FT_Stroker_LineTo( stroker, &vec );
1749            if ( error )
1750              goto Exit;
1751            continue;
1752          }
1753
1754        case FT_CURVE_TAG_CONIC:  /* consume conic arcs */
1755          v_control.x = point->x;
1756          v_control.y = point->y;
1757
1758        Do_Conic:
1759          if ( point < limit )
1760          {
1761            FT_Vector  vec;
1762            FT_Vector  v_middle;
1763
1764
1765            point++;
1766            tags++;
1767            tag = FT_CURVE_TAG( tags[0] );
1768
1769            vec = point[0];
1770
1771            if ( tag == FT_CURVE_TAG_ON )
1772            {
1773              error = FT_Stroker_ConicTo( stroker, &v_control, &vec );
1774              if ( error )
1775                goto Exit;
1776              continue;
1777            }
1778
1779            if ( tag != FT_CURVE_TAG_CONIC )
1780              goto Invalid_Outline;
1781
1782            v_middle.x = ( v_control.x + vec.x ) / 2;
1783            v_middle.y = ( v_control.y + vec.y ) / 2;
1784
1785            error = FT_Stroker_ConicTo( stroker, &v_control, &v_middle );
1786            if ( error )
1787              goto Exit;
1788
1789            v_control = vec;
1790            goto Do_Conic;
1791          }
1792
1793          error = FT_Stroker_ConicTo( stroker, &v_control, &v_start );
1794          goto Close;
1795
1796        default:  /* FT_CURVE_TAG_CUBIC */
1797          {
1798            FT_Vector  vec1, vec2;
1799
1800
1801            if ( point + 1 > limit                             ||
1802                 FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC )
1803              goto Invalid_Outline;
1804
1805            point += 2;
1806            tags  += 2;
1807
1808            vec1 = point[-2];
1809            vec2 = point[-1];
1810
1811            if ( point <= limit )
1812            {
1813              FT_Vector  vec;
1814
1815
1816              vec = point[0];
1817
1818              error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &vec );
1819              if ( error )
1820                goto Exit;
1821              continue;
1822            }
1823
1824            error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &v_start );
1825            goto Close;
1826          }
1827        }
1828      }
1829
1830    Close:
1831      if ( error )
1832        goto Exit;
1833
1834      error = FT_Stroker_EndSubPath( stroker );
1835      if ( error )
1836        goto Exit;
1837
1838      first = last + 1;
1839    }
1840
1841    return FT_Err_Ok;
1842
1843  Exit:
1844    return error;
1845
1846  Invalid_Outline:
1847    return FT_Err_Invalid_Outline;
1848  }
1849
1850
1851  extern const FT_Glyph_Class  ft_outline_glyph_class;
1852
1853
1854  /* documentation is in ftstroke.h */
1855
1856  FT_EXPORT_DEF( FT_Error )
1857  FT_Glyph_Stroke( FT_Glyph    *pglyph,
1858                   FT_Stroker   stroker,
1859                   FT_Bool      destroy )
1860  {
1861    FT_Error  error = FT_Err_Invalid_Argument;
1862    FT_Glyph  glyph = NULL;
1863
1864
1865    if ( pglyph == NULL )
1866      goto Exit;
1867
1868    glyph = *pglyph;
1869    if ( glyph == NULL || glyph->clazz != &ft_outline_glyph_class )
1870      goto Exit;
1871
1872    {
1873      FT_Glyph  copy;
1874
1875
1876      error = FT_Glyph_Copy( glyph, &copy );
1877      if ( error )
1878        goto Exit;
1879
1880      glyph = copy;
1881    }
1882
1883    {
1884      FT_OutlineGlyph  oglyph  = (FT_OutlineGlyph) glyph;
1885      FT_Outline*      outline = &oglyph->outline;
1886      FT_UInt          num_points, num_contours;
1887
1888
1889      error = FT_Stroker_ParseOutline( stroker, outline, FALSE );
1890      if ( error )
1891        goto Fail;
1892
1893      (void)FT_Stroker_GetCounts( stroker, &num_points, &num_contours );
1894
1895      FT_Outline_Done( glyph->library, outline );
1896
1897      error = FT_Outline_New( glyph->library,
1898                              num_points, num_contours, outline );
1899      if ( error )
1900        goto Fail;
1901
1902      outline->n_points   = 0;
1903      outline->n_contours = 0;
1904
1905      FT_Stroker_Export( stroker, outline );
1906    }
1907
1908    if ( destroy )
1909      FT_Done_Glyph( *pglyph );
1910
1911    *pglyph = glyph;
1912    goto Exit;
1913
1914  Fail:
1915    FT_Done_Glyph( glyph );
1916    glyph = NULL;
1917
1918    if ( !destroy )
1919      *pglyph = NULL;
1920
1921  Exit:
1922    return error;
1923  }
1924
1925
1926  /* documentation is in ftstroke.h */
1927
1928  FT_EXPORT_DEF( FT_Error )
1929  FT_Glyph_StrokeBorder( FT_Glyph    *pglyph,
1930                         FT_Stroker   stroker,
1931                         FT_Bool      inside,
1932                         FT_Bool      destroy )
1933  {
1934    FT_Error  error = FT_Err_Invalid_Argument;
1935    FT_Glyph  glyph = NULL;
1936
1937
1938    if ( pglyph == NULL )
1939      goto Exit;
1940
1941    glyph = *pglyph;
1942    if ( glyph == NULL || glyph->clazz != &ft_outline_glyph_class )
1943      goto Exit;
1944
1945    {
1946      FT_Glyph  copy;
1947
1948
1949      error = FT_Glyph_Copy( glyph, &copy );
1950      if ( error )
1951        goto Exit;
1952
1953      glyph = copy;
1954    }
1955
1956    {
1957      FT_OutlineGlyph   oglyph  = (FT_OutlineGlyph) glyph;
1958      FT_StrokerBorder  border;
1959      FT_Outline*       outline = &oglyph->outline;
1960      FT_UInt           num_points, num_contours;
1961
1962
1963      border = FT_Outline_GetOutsideBorder( outline );
1964      if ( inside )
1965      {
1966        if ( border == FT_STROKER_BORDER_LEFT )
1967          border = FT_STROKER_BORDER_RIGHT;
1968        else
1969          border = FT_STROKER_BORDER_LEFT;
1970      }
1971
1972      error = FT_Stroker_ParseOutline( stroker, outline, FALSE );
1973      if ( error )
1974        goto Fail;
1975
1976      (void)FT_Stroker_GetBorderCounts( stroker, border,
1977                                        &num_points, &num_contours );
1978
1979      FT_Outline_Done( glyph->library, outline );
1980
1981      error = FT_Outline_New( glyph->library,
1982                              num_points,
1983                              num_contours,
1984                              outline );
1985      if ( error )
1986        goto Fail;
1987
1988      outline->n_points   = 0;
1989      outline->n_contours = 0;
1990
1991      FT_Stroker_ExportBorder( stroker, border, outline );
1992    }
1993
1994    if ( destroy )
1995      FT_Done_Glyph( *pglyph );
1996
1997    *pglyph = glyph;
1998    goto Exit;
1999
2000  Fail:
2001    FT_Done_Glyph( glyph );
2002    glyph = NULL;
2003
2004    if ( !destroy )
2005      *pglyph = NULL;
2006
2007  Exit:
2008    return error;
2009  }
2010
2011
2012/* END */
Note: See TracBrowser for help on using the repository browser.