ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

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

Generated on Sun May 27 2012 04:34:00 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.