pshalgo.c

Go to the documentation of this file.
00001 /***************************************************************************/
00002 /*                                                                         */
00003 /*  pshalgo.c                                                              */
00004 /*                                                                         */
00005 /*    PostScript hinting algorithm (body).                                 */
00006 /*                                                                         */
00007 /*  Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 by      */
00008 /*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
00009 /*                                                                         */
00010 /*  This file is part of the FreeType project, and may only be used        */
00011 /*  modified and distributed under the terms of the FreeType project       */
00012 /*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
00013 /*  this file you indicate that you have read the license and              */
00014 /*  understand and accept it fully.                                        */
00015 /*                                                                         */
00016 /***************************************************************************/
00017 
00018 
00019 #include <ft2build.h>
00020 #include FT_INTERNAL_OBJECTS_H
00021 #include FT_INTERNAL_DEBUG_H
00022 #include FT_INTERNAL_CALC_H
00023 #include "pshalgo.h"
00024 
00025 #include "pshnterr.h"
00026 
00027 
00028 #undef  FT_COMPONENT
00029 #define FT_COMPONENT  trace_pshalgo2
00030 
00031 
00032 #ifdef DEBUG_HINTER
00033   PSH_Hint_Table  ps_debug_hint_table = 0;
00034   PSH_HintFunc    ps_debug_hint_func  = 0;
00035   PSH_Glyph       ps_debug_glyph      = 0;
00036 #endif
00037 
00038 
00039 #define  COMPUTE_INFLEXS  /* compute inflection points to optimize `S' */
00040                           /* and similar glyphs                        */
00041 #define  STRONGER         /* slightly increase the contrast of smooth  */
00042                           /* hinting                                   */
00043 
00044 
00045   /*************************************************************************/
00046   /*************************************************************************/
00047   /*****                                                               *****/
00048   /*****                  BASIC HINTS RECORDINGS                       *****/
00049   /*****                                                               *****/
00050   /*************************************************************************/
00051   /*************************************************************************/
00052 
00053   /* return true if two stem hints overlap */
00054   static FT_Int
00055   psh_hint_overlap( PSH_Hint  hint1,
00056                     PSH_Hint  hint2 )
00057   {
00058     return hint1->org_pos + hint1->org_len >= hint2->org_pos &&
00059            hint2->org_pos + hint2->org_len >= hint1->org_pos;
00060   }
00061 
00062 
00063   /* destroy hints table */
00064   static void
00065   psh_hint_table_done( PSH_Hint_Table  table,
00066                        FT_Memory       memory )
00067   {
00068     FT_FREE( table->zones );
00069     table->num_zones = 0;
00070     table->zone      = 0;
00071 
00072     FT_FREE( table->sort );
00073     FT_FREE( table->hints );
00074     table->num_hints   = 0;
00075     table->max_hints   = 0;
00076     table->sort_global = 0;
00077   }
00078 
00079 
00080   /* deactivate all hints in a table */
00081   static void
00082   psh_hint_table_deactivate( PSH_Hint_Table  table )
00083   {
00084     FT_UInt   count = table->max_hints;
00085     PSH_Hint  hint  = table->hints;
00086 
00087 
00088     for ( ; count > 0; count--, hint++ )
00089     {
00090       psh_hint_deactivate( hint );
00091       hint->order = -1;
00092     }
00093   }
00094 
00095 
00096   /* internal function to record a new hint */
00097   static void
00098   psh_hint_table_record( PSH_Hint_Table  table,
00099                          FT_UInt         idx )
00100   {
00101     PSH_Hint  hint = table->hints + idx;
00102 
00103 
00104     if ( idx >= table->max_hints )
00105     {
00106       FT_TRACE0(( "psh_hint_table_record: invalid hint index %d\n", idx ));
00107       return;
00108     }
00109 
00110     /* ignore active hints */
00111     if ( psh_hint_is_active( hint ) )
00112       return;
00113 
00114     psh_hint_activate( hint );
00115 
00116     /* now scan the current active hint set to check */
00117     /* whether `hint' overlaps with another hint     */
00118     {
00119       PSH_Hint*  sorted = table->sort_global;
00120       FT_UInt    count  = table->num_hints;
00121       PSH_Hint   hint2;
00122 
00123 
00124       hint->parent = 0;
00125       for ( ; count > 0; count--, sorted++ )
00126       {
00127         hint2 = sorted[0];
00128 
00129         if ( psh_hint_overlap( hint, hint2 ) )
00130         {
00131           hint->parent = hint2;
00132           break;
00133         }
00134       }
00135     }
00136 
00137     if ( table->num_hints < table->max_hints )
00138       table->sort_global[table->num_hints++] = hint;
00139     else
00140       FT_TRACE0(( "psh_hint_table_record: too many sorted hints!  BUG!\n" ));
00141   }
00142 
00143 
00144   static void
00145   psh_hint_table_record_mask( PSH_Hint_Table  table,
00146                               PS_Mask         hint_mask )
00147   {
00148     FT_Int    mask = 0, val = 0;
00149     FT_Byte*  cursor = hint_mask->bytes;
00150     FT_UInt   idx, limit;
00151 
00152 
00153     limit = hint_mask->num_bits;
00154 
00155     for ( idx = 0; idx < limit; idx++ )
00156     {
00157       if ( mask == 0 )
00158       {
00159         val  = *cursor++;
00160         mask = 0x80;
00161       }
00162 
00163       if ( val & mask )
00164         psh_hint_table_record( table, idx );
00165 
00166       mask >>= 1;
00167     }
00168   }
00169 
00170 
00171   /* create hints table */
00172   static FT_Error
00173   psh_hint_table_init( PSH_Hint_Table  table,
00174                        PS_Hint_Table   hints,
00175                        PS_Mask_Table   hint_masks,
00176                        PS_Mask_Table   counter_masks,
00177                        FT_Memory       memory )
00178   {
00179     FT_UInt   count;
00180     FT_Error  error;
00181 
00182     FT_UNUSED( counter_masks );
00183 
00184 
00185     count = hints->num_hints;
00186 
00187     /* allocate our tables */
00188     if ( FT_NEW_ARRAY( table->sort,  2 * count     ) ||
00189          FT_NEW_ARRAY( table->hints,     count     ) ||
00190          FT_NEW_ARRAY( table->zones, 2 * count + 1 ) )
00191       goto Exit;
00192 
00193     table->max_hints   = count;
00194     table->sort_global = table->sort + count;
00195     table->num_hints   = 0;
00196     table->num_zones   = 0;
00197     table->zone        = 0;
00198 
00199     /* initialize the `table->hints' array */
00200     {
00201       PSH_Hint  write = table->hints;
00202       PS_Hint   read  = hints->hints;
00203 
00204 
00205       for ( ; count > 0; count--, write++, read++ )
00206       {
00207         write->org_pos = read->pos;
00208         write->org_len = read->len;
00209         write->flags   = read->flags;
00210       }
00211     }
00212 
00213     /* we now need to determine the initial `parent' stems; first  */
00214     /* activate the hints that are given by the initial hint masks */
00215     if ( hint_masks )
00216     {
00217       PS_Mask  mask = hint_masks->masks;
00218 
00219 
00220       count             = hint_masks->num_masks;
00221       table->hint_masks = hint_masks;
00222 
00223       for ( ; count > 0; count--, mask++ )
00224         psh_hint_table_record_mask( table, mask );
00225     }
00226 
00227     /* finally, do a linear parse in case some hints were left alone */
00228     if ( table->num_hints != table->max_hints )
00229     {
00230       FT_UInt  idx;
00231 
00232 
00233       FT_TRACE0(( "psh_hint_table_init: missing/incorrect hint masks\n" ));
00234 
00235       count = table->max_hints;
00236       for ( idx = 0; idx < count; idx++ )
00237         psh_hint_table_record( table, idx );
00238     }
00239 
00240   Exit:
00241     return error;
00242   }
00243 
00244 
00245   static void
00246   psh_hint_table_activate_mask( PSH_Hint_Table  table,
00247                                 PS_Mask         hint_mask )
00248   {
00249     FT_Int    mask = 0, val = 0;
00250     FT_Byte*  cursor = hint_mask->bytes;
00251     FT_UInt   idx, limit, count;
00252 
00253 
00254     limit = hint_mask->num_bits;
00255     count = 0;
00256 
00257     psh_hint_table_deactivate( table );
00258 
00259     for ( idx = 0; idx < limit; idx++ )
00260     {
00261       if ( mask == 0 )
00262       {
00263         val  = *cursor++;
00264         mask = 0x80;
00265       }
00266 
00267       if ( val & mask )
00268       {
00269         PSH_Hint  hint = &table->hints[idx];
00270 
00271 
00272         if ( !psh_hint_is_active( hint ) )
00273         {
00274           FT_UInt     count2;
00275 
00276 #if 0
00277           PSH_Hint*  sort = table->sort;
00278           PSH_Hint   hint2;
00279 
00280 
00281           for ( count2 = count; count2 > 0; count2--, sort++ )
00282           {
00283             hint2 = sort[0];
00284             if ( psh_hint_overlap( hint, hint2 ) )
00285               FT_TRACE0(( "psh_hint_table_activate_mask:"
00286                           " found overlapping hints\n" ))
00287           }
00288 #else
00289           count2 = 0;
00290 #endif
00291 
00292           if ( count2 == 0 )
00293           {
00294             psh_hint_activate( hint );
00295             if ( count < table->max_hints )
00296               table->sort[count++] = hint;
00297             else
00298               FT_TRACE0(( "psh_hint_tableactivate_mask:"
00299                           " too many active hints\n" ));
00300           }
00301         }
00302       }
00303 
00304       mask >>= 1;
00305     }
00306     table->num_hints = count;
00307 
00308     /* now, sort the hints; they are guaranteed to not overlap */
00309     /* so we can compare their "org_pos" field directly        */
00310     {
00311       FT_Int     i1, i2;
00312       PSH_Hint   hint1, hint2;
00313       PSH_Hint*  sort = table->sort;
00314 
00315 
00316       /* a simple bubble sort will do, since in 99% of cases, the hints */
00317       /* will be already sorted -- and the sort will be linear          */
00318       for ( i1 = 1; i1 < (FT_Int)count; i1++ )
00319       {
00320         hint1 = sort[i1];
00321         for ( i2 = i1 - 1; i2 >= 0; i2-- )
00322         {
00323           hint2 = sort[i2];
00324 
00325           if ( hint2->org_pos < hint1->org_pos )
00326             break;
00327 
00328           sort[i2 + 1] = hint2;
00329           sort[i2]     = hint1;
00330         }
00331       }
00332     }
00333   }
00334 
00335 
00336   /*************************************************************************/
00337   /*************************************************************************/
00338   /*****                                                               *****/
00339   /*****               HINTS GRID-FITTING AND OPTIMIZATION             *****/
00340   /*****                                                               *****/
00341   /*************************************************************************/
00342   /*************************************************************************/
00343 
00344 #if 1
00345   static FT_Pos
00346   psh_dimension_quantize_len( PSH_Dimension  dim,
00347                               FT_Pos         len,
00348                               FT_Bool        do_snapping )
00349   {
00350     if ( len <= 64 )
00351       len = 64;
00352     else
00353     {
00354       FT_Pos  delta = len - dim->stdw.widths[0].cur;
00355 
00356 
00357       if ( delta < 0 )
00358         delta = -delta;
00359 
00360       if ( delta < 40 )
00361       {
00362         len = dim->stdw.widths[0].cur;
00363         if ( len < 48 )
00364           len = 48;
00365       }
00366 
00367       if ( len < 3 * 64 )
00368       {
00369         delta = ( len & 63 );
00370         len  &= -64;
00371 
00372         if ( delta < 10 )
00373           len += delta;
00374 
00375         else if ( delta < 32 )
00376           len += 10;
00377 
00378         else if ( delta < 54 )
00379           len += 54;
00380 
00381         else
00382           len += delta;
00383       }
00384       else
00385         len = FT_PIX_ROUND( len );
00386     }
00387 
00388     if ( do_snapping )
00389       len = FT_PIX_ROUND( len );
00390 
00391     return  len;
00392   }
00393 #endif /* 0 */
00394 
00395 
00396 #ifdef DEBUG_HINTER
00397 
00398   static void
00399   ps_simple_scale( PSH_Hint_Table  table,
00400                    FT_Fixed        scale,
00401                    FT_Fixed        delta,
00402                    FT_Int          dimension )
00403   {
00404     PSH_Hint  hint;
00405     FT_UInt   count;
00406 
00407 
00408     for ( count = 0; count < table->max_hints; count++ )
00409     {
00410       hint = table->hints + count;
00411 
00412       hint->cur_pos = FT_MulFix( hint->org_pos, scale ) + delta;
00413       hint->cur_len = FT_MulFix( hint->org_len, scale );
00414 
00415       if ( ps_debug_hint_func )
00416         ps_debug_hint_func( hint, dimension );
00417     }
00418   }
00419 
00420 #endif /* DEBUG_HINTER */
00421 
00422 
00423   static FT_Fixed
00424   psh_hint_snap_stem_side_delta( FT_Fixed  pos,
00425                                  FT_Fixed  len )
00426   {
00427     FT_Fixed  delta1 = FT_PIX_ROUND( pos ) - pos;
00428     FT_Fixed  delta2 = FT_PIX_ROUND( pos + len ) - pos - len;
00429 
00430 
00431     if ( FT_ABS( delta1 ) <= FT_ABS( delta2 ) )
00432       return delta1;
00433     else
00434       return delta2;
00435   }
00436 
00437 
00438   static void
00439   psh_hint_align( PSH_Hint     hint,
00440                   PSH_Globals  globals,
00441                   FT_Int       dimension,
00442                   PSH_Glyph    glyph )
00443   {
00444     PSH_Dimension  dim   = &globals->dimension[dimension];
00445     FT_Fixed       scale = dim->scale_mult;
00446     FT_Fixed       delta = dim->scale_delta;
00447 
00448 
00449     if ( !psh_hint_is_fitted( hint ) )
00450     {
00451       FT_Pos  pos = FT_MulFix( hint->org_pos, scale ) + delta;
00452       FT_Pos  len = FT_MulFix( hint->org_len, scale );
00453 
00454       FT_Int            do_snapping;
00455       FT_Pos            fit_len;
00456       PSH_AlignmentRec  align;
00457 
00458 
00459       /* ignore stem alignments when requested through the hint flags */
00460       if ( ( dimension == 0 && !glyph->do_horz_hints ) ||
00461            ( dimension == 1 && !glyph->do_vert_hints ) )
00462       {
00463         hint->cur_pos = pos;
00464         hint->cur_len = len;
00465 
00466         psh_hint_set_fitted( hint );
00467         return;
00468       }
00469 
00470       /* perform stem snapping when requested - this is necessary
00471        * for monochrome and LCD hinting modes only
00472        */
00473       do_snapping = ( dimension == 0 && glyph->do_horz_snapping ) ||
00474                     ( dimension == 1 && glyph->do_vert_snapping );
00475 
00476       hint->cur_len = fit_len = len;
00477 
00478       /* check blue zones for horizontal stems */
00479       align.align     = PSH_BLUE_ALIGN_NONE;
00480       align.align_bot = align.align_top = 0;
00481 
00482       if ( dimension == 1 )
00483         psh_blues_snap_stem( &globals->blues,
00484                              hint->org_pos + hint->org_len,
00485                              hint->org_pos,
00486                              &align );
00487 
00488       switch ( align.align )
00489       {
00490       case PSH_BLUE_ALIGN_TOP:
00491         /* the top of the stem is aligned against a blue zone */
00492         hint->cur_pos = align.align_top - fit_len;
00493         break;
00494 
00495       case PSH_BLUE_ALIGN_BOT:
00496         /* the bottom of the stem is aligned against a blue zone */
00497         hint->cur_pos = align.align_bot;
00498         break;
00499 
00500       case PSH_BLUE_ALIGN_TOP | PSH_BLUE_ALIGN_BOT:
00501         /* both edges of the stem are aligned against blue zones */
00502         hint->cur_pos = align.align_bot;
00503         hint->cur_len = align.align_top - align.align_bot;
00504         break;
00505 
00506       default:
00507         {
00508           PSH_Hint  parent = hint->parent;
00509 
00510 
00511           if ( parent )
00512           {
00513             FT_Pos  par_org_center, par_cur_center;
00514             FT_Pos  cur_org_center, cur_delta;
00515 
00516 
00517             /* ensure that parent is already fitted */
00518             if ( !psh_hint_is_fitted( parent ) )
00519               psh_hint_align( parent, globals, dimension, glyph );
00520 
00521             /* keep original relation between hints, this is, use the */
00522             /* scaled distance between the centers of the hints to    */
00523             /* compute the new position                               */
00524             par_org_center = parent->org_pos + ( parent->org_len >> 1 );
00525             par_cur_center = parent->cur_pos + ( parent->cur_len >> 1 );
00526             cur_org_center = hint->org_pos   + ( hint->org_len   >> 1 );
00527 
00528             cur_delta = FT_MulFix( cur_org_center - par_org_center, scale );
00529             pos       = par_cur_center + cur_delta - ( len >> 1 );
00530           }
00531 
00532           hint->cur_pos = pos;
00533           hint->cur_len = fit_len;
00534 
00535           /* Stem adjustment tries to snap stem widths to standard
00536            * ones.  This is important to prevent unpleasant rounding
00537            * artefacts.
00538            */
00539           if ( glyph->do_stem_adjust )
00540           {
00541             if ( len <= 64 )
00542             {
00543               /* the stem is less than one pixel; we will center it
00544                * around the nearest pixel center
00545                */
00546               if ( len >= 32 )
00547               {
00548                 /* This is a special case where we also widen the stem
00549                  * and align it to the pixel grid.
00550                  *
00551                  *   stem_center          = pos + (len/2)
00552                  *   nearest_pixel_center = FT_ROUND(stem_center-32)+32
00553                  *   new_pos              = nearest_pixel_center-32
00554                  *                        = FT_ROUND(stem_center-32)
00555                  *                        = FT_FLOOR(stem_center-32+32)
00556                  *                        = FT_FLOOR(stem_center)
00557                  *   new_len              = 64
00558                  */
00559                 pos = FT_PIX_FLOOR( pos + ( len >> 1 ) );
00560                 len = 64;
00561               }
00562               else if ( len > 0 )
00563               {
00564                 /* This is a very small stem; we simply align it to the
00565                  * pixel grid, trying to find the minimal displacement.
00566                  *
00567                  * left               = pos
00568                  * right              = pos + len
00569                  * left_nearest_edge  = ROUND(pos)
00570                  * right_nearest_edge = ROUND(right)
00571                  *
00572                  * if ( ABS(left_nearest_edge - left) <=
00573                  *      ABS(right_nearest_edge - right) )
00574                  *    new_pos = left
00575                  * else
00576                  *    new_pos = right
00577                  */
00578                 FT_Pos  left_nearest  = FT_PIX_ROUND( pos );
00579                 FT_Pos  right_nearest = FT_PIX_ROUND( pos + len );
00580                 FT_Pos  left_disp     = left_nearest - pos;
00581                 FT_Pos  right_disp    = right_nearest - ( pos + len );
00582 
00583 
00584                 if ( left_disp < 0 )
00585                   left_disp = -left_disp;
00586                 if ( right_disp < 0 )
00587                   right_disp = -right_disp;
00588                 if ( left_disp <= right_disp )
00589                   pos = left_nearest;
00590                 else
00591                   pos = right_nearest;
00592               }
00593               else
00594               {
00595                 /* this is a ghost stem; we simply round it */
00596                 pos = FT_PIX_ROUND( pos );
00597               }
00598             }
00599             else
00600             {
00601               len = psh_dimension_quantize_len( dim, len, 0 );
00602             }
00603           }
00604 
00605           /* now that we have a good hinted stem width, try to position */
00606           /* the stem along a pixel grid integer coordinate             */
00607           hint->cur_pos = pos + psh_hint_snap_stem_side_delta( pos, len );
00608           hint->cur_len = len;
00609         }
00610       }
00611 
00612       if ( do_snapping )
00613       {
00614         pos = hint->cur_pos;
00615         len = hint->cur_len;
00616 
00617         if ( len < 64 )
00618           len = 64;
00619         else
00620           len = FT_PIX_ROUND( len );
00621 
00622         switch ( align.align )
00623         {
00624           case PSH_BLUE_ALIGN_TOP:
00625             hint->cur_pos = align.align_top - len;
00626             hint->cur_len = len;
00627             break;
00628 
00629           case PSH_BLUE_ALIGN_BOT:
00630             hint->cur_len = len;
00631             break;
00632 
00633           case PSH_BLUE_ALIGN_BOT | PSH_BLUE_ALIGN_TOP:
00634             /* don't touch */
00635             break;
00636 
00637 
00638           default:
00639             hint->cur_len = len;
00640             if ( len & 64 )
00641               pos = FT_PIX_FLOOR( pos + ( len >> 1 ) ) + 32;
00642             else
00643               pos = FT_PIX_ROUND( pos + ( len >> 1 ) );
00644 
00645             hint->cur_pos = pos - ( len >> 1 );
00646             hint->cur_len = len;
00647         }
00648       }
00649 
00650       psh_hint_set_fitted( hint );
00651 
00652 #ifdef DEBUG_HINTER
00653       if ( ps_debug_hint_func )
00654         ps_debug_hint_func( hint, dimension );
00655 #endif
00656     }
00657   }
00658 
00659 
00660 #if 0  /* not used for now, experimental */
00661 
00662  /*
00663   *  A variant to perform "light" hinting (i.e. FT_RENDER_MODE_LIGHT)
00664   *  of stems
00665   */
00666   static void
00667   psh_hint_align_light( PSH_Hint     hint,
00668                         PSH_Globals  globals,
00669                         FT_Int       dimension,
00670                         PSH_Glyph    glyph )
00671   {
00672     PSH_Dimension  dim   = &globals->dimension[dimension];
00673     FT_Fixed       scale = dim->scale_mult;
00674     FT_Fixed       delta = dim->scale_delta;
00675 
00676 
00677     if ( !psh_hint_is_fitted( hint ) )
00678     {
00679       FT_Pos  pos = FT_MulFix( hint->org_pos, scale ) + delta;
00680       FT_Pos  len = FT_MulFix( hint->org_len, scale );
00681 
00682       FT_Pos  fit_len;
00683 
00684       PSH_AlignmentRec  align;
00685 
00686 
00687       /* ignore stem alignments when requested through the hint flags */
00688       if ( ( dimension == 0 && !glyph->do_horz_hints ) ||
00689            ( dimension == 1 && !glyph->do_vert_hints ) )
00690       {
00691         hint->cur_pos = pos;
00692         hint->cur_len = len;
00693 
00694         psh_hint_set_fitted( hint );
00695         return;
00696       }
00697 
00698       fit_len = len;
00699 
00700       hint->cur_len = fit_len;
00701 
00702       /* check blue zones for horizontal stems */
00703       align.align = PSH_BLUE_ALIGN_NONE;
00704       align.align_bot = align.align_top = 0;
00705 
00706       if ( dimension == 1 )
00707         psh_blues_snap_stem( &globals->blues,
00708                              hint->org_pos + hint->org_len,
00709                              hint->org_pos,
00710                              &align );
00711 
00712       switch ( align.align )
00713       {
00714       case PSH_BLUE_ALIGN_TOP:
00715         /* the top of the stem is aligned against a blue zone */
00716         hint->cur_pos = align.align_top - fit_len;
00717         break;
00718 
00719       case PSH_BLUE_ALIGN_BOT:
00720         /* the bottom of the stem is aligned against a blue zone */
00721         hint->cur_pos = align.align_bot;
00722         break;
00723 
00724       case PSH_BLUE_ALIGN_TOP | PSH_BLUE_ALIGN_BOT:
00725         /* both edges of the stem are aligned against blue zones */
00726         hint->cur_pos = align.align_bot;
00727         hint->cur_len = align.align_top - align.align_bot;
00728         break;
00729 
00730       default:
00731         {
00732           PSH_Hint  parent = hint->parent;
00733 
00734 
00735           if ( parent )
00736           {
00737             FT_Pos  par_org_center, par_cur_center;
00738             FT_Pos  cur_org_center, cur_delta;
00739 
00740 
00741             /* ensure that parent is already fitted */
00742             if ( !psh_hint_is_fitted( parent ) )
00743               psh_hint_align_light( parent, globals, dimension, glyph );
00744 
00745             par_org_center = parent->org_pos + ( parent->org_len / 2 );
00746             par_cur_center = parent->cur_pos + ( parent->cur_len / 2 );
00747             cur_org_center = hint->org_pos   + ( hint->org_len   / 2 );
00748 
00749             cur_delta = FT_MulFix( cur_org_center - par_org_center, scale );
00750             pos       = par_cur_center + cur_delta - ( len >> 1 );
00751           }
00752 
00753           /* Stems less than one pixel wide are easy -- we want to
00754            * make them as dark as possible, so they must fall within
00755            * one pixel.  If the stem is split between two pixels
00756            * then snap the edge that is nearer to the pixel boundary
00757            * to the pixel boundary.
00758            */
00759           if ( len <= 64 )
00760           {
00761             if ( ( pos + len + 63 ) / 64  != pos / 64 + 1 )
00762               pos += psh_hint_snap_stem_side_delta ( pos, len );
00763           }
00764 
00765           /* Position stems other to minimize the amount of mid-grays.
00766            * There are, in general, two positions that do this,
00767            * illustrated as A) and B) below.
00768            *
00769            *   +                   +                   +                   +
00770            *
00771            * A)             |--------------------------------|
00772            * B)   |--------------------------------|
00773            * C)       |--------------------------------|
00774            *
00775            * Position A) (split the excess stem equally) should be better
00776            * for stems of width N + f where f < 0.5.
00777            *
00778            * Position B) (split the deficiency equally) should be better
00779            * for stems of width N + f where f > 0.5.
00780            *
00781            * It turns out though that minimizing the total number of lit
00782            * pixels is also important, so position C), with one edge
00783            * aligned with a pixel boundary is actually preferable
00784            * to A).  There are also more possibile positions for C) than
00785            * for A) or B), so it involves less distortion of the overall
00786            * character shape.
00787            */
00788           else /* len > 64 */
00789           {
00790             FT_Fixed  frac_len = len & 63;
00791             FT_Fixed  center = pos + ( len >> 1 );
00792             FT_Fixed  delta_a, delta_b;
00793 
00794 
00795             if ( ( len / 64 ) & 1 )
00796             {
00797               delta_a = FT_PIX_FLOOR( center ) + 32 - center;
00798               delta_b = FT_PIX_ROUND( center ) - center;
00799             }
00800             else
00801             {
00802               delta_a = FT_PIX_ROUND( center ) - center;
00803               delta_b = FT_PIX_FLOOR( center ) + 32 - center;
00804             }
00805 
00806             /* We choose between B) and C) above based on the amount
00807              * of fractinal stem width; for small amounts, choose
00808              * C) always, for large amounts, B) always, and inbetween,
00809              * pick whichever one involves less stem movement.
00810              */
00811             if ( frac_len < 32 )
00812             {
00813               pos += psh_hint_snap_stem_side_delta ( pos, len );
00814             }
00815             else if ( frac_len < 48 )
00816             {
00817               FT_Fixed  side_delta = psh_hint_snap_stem_side_delta ( pos,
00818                                                                      len );
00819 
00820               if ( FT_ABS( side_delta ) < FT_ABS( delta_b ) )
00821                 pos += side_delta;
00822               else
00823                 pos += delta_b;
00824             }
00825             else
00826             {
00827               pos += delta_b;
00828             }
00829           }
00830 
00831           hint->cur_pos = pos;
00832         }
00833       }  /* switch */
00834 
00835       psh_hint_set_fitted( hint );
00836 
00837 #ifdef DEBUG_HINTER
00838       if ( ps_debug_hint_func )
00839         ps_debug_hint_func( hint, dimension );
00840 #endif
00841     }
00842   }
00843 
00844 #endif /* 0 */
00845 
00846 
00847   static void
00848   psh_hint_table_align_hints( PSH_Hint_Table  table,
00849                               PSH_Globals     globals,
00850                               FT_Int          dimension,
00851                               PSH_Glyph       glyph )
00852   {
00853     PSH_Hint       hint;
00854     FT_UInt        count;
00855 
00856 #ifdef DEBUG_HINTER
00857 
00858     PSH_Dimension  dim   = &globals->dimension[dimension];
00859     FT_Fixed       scale = dim->scale_mult;
00860     FT_Fixed       delta = dim->scale_delta;
00861 
00862 
00863     if ( ps_debug_no_vert_hints && dimension == 0 )
00864     {
00865       ps_simple_scale( table, scale, delta, dimension );
00866       return;
00867     }
00868 
00869     if ( ps_debug_no_horz_hints && dimension == 1 )
00870     {
00871       ps_simple_scale( table, scale, delta, dimension );
00872       return;
00873     }
00874 
00875 #endif /* DEBUG_HINTER*/
00876 
00877     hint  = table->hints;
00878     count = table->max_hints;
00879 
00880     for ( ; count > 0; count--, hint++ )
00881       psh_hint_align( hint, globals, dimension, glyph );
00882   }
00883 
00884 
00885   /*************************************************************************/
00886   /*************************************************************************/
00887   /*****                                                               *****/
00888   /*****                POINTS INTERPOLATION ROUTINES                  *****/
00889   /*****                                                               *****/
00890   /*************************************************************************/
00891   /*************************************************************************/
00892 
00893 #define PSH_ZONE_MIN  -3200000L
00894 #define PSH_ZONE_MAX  +3200000L
00895 
00896 #define xxDEBUG_ZONES
00897 
00898 
00899 #ifdef DEBUG_ZONES
00900 
00901 #include FT_CONFIG_STANDARD_LIBRARY_H
00902 
00903   static void
00904   psh_print_zone( PSH_Zone  zone )
00905   {
00906     printf( "zone [scale,delta,min,max] = [%.3f,%.3f,%d,%d]\n",
00907              zone->scale / 65536.0,
00908              zone->delta / 64.0,
00909              zone->min,
00910              zone->max );
00911   }
00912 
00913 #else
00914 
00915 #define psh_print_zone( x )  do { } while ( 0 )
00916 
00917 #endif /* DEBUG_ZONES */
00918 
00919 
00920   /*************************************************************************/
00921   /*************************************************************************/
00922   /*****                                                               *****/
00923   /*****                    HINTER GLYPH MANAGEMENT                    *****/
00924   /*****                                                               *****/
00925   /*************************************************************************/
00926   /*************************************************************************/
00927 
00928 #if 1
00929 
00930 #define  psh_corner_is_flat      ft_corner_is_flat
00931 #define  psh_corner_orientation  ft_corner_orientation
00932 
00933 #else
00934 
00935   FT_LOCAL_DEF( FT_Int )
00936   psh_corner_is_flat( FT_Pos  x_in,
00937                       FT_Pos  y_in,
00938                       FT_Pos  x_out,
00939                       FT_Pos  y_out )
00940   {
00941     FT_Pos  ax = x_in;
00942     FT_Pos  ay = y_in;
00943 
00944     FT_Pos  d_in, d_out, d_corner;
00945 
00946 
00947     if ( ax < 0 )
00948       ax = -ax;
00949     if ( ay < 0 )
00950       ay = -ay;
00951     d_in = ax + ay;
00952 
00953     ax = x_out;
00954     if ( ax < 0 )
00955       ax = -ax;
00956     ay = y_out;
00957     if ( ay < 0 )
00958       ay = -ay;
00959     d_out = ax + ay;
00960 
00961     ax = x_out + x_in;
00962     if ( ax < 0 )
00963       ax = -ax;
00964     ay = y_out + y_in;
00965     if ( ay < 0 )
00966       ay = -ay;
00967     d_corner = ax + ay;
00968 
00969     return ( d_in + d_out - d_corner ) < ( d_corner >> 4 );
00970   }
00971 
00972   static FT_Int
00973   psh_corner_orientation( FT_Pos  in_x,
00974                           FT_Pos  in_y,
00975                           FT_Pos  out_x,
00976                           FT_Pos  out_y )
00977   {
00978     FT_Int  result;
00979 
00980 
00981     /* deal with the trivial cases quickly */
00982     if ( in_y == 0 )
00983     {
00984       if ( in_x >= 0 )
00985         result = out_y;
00986       else
00987         result = -out_y;
00988     }
00989     else if ( in_x == 0 )
00990     {
00991       if ( in_y >= 0 )
00992         result = -out_x;
00993       else
00994         result = out_x;
00995     }
00996     else if ( out_y == 0 )
00997     {
00998       if ( out_x >= 0 )
00999         result = in_y;
01000       else
01001         result = -in_y;
01002     }
01003     else if ( out_x == 0 )
01004     {
01005       if ( out_y >= 0 )
01006         result = -in_x;
01007       else
01008         result =  in_x;
01009     }
01010     else /* general case */
01011     {
01012       long long  delta = (long long)in_x * out_y - (long long)in_y * out_x;
01013 
01014       if ( delta == 0 )
01015         result = 0;
01016       else
01017         result = 1 - 2 * ( delta < 0 );
01018     }
01019 
01020     return result;
01021   }
01022 
01023 #endif /* !1 */
01024 
01025 
01026 #ifdef COMPUTE_INFLEXS
01027 
01028   /* compute all inflex points in a given glyph */
01029   static void
01030   psh_glyph_compute_inflections( PSH_Glyph  glyph )
01031   {
01032     FT_UInt  n;
01033 
01034 
01035     for ( n = 0; n < glyph->num_contours; n++ )
01036     {
01037       PSH_Point  first, start, end, before, after;
01038       FT_Pos     in_x, in_y, out_x, out_y;
01039       FT_Int     orient_prev, orient_cur;
01040       FT_Int     finished = 0;
01041 
01042 
01043       /* we need at least 4 points to create an inflection point */
01044       if ( glyph->contours[n].count < 4 )
01045         continue;
01046 
01047       /* compute first segment in contour */
01048       first = glyph->contours[n].start;
01049 
01050       start = end = first;
01051       do
01052       {
01053         end = end->next;
01054         if ( end == first )
01055           goto Skip;
01056 
01057         in_x = end->org_u - start->org_u;
01058         in_y = end->org_v - start->org_v;
01059 
01060       } while ( in_x == 0 && in_y == 0 );
01061 
01062       /* extend the segment start whenever possible */
01063       before = start;
01064       do
01065       {
01066         do
01067         {
01068           start  = before;
01069           before = before->prev;
01070           if ( before == first )
01071             goto Skip;
01072 
01073           out_x = start->org_u - before->org_u;
01074           out_y = start->org_v - before->org_v;
01075 
01076         } while ( out_x == 0 && out_y == 0 );
01077 
01078         orient_prev = psh_corner_orientation( in_x, in_y, out_x, out_y );
01079 
01080       } while ( orient_prev == 0 );
01081 
01082       first = start;
01083       in_x  = out_x;
01084       in_y  = out_y;
01085 
01086       /* now, process all segments in the contour */
01087       do
01088       {
01089         /* first, extend current segment's end whenever possible */
01090         after = end;
01091         do
01092         {
01093           do
01094           {
01095             end   = after;
01096             after = after->next;
01097             if ( after == first )
01098               finished = 1;
01099 
01100             out_x = after->org_u - end->org_u;
01101             out_y = after->org_v - end->org_v;
01102 
01103           } while ( out_x == 0 && out_y == 0 );
01104 
01105           orient_cur = psh_corner_orientation( in_x, in_y, out_x, out_y );
01106 
01107         } while ( orient_cur == 0 );
01108 
01109         if ( ( orient_cur ^ orient_prev ) < 0 )
01110         {
01111           do
01112           {
01113             psh_point_set_inflex( start );
01114             start = start->next;
01115           }
01116           while ( start != end );
01117 
01118           psh_point_set_inflex( start );
01119         }
01120 
01121         start       = end;
01122         end         = after;
01123         orient_prev = orient_cur;
01124         in_x        = out_x;
01125         in_y        = out_y;
01126 
01127       } while ( !finished );
01128 
01129     Skip:
01130       ;
01131     }
01132   }
01133 
01134 #endif /* COMPUTE_INFLEXS */
01135 
01136 
01137   static void
01138   psh_glyph_done( PSH_Glyph  glyph )
01139   {
01140     FT_Memory  memory = glyph->memory;
01141 
01142 
01143     psh_hint_table_done( &glyph->hint_tables[1], memory );
01144     psh_hint_table_done( &glyph->hint_tables[0], memory );
01145 
01146     FT_FREE( glyph->points );
01147     FT_FREE( glyph->contours );
01148 
01149     glyph->num_points   = 0;
01150     glyph->num_contours = 0;
01151 
01152     glyph->memory = 0;
01153   }
01154 
01155 
01156   static int
01157   psh_compute_dir( FT_Pos  dx,
01158                    FT_Pos  dy )
01159   {
01160     FT_Pos  ax, ay;
01161     int     result = PSH_DIR_NONE;
01162 
01163 
01164     ax = ( dx >= 0 ) ? dx : -dx;
01165     ay = ( dy >= 0 ) ? dy : -dy;
01166 
01167     if ( ay * 12 < ax )
01168     {
01169       /* |dy| <<< |dx|  means a near-horizontal segment */
01170       result = ( dx >= 0 ) ? PSH_DIR_RIGHT : PSH_DIR_LEFT;
01171     }
01172     else if ( ax * 12 < ay )
01173     {
01174       /* |dx| <<< |dy|  means a near-vertical segment */
01175       result = ( dy >= 0 ) ? PSH_DIR_UP : PSH_DIR_DOWN;
01176     }
01177 
01178     return result;
01179   }
01180 
01181 
01182   /* load outline point coordinates into hinter glyph */
01183   static void
01184   psh_glyph_load_points( PSH_Glyph  glyph,
01185                          FT_Int     dimension )
01186   {
01187     FT_Vector*  vec   = glyph->outline->points;
01188     PSH_Point   point = glyph->points;
01189     FT_UInt     count = glyph->num_points;
01190 
01191 
01192     for ( ; count > 0; count--, point++, vec++ )
01193     {
01194       point->flags2 = 0;
01195       point->hint   = NULL;
01196       if ( dimension == 0 )
01197       {
01198         point->org_u = vec->x;
01199         point->org_v = vec->y;
01200       }
01201       else
01202       {
01203         point->org_u = vec->y;
01204         point->org_v = vec->x;
01205       }
01206 
01207 #ifdef DEBUG_HINTER
01208       point->org_x = vec->x;
01209       point->org_y = vec->y;
01210 #endif
01211 
01212     }
01213   }
01214 
01215 
01216   /* save hinted point coordinates back to outline */
01217   static void
01218   psh_glyph_save_points( PSH_Glyph  glyph,
01219                          FT_Int     dimension )
01220   {
01221     FT_UInt     n;
01222     PSH_Point   point = glyph->points;
01223     FT_Vector*  vec   = glyph->outline->points;
01224     char*       tags  = glyph->outline->tags;
01225 
01226 
01227     for ( n = 0; n < glyph->num_points; n++ )
01228     {
01229       if ( dimension == 0 )
01230         vec[n].x = point->cur_u;
01231       else
01232         vec[n].y = point->cur_u;
01233 
01234       if ( psh_point_is_strong( point ) )
01235         tags[n] |= (char)( ( dimension == 0 ) ? 32 : 64 );
01236 
01237 #ifdef DEBUG_HINTER
01238 
01239       if ( dimension == 0 )
01240       {
01241         point->cur_x   = point->cur_u;
01242         point->flags_x = point->flags2 | point->flags;
01243       }
01244       else
01245       {
01246         point->cur_y   = point->cur_u;
01247         point->flags_y = point->flags2 | point->flags;
01248       }
01249 
01250 #endif
01251 
01252       point++;
01253     }
01254   }
01255 
01256 
01257   static FT_Error
01258   psh_glyph_init( PSH_Glyph    glyph,
01259                   FT_Outline*  outline,
01260                   PS_Hints     ps_hints,
01261                   PSH_Globals  globals )
01262   {
01263     FT_Error   error;
01264     FT_Memory  memory;
01265 
01266 
01267     /* clear all fields */
01268     FT_MEM_ZERO( glyph, sizeof ( *glyph ) );
01269 
01270     memory = glyph->memory = globals->memory;
01271 
01272     /* allocate and setup points + contours arrays */
01273     if ( FT_NEW_ARRAY( glyph->points,   outline->n_points   ) ||
01274          FT_NEW_ARRAY( glyph->contours, outline->n_contours ) )
01275       goto Exit;
01276 
01277     glyph->num_points   = outline->n_points;
01278     glyph->num_contours = outline->n_contours;
01279 
01280     {
01281       FT_UInt      first = 0, next, n;
01282       PSH_Point    points  = glyph->points;
01283       PSH_Contour  contour = glyph->contours;
01284 
01285 
01286       for ( n = 0; n < glyph->num_contours; n++ )
01287       {
01288         FT_Int     count;
01289         PSH_Point  point;
01290 
01291 
01292         next  = outline->contours[n] + 1;
01293         count = next - first;
01294 
01295         contour->start = points + first;
01296         contour->count = (FT_UInt)count;
01297 
01298         if ( count > 0 )
01299         {
01300           point = points + first;
01301 
01302           point->prev    = points + next - 1;
01303           point->contour = contour;
01304 
01305           for ( ; count > 1; count-- )
01306           {
01307             point[0].next = point + 1;
01308             point[1].prev = point;
01309             point++;
01310             point->contour = contour;
01311           }
01312           point->next = points + first;
01313         }
01314 
01315         contour++;
01316         first = next;
01317       }
01318     }
01319 
01320     {
01321       PSH_Point   points = glyph->points;
01322       PSH_Point   point  = points;
01323       FT_Vector*  vec    = outline->points;
01324       FT_UInt     n;
01325 
01326 
01327       for ( n = 0; n < glyph->num_points; n++, point++ )
01328       {
01329         FT_Int  n_prev = (FT_Int)( point->prev - points );
01330         FT_Int  n_next = (FT_Int)( point->next - points );
01331         FT_Pos  dxi, dyi, dxo, dyo;
01332 
01333 
01334         if ( !( outline->tags[n] & FT_CURVE_TAG_ON ) )
01335           point->flags = PSH_POINT_OFF;
01336 
01337         dxi = vec[n].x - vec[n_prev].x;
01338         dyi = vec[n].y - vec[n_prev].y;
01339 
01340         point->dir_in = (FT_Char)psh_compute_dir( dxi, dyi );
01341 
01342         dxo = vec[n_next].x - vec[n].x;
01343         dyo = vec[n_next].y - vec[n].y;
01344 
01345         point->dir_out = (FT_Char)psh_compute_dir( dxo, dyo );
01346 
01347         /* detect smooth points */
01348         if ( point->flags & PSH_POINT_OFF )
01349           point->flags |= PSH_POINT_SMOOTH;
01350 
01351         else if ( point->dir_in == point->dir_out )
01352         {
01353           if ( point->dir_out != PSH_DIR_NONE           ||
01354                psh_corner_is_flat( dxi, dyi, dxo, dyo ) )
01355             point->flags |= PSH_POINT_SMOOTH;
01356         }
01357       }
01358     }
01359 
01360     glyph->outline = outline;
01361     glyph->globals = globals;
01362 
01363 #ifdef COMPUTE_INFLEXS
01364     psh_glyph_load_points( glyph, 0 );
01365     psh_glyph_compute_inflections( glyph );
01366 #endif /* COMPUTE_INFLEXS */
01367 
01368     /* now deal with hints tables */
01369     error = psh_hint_table_init( &glyph->hint_tables [0],
01370                                  &ps_hints->dimension[0].hints,
01371                                  &ps_hints->dimension[0].masks,
01372                                  &ps_hints->dimension[0].counters,
01373                                  memory );
01374     if ( error )
01375       goto Exit;
01376 
01377     error = psh_hint_table_init( &glyph->hint_tables [1],
01378                                  &ps_hints->dimension[1].hints,
01379                                  &ps_hints->dimension[1].masks,
01380                                  &ps_hints->dimension[1].counters,
01381                                  memory );
01382     if ( error )
01383       goto Exit;
01384 
01385   Exit:
01386     return error;
01387   }
01388 
01389 
01390   /* compute all extrema in a glyph for a given dimension */
01391   static void
01392   psh_glyph_compute_extrema( PSH_Glyph  glyph )
01393   {
01394     FT_UInt  n;
01395 
01396 
01397     /* first of all, compute all local extrema */
01398     for ( n = 0; n < glyph->num_contours; n++ )
01399     {
01400       PSH_Point  first = glyph->contours[n].start;
01401       PSH_Point  point, before, after;
01402 
01403 
01404       if ( glyph->contours[n].count == 0 )
01405         continue;
01406 
01407       point  = first;
01408       before = point;
01409       after  = point;
01410 
01411       do
01412       {
01413         before = before->prev;
01414         if ( before == first )
01415           goto Skip;
01416 
01417       } while ( before->org_u == point->org_u );
01418 
01419       first = point = before->next;
01420 
01421       for (;;)
01422       {
01423         after = point;
01424         do
01425         {
01426           after = after->next;
01427           if ( after == first )
01428             goto Next;
01429 
01430         } while ( after->org_u == point->org_u );
01431 
01432         if ( before->org_u < point->org_u )
01433         {
01434           if ( after->org_u < point->org_u )
01435           {
01436             /* local maximum */
01437             goto Extremum;
01438           }
01439         }
01440         else /* before->org_u > point->org_u */
01441         {
01442           if ( after->org_u > point->org_u )
01443           {
01444             /* local minimum */
01445           Extremum:
01446             do
01447             {
01448               psh_point_set_extremum( point );
01449               point = point->next;
01450 
01451             } while ( point != after );
01452           }
01453         }
01454 
01455         before = after->prev;
01456         point  = after;
01457 
01458       } /* for  */
01459 
01460     Next:
01461       ;
01462     }
01463 
01464     /* for each extremum, determine its direction along the */
01465     /* orthogonal axis                                      */
01466     for ( n = 0; n < glyph->num_points; n++ )
01467     {
01468       PSH_Point  point, before, after;
01469 
01470 
01471       point  = &glyph->points[n];
01472       before = point;
01473       after  = point;
01474 
01475       if ( psh_point_is_extremum( point ) )
01476       {
01477         do
01478         {
01479           before = before->prev;
01480           if ( before == point )
01481             goto Skip;
01482 
01483         } while ( before->org_v == point->org_v );
01484 
01485         do
01486         {
01487           after = after->next;
01488           if ( after == point )
01489             goto Skip;
01490 
01491         } while ( after->org_v == point->org_v );
01492       }
01493 
01494       if ( before->org_v < point->org_v &&
01495            after->org_v  > point->org_v )
01496       {
01497         psh_point_set_positive( point );
01498       }
01499       else if ( before->org_v > point->org_v &&
01500                 after->org_v  < point->org_v )
01501       {
01502         psh_point_set_negative( point );
01503       }
01504 
01505     Skip:
01506       ;
01507     }
01508   }
01509 
01510 
01511   /* major_dir is the direction for points on the bottom/left of the stem; */
01512   /* Points on the top/right of the stem will have a direction of          */
01513   /* -major_dir.                                                           */
01514 
01515   static void
01516   psh_hint_table_find_strong_points( PSH_Hint_Table  table,
01517                                      PSH_Point       point,
01518                                      FT_UInt         count,
01519                                      FT_Int          threshold,
01520                                      FT_Int          major_dir )
01521   {
01522     PSH_Hint*  sort      = table->sort;
01523     FT_UInt    num_hints = table->num_hints;
01524 
01525 
01526     for ( ; count > 0; count--, point++ )
01527     {
01528       FT_Int  point_dir = 0;
01529       FT_Pos  org_u     = point->org_u;
01530 
01531 
01532       if ( psh_point_is_strong( point ) )
01533         continue;
01534 
01535       if ( PSH_DIR_COMPARE( point->dir_in, major_dir ) )
01536         point_dir = point->dir_in;
01537 
01538       else if ( PSH_DIR_COMPARE( point->dir_out, major_dir ) )
01539         point_dir = point->dir_out;
01540 
01541       if ( point_dir )
01542       {
01543         if ( point_dir == major_dir )
01544         {
01545           FT_UInt  nn;
01546 
01547 
01548           for ( nn = 0; nn < num_hints; nn++ )
01549           {
01550             PSH_Hint  hint = sort[nn];
01551             FT_Pos    d    = org_u - hint->org_pos;
01552 
01553 
01554             if ( d < threshold && -d < threshold )
01555             {
01556               psh_point_set_strong( point );
01557               point->flags2 |= PSH_POINT_EDGE_MIN;
01558               point->hint    = hint;
01559               break;
01560             }
01561           }
01562         }
01563         else if ( point_dir == -major_dir )
01564         {
01565           FT_UInt  nn;
01566 
01567 
01568           for ( nn = 0; nn < num_hints; nn++ )
01569           {
01570             PSH_Hint  hint = sort[nn];
01571             FT_Pos    d    = org_u - hint->org_pos - hint->org_len;
01572 
01573 
01574             if ( d < threshold && -d < threshold )
01575             {
01576               psh_point_set_strong( point );
01577               point->flags2 |= PSH_POINT_EDGE_MAX;
01578               point->hint    = hint;
01579               break;
01580             }
01581           }
01582         }
01583       }
01584 
01585 #if 1
01586       else if ( psh_point_is_extremum( point ) )
01587       {
01588         /* treat extrema as special cases for stem edge alignment */
01589         FT_UInt  nn, min_flag, max_flag;
01590 
01591 
01592         if ( major_dir == PSH_DIR_HORIZONTAL )
01593         {
01594           min_flag = PSH_POINT_POSITIVE;
01595           max_flag = PSH_POINT_NEGATIVE;
01596         }
01597         else
01598         {
01599           min_flag = PSH_POINT_NEGATIVE;
01600           max_flag = PSH_POINT_POSITIVE;
01601         }
01602 
01603         if ( point->flags2 & min_flag )
01604         {
01605           for ( nn = 0; nn < num_hints; nn++ )
01606           {
01607             PSH_Hint  hint = sort[nn];
01608             FT_Pos    d    = org_u - hint->org_pos;
01609 
01610 
01611             if ( d < threshold && -d < threshold )
01612             {
01613               point->flags2 |= PSH_POINT_EDGE_MIN;
01614               point->hint    = hint;
01615               psh_point_set_strong( point );
01616               break;
01617             }
01618           }
01619         }
01620         else if ( point->flags2 & max_flag )
01621         {
01622           for ( nn = 0; nn < num_hints; nn++ )
01623           {
01624             PSH_Hint  hint = sort[nn];
01625             FT_Pos    d    = org_u - hint->org_pos - hint->org_len;
01626 
01627 
01628             if ( d < threshold && -d < threshold )
01629             {
01630               point->flags2 |= PSH_POINT_EDGE_MAX;
01631               point->hint    = hint;
01632               psh_point_set_strong( point );
01633               break;
01634             }
01635           }
01636         }
01637 
01638         if ( point->hint == NULL )
01639         {
01640           for ( nn = 0; nn < num_hints; nn++ )
01641           {
01642             PSH_Hint  hint = sort[nn];
01643 
01644 
01645             if ( org_u >= hint->org_pos                 &&
01646                 org_u <= hint->org_pos + hint->org_len )
01647             {
01648               point->hint = hint;
01649               break;
01650             }
01651           }
01652         }
01653       }
01654 
01655 #endif /* 1 */
01656     }
01657   }
01658 
01659 
01660   /* the accepted shift for strong points in fractional pixels */
01661 #define PSH_STRONG_THRESHOLD  32
01662 
01663   /* the maximum shift value in font units */
01664 #define PSH_STRONG_THRESHOLD_MAXIMUM  30
01665 
01666 
01667   /* find strong points in a glyph */
01668   static void
01669   psh_glyph_find_strong_points( PSH_Glyph  glyph,
01670                                 FT_Int     dimension )
01671   {
01672     /* a point is `strong' if it is located on a stem edge and       */
01673     /* has an `in' or `out' tangent parallel to the hint's direction */
01674 
01675     PSH_Hint_Table  table     = &glyph->hint_tables[dimension];
01676     PS_Mask         mask      = table->hint_masks->masks;
01677     FT_UInt         num_masks = table->hint_masks->num_masks;
01678     FT_UInt         first     = 0;
01679     FT_Int          major_dir = dimension == 0 ? PSH_DIR_VERTICAL
01680                                                : PSH_DIR_HORIZONTAL;
01681     PSH_Dimension   dim       = &glyph->globals->dimension[dimension];
01682     FT_Fixed        scale     = dim->scale_mult;
01683     FT_Int          threshold;
01684 
01685 
01686     threshold = (FT_Int)FT_DivFix( PSH_STRONG_THRESHOLD, scale );
01687     if ( threshold > PSH_STRONG_THRESHOLD_MAXIMUM )
01688       threshold = PSH_STRONG_THRESHOLD_MAXIMUM;
01689 
01690     /* process secondary hints to `selected' points */
01691     if ( num_masks > 1 && glyph->num_points > 0 )
01692     {
01693       first = mask->end_point;
01694       mask++;
01695       for ( ; num_masks > 1; num_masks--, mask++ )
01696       {
01697         FT_UInt  next;
01698         FT_Int   count;
01699 
01700 
01701         next  = mask->end_point;
01702         count = next - first;
01703         if ( count > 0 )
01704         {
01705           PSH_Point  point = glyph->points + first;
01706 
01707 
01708           psh_hint_table_activate_mask( table, mask );
01709 
01710           psh_hint_table_find_strong_points( table, point, count,
01711                                              threshold, major_dir );
01712         }
01713         first = next;
01714       }
01715     }
01716 
01717     /* process primary hints for all points */
01718     if ( num_masks == 1 )
01719     {
01720       FT_UInt    count = glyph->num_points;
01721       PSH_Point  point = glyph->points;
01722 
01723 
01724       psh_hint_table_activate_mask( table, table->hint_masks->masks );
01725 
01726       psh_hint_table_find_strong_points( table, point, count,
01727                                          threshold, major_dir );
01728     }
01729 
01730     /* now, certain points may have been attached to a hint and */
01731     /* not marked as strong; update their flags then            */
01732     {
01733       FT_UInt    count = glyph->num_points;
01734       PSH_Point  point = glyph->points;
01735 
01736 
01737       for ( ; count > 0; count--, point++ )
01738         if ( point->hint && !psh_point_is_strong( point ) )
01739           psh_point_set_strong( point );
01740     }
01741   }
01742 
01743 
01744   /* find points in a glyph which are in a blue zone and have `in' or */
01745   /* `out' tangents parallel to the horizontal axis                   */
01746   static void
01747   psh_glyph_find_blue_points( PSH_Blues  blues,
01748                               PSH_Glyph  glyph )
01749   {
01750     PSH_Blue_Table  table;
01751     PSH_Blue_Zone   zone;
01752     FT_UInt         glyph_count = glyph->num_points;
01753     FT_UInt         blue_count;
01754     PSH_Point       point = glyph->points;
01755 
01756 
01757     for ( ; glyph_count > 0; glyph_count--, point++ )
01758     {
01759       FT_Pos  y;
01760 
01761 
01762       /* check tangents */
01763       if ( !PSH_DIR_COMPARE( point->dir_in,  PSH_DIR_HORIZONTAL ) &&
01764            !PSH_DIR_COMPARE( point->dir_out, PSH_DIR_HORIZONTAL ) )
01765         continue;
01766 
01767       /* skip strong points */
01768       if ( psh_point_is_strong( point ) )
01769         continue;
01770 
01771       y = point->org_u;
01772 
01773       /* look up top zones */
01774       table      = &blues->normal_top;
01775       blue_count = table->count;
01776       zone       = table->zones;
01777 
01778       for ( ; blue_count > 0; blue_count--, zone++ )
01779       {
01780         FT_Pos  delta = y - zone->org_bottom;
01781 
01782 
01783         if ( delta < -blues->blue_fuzz )
01784           break;
01785 
01786         if ( y <= zone->org_top + blues->blue_fuzz )
01787           if ( blues->no_overshoots || delta <= blues->blue_threshold )
01788           {
01789             point->cur_u = zone->cur_bottom;
01790             psh_point_set_strong( point );
01791             psh_point_set_fitted( point );
01792           }
01793       }
01794 
01795       /* look up bottom zones */
01796       table      = &blues->normal_bottom;
01797       blue_count = table->count;
01798       zone       = table->zones + blue_count - 1;
01799 
01800       for ( ; blue_count > 0; blue_count--, zone-- )
01801       {
01802         FT_Pos  delta = zone->org_top - y;
01803 
01804 
01805         if ( delta < -blues->blue_fuzz )
01806           break;
01807 
01808         if ( y >= zone->org_bottom - blues->blue_fuzz )
01809           if ( blues->no_overshoots || delta < blues->blue_threshold )
01810           {
01811             point->cur_u = zone->cur_top;
01812             psh_point_set_strong( point );
01813             psh_point_set_fitted( point );
01814           }
01815       }
01816     }
01817   }
01818 
01819 
01820   /* interpolate strong points with the help of hinted coordinates */
01821   static void
01822   psh_glyph_interpolate_strong_points( PSH_Glyph  glyph,
01823                                        FT_Int     dimension )
01824   {
01825     PSH_Dimension  dim   = &glyph->globals->dimension[dimension];
01826     FT_Fixed       scale = dim->scale_mult;
01827 
01828     FT_UInt        count = glyph->num_points;
01829     PSH_Point      point = glyph->points;
01830 
01831 
01832     for ( ; count > 0; count--, point++ )
01833     {
01834       PSH_Hint  hint = point->hint;
01835 
01836 
01837       if ( hint )
01838       {
01839         FT_Pos  delta;
01840 
01841 
01842         if ( psh_point_is_edge_min( point ) )
01843           point->cur_u = hint->cur_pos;
01844 
01845         else if ( psh_point_is_edge_max( point ) )
01846           point->cur_u = hint->cur_pos + hint->cur_len;
01847 
01848         else
01849         {
01850           delta = point->org_u - hint->org_pos;
01851 
01852           if ( delta <= 0 )
01853             point->cur_u = hint->cur_pos + FT_MulFix( delta, scale );
01854 
01855           else if ( delta >= hint->org_len )
01856             point->cur_u = hint->cur_pos + hint->cur_len +
01857                              FT_MulFix( delta - hint->org_len, scale );
01858 
01859           else if ( hint->org_len > 0 )
01860             point->cur_u = hint->cur_pos +
01861                              FT_MulDiv( delta, hint->cur_len,
01862                                         hint->org_len );
01863           else
01864             point->cur_u = hint->cur_pos;
01865         }
01866         psh_point_set_fitted( point );
01867       }
01868     }
01869   }
01870 
01871 
01872 #define  PSH_MAX_STRONG_INTERNAL  16
01873 
01874   static void
01875   psh_glyph_interpolate_normal_points( PSH_Glyph  glyph,
01876                                        FT_Int     dimension )
01877   {
01878 
01879 #if 1
01880     /* first technique: a point is strong if it is a local extremum */
01881 
01882     PSH_Dimension  dim    = &glyph->globals->dimension[dimension];
01883     FT_Fixed       scale  = dim->scale_mult;
01884     FT_Memory      memory = glyph->memory;
01885 
01886     PSH_Point*     strongs     = NULL;
01887     PSH_Point      strongs_0[PSH_MAX_STRONG_INTERNAL];
01888     FT_UInt        num_strongs = 0;
01889 
01890     PSH_Point      points = glyph->points;
01891     PSH_Point      points_end = points + glyph->num_points;
01892     PSH_Point      point;
01893 
01894 
01895     /* first count the number of strong points */
01896     for ( point = points; point < points_end; point++ )
01897     {
01898       if ( psh_point_is_strong( point ) )
01899         num_strongs++;
01900     }
01901 
01902     if ( num_strongs == 0 )  /* nothing to do here */
01903       return;
01904 
01905     /* allocate an array to store a list of points, */
01906     /* stored in increasing org_u order             */
01907     if ( num_strongs <= PSH_MAX_STRONG_INTERNAL )
01908       strongs = strongs_0;
01909     else
01910     {
01911       FT_Error  error;
01912 
01913 
01914       if ( FT_NEW_ARRAY( strongs, num_strongs ) )
01915         return;
01916     }
01917 
01918     num_strongs = 0;
01919     for ( point = points; point < points_end; point++ )
01920     {
01921       PSH_Point*  insert;
01922 
01923 
01924       if ( !psh_point_is_strong( point ) )
01925         continue;
01926 
01927       for ( insert = strongs + num_strongs; insert > strongs; insert-- )
01928       {
01929         if ( insert[-1]->org_u <= point->org_u )
01930           break;
01931 
01932         insert[0] = insert[-1];
01933       }
01934       insert[0] = point;
01935       num_strongs++;
01936     }
01937 
01938     /* now try to interpolate all normal points */
01939     for ( point = points; point < points_end; point++ )
01940     {
01941       if ( psh_point_is_strong( point ) )
01942         continue;
01943 
01944       /* sometimes, some local extrema are smooth points */
01945       if ( psh_point_is_smooth( point ) )
01946       {
01947         if ( point->dir_in == PSH_DIR_NONE   ||
01948              point->dir_in != point->dir_out )
01949           continue;
01950 
01951         if ( !psh_point_is_extremum( point ) &&
01952              !psh_point_is_inflex( point )   )
01953           continue;
01954 
01955         point->flags &= ~PSH_POINT_SMOOTH;
01956       }
01957 
01958       /* find best enclosing point coordinates then interpolate */
01959       {
01960         PSH_Point   before, after;
01961         FT_UInt     nn;
01962 
01963 
01964         for ( nn = 0; nn < num_strongs; nn++ )
01965           if ( strongs[nn]->org_u > point->org_u )
01966             break;
01967 
01968         if ( nn == 0 )  /* point before the first strong point */
01969         {
01970           after = strongs[0];
01971 
01972           point->cur_u = after->cur_u +
01973                            FT_MulFix( point->org_u - after->org_u,
01974                                       scale );
01975         }
01976         else
01977         {
01978           before = strongs[nn - 1];
01979 
01980           for ( nn = num_strongs; nn > 0; nn-- )
01981             if ( strongs[nn - 1]->org_u < point->org_u )
01982               break;
01983 
01984           if ( nn == num_strongs )  /* point is after last strong point */
01985           {
01986             before = strongs[nn - 1];
01987 
01988             point->cur_u = before->cur_u +
01989                              FT_MulFix( point->org_u - before->org_u,
01990                                         scale );
01991           }
01992           else
01993           {
01994             FT_Pos  u;
01995 
01996 
01997             after = strongs[nn];
01998 
01999             /* now interpolate point between before and after */
02000             u = point->org_u;
02001 
02002             if ( u == before->org_u )
02003               point->cur_u = before->cur_u;
02004 
02005             else if ( u == after->org_u )
02006               point->cur_u = after->cur_u;
02007 
02008             else
02009               point->cur_u = before->cur_u +
02010                                FT_MulDiv( u - before->org_u,
02011                                           after->cur_u - before->cur_u,
02012                                           after->org_u - before->org_u );
02013           }
02014         }
02015         psh_point_set_fitted( point );
02016       }
02017     }
02018 
02019     if ( strongs != strongs_0 )
02020       FT_FREE( strongs );
02021 
02022 #endif /* 1 */
02023 
02024   }
02025 
02026 
02027   /* interpolate other points */
02028   static void
02029   psh_glyph_interpolate_other_points( PSH_Glyph  glyph,
02030                                       FT_Int     dimension )
02031   {
02032     PSH_Dimension  dim          = &glyph->globals->dimension[dimension];
02033     FT_Fixed       scale        = dim->scale_mult;
02034     FT_Fixed       delta        = dim->scale_delta;
02035     PSH_Contour    contour      = glyph->contours;
02036     FT_UInt        num_contours = glyph->num_contours;
02037 
02038 
02039     for ( ; num_contours > 0; num_contours--, contour++ )
02040     {
02041       PSH_Point  start = contour->start;
02042       PSH_Point  first, next, point;
02043       FT_UInt    fit_count;
02044 
02045 
02046       /* count the number of strong points in this contour */
02047       next      = start + contour->count;
02048       fit_count = 0;
02049       first     = 0;
02050 
02051       for ( point = start; point < next; point++ )
02052         if ( psh_point_is_fitted( point ) )
02053         {
02054           if ( !first )
02055             first = point;
02056 
02057           fit_count++;
02058         }
02059 
02060       /* if there are less than 2 fitted points in the contour, we */
02061       /* simply scale and eventually translate the contour points  */
02062       if ( fit_count < 2 )
02063       {
02064         if ( fit_count == 1 )
02065           delta = first->cur_u - FT_MulFix( first->org_u, scale );
02066 
02067         for ( point = start; point < next; point++ )
02068           if ( point != first )
02069             point->cur_u = FT_MulFix( point->org_u, scale ) + delta;
02070 
02071         goto Next_Contour;
02072       }
02073 
02074       /* there are more than 2 strong points in this contour; we */
02075       /* need to interpolate weak points between them            */
02076       start = first;
02077       do
02078       {
02079         point = first;
02080 
02081         /* skip consecutive fitted points */
02082         for (;;)
02083         {
02084           next = first->next;
02085           if ( next == start )
02086             goto Next_Contour;
02087 
02088           if ( !psh_point_is_fitted( next ) )
02089             break;
02090 
02091           first = next;
02092         }
02093 
02094         /* find next fitted point after unfitted one */
02095         for (;;)
02096         {
02097           next = next->next;
02098           if ( psh_point_is_fitted( next ) )
02099             break;
02100         }
02101 
02102         /* now interpolate between them */
02103         {
02104           FT_Pos    org_a, org_ab, cur_a, cur_ab;
02105           FT_Pos    org_c, org_ac, cur_c;
02106           FT_Fixed  scale_ab;
02107 
02108 
02109           if ( first->org_u <= next->org_u )
02110           {
02111             org_a  = first->org_u;
02112             cur_a  = first->cur_u;
02113             org_ab = next->org_u - org_a;
02114             cur_ab = next->cur_u - cur_a;
02115           }
02116           else
02117           {
02118             org_a  = next->org_u;
02119             cur_a  = next->cur_u;
02120             org_ab = first->org_u - org_a;
02121             cur_ab = first->cur_u - cur_a;
02122           }
02123 
02124           scale_ab = 0x10000L;
02125           if ( org_ab > 0 )
02126             scale_ab = FT_DivFix( cur_ab, org_ab );
02127 
02128           point = first->next;
02129           do
02130           {
02131             org_c  = point->org_u;
02132             org_ac = org_c - org_a;
02133 
02134             if ( org_ac <= 0 )
02135             {
02136               /* on the left of the interpolation zone */
02137               cur_c = cur_a + FT_MulFix( org_ac, scale );
02138             }
02139             else if ( org_ac >= org_ab )
02140             {
02141               /* on the right on the interpolation zone */
02142               cur_c = cur_a + cur_ab + FT_MulFix( org_ac - org_ab, scale );
02143             }
02144             else
02145             {
02146               /* within the interpolation zone */
02147               cur_c = cur_a + FT_MulFix( org_ac, scale_ab );
02148             }
02149 
02150             point->cur_u = cur_c;
02151 
02152             point = point->next;
02153 
02154           } while ( point != next );
02155         }
02156 
02157         /* keep going until all points in the contours have been processed */
02158         first = next;
02159 
02160       } while ( first != start );
02161 
02162     Next_Contour:
02163       ;
02164     }
02165   }
02166 
02167 
02168   /*************************************************************************/
02169   /*************************************************************************/
02170   /*****                                                               *****/
02171   /*****                     HIGH-LEVEL INTERFACE                      *****/
02172   /*****                                                               *****/
02173   /*************************************************************************/
02174   /*************************************************************************/
02175 
02176   FT_Error
02177   ps_hints_apply( PS_Hints        ps_hints,
02178                   FT_Outline*     outline,
02179                   PSH_Globals     globals,
02180                   FT_Render_Mode  hint_mode )
02181   {
02182     PSH_GlyphRec  glyphrec;
02183     PSH_Glyph     glyph = &glyphrec;
02184     FT_Error      error;
02185 #ifdef DEBUG_HINTER
02186     FT_Memory     memory;
02187 #endif
02188     FT_Int        dimension;
02189 
02190 
02191     /* something to do? */
02192     if ( outline->n_points == 0 || outline->n_contours == 0 )
02193       return PSH_Err_Ok;
02194 
02195 #ifdef DEBUG_HINTER
02196 
02197     memory = globals->memory;
02198 
02199     if ( ps_debug_glyph )
02200     {
02201       psh_glyph_done( ps_debug_glyph );
02202       FT_FREE( ps_debug_glyph );
02203     }
02204 
02205     if ( FT_NEW( glyph ) )
02206       return error;
02207 
02208     ps_debug_glyph = glyph;
02209 
02210 #endif /* DEBUG_HINTER */
02211 
02212     error = psh_glyph_init( glyph, outline, ps_hints, globals );
02213     if ( error )
02214       goto Exit;
02215 
02216     /* try to optimize the y_scale so that the top of non-capital letters
02217      * is aligned on a pixel boundary whenever possible
02218      */
02219     {
02220       PSH_Dimension  dim_x = &glyph->globals->dimension[0];
02221       PSH_Dimension  dim_y = &glyph->globals->dimension[1];
02222 
02223       FT_Fixed  x_scale = dim_x->scale_mult;
02224       FT_Fixed  y_scale = dim_y->scale_mult;
02225 
02226       FT_Fixed  old_x_scale = x_scale;
02227       FT_Fixed  old_y_scale = y_scale;
02228 
02229       FT_Fixed  scaled;
02230       FT_Fixed  fitted;
02231 
02232       FT_Bool  rescale = FALSE;
02233 
02234 
02235       scaled = FT_MulFix( globals->blues.normal_top.zones->org_ref, y_scale );
02236       fitted = FT_PIX_ROUND( scaled );
02237 
02238       if ( fitted != 0 && scaled != fitted )
02239       {
02240         rescale = TRUE;
02241 
02242         y_scale = FT_MulDiv( y_scale, fitted, scaled );
02243 
02244         if ( fitted < scaled )
02245           x_scale -= x_scale / 50;
02246 
02247         psh_globals_set_scale( glyph->globals, x_scale, y_scale, 0, 0 );
02248       }
02249 
02250       glyph->do_horz_hints = 1;
02251       glyph->do_vert_hints = 1;
02252 
02253       glyph->do_horz_snapping = FT_BOOL( hint_mode == FT_RENDER_MODE_MONO ||
02254                                          hint_mode == FT_RENDER_MODE_LCD  );
02255 
02256       glyph->do_vert_snapping = FT_BOOL( hint_mode == FT_RENDER_MODE_MONO  ||
02257                                          hint_mode == FT_RENDER_MODE_LCD_V );
02258 
02259       glyph->do_stem_adjust   = FT_BOOL( hint_mode != FT_RENDER_MODE_LIGHT );
02260 
02261       for ( dimension = 0; dimension < 2; dimension++ )
02262       {
02263         /* load outline coordinates into glyph */
02264         psh_glyph_load_points( glyph, dimension );
02265 
02266         /* compute local extrema */
02267         psh_glyph_compute_extrema( glyph );
02268 
02269         /* compute aligned stem/hints positions */
02270         psh_hint_table_align_hints( &glyph->hint_tables[dimension],
02271                                     glyph->globals,
02272                                     dimension,
02273                                     glyph );
02274 
02275         /* find strong points, align them, then interpolate others */
02276         psh_glyph_find_strong_points( glyph, dimension );
02277         if ( dimension == 1 )
02278           psh_glyph_find_blue_points( &globals->blues, glyph );
02279         psh_glyph_interpolate_strong_points( glyph, dimension );
02280         psh_glyph_interpolate_normal_points( glyph, dimension );
02281         psh_glyph_interpolate_other_points( glyph, dimension );
02282 
02283         /* save hinted coordinates back to outline */
02284         psh_glyph_save_points( glyph, dimension );
02285 
02286         if ( rescale )
02287           psh_globals_set_scale( glyph->globals,
02288                                  old_x_scale, old_y_scale, 0, 0 );
02289       }
02290     }
02291 
02292   Exit:
02293 
02294 #ifndef DEBUG_HINTER
02295     psh_glyph_done( glyph );
02296 #endif
02297 
02298     return error;
02299   }
02300 
02301 
02302 /* END */

Generated on Tue Jul 5 14:13:50 2011 for ROOT_528-00b_version by  doxygen 1.5.1