1 /****************************************************************************
   2  *
   3  * pshrec.c
   4  *
   5  *   FreeType PostScript hints recorder (body).
   6  *
   7  * Copyright (C) 2001-2019 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  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    = ADD_INT( 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        = ADD_LONG( 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 */