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 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 = 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 */