Edit

kc3-lang/freetype/src/cache/ftcbasic.c

Branch :

  • Show log

    Commit

  • Author : Werner Lemberg
    Date : 2007-05-14 18:53:58
    Hash : 56ceaa48
    Message : Formatting.

  • src/cache/ftcbasic.c
  • /***************************************************************************/
    /*                                                                         */
    /*  ftcbasic.c                                                             */
    /*                                                                         */
    /*    The FreeType basic cache interface (body).                           */
    /*                                                                         */
    /*  Copyright 2003, 2004, 2005, 2006, 2007 by                              */
    /*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
    /*                                                                         */
    /*  This file is part of the FreeType project, and may only be used,       */
    /*  modified, and distributed under the terms of the FreeType project      */
    /*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
    /*  this file you indicate that you have read the license and              */
    /*  understand and accept it fully.                                        */
    /*                                                                         */
    /***************************************************************************/
    
    
    #include <ft2build.h>
    #include FT_CACHE_H
    #include "ftcglyph.h"
    #include "ftcimage.h"
    #include "ftcsbits.h"
    #include FT_INTERNAL_MEMORY_H
    
    #include "ftccback.h"
    #include "ftcerror.h"
    
    
    #ifdef FT_CONFIG_OPTION_OLD_INTERNALS
    
      /*
       *  These structures correspond to the FTC_Font and FTC_ImageDesc types
       *  that were defined in version 2.1.7.
       */
      typedef struct  FTC_OldFontRec_
      {
        FTC_FaceID  face_id;
        FT_UShort   pix_width;
        FT_UShort   pix_height;
    
      } FTC_OldFontRec, *FTC_OldFont;
    
    
      typedef struct  FTC_OldImageDescRec_
      {
        FTC_OldFontRec  font;
        FT_UInt32       flags;
    
      } FTC_OldImageDescRec, *FTC_OldImageDesc;
    
    
      /*
       *  Notice that FTC_OldImageDescRec and FTC_ImageTypeRec are nearly
       *  identical, bit-wise.  The only difference is that the `width' and
       *  `height' fields are expressed as 16-bit integers in the old structure,
       *  and as normal `int' in the new one.
       *
       *  We are going to perform a weird hack to detect which structure is
       *  being passed to the image and sbit caches.  If the new structure's
       *  `width' is larger than 0x10000, we assume that we are really receiving
       *  an FTC_OldImageDesc.
       */
    
    #endif /* FT_CONFIG_OPTION_OLD_INTERNALS */
    
    
      /*
       *  Basic Families
       *
       */
      typedef struct  FTC_BasicAttrRec_
      {
        FTC_ScalerRec  scaler;
        FT_UInt        load_flags;
    
      } FTC_BasicAttrRec, *FTC_BasicAttrs;
    
    #define FTC_BASIC_ATTR_COMPARE( a, b )                                 \
              FT_BOOL( FTC_SCALER_COMPARE( &(a)->scaler, &(b)->scaler ) && \
                       (a)->load_flags == (b)->load_flags               )
    
    #define FTC_BASIC_ATTR_HASH( a )                                   \
              ( FTC_SCALER_HASH( &(a)->scaler ) + 31*(a)->load_flags )
    
    
      typedef struct  FTC_BasicQueryRec_
      {
        FTC_GQueryRec     gquery;
        FTC_BasicAttrRec  attrs;
    
      } FTC_BasicQueryRec, *FTC_BasicQuery;
    
    
      typedef struct  FTC_BasicFamilyRec_
      {
        FTC_FamilyRec     family;
        FTC_BasicAttrRec  attrs;
    
      } FTC_BasicFamilyRec, *FTC_BasicFamily;
    
    
      FT_CALLBACK_DEF( FT_Bool )
      ftc_basic_family_compare( FTC_MruNode  ftcfamily,
                                FT_Pointer   ftcquery )
      {
        FTC_BasicFamily  family = (FTC_BasicFamily)ftcfamily;
        FTC_BasicQuery   query  = (FTC_BasicQuery)ftcquery;
    
    
        return FTC_BASIC_ATTR_COMPARE( &family->attrs, &query->attrs );
      }
    
    
      FT_CALLBACK_DEF( FT_Error )
      ftc_basic_family_init( FTC_MruNode  ftcfamily,
                             FT_Pointer   ftcquery,
                             FT_Pointer   ftccache )
      {
        FTC_BasicFamily  family = (FTC_BasicFamily)ftcfamily;
        FTC_BasicQuery   query  = (FTC_BasicQuery)ftcquery;
        FTC_Cache        cache  = (FTC_Cache)ftccache;
    
    
        FTC_Family_Init( FTC_FAMILY( family ), cache );
        family->attrs = query->attrs;
        return 0;
      }
    
    
      FT_CALLBACK_DEF( FT_UInt )
      ftc_basic_family_get_count( FTC_Family   ftcfamily,
                                  FTC_Manager  manager )
      {
        FTC_BasicFamily  family = (FTC_BasicFamily)ftcfamily;
        FT_Error         error;
        FT_Face          face;
        FT_UInt          result = 0;
    
    
        error = FTC_Manager_LookupFace( manager, family->attrs.scaler.face_id,
                                        &face );
        if ( !error )
          result = face->num_glyphs;
    
        return result;
      }
    
    
      FT_CALLBACK_DEF( FT_Error )
      ftc_basic_family_load_bitmap( FTC_Family   ftcfamily,
                                    FT_UInt      gindex,
                                    FTC_Manager  manager,
                                    FT_Face     *aface )
      {
        FTC_BasicFamily  family = (FTC_BasicFamily)ftcfamily;
        FT_Error         error;
        FT_Size          size;
    
    
        error = FTC_Manager_LookupSize( manager, &family->attrs.scaler, &size );
        if ( !error )
        {
          FT_Face  face = size->face;
    
    
          error = FT_Load_Glyph( face, gindex,
                                 family->attrs.load_flags | FT_LOAD_RENDER );
          if ( !error )
            *aface = face;
        }
    
        return error;
      }
    
    
      FT_CALLBACK_DEF( FT_Error )
      ftc_basic_family_load_glyph( FTC_Family  ftcfamily,
                                   FT_UInt     gindex,
                                   FTC_Cache   cache,
                                   FT_Glyph   *aglyph )
      {
        FTC_BasicFamily  family = (FTC_BasicFamily)ftcfamily;
        FT_Error         error;
        FTC_Scaler       scaler = &family->attrs.scaler;
        FT_Face          face;
        FT_Size          size;
    
    
        /* we will now load the glyph image */
        error = FTC_Manager_LookupSize( cache->manager,
                                        scaler,
                                        &size );
        if ( !error )
        {
          face = size->face;
    
          error = FT_Load_Glyph( face, gindex, family->attrs.load_flags );
          if ( !error )
          {
            if ( face->glyph->format == FT_GLYPH_FORMAT_BITMAP  ||
                 face->glyph->format == FT_GLYPH_FORMAT_OUTLINE )
            {
              /* ok, copy it */
              FT_Glyph  glyph;
    
    
              error = FT_Get_Glyph( face->glyph, &glyph );
              if ( !error )
              {
                *aglyph = glyph;
                goto Exit;
              }
            }
            else
              error = FTC_Err_Invalid_Argument;
          }
        }
    
      Exit:
        return error;
      }
    
    
      FT_CALLBACK_DEF( FT_Bool )
      ftc_basic_gnode_compare_faceid( FTC_Node    ftcgnode,
                                      FT_Pointer  ftcface_id,
                                      FTC_Cache   cache )
      {
        FTC_GNode        gnode   = (FTC_GNode)ftcgnode;
        FTC_FaceID       face_id = (FTC_FaceID)ftcface_id;
        FTC_BasicFamily  family  = (FTC_BasicFamily)gnode->family;
        FT_Bool          result;
    
    
        result = FT_BOOL( family->attrs.scaler.face_id == face_id );
        if ( result )
        {
          /* we must call this function to avoid this node from appearing
           * in later lookups with the same face_id!
           */
          FTC_GNode_UnselectFamily( gnode, cache );
        }
        return result;
      }
    
    
     /*
      *
      * basic image cache
      *
      */
    
      FT_CALLBACK_TABLE_DEF
      const FTC_IFamilyClassRec  ftc_basic_image_family_class =
      {
        {
          sizeof ( FTC_BasicFamilyRec ),
          ftc_basic_family_compare,
          ftc_basic_family_init,
          0,                        /* FTC_MruNode_ResetFunc */
          0                         /* FTC_MruNode_DoneFunc  */
        },
        ftc_basic_family_load_glyph
      };
    
    
      FT_CALLBACK_TABLE_DEF
      const FTC_GCacheClassRec  ftc_basic_image_cache_class =
      {
        {
          ftc_inode_new,
          ftc_inode_weight,
          ftc_gnode_compare,
          ftc_basic_gnode_compare_faceid,
          ftc_inode_free,
    
          sizeof ( FTC_GCacheRec ),
          ftc_gcache_init,
          ftc_gcache_done
        },
        (FTC_MruListClass)&ftc_basic_image_family_class
      };
    
    
      /* documentation is in ftcache.h */
    
      FT_EXPORT_DEF( FT_Error )
      FTC_ImageCache_New( FTC_Manager      manager,
                          FTC_ImageCache  *acache )
      {
        return FTC_GCache_New( manager, &ftc_basic_image_cache_class,
                               (FTC_GCache*)acache );
      }
    
    
      /* documentation is in ftcache.h */
    
      FT_EXPORT_DEF( FT_Error )
      FTC_ImageCache_Lookup( FTC_ImageCache  cache,
                             FTC_ImageType   type,
                             FT_UInt         gindex,
                             FT_Glyph       *aglyph,
                             FTC_Node       *anode )
      {
        FTC_BasicQueryRec  query;
        FTC_INode          node = 0;  /* make compiler happy */
        FT_Error           error;
        FT_UInt32          hash;
    
    
        /* some argument checks are delayed to FTC_Cache_Lookup */
        if ( !aglyph )
        {
          error = FTC_Err_Invalid_Argument;
          goto Exit;
        }
    
        *aglyph = NULL;
        if ( anode )
          *anode  = NULL;
    
    #ifdef FT_CONFIG_OPTION_OLD_INTERNALS
    
        /*
         *  This one is a major hack used to detect whether we are passed a
         *  regular FTC_ImageType handle, or a legacy FTC_OldImageDesc one.
         */
        if ( type->width >= 0x10000 )
        {
          FTC_OldImageDesc  desc = (FTC_OldImageDesc)type;
    
    
          query.attrs.scaler.face_id = desc->font.face_id;
          query.attrs.scaler.width   = desc->font.pix_width;
          query.attrs.scaler.height  = desc->font.pix_height;
          query.attrs.load_flags     = desc->flags;
        }
        else
    
    #endif /* FT_CONFIG_OPTION_OLD_INTERNALS */
    
        {
          query.attrs.scaler.face_id = type->face_id;
          query.attrs.scaler.width   = type->width;
          query.attrs.scaler.height  = type->height;
          query.attrs.load_flags     = type->flags;
        }
    
        query.attrs.scaler.pixel = 1;
        query.attrs.scaler.x_res = 0;  /* make compilers happy */
        query.attrs.scaler.y_res = 0;
    
        hash = FTC_BASIC_ATTR_HASH( &query.attrs ) + gindex;
    
    #if 1  /* inlining is about 50% faster! */
        FTC_GCACHE_LOOKUP_CMP( cache,
                               ftc_basic_family_compare,
                               FTC_GNode_Compare,
                               hash, gindex,
                               &query,
                               node,
                               error );
    #else
        error = FTC_GCache_Lookup( FTC_GCACHE( cache ),
                                   hash, gindex,
                                   FTC_GQUERY( &query ),
                                   (FTC_Node*) &node );
    #endif
        if ( !error )
        {
          *aglyph = FTC_INODE( node )->glyph;
    
          if ( anode )
          {
            *anode = FTC_NODE( node );
            FTC_NODE( node )->ref_count++;
          }
        }
    
      Exit:
        return error;
      }
    
    
      /* documentation is in ftcache.h */
    
      FT_EXPORT_DEF( FT_Error )
      FTC_ImageCache_LookupScaler( FTC_ImageCache  cache,
                                   FTC_Scaler      scaler,
                                   FT_ULong        load_flags,
                                   FT_UInt         gindex,
                                   FT_Glyph       *aglyph,
                                   FTC_Node       *anode )
      {
        FTC_BasicQueryRec  query;
        FTC_INode          node = 0;  /* make compiler happy */
        FT_Error           error;
        FT_UInt32          hash;
    
    
        /* some argument checks are delayed to FTC_Cache_Lookup */
        if ( !aglyph || !scaler )
        {
          error = FTC_Err_Invalid_Argument;
          goto Exit;
        }
    
        *aglyph = NULL;
        if ( anode )
          *anode  = NULL;
    
        query.attrs.scaler     = scaler[0];
        query.attrs.load_flags = load_flags;
    
        hash = FTC_BASIC_ATTR_HASH( &query.attrs ) + gindex;
    
        FTC_GCACHE_LOOKUP_CMP( cache,
                               ftc_basic_family_compare,
                               FTC_GNode_Compare,
                               hash, gindex,
                               &query,
                               node,
                               error );
        if ( !error )
        {
          *aglyph = FTC_INODE( node )->glyph;
    
          if ( anode )
          {
            *anode = FTC_NODE( node );
            FTC_NODE( node )->ref_count++;
          }
        }
    
      Exit:
        return error;
      }
    
    
      
    #ifdef FT_CONFIG_OPTION_OLD_INTERNALS
    
      /* yet another backwards-legacy structure */
      typedef struct  FTC_OldImage_Desc_
      {
        FTC_FontRec  font;
        FT_UInt      image_type;
    
      } FTC_OldImage_Desc;
    
    
    #define FTC_OLD_IMAGE_FORMAT( x )  ( (x) & 7 )
    
    
    #define ftc_old_image_format_bitmap    0x0000
    #define ftc_old_image_format_outline   0x0001
    
    #define ftc_old_image_format_mask      0x000F
    
    #define ftc_old_image_flag_monochrome  0x0010
    #define ftc_old_image_flag_unhinted    0x0020
    #define ftc_old_image_flag_autohinted  0x0040
    #define ftc_old_image_flag_unscaled    0x0080
    #define ftc_old_image_flag_no_sbits    0x0100
    
      /* monochrome bitmap */
    #define ftc_old_image_mono             ftc_old_image_format_bitmap   | \
                                           ftc_old_image_flag_monochrome
    
      /* anti-aliased bitmap */
    #define ftc_old_image_grays            ftc_old_image_format_bitmap
    
      /* scaled outline */
    #define ftc_old_image_outline          ftc_old_image_format_outline
    
    
      static void
      ftc_image_type_from_old_desc( FTC_ImageType       typ,
                                    FTC_OldImage_Desc*  desc )
      {
        typ->face_id = desc->font.face_id;
        typ->width   = desc->font.pix_width;
        typ->height  = desc->font.pix_height;
    
        /* convert image type flags to load flags */
        {
          FT_UInt  load_flags = FT_LOAD_DEFAULT;
          FT_UInt  type       = desc->image_type;
    
    
          /* determine load flags, depending on the font description's */
          /* image type                                                */
    
          if ( FTC_OLD_IMAGE_FORMAT( type ) == ftc_old_image_format_bitmap )
          {
            if ( type & ftc_old_image_flag_monochrome )
              load_flags |= FT_LOAD_MONOCHROME;
    
            /* disable embedded bitmaps loading if necessary */
            if ( type & ftc_old_image_flag_no_sbits )
              load_flags |= FT_LOAD_NO_BITMAP;
          }
          else
          {
            /* we want an outline, don't load embedded bitmaps */
            load_flags |= FT_LOAD_NO_BITMAP;
    
            if ( type & ftc_old_image_flag_unscaled )
              load_flags |= FT_LOAD_NO_SCALE;
          }
    
          /* always render glyphs to bitmaps */
          load_flags |= FT_LOAD_RENDER;
    
          if ( type & ftc_old_image_flag_unhinted )
            load_flags |= FT_LOAD_NO_HINTING;
    
          if ( type & ftc_old_image_flag_autohinted )
            load_flags |= FT_LOAD_FORCE_AUTOHINT;
    
          typ->flags = load_flags;
        }
      }
    
    
      FT_EXPORT( FT_Error )
      FTC_Image_Cache_New( FTC_Manager      manager,
                           FTC_ImageCache  *acache );
    
      FT_EXPORT( FT_Error )
      FTC_Image_Cache_Lookup( FTC_ImageCache      icache,
                              FTC_OldImage_Desc*  desc,
                              FT_UInt             gindex,
                              FT_Glyph           *aglyph );
    
    
      FT_EXPORT_DEF( FT_Error )
      FTC_Image_Cache_New( FTC_Manager      manager,
                           FTC_ImageCache  *acache )
      {
        return FTC_ImageCache_New( manager, (FTC_ImageCache*)acache );
      }
    
    
    
      FT_EXPORT_DEF( FT_Error )
      FTC_Image_Cache_Lookup( FTC_ImageCache      icache,
                              FTC_OldImage_Desc*  desc,
                              FT_UInt             gindex,
                              FT_Glyph           *aglyph )
      {
        FTC_ImageTypeRec  type0;
    
    
        if ( !desc )
          return FTC_Err_Invalid_Argument;
    
        ftc_image_type_from_old_desc( &type0, desc );
    
        return FTC_ImageCache_Lookup( (FTC_ImageCache)icache,
                                       &type0,
                                       gindex,
                                       aglyph,
                                       NULL );
      }
    
    #endif /* FT_CONFIG_OPTION_OLD_INTERNALS */
    
    
     /*
      *
      * basic small bitmap cache
      *
      */
    
    
      FT_CALLBACK_TABLE_DEF
      const FTC_SFamilyClassRec  ftc_basic_sbit_family_class =
      {
        {
          sizeof( FTC_BasicFamilyRec ),
          ftc_basic_family_compare,
          ftc_basic_family_init,
          0,                            /* FTC_MruNode_ResetFunc */
          0                             /* FTC_MruNode_DoneFunc  */
        },
        ftc_basic_family_get_count,
        ftc_basic_family_load_bitmap
      };
    
    
      FT_CALLBACK_TABLE_DEF
      const FTC_GCacheClassRec  ftc_basic_sbit_cache_class =
      {
        {
          ftc_snode_new,
          ftc_snode_weight,
          ftc_snode_compare,
          ftc_basic_gnode_compare_faceid,
          ftc_snode_free,
    
          sizeof ( FTC_GCacheRec ),
          ftc_gcache_init,
          ftc_gcache_done
        },
        (FTC_MruListClass)&ftc_basic_sbit_family_class
      };
    
    
      /* documentation is in ftcache.h */
    
      FT_EXPORT_DEF( FT_Error )
      FTC_SBitCache_New( FTC_Manager     manager,
                         FTC_SBitCache  *acache )
      {
        return FTC_GCache_New( manager, &ftc_basic_sbit_cache_class,
                               (FTC_GCache*)acache );
      }
    
    
      /* documentation is in ftcache.h */
    
      FT_EXPORT_DEF( FT_Error )
      FTC_SBitCache_Lookup( FTC_SBitCache  cache,
                            FTC_ImageType  type,
                            FT_UInt        gindex,
                            FTC_SBit      *ansbit,
                            FTC_Node      *anode )
      {
        FT_Error           error;
        FTC_BasicQueryRec  query;
        FTC_SNode          node = 0; /* make compiler happy */
        FT_UInt32          hash;
    
    
        if ( anode )
          *anode = NULL;
    
        /* other argument checks delayed to FTC_Cache_Lookup */
        if ( !ansbit )
          return FTC_Err_Invalid_Argument;
    
        *ansbit = NULL;
    
    #ifdef FT_CONFIG_OPTION_OLD_INTERNALS
    
        /*  This one is a major hack used to detect whether we are passed a
         *  regular FTC_ImageType handle, or a legacy FTC_OldImageDesc one.
         */
        if ( type->width >= 0x10000 )
        {
          FTC_OldImageDesc  desc = (FTC_OldImageDesc)type;
    
    
          query.attrs.scaler.face_id = desc->font.face_id;
          query.attrs.scaler.width   = desc->font.pix_width;
          query.attrs.scaler.height  = desc->font.pix_height;
          query.attrs.load_flags     = desc->flags;
        }
        else
    
    #endif /* FT_CONFIG_OPTION_OLD_INTERNALS */
    
        {
          query.attrs.scaler.face_id = type->face_id;
          query.attrs.scaler.width   = type->width;
          query.attrs.scaler.height  = type->height;
          query.attrs.load_flags     = type->flags;
        }
    
        query.attrs.scaler.pixel = 1;
        query.attrs.scaler.x_res = 0;  /* make compilers happy */
        query.attrs.scaler.y_res = 0;
    
        /* beware, the hash must be the same for all glyph ranges! */
        hash = FTC_BASIC_ATTR_HASH( &query.attrs ) +
               gindex / FTC_SBIT_ITEMS_PER_NODE;
    
    #if 1  /* inlining is about 50% faster! */
        FTC_GCACHE_LOOKUP_CMP( cache,
                               ftc_basic_family_compare,
                               FTC_SNode_Compare,
                               hash, gindex,
                               &query,
                               node,
                               error );
    #else
        error = FTC_GCache_Lookup( FTC_GCACHE( cache ),
                                   hash,
                                   gindex,
                                   FTC_GQUERY( &query ),
                                   (FTC_Node*)&node );
    #endif
        if ( error )
          goto Exit;
    
        *ansbit = node->sbits + ( gindex - FTC_GNODE( node )->gindex );
    
        if ( anode )
        {
          *anode = FTC_NODE( node );
          FTC_NODE( node )->ref_count++;
        }
    
      Exit:
        return error;
      }
    
    
      /* documentation is in ftcache.h */
    
      FT_EXPORT_DEF( FT_Error )
      FTC_SBitCache_LookupScaler( FTC_SBitCache  cache,
                                  FTC_Scaler     scaler,
                                  FT_ULong       load_flags,
                                  FT_UInt        gindex,
                                  FTC_SBit      *ansbit,
                                  FTC_Node      *anode )
      {
        FT_Error           error;
        FTC_BasicQueryRec  query;
        FTC_SNode          node = 0; /* make compiler happy */
        FT_UInt32          hash;
    
    
        if ( anode )
            *anode = NULL;
    
        /* other argument checks delayed to FTC_Cache_Lookup */
        if ( !ansbit || !scaler )
            return FTC_Err_Invalid_Argument;
    
        *ansbit = NULL;
    
        query.attrs.scaler     = scaler[0];
        query.attrs.load_flags = load_flags;
    
        /* beware, the hash must be the same for all glyph ranges! */
        hash = FTC_BASIC_ATTR_HASH( &query.attrs ) +
                 gindex / FTC_SBIT_ITEMS_PER_NODE;
    
        FTC_GCACHE_LOOKUP_CMP( cache,
                               ftc_basic_family_compare,
                               FTC_SNode_Compare,
                               hash, gindex,
                               &query,
                               node,
                               error );
        if ( error )
          goto Exit;
    
        *ansbit = node->sbits + ( gindex - FTC_GNODE( node )->gindex );
    
        if ( anode )
        {
          *anode = FTC_NODE( node );
          FTC_NODE( node )->ref_count++;
        }
    
      Exit:
        return error;
      }
    
    
    #ifdef FT_CONFIG_OPTION_OLD_INTERNALS
    
      FT_EXPORT( FT_Error )
      FTC_SBit_Cache_New( FTC_Manager     manager,
                          FTC_SBitCache  *acache );
    
      FT_EXPORT( FT_Error )
      FTC_SBit_Cache_Lookup( FTC_SBitCache       cache,
                             FTC_OldImage_Desc*  desc,
                             FT_UInt             gindex,
                             FTC_SBit           *ansbit );
    
    
      FT_EXPORT_DEF( FT_Error )
      FTC_SBit_Cache_New( FTC_Manager     manager,
                          FTC_SBitCache  *acache )
      {
        return FTC_SBitCache_New( manager, (FTC_SBitCache*)acache );
      }
    
    
      FT_EXPORT_DEF( FT_Error )
      FTC_SBit_Cache_Lookup( FTC_SBitCache       cache,
                             FTC_OldImage_Desc*  desc,
                             FT_UInt             gindex,
                             FTC_SBit           *ansbit )
      {
        FTC_ImageTypeRec  type0;
    
    
        if ( !desc )
          return FT_Err_Invalid_Argument;
    
        ftc_image_type_from_old_desc( &type0, desc );
    
        return FTC_SBitCache_Lookup( (FTC_SBitCache)cache,
                                      &type0,
                                      gindex,
                                      ansbit,
                                      NULL );
      }
    
    #endif /* FT_CONFIG_OPTION_OLD_INTERNALS */
    
    
    /* END */