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
ps_hint_table_done(PS_Hint_Table table,FT_Memory memory)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
ps_hint_table_ensure(PS_Hint_Table table,FT_UInt count,FT_Memory memory)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
ps_hint_table_alloc(PS_Hint_Table table,FT_Memory memory,PS_Hint * ahint)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
ps_mask_done(PS_Mask mask,FT_Memory memory)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
ps_mask_ensure(PS_Mask mask,FT_UInt count,FT_Memory memory)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
ps_mask_test_bit(PS_Mask mask,FT_Int idx)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
ps_mask_clear_bit(PS_Mask mask,FT_UInt idx)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
ps_mask_set_bit(PS_Mask mask,FT_UInt idx,FT_Memory memory)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
ps_mask_table_done(PS_Mask_Table table,FT_Memory memory)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
ps_mask_table_ensure(PS_Mask_Table table,FT_UInt count,FT_Memory memory)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
ps_mask_table_alloc(PS_Mask_Table table,FT_Memory memory,PS_Mask * amask)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
ps_mask_table_last(PS_Mask_Table table,FT_Memory memory,PS_Mask * amask)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
ps_mask_table_set_bits(PS_Mask_Table table,const FT_Byte * source,FT_UInt bit_pos,FT_UInt bit_count,FT_Memory memory)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
ps_mask_table_test_intersect(PS_Mask_Table table,FT_UInt index1,FT_UInt index2)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
ps_mask_table_merge(PS_Mask_Table table,FT_UInt index1,FT_UInt index2,FT_Memory memory)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
ps_mask_table_merge_all(PS_Mask_Table table,FT_Memory memory)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
ps_dimension_done(PS_Dimension dimension,FT_Memory memory)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
ps_dimension_init(PS_Dimension dimension)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
ps_dimension_end_mask(PS_Dimension dim,FT_UInt end_point)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
ps_dimension_reset_mask(PS_Dimension dim,FT_UInt end_point,FT_Memory memory)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
ps_dimension_set_mask_bits(PS_Dimension dim,const FT_Byte * source,FT_UInt source_pos,FT_UInt source_bits,FT_UInt end_point,FT_Memory memory)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
ps_dimension_add_t1stem(PS_Dimension dim,FT_Int pos,FT_Int len,FT_Memory memory,FT_Int * aindex)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
ps_dimension_add_counter(PS_Dimension dim,FT_Int hint1,FT_Int hint2,FT_Int hint3,FT_Memory memory)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
ps_dimension_end(PS_Dimension dim,FT_UInt end_point,FT_Memory memory)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 )
ps_hints_done(PS_Hints hints)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 )
ps_hints_init(PS_Hints hints,FT_Memory memory)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
ps_hints_open(PS_Hints hints,PS_Hint_Type hint_type)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
ps_hints_stem(PS_Hints hints,FT_UInt dimension,FT_Int count,FT_Long * stems)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
ps_hints_t1stem3(PS_Hints hints,FT_UInt dimension,FT_Fixed * stems)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
ps_hints_t1reset(PS_Hints hints,FT_UInt end_point)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
ps_hints_t2mask(PS_Hints hints,FT_UInt end_point,FT_UInt bit_count,const FT_Byte * bytes)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
ps_hints_t2counter(PS_Hints hints,FT_UInt bit_count,const FT_Byte * bytes)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
ps_hints_close(PS_Hints hints,FT_UInt end_point)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
t1_hints_open(T1_Hints hints)1120   t1_hints_open( T1_Hints  hints )
1121   {
1122     ps_hints_open( (PS_Hints)hints, PS_HINT_TYPE_1 );
1123   }
1124 
1125   static void
t1_hints_stem(T1_Hints hints,FT_UInt dimension,FT_Fixed * coords)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 )
t1_hints_funcs_init(T1_Hints_FuncsRec * funcs)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
t2_hints_open(T2_Hints hints)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
t2_hints_stems(T2_Hints hints,FT_UInt dimension,FT_Int count,FT_Fixed * coords)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 )
t2_hints_funcs_init(T2_Hints_FuncsRec * funcs)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