source: trunk/poppler/freetype-2.1.10/src/pshinter/pshalgo.c @ 2

Last change on this file since 2 was 2, checked in by Eugene Romanenko, 15 years ago

First import

File size: 54.8 KB
Line 
1/***************************************************************************/
2/*                                                                         */
3/*  pshalgo.c                                                              */
4/*                                                                         */
5/*    PostScript hinting algorithm (body).                                 */
6/*                                                                         */
7/*  Copyright 2001, 2002, 2003, 2004, 2005 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_INTERNAL_OBJECTS_H
21#include FT_INTERNAL_DEBUG_H
22#include "pshalgo.h"
23
24#include "pshnterr.h"
25
26
27#undef  FT_COMPONENT
28#define FT_COMPONENT  trace_pshalgo2
29
30
31#ifdef DEBUG_HINTER
32  PSH_Hint_Table  ps_debug_hint_table = 0;
33  PSH_HintFunc    ps_debug_hint_func  = 0;
34  PSH_Glyph       ps_debug_glyph      = 0;
35#endif
36
37
38#define  COMPUTE_INFLEXS  /* compute inflection points to optimize `S' */
39                          /* and similar glyphs                        */
40#define  STRONGER         /* slightly increase the contrast of smooth  */
41                          /* hinting                                   */
42
43
44  /*************************************************************************/
45  /*************************************************************************/
46  /*****                                                               *****/
47  /*****                  BASIC HINTS RECORDINGS                       *****/
48  /*****                                                               *****/
49  /*************************************************************************/
50  /*************************************************************************/
51
52  /* return true if two stem hints overlap */
53  static FT_Int
54  psh_hint_overlap( PSH_Hint  hint1,
55                    PSH_Hint  hint2 )
56  {
57    return hint1->org_pos + hint1->org_len >= hint2->org_pos &&
58           hint2->org_pos + hint2->org_len >= hint1->org_pos;
59  }
60
61
62  /* destroy hints table */
63  static void
64  psh_hint_table_done( PSH_Hint_Table  table,
65                       FT_Memory       memory )
66  {
67    FT_FREE( table->zones );
68    table->num_zones = 0;
69    table->zone      = 0;
70
71    FT_FREE( table->sort );
72    FT_FREE( table->hints );
73    table->num_hints   = 0;
74    table->max_hints   = 0;
75    table->sort_global = 0;
76  }
77
78
79  /* deactivate all hints in a table */
80  static void
81  psh_hint_table_deactivate( PSH_Hint_Table  table )
82  {
83    FT_UInt   count = table->max_hints;
84    PSH_Hint  hint  = table->hints;
85
86
87    for ( ; count > 0; count--, hint++ )
88    {
89      psh_hint_deactivate( hint );
90      hint->order = -1;
91    }
92  }
93
94
95  /* internal function to record a new hint */
96  static void
97  psh_hint_table_record( PSH_Hint_Table  table,
98                         FT_UInt         idx )
99  {
100    PSH_Hint  hint = table->hints + idx;
101
102
103    if ( idx >= table->max_hints )
104    {
105      FT_ERROR(( "psh_hint_table_record: invalid hint index %d\n", idx ));
106      return;
107    }
108
109    /* ignore active hints */
110    if ( psh_hint_is_active( hint ) )
111      return;
112
113    psh_hint_activate( hint );
114
115    /* now scan the current active hint set to check */
116    /* whether `hint' overlaps with another hint     */
117    {
118      PSH_Hint*  sorted = table->sort_global;
119      FT_UInt    count  = table->num_hints;
120      PSH_Hint   hint2;
121
122
123      hint->parent = 0;
124      for ( ; count > 0; count--, sorted++ )
125      {
126        hint2 = sorted[0];
127
128        if ( psh_hint_overlap( hint, hint2 ) )
129        {
130          hint->parent = hint2;
131          break;
132        }
133      }
134    }
135
136    if ( table->num_hints < table->max_hints )
137      table->sort_global[table->num_hints++] = hint;
138    else
139      FT_ERROR(( "psh_hint_table_record: too many sorted hints!  BUG!\n" ));
140  }
141
142
143  static void
144  psh_hint_table_record_mask( PSH_Hint_Table  table,
145                              PS_Mask         hint_mask )
146  {
147    FT_Int    mask = 0, val = 0;
148    FT_Byte*  cursor = hint_mask->bytes;
149    FT_UInt   idx, limit;
150
151
152    limit = hint_mask->num_bits;
153
154    for ( idx = 0; idx < limit; idx++ )
155    {
156      if ( mask == 0 )
157      {
158        val  = *cursor++;
159        mask = 0x80;
160      }
161
162      if ( val & mask )
163        psh_hint_table_record( table, idx );
164
165      mask >>= 1;
166    }
167  }
168
169
170  /* create hints table */
171  static FT_Error
172  psh_hint_table_init( PSH_Hint_Table  table,
173                       PS_Hint_Table   hints,
174                       PS_Mask_Table   hint_masks,
175                       PS_Mask_Table   counter_masks,
176                       FT_Memory       memory )
177  {
178    FT_UInt   count;
179    FT_Error  error;
180
181    FT_UNUSED( counter_masks );
182
183
184    count = hints->num_hints;
185
186    /* allocate our tables */
187    if ( FT_NEW_ARRAY( table->sort,  2 * count     ) ||
188         FT_NEW_ARRAY( table->hints,     count     ) ||
189         FT_NEW_ARRAY( table->zones, 2 * count + 1 ) )
190      goto Exit;
191
192    table->max_hints   = count;
193    table->sort_global = table->sort + count;
194    table->num_hints   = 0;
195    table->num_zones   = 0;
196    table->zone        = 0;
197
198    /* initialize the `table->hints' array */
199    {
200      PSH_Hint  write = table->hints;
201      PS_Hint   read  = hints->hints;
202
203
204      for ( ; count > 0; count--, write++, read++ )
205      {
206        write->org_pos = read->pos;
207        write->org_len = read->len;
208        write->flags   = read->flags;
209      }
210    }
211
212    /* we now need to determine the initial `parent' stems; first  */
213    /* activate the hints that are given by the initial hint masks */
214    if ( hint_masks )
215    {
216      PS_Mask  mask = hint_masks->masks;
217
218
219      count             = hint_masks->num_masks;
220      table->hint_masks = hint_masks;
221
222      for ( ; count > 0; count--, mask++ )
223        psh_hint_table_record_mask( table, mask );
224    }
225
226    /* finally, do a linear parse in case some hints were left alone */
227    if ( table->num_hints != table->max_hints )
228    {
229      FT_UInt  idx;
230
231
232      FT_ERROR(( "psh_hint_table_init: missing/incorrect hint masks!\n" ));
233
234      count = table->max_hints;
235      for ( idx = 0; idx < count; idx++ )
236        psh_hint_table_record( table, idx );
237    }
238
239  Exit:
240    return error;
241  }
242
243
244  static void
245  psh_hint_table_activate_mask( PSH_Hint_Table  table,
246                                PS_Mask         hint_mask )
247  {
248    FT_Int    mask = 0, val = 0;
249    FT_Byte*  cursor = hint_mask->bytes;
250    FT_UInt   idx, limit, count;
251
252
253    limit = hint_mask->num_bits;
254    count = 0;
255
256    psh_hint_table_deactivate( table );
257
258    for ( idx = 0; idx < limit; idx++ )
259    {
260      if ( mask == 0 )
261      {
262        val  = *cursor++;
263        mask = 0x80;
264      }
265
266      if ( val & mask )
267      {
268        PSH_Hint  hint = &table->hints[idx];
269
270
271        if ( !psh_hint_is_active( hint ) )
272        {
273          FT_UInt     count2;
274
275#if 0
276          PSH_Hint*  sort = table->sort;
277          PSH_Hint   hint2;
278
279
280          for ( count2 = count; count2 > 0; count2--, sort++ )
281          {
282            hint2 = sort[0];
283            if ( psh_hint_overlap( hint, hint2 ) )
284              FT_ERROR(( "psh_hint_table_activate_mask:"
285                         " found overlapping hints\n" ))
286          }
287#else
288          count2 = 0;
289#endif
290
291          if ( count2 == 0 )
292          {
293            psh_hint_activate( hint );
294            if ( count < table->max_hints )
295              table->sort[count++] = hint;
296            else
297              FT_ERROR(( "psh_hint_tableactivate_mask:"
298                         " too many active hints\n" ));
299          }
300        }
301      }
302
303      mask >>= 1;
304    }
305    table->num_hints = count;
306
307    /* now, sort the hints; they are guaranteed to not overlap */
308    /* so we can compare their "org_pos" field directly        */
309    {
310      FT_Int     i1, i2;
311      PSH_Hint   hint1, hint2;
312      PSH_Hint*  sort = table->sort;
313
314
315      /* a simple bubble sort will do, since in 99% of cases, the hints */
316      /* will be already sorted -- and the sort will be linear          */
317      for ( i1 = 1; i1 < (FT_Int)count; i1++ )
318      {
319        hint1 = sort[i1];
320        for ( i2 = i1 - 1; i2 >= 0; i2-- )
321        {
322          hint2 = sort[i2];
323
324          if ( hint2->org_pos < hint1->org_pos )
325            break;
326
327          sort[i2 + 1] = hint2;
328          sort[i2]     = hint1;
329        }
330      }
331    }
332  }
333
334
335  /*************************************************************************/
336  /*************************************************************************/
337  /*****                                                               *****/
338  /*****               HINTS GRID-FITTING AND OPTIMIZATION             *****/
339  /*****                                                               *****/
340  /*************************************************************************/
341  /*************************************************************************/
342
343#if 1
344  static FT_Pos
345  psh_dimension_quantize_len( PSH_Dimension  dim,
346                              FT_Pos         len,
347                              FT_Bool        do_snapping )
348  {
349    if ( len <= 64 )
350      len = 64;
351    else
352    {
353      FT_Pos  delta = len - dim->stdw.widths[0].cur;
354
355
356      if ( delta < 0 )
357        delta = -delta;
358
359      if ( delta < 40 )
360      {
361        len = dim->stdw.widths[0].cur;
362        if ( len < 48 )
363          len = 48;
364      }
365
366      if ( len < 3 * 64 )
367      {
368        delta = ( len & 63 );
369        len  &= -64;
370
371        if ( delta < 10 )
372          len += delta;
373
374        else if ( delta < 32 )
375          len += 10;
376
377        else if ( delta < 54 )
378          len += 54;
379
380        else
381          len += delta;
382      }
383      else
384        len = FT_PIX_ROUND( len );
385    }
386
387    if ( do_snapping )
388      len = FT_PIX_ROUND( len );
389
390    return  len;
391  }
392#endif /* 0 */
393
394
395#ifdef DEBUG_HINTER
396
397  static void
398  ps_simple_scale( PSH_Hint_Table  table,
399                   FT_Fixed        scale,
400                   FT_Fixed        delta,
401                   FT_Int          dimension )
402  {
403    PSH_Hint  hint;
404    FT_UInt   count;
405
406
407    for ( count = 0; count < table->max_hints; count++ )
408    {
409      hint = table->hints + count;
410
411      hint->cur_pos = FT_MulFix( hint->org_pos, scale ) + delta;
412      hint->cur_len = FT_MulFix( hint->org_len, scale );
413
414      if ( ps_debug_hint_func )
415        ps_debug_hint_func( hint, dimension );
416    }
417  }
418
419#endif /* DEBUG_HINTER */
420
421
422  static FT_Fixed
423  psh_hint_snap_stem_side_delta( FT_Fixed  pos,
424                                 FT_Fixed  len )
425  {
426    FT_Fixed  delta1 = FT_PIX_ROUND( pos ) - pos;
427    FT_Fixed  delta2 = FT_PIX_ROUND( pos + len ) - pos - len;
428
429
430    if ( FT_ABS( delta1 ) <= FT_ABS( delta2 ) )
431      return delta1;
432    else
433      return delta2;
434  }
435
436
437  static void
438  psh_hint_align( PSH_Hint     hint,
439                  PSH_Globals  globals,
440                  FT_Int       dimension,
441                  PSH_Glyph    glyph )
442  {
443    PSH_Dimension  dim   = &globals->dimension[dimension];
444    FT_Fixed       scale = dim->scale_mult;
445    FT_Fixed       delta = dim->scale_delta;
446
447
448    if ( !psh_hint_is_fitted( hint ) )
449    {
450      FT_Pos  pos = FT_MulFix( hint->org_pos, scale ) + delta;
451      FT_Pos  len = FT_MulFix( hint->org_len, scale );
452
453      FT_Int            do_snapping;
454      FT_Pos            fit_len;
455      PSH_AlignmentRec  align;
456
457
458      /* ignore stem alignments when requested through the hint flags */
459      if ( ( dimension == 0 && !glyph->do_horz_hints ) ||
460           ( dimension == 1 && !glyph->do_vert_hints ) )
461      {
462        hint->cur_pos = pos;
463        hint->cur_len = len;
464
465        psh_hint_set_fitted( hint );
466        return;
467      }
468
469      /* perform stem snapping when requested - this is necessary
470       * for monochrome and LCD hinting modes only
471       */
472      do_snapping = ( dimension == 0 && glyph->do_horz_snapping ) ||
473                    ( dimension == 1 && glyph->do_vert_snapping );
474
475      hint->cur_len = fit_len = len;
476
477      /* check blue zones for horizontal stems */
478      align.align     = PSH_BLUE_ALIGN_NONE;
479      align.align_bot = align.align_top = 0;
480
481      if ( dimension == 1 )
482        psh_blues_snap_stem( &globals->blues,
483                             hint->org_pos + hint->org_len,
484                             hint->org_pos,
485                             &align );
486
487      switch ( align.align )
488      {
489      case PSH_BLUE_ALIGN_TOP:
490        /* the top of the stem is aligned against a blue zone */
491        hint->cur_pos = align.align_top - fit_len;
492        break;
493
494      case PSH_BLUE_ALIGN_BOT:
495        /* the bottom of the stem is aligned against a blue zone */
496        hint->cur_pos = align.align_bot;
497        break;
498
499      case PSH_BLUE_ALIGN_TOP | PSH_BLUE_ALIGN_BOT:
500        /* both edges of the stem are aligned against blue zones */
501        hint->cur_pos = align.align_bot;
502        hint->cur_len = align.align_top - align.align_bot;
503        break;
504
505      default:
506        {
507          PSH_Hint  parent = hint->parent;
508
509
510          if ( parent )
511          {
512            FT_Pos  par_org_center, par_cur_center;
513            FT_Pos  cur_org_center, cur_delta;
514
515
516            /* ensure that parent is already fitted */
517            if ( !psh_hint_is_fitted( parent ) )
518              psh_hint_align( parent, globals, dimension, glyph );
519
520            /* keep original relation between hints, this is, use the */
521            /* scaled distance between the centers of the hints to    */
522            /* compute the new position                               */
523            par_org_center = parent->org_pos + ( parent->org_len >> 1 );
524            par_cur_center = parent->cur_pos + ( parent->cur_len >> 1 );
525            cur_org_center = hint->org_pos   + ( hint->org_len   >> 1 );
526
527            cur_delta = FT_MulFix( cur_org_center - par_org_center, scale );
528            pos       = par_cur_center + cur_delta - ( len >> 1 );
529          }
530
531          hint->cur_pos = pos;
532          hint->cur_len = fit_len;
533
534          /* Stem adjustment tries to snap stem widths to standard
535           * ones.  This is important to prevent unpleasant rounding
536           * artefacts.
537           */
538          if ( glyph->do_stem_adjust )
539          {
540            if ( len <= 64 )
541            {
542              /* the stem is less than one pixel; we will center it
543               * around the nearest pixel center
544               */
545#if 1
546              pos = FT_PIX_FLOOR( pos + ( len >> 1 ) );
547#else
548             /* this seems to be a bug! */
549              pos = pos + FT_PIX_FLOOR( len >> 1 );
550#endif
551              len = 64;
552            }
553            else
554            {
555              len = psh_dimension_quantize_len( dim, len, 0 );
556            }
557          }
558
559          /* now that we have a good hinted stem width, try to position */
560          /* the stem along a pixel grid integer coordinate             */
561          hint->cur_pos = pos + psh_hint_snap_stem_side_delta( pos, len );
562          hint->cur_len = len;
563        }
564      }
565
566      if ( do_snapping )
567      {
568        pos = hint->cur_pos;
569        len = hint->cur_len;
570
571        if ( len < 64 )
572          len = 64;
573        else
574          len = FT_PIX_ROUND( len );
575
576        switch ( align.align )
577        {
578          case PSH_BLUE_ALIGN_TOP:
579            hint->cur_pos = align.align_top - len;
580            hint->cur_len = len;
581            break;
582
583          case PSH_BLUE_ALIGN_BOT:
584            hint->cur_len = len;
585            break;
586
587          case PSH_BLUE_ALIGN_BOT | PSH_BLUE_ALIGN_TOP:
588            /* don't touch */
589            break;
590
591
592          default:
593            hint->cur_len = len;
594            if ( len & 64 )
595              pos = FT_PIX_FLOOR( pos + ( len >> 1 ) ) + 32;
596            else
597              pos = FT_PIX_ROUND( pos + ( len >> 1 ) );
598
599            hint->cur_pos = pos - ( len >> 1 );
600            hint->cur_len = len;
601        }
602      }
603
604      psh_hint_set_fitted( hint );
605
606#ifdef DEBUG_HINTER
607      if ( ps_debug_hint_func )
608        ps_debug_hint_func( hint, dimension );
609#endif
610    }
611  }
612
613
614#if 0  /* not used for now, experimental */
615
616 /*
617  *  A variant to perform "light" hinting (i.e. FT_RENDER_MODE_LIGHT)
618  *  of stems
619  */
620  static void
621  psh_hint_align_light( PSH_Hint     hint,
622                        PSH_Globals  globals,
623                        FT_Int       dimension,
624                        PSH_Glyph    glyph )
625  {
626    PSH_Dimension  dim   = &globals->dimension[dimension];
627    FT_Fixed       scale = dim->scale_mult;
628    FT_Fixed       delta = dim->scale_delta;
629
630
631    if ( !psh_hint_is_fitted( hint ) )
632    {
633      FT_Pos  pos = FT_MulFix( hint->org_pos, scale ) + delta;
634      FT_Pos  len = FT_MulFix( hint->org_len, scale );
635
636      FT_Pos  fit_len;
637
638      PSH_AlignmentRec  align;
639
640
641      /* ignore stem alignments when requested through the hint flags */
642      if ( ( dimension == 0 && !glyph->do_horz_hints ) ||
643           ( dimension == 1 && !glyph->do_vert_hints ) )
644      {
645        hint->cur_pos = pos;
646        hint->cur_len = len;
647
648        psh_hint_set_fitted( hint );
649        return;
650      }
651
652      fit_len = len;
653
654      hint->cur_len = fit_len;
655
656      /* check blue zones for horizontal stems */
657      align.align = PSH_BLUE_ALIGN_NONE;
658      align.align_bot = align.align_top = 0;
659
660      if ( dimension == 1 )
661        psh_blues_snap_stem( &globals->blues,
662                             hint->org_pos + hint->org_len,
663                             hint->org_pos,
664                             &align );
665
666      switch ( align.align )
667      {
668      case PSH_BLUE_ALIGN_TOP:
669        /* the top of the stem is aligned against a blue zone */
670        hint->cur_pos = align.align_top - fit_len;
671        break;
672
673      case PSH_BLUE_ALIGN_BOT:
674        /* the bottom of the stem is aligned against a blue zone */
675        hint->cur_pos = align.align_bot;
676        break;
677
678      case PSH_BLUE_ALIGN_TOP | PSH_BLUE_ALIGN_BOT:
679        /* both edges of the stem are aligned against blue zones */
680        hint->cur_pos = align.align_bot;
681        hint->cur_len = align.align_top - align.align_bot;
682        break;
683
684      default:
685        {
686          PSH_Hint  parent = hint->parent;
687
688
689          if ( parent )
690          {
691            FT_Pos  par_org_center, par_cur_center;
692            FT_Pos  cur_org_center, cur_delta;
693
694
695            /* ensure that parent is already fitted */
696            if ( !psh_hint_is_fitted( parent ) )
697              psh_hint_align_light( parent, globals, dimension, glyph );
698
699            par_org_center = parent->org_pos + ( parent->org_len / 2 );
700            par_cur_center = parent->cur_pos + ( parent->cur_len / 2 );
701            cur_org_center = hint->org_pos   + ( hint->org_len   / 2 );
702
703            cur_delta = FT_MulFix( cur_org_center - par_org_center, scale );
704            pos       = par_cur_center + cur_delta - ( len >> 1 );
705          }
706
707          /* Stems less than one pixel wide are easy -- we want to
708           * make them as dark as possible, so they must fall within
709           * one pixel.  If the stem is split between two pixels
710           * then snap the edge that is nearer to the pixel boundary
711           * to the pixel boundary.
712           */
713          if ( len <= 64 )
714          {
715            if ( ( pos + len + 63 ) / 64  != pos / 64 + 1 )
716              pos += psh_hint_snap_stem_side_delta ( pos, len );
717          }
718
719          /* Position stems other to minimize the amount of mid-grays.
720           * There are, in general, two positions that do this,
721           * illustrated as A) and B) below.
722           *
723           *   +                   +                   +                   +
724           *
725           * A)             |--------------------------------|
726           * B)   |--------------------------------|
727           * C)       |--------------------------------|
728           *
729           * Position A) (split the excess stem equally) should be better
730           * for stems of width N + f where f < 0.5.
731           *
732           * Position B) (split the deficiency equally) should be better
733           * for stems of width N + f where f > 0.5.
734           *
735           * It turns out though that minimizing the total number of lit
736           * pixels is also important, so position C), with one edge
737           * aligned with a pixel boundary is actually preferable
738           * to A).  There are also more possibile positions for C) than
739           * for A) or B), so it involves less distortion of the overall
740           * character shape.
741           */
742          else /* len > 64 */
743          {
744            FT_Fixed  frac_len = len & 63;
745            FT_Fixed  center = pos + ( len >> 1 );
746            FT_Fixed  delta_a, delta_b;
747
748
749            if ( ( len / 64 ) & 1 )
750            {
751              delta_a = FT_PIX_FLOOR( center ) + 32 - center;
752              delta_b = FT_PIX_ROUND( center ) - center;
753            }
754            else
755            {
756              delta_a = FT_PIX_ROUND( center ) - center;
757              delta_b = FT_PIX_FLOOR( center ) + 32 - center;
758            }
759
760            /* We choose between B) and C) above based on the amount
761             * of fractinal stem width; for small amounts, choose
762             * C) always, for large amounts, B) always, and inbetween,
763             * pick whichever one involves less stem movement.
764             */
765            if ( frac_len < 32 )
766            {
767              pos += psh_hint_snap_stem_side_delta ( pos, len );
768            }
769            else if ( frac_len < 48 )
770            {
771              FT_Fixed  side_delta = psh_hint_snap_stem_side_delta ( pos,
772                                                                     len );
773
774              if ( FT_ABS( side_delta ) < FT_ABS( delta_b ) )
775                pos += side_delta;
776              else
777                pos += delta_b;
778            }
779            else
780            {
781              pos += delta_b;
782            }
783          }
784
785          hint->cur_pos = pos;
786        }
787      }  /* switch */
788
789      psh_hint_set_fitted( hint );
790
791#ifdef DEBUG_HINTER
792      if ( ps_debug_hint_func )
793        ps_debug_hint_func( hint, dimension );
794#endif
795    }
796  }
797
798#endif /* 0 */
799
800
801  static void
802  psh_hint_table_align_hints( PSH_Hint_Table  table,
803                              PSH_Globals     globals,
804                              FT_Int          dimension,
805                              PSH_Glyph       glyph )
806  {
807    PSH_Hint       hint;
808    FT_UInt        count;
809
810#ifdef DEBUG_HINTER
811
812    PSH_Dimension  dim   = &globals->dimension[dimension];
813    FT_Fixed       scale = dim->scale_mult;
814    FT_Fixed       delta = dim->scale_delta;
815
816
817    if ( ps_debug_no_vert_hints && dimension == 0 )
818    {
819      ps_simple_scale( table, scale, delta, dimension );
820      return;
821    }
822
823    if ( ps_debug_no_horz_hints && dimension == 1 )
824    {
825      ps_simple_scale( table, scale, delta, dimension );
826      return;
827    }
828
829#endif /* DEBUG_HINTER*/
830
831    hint  = table->hints;
832    count = table->max_hints;
833
834    for ( ; count > 0; count--, hint++ )
835      psh_hint_align( hint, globals, dimension, glyph );
836  }
837
838
839  /*************************************************************************/
840  /*************************************************************************/
841  /*****                                                               *****/
842  /*****                POINTS INTERPOLATION ROUTINES                  *****/
843  /*****                                                               *****/
844  /*************************************************************************/
845  /*************************************************************************/
846
847#define PSH_ZONE_MIN  -3200000L
848#define PSH_ZONE_MAX  +3200000L
849
850#define xxDEBUG_ZONES
851
852
853#ifdef DEBUG_ZONES
854
855#include <stdio.h>
856
857  static void
858  psh_print_zone( PSH_Zone  zone )
859  {
860    printf( "zone [scale,delta,min,max] = [%.3f,%.3f,%d,%d]\n",
861             zone->scale / 65536.0,
862             zone->delta / 64.0,
863             zone->min,
864             zone->max );
865  }
866
867#else
868
869#define psh_print_zone( x )  do { } while ( 0 )
870
871#endif /* DEBUG_ZONES */
872
873
874  /*************************************************************************/
875  /*************************************************************************/
876  /*****                                                               *****/
877  /*****                    HINTER GLYPH MANAGEMENT                    *****/
878  /*****                                                               *****/
879  /*************************************************************************/
880  /*************************************************************************/
881
882#ifdef COMPUTE_INFLEXS
883
884  /* compute all inflex points in a given glyph */
885  static void
886  psh_glyph_compute_inflections( PSH_Glyph  glyph )
887  {
888    FT_UInt  n;
889
890
891    for ( n = 0; n < glyph->num_contours; n++ )
892    {
893      PSH_Point  first, start, end, before, after;
894      FT_Angle   angle_in, angle_seg, angle_out;
895      FT_Angle   diff_in, diff_out;
896      FT_Int     finished = 0;
897
898
899      /* we need at least 4 points to create an inflection point */
900      if ( glyph->contours[n].count < 4 )
901        continue;
902
903      /* compute first segment in contour */
904      first = glyph->contours[n].start;
905
906      start = end = first;
907      do
908      {
909        end = end->next;
910        if ( end == first )
911          goto Skip;
912
913      } while ( PSH_POINT_EQUAL_ORG( end, first ) );
914
915      angle_seg = PSH_POINT_ANGLE( start, end );
916
917      /* extend the segment start whenever possible */
918      before = start;
919      do
920      {
921        do
922        {
923          start  = before;
924          before = before->prev;
925          if ( before == first )
926            goto Skip;
927
928        } while ( PSH_POINT_EQUAL_ORG( before, start ) );
929
930        angle_in = PSH_POINT_ANGLE( before, start );
931
932      } while ( angle_in == angle_seg );
933
934      first   = start;
935      diff_in = FT_Angle_Diff( angle_in, angle_seg );
936
937      /* now, process all segments in the contour */
938      do
939      {
940        /* first, extend current segment's end whenever possible */
941        after = end;
942        do
943        {
944          do
945          {
946            end   = after;
947            after = after->next;
948            if ( after == first )
949              finished = 1;
950
951          } while ( PSH_POINT_EQUAL_ORG( end, after ) );
952
953          angle_out = PSH_POINT_ANGLE( end, after );
954
955        } while ( angle_out == angle_seg );
956
957        diff_out = FT_Angle_Diff( angle_seg, angle_out );
958
959        if ( ( diff_in ^ diff_out ) < 0 )
960        {
961          /* diff_in and diff_out have different signs, we have */
962          /* inflection points here...                          */
963
964          do
965          {
966            psh_point_set_inflex( start );
967            start = start->next;
968          }
969          while ( start != end );
970
971          psh_point_set_inflex( start );
972        }
973
974        start     = end;
975        end       = after;
976        angle_seg = angle_out;
977        diff_in   = diff_out;
978
979      } while ( !finished );
980
981    Skip:
982      ;
983    }
984  }
985
986#endif /* COMPUTE_INFLEXS */
987
988
989  static void
990  psh_glyph_done( PSH_Glyph  glyph )
991  {
992    FT_Memory  memory = glyph->memory;
993
994
995    psh_hint_table_done( &glyph->hint_tables[1], memory );
996    psh_hint_table_done( &glyph->hint_tables[0], memory );
997
998    FT_FREE( glyph->points );
999    FT_FREE( glyph->contours );
1000
1001    glyph->num_points   = 0;
1002    glyph->num_contours = 0;
1003
1004    glyph->memory = 0;
1005  }
1006
1007
1008  static int
1009  psh_compute_dir( FT_Pos  dx,
1010                   FT_Pos  dy )
1011  {
1012    FT_Pos  ax, ay;
1013    int     result = PSH_DIR_NONE;
1014
1015
1016    ax = ( dx >= 0 ) ? dx : -dx;
1017    ay = ( dy >= 0 ) ? dy : -dy;
1018
1019    if ( ay * 12 < ax )
1020    {
1021      /* |dy| <<< |dx|  means a near-horizontal segment */
1022      result = ( dx >= 0 ) ? PSH_DIR_RIGHT : PSH_DIR_LEFT;
1023    }
1024    else if ( ax * 12 < ay )
1025    {
1026      /* |dx| <<< |dy|  means a near-vertical segment */
1027      result = ( dy >= 0 ) ? PSH_DIR_UP : PSH_DIR_DOWN;
1028    }
1029
1030    return result;
1031  }
1032
1033
1034  /* load outline point coordinates into hinter glyph */
1035  static void
1036  psh_glyph_load_points( PSH_Glyph  glyph,
1037                         FT_Int     dimension )
1038  {
1039    FT_Vector*  vec   = glyph->outline->points;
1040    PSH_Point   point = glyph->points;
1041    FT_UInt     count = glyph->num_points;
1042
1043
1044    for ( ; count > 0; count--, point++, vec++ )
1045    {
1046      point->flags2 = 0;
1047      point->hint   = NULL;
1048      if ( dimension == 0 )
1049      {
1050        point->org_u = vec->x;
1051        point->org_v = vec->y;
1052      }
1053      else
1054      {
1055        point->org_u = vec->y;
1056        point->org_v = vec->x;
1057      }
1058
1059#ifdef DEBUG_HINTER
1060      point->org_x = vec->x;
1061      point->org_y = vec->y;
1062#endif
1063
1064    }
1065  }
1066
1067
1068  /* save hinted point coordinates back to outline */
1069  static void
1070  psh_glyph_save_points( PSH_Glyph  glyph,
1071                         FT_Int     dimension )
1072  {
1073    FT_UInt     n;
1074    PSH_Point   point = glyph->points;
1075    FT_Vector*  vec   = glyph->outline->points;
1076    char*       tags  = glyph->outline->tags;
1077
1078
1079    for ( n = 0; n < glyph->num_points; n++ )
1080    {
1081      if ( dimension == 0 )
1082        vec[n].x = point->cur_u;
1083      else
1084        vec[n].y = point->cur_u;
1085
1086      if ( psh_point_is_strong( point ) )
1087        tags[n] |= (char)( ( dimension == 0 ) ? 32 : 64 );
1088
1089#ifdef DEBUG_HINTER
1090
1091      if ( dimension == 0 )
1092      {
1093        point->cur_x   = point->cur_u;
1094        point->flags_x = point->flags2 | point->flags;
1095      }
1096      else
1097      {
1098        point->cur_y   = point->cur_u;
1099        point->flags_y = point->flags2 | point->flags;
1100      }
1101
1102#endif
1103
1104      point++;
1105    }
1106  }
1107
1108
1109  static FT_Error
1110  psh_glyph_init( PSH_Glyph    glyph,
1111                  FT_Outline*  outline,
1112                  PS_Hints     ps_hints,
1113                  PSH_Globals  globals )
1114  {
1115    FT_Error   error;
1116    FT_Memory  memory;
1117
1118
1119    /* clear all fields */
1120    FT_MEM_ZERO( glyph, sizeof ( *glyph ) );
1121
1122    memory = glyph->memory = globals->memory;
1123
1124    /* allocate and setup points + contours arrays */
1125    if ( FT_NEW_ARRAY( glyph->points,   outline->n_points   ) ||
1126         FT_NEW_ARRAY( glyph->contours, outline->n_contours ) )
1127      goto Exit;
1128
1129    glyph->num_points   = outline->n_points;
1130    glyph->num_contours = outline->n_contours;
1131
1132    {
1133      FT_UInt      first = 0, next, n;
1134      PSH_Point    points  = glyph->points;
1135      PSH_Contour  contour = glyph->contours;
1136
1137
1138      for ( n = 0; n < glyph->num_contours; n++ )
1139      {
1140        FT_Int     count;
1141        PSH_Point  point;
1142
1143
1144        next  = outline->contours[n] + 1;
1145        count = next - first;
1146
1147        contour->start = points + first;
1148        contour->count = (FT_UInt)count;
1149
1150        if ( count > 0 )
1151        {
1152          point = points + first;
1153
1154          point->prev    = points + next - 1;
1155          point->contour = contour;
1156
1157          for ( ; count > 1; count-- )
1158          {
1159            point[0].next = point + 1;
1160            point[1].prev = point;
1161            point++;
1162            point->contour = contour;
1163          }
1164          point->next = points + first;
1165        }
1166
1167        contour++;
1168        first = next;
1169      }
1170    }
1171
1172    {
1173      PSH_Point   points = glyph->points;
1174      PSH_Point   point  = points;
1175      FT_Vector*  vec    = outline->points;
1176      FT_UInt     n;
1177
1178
1179      for ( n = 0; n < glyph->num_points; n++, point++ )
1180      {
1181        FT_Int  n_prev = (FT_Int)( point->prev - points );
1182        FT_Int  n_next = (FT_Int)( point->next - points );
1183        FT_Pos  dxi, dyi, dxo, dyo;
1184
1185
1186        if ( !( outline->tags[n] & FT_CURVE_TAG_ON ) )
1187          point->flags = PSH_POINT_OFF;
1188
1189        dxi = vec[n].x - vec[n_prev].x;
1190        dyi = vec[n].y - vec[n_prev].y;
1191
1192        point->dir_in = (FT_Char)psh_compute_dir( dxi, dyi );
1193
1194        dxo = vec[n_next].x - vec[n].x;
1195        dyo = vec[n_next].y - vec[n].y;
1196
1197        point->dir_out = (FT_Char)psh_compute_dir( dxo, dyo );
1198
1199        /* detect smooth points */
1200        if ( point->flags & PSH_POINT_OFF )
1201          point->flags |= PSH_POINT_SMOOTH;
1202        else if ( point->dir_in  != PSH_DIR_NONE ||
1203                  point->dir_out != PSH_DIR_NONE )
1204        {
1205          if ( point->dir_in == point->dir_out )
1206            point->flags |= PSH_POINT_SMOOTH;
1207        }
1208        else
1209        {
1210          FT_Angle  angle_in, angle_out, diff;
1211
1212
1213          angle_in  = FT_Atan2( dxi, dyi );
1214          angle_out = FT_Atan2( dxo, dyo );
1215
1216          diff = angle_in - angle_out;
1217          if ( diff < 0 )
1218            diff = -diff;
1219
1220          if ( diff > FT_ANGLE_PI )
1221            diff = FT_ANGLE_2PI - diff;
1222
1223          if ( diff < FT_ANGLE_PI / 16 )
1224            point->flags |= PSH_POINT_SMOOTH;
1225        }
1226      }
1227    }
1228
1229    glyph->outline = outline;
1230    glyph->globals = globals;
1231
1232#ifdef COMPUTE_INFLEXS
1233    psh_glyph_load_points( glyph, 0 );
1234    psh_glyph_compute_inflections( glyph );
1235#endif /* COMPUTE_INFLEXS */
1236
1237    /* now deal with hints tables */
1238    error = psh_hint_table_init( &glyph->hint_tables [0],
1239                                 &ps_hints->dimension[0].hints,
1240                                 &ps_hints->dimension[0].masks,
1241                                 &ps_hints->dimension[0].counters,
1242                                 memory );
1243    if ( error )
1244      goto Exit;
1245
1246    error = psh_hint_table_init( &glyph->hint_tables [1],
1247                                 &ps_hints->dimension[1].hints,
1248                                 &ps_hints->dimension[1].masks,
1249                                 &ps_hints->dimension[1].counters,
1250                                 memory );
1251    if ( error )
1252      goto Exit;
1253
1254  Exit:
1255    return error;
1256  }
1257
1258
1259  /* compute all extrema in a glyph for a given dimension */
1260  static void
1261  psh_glyph_compute_extrema( PSH_Glyph  glyph )
1262  {
1263    FT_UInt  n;
1264
1265
1266    /* first of all, compute all local extrema */
1267    for ( n = 0; n < glyph->num_contours; n++ )
1268    {
1269      PSH_Point  first = glyph->contours[n].start;
1270      PSH_Point  point, before, after;
1271
1272
1273      if ( glyph->contours[n].count == 0 )
1274        continue;
1275
1276      point  = first;
1277      before = point;
1278      after  = point;
1279
1280      do
1281      {
1282        before = before->prev;
1283        if ( before == first )
1284          goto Skip;
1285
1286      } while ( before->org_u == point->org_u );
1287
1288      first = point = before->next;
1289
1290      for (;;)
1291      {
1292        after = point;
1293        do
1294        {
1295          after = after->next;
1296          if ( after == first )
1297            goto Next;
1298
1299        } while ( after->org_u == point->org_u );
1300
1301        if ( before->org_u < point->org_u )
1302        {
1303          if ( after->org_u < point->org_u )
1304          {
1305            /* local maximum */
1306            goto Extremum;
1307          }
1308        }
1309        else /* before->org_u > point->org_u */
1310        {
1311          if ( after->org_u > point->org_u )
1312          {
1313            /* local minimum */
1314          Extremum:
1315            do
1316            {
1317              psh_point_set_extremum( point );
1318              point = point->next;
1319
1320            } while ( point != after );
1321          }
1322        }
1323
1324        before = after->prev;
1325        point  = after;
1326
1327      } /* for  */
1328
1329    Next:
1330      ;
1331    }
1332
1333    /* for each extremum, determine its direction along the */
1334    /* orthogonal axis                                      */
1335    for ( n = 0; n < glyph->num_points; n++ )
1336    {
1337      PSH_Point  point, before, after;
1338
1339
1340      point  = &glyph->points[n];
1341      before = point;
1342      after  = point;
1343
1344      if ( psh_point_is_extremum( point ) )
1345      {
1346        do
1347        {
1348          before = before->prev;
1349          if ( before == point )
1350            goto Skip;
1351
1352        } while ( before->org_v == point->org_v );
1353
1354        do
1355        {
1356          after = after->next;
1357          if ( after == point )
1358            goto Skip;
1359
1360        } while ( after->org_v == point->org_v );
1361      }
1362
1363      if ( before->org_v < point->org_v &&
1364           after->org_v  > point->org_v )
1365      {
1366        psh_point_set_positive( point );
1367      }
1368      else if ( before->org_v > point->org_v &&
1369                after->org_v  < point->org_v )
1370      {
1371        psh_point_set_negative( point );
1372      }
1373
1374    Skip:
1375      ;
1376    }
1377  }
1378
1379
1380  /* major_dir is the direction for points on the bottom/left of the stem; */
1381  /* Points on the top/right of the stem will have a direction of          */
1382  /* -major_dir.                                                           */
1383
1384  static void
1385  psh_hint_table_find_strong_point( PSH_Hint_Table  table,
1386                                    PSH_Point       point,
1387                                    FT_Int          threshold,
1388                                    FT_Int          major_dir )
1389  {
1390    PSH_Hint*  sort      = table->sort;
1391    FT_UInt    num_hints = table->num_hints;
1392    FT_Int     point_dir = 0;
1393
1394
1395    if ( PSH_DIR_COMPARE( point->dir_in, major_dir ) )
1396      point_dir = point->dir_in;
1397
1398    else if ( PSH_DIR_COMPARE( point->dir_out, major_dir ) )
1399      point_dir = point->dir_out;
1400
1401    if ( point_dir )
1402    {
1403      FT_UInt  flag;
1404
1405
1406      for ( ; num_hints > 0; num_hints--, sort++ )
1407      {
1408        PSH_Hint  hint = sort[0];
1409        FT_Pos    d;
1410
1411
1412        if ( point_dir == major_dir )
1413        {
1414          flag = PSH_POINT_EDGE_MIN;
1415          d    = point->org_u - hint->org_pos;
1416
1417          if ( FT_ABS( d ) < threshold )
1418          {
1419          Is_Strong:
1420            psh_point_set_strong( point );
1421            point->flags2 |= flag;
1422            point->hint    = hint;
1423            break;
1424          }
1425        }
1426        else if ( point_dir == -major_dir )
1427        {
1428          flag = PSH_POINT_EDGE_MAX;
1429          d    = point->org_u - hint->org_pos - hint->org_len;
1430
1431          if ( FT_ABS( d ) < threshold )
1432            goto Is_Strong;
1433        }
1434      }
1435    }
1436
1437#if 1
1438    else if ( psh_point_is_extremum( point ) )
1439    {
1440      /* treat extrema as special cases for stem edge alignment */
1441      FT_UInt  min_flag, max_flag;
1442
1443
1444      if ( major_dir == PSH_DIR_HORIZONTAL )
1445      {
1446        min_flag = PSH_POINT_POSITIVE;
1447        max_flag = PSH_POINT_NEGATIVE;
1448      }
1449      else
1450      {
1451        min_flag = PSH_POINT_NEGATIVE;
1452        max_flag = PSH_POINT_POSITIVE;
1453      }
1454
1455      for ( ; num_hints > 0; num_hints--, sort++ )
1456      {
1457        PSH_Hint  hint = sort[0];
1458        FT_Pos    d;
1459        FT_Int    flag;
1460
1461
1462        if ( point->flags2 & min_flag )
1463        {
1464          flag = PSH_POINT_EDGE_MIN;
1465          d    = point->org_u - hint->org_pos;
1466
1467          if ( FT_ABS( d ) < threshold )
1468          {
1469          Is_Strong2:
1470            point->flags2 |= flag;
1471            point->hint    = hint;
1472            psh_point_set_strong( point );
1473            break;
1474          }
1475        }
1476        else if ( point->flags2 & max_flag )
1477        {
1478          flag = PSH_POINT_EDGE_MAX;
1479          d    = point->org_u - hint->org_pos - hint->org_len;
1480
1481          if ( FT_ABS( d ) < threshold )
1482            goto Is_Strong2;
1483        }
1484
1485        if ( point->org_u >= hint->org_pos                 &&
1486             point->org_u <= hint->org_pos + hint->org_len )
1487        {
1488          point->hint = hint;
1489        }
1490      }
1491    }
1492
1493#endif /* 1 */
1494  }
1495
1496
1497  /* the accepted shift for strong points in fractional pixels */
1498#define PSH_STRONG_THRESHOLD  32
1499
1500  /* the maximum shift value in font units */
1501#define PSH_STRONG_THRESHOLD_MAXIMUM  30
1502
1503
1504  /* find strong points in a glyph */
1505  static void
1506  psh_glyph_find_strong_points( PSH_Glyph  glyph,
1507                                FT_Int     dimension )
1508  {
1509    /* a point is `strong' if it is located on a stem edge and       */
1510    /* has an `in' or `out' tangent parallel to the hint's direction */
1511
1512    PSH_Hint_Table  table     = &glyph->hint_tables[dimension];
1513    PS_Mask         mask      = table->hint_masks->masks;
1514    FT_UInt         num_masks = table->hint_masks->num_masks;
1515    FT_UInt         first     = 0;
1516    FT_Int          major_dir = dimension == 0 ? PSH_DIR_VERTICAL
1517                                               : PSH_DIR_HORIZONTAL;
1518    PSH_Dimension   dim       = &glyph->globals->dimension[dimension];
1519    FT_Fixed        scale     = dim->scale_mult;
1520    FT_Int          threshold;
1521
1522
1523    threshold = (FT_Int)FT_DivFix( PSH_STRONG_THRESHOLD, scale );
1524    if ( threshold > PSH_STRONG_THRESHOLD_MAXIMUM )
1525      threshold = PSH_STRONG_THRESHOLD_MAXIMUM;
1526
1527    /* process secondary hints to `selected' points */
1528    if ( num_masks > 1 && glyph->num_points > 0 )
1529    {
1530      first = mask->end_point;
1531      mask++;
1532      for ( ; num_masks > 1; num_masks--, mask++ )
1533      {
1534        FT_UInt  next;
1535        FT_Int   count;
1536
1537
1538        next  = mask->end_point;
1539        count = next - first;
1540        if ( count > 0 )
1541        {
1542          PSH_Point  point = glyph->points + first;
1543
1544
1545          psh_hint_table_activate_mask( table, mask );
1546
1547          for ( ; count > 0; count--, point++ )
1548            psh_hint_table_find_strong_point( table, point,
1549                                              threshold, major_dir );
1550        }
1551        first = next;
1552      }
1553    }
1554
1555    /* process primary hints for all points */
1556    if ( num_masks == 1 )
1557    {
1558      FT_UInt    count = glyph->num_points;
1559      PSH_Point  point = glyph->points;
1560
1561
1562      psh_hint_table_activate_mask( table, table->hint_masks->masks );
1563      for ( ; count > 0; count--, point++ )
1564      {
1565        if ( !psh_point_is_strong( point ) )
1566          psh_hint_table_find_strong_point( table, point,
1567                                            threshold, major_dir );
1568      }
1569    }
1570
1571    /* now, certain points may have been attached to a hint and */
1572    /* not marked as strong; update their flags then            */
1573    {
1574      FT_UInt    count = glyph->num_points;
1575      PSH_Point  point = glyph->points;
1576
1577
1578      for ( ; count > 0; count--, point++ )
1579        if ( point->hint && !psh_point_is_strong( point ) )
1580          psh_point_set_strong( point );
1581    }
1582  }
1583
1584
1585  /* find points in a glyph which are in a blue zone and have `in' or */
1586  /* `out' tangents parallel to the horizontal axis                   */
1587  static void
1588  psh_glyph_find_blue_points( PSH_Blues  blues,
1589                              PSH_Glyph  glyph )
1590  {
1591    PSH_Blue_Table  table;
1592    PSH_Blue_Zone   zone;
1593    FT_UInt         glyph_count = glyph->num_points;
1594    FT_UInt         blue_count;
1595    PSH_Point       point = glyph->points;
1596
1597
1598    for ( ; glyph_count > 0; glyph_count--, point++ )
1599    {
1600      FT_Pos  y;
1601
1602
1603      /* check tangents */
1604      if ( !PSH_DIR_COMPARE( point->dir_in,  PSH_DIR_HORIZONTAL ) &&
1605           !PSH_DIR_COMPARE( point->dir_out, PSH_DIR_HORIZONTAL ) )
1606        continue;
1607
1608      /* skip strong points */
1609      if ( psh_point_is_strong( point ) )
1610        continue;
1611
1612      y = point->org_u;
1613
1614      /* look up top zones */
1615      table      = &blues->normal_top;
1616      blue_count = table->count;
1617      zone       = table->zones;
1618
1619      for ( ; blue_count > 0; blue_count--, zone++ )
1620      {
1621        FT_Pos  delta = y - zone->org_bottom;
1622
1623
1624        if ( delta < -blues->blue_fuzz )
1625          break;
1626
1627        if ( y <= zone->org_top + blues->blue_fuzz )
1628          if ( blues->no_overshoots || delta <= blues->blue_threshold )
1629          {
1630            point->cur_u = zone->cur_bottom;
1631            psh_point_set_strong( point );
1632            psh_point_set_fitted( point );
1633          }
1634      }
1635
1636      /* look up bottom zones */
1637      table      = &blues->normal_bottom;
1638      blue_count = table->count;
1639      zone       = table->zones + blue_count - 1;
1640
1641      for ( ; blue_count > 0; blue_count--, zone-- )
1642      {
1643        FT_Pos  delta = zone->org_top - y;
1644
1645
1646        if ( delta < -blues->blue_fuzz )
1647          break;
1648
1649        if ( y >= zone->org_bottom - blues->blue_fuzz )
1650          if ( blues->no_overshoots || delta < blues->blue_threshold )
1651          {
1652            point->cur_u = zone->cur_top;
1653            psh_point_set_strong( point );
1654            psh_point_set_fitted( point );
1655          }
1656      }
1657    }
1658  }
1659
1660
1661  /* interpolate strong points with the help of hinted coordinates */
1662  static void
1663  psh_glyph_interpolate_strong_points( PSH_Glyph  glyph,
1664                                       FT_Int     dimension )
1665  {
1666    PSH_Dimension  dim   = &glyph->globals->dimension[dimension];
1667    FT_Fixed       scale = dim->scale_mult;
1668
1669    FT_UInt        count = glyph->num_points;
1670    PSH_Point      point = glyph->points;
1671
1672
1673    for ( ; count > 0; count--, point++ )
1674    {
1675      PSH_Hint  hint = point->hint;
1676
1677
1678      if ( hint )
1679      {
1680        FT_Pos  delta;
1681
1682
1683        if ( psh_point_is_edge_min( point ) )
1684          point->cur_u = hint->cur_pos;
1685
1686        else if ( psh_point_is_edge_max( point ) )
1687          point->cur_u = hint->cur_pos + hint->cur_len;
1688
1689        else
1690        {
1691          delta = point->org_u - hint->org_pos;
1692
1693          if ( delta <= 0 )
1694            point->cur_u = hint->cur_pos + FT_MulFix( delta, scale );
1695
1696          else if ( delta >= hint->org_len )
1697            point->cur_u = hint->cur_pos + hint->cur_len +
1698                             FT_MulFix( delta - hint->org_len, scale );
1699
1700          else if ( hint->org_len > 0 )
1701            point->cur_u = hint->cur_pos +
1702                             FT_MulDiv( delta, hint->cur_len,
1703                                        hint->org_len );
1704          else
1705            point->cur_u = hint->cur_pos;
1706        }
1707        psh_point_set_fitted( point );
1708      }
1709    }
1710  }
1711
1712
1713  static void
1714  psh_glyph_interpolate_normal_points( PSH_Glyph  glyph,
1715                                       FT_Int     dimension )
1716  {
1717
1718#if 1
1719    /* first technique: a point is strong if it is a local extremum */
1720
1721    PSH_Dimension  dim   = &glyph->globals->dimension[dimension];
1722    FT_Fixed       scale = dim->scale_mult;
1723
1724    FT_UInt        count = glyph->num_points;
1725    PSH_Point      point = glyph->points;
1726
1727
1728    for ( ; count > 0; count--, point++ )
1729    {
1730      if ( psh_point_is_strong( point ) )
1731        continue;
1732
1733      /* sometimes, some local extrema are smooth points */
1734      if ( psh_point_is_smooth( point ) )
1735      {
1736        if ( point->dir_in == PSH_DIR_NONE   ||
1737             point->dir_in != point->dir_out )
1738          continue;
1739
1740        if ( !psh_point_is_extremum( point ) &&
1741             !psh_point_is_inflex( point )   )
1742          continue;
1743
1744        point->flags &= ~PSH_POINT_SMOOTH;
1745      }
1746
1747      /* find best enclosing point coordinates */
1748      {
1749        PSH_Point  before = 0;
1750        PSH_Point  after  = 0;
1751
1752        FT_Pos     diff_before = -32000;
1753        FT_Pos     diff_after  =  32000;
1754        FT_Pos     u = point->org_u;
1755
1756        FT_Int     count2 = glyph->num_points;
1757        PSH_Point  cur    = glyph->points;
1758
1759
1760        for ( ; count2 > 0; count2--, cur++ )
1761        {
1762          if ( psh_point_is_strong( cur ) )
1763          {
1764            FT_Pos  diff = cur->org_u - u;
1765
1766
1767            if ( diff <= 0 )
1768            {
1769              if ( diff > diff_before )
1770              {
1771                diff_before = diff;
1772                before      = cur;
1773              }
1774            }
1775
1776            else if ( diff >= 0 )
1777            {
1778              if ( diff < diff_after )
1779              {
1780                diff_after = diff;
1781                after      = cur;
1782              }
1783            }
1784          }
1785        }
1786
1787        if ( !before )
1788        {
1789          if ( !after )
1790            continue;
1791
1792          /* we are before the first strong point coordinate; */
1793          /* simply translate the point                       */
1794          point->cur_u = after->cur_u +
1795                           FT_MulFix( point->org_u - after->org_u, scale );
1796        }
1797        else if ( !after )
1798        {
1799          /* we are after the last strong point coordinate; */
1800          /* simply translate the point                     */
1801          point->cur_u = before->cur_u +
1802                           FT_MulFix( point->org_u - before->org_u, scale );
1803        }
1804        else
1805        {
1806          if ( diff_before == 0 )
1807            point->cur_u = before->cur_u;
1808
1809          else if ( diff_after == 0 )
1810            point->cur_u = after->cur_u;
1811
1812          else
1813            point->cur_u = before->cur_u +
1814                             FT_MulDiv( u - before->org_u,
1815                                        after->cur_u - before->cur_u,
1816                                        after->org_u - before->org_u );
1817        }
1818
1819        psh_point_set_fitted( point );
1820      }
1821    }
1822
1823#endif /* 1 */
1824
1825  }
1826
1827
1828  /* interpolate other points */
1829  static void
1830  psh_glyph_interpolate_other_points( PSH_Glyph  glyph,
1831                                      FT_Int     dimension )
1832  {
1833    PSH_Dimension  dim          = &glyph->globals->dimension[dimension];
1834    FT_Fixed       scale        = dim->scale_mult;
1835    FT_Fixed       delta        = dim->scale_delta;
1836    PSH_Contour    contour      = glyph->contours;
1837    FT_UInt        num_contours = glyph->num_contours;
1838
1839
1840    for ( ; num_contours > 0; num_contours--, contour++ )
1841    {
1842      PSH_Point  start = contour->start;
1843      PSH_Point  first, next, point;
1844      FT_UInt    fit_count;
1845
1846
1847      /* count the number of strong points in this contour */
1848      next      = start + contour->count;
1849      fit_count = 0;
1850      first     = 0;
1851
1852      for ( point = start; point < next; point++ )
1853        if ( psh_point_is_fitted( point ) )
1854        {
1855          if ( !first )
1856            first = point;
1857
1858          fit_count++;
1859        }
1860
1861      /* if there are less than 2 fitted points in the contour, we */
1862      /* simply scale and eventually translate the contour points  */
1863      if ( fit_count < 2 )
1864      {
1865        if ( fit_count == 1 )
1866          delta = first->cur_u - FT_MulFix( first->org_u, scale );
1867
1868        for ( point = start; point < next; point++ )
1869          if ( point != first )
1870            point->cur_u = FT_MulFix( point->org_u, scale ) + delta;
1871
1872        goto Next_Contour;
1873      }
1874
1875      /* there are more than 2 strong points in this contour; we */
1876      /* need to interpolate weak points between them            */
1877      start = first;
1878      do
1879      {
1880        point = first;
1881
1882        /* skip consecutive fitted points */
1883        for (;;)
1884        {
1885          next = first->next;
1886          if ( next == start )
1887            goto Next_Contour;
1888
1889          if ( !psh_point_is_fitted( next ) )
1890            break;
1891
1892          first = next;
1893        }
1894
1895        /* find next fitted point after unfitted one */
1896        for (;;)
1897        {
1898          next = next->next;
1899          if ( psh_point_is_fitted( next ) )
1900            break;
1901        }
1902
1903        /* now interpolate between them */
1904        {
1905          FT_Pos    org_a, org_ab, cur_a, cur_ab;
1906          FT_Pos    org_c, org_ac, cur_c;
1907          FT_Fixed  scale_ab;
1908
1909
1910          if ( first->org_u <= next->org_u )
1911          {
1912            org_a  = first->org_u;
1913            cur_a  = first->cur_u;
1914            org_ab = next->org_u - org_a;
1915            cur_ab = next->cur_u - cur_a;
1916          }
1917          else
1918          {
1919            org_a  = next->org_u;
1920            cur_a  = next->cur_u;
1921            org_ab = first->org_u - org_a;
1922            cur_ab = first->cur_u - cur_a;
1923          }
1924
1925          scale_ab = 0x10000L;
1926          if ( org_ab > 0 )
1927            scale_ab = FT_DivFix( cur_ab, org_ab );
1928
1929          point = first->next;
1930          do
1931          {
1932            org_c  = point->org_u;
1933            org_ac = org_c - org_a;
1934
1935            if ( org_ac <= 0 )
1936            {
1937              /* on the left of the interpolation zone */
1938              cur_c = cur_a + FT_MulFix( org_ac, scale );
1939            }
1940            else if ( org_ac >= org_ab )
1941            {
1942              /* on the right on the interpolation zone */
1943              cur_c = cur_a + cur_ab + FT_MulFix( org_ac - org_ab, scale );
1944            }
1945            else
1946            {
1947              /* within the interpolation zone */
1948              cur_c = cur_a + FT_MulFix( org_ac, scale_ab );
1949            }
1950
1951            point->cur_u = cur_c;
1952
1953            point = point->next;
1954
1955          } while ( point != next );
1956        }
1957
1958        /* keep going until all points in the contours have been processed */
1959        first = next;
1960
1961      } while ( first != start );
1962
1963    Next_Contour:
1964      ;
1965    }
1966  }
1967
1968
1969  /*************************************************************************/
1970  /*************************************************************************/
1971  /*****                                                               *****/
1972  /*****                     HIGH-LEVEL INTERFACE                      *****/
1973  /*****                                                               *****/
1974  /*************************************************************************/
1975  /*************************************************************************/
1976
1977  FT_Error
1978  ps_hints_apply( PS_Hints        ps_hints,
1979                  FT_Outline*     outline,
1980                  PSH_Globals     globals,
1981                  FT_Render_Mode  hint_mode )
1982  {
1983    PSH_GlyphRec  glyphrec;
1984    PSH_Glyph     glyph = &glyphrec;
1985    FT_Error      error;
1986#ifdef DEBUG_HINTER
1987    FT_Memory     memory;
1988#endif
1989    FT_Int        dimension;
1990
1991
1992    /* something to do? */
1993    if ( outline->n_points == 0 || outline->n_contours == 0 )
1994      return PSH_Err_Ok;
1995
1996#ifdef DEBUG_HINTER
1997
1998    memory = globals->memory;
1999
2000    if ( ps_debug_glyph )
2001    {
2002      psh_glyph_done( ps_debug_glyph );
2003      FT_FREE( ps_debug_glyph );
2004    }
2005
2006    if ( FT_NEW( glyph ) )
2007      return error;
2008
2009    ps_debug_glyph = glyph;
2010
2011#endif /* DEBUG_HINTER */
2012
2013    error = psh_glyph_init( glyph, outline, ps_hints, globals );
2014    if ( error )
2015      goto Exit;
2016
2017    /* try to optimize the y_scale so that the top of non-capital letters
2018     * is aligned on a pixel boundary whenever possible
2019     */
2020    {
2021      PSH_Dimension  dim_x = &glyph->globals->dimension[0];
2022      PSH_Dimension  dim_y = &glyph->globals->dimension[1];
2023
2024      FT_Fixed x_scale = dim_x->scale_mult;
2025      FT_Fixed y_scale = dim_y->scale_mult;
2026
2027      FT_Fixed scaled;
2028      FT_Fixed fitted;
2029
2030
2031      scaled = FT_MulFix( globals->blues.normal_top.zones->org_ref, y_scale );
2032      fitted = FT_PIX_ROUND( scaled );
2033
2034      if ( fitted != 0 && scaled != fitted )
2035      {
2036        y_scale = FT_MulDiv( y_scale, fitted, scaled );
2037
2038        if ( fitted < scaled )
2039          x_scale -= x_scale / 50;
2040
2041        psh_globals_set_scale( glyph->globals, x_scale, y_scale, 0, 0 );
2042      }
2043    }
2044
2045    glyph->do_horz_hints = 1;
2046    glyph->do_vert_hints = 1;
2047
2048    glyph->do_horz_snapping = FT_BOOL( hint_mode == FT_RENDER_MODE_MONO ||
2049                                       hint_mode == FT_RENDER_MODE_LCD  );
2050
2051    glyph->do_vert_snapping = FT_BOOL( hint_mode == FT_RENDER_MODE_MONO  ||
2052                                       hint_mode == FT_RENDER_MODE_LCD_V );
2053
2054    glyph->do_stem_adjust   = FT_BOOL( hint_mode != FT_RENDER_MODE_LIGHT );
2055
2056    for ( dimension = 0; dimension < 2; dimension++ )
2057    {
2058      /* load outline coordinates into glyph */
2059      psh_glyph_load_points( glyph, dimension );
2060
2061      /* compute local extrema */
2062      psh_glyph_compute_extrema( glyph );
2063
2064      /* compute aligned stem/hints positions */
2065      psh_hint_table_align_hints( &glyph->hint_tables[dimension],
2066                                  glyph->globals,
2067                                  dimension,
2068                                  glyph );
2069
2070      /* find strong points, align them, then interpolate others */
2071      psh_glyph_find_strong_points( glyph, dimension );
2072      if ( dimension == 1 )
2073        psh_glyph_find_blue_points( &globals->blues, glyph );
2074      psh_glyph_interpolate_strong_points( glyph, dimension );
2075      psh_glyph_interpolate_normal_points( glyph, dimension );
2076      psh_glyph_interpolate_other_points( glyph, dimension );
2077
2078      /* save hinted coordinates back to outline */
2079      psh_glyph_save_points( glyph, dimension );
2080    }
2081
2082  Exit:
2083
2084#ifndef DEBUG_HINTER
2085    psh_glyph_done( glyph );
2086#endif
2087
2088    return error;
2089  }
2090
2091
2092/* END */
Note: See TracBrowser for help on using the repository browser.