Home | History | Annotate | Download | only in pshinter
      1 /***************************************************************************/
      2 /*                                                                         */
      3 /*  pshglob.c                                                              */
      4 /*                                                                         */
      5 /*    PostScript hinter global hinting management (body).                  */
      6 /*    Inspired by the new auto-hinter module.                              */
      7 /*                                                                         */
      8 /*  Copyright 2001, 2002, 2003, 2004, 2006 by                              */
      9 /*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
     10 /*                                                                         */
     11 /*  This file is part of the FreeType project, and may only be used        */
     12 /*  modified and distributed under the terms of the FreeType project       */
     13 /*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
     14 /*  this file you indicate that you have read the license and              */
     15 /*  understand and accept it fully.                                        */
     16 /*                                                                         */
     17 /***************************************************************************/
     18 
     19 
     20 #include <ft2build.h>
     21 #include FT_FREETYPE_H
     22 #include FT_INTERNAL_OBJECTS_H
     23 #include "pshglob.h"
     24 
     25 #ifdef DEBUG_HINTER
     26   PSH_Globals  ps_debug_globals = 0;
     27 #endif
     28 
     29 
     30   /*************************************************************************/
     31   /*************************************************************************/
     32   /*****                                                               *****/
     33   /*****                       STANDARD WIDTHS                         *****/
     34   /*****                                                               *****/
     35   /*************************************************************************/
     36   /*************************************************************************/
     37 
     38 
     39   /* scale the widths/heights table */
     40   static void
     41   psh_globals_scale_widths( PSH_Globals  globals,
     42                             FT_UInt      direction )
     43   {
     44     PSH_Dimension  dim   = &globals->dimension[direction];
     45     PSH_Widths     stdw  = &dim->stdw;
     46     FT_UInt        count = stdw->count;
     47     PSH_Width      width = stdw->widths;
     48     PSH_Width      stand = width;               /* standard width/height */
     49     FT_Fixed       scale = dim->scale_mult;
     50 
     51 
     52     if ( count > 0 )
     53     {
     54       width->cur = FT_MulFix( width->org, scale );
     55       width->fit = FT_PIX_ROUND( width->cur );
     56 
     57       width++;
     58       count--;
     59 
     60       for ( ; count > 0; count--, width++ )
     61       {
     62         FT_Pos  w, dist;
     63 
     64 
     65         w    = FT_MulFix( width->org, scale );
     66         dist = w - stand->cur;
     67 
     68         if ( dist < 0 )
     69           dist = -dist;
     70 
     71         if ( dist < 128 )
     72           w = stand->cur;
     73 
     74         width->cur = w;
     75         width->fit = FT_PIX_ROUND( w );
     76       }
     77     }
     78   }
     79 
     80 
     81 #if 0
     82 
     83   /* org_width is is font units, result in device pixels, 26.6 format */
     84   FT_LOCAL_DEF( FT_Pos )
     85   psh_dimension_snap_width( PSH_Dimension  dimension,
     86                             FT_Int         org_width )
     87   {
     88     FT_UInt  n;
     89     FT_Pos   width     = FT_MulFix( org_width, dimension->scale_mult );
     90     FT_Pos   best      = 64 + 32 + 2;
     91     FT_Pos   reference = width;
     92 
     93 
     94     for ( n = 0; n < dimension->stdw.count; n++ )
     95     {
     96       FT_Pos  w;
     97       FT_Pos  dist;
     98 
     99 
    100       w = dimension->stdw.widths[n].cur;
    101       dist = width - w;
    102       if ( dist < 0 )
    103         dist = -dist;
    104       if ( dist < best )
    105       {
    106         best      = dist;
    107         reference = w;
    108       }
    109     }
    110 
    111     if ( width >= reference )
    112     {
    113       width -= 0x21;
    114       if ( width < reference )
    115         width = reference;
    116     }
    117     else
    118     {
    119       width += 0x21;
    120       if ( width > reference )
    121         width = reference;
    122     }
    123 
    124     return width;
    125   }
    126 
    127 #endif /* 0 */
    128 
    129 
    130   /*************************************************************************/
    131   /*************************************************************************/
    132   /*****                                                               *****/
    133   /*****                       BLUE ZONES                              *****/
    134   /*****                                                               *****/
    135   /*************************************************************************/
    136   /*************************************************************************/
    137 
    138   static void
    139   psh_blues_set_zones_0( PSH_Blues       target,
    140                          FT_Bool         is_others,
    141                          FT_UInt         read_count,
    142                          FT_Short*       read,
    143                          PSH_Blue_Table  top_table,
    144                          PSH_Blue_Table  bot_table )
    145   {
    146     FT_UInt  count_top = top_table->count;
    147     FT_UInt  count_bot = bot_table->count;
    148     FT_Bool  first     = 1;
    149 
    150     FT_UNUSED( target );
    151 
    152 
    153     for ( ; read_count > 1; read_count -= 2 )
    154     {
    155       FT_Int         reference, delta;
    156       FT_UInt        count;
    157       PSH_Blue_Zone  zones, zone;
    158       FT_Bool        top;
    159 
    160 
    161       /* read blue zone entry, and select target top/bottom zone */
    162       top = 0;
    163       if ( first || is_others )
    164       {
    165         reference = read[1];
    166         delta     = read[0] - reference;
    167 
    168         zones = bot_table->zones;
    169         count = count_bot;
    170         first = 0;
    171       }
    172       else
    173       {
    174         reference = read[0];
    175         delta     = read[1] - reference;
    176 
    177         zones = top_table->zones;
    178         count = count_top;
    179         top   = 1;
    180       }
    181 
    182       /* insert into sorted table */
    183       zone = zones;
    184       for ( ; count > 0; count--, zone++ )
    185       {
    186         if ( reference < zone->org_ref )
    187           break;
    188 
    189         if ( reference == zone->org_ref )
    190         {
    191           FT_Int  delta0 = zone->org_delta;
    192 
    193 
    194           /* we have two zones on the same reference position -- */
    195           /* only keep the largest one                           */
    196           if ( delta < 0 )
    197           {
    198             if ( delta < delta0 )
    199               zone->org_delta = delta;
    200           }
    201           else
    202           {
    203             if ( delta > delta0 )
    204               zone->org_delta = delta;
    205           }
    206           goto Skip;
    207         }
    208       }
    209 
    210       for ( ; count > 0; count-- )
    211         zone[count] = zone[count-1];
    212 
    213       zone->org_ref   = reference;
    214       zone->org_delta = delta;
    215 
    216       if ( top )
    217         count_top++;
    218       else
    219         count_bot++;
    220 
    221     Skip:
    222       read += 2;
    223     }
    224 
    225     top_table->count = count_top;
    226     bot_table->count = count_bot;
    227   }
    228 
    229 
    230   /* Re-read blue zones from the original fonts and store them into out */
    231   /* private structure.  This function re-orders, sanitizes and         */
    232   /* fuzz-expands the zones as well.                                    */
    233   static void
    234   psh_blues_set_zones( PSH_Blues  target,
    235                        FT_UInt    count,
    236                        FT_Short*  blues,
    237                        FT_UInt    count_others,
    238                        FT_Short*  other_blues,
    239                        FT_Int     fuzz,
    240                        FT_Int     family )
    241   {
    242     PSH_Blue_Table  top_table, bot_table;
    243     FT_Int          count_top, count_bot;
    244 
    245 
    246     if ( family )
    247     {
    248       top_table = &target->family_top;
    249       bot_table = &target->family_bottom;
    250     }
    251     else
    252     {
    253       top_table = &target->normal_top;
    254       bot_table = &target->normal_bottom;
    255     }
    256 
    257     /* read the input blue zones, and build two sorted tables  */
    258     /* (one for the top zones, the other for the bottom zones) */
    259     top_table->count = 0;
    260     bot_table->count = 0;
    261 
    262     /* first, the blues */
    263     psh_blues_set_zones_0( target, 0,
    264                            count, blues, top_table, bot_table );
    265     psh_blues_set_zones_0( target, 1,
    266                            count_others, other_blues, top_table, bot_table );
    267 
    268     count_top = top_table->count;
    269     count_bot = bot_table->count;
    270 
    271     /* sanitize top table */
    272     if ( count_top > 0 )
    273     {
    274       PSH_Blue_Zone  zone = top_table->zones;
    275 
    276 
    277       for ( count = count_top; count > 0; count--, zone++ )
    278       {
    279         FT_Int  delta;
    280 
    281 
    282         if ( count > 1 )
    283         {
    284           delta = zone[1].org_ref - zone[0].org_ref;
    285           if ( zone->org_delta > delta )
    286             zone->org_delta = delta;
    287         }
    288 
    289         zone->org_bottom = zone->org_ref;
    290         zone->org_top    = zone->org_delta + zone->org_ref;
    291       }
    292     }
    293 
    294     /* sanitize bottom table */
    295     if ( count_bot > 0 )
    296     {
    297       PSH_Blue_Zone  zone = bot_table->zones;
    298 
    299 
    300       for ( count = count_bot; count > 0; count--, zone++ )
    301       {
    302         FT_Int  delta;
    303 
    304 
    305         if ( count > 1 )
    306         {
    307           delta = zone[0].org_ref - zone[1].org_ref;
    308           if ( zone->org_delta < delta )
    309             zone->org_delta = delta;
    310         }
    311 
    312         zone->org_top    = zone->org_ref;
    313         zone->org_bottom = zone->org_delta + zone->org_ref;
    314       }
    315     }
    316 
    317     /* expand top and bottom tables with blue fuzz */
    318     {
    319       FT_Int         dim, top, bot, delta;
    320       PSH_Blue_Zone  zone;
    321 
    322 
    323       zone  = top_table->zones;
    324       count = count_top;
    325 
    326       for ( dim = 1; dim >= 0; dim-- )
    327       {
    328         if ( count > 0 )
    329         {
    330           /* expand the bottom of the lowest zone normally */
    331           zone->org_bottom -= fuzz;
    332 
    333           /* expand the top and bottom of intermediate zones;    */
    334           /* checking that the interval is smaller than the fuzz */
    335           top = zone->org_top;
    336 
    337           for ( count--; count > 0; count-- )
    338           {
    339             bot   = zone[1].org_bottom;
    340             delta = bot - top;
    341 
    342             if ( delta < 2 * fuzz )
    343               zone[0].org_top = zone[1].org_bottom = top + delta / 2;
    344             else
    345             {
    346               zone[0].org_top    = top + fuzz;
    347               zone[1].org_bottom = bot - fuzz;
    348             }
    349 
    350             zone++;
    351             top = zone->org_top;
    352           }
    353 
    354           /* expand the top of the highest zone normally */
    355           zone->org_top = top + fuzz;
    356         }
    357         zone  = bot_table->zones;
    358         count = count_bot;
    359       }
    360     }
    361   }
    362 
    363 
    364   /* reset the blues table when the device transform changes */
    365   static void
    366   psh_blues_scale_zones( PSH_Blues  blues,
    367                          FT_Fixed   scale,
    368                          FT_Pos     delta )
    369   {
    370     FT_UInt         count;
    371     FT_UInt         num;
    372     PSH_Blue_Table  table = 0;
    373 
    374     /*                                                        */
    375     /* Determine whether we need to suppress overshoots or    */
    376     /* not.  We simply need to compare the vertical scale     */
    377     /* parameter to the raw bluescale value.  Here is why:    */
    378     /*                                                        */
    379     /*   We need to suppress overshoots for all pointsizes.   */
    380     /*   At 300dpi that satisfies:                            */
    381     /*                                                        */
    382     /*      pointsize < 240*bluescale + 0.49                  */
    383     /*                                                        */
    384     /*   This corresponds to:                                 */
    385     /*                                                        */
    386     /*      pixelsize < 1000*bluescale + 49/24                */
    387     /*                                                        */
    388     /*      scale*EM_Size < 1000*bluescale + 49/24            */
    389     /*                                                        */
    390     /*   However, for normal Type 1 fonts, EM_Size is 1000!   */
    391     /*   We thus only check:                                  */
    392     /*                                                        */
    393     /*      scale < bluescale + 49/24000                      */
    394     /*                                                        */
    395     /*   which we shorten to                                  */
    396     /*                                                        */
    397     /*      "scale < bluescale"                               */
    398     /*                                                        */
    399     /* Note that `blue_scale' is stored 1000 times its real   */
    400     /* value, and that `scale' converts from font units to    */
    401     /* fractional pixels.                                     */
    402     /*                                                        */
    403 
    404     /* 1000 / 64 = 125 / 8 */
    405     if ( scale >= 0x20C49BAL )
    406       blues->no_overshoots = FT_BOOL( scale < blues->blue_scale * 8 / 125 );
    407     else
    408       blues->no_overshoots = FT_BOOL( scale * 125 < blues->blue_scale * 8 );
    409 
    410     /*                                                        */
    411     /*  The blue threshold is the font units distance under   */
    412     /*  which overshoots are suppressed due to the BlueShift  */
    413     /*  even if the scale is greater than BlueScale.          */
    414     /*                                                        */
    415     /*  It is the smallest distance such that                 */
    416     /*                                                        */
    417     /*    dist <= BlueShift && dist*scale <= 0.5 pixels       */
    418     /*                                                        */
    419     {
    420       FT_Int  threshold = blues->blue_shift;
    421 
    422 
    423       while ( threshold > 0 && FT_MulFix( threshold, scale ) > 32 )
    424         threshold--;
    425 
    426       blues->blue_threshold = threshold;
    427     }
    428 
    429     for ( num = 0; num < 4; num++ )
    430     {
    431       PSH_Blue_Zone  zone;
    432 
    433 
    434       switch ( num )
    435       {
    436       case 0:
    437         table = &blues->normal_top;
    438         break;
    439       case 1:
    440         table = &blues->normal_bottom;
    441         break;
    442       case 2:
    443         table = &blues->family_top;
    444         break;
    445       default:
    446         table = &blues->family_bottom;
    447         break;
    448       }
    449 
    450       zone  = table->zones;
    451       count = table->count;
    452       for ( ; count > 0; count--, zone++ )
    453       {
    454         zone->cur_top    = FT_MulFix( zone->org_top,    scale ) + delta;
    455         zone->cur_bottom = FT_MulFix( zone->org_bottom, scale ) + delta;
    456         zone->cur_ref    = FT_MulFix( zone->org_ref,    scale ) + delta;
    457         zone->cur_delta  = FT_MulFix( zone->org_delta,  scale );
    458 
    459         /* round scaled reference position */
    460         zone->cur_ref = FT_PIX_ROUND( zone->cur_ref );
    461 
    462 #if 0
    463         if ( zone->cur_ref > zone->cur_top )
    464           zone->cur_ref -= 64;
    465         else if ( zone->cur_ref < zone->cur_bottom )
    466           zone->cur_ref += 64;
    467 #endif
    468       }
    469     }
    470 
    471     /* process the families now */
    472 
    473     for ( num = 0; num < 2; num++ )
    474     {
    475       PSH_Blue_Zone   zone1, zone2;
    476       FT_UInt         count1, count2;
    477       PSH_Blue_Table  normal, family;
    478 
    479 
    480       switch ( num )
    481       {
    482       case 0:
    483         normal = &blues->normal_top;
    484         family = &blues->family_top;
    485         break;
    486 
    487       default:
    488         normal = &blues->normal_bottom;
    489         family = &blues->family_bottom;
    490       }
    491 
    492       zone1  = normal->zones;
    493       count1 = normal->count;
    494 
    495       for ( ; count1 > 0; count1--, zone1++ )
    496       {
    497         /* try to find a family zone whose reference position is less */
    498         /* than 1 pixel far from the current zone                     */
    499         zone2  = family->zones;
    500         count2 = family->count;
    501 
    502         for ( ; count2 > 0; count2--, zone2++ )
    503         {
    504           FT_Pos  Delta;
    505 
    506 
    507           Delta = zone1->org_ref - zone2->org_ref;
    508           if ( Delta < 0 )
    509             Delta = -Delta;
    510 
    511           if ( FT_MulFix( Delta, scale ) < 64 )
    512           {
    513             zone1->cur_top    = zone2->cur_top;
    514             zone1->cur_bottom = zone2->cur_bottom;
    515             zone1->cur_ref    = zone2->cur_ref;
    516             zone1->cur_delta  = zone2->cur_delta;
    517             break;
    518           }
    519         }
    520       }
    521     }
    522   }
    523 
    524 
    525   FT_LOCAL_DEF( void )
    526   psh_blues_snap_stem( PSH_Blues      blues,
    527                        FT_Int         stem_top,
    528                        FT_Int         stem_bot,
    529                        PSH_Alignment  alignment )
    530   {
    531     PSH_Blue_Table  table;
    532     FT_UInt         count;
    533     FT_Pos          delta;
    534     PSH_Blue_Zone   zone;
    535     FT_Int          no_shoots;
    536 
    537 
    538     alignment->align = PSH_BLUE_ALIGN_NONE;
    539 
    540     no_shoots = blues->no_overshoots;
    541 
    542     /* look up stem top in top zones table */
    543     table = &blues->normal_top;
    544     count = table->count;
    545     zone  = table->zones;
    546 
    547     for ( ; count > 0; count--, zone++ )
    548     {
    549       delta = stem_top - zone->org_bottom;
    550       if ( delta < -blues->blue_fuzz )
    551         break;
    552 
    553       if ( stem_top <= zone->org_top + blues->blue_fuzz )
    554       {
    555         if ( no_shoots || delta <= blues->blue_threshold )
    556         {
    557           alignment->align    |= PSH_BLUE_ALIGN_TOP;
    558           alignment->align_top = zone->cur_ref;
    559         }
    560         break;
    561       }
    562     }
    563 
    564     /* look up stem bottom in bottom zones table */
    565     table = &blues->normal_bottom;
    566     count = table->count;
    567     zone  = table->zones + count-1;
    568 
    569     for ( ; count > 0; count--, zone-- )
    570     {
    571       delta = zone->org_top - stem_bot;
    572       if ( delta < -blues->blue_fuzz )
    573         break;
    574 
    575       if ( stem_bot >= zone->org_bottom - blues->blue_fuzz )
    576       {
    577         if ( no_shoots || delta < blues->blue_threshold )
    578         {
    579           alignment->align    |= PSH_BLUE_ALIGN_BOT;
    580           alignment->align_bot = zone->cur_ref;
    581         }
    582         break;
    583       }
    584     }
    585   }
    586 
    587 
    588   /*************************************************************************/
    589   /*************************************************************************/
    590   /*****                                                               *****/
    591   /*****                        GLOBAL HINTS                           *****/
    592   /*****                                                               *****/
    593   /*************************************************************************/
    594   /*************************************************************************/
    595 
    596   static void
    597   psh_globals_destroy( PSH_Globals  globals )
    598   {
    599     if ( globals )
    600     {
    601       FT_Memory  memory;
    602 
    603 
    604       memory = globals->memory;
    605       globals->dimension[0].stdw.count = 0;
    606       globals->dimension[1].stdw.count = 0;
    607 
    608       globals->blues.normal_top.count    = 0;
    609       globals->blues.normal_bottom.count = 0;
    610       globals->blues.family_top.count    = 0;
    611       globals->blues.family_bottom.count = 0;
    612 
    613       FT_FREE( globals );
    614 
    615 #ifdef DEBUG_HINTER
    616       ps_debug_globals = 0;
    617 #endif
    618     }
    619   }
    620 
    621 
    622   static FT_Error
    623   psh_globals_new( FT_Memory     memory,
    624                    T1_Private*   priv,
    625                    PSH_Globals  *aglobals )
    626   {
    627     PSH_Globals  globals;
    628     FT_Error     error;
    629 
    630 
    631     if ( !FT_NEW( globals ) )
    632     {
    633       FT_UInt    count;
    634       FT_Short*  read;
    635 
    636 
    637       globals->memory = memory;
    638 
    639       /* copy standard widths */
    640       {
    641         PSH_Dimension  dim   = &globals->dimension[1];
    642         PSH_Width      write = dim->stdw.widths;
    643 
    644 
    645         write->org = priv->standard_width[0];
    646         write++;
    647 
    648         read = priv->snap_widths;
    649         for ( count = priv->num_snap_widths; count > 0; count-- )
    650         {
    651           write->org = *read;
    652           write++;
    653           read++;
    654         }
    655 
    656         dim->stdw.count = priv->num_snap_widths + 1;
    657       }
    658 
    659       /* copy standard heights */
    660       {
    661         PSH_Dimension  dim = &globals->dimension[0];
    662         PSH_Width      write = dim->stdw.widths;
    663 
    664 
    665         write->org = priv->standard_height[0];
    666         write++;
    667         read = priv->snap_heights;
    668         for ( count = priv->num_snap_heights; count > 0; count-- )
    669         {
    670           write->org = *read;
    671           write++;
    672           read++;
    673         }
    674 
    675         dim->stdw.count = priv->num_snap_heights + 1;
    676       }
    677 
    678       /* copy blue zones */
    679       psh_blues_set_zones( &globals->blues, priv->num_blue_values,
    680                            priv->blue_values, priv->num_other_blues,
    681                            priv->other_blues, priv->blue_fuzz, 0 );
    682 
    683       psh_blues_set_zones( &globals->blues, priv->num_family_blues,
    684                            priv->family_blues, priv->num_family_other_blues,
    685                            priv->family_other_blues, priv->blue_fuzz, 1 );
    686 
    687       globals->blues.blue_scale = priv->blue_scale;
    688       globals->blues.blue_shift = priv->blue_shift;
    689       globals->blues.blue_fuzz  = priv->blue_fuzz;
    690 
    691       globals->dimension[0].scale_mult  = 0;
    692       globals->dimension[0].scale_delta = 0;
    693       globals->dimension[1].scale_mult  = 0;
    694       globals->dimension[1].scale_delta = 0;
    695 
    696 #ifdef DEBUG_HINTER
    697       ps_debug_globals = globals;
    698 #endif
    699     }
    700 
    701     *aglobals = globals;
    702     return error;
    703   }
    704 
    705 
    706   FT_LOCAL_DEF( FT_Error )
    707   psh_globals_set_scale( PSH_Globals  globals,
    708                          FT_Fixed     x_scale,
    709                          FT_Fixed     y_scale,
    710                          FT_Fixed     x_delta,
    711                          FT_Fixed     y_delta )
    712   {
    713     PSH_Dimension  dim = &globals->dimension[0];
    714 
    715 
    716     dim = &globals->dimension[0];
    717     if ( x_scale != dim->scale_mult  ||
    718          x_delta != dim->scale_delta )
    719     {
    720       dim->scale_mult  = x_scale;
    721       dim->scale_delta = x_delta;
    722 
    723       psh_globals_scale_widths( globals, 0 );
    724     }
    725 
    726     dim = &globals->dimension[1];
    727     if ( y_scale != dim->scale_mult  ||
    728          y_delta != dim->scale_delta )
    729     {
    730       dim->scale_mult  = y_scale;
    731       dim->scale_delta = y_delta;
    732 
    733       psh_globals_scale_widths( globals, 1 );
    734       psh_blues_scale_zones( &globals->blues, y_scale, y_delta );
    735     }
    736 
    737     return 0;
    738   }
    739 
    740 
    741   FT_LOCAL_DEF( void )
    742   psh_globals_funcs_init( PSH_Globals_FuncsRec*  funcs )
    743   {
    744     funcs->create    = psh_globals_new;
    745     funcs->set_scale = psh_globals_set_scale;
    746     funcs->destroy   = psh_globals_destroy;
    747   }
    748 
    749 
    750 /* END */
    751