misc/libfreetype/src/pshinter/pshrec.c
author belphegorr <szabibibi@gmail.com>
Mon, 23 Jul 2012 19:15:59 +0300
changeset 7263 644eabbc9218
parent 5172 88f2e05288ba
permissions -rw-r--r--
Added a new function: AddNewEvent, which only adds an event to the list if it doesn't already exist. Kept the old one as it might me useful to be able to add an event more than once.

/***************************************************************************/
/*                                                                         */
/*  pshrec.c                                                               */
/*                                                                         */
/*    FreeType PostScript hints recorder (body).                           */
/*                                                                         */
/*  Copyright 2001, 2002, 2003, 2004, 2007, 2009 by                        */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_INTERNAL_OBJECTS_H
#include FT_INTERNAL_DEBUG_H
#include FT_INTERNAL_CALC_H

#include "pshrec.h"
#include "pshalgo.h"

#include "pshnterr.h"

#undef  FT_COMPONENT
#define FT_COMPONENT  trace_pshrec

#ifdef DEBUG_HINTER
  PS_Hints  ps_debug_hints         = 0;
  int       ps_debug_no_horz_hints = 0;
  int       ps_debug_no_vert_hints = 0;
#endif


  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****                      PS_HINT MANAGEMENT                       *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/

  /* destroy hints table */
  static void
  ps_hint_table_done( PS_Hint_Table  table,
                      FT_Memory      memory )
  {
    FT_FREE( table->hints );
    table->num_hints = 0;
    table->max_hints = 0;
  }


  /* ensure that a table can contain "count" elements */
  static FT_Error
  ps_hint_table_ensure( PS_Hint_Table  table,
                        FT_UInt        count,
                        FT_Memory      memory )
  {
    FT_UInt   old_max = table->max_hints;
    FT_UInt   new_max = count;
    FT_Error  error   = PSH_Err_Ok;


    if ( new_max > old_max )
    {
      /* try to grow the table */
      new_max = FT_PAD_CEIL( new_max, 8 );
      if ( !FT_RENEW_ARRAY( table->hints, old_max, new_max ) )
        table->max_hints = new_max;
    }
    return error;
  }


  static FT_Error
  ps_hint_table_alloc( PS_Hint_Table  table,
                       FT_Memory      memory,
                       PS_Hint       *ahint )
  {
    FT_Error  error = PSH_Err_Ok;
    FT_UInt   count;
    PS_Hint   hint = 0;


    count = table->num_hints;
    count++;

    if ( count >= table->max_hints )
    {
      error = ps_hint_table_ensure( table, count, memory );
      if ( error )
        goto Exit;
    }

    hint        = table->hints + count - 1;
    hint->pos   = 0;
    hint->len   = 0;
    hint->flags = 0;

    table->num_hints = count;

  Exit:
    *ahint = hint;
    return error;
  }


  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****                      PS_MASK MANAGEMENT                       *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/

  /* destroy mask */
  static void
  ps_mask_done( PS_Mask    mask,
                FT_Memory  memory )
  {
    FT_FREE( mask->bytes );
    mask->num_bits  = 0;
    mask->max_bits  = 0;
    mask->end_point = 0;
  }


  /* ensure that a mask can contain "count" bits */
  static FT_Error
  ps_mask_ensure( PS_Mask    mask,
                  FT_UInt    count,
                  FT_Memory  memory )
  {
    FT_UInt   old_max = ( mask->max_bits + 7 ) >> 3;
    FT_UInt   new_max = ( count          + 7 ) >> 3;
    FT_Error  error   = PSH_Err_Ok;


    if ( new_max > old_max )
    {
      new_max = FT_PAD_CEIL( new_max, 8 );
      if ( !FT_RENEW_ARRAY( mask->bytes, old_max, new_max ) )
        mask->max_bits = new_max * 8;
    }
    return error;
  }


  /* test a bit value in a given mask */
  static FT_Int
  ps_mask_test_bit( PS_Mask  mask,
                    FT_Int   idx )
  {
    if ( (FT_UInt)idx >= mask->num_bits )
      return 0;

    return mask->bytes[idx >> 3] & ( 0x80 >> ( idx & 7 ) );
  }


  /* clear a given bit */
  static void
  ps_mask_clear_bit( PS_Mask  mask,
                     FT_Int   idx )
  {
    FT_Byte*  p;


    if ( (FT_UInt)idx >= mask->num_bits )
      return;

    p    = mask->bytes + ( idx >> 3 );
    p[0] = (FT_Byte)( p[0] & ~( 0x80 >> ( idx & 7 ) ) );
  }


  /* set a given bit, possibly grow the mask */
  static FT_Error
  ps_mask_set_bit( PS_Mask    mask,
                   FT_Int     idx,
                   FT_Memory  memory )
  {
    FT_Error  error = PSH_Err_Ok;
    FT_Byte*  p;


    if ( idx < 0 )
      goto Exit;

    if ( (FT_UInt)idx >= mask->num_bits )
    {
      error = ps_mask_ensure( mask, idx + 1, memory );
      if ( error )
        goto Exit;

      mask->num_bits = idx + 1;
    }

    p    = mask->bytes + ( idx >> 3 );
    p[0] = (FT_Byte)( p[0] | ( 0x80 >> ( idx & 7 ) ) );

  Exit:
    return error;
  }


  /* destroy mask table */
  static void
  ps_mask_table_done( PS_Mask_Table  table,
                      FT_Memory      memory )
  {
    FT_UInt  count = table->max_masks;
    PS_Mask  mask  = table->masks;


    for ( ; count > 0; count--, mask++ )
      ps_mask_done( mask, memory );

    FT_FREE( table->masks );
    table->num_masks = 0;
    table->max_masks = 0;
  }


  /* ensure that a mask table can contain "count" masks */
  static FT_Error
  ps_mask_table_ensure( PS_Mask_Table  table,
                        FT_UInt        count,
                        FT_Memory      memory )
  {
    FT_UInt   old_max = table->max_masks;
    FT_UInt   new_max = count;
    FT_Error  error   = PSH_Err_Ok;


    if ( new_max > old_max )
    {
      new_max = FT_PAD_CEIL( new_max, 8 );
      if ( !FT_RENEW_ARRAY( table->masks, old_max, new_max ) )
        table->max_masks = new_max;
    }
    return error;
  }


  /* allocate a new mask in a table */
  static FT_Error
  ps_mask_table_alloc( PS_Mask_Table  table,
                       FT_Memory      memory,
                       PS_Mask       *amask )
  {
    FT_UInt   count;
    FT_Error  error = PSH_Err_Ok;
    PS_Mask   mask  = 0;


    count = table->num_masks;
    count++;

    if ( count > table->max_masks )
    {
      error = ps_mask_table_ensure( table, count, memory );
      if ( error )
        goto Exit;
    }

    mask             = table->masks + count - 1;
    mask->num_bits   = 0;
    mask->end_point  = 0;
    table->num_masks = count;

  Exit:
    *amask = mask;
    return error;
  }


  /* return last hint mask in a table, create one if the table is empty */
  static FT_Error
  ps_mask_table_last( PS_Mask_Table  table,
                      FT_Memory      memory,
                      PS_Mask       *amask )
  {
    FT_Error  error = PSH_Err_Ok;
    FT_UInt   count;
    PS_Mask   mask;


    count = table->num_masks;
    if ( count == 0 )
    {
      error = ps_mask_table_alloc( table, memory, &mask );
      if ( error )
        goto Exit;
    }
    else
      mask = table->masks + count - 1;

  Exit:
    *amask = mask;
    return error;
  }


  /* set a new mask to a given bit range */
  static FT_Error
  ps_mask_table_set_bits( PS_Mask_Table   table,
                          const FT_Byte*  source,
                          FT_UInt         bit_pos,
                          FT_UInt         bit_count,
                          FT_Memory       memory )
  {
    FT_Error  error = PSH_Err_Ok;
    PS_Mask   mask;


    error = ps_mask_table_last( table, memory, &mask );
    if ( error )
      goto Exit;

    error = ps_mask_ensure( mask, bit_count, memory );
    if ( error )
      goto Exit;

    mask->num_bits = bit_count;

    /* now, copy bits */
    {
      FT_Byte*  read  = (FT_Byte*)source + ( bit_pos >> 3 );
      FT_Int    rmask = 0x80 >> ( bit_pos & 7 );
      FT_Byte*  write = mask->bytes;
      FT_Int    wmask = 0x80;
      FT_Int    val;


      for ( ; bit_count > 0; bit_count-- )
      {
        val = write[0] & ~wmask;

        if ( read[0] & rmask )
          val |= wmask;

        write[0] = (FT_Byte)val;

        rmask >>= 1;
        if ( rmask == 0 )
        {
          read++;
          rmask = 0x80;
        }

        wmask >>= 1;
        if ( wmask == 0 )
        {
          write++;
          wmask = 0x80;
        }
      }
    }

  Exit:
    return error;
  }


  /* test whether two masks in a table intersect */
  static FT_Int
  ps_mask_table_test_intersect( PS_Mask_Table  table,
                                FT_Int         index1,
                                FT_Int         index2 )
  {
    PS_Mask   mask1  = table->masks + index1;
    PS_Mask   mask2  = table->masks + index2;
    FT_Byte*  p1     = mask1->bytes;
    FT_Byte*  p2     = mask2->bytes;
    FT_UInt   count1 = mask1->num_bits;
    FT_UInt   count2 = mask2->num_bits;
    FT_UInt   count;


    count = ( count1 <= count2 ) ? count1 : count2;
    for ( ; count >= 8; count -= 8 )
    {
      if ( p1[0] & p2[0] )
        return 1;

      p1++;
      p2++;
    }

    if ( count == 0 )
      return 0;

    return ( p1[0] & p2[0] ) & ~( 0xFF >> count );
  }


  /* merge two masks, used by ps_mask_table_merge_all */
  static FT_Error
  ps_mask_table_merge( PS_Mask_Table  table,
                       FT_Int         index1,
                       FT_Int         index2,
                       FT_Memory      memory )
  {
    FT_UInt   temp;
    FT_Error  error = PSH_Err_Ok;


    /* swap index1 and index2 so that index1 < index2 */
    if ( index1 > index2 )
    {
      temp   = index1;
      index1 = index2;
      index2 = temp;
    }

    if ( index1 < index2 && index1 >= 0 && index2 < (FT_Int)table->num_masks )
    {
      /* we need to merge the bitsets of index1 and index2 with a */
      /* simple union                                             */
      PS_Mask  mask1  = table->masks + index1;
      PS_Mask  mask2  = table->masks + index2;
      FT_UInt  count1 = mask1->num_bits;
      FT_UInt  count2 = mask2->num_bits;
      FT_Int   delta;


      if ( count2 > 0 )
      {
        FT_UInt   pos;
        FT_Byte*  read;
        FT_Byte*  write;


        /* if "count2" is greater than "count1", we need to grow the */
        /* first bitset, and clear the highest bits                  */
        if ( count2 > count1 )
        {
          error = ps_mask_ensure( mask1, count2, memory );
          if ( error )
            goto Exit;

          for ( pos = count1; pos < count2; pos++ )
            ps_mask_clear_bit( mask1, pos );
        }

        /* merge (unite) the bitsets */
        read  = mask2->bytes;
        write = mask1->bytes;
        pos   = (FT_UInt)( ( count2 + 7 ) >> 3 );

        for ( ; pos > 0; pos-- )
        {
          write[0] = (FT_Byte)( write[0] | read[0] );
          write++;
          read++;
        }
      }

      /* Now, remove "mask2" from the list.  We need to keep the masks */
      /* sorted in order of importance, so move table elements.        */
      mask2->num_bits  = 0;
      mask2->end_point = 0;

      delta = table->num_masks - 1 - index2; /* number of masks to move */
      if ( delta > 0 )
      {
        /* move to end of table for reuse */
        PS_MaskRec  dummy = *mask2;


        ft_memmove( mask2, mask2 + 1, delta * sizeof ( PS_MaskRec ) );

        mask2[delta] = dummy;
      }

      table->num_masks--;
    }
    else
      FT_TRACE0(( "ps_mask_table_merge: ignoring invalid indices (%d,%d)\n",
                  index1, index2 ));

  Exit:
    return error;
  }


  /* Try to merge all masks in a given table.  This is used to merge */
  /* all counter masks into independent counter "paths".             */
  /*                                                                 */
  static FT_Error
  ps_mask_table_merge_all( PS_Mask_Table  table,
                           FT_Memory      memory )
  {
    FT_Int    index1, index2;
    FT_Error  error = PSH_Err_Ok;


    for ( index1 = table->num_masks - 1; index1 > 0; index1-- )
    {
      for ( index2 = index1 - 1; index2 >= 0; index2-- )
      {
        if ( ps_mask_table_test_intersect( table, index1, index2 ) )
        {
          error = ps_mask_table_merge( table, index2, index1, memory );
          if ( error )
            goto Exit;

          break;
        }
      }
    }

  Exit:
    return error;
  }


  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****                    PS_DIMENSION MANAGEMENT                    *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/


  /* finalize a given dimension */
  static void
  ps_dimension_done( PS_Dimension  dimension,
                     FT_Memory     memory )
  {
    ps_mask_table_done( &dimension->counters, memory );
    ps_mask_table_done( &dimension->masks,    memory );
    ps_hint_table_done( &dimension->hints,    memory );
  }


  /* initialize a given dimension */
  static void
  ps_dimension_init( PS_Dimension  dimension )
  {
    dimension->hints.num_hints    = 0;
    dimension->masks.num_masks    = 0;
    dimension->counters.num_masks = 0;
  }


#if 0

  /* set a bit at a given index in the current hint mask */
  static FT_Error
  ps_dimension_set_mask_bit( PS_Dimension  dim,
                             FT_UInt       idx,
                             FT_Memory     memory )
  {
    PS_Mask   mask;
    FT_Error  error = PSH_Err_Ok;


    /* get last hint mask */
    error = ps_mask_table_last( &dim->masks, memory, &mask );
    if ( error )
      goto Exit;

    error = ps_mask_set_bit( mask, idx, memory );

  Exit:
    return error;
  }

#endif

  /* set the end point in a mask, called from "End" & "Reset" methods */
  static void
  ps_dimension_end_mask( PS_Dimension  dim,
                         FT_UInt       end_point )
  {
    FT_UInt  count = dim->masks.num_masks;
    PS_Mask  mask;


    if ( count > 0 )
    {
      mask            = dim->masks.masks + count - 1;
      mask->end_point = end_point;
    }
  }


  /* set the end point in the current mask, then create a new empty one */
  /* (called by "Reset" method)                                         */
  static FT_Error
  ps_dimension_reset_mask( PS_Dimension  dim,
                           FT_UInt       end_point,
                           FT_Memory     memory )
  {
    PS_Mask  mask;


    /* end current mask */
    ps_dimension_end_mask( dim, end_point );

    /* allocate new one */
    return ps_mask_table_alloc( &dim->masks, memory, &mask );
  }


  /* set a new mask, called from the "T2Stem" method */
  static FT_Error
  ps_dimension_set_mask_bits( PS_Dimension    dim,
                              const FT_Byte*  source,
                              FT_UInt         source_pos,
                              FT_UInt         source_bits,
                              FT_UInt         end_point,
                              FT_Memory       memory )
  {
    FT_Error  error = PSH_Err_Ok;


    /* reset current mask, if any */
    error = ps_dimension_reset_mask( dim, end_point, memory );
    if ( error )
      goto Exit;

    /* set bits in new mask */
    error = ps_mask_table_set_bits( &dim->masks, source,
                                    source_pos, source_bits, memory );

  Exit:
    return error;
  }


  /* add a new single stem (called from "T1Stem" method) */
  static FT_Error
  ps_dimension_add_t1stem( PS_Dimension  dim,
                           FT_Int        pos,
                           FT_Int        len,
                           FT_Memory     memory,
                           FT_Int       *aindex )
  {
    FT_Error  error = PSH_Err_Ok;
    FT_UInt   flags = 0;


    /* detect ghost stem */
    if ( len < 0 )
    {
      flags |= PS_HINT_FLAG_GHOST;
      if ( len == -21 )
      {
        flags |= PS_HINT_FLAG_BOTTOM;
        pos   += len;
      }
      len = 0;
    }

    if ( aindex )
      *aindex = -1;

    /* now, lookup stem in the current hints table */
    {
      PS_Mask  mask;
      FT_UInt  idx;
      FT_UInt  max   = dim->hints.num_hints;
      PS_Hint  hint  = dim->hints.hints;


      for ( idx = 0; idx < max; idx++, hint++ )
      {
        if ( hint->pos == pos && hint->len == len )
          break;
      }

      /* we need to create a new hint in the table */
      if ( idx >= max )
      {
        error = ps_hint_table_alloc( &dim->hints, memory, &hint );
        if ( error )
          goto Exit;

        hint->pos   = pos;
        hint->len   = len;
        hint->flags = flags;
      }

      /* now, store the hint in the current mask */
      error = ps_mask_table_last( &dim->masks, memory, &mask );
      if ( error )
        goto Exit;

      error = ps_mask_set_bit( mask, idx, memory );
      if ( error )
        goto Exit;

      if ( aindex )
        *aindex = (FT_Int)idx;
    }

  Exit:
    return error;
  }


  /* add a "hstem3/vstem3" counter to our dimension table */
  static FT_Error
  ps_dimension_add_counter( PS_Dimension  dim,
                            FT_Int        hint1,
                            FT_Int        hint2,
                            FT_Int        hint3,
                            FT_Memory     memory )
  {
    FT_Error  error   = PSH_Err_Ok;
    FT_UInt   count   = dim->counters.num_masks;
    PS_Mask   counter = dim->counters.masks;


    /* try to find an existing counter mask that already uses */
    /* one of these stems here                                */
    for ( ; count > 0; count--, counter++ )
    {
      if ( ps_mask_test_bit( counter, hint1 ) ||
           ps_mask_test_bit( counter, hint2 ) ||
           ps_mask_test_bit( counter, hint3 ) )
        break;
    }

    /* create a new counter when needed */
    if ( count == 0 )
    {
      error = ps_mask_table_alloc( &dim->counters, memory, &counter );
      if ( error )
        goto Exit;
    }

    /* now, set the bits for our hints in the counter mask */
    error = ps_mask_set_bit( counter, hint1, memory );
    if ( error )
      goto Exit;

    error = ps_mask_set_bit( counter, hint2, memory );
    if ( error )
      goto Exit;

    error = ps_mask_set_bit( counter, hint3, memory );
    if ( error )
      goto Exit;

  Exit:
    return error;
  }


  /* end of recording session for a given dimension */
  static FT_Error
  ps_dimension_end( PS_Dimension  dim,
                    FT_UInt       end_point,
                    FT_Memory     memory )
  {
    /* end hint mask table */
    ps_dimension_end_mask( dim, end_point );

    /* merge all counter masks into independent "paths" */
    return ps_mask_table_merge_all( &dim->counters, memory );
  }


  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****                    PS_RECORDER MANAGEMENT                     *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/


  /* destroy hints */
  FT_LOCAL( void )
  ps_hints_done( PS_Hints  hints )
  {
    FT_Memory  memory = hints->memory;


    ps_dimension_done( &hints->dimension[0], memory );
    ps_dimension_done( &hints->dimension[1], memory );

    hints->error  = PSH_Err_Ok;
    hints->memory = 0;
  }


  FT_LOCAL( FT_Error )
  ps_hints_init( PS_Hints   hints,
                 FT_Memory  memory )
  {
    FT_MEM_ZERO( hints, sizeof ( *hints ) );
    hints->memory = memory;
    return PSH_Err_Ok;
  }


  /* initialize a hints for a new session */
  static void
  ps_hints_open( PS_Hints      hints,
                 PS_Hint_Type  hint_type )
  {
    switch ( hint_type )
    {
    case PS_HINT_TYPE_1:
    case PS_HINT_TYPE_2:
      hints->error     = PSH_Err_Ok;
      hints->hint_type = hint_type;

      ps_dimension_init( &hints->dimension[0] );
      ps_dimension_init( &hints->dimension[1] );
      break;

    default:
      hints->error     = PSH_Err_Invalid_Argument;
      hints->hint_type = hint_type;

      FT_TRACE0(( "ps_hints_open: invalid charstring type\n" ));
      break;
    }
  }


  /* add one or more stems to the current hints table */
  static void
  ps_hints_stem( PS_Hints  hints,
                 FT_Int    dimension,
                 FT_UInt   count,
                 FT_Long*  stems )
  {
    if ( !hints->error )
    {
      /* limit "dimension" to 0..1 */
      if ( dimension < 0 || dimension > 1 )
      {
        FT_TRACE0(( "ps_hints_stem: invalid dimension (%d) used\n",
                    dimension ));
        dimension = ( dimension != 0 );
      }

      /* record the stems in the current hints/masks table */
      switch ( hints->hint_type )
      {
      case PS_HINT_TYPE_1:  /* Type 1 "hstem" or "vstem" operator */
      case PS_HINT_TYPE_2:  /* Type 2 "hstem" or "vstem" operator */
        {
          PS_Dimension  dim = &hints->dimension[dimension];


          for ( ; count > 0; count--, stems += 2 )
          {
            FT_Error   error;
            FT_Memory  memory = hints->memory;


            error = ps_dimension_add_t1stem(
                      dim, (FT_Int)stems[0], (FT_Int)stems[1],
                      memory, NULL );
            if ( error )
            {
              FT_ERROR(( "ps_hints_stem: could not add stem"
                         " (%d,%d) to hints table\n", stems[0], stems[1] ));

              hints->error = error;
              return;
            }
          }
          break;
        }

      default:
        FT_TRACE0(( "ps_hints_stem: called with invalid hint type (%d)\n",
                    hints->hint_type ));
        break;
      }
    }
  }


  /* add one Type1 counter stem to the current hints table */
  static void
  ps_hints_t1stem3( PS_Hints   hints,
                    FT_Int     dimension,
                    FT_Fixed*  stems )
  {
    FT_Error  error = PSH_Err_Ok;


    if ( !hints->error )
    {
      PS_Dimension  dim;
      FT_Memory     memory = hints->memory;
      FT_Int        count;
      FT_Int        idx[3];


      /* limit "dimension" to 0..1 */
      if ( dimension < 0 || dimension > 1 )
      {
        FT_TRACE0(( "ps_hints_t1stem3: invalid dimension (%d) used\n",
                    dimension ));
        dimension = ( dimension != 0 );
      }

      dim = &hints->dimension[dimension];

      /* there must be 6 elements in the 'stem' array */
      if ( hints->hint_type == PS_HINT_TYPE_1 )
      {
        /* add the three stems to our hints/masks table */
        for ( count = 0; count < 3; count++, stems += 2 )
        {
          error = ps_dimension_add_t1stem( dim,
                                           (FT_Int)FIXED_TO_INT( stems[0] ),
                                           (FT_Int)FIXED_TO_INT( stems[1] ),
                                           memory, &idx[count] );
          if ( error )
            goto Fail;
        }

        /* now, add the hints to the counters table */
        error = ps_dimension_add_counter( dim, idx[0], idx[1], idx[2],
                                          memory );
        if ( error )
          goto Fail;
      }
      else
      {
        FT_ERROR(( "ps_hints_t1stem3: called with invalid hint type\n" ));
        error = PSH_Err_Invalid_Argument;
        goto Fail;
      }
    }

    return;

  Fail:
    FT_ERROR(( "ps_hints_t1stem3: could not add counter stems to table\n" ));
    hints->error = error;
  }


  /* reset hints (only with Type 1 hints) */
  static void
  ps_hints_t1reset( PS_Hints  hints,
                    FT_UInt   end_point )
  {
    FT_Error  error = PSH_Err_Ok;


    if ( !hints->error )
    {
      FT_Memory  memory = hints->memory;


      if ( hints->hint_type == PS_HINT_TYPE_1 )
      {
        error = ps_dimension_reset_mask( &hints->dimension[0],
                                         end_point, memory );
        if ( error )
          goto Fail;

        error = ps_dimension_reset_mask( &hints->dimension[1],
                                         end_point, memory );
        if ( error )
          goto Fail;
      }
      else
      {
        /* invalid hint type */
        error = PSH_Err_Invalid_Argument;
        goto Fail;
      }
    }
    return;

  Fail:
    hints->error = error;
  }


  /* Type2 "hintmask" operator, add a new hintmask to each direction */
  static void
  ps_hints_t2mask( PS_Hints        hints,
                   FT_UInt         end_point,
                   FT_UInt         bit_count,
                   const FT_Byte*  bytes )
  {
    FT_Error  error;


    if ( !hints->error )
    {
      PS_Dimension  dim    = hints->dimension;
      FT_Memory     memory = hints->memory;
      FT_UInt       count1 = dim[0].hints.num_hints;
      FT_UInt       count2 = dim[1].hints.num_hints;


      /* check bit count; must be equal to current total hint count */
      if ( bit_count !=  count1 + count2 )
      {
        FT_TRACE0(( "ps_hints_t2mask:"
                    " called with invalid bitcount %d (instead of %d)\n",
                   bit_count, count1 + count2 ));

        /* simply ignore the operator */
        return;
      }

      /* set-up new horizontal and vertical hint mask now */
      error = ps_dimension_set_mask_bits( &dim[0], bytes, count2, count1,
                                          end_point, memory );
      if ( error )
        goto Fail;

      error = ps_dimension_set_mask_bits( &dim[1], bytes, 0, count2,
                                          end_point, memory );
      if ( error )
        goto Fail;
    }
    return;

  Fail:
    hints->error = error;
  }


  static void
  ps_hints_t2counter( PS_Hints        hints,
                      FT_UInt         bit_count,
                      const FT_Byte*  bytes )
  {
    FT_Error  error;


    if ( !hints->error )
    {
      PS_Dimension  dim    = hints->dimension;
      FT_Memory     memory = hints->memory;
      FT_UInt       count1 = dim[0].hints.num_hints;
      FT_UInt       count2 = dim[1].hints.num_hints;


      /* check bit count, must be equal to current total hint count */
      if ( bit_count !=  count1 + count2 )
      {
        FT_TRACE0(( "ps_hints_t2counter:"
                    " called with invalid bitcount %d (instead of %d)\n",
                   bit_count, count1 + count2 ));

        /* simply ignore the operator */
        return;
      }

      /* set-up new horizontal and vertical hint mask now */
      error = ps_dimension_set_mask_bits( &dim[0], bytes, 0, count1,
                                          0, memory );
      if ( error )
        goto Fail;

      error = ps_dimension_set_mask_bits( &dim[1], bytes, count1, count2,
                                          0, memory );
      if ( error )
        goto Fail;
    }
    return;

  Fail:
    hints->error = error;
  }


  /* end recording session */
  static FT_Error
  ps_hints_close( PS_Hints  hints,
                  FT_UInt   end_point )
  {
    FT_Error  error;


    error = hints->error;
    if ( !error )
    {
      FT_Memory     memory = hints->memory;
      PS_Dimension  dim    = hints->dimension;


      error = ps_dimension_end( &dim[0], end_point, memory );
      if ( !error )
      {
        error = ps_dimension_end( &dim[1], end_point, memory );
      }
    }

#ifdef DEBUG_HINTER
    if ( !error )
      ps_debug_hints = hints;
#endif
    return error;
  }


  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****                TYPE 1 HINTS RECORDING INTERFACE               *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/

  static void
  t1_hints_open( T1_Hints  hints )
  {
    ps_hints_open( (PS_Hints)hints, PS_HINT_TYPE_1 );
  }

  static void
  t1_hints_stem( T1_Hints   hints,
                 FT_Int     dimension,
                 FT_Fixed*  coords )
  {
    FT_Pos  stems[2];


    stems[0] = FIXED_TO_INT( coords[0] );
    stems[1] = FIXED_TO_INT( coords[1] );

    ps_hints_stem( (PS_Hints)hints, dimension, 1, stems );
  }


  FT_LOCAL_DEF( void )
  t1_hints_funcs_init( T1_Hints_FuncsRec*  funcs )
  {
    FT_MEM_ZERO( (char*)funcs, sizeof ( *funcs ) );

    funcs->open  = (T1_Hints_OpenFunc)    t1_hints_open;
    funcs->close = (T1_Hints_CloseFunc)   ps_hints_close;
    funcs->stem  = (T1_Hints_SetStemFunc) t1_hints_stem;
    funcs->stem3 = (T1_Hints_SetStem3Func)ps_hints_t1stem3;
    funcs->reset = (T1_Hints_ResetFunc)   ps_hints_t1reset;
    funcs->apply = (T1_Hints_ApplyFunc)   ps_hints_apply;
  }


  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****                TYPE 2 HINTS RECORDING INTERFACE               *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/

  static void
  t2_hints_open( T2_Hints  hints )
  {
    ps_hints_open( (PS_Hints)hints, PS_HINT_TYPE_2 );
  }


  static void
  t2_hints_stems( T2_Hints   hints,
                  FT_Int     dimension,
                  FT_Int     count,
                  FT_Fixed*  coords )
  {
    FT_Pos  stems[32], y, n;
    FT_Int  total = count;


    y = 0;
    while ( total > 0 )
    {
      /* determine number of stems to write */
      count = total;
      if ( count > 16 )
        count = 16;

      /* compute integer stem positions in font units */
      for ( n = 0; n < count * 2; n++ )
      {
        y       += coords[n];
        stems[n] = FIXED_TO_INT( y );
      }

      /* compute lengths */
      for ( n = 0; n < count * 2; n += 2 )
        stems[n + 1] = stems[n + 1] - stems[n];

      /* add them to the current dimension */
      ps_hints_stem( (PS_Hints)hints, dimension, count, stems );

      total -= count;
    }
  }


  FT_LOCAL_DEF( void )
  t2_hints_funcs_init( T2_Hints_FuncsRec*  funcs )
  {
    FT_MEM_ZERO( funcs, sizeof ( *funcs ) );

    funcs->open    = (T2_Hints_OpenFunc)   t2_hints_open;
    funcs->close   = (T2_Hints_CloseFunc)  ps_hints_close;
    funcs->stems   = (T2_Hints_StemsFunc)  t2_hints_stems;
    funcs->hintmask= (T2_Hints_MaskFunc)   ps_hints_t2mask;
    funcs->counter = (T2_Hints_CounterFunc)ps_hints_t2counter;
    funcs->apply   = (T2_Hints_ApplyFunc)  ps_hints_apply;
  }


/* END */