tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

pshrec.c (31754B)


      1 /****************************************************************************
      2 *
      3 * pshrec.c
      4 *
      5 *   FreeType PostScript hints recorder (body).
      6 *
      7 * Copyright (C) 2001-2025 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 (%u,%u)\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_Pos*   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 (%u) 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 (%u) 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 %u (instead of %u)\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 %u (instead of %u)\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 */