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