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