shithub: freetype+ttf2subf

Download patch

ref: fd5770b359bcfb359401c15b1c1bb40381352008
parent: 9723e7e7ae5aba39c444c8aaa10f0a0710a3a0c2
author: David Turner <[email protected]>
date: Mon Aug 19 02:06:44 EDT 2002

- removal of compiler warnings
- slight improvements to the Postscript hinter

git/fs: mount .git/fs: mount/attach disallowed
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2002-08-20  David Turner   <[email protected]>
+
+	* src/pshinter/pshalgo1.c, src/pshinter/pshalog2.c, 
+	src/pshinter/pshglob.c, src/pshinter/pshrec.c,
+	src/autohint/ahmodule.c: removing compiler warnings with
+	DEBUG_HINTER defined (only used in development builds anyway)
+
+	* src/pshinter/pshalgo3.h, src/pshinter/pshalgo3.c: removing
+	compiler warnings, and improving the support of local extrema
+	and stem edge points
+
+	* test/gview.c: small updates to the hinting debugger
+	* Jamfile: small updates
+
 2002-08-18  Arkadiusz Miskiewicz  <[email protected]>
 
 	* builds/unix/install.mk (install, uninstall): Add $(DESTDIR) to
--- a/src/autohint/ahmodule.c
+++ b/src/autohint/ahmodule.c
@@ -25,9 +25,9 @@
 
 
 #ifdef  DEBUG_HINTER
-   extern AH_Hinter*  ah_debug_hinter       = NULL;
-   extern FT_Bool     ah_debug_disable_horz = 0;
-   extern FT_Bool     ah_debug_disable_vert = 0;
+   AH_Hinter*  ah_debug_hinter       = NULL;
+   FT_Bool     ah_debug_disable_horz = 0;
+   FT_Bool     ah_debug_disable_vert = 0;
 #endif
 
   typedef struct  FT_AutoHinterRec_
--- a/src/pshinter/pshalgo1.c
+++ b/src/pshinter/pshalgo1.c
@@ -25,8 +25,8 @@
 #define FT_COMPONENT  trace_pshalgo1
 
 #ifdef DEBUG_HINTER
-  extern PSH1_Hint_Table  ps1_debug_hint_table = 0;
-  extern PSH1_HintFunc    ps1_debug_hint_func  = 0;
+  PSH1_Hint_Table  ps1_debug_hint_table = 0;
+  PSH1_HintFunc    ps1_debug_hint_func  = 0;
 #endif
 
 
--- a/src/pshinter/pshalgo2.c
+++ b/src/pshinter/pshalgo2.c
@@ -25,9 +25,9 @@
 #define FT_COMPONENT  trace_pshalgo2
 
 #ifdef DEBUG_HINTER
-  extern PSH2_Hint_Table  ps2_debug_hint_table = 0;
-  extern PSH2_HintFunc    ps2_debug_hint_func  = 0;
-  extern PSH2_Glyph       ps2_debug_glyph      = 0;
+  PSH2_Hint_Table  ps2_debug_hint_table = 0;
+  PSH2_HintFunc    ps2_debug_hint_func  = 0;
+  PSH2_Glyph       ps2_debug_glyph      = 0;
 #endif
 
 
--- a/src/pshinter/pshalgo3.c
+++ b/src/pshinter/pshalgo3.c
@@ -27,9 +27,9 @@
 
 
 #ifdef DEBUG_HINTER
-  extern PSH3_Hint_Table  ps3_debug_hint_table = 0;
-  extern PSH3_HintFunc    ps3_debug_hint_func  = 0;
-  extern PSH3_Glyph       ps3_debug_glyph      = 0;
+  PSH3_Hint_Table  ps3_debug_hint_table = 0;
+  PSH3_HintFunc    ps3_debug_hint_func  = 0;
+  PSH3_Glyph       ps3_debug_glyph      = 0;
 #endif
 
 
@@ -36,6 +36,7 @@
 #undef  SNAP_STEMS
 #undef  ONLY_ALIGN_Y
 
+#define  COMPUTE_INFLEXS
 
   /*************************************************************************/
   /*************************************************************************/
@@ -51,7 +52,7 @@
                      PSH3_Hint  hint2 )
   {
     return ( hint1->org_pos + hint1->org_len >= hint2->org_pos &&
-             hint2->org_pos + hint2->org_len >= hint1->org_pos );
+              hint2->org_pos + hint2->org_len >= hint1->org_pos );
   }
 
 
@@ -427,7 +428,7 @@
       hint->cur_len = fit_len;
 
       /* check blue zones for horizontal stems */
-      align.align = PSH_BLUE_ALIGN_NONE;
+      align.align     = PSH_BLUE_ALIGN_NONE;
       align.align_bot = align.align_top = 0;
 
       if ( dimension == 1 )
@@ -469,9 +470,9 @@
             if ( !psh3_hint_is_fitted( parent ) )
               psh3_hint_align( parent, globals, dimension, hint_flags );
 
-            par_org_center = parent->org_pos + ( parent->org_len / 2 );
-            par_cur_center = parent->cur_pos + ( parent->cur_len / 2 );
-            cur_org_center = hint->org_pos   + ( hint->org_len   / 2 );
+            par_org_center = parent->org_pos + ( parent->org_len >> 1 );
+            par_cur_center = parent->cur_pos + ( parent->cur_len >> 1 );
+            cur_org_center = hint->org_pos   + ( hint->org_len   >> 1 );
 
             cur_delta = FT_MulFix( cur_org_center - par_org_center, scale );
             pos       = par_cur_center + cur_delta - ( len >> 1 );
@@ -480,7 +481,7 @@
           if ( ( hint_flags & FT_HINT_NO_INTEGER_STEM ) == 0 )
           {
             /* normal processing */
-            if ( ( fit_len / 64 ) & 1 )
+            if ( fit_len & 64 )
             {
               /* odd number of pixels */
               fit_center = ( ( pos + ( len >> 1 ) ) & -64 ) + 32;
@@ -663,40 +664,108 @@
   /*************************************************************************/
   /*************************************************************************/
 
-  static int
-  psh3_point_is_extremum( PSH3_Point  point )
+#ifdef COMPUTE_INFLEXS
+ /* compute all inflex points in a given glyph */
+  static void
+  psh3_glyph_compute_inflections( PSH3_Glyph  glyph )
   {
-    PSH3_Point  before = point;
-    PSH3_Point  after  = point;
-    FT_Pos      d_before;
-    FT_Pos      d_after;
+    FT_UInt  n;
 
-
-    do
+    for ( n = 0; n < glyph->num_contours; n++ )
     {
-      before = before->prev;
-      if ( before == point )
-        return 0;
+      PSH3_Point  first, start, end, before, after;
+      FT_Angle    angle_in, angle_seg, angle_out;
+      FT_Angle    diff_in, diff_out;
+      FT_Int      finished = 0;
 
-      d_before = before->org_u - point->org_u;
+      /* we need at least 4 points to create an inflection point */
+      if ( glyph->contours[n].count < 4 )
+        continue;
 
-    } while ( d_before == 0 );
+      /* compute first segment in contour */
+      first  = glyph->contours[n].start;
 
-    do
-    {
-      after = after->next;
-      if ( after == point )
-        return 0;
+      start = end = first;
+      do
+      {
+        end = end->next;
+        if ( end == first )
+          goto Skip;
+      }
+      while ( PSH3_POINT_EQUAL_ORG( end, first ) );
 
-      d_after = after->org_u - point->org_u;
+      angle_seg = PSH3_POINT_ANGLE( start, end );
 
-    } while ( d_after == 0 );
+      /* extend the segment start whenever possible */
+      before = start;
+      do
+      {
+        do
+        {
+          start  = before;
+          before = before->prev;
+          if ( before == first )
+            goto Skip;
+        }
+        while ( PSH3_POINT_EQUAL_ORG( before, start ) );
 
-    return ( ( d_before > 0 && d_after > 0 ) ||
-             ( d_before < 0 && d_after < 0 ) );
-  }
+        angle_in = PSH3_POINT_ANGLE( before, start );
+      }
+      while ( angle_in == angle_seg );
 
+      first   = start;
+      diff_in = FT_Angle_Diff( angle_in, angle_seg );
 
+      /* now, process all segments in the contour */
+      do
+      {
+        /* first, extend current segment's end whenever possible */
+        after = end;
+        do
+        {
+          do
+          {
+            end   = after;
+            after = after->next;
+            if ( after == first )
+              finished = 1;
+          }
+          while ( PSH3_POINT_EQUAL_ORG( end, after ) );
+
+          angle_out = PSH3_POINT_ANGLE( end, after );
+        }
+        while ( angle_out == angle_seg );
+
+        diff_out = FT_Angle_Diff( angle_seg, angle_out );
+
+        if ( diff_in ^ diff_out < 0 )
+        {
+          /* diff_in and diff_out have different signs, we have */
+          /* inflection points here...                          */
+
+          do
+          {
+            psh3_point_set_inflex( start );
+            start = start->next;
+          }
+          while ( start != end );
+
+          psh3_point_set_inflex( start );
+        }
+
+        start     = end;
+        end       = after;
+        angle_seg = angle_out;
+        diff_in   = diff_out;
+      }
+      while ( !finished );
+
+    Skip:
+      ;
+    }
+  }
+#endif /* COMPUTE_INFLEXS */
+
   static void
   psh3_glyph_done( PSH3_Glyph  glyph )
   {
@@ -742,6 +811,81 @@
   }
 
 
+  /* load outline point coordinates into hinter glyph */
+  static void
+  psh3_glyph_load_points( PSH3_Glyph  glyph,
+                          FT_Int      dimension )
+  {
+    FT_Vector*  vec   = glyph->outline->points;
+    PSH3_Point  point = glyph->points;
+    FT_UInt     count = glyph->num_points;
+
+
+    for ( ; count > 0; count--, point++, vec++ )
+    {
+      point->flags2 = 0;
+      point->hint   = NULL;
+      if ( dimension == 0 )
+      {
+        point->org_u = vec->x;
+        point->org_v = vec->y;
+      }
+      else
+      {
+        point->org_u = vec->y;
+        point->org_v = vec->x;
+      }
+
+#ifdef DEBUG_HINTER
+      point->org_x = vec->x;
+      point->org_y = vec->y;
+#endif
+
+    }
+  }
+
+
+  /* save hinted point coordinates back to outline */
+  static void
+  psh3_glyph_save_points( PSH3_Glyph  glyph,
+                          FT_Int      dimension )
+  {
+    FT_UInt     n;
+    PSH3_Point  point = glyph->points;
+    FT_Vector*  vec   = glyph->outline->points;
+    char*       tags  = glyph->outline->tags;
+
+
+    for ( n = 0; n < glyph->num_points; n++ )
+    {
+      if ( dimension == 0 )
+        vec[n].x = point->cur_u;
+      else
+        vec[n].y = point->cur_u;
+
+      if ( psh3_point_is_strong( point ) )
+        tags[n] |= (char)( ( dimension == 0 ) ? 32 : 64 );
+
+#ifdef DEBUG_HINTER
+
+      if ( dimension == 0 )
+      {
+        point->cur_x   = point->cur_u;
+        point->flags_x = point->flags2 | point->flags;
+      }
+      else
+      {
+        point->cur_y   = point->cur_u;
+        point->flags_y = point->flags2 | point->flags;
+      }
+
+#endif
+
+      point++;
+    }
+  }
+
+
   static FT_Error
   psh3_glyph_init( PSH3_Glyph   glyph,
                    FT_Outline*  outline,
@@ -866,6 +1010,11 @@
     glyph->outline = outline;
     glyph->globals = globals;
 
+#ifdef COMPUTE_INFLEXS
+    psh3_glyph_load_points( glyph, 0 );
+    psh3_glyph_compute_inflections( glyph );
+#endif /* COMPUTE_INFLEXS */
+
     /* now deal with hints tables */
     error = psh3_hint_table_init( &glyph->hint_tables [0],
                                   &ps_hints->dimension[0].hints,
@@ -888,78 +1037,133 @@
   }
 
 
-  /* load outline point coordinates into hinter glyph */
+
+
+ /* compute all extrema in a glyph for a given dimension */
   static void
-  psh3_glyph_load_points( PSH3_Glyph  glyph,
-                          FT_Int      dimension )
+  psh3_glyph_compute_extrema( PSH3_Glyph  glyph )
   {
-    FT_Vector*  vec   = glyph->outline->points;
-    PSH3_Point  point = glyph->points;
-    FT_UInt     count = glyph->num_points;
+    FT_UInt       first = 0, next, n;
+    PSH3_Point    points  = glyph->points;
+    PSH3_Contour  contour = glyph->contours;
 
-
-    for ( ; count > 0; count--, point++, vec++ )
+    /* first of all, compute all local extrema */
+    for ( n = 0; n < glyph->num_contours; n++ )
     {
-      point->flags &= PSH3_POINT_OFF | PSH3_POINT_SMOOTH;
-      point->hint   = 0;
-      if ( dimension == 0 )
-        point->org_u = vec->x;
-      else
-        point->org_u = vec->y;
+      PSH3_Point  first = glyph->contours[n].start;
+      PSH3_Point  point, before, after;
 
-#ifdef DEBUG_HINTER
-      point->org_x = vec->x;
-      point->org_y = vec->y;
-#endif
+      point  = first;
+      before = point;
+      after  = point;
 
-    }
-  }
+      do
+      {
+        before = before->prev;
+        if ( before == first )
+          goto Skip;
 
+      } while ( before->org_u == point->org_u );
 
-  /* save hinted point coordinates back to outline */
-  static void
-  psh3_glyph_save_points( PSH3_Glyph  glyph,
-                          FT_Int      dimension )
-  {
-    FT_UInt     n;
-    PSH3_Point  point = glyph->points;
-    FT_Vector*  vec   = glyph->outline->points;
-    char*       tags  = glyph->outline->tags;
+      first = point = before->next;
 
+      for (;;)
+      {
+        after = point;
+        do
+        {
+          after = after->next;
+          if ( after == first )
+            goto Next;
+        }
+        while ( after->org_u == point->org_u );
 
+        if ( before->org_u < point->org_u )
+        {
+          if ( after->org_u < point->org_u )
+          {
+            /* local maximum */
+            goto Extremum;
+          }
+        }
+        else /* before->org_u > point->org_u */
+        {
+          if ( after->org_u > point->org_u )
+          {
+            /* local minimum */
+          Extremum:
+            do
+            {
+              psh3_point_set_extremum( point );
+              point = point->next;
+            }
+            while ( point != after );
+          }
+        }
+
+        before = after->prev;
+        point  = after;
+
+      } /* for  */
+
+    Next:
+      ;
+    }
+
+  /* for each extrema, determine its direction along the */
+  /* orthogonal axis                                     */
     for ( n = 0; n < glyph->num_points; n++ )
     {
-      if ( dimension == 0 )
-        vec[n].x = point->cur_u;
-      else
-        vec[n].y = point->cur_u;
+      PSH3_Point  point, before, after;
 
-      if ( psh3_point_is_strong( point ) )
-        tags[n] |= (char)( ( dimension == 0 ) ? 32 : 64 );
+      point = &glyph->points[n];
+      if ( psh3_point_is_extremum( point ) )
+      {
+        PSH3_Point  before = point;
+        PSH3_Point  after  = point;
 
-#ifdef DEBUG_HINTER
+        do
+        {
+          before = before->prev;
+          if ( before == point )
+            goto Skip;
+        }
+        while ( before->org_v == point->org_v );
 
-      if ( dimension == 0 )
+        do
+        {
+          after = after->next;
+          if ( after == point )
+            goto Skip;
+        }
+        while ( after->org_v == point->org_v );
+      }
+
+      if ( before->org_v < point->org_v &&
+           after->org_v  > point->org_v )
       {
-        point->cur_x   = point->cur_u;
-        point->flags_x = point->flags;
+        psh3_point_set_positive( point );
       }
-      else
+      else if ( before->org_v > point->org_v &&
+                after->org_v  < point->org_v )
       {
-        point->cur_y   = point->cur_u;
-        point->flags_y = point->flags;
+        psh3_point_set_negative( point );
       }
-
-#endif
-
-      point++;
+    Skip:
+      ;
     }
   }
 
 
-#define PSH3_STRONG_THRESHOLD  10
 
 
+#define PSH3_STRONG_THRESHOLD  30
+
+
+  /* major_dir is the direction for points on the bottom/left of the stem;
+   * Points on the top/right of the stem will have a direction of
+   * -major_dir.
+   */
   static void
   psh3_hint_table_find_strong_point( PSH3_Hint_Table  table,
                                      PSH3_Point       point,
@@ -967,47 +1171,97 @@
   {
     PSH3_Hint*   sort      = table->sort;
     FT_UInt      num_hints = table->num_hints;
+    FT_Int       point_dir = 0;
+    FT_UInt      flag;
 
+    if ( PSH3_DIR_COMPARE( point->dir_in, major_dir ) )
+      point_dir = point->dir_in;
 
-    for ( ; num_hints > 0; num_hints--, sort++ )
+    else if ( PSH3_DIR_COMPARE( point->dir_out, major_dir ) )
+      point_dir = point->dir_out;
+
+    if ( point_dir )
     {
-      PSH3_Hint  hint = sort[0];
+      FT_UInt  flag;
 
-
-      if ( ABS( point->dir_in )  == major_dir ||
-           ABS( point->dir_out ) == major_dir )
+      for ( ; num_hints > 0; num_hints--, sort++ )
       {
-        FT_Pos  d;
+        PSH3_Hint  hint = sort[0];
+        FT_Pos     d;
 
-
-        d = point->org_u - hint->org_pos;
-        if ( ABS( d ) < PSH3_STRONG_THRESHOLD )
+        if ( point_dir == major_dir )
         {
-        Is_Strong:
-          psh3_point_set_strong( point );
-          point->hint = hint;
-          break;
+          flag = PSH3_POINT_EDGE_MIN;
+          d    = point->org_u - hint->org_pos;
+          if ( ABS( d ) < PSH3_STRONG_THRESHOLD )
+          {
+          Is_Strong:
+            psh3_point_set_strong( point );
+            point->flags2 |= flag;
+            point->hint    = hint;
+            break;
+          }
         }
-
-        d -= hint->org_len;
-        if ( ABS( d ) < PSH3_STRONG_THRESHOLD )
-          goto Is_Strong;
+        else if ( point_dir == -major_dir )
+        {
+          flag  = PSH3_POINT_EDGE_MAX;
+          d     = point->org_u - hint->org_pos - hint->org_len;
+          if ( ABS( d ) < PSH3_STRONG_THRESHOLD )
+            goto Is_Strong;
+        }
       }
-
+    }
 #if 1
+    else if ( psh3_point_is_extremum( point ) )
+    {
+      /* treat extrema as special cases for stem edge alignment */
+      FT_UInt  min_flag, max_flag;
 
-      if ( point->org_u >= hint->org_pos &&
-           point->org_u <= hint->org_pos + hint->org_len &&
-           psh3_point_is_extremum( point ) )
+      if ( major_dir == PSH3_DIR_HORIZONTAL )
       {
-        /* attach to hint, but don't mark as strong */
-        point->hint = hint;
-        break;
+        min_flag = PSH3_POINT_POSITIVE;
+        max_flag = PSH3_POINT_NEGATIVE;
       }
+      else
+      {
+        min_flag = PSH3_POINT_NEGATIVE;
+        max_flag = PSH3_POINT_POSITIVE;
+      }
 
-#endif
+      for ( ; num_hints > 0; num_hints--, sort++ )
+      {
+        PSH3_Hint  hint = sort[0];
+        FT_Pos     d, flag;
 
+        if ( point->flags2 & min_flag )
+        {
+          flag = PSH3_POINT_EDGE_MIN;
+          d    = point->org_u - hint->org_pos;
+          if ( ABS( d ) < PSH3_STRONG_THRESHOLD )
+          {
+          Is_Strong2:
+            point->flags2 |= flag;
+            point->hint    = hint;
+            psh3_point_set_strong( point );
+            break;
+          }
+        }
+        else if ( point->flags2 & max_flag )
+        {
+          flag = PSH3_POINT_EDGE_MAX;
+          d    = point->org_u - hint->org_pos - hint->org_len;
+          if ( ABS( d ) < PSH3_STRONG_THRESHOLD )
+            goto Is_Strong2;
+        }
+
+        if ( point->org_u >= hint->org_pos                 &&
+             point->org_u <= hint->org_pos + hint->org_len )
+        {
+          point->hint = hint;
+        }
+      }
     }
+#endif
   }
 
 
@@ -1023,8 +1277,8 @@
       PS_Mask          mask      = table->hint_masks->masks;
       FT_UInt          num_masks = table->hint_masks->num_masks;
       FT_UInt          first     = 0;
-      FT_Int           major_dir = dimension == 0 ? PSH3_DIR_UP
-                                                  : PSH3_DIR_RIGHT;
+      FT_Int           major_dir = dimension == 0 ? PSH3_DIR_VERTICAL
+                                                  : PSH3_DIR_HORIZONTAL;
 
 
       /* process secondary hints to "selected" points */
@@ -1107,22 +1361,31 @@
         {
           FT_Pos  delta;
 
+          if ( psh3_point_is_edge_min( point ) )
+          {
+            point->cur_u = hint->cur_pos;
+          }
+          else if ( psh3_point_is_edge_max( point ) )
+          {
+            point->cur_u = hint->cur_pos + hint->cur_len;
+          }
+          else
+          {
+            delta = point->org_u - hint->org_pos;
 
-          delta = point->org_u - hint->org_pos;
+            if ( delta <= 0 )
+              point->cur_u = hint->cur_pos + FT_MulFix( delta, scale );
 
-          if ( delta <= 0 )
-            point->cur_u = hint->cur_pos + FT_MulFix( delta, scale );
+            else if ( delta >= hint->org_len )
+              point->cur_u = hint->cur_pos + hint->cur_len +
+                            FT_MulFix( delta - hint->org_len, scale );
 
-          else if ( delta >= hint->org_len )
-            point->cur_u = hint->cur_pos + hint->cur_len +
-                           FT_MulFix( delta - hint->org_len, scale );
-
-          else if ( hint->org_len > 0 )
-            point->cur_u = hint->cur_pos +
-                           FT_MulDiv( delta, hint->cur_len, hint->org_len );
-          else
-            point->cur_u = hint->cur_pos;
-
+            else if ( hint->org_len > 0 )
+              point->cur_u = hint->cur_pos +
+                            FT_MulDiv( delta, hint->cur_len, hint->org_len );
+            else
+              point->cur_u = hint->cur_pos;
+          }
           psh3_point_set_fitted( point );
         }
       }
@@ -1435,6 +1698,9 @@
     {
       /* load outline coordinates into glyph */
       psh3_glyph_load_points( glyph, dimension );
+      
+      /* compute local extrema */
+      psh3_glyph_compute_extrema( glyph );
 
       /* compute aligned stem/hints positions */
       psh3_hint_table_align_hints( &glyph->hint_tables[dimension],
--- a/src/pshinter/pshalgo3.h
+++ b/src/pshinter/pshalgo3.h
@@ -28,8 +28,10 @@
 FT_BEGIN_HEADER
 
 
+ /* handle to Hint structure */
   typedef struct PSH3_HintRec_*  PSH3_Hint;
-
+ 
+ /* hint bit-flags */
   typedef enum
   {
     PSH3_HINT_GHOST  = PS_HINT_FLAG_GHOST,
@@ -48,7 +50,7 @@
 #define psh3_hint_deactivate( x )  (x)->flags &= ~PSH3_HINT_ACTIVE
 #define psh3_hint_set_fitted( x )  (x)->flags |=  PSH3_HINT_FITTED
 
-
+ /* hint structure */
   typedef struct  PSH3_HintRec_
   {
     FT_Int     org_pos;
@@ -83,7 +85,7 @@
     PSH3_Hint*     sort;
     PSH3_Hint*     sort_global;
     FT_UInt        num_zones;
-    PSH3_Zone      zones;
+    PSH3_ZoneRec*  zones;
     PSH3_Zone      zone;
     PS_Mask_Table  hint_masks;
     PS_Mask_Table  counter_masks;
@@ -97,21 +99,66 @@
   enum
   {
     PSH3_DIR_NONE  =  4,
-    PSH3_DIR_UP    =  1,
-    PSH3_DIR_DOWN  = -1,
+    PSH3_DIR_UP    = -1,
+    PSH3_DIR_DOWN  =  1,
     PSH3_DIR_LEFT  = -2,
     PSH3_DIR_RIGHT =  2
   };
 
+#define  PSH3_DIR_HORIZONTAL    2
+#define  PSH3_DIR_VERTICAL      1
+
+#define  PSH3_DIR_COMPARE(d1,d2)  ( (d1) == (d2) || (d1) == -(d2) )
+#define  PSH3_DIR_IS_HORIZONTAL(d)  PSH3_DIR_COMPARE(d,PSH3_DIR_HORIZONTAL)
+#define  PSH3_DIR_IS_VERTICAL(d)    PSH3_DIR_COMPARE(d,PSH3_DIR_VERTICAL)
+
+
+ /* the following bit-flags are computed once by the glyph */
+ /* analyzer, for both dimensions                          */
   enum
   {
-    PSH3_POINT_OFF    = 1,   /* point is off the curve  */
-    PSH3_POINT_STRONG = 2,   /* point is strong         */
-    PSH3_POINT_SMOOTH = 4,   /* point is smooth         */
-    PSH3_POINT_FITTED = 8    /* point is already fitted */
+    PSH3_POINT_OFF         = 1,   /* point is off the curve          */
+    PSH3_POINT_SMOOTH      = 2,   /* point is smooth                 */
+    PSH3_POINT_INFLEX      = 4    /* point is inflection             */
   };
 
+#define psh3_point_is_smooth( p )      ( (p)->flags  & PSH3_POINT_SMOOTH )
+#define psh3_point_is_off( p )         ( (p)->flags  & PSH3_POINT_OFF    )
+#define psh3_point_is_inflection( p )  ( (p)->flags & PSH3_POINT_INFLEX )
 
+#define psh3_point_set_smooth( p )  (p)->flags  |= PSH3_POINT_SMOOTH
+#define psh3_point_set_off( p )     (p)->flags  |= PSH3_POINT_OFF
+#define psh3_point_set_inflex( p )  (p)->flags  |= PSH3_POINT_INFLEX
+
+ /* the following bit-flags are re-computed for each dimension */
+  enum
+  {
+    PSH3_POINT_STRONG      = 16,   /* point is strong                             */
+    PSH3_POINT_FITTED      = 32,   /* point is already fitted                     */
+    PSH3_POINT_EXTREMUM    = 64,   /* point is local extremum                     */
+    PSH3_POINT_POSITIVE    = 128,  /* extremum has positive contour flow          */
+    PSH3_POINT_NEGATIVE    = 256,  /* extremum has negative contour flow          */
+    PSH3_POINT_EDGE_MIN    = 512,  /* point is aligned to left/bottom stem edge   */
+    PSH3_POINT_EDGE_MAX    = 1024  /* point is aligned to top/right stem edge     */
+  };
+
+#define psh3_point_is_strong( p )   ( (p)->flags2 & PSH3_POINT_STRONG )
+#define psh3_point_is_fitted( p )   ( (p)->flags2 & PSH3_POINT_FITTED )
+#define psh3_point_is_extremum( p ) ( (p)->flags2 & PSH3_POINT_EXTREMUM )
+#define psh3_point_is_positive( p ) ( (p)->flags2 & PSH3_POINT_POSITIVE )
+#define psh3_point_is_negative( p ) ( (p)->flags2 & PSH3_POINT_NEGATIVE )
+#define psh3_point_is_edge_min( p ) ( (p)->flags2 & PSH3_POINT_EDGE_MIN )
+#define psh3_point_is_edge_max( p ) ( (p)->flags2 & PSH3_POINT_EDGE_MAX )
+
+#define psh3_point_set_strong( p )    (p)->flags2 |= PSH3_POINT_STRONG
+#define psh3_point_set_fitted( p )    (p)->flags2 |= PSH3_POINT_FITTED
+#define psh3_point_set_extremum( p )  (p)->flags2 |= PSH3_POINT_EXTREMUM
+#define psh3_point_set_positive( p )  (p)->flags2 |= PSH3_POINT_POSITIVE
+#define psh3_point_set_negative( p )  (p)->flags2 |= PSH3_POINT_NEGATIVE
+#define psh3_point_set_edge_min( p )  (p)->flags2 |= PSH3_POINT_EDGE_MIN
+#define psh3_point_set_edge_max( p )  (p)->flags2 |= PSH3_POINT_EDGE_MAX
+
+
   typedef struct  PSH3_PointRec_
   {
     PSH3_Point    prev;
@@ -118,6 +165,7 @@
     PSH3_Point    next;
     PSH3_Contour  contour;
     FT_UInt       flags;
+    FT_UInt       flags2;
     FT_Char       dir_in;
     FT_Char       dir_out;
     FT_Angle      angle_in;
@@ -124,6 +172,7 @@
     FT_Angle      angle_out;
     PSH3_Hint     hint;
     FT_Pos        org_u;
+    FT_Pos        org_v;
     FT_Pos        cur_u;
 #ifdef DEBUG_HINTER
     FT_Pos        org_x;
@@ -137,14 +186,11 @@
   } PSH3_PointRec;
 
 
-#define psh3_point_is_strong( p )   ( (p)->flags & PSH3_POINT_STRONG )
-#define psh3_point_is_fitted( p )   ( (p)->flags & PSH3_POINT_FITTED )
-#define psh3_point_is_smooth( p )   ( (p)->flags & PSH3_POINT_SMOOTH )
+#define PSH3_POINT_EQUAL_ORG(a,b)  ( (a)->org_u == (b)->org_u && \
+                                     (a)->org_v == (b)->org_v )
 
-#define psh3_point_set_strong( p )  (p)->flags |= PSH3_POINT_STRONG
-#define psh3_point_set_fitted( p )  (p)->flags |= PSH3_POINT_FITTED
-#define psh3_point_set_smooth( p )  (p)->flags |= PSH3_POINT_SMOOTH
-
+#define PSH3_POINT_ANGLE(a,b)  FT_Atan2( (b)->org_u - (a)->org_u,  \
+                                         (b)->org_v - (a)->org_v )
 
   typedef struct  PSH3_ContourRec_
   {
--- a/src/pshinter/pshglob.c
+++ b/src/pshinter/pshglob.c
@@ -23,7 +23,7 @@
 #include "pshglob.h"
 
 #ifdef DEBUG_HINTER
-  extern PSH_Globals  ps_debug_globals = 0;
+  PSH_Globals  ps_debug_globals = 0;
 #endif
 
 
--- a/src/pshinter/pshrec.c
+++ b/src/pshinter/pshrec.c
@@ -27,9 +27,9 @@
 #define FT_COMPONENT  trace_pshrec
 
 #ifdef DEBUG_HINTER
-  extern PS_Hints  ps_debug_hints         = 0;
-  extern int       ps_debug_no_horz_hints = 0;
-  extern int       ps_debug_no_vert_hints = 0;
+  PS_Hints  ps_debug_hints         = 0;
+  int       ps_debug_no_horz_hints = 0;
+  int       ps_debug_no_vert_hints = 0;
 #endif
 
 
--- a/tests/Jamfile
+++ b/tests/Jamfile
@@ -7,28 +7,35 @@
 
 SubDirHdrs [ FT2_SubDir  .. nirvana include ] ;
 
-NV_TOP = [ FT2_SubDir  .. .. nirvana ] ;
+NV_TOP = [ FT2_SubDir .. .. nirvana ] ;
 
-NIRVANA_LINKLIBS = $(NV_TOP)\\objs\\nirvana$(SUFLIB) ;
+NIRVANA_LINKLIBS = $(NV_TOP)/objs/libnirvana$(SUFLIB) ;
 
 {
   local t ;
-  
+
   for t in $(test_programs)
   {
     Main  $(t) : $(t).c ;
-    
+
     LinkLibraries $(t) : $(FT2_LIB) ;
-    
-    if $(TOOLSET) = MINGW
+
+    if $(WIN)
     {
-      LINKKLIBS on $(t)$(SUFEXE) = "-luser32 -lgdi32" ;
+      if $(TOOLSET) = MINGW
+      {
+        LINKKLIBS on $(t)$(SUFEXE) = "-luser32 -lgdi32" ;
+      }
+      else
+      {
+        LINKLIBS on $(t)$(SUFEXE) = user32.lib gdi32.lib ;
+      }
     }
     else
     {
-      LINKLIBS on $(t)$(SUFEXE) = user32.lib gdi32.lib ;
+      LINKLIBS on $(t)$(SUFEXE) = -L/usr/X11R6/lib -lX11 -lm ;
     }
-    
+
     NEEDLIBS on $(t)$(SUFEXE) += $(NIRVANA_LINKLIBS) ;
   }
 }
--- a/tests/gview.c
+++ b/tests/gview.c
@@ -9,6 +9,7 @@
 #include <../src/pshinter/pshrec.h>
 #include <../src/pshinter/pshalgo1.h>
 #include <../src/pshinter/pshalgo2.h>
+#include <../src/pshinter/pshalgo3.h>
 
 #include <../src/autohint/ahtypes.h>
 
@@ -610,8 +611,157 @@
   }
 }
 
+ /************************************************************************/
+ /************************************************************************/
+ /*****                                                              *****/
+ /*****            POSTSCRIPT HINTER ALGORITHM 3 ROUTINES            *****/
+ /*****                                                              *****/
+ /************************************************************************/
+ /************************************************************************/
 
+#include <../src/pshinter/pshalgo3.h>
+
 static void
+draw_ps3_hint( PSH3_Hint   hint, FT_Bool  vertical )
+{
+  int        x1, x2;
+  NV_Vector  v;
+
+  if ( pshint_vertical != vertical )
+  {
+    if (vertical)
+      pshint_cpos = 40;
+    else
+      pshint_cpos = 10;
+
+    pshint_vertical = vertical;
+  }
+
+  if (!vertical)
+  {
+    if ( !option_show_vert_hints )
+      return;
+
+    v.x = hint->cur_pos;
+    v.y = 0;
+    nv_vector_transform( &v, &size_transform );
+    x1 = (int)(v.x + 0.5);
+
+    v.x = hint->cur_pos + hint->cur_len;
+    v.y = 0;
+    nv_vector_transform( &v, &size_transform );
+    x2 = (int)(v.x + 0.5);
+
+    nv_pixmap_fill_rect( target, x1, 0, 1, target->height,
+                         psh3_hint_is_ghost(hint)
+                         ? GHOST_HINT_COLOR : STEM_HINT_COLOR );
+
+    if ( psh3_hint_is_ghost(hint) )
+    {
+      x1 --;
+      x2 = x1 + 2;
+    }
+    else
+      nv_pixmap_fill_rect( target, x2, 0, 1, target->height,
+                           psh3_hint_is_ghost(hint)
+                           ? GHOST_HINT_COLOR : STEM_HINT_COLOR );
+
+    nv_pixmap_fill_rect( target, x1, pshint_cpos, x2+1-x1, 1,
+                         STEM_JOIN_COLOR );
+  }
+  else
+  {
+    if (!option_show_horz_hints)
+      return;
+
+    v.y = hint->cur_pos;
+    v.x = 0;
+    nv_vector_transform( &v, &size_transform );
+    x1 = (int)(v.y + 0.5);
+
+    v.y = hint->cur_pos + hint->cur_len;
+    v.x = 0;
+    nv_vector_transform( &v, &size_transform );
+    x2 = (int)(v.y + 0.5);
+
+    nv_pixmap_fill_rect( target, 0, x1, target->width, 1,
+                         psh3_hint_is_ghost(hint)
+                         ? GHOST_HINT_COLOR : STEM_HINT_COLOR );
+
+    if ( psh3_hint_is_ghost(hint) )
+    {
+      x1 --;
+      x2 = x1 + 2;
+    }
+    else
+      nv_pixmap_fill_rect( target, 0, x2, target->width, 1,
+                           psh3_hint_is_ghost(hint)
+                           ? GHOST_HINT_COLOR : STEM_HINT_COLOR );
+
+    nv_pixmap_fill_rect( target, pshint_cpos, x2, 1, x1+1-x2,
+                         STEM_JOIN_COLOR );
+  }
+
+#if 0
+  printf( "[%7.3f %7.3f] %c\n", hint->cur_pos/64.0, (hint->cur_pos+hint->cur_len)/64.0, vertical ? 'v' : 'h' );
+#endif
+
+  pshint_cpos += 10;
+}
+
+
+static void
+ps3_draw_control_points( void )
+{
+  if ( ps3_debug_glyph )
+  {
+    PSH3_Glyph    glyph = ps3_debug_glyph;
+    PSH3_Point    point = glyph->points;
+    FT_UInt       count = glyph->num_points;
+    NV_Transform  transform, *trans = &transform;
+    NV_Path       vert_rect;
+    NV_Path       horz_rect;
+    NV_Path       dot, circle;
+
+    for ( ; count > 0; count--, point++ )
+    {
+      NV_Vector  vec;
+
+      vec.x = point->cur_x;
+      vec.y = point->cur_y;
+      nv_vector_transform( &vec, &size_transform );
+
+      nv_transform_set_translate( trans, vec.x, vec.y );
+
+      if ( option_show_smooth && !psh3_point_is_smooth(point) )
+      {
+        nv_painter_set_color( painter, SMOOTH_COLOR, 256 );
+        nv_painter_fill_path( painter, trans, 0, symbol_circle );
+      }
+
+      if (option_show_horz_hints)
+      {
+        if ( point->flags_y & PSH3_POINT_STRONG )
+        {
+          nv_painter_set_color( painter, STRONG_COLOR, 256 );
+          nv_painter_fill_path( painter, trans, 0, symbol_rect_h );
+        }
+      }
+
+      if (option_show_vert_hints)
+      {
+        if ( point->flags_x & PSH3_POINT_STRONG )
+        {
+          nv_painter_set_color( painter, STRONG_COLOR, 256 );
+          nv_painter_fill_path( painter, trans, 0, symbol_rect_v );
+        }
+      }
+    }
+  }
+}
+
+
+static void
 ps_print_hints( void )
 {
   if ( ps_debug_hints )
@@ -742,7 +892,7 @@
 
     if ( option_show_edges )
     {
-      /* draw verticla edges */
+      /* draw vertical edges */
       if ( option_show_vert_hints )
       {
         count = glyph->num_vedges;
@@ -956,12 +1106,11 @@
 
   ps1_debug_hint_func = option_show_ps_hints ? draw_ps1_hint : 0;
   ps2_debug_hint_func = option_show_ps_hints ? draw_ps2_hint : 0;
+  ps3_debug_hint_func = option_show_ps_hints ? draw_ps3_hint : 0;
 
   ah_debug_hinter = NULL;
 
-  error = FT_Load_Glyph( face, glyph_index, option_hinting
-                                          ? FT_LOAD_NO_BITMAP
-                                          : FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING );
+  error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_BITMAP );
   if (error) Panic( "could not load glyph" );
 
   if ( face->glyph->format != ft_glyph_format_outline )
@@ -1182,6 +1331,9 @@
       TOGGLE_OPTION( option_show_blues, "blue zones display" );
 
     case NVV_KEY('h'):
+      ps_debug_no_horz_hints = option_hinting;
+      ps_debug_no_vert_hints = option_hinting;
+
       TOGGLE_OPTION( option_hinting, "hinting" )
 
     case NVV_KEY('H'):
@@ -1302,7 +1454,7 @@
 
       draw_ps_blue_zones();
       draw_glyph( glyph_index );
-      ps2_draw_control_points();
+      ps3_draw_control_points();
 
       nvv_surface_refresh( surface, NULL );