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-2018 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 = 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 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_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 = 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 = 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_NEW( 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 */
    796