(root)/
freetype-2.13.2/
src/
pshinter/
pshglob.c
       1  /****************************************************************************
       2   *
       3   * pshglob.c
       4   *
       5   *   PostScript hinter global hinting management (body).
       6   *   Inspired by the new auto-hinter module.
       7   *
       8   * Copyright (C) 2001-2023 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 <freetype/freetype.h>
      21  #include <freetype/internal/ftobjs.h>
      22  #include <freetype/internal/ftcalc.h>
      23  #include "pshglob.h"
      24  
      25  #ifdef DEBUG_HINTER
      26    PSH_Globals  ps_debug_globals = NULL;
      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 in 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 our */
     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_UInt         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 = NULL;
     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    /* calculate the maximum height of given blue zones */
     526    static FT_Short
     527    psh_calc_max_height( FT_UInt          num,
     528                         const FT_Short*  values,
     529                         FT_Short         cur_max )
     530    {
     531      FT_UInt  count;
     532  
     533  
     534      for ( count = 0; count < num; count += 2 )
     535      {
     536        FT_Short  cur_height = values[count + 1] - values[count];
     537  
     538  
     539        if ( cur_height > cur_max )
     540          cur_max = cur_height;
     541      }
     542  
     543      return cur_max;
     544    }
     545  
     546  
     547    FT_LOCAL_DEF( void )
     548    psh_blues_snap_stem( PSH_Blues      blues,
     549                         FT_Int         stem_top,
     550                         FT_Int         stem_bot,
     551                         PSH_Alignment  alignment )
     552    {
     553      PSH_Blue_Table  table;
     554      FT_UInt         count;
     555      FT_Pos          delta;
     556      PSH_Blue_Zone   zone;
     557      FT_Int          no_shoots;
     558  
     559  
     560      alignment->align = PSH_BLUE_ALIGN_NONE;
     561  
     562      no_shoots = blues->no_overshoots;
     563  
     564      /* look up stem top in top zones table */
     565      table = &blues->normal_top;
     566      count = table->count;
     567      zone  = table->zones;
     568  
     569      for ( ; count > 0; count--, zone++ )
     570      {
     571        delta = SUB_LONG( stem_top, zone->org_bottom );
     572        if ( delta < -blues->blue_fuzz )
     573          break;
     574  
     575        if ( stem_top <= zone->org_top + blues->blue_fuzz )
     576        {
     577          if ( no_shoots || delta <= blues->blue_threshold )
     578          {
     579            alignment->align    |= PSH_BLUE_ALIGN_TOP;
     580            alignment->align_top = zone->cur_ref;
     581          }
     582          break;
     583        }
     584      }
     585  
     586      /* look up stem bottom in bottom zones table */
     587      table = &blues->normal_bottom;
     588      count = table->count;
     589      zone  = table->zones + count-1;
     590  
     591      for ( ; count > 0; count--, zone-- )
     592      {
     593        delta = SUB_LONG( zone->org_top, stem_bot );
     594        if ( delta < -blues->blue_fuzz )
     595          break;
     596  
     597        if ( stem_bot >= zone->org_bottom - blues->blue_fuzz )
     598        {
     599          if ( no_shoots || delta < blues->blue_threshold )
     600          {
     601            alignment->align    |= PSH_BLUE_ALIGN_BOT;
     602            alignment->align_bot = zone->cur_ref;
     603          }
     604          break;
     605        }
     606      }
     607    }
     608  
     609  
     610    /*************************************************************************/
     611    /*************************************************************************/
     612    /*****                                                               *****/
     613    /*****                        GLOBAL HINTS                           *****/
     614    /*****                                                               *****/
     615    /*************************************************************************/
     616    /*************************************************************************/
     617  
     618    static void
     619    psh_globals_destroy( PSH_Globals  globals )
     620    {
     621      if ( globals )
     622      {
     623        FT_Memory  memory;
     624  
     625  
     626        memory = globals->memory;
     627        globals->dimension[0].stdw.count = 0;
     628        globals->dimension[1].stdw.count = 0;
     629  
     630        globals->blues.normal_top.count    = 0;
     631        globals->blues.normal_bottom.count = 0;
     632        globals->blues.family_top.count    = 0;
     633        globals->blues.family_bottom.count = 0;
     634  
     635        FT_FREE( globals );
     636  
     637  #ifdef DEBUG_HINTER
     638        ps_debug_globals = NULL;
     639  #endif
     640      }
     641    }
     642  
     643  
     644    static FT_Error
     645    psh_globals_new( FT_Memory     memory,
     646                     T1_Private*   priv,
     647                     PSH_Globals  *aglobals )
     648    {
     649      PSH_Globals  globals = NULL;
     650      FT_Error     error;
     651  
     652  
     653      if ( !FT_QNEW( globals ) )
     654      {
     655        FT_UInt    count;
     656        FT_Short*  read;
     657  
     658  
     659        globals->memory = memory;
     660  
     661        /* copy standard widths */
     662        {
     663          PSH_Dimension  dim   = &globals->dimension[1];
     664          PSH_Width      write = dim->stdw.widths;
     665  
     666  
     667          write->org = priv->standard_width[0];
     668          write++;
     669  
     670          read = priv->snap_widths;
     671          for ( count = priv->num_snap_widths; count > 0; count-- )
     672          {
     673            write->org = *read;
     674            write++;
     675            read++;
     676          }
     677  
     678          dim->stdw.count = priv->num_snap_widths + 1;
     679        }
     680  
     681        /* copy standard heights */
     682        {
     683          PSH_Dimension  dim = &globals->dimension[0];
     684          PSH_Width      write = dim->stdw.widths;
     685  
     686  
     687          write->org = priv->standard_height[0];
     688          write++;
     689          read = priv->snap_heights;
     690          for ( count = priv->num_snap_heights; count > 0; count-- )
     691          {
     692            write->org = *read;
     693            write++;
     694            read++;
     695          }
     696  
     697          dim->stdw.count = priv->num_snap_heights + 1;
     698        }
     699  
     700        /* copy blue zones */
     701        psh_blues_set_zones( &globals->blues, priv->num_blue_values,
     702                             priv->blue_values, priv->num_other_blues,
     703                             priv->other_blues, priv->blue_fuzz, 0 );
     704  
     705        psh_blues_set_zones( &globals->blues, priv->num_family_blues,
     706                             priv->family_blues, priv->num_family_other_blues,
     707                             priv->family_other_blues, priv->blue_fuzz, 1 );
     708  
     709        /* limit the BlueScale value to `1 / max_of_blue_zone_heights' */
     710        {
     711          FT_Fixed  max_scale;
     712          FT_Short  max_height = 1;
     713  
     714  
     715          max_height = psh_calc_max_height( priv->num_blue_values,
     716                                            priv->blue_values,
     717                                            max_height );
     718          max_height = psh_calc_max_height( priv->num_other_blues,
     719                                            priv->other_blues,
     720                                            max_height );
     721          max_height = psh_calc_max_height( priv->num_family_blues,
     722                                            priv->family_blues,
     723                                            max_height );
     724          max_height = psh_calc_max_height( priv->num_family_other_blues,
     725                                            priv->family_other_blues,
     726                                            max_height );
     727  
     728          /* BlueScale is scaled 1000 times */
     729          max_scale = FT_DivFix( 1000, max_height );
     730          globals->blues.blue_scale = priv->blue_scale < max_scale
     731                                        ? priv->blue_scale
     732                                        : max_scale;
     733        }
     734  
     735        globals->blues.blue_shift = priv->blue_shift;
     736        globals->blues.blue_fuzz  = priv->blue_fuzz;
     737  
     738        globals->dimension[0].scale_mult  = 0;
     739        globals->dimension[0].scale_delta = 0;
     740        globals->dimension[1].scale_mult  = 0;
     741        globals->dimension[1].scale_delta = 0;
     742  
     743  #ifdef DEBUG_HINTER
     744        ps_debug_globals = globals;
     745  #endif
     746      }
     747  
     748      *aglobals = globals;
     749      return error;
     750    }
     751  
     752  
     753    FT_LOCAL_DEF( void )
     754    psh_globals_set_scale( PSH_Globals  globals,
     755                           FT_Fixed     x_scale,
     756                           FT_Fixed     y_scale,
     757                           FT_Fixed     x_delta,
     758                           FT_Fixed     y_delta )
     759    {
     760      PSH_Dimension  dim;
     761  
     762  
     763      dim = &globals->dimension[0];
     764      if ( x_scale != dim->scale_mult  ||
     765           x_delta != dim->scale_delta )
     766      {
     767        dim->scale_mult  = x_scale;
     768        dim->scale_delta = x_delta;
     769  
     770        psh_globals_scale_widths( globals, 0 );
     771      }
     772  
     773      dim = &globals->dimension[1];
     774      if ( y_scale != dim->scale_mult  ||
     775           y_delta != dim->scale_delta )
     776      {
     777        dim->scale_mult  = y_scale;
     778        dim->scale_delta = y_delta;
     779  
     780        psh_globals_scale_widths( globals, 1 );
     781        psh_blues_scale_zones( &globals->blues, y_scale, y_delta );
     782      }
     783    }
     784  
     785  
     786    FT_LOCAL_DEF( void )
     787    psh_globals_funcs_init( PSH_Globals_FuncsRec*  funcs )
     788    {
     789      funcs->create    = psh_globals_new;
     790      funcs->set_scale = psh_globals_set_scale;
     791      funcs->destroy   = psh_globals_destroy;
     792    }
     793  
     794  
     795  /* END */