(root)/
freetype-2.13.2/
src/
pshinter/
pshrec.c
       1  /****************************************************************************
       2   *
       3   * pshrec.c
       4   *
       5   *   FreeType PostScript hints recorder (body).
       6   *
       7   * Copyright (C) 2001-2023 by
       8   * David Turner, Robert Wilhelm, and Werner Lemberg.
       9   *
      10   * This file is part of the FreeType project, and may only be used,
      11   * modified, and distributed under the terms of the FreeType project
      12   * license, LICENSE.TXT.  By continuing to use, modify, or distribute
      13   * this file you indicate that you have read the license and
      14   * understand and accept it fully.
      15   *
      16   */
      17  
      18  
      19  #include <freetype/freetype.h>
      20  #include <freetype/internal/ftobjs.h>
      21  #include <freetype/internal/ftdebug.h>
      22  #include <freetype/internal/ftcalc.h>
      23  
      24  #include "pshrec.h"
      25  #include "pshalgo.h"
      26  
      27  #include "pshnterr.h"
      28  
      29  #undef  FT_COMPONENT
      30  #define FT_COMPONENT  pshrec
      31  
      32  #ifdef DEBUG_HINTER
      33    PS_Hints  ps_debug_hints         = NULL;
      34    int       ps_debug_no_horz_hints = 0;
      35    int       ps_debug_no_vert_hints = 0;
      36  #endif
      37  
      38  
      39    /*************************************************************************/
      40    /*************************************************************************/
      41    /*****                                                               *****/
      42    /*****                      PS_HINT MANAGEMENT                       *****/
      43    /*****                                                               *****/
      44    /*************************************************************************/
      45    /*************************************************************************/
      46  
      47    /* destroy hints table */
      48    static void
      49    ps_hint_table_done( PS_Hint_Table  table,
      50                        FT_Memory      memory )
      51    {
      52      FT_FREE( table->hints );
      53      table->num_hints = 0;
      54      table->max_hints = 0;
      55    }
      56  
      57  
      58    /* ensure that a table can contain "count" elements */
      59    static FT_Error
      60    ps_hint_table_ensure( PS_Hint_Table  table,
      61                          FT_UInt        count,
      62                          FT_Memory      memory )
      63    {
      64      FT_UInt   old_max = table->max_hints;
      65      FT_UInt   new_max = count;
      66      FT_Error  error;
      67  
      68  
      69      /* try to grow the table */
      70      new_max = FT_PAD_CEIL( new_max, 8 );
      71      if ( !FT_QRENEW_ARRAY( table->hints, old_max, new_max ) )
      72        table->max_hints = new_max;
      73  
      74      return error;
      75    }
      76  
      77  
      78    static FT_Error
      79    ps_hint_table_alloc( PS_Hint_Table  table,
      80                         FT_Memory      memory,
      81                         PS_Hint       *ahint )
      82    {
      83      FT_Error  error = FT_Err_Ok;
      84      FT_UInt   count;
      85      PS_Hint   hint = NULL;
      86  
      87  
      88      count = table->num_hints;
      89      count++;
      90  
      91      if ( count > table->max_hints )
      92      {
      93        error = ps_hint_table_ensure( table, count, memory );
      94        if ( error )
      95          goto Exit;
      96      }
      97  
      98      hint = table->hints + count - 1;  /* initialized upstream */
      99  
     100      table->num_hints = count;
     101  
     102    Exit:
     103      *ahint = hint;
     104      return error;
     105    }
     106  
     107  
     108    /*************************************************************************/
     109    /*************************************************************************/
     110    /*****                                                               *****/
     111    /*****                      PS_MASK MANAGEMENT                       *****/
     112    /*****                                                               *****/
     113    /*************************************************************************/
     114    /*************************************************************************/
     115  
     116    /* destroy mask */
     117    static void
     118    ps_mask_done( PS_Mask    mask,
     119                  FT_Memory  memory )
     120    {
     121      FT_FREE( mask->bytes );
     122      mask->num_bits  = 0;
     123      mask->max_bits  = 0;
     124      mask->end_point = 0;
     125    }
     126  
     127  
     128    /* ensure that a mask can contain "count" bits */
     129    static FT_Error
     130    ps_mask_ensure( PS_Mask    mask,
     131                    FT_UInt    count,
     132                    FT_Memory  memory )
     133    {
     134      FT_UInt   old_max = mask->max_bits >> 3;
     135      FT_UInt   new_max = ( count + 7 ) >> 3;
     136      FT_Error  error   = FT_Err_Ok;
     137  
     138  
     139      if ( new_max > old_max )
     140      {
     141        new_max = FT_PAD_CEIL( new_max, 8 );
     142        /* added bytes are zeroed here */
     143        if ( !FT_RENEW_ARRAY( mask->bytes, old_max, new_max ) )
     144          mask->max_bits = new_max * 8;
     145      }
     146      return error;
     147    }
     148  
     149  
     150    /* test a bit value in a given mask */
     151    static FT_Int
     152    ps_mask_test_bit( PS_Mask  mask,
     153                      FT_UInt  idx )
     154    {
     155      if ( idx >= mask->num_bits )
     156        return 0;
     157  
     158      return mask->bytes[idx >> 3] & ( 0x80 >> ( idx & 7 ) );
     159    }
     160  
     161  
     162    /* set a given bit, possibly grow the mask */
     163    static FT_Error
     164    ps_mask_set_bit( PS_Mask    mask,
     165                     FT_UInt    idx,
     166                     FT_Memory  memory )
     167    {
     168      FT_Error  error = FT_Err_Ok;
     169      FT_Byte*  p;
     170  
     171  
     172      if ( idx >= mask->num_bits )
     173      {
     174        error = ps_mask_ensure( mask, idx + 1, memory );
     175        if ( error )
     176          goto Exit;
     177  
     178        mask->num_bits = idx + 1;
     179      }
     180  
     181      p    = mask->bytes + ( idx >> 3 );
     182      p[0] = (FT_Byte)( p[0] | ( 0x80 >> ( idx & 7 ) ) );
     183  
     184    Exit:
     185      return error;
     186    }
     187  
     188  
     189    /* destroy mask table */
     190    static void
     191    ps_mask_table_done( PS_Mask_Table  table,
     192                        FT_Memory      memory )
     193    {
     194      FT_UInt  count = table->max_masks;
     195      PS_Mask  mask  = table->masks;
     196  
     197  
     198      for ( ; count > 0; count--, mask++ )
     199        ps_mask_done( mask, memory );
     200  
     201      FT_FREE( table->masks );
     202      table->num_masks = 0;
     203      table->max_masks = 0;
     204    }
     205  
     206  
     207    /* ensure that a mask table can contain "count" masks */
     208    static FT_Error
     209    ps_mask_table_ensure( PS_Mask_Table  table,
     210                          FT_UInt        count,
     211                          FT_Memory      memory )
     212    {
     213      FT_UInt   old_max = table->max_masks;
     214      FT_UInt   new_max = count;
     215      FT_Error  error   = FT_Err_Ok;
     216  
     217  
     218      if ( new_max > old_max )
     219      {
     220        new_max = FT_PAD_CEIL( new_max, 8 );
     221        if ( !FT_RENEW_ARRAY( table->masks, old_max, new_max ) )
     222          table->max_masks = new_max;
     223      }
     224      return error;
     225    }
     226  
     227  
     228    /* allocate a new mask in a table */
     229    static FT_Error
     230    ps_mask_table_alloc( PS_Mask_Table  table,
     231                         FT_Memory      memory,
     232                         PS_Mask       *amask )
     233    {
     234      FT_UInt   count;
     235      FT_Error  error = FT_Err_Ok;
     236      PS_Mask   mask  = NULL;
     237  
     238  
     239      count = table->num_masks;
     240      count++;
     241  
     242      if ( count > table->max_masks )
     243      {
     244        error = ps_mask_table_ensure( table, count, memory );
     245        if ( error )
     246          goto Exit;
     247      }
     248  
     249      mask             = table->masks + count - 1;
     250      mask->num_bits   = 0;
     251      mask->end_point  = 0;
     252      /* reused mask must be cleared */
     253      if ( mask->max_bits )
     254        FT_MEM_ZERO( mask->bytes, mask->max_bits >> 3 );
     255  
     256      table->num_masks = count;
     257  
     258    Exit:
     259      *amask = mask;
     260      return error;
     261    }
     262  
     263  
     264    /* return last hint mask in a table, create one if the table is empty */
     265    static FT_Error
     266    ps_mask_table_last( PS_Mask_Table  table,
     267                        FT_Memory      memory,
     268                        PS_Mask       *amask )
     269    {
     270      FT_Error  error = FT_Err_Ok;
     271      FT_UInt   count;
     272      PS_Mask   mask;
     273  
     274  
     275      count = table->num_masks;
     276      if ( count == 0 )
     277      {
     278        error = ps_mask_table_alloc( table, memory, &mask );
     279        if ( error )
     280          goto Exit;
     281      }
     282      else
     283        mask = table->masks + count - 1;
     284  
     285    Exit:
     286      *amask = mask;
     287      return error;
     288    }
     289  
     290  
     291    /* set a new mask to a given bit range */
     292    static FT_Error
     293    ps_mask_table_set_bits( PS_Mask_Table   table,
     294                            const FT_Byte*  source,
     295                            FT_UInt         bit_pos,
     296                            FT_UInt         bit_count,
     297                            FT_Memory       memory )
     298    {
     299      FT_Error  error;
     300      PS_Mask   mask;
     301  
     302  
     303      error = ps_mask_table_last( table, memory, &mask );
     304      if ( error )
     305        goto Exit;
     306  
     307      error = ps_mask_ensure( mask, bit_count, memory );
     308      if ( error )
     309        goto Exit;
     310  
     311      mask->num_bits = bit_count;
     312  
     313      /* now, copy bits */
     314      {
     315        FT_Byte*  read  = (FT_Byte*)source + ( bit_pos >> 3 );
     316        FT_Int    rmask = 0x80 >> ( bit_pos & 7 );
     317        FT_Byte*  write = mask->bytes;
     318        FT_Int    wmask = 0x80;
     319        FT_Int    val;
     320  
     321  
     322        for ( ; bit_count > 0; bit_count-- )
     323        {
     324          val = write[0] & ~wmask;
     325  
     326          if ( read[0] & rmask )
     327            val |= wmask;
     328  
     329          write[0] = (FT_Byte)val;
     330  
     331          rmask >>= 1;
     332          if ( rmask == 0 )
     333          {
     334            read++;
     335            rmask = 0x80;
     336          }
     337  
     338          wmask >>= 1;
     339          if ( wmask == 0 )
     340          {
     341            write++;
     342            wmask = 0x80;
     343          }
     344        }
     345      }
     346  
     347    Exit:
     348      return error;
     349    }
     350  
     351  
     352    /* test whether two masks in a table intersect */
     353    static FT_Int
     354    ps_mask_table_test_intersect( PS_Mask_Table  table,
     355                                  FT_UInt        index1,
     356                                  FT_UInt        index2 )
     357    {
     358      PS_Mask   mask1  = table->masks + index1;
     359      PS_Mask   mask2  = table->masks + index2;
     360      FT_Byte*  p1     = mask1->bytes;
     361      FT_Byte*  p2     = mask2->bytes;
     362      FT_UInt   count1 = mask1->num_bits;
     363      FT_UInt   count2 = mask2->num_bits;
     364      FT_UInt   count;
     365  
     366  
     367      count = FT_MIN( count1, count2 );
     368      for ( ; count >= 8; count -= 8 )
     369      {
     370        if ( p1[0] & p2[0] )
     371          return 1;
     372  
     373        p1++;
     374        p2++;
     375      }
     376  
     377      if ( count == 0 )
     378        return 0;
     379  
     380      return ( p1[0] & p2[0] ) & ~( 0xFF >> count );
     381    }
     382  
     383  
     384    /* merge two masks, used by ps_mask_table_merge_all */
     385    static FT_Error
     386    ps_mask_table_merge( PS_Mask_Table  table,
     387                         FT_UInt        index1,
     388                         FT_UInt        index2,
     389                         FT_Memory      memory )
     390    {
     391      FT_Error  error = FT_Err_Ok;
     392  
     393  
     394      /* swap index1 and index2 so that index1 < index2 */
     395      if ( index1 > index2 )
     396      {
     397        FT_UInt  temp;
     398  
     399  
     400        temp   = index1;
     401        index1 = index2;
     402        index2 = temp;
     403      }
     404  
     405      if ( index1 < index2 && index2 < table->num_masks )
     406      {
     407        /* we need to merge the bitsets of index1 and index2 with a */
     408        /* simple union                                             */
     409        PS_Mask  mask1  = table->masks + index1;
     410        PS_Mask  mask2  = table->masks + index2;
     411        FT_UInt  count1 = mask1->num_bits;
     412        FT_UInt  count2 = mask2->num_bits;
     413        FT_UInt  delta;
     414  
     415  
     416        if ( count2 > 0 )
     417        {
     418          FT_UInt   pos;
     419          FT_Byte*  read;
     420          FT_Byte*  write;
     421  
     422  
     423          /* if "count2" is greater than "count1", we need to grow the */
     424          /* first bitset                                              */
     425          if ( count2 > count1 )
     426          {
     427            error = ps_mask_ensure( mask1, count2, memory );
     428            if ( error )
     429              goto Exit;
     430  
     431            mask1->num_bits = count2;
     432          }
     433  
     434          /* merge (unite) the bitsets */
     435          read  = mask2->bytes;
     436          write = mask1->bytes;
     437          pos   = ( count2 + 7 ) >> 3;
     438  
     439          for ( ; pos > 0; pos-- )
     440          {
     441            write[0] = (FT_Byte)( write[0] | read[0] );
     442            write++;
     443            read++;
     444          }
     445        }
     446  
     447        /* Now, remove "mask2" from the list.  We need to keep the masks */
     448        /* sorted in order of importance, so move table elements.        */
     449        mask2->num_bits  = 0;
     450        mask2->end_point = 0;
     451  
     452        /* number of masks to move */
     453        delta = table->num_masks - 1 - index2;
     454        if ( delta > 0 )
     455        {
     456          /* move to end of table for reuse */
     457          PS_MaskRec  dummy = *mask2;
     458  
     459  
     460          ft_memmove( mask2,
     461                      mask2 + 1,
     462                      delta * sizeof ( PS_MaskRec ) );
     463  
     464          mask2[delta] = dummy;
     465        }
     466  
     467        table->num_masks--;
     468      }
     469      else
     470        FT_TRACE0(( "ps_mask_table_merge: ignoring invalid indices (%d,%d)\n",
     471                    index1, index2 ));
     472  
     473    Exit:
     474      return error;
     475    }
     476  
     477  
     478    /* Try to merge all masks in a given table.  This is used to merge */
     479    /* all counter masks into independent counter "paths".             */
     480    /*                                                                 */
     481    static FT_Error
     482    ps_mask_table_merge_all( PS_Mask_Table  table,
     483                             FT_Memory      memory )
     484    {
     485      FT_UInt   index1, index2;
     486      FT_Error  error = FT_Err_Ok;
     487  
     488  
     489      /* the loops stop when unsigned indices wrap around after 0 */
     490      for ( index1 = table->num_masks - 1; index1 < table->num_masks; index1-- )
     491      {
     492        for ( index2 = index1 - 1; index2 < index1; index2-- )
     493        {
     494          if ( ps_mask_table_test_intersect( table, index1, index2 ) )
     495          {
     496            error = ps_mask_table_merge( table, index2, index1, memory );
     497            if ( error )
     498              goto Exit;
     499  
     500            break;
     501          }
     502        }
     503      }
     504  
     505    Exit:
     506      return error;
     507    }
     508  
     509  
     510    /*************************************************************************/
     511    /*************************************************************************/
     512    /*****                                                               *****/
     513    /*****                    PS_DIMENSION MANAGEMENT                    *****/
     514    /*****                                                               *****/
     515    /*************************************************************************/
     516    /*************************************************************************/
     517  
     518  
     519    /* finalize a given dimension */
     520    static void
     521    ps_dimension_done( PS_Dimension  dimension,
     522                       FT_Memory     memory )
     523    {
     524      ps_mask_table_done( &dimension->counters, memory );
     525      ps_mask_table_done( &dimension->masks,    memory );
     526      ps_hint_table_done( &dimension->hints,    memory );
     527    }
     528  
     529  
     530    /* initialize a given dimension */
     531    static void
     532    ps_dimension_init( PS_Dimension  dimension )
     533    {
     534      dimension->hints.num_hints    = 0;
     535      dimension->masks.num_masks    = 0;
     536      dimension->counters.num_masks = 0;
     537    }
     538  
     539  
     540  #if 0
     541  
     542    /* set a bit at a given index in the current hint mask */
     543    static FT_Error
     544    ps_dimension_set_mask_bit( PS_Dimension  dim,
     545                               FT_UInt       idx,
     546                               FT_Memory     memory )
     547    {
     548      PS_Mask   mask;
     549      FT_Error  error = FT_Err_Ok;
     550  
     551  
     552      /* get last hint mask */
     553      error = ps_mask_table_last( &dim->masks, memory, &mask );
     554      if ( error )
     555        goto Exit;
     556  
     557      error = ps_mask_set_bit( mask, idx, memory );
     558  
     559    Exit:
     560      return error;
     561    }
     562  
     563  #endif
     564  
     565    /* set the end point in a mask, called from "End" & "Reset" methods */
     566    static void
     567    ps_dimension_end_mask( PS_Dimension  dim,
     568                           FT_UInt       end_point )
     569    {
     570      FT_UInt  count = dim->masks.num_masks;
     571  
     572  
     573      if ( count > 0 )
     574      {
     575        PS_Mask  mask = dim->masks.masks + count - 1;
     576  
     577  
     578        mask->end_point = end_point;
     579      }
     580    }
     581  
     582  
     583    /* set the end point in the current mask, then create a new empty one */
     584    /* (called by "Reset" method)                                         */
     585    static FT_Error
     586    ps_dimension_reset_mask( PS_Dimension  dim,
     587                             FT_UInt       end_point,
     588                             FT_Memory     memory )
     589    {
     590      PS_Mask  mask;
     591  
     592  
     593      /* end current mask */
     594      ps_dimension_end_mask( dim, end_point );
     595  
     596      /* allocate new one */
     597      return ps_mask_table_alloc( &dim->masks, memory, &mask );
     598    }
     599  
     600  
     601    /* set a new mask, called from the "T2Stem" method */
     602    static FT_Error
     603    ps_dimension_set_mask_bits( PS_Dimension    dim,
     604                                const FT_Byte*  source,
     605                                FT_UInt         source_pos,
     606                                FT_UInt         source_bits,
     607                                FT_UInt         end_point,
     608                                FT_Memory       memory )
     609    {
     610      FT_Error  error;
     611  
     612  
     613      /* reset current mask, if any */
     614      error = ps_dimension_reset_mask( dim, end_point, memory );
     615      if ( error )
     616        goto Exit;
     617  
     618      /* set bits in new mask */
     619      error = ps_mask_table_set_bits( &dim->masks, source,
     620                                      source_pos, source_bits, memory );
     621  
     622    Exit:
     623      return error;
     624    }
     625  
     626  
     627    /* add a new single stem (called from "T1Stem" method) */
     628    static FT_Error
     629    ps_dimension_add_t1stem( PS_Dimension  dim,
     630                             FT_Int        pos,
     631                             FT_Int        len,
     632                             FT_Memory     memory,
     633                             FT_UInt      *aindex )
     634    {
     635      FT_Error  error = FT_Err_Ok;
     636      FT_UInt   flags = 0;
     637  
     638  
     639      /* detect ghost stem */
     640      if ( len < 0 )
     641      {
     642        flags |= PS_HINT_FLAG_GHOST;
     643        if ( len == -21 )
     644        {
     645          flags |= PS_HINT_FLAG_BOTTOM;
     646          pos    = ADD_INT( pos, len );
     647        }
     648        len = 0;
     649      }
     650  
     651      /* now, lookup stem in the current hints table */
     652      {
     653        PS_Mask  mask;
     654        FT_UInt  idx;
     655        FT_UInt  max  = dim->hints.num_hints;
     656        PS_Hint  hint = dim->hints.hints;
     657  
     658  
     659        for ( idx = 0; idx < max; idx++, hint++ )
     660        {
     661          if ( hint->pos == pos && hint->len == len )
     662            break;
     663        }
     664  
     665        /* we need to create a new hint in the table */
     666        if ( idx >= max )
     667        {
     668          error = ps_hint_table_alloc( &dim->hints, memory, &hint );
     669          if ( error )
     670            goto Exit;
     671  
     672          hint->pos   = pos;
     673          hint->len   = len;
     674          hint->flags = flags;
     675        }
     676  
     677        /* now, store the hint in the current mask */
     678        error = ps_mask_table_last( &dim->masks, memory, &mask );
     679        if ( error )
     680          goto Exit;
     681  
     682        error = ps_mask_set_bit( mask, idx, memory );
     683        if ( error )
     684          goto Exit;
     685  
     686        if ( aindex )
     687          *aindex = idx;
     688      }
     689  
     690    Exit:
     691      return error;
     692    }
     693  
     694  
     695    /* add a "hstem3/vstem3" counter to our dimension table */
     696    static FT_Error
     697    ps_dimension_add_counter( PS_Dimension  dim,
     698                              FT_UInt       hint1,
     699                              FT_UInt       hint2,
     700                              FT_UInt       hint3,
     701                              FT_Memory     memory )
     702    {
     703      FT_Error  error   = FT_Err_Ok;
     704      FT_UInt   count   = dim->counters.num_masks;
     705      PS_Mask   counter = dim->counters.masks;
     706  
     707  
     708      /* try to find an existing counter mask that already uses */
     709      /* one of these stems here                                */
     710      for ( ; count > 0; count--, counter++ )
     711      {
     712        if ( ps_mask_test_bit( counter, hint1 ) ||
     713             ps_mask_test_bit( counter, hint2 ) ||
     714             ps_mask_test_bit( counter, hint3 ) )
     715          break;
     716      }
     717  
     718      /* create a new counter when needed */
     719      if ( count == 0 )
     720      {
     721        error = ps_mask_table_alloc( &dim->counters, memory, &counter );
     722        if ( error )
     723          goto Exit;
     724      }
     725  
     726      /* now, set the bits for our hints in the counter mask */
     727      error = ps_mask_set_bit( counter, hint1, memory );
     728      if ( error )
     729        goto Exit;
     730  
     731      error = ps_mask_set_bit( counter, hint2, memory );
     732      if ( error )
     733        goto Exit;
     734  
     735      error = ps_mask_set_bit( counter, hint3, memory );
     736      if ( error )
     737        goto Exit;
     738  
     739    Exit:
     740      return error;
     741    }
     742  
     743  
     744    /* end of recording session for a given dimension */
     745    static FT_Error
     746    ps_dimension_end( PS_Dimension  dim,
     747                      FT_UInt       end_point,
     748                      FT_Memory     memory )
     749    {
     750      /* end hint mask table */
     751      ps_dimension_end_mask( dim, end_point );
     752  
     753      /* merge all counter masks into independent "paths" */
     754      return ps_mask_table_merge_all( &dim->counters, memory );
     755    }
     756  
     757  
     758    /*************************************************************************/
     759    /*************************************************************************/
     760    /*****                                                               *****/
     761    /*****                    PS_RECORDER MANAGEMENT                     *****/
     762    /*****                                                               *****/
     763    /*************************************************************************/
     764    /*************************************************************************/
     765  
     766  
     767    /* destroy hints */
     768    FT_LOCAL_DEF( void )
     769    ps_hints_done( PS_Hints  hints )
     770    {
     771      FT_Memory  memory = hints->memory;
     772  
     773  
     774      ps_dimension_done( &hints->dimension[0], memory );
     775      ps_dimension_done( &hints->dimension[1], memory );
     776  
     777      hints->error  = FT_Err_Ok;
     778      hints->memory = NULL;
     779    }
     780  
     781  
     782    FT_LOCAL_DEF( void )
     783    ps_hints_init( PS_Hints   hints,
     784                   FT_Memory  memory )
     785    {
     786      FT_ZERO( hints );
     787      hints->memory = memory;
     788    }
     789  
     790  
     791    /* initialize a hints for a new session */
     792    static void
     793    ps_hints_open( PS_Hints      hints,
     794                   PS_Hint_Type  hint_type )
     795    {
     796      hints->error     = FT_Err_Ok;
     797      hints->hint_type = hint_type;
     798  
     799      ps_dimension_init( &hints->dimension[0] );
     800      ps_dimension_init( &hints->dimension[1] );
     801    }
     802  
     803  
     804    /* add one or more stems to the current hints table */
     805    static void
     806    ps_hints_stem( PS_Hints  hints,
     807                   FT_UInt   dimension,
     808                   FT_Int    count,
     809                   FT_Long*  stems )
     810    {
     811      PS_Dimension  dim;
     812  
     813  
     814      if ( hints->error )
     815        return;
     816  
     817      /* limit "dimension" to 0..1 */
     818      if ( dimension > 1 )
     819      {
     820        FT_TRACE0(( "ps_hints_stem: invalid dimension (%d) used\n",
     821                    dimension ));
     822        dimension = ( dimension != 0 );
     823      }
     824  
     825      /* record the stems in the current hints/masks table */
     826      /* (Type 1 & 2's `hstem' or `vstem' operators)       */
     827      dim = &hints->dimension[dimension];
     828  
     829      for ( ; count > 0; count--, stems += 2 )
     830      {
     831        FT_Error   error;
     832        FT_Memory  memory = hints->memory;
     833  
     834  
     835        error = ps_dimension_add_t1stem( dim,
     836                                         (FT_Int)stems[0],
     837                                         (FT_Int)stems[1],
     838                                         memory,
     839                                         NULL );
     840        if ( error )
     841        {
     842          FT_ERROR(( "ps_hints_stem: could not add stem"
     843                     " (%ld,%ld) to hints table\n", stems[0], stems[1] ));
     844  
     845          hints->error = error;
     846          return;
     847        }
     848      }
     849    }
     850  
     851  
     852    /* add one Type1 counter stem to the current hints table */
     853    static void
     854    ps_hints_t1stem3( T1_Hints   hints_,    /* PS_Hints */
     855                      FT_UInt    dimension,
     856                      FT_Fixed*  stems )
     857    {
     858      PS_Hints  hints = (PS_Hints)hints_;
     859      FT_Error  error = FT_Err_Ok;
     860  
     861  
     862      if ( !hints->error )
     863      {
     864        PS_Dimension  dim;
     865        FT_Memory     memory = hints->memory;
     866        FT_Int        count;
     867        FT_UInt       idx[3];
     868  
     869  
     870        /* limit "dimension" to 0..1 */
     871        if ( dimension > 1 )
     872        {
     873          FT_TRACE0(( "ps_hints_t1stem3: invalid dimension (%d) used\n",
     874                      dimension ));
     875          dimension = ( dimension != 0 );
     876        }
     877  
     878        dim = &hints->dimension[dimension];
     879  
     880        /* there must be 6 elements in the 'stem' array */
     881        if ( hints->hint_type == PS_HINT_TYPE_1 )
     882        {
     883          /* add the three stems to our hints/masks table */
     884          for ( count = 0; count < 3; count++, stems += 2 )
     885          {
     886            error = ps_dimension_add_t1stem( dim,
     887                                             (FT_Int)FIXED_TO_INT( stems[0] ),
     888                                             (FT_Int)FIXED_TO_INT( stems[1] ),
     889                                             memory, &idx[count] );
     890            if ( error )
     891              goto Fail;
     892          }
     893  
     894          /* now, add the hints to the counters table */
     895          error = ps_dimension_add_counter( dim, idx[0], idx[1], idx[2],
     896                                            memory );
     897          if ( error )
     898            goto Fail;
     899        }
     900        else
     901        {
     902          FT_ERROR(( "ps_hints_t1stem3: called with invalid hint type\n" ));
     903          error = FT_THROW( Invalid_Argument );
     904          goto Fail;
     905        }
     906      }
     907  
     908      return;
     909  
     910    Fail:
     911      FT_ERROR(( "ps_hints_t1stem3: could not add counter stems to table\n" ));
     912      hints->error = error;
     913    }
     914  
     915  
     916    /* reset hints (only with Type 1 hints) */
     917    static void
     918    ps_hints_t1reset( T1_Hints  hints_,     /* PS_Hints */
     919                      FT_UInt   end_point )
     920    {
     921      PS_Hints  hints = (PS_Hints)hints_;
     922      FT_Error  error = FT_Err_Ok;
     923  
     924  
     925      if ( !hints->error )
     926      {
     927        FT_Memory  memory = hints->memory;
     928  
     929  
     930        if ( hints->hint_type == PS_HINT_TYPE_1 )
     931        {
     932          error = ps_dimension_reset_mask( &hints->dimension[0],
     933                                           end_point, memory );
     934          if ( error )
     935            goto Fail;
     936  
     937          error = ps_dimension_reset_mask( &hints->dimension[1],
     938                                           end_point, memory );
     939          if ( error )
     940            goto Fail;
     941        }
     942        else
     943        {
     944          /* invalid hint type */
     945          error = FT_THROW( Invalid_Argument );
     946          goto Fail;
     947        }
     948      }
     949      return;
     950  
     951    Fail:
     952      hints->error = error;
     953    }
     954  
     955  
     956    /* Type2 "hintmask" operator, add a new hintmask to each direction */
     957    static void
     958    ps_hints_t2mask( T2_Hints        hints_,    /* PS_Hints */
     959                     FT_UInt         end_point,
     960                     FT_UInt         bit_count,
     961                     const FT_Byte*  bytes )
     962    {
     963      PS_Hints  hints = (PS_Hints)hints_;
     964      FT_Error  error;
     965  
     966  
     967      if ( !hints->error )
     968      {
     969        PS_Dimension  dim    = hints->dimension;
     970        FT_Memory     memory = hints->memory;
     971        FT_UInt       count1 = dim[0].hints.num_hints;
     972        FT_UInt       count2 = dim[1].hints.num_hints;
     973  
     974  
     975        /* check bit count; must be equal to current total hint count */
     976        if ( bit_count !=  count1 + count2 )
     977        {
     978          FT_TRACE0(( "ps_hints_t2mask:"
     979                      " called with invalid bitcount %d (instead of %d)\n",
     980                     bit_count, count1 + count2 ));
     981  
     982          /* simply ignore the operator */
     983          return;
     984        }
     985  
     986        /* set-up new horizontal and vertical hint mask now */
     987        error = ps_dimension_set_mask_bits( &dim[0], bytes, count2, count1,
     988                                            end_point, memory );
     989        if ( error )
     990          goto Fail;
     991  
     992        error = ps_dimension_set_mask_bits( &dim[1], bytes, 0, count2,
     993                                            end_point, memory );
     994        if ( error )
     995          goto Fail;
     996      }
     997      return;
     998  
     999    Fail:
    1000      hints->error = error;
    1001    }
    1002  
    1003  
    1004    static void
    1005    ps_hints_t2counter( T2_Hints        hints_,    /* PS_Hints */
    1006                        FT_UInt         bit_count,
    1007                        const FT_Byte*  bytes )
    1008    {
    1009      PS_Hints  hints = (PS_Hints)hints_;
    1010      FT_Error  error;
    1011  
    1012  
    1013      if ( !hints->error )
    1014      {
    1015        PS_Dimension  dim    = hints->dimension;
    1016        FT_Memory     memory = hints->memory;
    1017        FT_UInt       count1 = dim[0].hints.num_hints;
    1018        FT_UInt       count2 = dim[1].hints.num_hints;
    1019  
    1020  
    1021        /* check bit count, must be equal to current total hint count */
    1022        if ( bit_count !=  count1 + count2 )
    1023        {
    1024          FT_TRACE0(( "ps_hints_t2counter:"
    1025                      " called with invalid bitcount %d (instead of %d)\n",
    1026                     bit_count, count1 + count2 ));
    1027  
    1028          /* simply ignore the operator */
    1029          return;
    1030        }
    1031  
    1032        /* set-up new horizontal and vertical hint mask now */
    1033        error = ps_dimension_set_mask_bits( &dim[0], bytes, 0, count1,
    1034                                            0, memory );
    1035        if ( error )
    1036          goto Fail;
    1037  
    1038        error = ps_dimension_set_mask_bits( &dim[1], bytes, count1, count2,
    1039                                            0, memory );
    1040        if ( error )
    1041          goto Fail;
    1042      }
    1043      return;
    1044  
    1045    Fail:
    1046      hints->error = error;
    1047    }
    1048  
    1049  
    1050    /* end recording session */
    1051    static FT_Error
    1052    ps_hints_close( PS_Hints  hints,
    1053                    FT_UInt   end_point )
    1054    {
    1055      FT_Error  error;
    1056  
    1057  
    1058      error = hints->error;
    1059      if ( !error )
    1060      {
    1061        FT_Memory     memory = hints->memory;
    1062        PS_Dimension  dim    = hints->dimension;
    1063  
    1064  
    1065        error = ps_dimension_end( &dim[0], end_point, memory );
    1066        if ( !error )
    1067        {
    1068          error = ps_dimension_end( &dim[1], end_point, memory );
    1069        }
    1070      }
    1071  
    1072  #ifdef DEBUG_HINTER
    1073      if ( !error )
    1074        ps_debug_hints = hints;
    1075  #endif
    1076      return error;
    1077    }
    1078  
    1079  
    1080    /*************************************************************************/
    1081    /*************************************************************************/
    1082    /*****                                                               *****/
    1083    /*****                TYPE 1 HINTS RECORDING INTERFACE               *****/
    1084    /*****                                                               *****/
    1085    /*************************************************************************/
    1086    /*************************************************************************/
    1087  
    1088    static void
    1089    t1_hints_open( T1_Hints  hints )
    1090    {
    1091      ps_hints_open( (PS_Hints)hints, PS_HINT_TYPE_1 );
    1092    }
    1093  
    1094    static FT_Error
    1095    t1_hints_close( T1_Hints  hints,
    1096                    FT_UInt   end_point )
    1097    {
    1098      return ps_hints_close( (PS_Hints)hints, end_point );
    1099    }
    1100  
    1101    static void
    1102    t1_hints_stem( T1_Hints   hints,
    1103                   FT_UInt    dimension,
    1104                   FT_Fixed*  coords )
    1105    {
    1106      FT_Pos  stems[2];
    1107  
    1108  
    1109      stems[0] = FIXED_TO_INT( coords[0] );
    1110      stems[1] = FIXED_TO_INT( coords[1] );
    1111  
    1112      ps_hints_stem( (PS_Hints)hints, dimension, 1, stems );
    1113    }
    1114  
    1115  
    1116    static FT_Error
    1117    t1_hints_apply( T1_Hints        hints,
    1118                    FT_Outline*     outline,
    1119                    PSH_Globals     globals,
    1120                    FT_Render_Mode  hint_mode )
    1121    {
    1122      return ps_hints_apply( (PS_Hints)hints, outline, globals, hint_mode );
    1123    }
    1124  
    1125  
    1126    FT_LOCAL_DEF( void )
    1127    t1_hints_funcs_init( T1_Hints_FuncsRec*  funcs )
    1128    {
    1129      FT_ZERO( funcs );
    1130  
    1131      funcs->open  = (T1_Hints_OpenFunc)    t1_hints_open;
    1132      funcs->close = (T1_Hints_CloseFunc)   t1_hints_close;
    1133      funcs->stem  = (T1_Hints_SetStemFunc) t1_hints_stem;
    1134      funcs->stem3 = (T1_Hints_SetStem3Func)ps_hints_t1stem3;
    1135      funcs->reset = (T1_Hints_ResetFunc)   ps_hints_t1reset;
    1136      funcs->apply = (T1_Hints_ApplyFunc)   t1_hints_apply;
    1137    }
    1138  
    1139  
    1140    /*************************************************************************/
    1141    /*************************************************************************/
    1142    /*****                                                               *****/
    1143    /*****                TYPE 2 HINTS RECORDING INTERFACE               *****/
    1144    /*****                                                               *****/
    1145    /*************************************************************************/
    1146    /*************************************************************************/
    1147  
    1148    static void
    1149    t2_hints_open( T2_Hints  hints )
    1150    {
    1151      ps_hints_open( (PS_Hints)hints, PS_HINT_TYPE_2 );
    1152    }
    1153  
    1154  
    1155    static FT_Error
    1156    t2_hints_close( T2_Hints  hints,
    1157                    FT_UInt   end_point )
    1158    {
    1159      return ps_hints_close( (PS_Hints)hints, end_point );
    1160    }
    1161  
    1162  
    1163    static void
    1164    t2_hints_stems( T2_Hints   hints,
    1165                    FT_UInt    dimension,
    1166                    FT_Int     count,
    1167                    FT_Fixed*  coords )
    1168    {
    1169      FT_Pos  stems[32], y;
    1170      FT_Int  total = count, n;
    1171  
    1172  
    1173      y = 0;
    1174      while ( total > 0 )
    1175      {
    1176        /* determine number of stems to write */
    1177        count = total;
    1178        if ( count > 16 )
    1179          count = 16;
    1180  
    1181        /* compute integer stem positions in font units */
    1182        for ( n = 0; n < count * 2; n++ )
    1183        {
    1184          y        = ADD_LONG( y, coords[n] );
    1185          stems[n] = FIXED_TO_INT( y );
    1186        }
    1187  
    1188        /* compute lengths */
    1189        for ( n = 0; n < count * 2; n += 2 )
    1190          stems[n + 1] = stems[n + 1] - stems[n];
    1191  
    1192        /* add them to the current dimension */
    1193        ps_hints_stem( (PS_Hints)hints, dimension, count, stems );
    1194  
    1195        total -= count;
    1196      }
    1197    }
    1198  
    1199  
    1200    static FT_Error
    1201    t2_hints_apply( T2_Hints        hints,
    1202                    FT_Outline*     outline,
    1203                    PSH_Globals     globals,
    1204                    FT_Render_Mode  hint_mode )
    1205    {
    1206      return ps_hints_apply( (PS_Hints)hints, outline, globals, hint_mode );
    1207    }
    1208  
    1209  
    1210    FT_LOCAL_DEF( void )
    1211    t2_hints_funcs_init( T2_Hints_FuncsRec*  funcs )
    1212    {
    1213      FT_ZERO( funcs );
    1214  
    1215      funcs->open     = (T2_Hints_OpenFunc)   t2_hints_open;
    1216      funcs->close    = (T2_Hints_CloseFunc)  t2_hints_close;
    1217      funcs->stems    = (T2_Hints_StemsFunc)  t2_hints_stems;
    1218      funcs->hintmask = (T2_Hints_MaskFunc)   ps_hints_t2mask;
    1219      funcs->counter  = (T2_Hints_CounterFunc)ps_hints_t2counter;
    1220      funcs->apply    = (T2_Hints_ApplyFunc)  t2_hints_apply;
    1221    }
    1222  
    1223  
    1224  /* END */