Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

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