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