Commit 432efa25b3476a6884426c0a30f6d6a624310e5d

Alexei Podtelezhnikov 2019-09-25T21:50:16

* src/base/ftstroke.c (ft_stroker_outside): Speed up clipped miter. * include/freetype/ftstroke.h: Wordsmith miter docs.

diff --git a/ChangeLog b/ChangeLog
index 16a5568..fe91986 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2019-09-25  Alexei Podtelezhnikov  <apodtele@gmail.com>
+
+	* src/base/ftstroke.c (ft_stroker_outside): Speed up clipped miter.
+	* include/freetype/ftstroke.h: Wordsmith miter docs.
+
 2019-09-25  Werner Lemberg  <wl@gnu.org>
 
 	* src/sfnt/sfwoff2.c (woff2_open_font): Check (sum of) table sizes.
diff --git a/include/freetype/ftstroke.h b/include/freetype/ftstroke.h
index 01a9c18..282f6f6 100644
--- a/include/freetype/ftstroke.h
+++ b/include/freetype/ftstroke.h
@@ -114,22 +114,19 @@ FT_BEGIN_HEADER
    *   FT_STROKER_LINEJOIN_MITER_FIXED ::
    *     Used to render mitered line joins, with fixed bevels if the miter
    *     limit is exceeded.  The outer edges of the strokes for the two
-   *     segments are extended until they meet at an angle.  If the segments
-   *     meet at too sharp an angle (such that the miter would extend from
-   *     the intersection of the segments a distance greater than the product
-   *     of the miter limit value and the border radius), then a bevel join
-   *     (see above) is used instead.  This prevents long spikes being
-   *     created.  `FT_STROKER_LINEJOIN_MITER_FIXED` generates a miter line
-   *     join as used in PostScript and PDF.
+   *     segments are extended until they meet at an angle.  A bevel join
+   *     (see above) is used if the segments meet at too sharp an angle and
+   *     the outer edges meet beyond a distance corresponding to the meter
+   *     limit.  This prevents long spikes being created.
+   *     `FT_STROKER_LINEJOIN_MITER_FIXED` generates a miter line join as
+   *     used in PostScript and PDF.
    *
    *   FT_STROKER_LINEJOIN_MITER_VARIABLE ::
    *   FT_STROKER_LINEJOIN_MITER ::
    *     Used to render mitered line joins, with variable bevels if the miter
-   *     limit is exceeded.  The intersection of the strokes is clipped at a
-   *     line perpendicular to the bisector of the angle between the strokes,
-   *     at the distance from the intersection of the segments equal to the
-   *     product of the miter limit value and the border radius.  This
-   *     prevents long spikes being created. 
+   *     limit is exceeded.  The intersection of the strokes is clipped
+   *     perpendicularly to the bisector, at a distance corresponding to
+   *     the miter limit. This prevents long spikes being created.
    *     `FT_STROKER_LINEJOIN_MITER_VARIABLE` generates a mitered line join
    *     as used in XPS.  `FT_STROKER_LINEJOIN_MITER` is an alias for
    *     `FT_STROKER_LINEJOIN_MITER_VARIABLE`, retained for backward
@@ -296,13 +293,17 @@ FT_BEGIN_HEADER
    *     The line join style.
    *
    *   miter_limit ::
-   *     The miter limit for the `FT_STROKER_LINEJOIN_MITER_FIXED` and
-   *     `FT_STROKER_LINEJOIN_MITER_VARIABLE` line join styles, expressed as
-   *     16.16 fixed-point value.
+   *     The maximum reciprocal sine of half-angle at the miter join,
+   *     expressed as 16.16 fixed point value.
    *
    * @note:
    *   The radius is expressed in the same units as the outline coordinates.
    *
+   *   The miter limit multiplied by the radius gives the maximum size
+   *   of a miter spike, at which it is clipped for
+   *   `FT_STROKER_LINEJOIN_MITER_VARIABLE` or replaced with a bevel join for
+   *   `FT_STROKER_LINEJOIN_MITER_FIXED`.
+   *
    *   This function calls @FT_Stroker_Rewind automatically.
    */
   FT_EXPORT( void )
diff --git a/src/base/ftstroke.c b/src/base/ftstroke.c
index 18c746f..b02658f 100644
--- a/src/base/ftstroke.c
+++ b/src/base/ftstroke.c
@@ -1062,10 +1062,10 @@
     else
     {
       /* this is a mitered (pointed) or beveled (truncated) corner */
-      FT_Fixed  sigma = 0, radius = stroker->radius;
-      FT_Angle  theta = 0, phi = 0;
-      FT_Fixed  thcos = 0;
-      FT_Bool   bevel, fixed_bevel;
+      FT_Fixed   radius = stroker->radius;
+      FT_Vector  sigma;
+      FT_Angle   theta = 0, phi = 0;
+      FT_Bool    bevel, fixed_bevel;
 
 
       rotate = FT_SIDE_TO_ROTATE( side );
@@ -1076,26 +1076,20 @@
       fixed_bevel =
         FT_BOOL( stroker->line_join != FT_STROKER_LINEJOIN_MITER_VARIABLE );
 
+      /* check miter limit first */
       if ( !bevel )
       {
-        theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
+        theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out ) / 2;
 
-        if ( theta == FT_ANGLE_PI )
-        {
-          theta = rotate;
-          phi   = stroker->angle_in;
-        }
-        else
-        {
-          theta /= 2;
-          phi    = stroker->angle_in + theta + rotate;
-        }
+        if ( theta == FT_ANGLE_PI2 )
+          theta = -rotate;
 
-        thcos = FT_Cos( theta );
-        sigma = FT_MulFix( stroker->miter_limit, thcos );
+        phi    = stroker->angle_in + theta + rotate;
+
+        FT_Vector_From_Polar( &sigma, stroker->miter_limit, theta );
 
         /* is miter limit exceeded? */
-        if ( sigma < 0x10000L )
+        if ( sigma.x < 0x10000L )
         {
           /* don't create variable bevels for very small deviations; */
           /* FT_Sin(x) = 0 for x <= 57                               */
@@ -1122,36 +1116,34 @@
           border->movable = FALSE;
           error = ft_stroke_border_lineto( border, &delta, FALSE );
         }
-        else /* variable bevel */
+        else /* variable bevel or clipped miter */
         {
           /* the miter is truncated */
           FT_Vector  middle, delta;
-          FT_Fixed   length;
+          FT_Fixed   coef;
 
 
-          /* compute middle point */
+          /* compute middle point and first angle point */
           FT_Vector_From_Polar( &middle,
                                 FT_MulFix( radius, stroker->miter_limit ),
                                 phi );
-          middle.x += stroker->center.x;
-          middle.y += stroker->center.y;
 
-          /* compute first angle point */
-          length = FT_MulDiv( radius, 0x10000L - sigma,
-                              ft_pos_abs( FT_Sin( theta ) ) );
+          coef    = FT_DivFix(  0x10000L - sigma.x, sigma.y );
+          delta.x = FT_MulFix(  middle.y, coef );
+          delta.y = FT_MulFix( -middle.x, coef );
 
-          FT_Vector_From_Polar( &delta, length, phi + rotate );
-          delta.x += middle.x;
-          delta.y += middle.y;
+          middle.x += stroker->center.x;
+          middle.y += stroker->center.y;
+          delta.x  += middle.x;
+          delta.y  += middle.y;
 
           error = ft_stroke_border_lineto( border, &delta, FALSE );
           if ( error )
             goto Exit;
 
           /* compute second angle point */
-          FT_Vector_From_Polar( &delta, length, phi - rotate );
-          delta.x += middle.x;
-          delta.y += middle.y;
+          delta.x = middle.x - delta.x + middle.x;
+          delta.y = middle.y - delta.y + middle.y;
 
           error = ft_stroke_border_lineto( border, &delta, FALSE );
           if ( error )
@@ -1178,7 +1170,7 @@
         FT_Vector  delta;
 
 
-        length = FT_DivFix( stroker->radius, thcos );
+        length = FT_MulDiv( stroker->radius, stroker->miter_limit, sigma.x );
 
         FT_Vector_From_Polar( &delta, length, phi );
         delta.x += stroker->center.x;