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