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