Edit

kc3-lang/freetype/tests/gview.c

Branch :

  • Show log

    Commit

  • Author : David Turner
    Date : 2001-11-20 01:29:34
    Hash : f2c56515
    Message : * src/pshinter/{pshalgo2.c, pshalgo1.c}: fixed stupid bug in sorting routine that created nasty alignment artefacts. * src/pshinter/pshrec.c, tests/gview.c: debugging updates.. * src/smooth/ftgrays.c: de-activated experimental gamme support, apparently, "optimal" gamma tables depend on the monitor type, resolution and general karma, so it's better to compute them outside of the rasterizer itself..

  • tests/gview.c
  • #include <nirvana.h>
    #include NV_VIEWPORT_H
    #include <stdio.h>
    
    #include <ft2build.h>
    #include FT_FREETYPE_H
    
    /* include FreeType internals to debug hints */
    #include <../src/pshinter/pshrec.h>
    #include <../src/pshinter/pshalgo1.h>
    #include <../src/pshinter/pshalgo2.h>
    
    #include <../src/autohint/ahtypes.h>
    
     /************************************************************************/
     /************************************************************************/
     /*****                                                              *****/
     /*****                     ROOT DEFINITIONS                         *****/
     /*****                                                              *****/
     /************************************************************************/
     /************************************************************************/
    
    
    #include <time.h>    /* for clock() */
    
    /* SunOS 4.1.* does not define CLOCKS_PER_SEC, so include <sys/param.h> */
    /* to get the HZ macro which is the equivalent.                         */
    #if defined(__sun__) && !defined(SVR4) && !defined(__SVR4)
    #include <sys/param.h>
    #define CLOCKS_PER_SEC HZ
    #endif
    
    static int  first_glyph = 0;
    
    static NV_Renderer   renderer;
    static NV_Painter    painter;
    static NV_Pixmap     target;
    static NV_Error      error;
    static NV_Memory     memory;
    static NVV_Display   display;
    static NVV_Surface   surface;
    
    static FT_Library    freetype;
    static FT_Face       face;
    
    
    static  NV_Pos        glyph_scale;
    static  NV_Pos        glyph_org_x;
    static  NV_Pos        glyph_org_y;
    static  NV_Transform  glyph_transform;  /* font units -> device pixels */
    static  NV_Transform  size_transform;   /* subpixels  -> device pixels */
    
    static  NV_Scale      grid_scale = 1.0;
    
    static  int   glyph_index;
    static  int   pixel_size = 12;
    
     /************************************************************************/
     /************************************************************************/
     /*****                                                              *****/
     /*****                 OPTIONS, COLORS and OTHERS                   *****/
     /*****                                                              *****/
     /************************************************************************/
     /************************************************************************/
    
    static  int   option_show_axis     = 1;
    static  int   option_show_dots     = 1;
    static  int   option_show_stroke   = 0;
    static  int   option_show_glyph    = 1;
    static  int   option_show_grid     = 1;
    static  int   option_show_em       = 0;
    static  int   option_show_smooth   = 1;
    static  int   option_show_blues    = 0;
    static  int   option_show_edges    = 0;
    static  int   option_show_segments = 1;
    static  int   option_show_links    = 1;
    static  int   option_show_indices  = 0;
    
    static  int   option_show_ps_hints   = 1;
    static  int   option_show_horz_hints = 1;
    static  int   option_show_vert_hints = 1;
    
    
    static  int   option_hinting = 1;
    
    static  char  temp_message[1024];
    
    static  NV_Path   symbol_dot     = NULL;
    static  NV_Path   symbol_circle  = NULL;
    static  NV_Path   symbol_square  = NULL;
    static  NV_Path   symbol_rect_h  = NULL;
    static  NV_Path   symbol_rect_v  = NULL;
    
    
    
    #define  AXIS_COLOR        0xFFFF0000
    #define  GRID_COLOR        0xFFD0D0D0
    #define  ON_COLOR          0xFFFF2000
    #define  OFF_COLOR         0xFFFF0080
    #define  STRONG_COLOR      0xFF404040
    #define  INTERP_COLOR      0xFF206040
    #define  SMOOTH_COLOR      0xF000B040
    #define  BACKGROUND_COLOR  0xFFFFFFFF
    #define  TEXT_COLOR        0xFF000000
    #define  EM_COLOR          0x80008000
    #define  BLUES_TOP_COLOR   0x4000008F
    #define  BLUES_BOT_COLOR   0x40008F00
    
    #define  GHOST_HINT_COLOR  0xE00000FF
    #define  STEM_HINT_COLOR   0xE02020FF
    #define  STEM_JOIN_COLOR   0xE020FF20
    
    #define  EDGE_COLOR        0xF0704070
    #define  SEGMENT_COLOR     0xF0206040
    #define  LINK_COLOR        0xF0FFFF00
    #define  SERIF_LINK_COLOR  0xF0FF808F
    
    /* print message and abort program */
    static void
    Panic( const char*  message )
    {
      fprintf( stderr, "PANIC: %s\n", message );
      exit(1);
    }
    
    
    static void
    init_symbols( void )
    {
      nv_path_new_rectangle( renderer, -1, -1, 3, 3, 0, 0, &symbol_square );
      nv_path_new_rectangle( renderer, -1, -6, 2, 12, 0, 0, &symbol_rect_v );
      nv_path_new_rectangle( renderer, -6, -1, 12, 2, 0, 0, &symbol_rect_h );
      nv_path_new_circle( renderer, 0, 0, 3., &symbol_dot );
      nv_path_stroke( symbol_dot, 0.6,
                      nv_path_linecap_butt,
                      nv_path_linejoin_miter, 1.,
                      &symbol_circle );
    
      nv_path_destroy( symbol_dot );
      nv_path_new_circle( renderer, 0, 0, 2., &symbol_dot );
    
     }
    
    static void
    done_symbols( void )
    {
      nv_path_destroy( symbol_circle );
      nv_path_destroy( symbol_dot );
      nv_path_destroy( symbol_rect_v );
      nv_path_destroy( symbol_rect_h );
      nv_path_destroy( symbol_square );
    }
    
     /************************************************************************/
     /************************************************************************/
     /*****                                                              *****/
     /*****                     COMMON GRID DRAWING ROUTINES             *****/
     /*****                                                              *****/
     /************************************************************************/
     /************************************************************************/
    
    static void
    reset_scale( NV_Scale  scale )
    {
     /* compute font units -> grid pixels scale factor */
      glyph_scale = target->width*0.75 / face->units_per_EM * scale;
    
     /* setup font units -> grid pixels transform */
      nv_transform_set_scale( &glyph_transform, glyph_scale, -glyph_scale );
      glyph_org_x = glyph_transform.delta.x = target->width*0.125;
      glyph_org_y = glyph_transform.delta.y = target->height*0.875;
    
     /* setup subpixels -> grid pixels transform */
      nv_transform_set_scale( &size_transform,
                                glyph_scale/nv_fromfixed(face->size->metrics.x_scale),
                              - glyph_scale/nv_fromfixed(face->size->metrics.y_scale) );
    
      size_transform.delta = glyph_transform.delta;
    }
    
    
    static void
    reset_size( int  pixel_size, NV_Scale  scale )
    {
      FT_Set_Pixel_Sizes( face, pixel_size, pixel_size );
      reset_scale( scale );
    }
    
    
    static void
    clear_background( void )
    {
      nv_pixmap_fill_rect( target, 0, 0, target->width, target->height,
                           BACKGROUND_COLOR );
    }
    
    
    static void
    draw_grid( void )
    {
      int  x = (int)glyph_org_x;
      int  y = (int)glyph_org_y;
    
      /* draw grid */
      if ( option_show_grid )
      {
        NV_Scale  min, max, x, step;
    
        /* draw vertical grid bars */
        step = 64. * size_transform.matrix.xx;
        if (step > 1.)
        {
          min  = max = glyph_org_x;
          while ( min - step >= 0 )             min -= step;
          while ( max + step < target->width )  max += step;
    
          for ( x = min; x <= max; x += step )
            nv_pixmap_fill_rect( target, (NV_Int)(x+.5), 0,
                                 1, target->height, GRID_COLOR );
        }
    
        /* draw horizontal grid bars */
        step = -64. * size_transform.matrix.yy;
        if (step > 1.)
        {
          min  = max = glyph_org_y;
          while ( min - step >= 0 )              min -= step;
          while ( max + step < target->height )  max += step;
    
          for ( x = min; x <= max; x += step )
            nv_pixmap_fill_rect( target, 0, (NV_Int)(x+.5),
                                 target->width, 1, GRID_COLOR );
        }
      }
    
      /* draw axis */
      if ( option_show_axis )
      {
        nv_pixmap_fill_rect( target, x, 0, 1, target->height, AXIS_COLOR );
        nv_pixmap_fill_rect( target, 0, y, target->width, 1, AXIS_COLOR );
      }
    
      if ( option_show_em )
      {
        NV_Path  path;
        NV_Path  stroke;
        NV_UInt  units = (NV_UInt)face->units_per_EM;
    
        nv_path_new_rectangle( renderer, 0, 0, units, units, 0, 0, &path );
        nv_path_transform( path, &glyph_transform );
    
        nv_path_stroke( path, 1.5, nv_path_linecap_butt, nv_path_linejoin_miter,
                        4.0, &stroke );
    
        nv_painter_set_color( painter, EM_COLOR, 256 );
        nv_painter_fill_path( painter, NULL, 0, stroke );
    
        nv_path_destroy( stroke );
        nv_path_destroy( path );
      }
    
    }
    
    
     /************************************************************************/
     /************************************************************************/
     /*****                                                              *****/
     /*****            POSTSCRIPT GLOBALS ROUTINES                       *****/
     /*****                                                              *****/
     /************************************************************************/
     /************************************************************************/
    
    #include <../src/pshinter/pshglob.h>
    
    static void
    draw_ps_blue_zones( void )
    {
      if ( option_show_blues && ps_debug_globals )
      {
        PSH_Blues       blues = &ps_debug_globals->blues;
        PSH_Blue_Table  table;
        NV_Vector       v;
        FT_Int          y1, y2;
        FT_UInt         count;
        PSH_Blue_Zone   zone;
    
        /* draw top zones */
        table = &blues->normal_top;
        count = table->count;
        zone  = table->zones;
    
        for ( ; count > 0; count--, zone++ )
        {
          v.x = 0;
          if ( !ps_debug_no_horz_hints )
          {
            v.y = zone->cur_ref + zone->cur_delta;
            nv_vector_transform( &v, &size_transform );
          }
          else
          {
            v.y = zone->org_ref + zone->org_delta;
            nv_vector_transform( &v, &glyph_transform );
          }
          y1  = (int)(v.y + 0.5);
    
          v.x = 0;
          if ( !ps_debug_no_horz_hints )
          {
            v.y = zone->cur_ref;
            nv_vector_transform( &v, &size_transform );
          }
          else
          {
            v.y = zone->org_ref;
            nv_vector_transform( &v, &glyph_transform );
          }
          y2 = (int)(v.y + 0.5);
    
          nv_pixmap_fill_rect( target, 0, y1,
                               target->width, y2-y1+1,
                               BLUES_TOP_COLOR );
    
    #if 0
          printf( "top [%.3f %.3f]\n", zone->cur_bottom/64.0, zone->cur_top/64.0 );
    #endif
        }
    
    
        /* draw bottom zones */
        table = &blues->normal_bottom;
        count = table->count;
        zone  = table->zones;
    
        for ( ; count > 0; count--, zone++ )
        {
          v.x = 0;
          v.y = zone->cur_ref;
          nv_vector_transform( &v, &size_transform );
          y1  = (int)(v.y + 0.5);
    
          v.x = 0;
          v.y = zone->cur_ref + zone->cur_delta;
          nv_vector_transform( &v, &size_transform );
          y2 = (int)(v.y + 0.5);
    
          nv_pixmap_fill_rect( target, 0, y1,
                               target->width, y2-y1+1,
                               BLUES_BOT_COLOR );
    
    #if 0
          printf( "bot [%.3f %.3f]\n", zone->cur_bottom/64.0, zone->cur_top/64.0 );
    #endif
        }
      }
    }
    
     /************************************************************************/
     /************************************************************************/
     /*****                                                              *****/
     /*****            POSTSCRIPT HINTER ALGORITHM 1 ROUTINES            *****/
     /*****                                                              *****/
     /************************************************************************/
     /************************************************************************/
    
    #include <../src/pshinter/pshalgo1.h>
    
    static int pshint_cpos     = 0;
    static int pshint_vertical = -1;
    
    static void
    draw_ps1_hint( PSH1_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,
                             psh1_hint_is_ghost(hint)
                             ? GHOST_HINT_COLOR : STEM_HINT_COLOR );
    
        if ( psh1_hint_is_ghost(hint) )
        {
          x1 --;
          x2 = x1 + 2;
        }
        else
          nv_pixmap_fill_rect( target, x2, 0, 1, target->height,
                               psh1_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,
                             psh1_hint_is_ghost(hint)
                             ? GHOST_HINT_COLOR : STEM_HINT_COLOR );
    
        if ( psh1_hint_is_ghost(hint) )
        {
          x1 --;
          x2 = x1 + 2;
        }
        else
          nv_pixmap_fill_rect( target, 0, x2, target->width, 1,
                               psh1_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;
    }
    
    
    
     /************************************************************************/
     /************************************************************************/
     /*****                                                              *****/
     /*****            POSTSCRIPT HINTER ALGORITHM 2 ROUTINES            *****/
     /*****                                                              *****/
     /************************************************************************/
     /************************************************************************/
    
    #include <../src/pshinter/pshalgo2.h>
    
    static void
    draw_ps2_hint( PSH2_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,
                             psh2_hint_is_ghost(hint)
                             ? GHOST_HINT_COLOR : STEM_HINT_COLOR );
    
        if ( psh2_hint_is_ghost(hint) )
        {
          x1 --;
          x2 = x1 + 2;
        }
        else
          nv_pixmap_fill_rect( target, x2, 0, 1, target->height,
                               psh2_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,
                             psh2_hint_is_ghost(hint)
                             ? GHOST_HINT_COLOR : STEM_HINT_COLOR );
    
        if ( psh2_hint_is_ghost(hint) )
        {
          x1 --;
          x2 = x1 + 2;
        }
        else
          nv_pixmap_fill_rect( target, 0, x2, target->width, 1,
                               psh2_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
    ps2_draw_control_points( void )
    {
      if ( ps2_debug_glyph )
      {
        PSH2_Glyph    glyph = ps2_debug_glyph;
        PSH2_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 && !psh2_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 & PSH2_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 & PSH2_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 )
      {
        FT_Int         dimension;
        PSH_Dimension  dim;
    
        for ( dimension = 1; dimension >= 0; dimension-- )
        {
          PS_Dimension  dim   = &ps_debug_hints->dimension[ dimension ];
          PS_Mask       mask  = dim->masks.masks;
          FT_UInt       count = dim->masks.num_masks;
    
          printf( "%s hints -------------------------\n",
                  dimension ? "vertical" : "horizontal" );
    
          for ( ; count > 0; count--, mask++ )
          {
            FT_UInt  index;
    
            printf( "mask -> %d\n", mask->end_point );
            for ( index = 0; index < mask->num_bits; index++ )
            {
              if ( mask->bytes[ index >> 3 ] & (0x80 >> (index & 7)) )
              {
                PS_Hint  hint = dim->hints.hints + index;
    
                printf( "%c [%3d %3d (%4d)]\n", dimension ? "v" : "h",
                        hint->pos, hint->pos + hint->len, hint->len );
              }
            }
          }
        }
      }
    }
    
     /************************************************************************/
     /************************************************************************/
     /*****                                                              *****/
     /*****            AUTOHINTER DRAWING ROUTINES                       *****/
     /*****                                                              *****/
     /************************************************************************/
     /************************************************************************/
    
    static NV_Path
    ah_link_path( NV_Vector*   p1,
                  NV_Vector*   p4,
                  NV_Bool      vertical )
    {
      NV_PathWriter  writer;
      NV_Vector      p2, p3;
      NV_Path        path, stroke;
    
      if ( vertical )
      {
        p2.x = p4->x;
        p2.y = p1->y;
    
        p3.x = p1->x;
        p3.y = p4->y;
      }
      else
      {
        p2.x = p1->x;
        p2.y = p4->y;
    
        p3.x = p4->x;
        p3.y = p1->y;
      }
    
      nv_path_writer_new( renderer, &writer );
      nv_path_writer_moveto( writer, p1 );
      nv_path_writer_cubicto( writer, &p2, &p3, p4 );
      nv_path_writer_end( writer );
    
      path = nv_path_writer_get_path( writer );
      nv_path_writer_destroy( writer );
    
      nv_path_stroke( path, 1., nv_path_linecap_butt, nv_path_linejoin_round, 1.,  &stroke );
    
      nv_path_destroy( path );
    
      return stroke;
    }
    
    
    static void
    ah_draw_smooth_points( void )
    {
      if ( ah_debug_hinter && option_show_smooth )
      {
        AH_Outline*  glyph = ah_debug_hinter->glyph;
        FT_UInt      count = glyph->num_points;
        AH_Point*    point = glyph->points;
    
        nv_painter_set_color( painter, SMOOTH_COLOR, 256 );
    
        for ( ; count > 0; count--, point++ )
        {
          if ( !( point->flags & ah_flag_weak_interpolation ) )
          {
            NV_Transform  transform, *trans = &transform;
            NV_Vector     vec;
    
            vec.x = point->x - ah_debug_hinter->pp1.x;
            vec.y = point->y;
            nv_vector_transform( &vec, &size_transform );
    
            nv_transform_set_translate( &transform, vec.x, vec.y );
            nv_painter_fill_path( painter, trans, 0, symbol_circle );
          }
        }
      }
    }
    
    
    static void
    ah_draw_edges( void )
    {
      if ( ah_debug_hinter )
      {
        AH_Outline*  glyph = ah_debug_hinter->glyph;
        FT_UInt      count;
        AH_Edge*     edge;
        FT_Pos       pp1 = ah_debug_hinter->pp1.x;
    
        nv_painter_set_color( painter, EDGE_COLOR, 256 );
    
        if ( option_show_edges )
        {
          /* draw verticla edges */
          if ( option_show_vert_hints )
          {
            count = glyph->num_vedges;
            edge  = glyph->vert_edges;
            for ( ; count > 0; count--, edge++ )
            {
              NV_Vector     vec;
              NV_Pos        x;
    
              vec.x = edge->pos - pp1;
              vec.y = 0;
    
              nv_vector_transform( &vec, &size_transform );
              x = (FT_Pos)( vec.x + 0.5 );
    
              nv_pixmap_fill_rect( target, x, 0, 1, target->height, EDGE_COLOR );
            }
          }
    
          /* draw horizontal edges */
          if ( option_show_horz_hints )
          {
            count = glyph->num_hedges;
            edge  = glyph->horz_edges;
            for ( ; count > 0; count--, edge++ )
            {
              NV_Vector     vec;
              NV_Pos        x;
    
              vec.x = 0;
              vec.y = edge->pos;
    
              nv_vector_transform( &vec, &size_transform );
              x = (FT_Pos)( vec.y + 0.5 );
    
              nv_pixmap_fill_rect( target, 0, x, target->width, 1, EDGE_COLOR );
            }
          }
        }
    
        if ( option_show_segments )
        {
          /* draw vertical segments */
          if ( option_show_vert_hints )
          {
            AH_Segment*  seg   = glyph->vert_segments;
            FT_UInt      count = glyph->num_vsegments;
    
            for ( ; count > 0; count--, seg++ )
            {
              AH_Point  *first, *last;
              NV_Vector  v1, v2;
              NV_Pos     y1, y2, x;
    
              first = seg->first;
              last  = seg->last;
    
              v1.x = v2.x = first->x - pp1;
    
              if ( first->y <= last->y )
              {
                v1.y = first->y;
                v2.y = last->y;
              }
              else
              {
                v1.y = last->y;
                v2.y = first->y;
              }
    
              nv_vector_transform( &v1, &size_transform );
              nv_vector_transform( &v2, &size_transform );
    
              y1 = (NV_Pos)( v1.y + 0.5 );
              y2 = (NV_Pos)( v2.y + 0.5 );
              x  = (NV_Pos)( v1.x + 0.5 );
    
              nv_pixmap_fill_rect( target, x-1, y2, 3, ABS(y1-y2)+1, SEGMENT_COLOR );
            }
          }
    
          /* draw horizontal segments */
          if ( option_show_horz_hints )
          {
            AH_Segment*  seg   = glyph->horz_segments;
            FT_UInt      count = glyph->num_hsegments;
    
            for ( ; count > 0; count--, seg++ )
            {
              AH_Point  *first, *last;
              NV_Vector  v1, v2;
              NV_Pos     y1, y2, x;
    
              first = seg->first;
              last  = seg->last;
    
              v1.y = v2.y = first->y;
    
              if ( first->x <= last->x )
              {
                v1.x = first->x - pp1;
                v2.x = last->x - pp1;
              }
              else
              {
                v1.x = last->x - pp1;
                v2.x = first->x - pp1;
              }
    
              nv_vector_transform( &v1, &size_transform );
              nv_vector_transform( &v2, &size_transform );
    
              y1 = (NV_Pos)( v1.x + 0.5 );
              y2 = (NV_Pos)( v2.x + 0.5 );
              x  = (NV_Pos)( v1.y + 0.5 );
    
              nv_pixmap_fill_rect( target, y1, x-1, ABS(y1-y2)+1, 3, SEGMENT_COLOR );
            }
          }
    
    
          if ( option_show_vert_hints && option_show_links )
          {
            AH_Segment*  seg   = glyph->vert_segments;
            FT_UInt      count = glyph->num_vsegments;
    
            for ( ; count > 0; count--, seg++ )
            {
              AH_Segment*  seg2 = NULL;
              NV_Path      link;
              NV_Vector    v1, v2;
    
              if ( seg->link )
              {
                if ( seg->link > seg )
                  seg2 = seg->link;
              }
              else if ( seg->serif )
                seg2 = seg->serif;
    
              if ( seg2 )
              {
                v1.x = seg->first->x  - pp1;
                v2.x = seg2->first->x - pp1;
                v1.y = (seg->first->y + seg->last->y)/2;
                v2.y = (seg2->first->y + seg2->last->y)/2;
    
                link = ah_link_path( &v1, &v2, 1 );
    
                nv_painter_set_color( painter, seg->serif ? SERIF_LINK_COLOR : LINK_COLOR, 256 );
                nv_painter_fill_path( painter, &size_transform, 0, link );
    
                nv_path_destroy( link );
              }
            }
          }
    
          if ( option_show_horz_hints && option_show_links )
          {
            AH_Segment*  seg   = glyph->horz_segments;
            FT_UInt      count = glyph->num_hsegments;
    
            for ( ; count > 0; count--, seg++ )
            {
              AH_Segment*  seg2 = NULL;
              NV_Path      link;
              NV_Vector    v1, v2;
    
              if ( seg->link )
              {
                if ( seg->link > seg )
                  seg2 = seg->link;
              }
              else if ( seg->serif )
                seg2 = seg->serif;
    
              if ( seg2 )
              {
                v1.y = seg->first->y;
                v2.y = seg2->first->y;
                v1.x = (seg->first->x + seg->last->x)/2 - pp1;
                v2.x = (seg2->first->x + seg2->last->x)/2 - pp1;
    
                link = ah_link_path( &v1, &v2, 0 );
    
                nv_painter_set_color( painter, seg->serif ? SERIF_LINK_COLOR : LINK_COLOR, 256 );
                nv_painter_fill_path( painter, &size_transform, 0, link );
    
                nv_path_destroy( link );
              }
            }
          }
        }
      }
    }
    
     /************************************************************************/
     /************************************************************************/
     /*****                                                              *****/
     /*****                        MAIN LOOP(S)                          *****/
     /*****                                                              *****/
     /************************************************************************/
     /************************************************************************/
    
    static void
    draw_glyph( int  glyph_index )
    {
      NV_Path   path;
    
      pshint_vertical    = -1;
    
      ps1_debug_hint_func = option_show_ps_hints ? draw_ps1_hint : 0;
      ps2_debug_hint_func = option_show_ps_hints ? draw_ps2_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 );
      if (error) Panic( "could not load glyph" );
    
      if ( face->glyph->format != ft_glyph_format_outline )
        Panic( "could not load glyph outline" );
    
      error = nv_path_new_from_outline( renderer,
                                        (NV_Outline*)&face->glyph->outline,
                                        &size_transform,
                                        &path );
      if (error) Panic( "could not create glyph path" );
    
      /* trac